--- /dev/null
+ ; must begin on a page boundary (address % 256 == 0) to ensure
+ ; the jump table is correctly aligned
+ ;
+ ; This varies by platform so we really want to make this relocatable
+ ; not linked per platform somehow
+ ;
+ ;
+ ; TODO
+ ; - Recheck all syscall translations
+ ; - Fix up directory mapping a bit more
+ ; - Move some of the scratch bytes into the CP/M work area
+ ; - Save the syscall vector on entry and go via the saved copy as the
+ ; RST could be re-used by CP/M programs
+ ; - Make the code use indirect pointers to the directory buffer etc
+ ; from the scratch space so that we can eventually make it
+ ; multi-threaded for boxes with a large fixed common where we want to
+ ; try and maximise the TPA by having CP/M emulation in the fixed
+ ; common and allocating buffers in that space indexed off the process
+ ;
+ .module cpm
+ .area ASEG(ABS)
+
+ .include "cpm.def"
+
+startaddr:
+
+fcb .EQU 0x005C ; Default CP/M FCB
+buff .EQU 0x0080 ; Default CP/M Buffer
+BS .EQU 0x08 ; ASCII BackSpace
+TAB .EQU 0x09 ; ASCII Horizontal Tab
+LF .EQU 0x0A ; ASCII Line Feed
+CR .EQU 0x0D ; ASCII Carriage Return
+ESC .EQU 0x1B ; ASCII ESCape Char
+
+TCGETS .EQU 1 ; Fuzix - get tty data
+TCSETS .EQU 2 ; Fuzix - set tty data
+TIOCINQ .EQU 5 ; Fuzix - characters pending
+
+STDIN .EQU 0 ; file descriptor value of keyboard
+STDOUT .EQU 1 ; file descriptor value of display
+
+;
+; FIXME: set up phase can be in the runcpm tool
+;
+; Initialize both FCB entries to blank values
+
+EStart: LD HL,#fcbDat ; Move initial FCB
+ PUSH HL
+ LD DE,#fcb ; into
+ LD BC,#16 ; position
+ LDIR
+ POP HL
+ LD C,#16 ; init 2nd entry
+ LDIR
+
+; Catenate argv[] elements into default buffer
+
+ POP IX ; Get Ptr to argv[]
+ INC IX ; Skip "runcpm"
+ INC IX
+;
+; Load the binary of argv[1] at 0x0100 (obliterating runcpm) (passed as fd
+; 2)
+;
+ LD DE,#__bdos - 0x100 ; largest possible binary space
+ PUSH DE
+ LD DE, #0x0100
+ PUSH DE
+ LD DE,#3 ; file handle
+ PUSH DE
+ LD DE,#7 ; read()
+ PUSH DE
+ RST 0x30
+ POP DE
+ POP DE
+ POP DE
+ POP DE
+ LD DE,#3
+ PUSH DE
+ DEC DE ; #2 close
+ PUSH DE
+ RST 0x30
+ POP DE
+ POP DE
+ INC IX ; Skip binary name
+ INC IX
+;
+; Turn the following arguments into the CP/M tail and default FCB
+;
+ LD DE,#buff+1 ; Pt to CP/M Dflt Buffer
+ LD C,#0 ; Cnt to 0
+ INC IX ; Skip Argv[0]
+ INC IX
+Cold0: LD L, 0(IX) ; Get Ptr to Arg element
+ INC IX
+ LD H, 0(IX)
+ INC IX
+ LD A,H
+ OR L ; End?
+ JR Z,Cold2 ; ..exit if Yes
+ LD A,#' ' ; Add space separator for args
+ LD (DE),A
+ INC DE
+ INC C ; bump count
+Cold1: LD A,(HL)
+ OR A ; End of string?
+ JR Z,Cold0 ; ..try next if Yes
+ CP #'a' ; ensure
+ JR C,NoCap ; it
+ CP #'z'+1 ; is
+ JR NC,NoCap ; UCase
+ AND #0x5F
+NoCap: LD (DE),A ; Move a byte
+ INC HL
+ INC C ; bump count
+ INC E ; bump ptr
+ JR NZ,Cold1 ; ..get next byte if no bfr ovfl
+ ;..else 0FF->100H, terminate
+ DEC E ; (back up for Null-termination)
+Cold2: XOR A
+ LD (DE),A ; Null-terminate for safety
+
+ LD HL,#buff ; Pt to count loc'n in buff
+ LD (HL),C ; save total arg count
+ INC HL ; advance to 1st char
+ LD DE,#fcb+1
+ CALL FilNm ; Get Name/Typ in 1st FCB
+ OR A ; (set End flag)
+ LD DE,#fcb+17 ; (prepare)
+ CALL NZ,FilNm ; Get Tame/Typ in 2nd FCB if present
+
+ LD DE,#dir
+ LD B,#128
+ CALL ZeroDE ; Clear Directory Buffer
+
+ LD HL,#0
+ LD (0x0003),HL ; Clear IOBYTE and Default Drive/User
+
+ JP __bios ; Go to Cold Start setup
+
+; Fill FCB Name.Typ fields with any present data
+
+FilNm: LD A,(HL) ; Get char
+ INC HL ; bump
+ OR A ; End of String?
+ RET Z
+ CP #' ' ; "Whitespace"?
+ JR Z,FilNm0 ; ..jump if Yes
+ CP #TAB
+ JR NZ,FilNm1 ; ..jump if No
+FilNm0: DEC C ; Count down total length
+ LD A,C ; (prepare)
+ JR NZ,FilNm ; ..loop if Not End
+ RET ; ..else Exit showing EOL
+
+FilNm1: LD B,#8 ; Set length of Name field
+ PUSH DE ; save Ptr to Name[0]
+ CALL FilFl0 ; Get Name
+ POP DE ; restore Ptr to Name
+ OR A
+ RET Z ; ..return if End-of-Line
+ CP #' '
+ RET Z ; ..return if separator
+ CP #'.'
+ JR Z,FilNm2 ; ..bypass char skip
+
+FilNm3: LD A,(HL)
+ INC HL
+ OR A
+ RET Z ; Exit if End of Line
+ CP #' '
+ RET Z ; or End of Field
+ CP #'.'
+ JR NZ,FilNm3 ; ..loop til End or period
+
+FilNm2: LD A,E
+ ADD A,#8 ; Adjust FCB ptr to type field
+ LD E,A
+ LD B,#3
+ ;..fall thru to get next char..
+; Move bytes from (HL) to (DE) for Count in C, Count in B or Ch in {' ','.',0}
+
+FilFld: LD A,(HL) ; Get Char
+ INC HL ; bump ptr
+ OR A ; End of String?
+ RET Z ; ..return if Yes
+FilFl0: CP #'.' ; Period?
+ RET Z
+ CP #' ' ; Space?
+ RET Z
+ LD (DE),A ; Else Store byte
+ INC DE ; bump dest ptr
+ DEC C ; End of Input String?
+ LD A,C ; (prepare)
+ RET Z ; .return End if Yes
+ DJNZ FilFld ; ..loop til field counter ends
+ OR #0x0FF ; Return flag
+ RET
+
+fcbDat: .db 0
+ .ascii ' '
+ .db 0,0,0,0
+
+
+;==========================================================
+; Resident Portion of Basic Disk Operating System
+;==========================================================
+; bdos()
+; {
+__bdos: JP _bdos0
+; }
+
+;.....
+; BDOS Function Dispatch Table
+
+fcnTbl: .dw Fcn0 ; Warm Boot
+ .dw Fcn1 ; ConIn
+ .dw Fcn2 ; ConOut
+ .dw Fcn3 ; Reader In
+ .dw Fcn4 ; Punch Out
+ .dw Fcn5 ; List Output
+ .dw Fcn6 ; Direct Console IO
+ .dw Fcn7 ; Get IOBYTE
+ .dw Fcn8 ; Set IOBYTE
+ .dw Fcn9 ; WrBuf
+ .dw Fcn10 ; RdBuf
+ .dw Fcn11 ; Get Console Status
+ .dw Fcn12 ; Return Version #
+ .dw Fcn13 ; Reset Disk Drive
+ .dw Fcn14 ; Select Disk
+ .dw Fcn15 ; Open File
+ .dw Fcn16 ; Close File
+ .dw Fcn17 ; Search First Occurance
+ .dw Fcn18 ; Search Next Occurance
+ .dw Fcn19 ; Delete File
+ .dw Fcn20 ; Read File
+ .dw Fcn21 ; Write File
+ .dw Fcn22 ; Create File
+ .dw Fcn23 ; Rename File
+ .dw Fcn24 ; Return Disk Login Vector
+ .dw Fcn25 ; Return Current Disk
+ .dw Fcn26 ; Set DMA
+ .dw Fcn27 ; Get Allocation Map
+ .dw Fcn28 ; Write Protect Disk
+ .dw Fcn29 ; Get R/O Vector Address
+ .dw Fcn30 ; Set File Attributes
+ .dw Fcn31 ; Get Disk Parameter Table Address
+ .dw Fcn32 ; Set/Get User Code
+ .dw Fcn33 ; Read Random
+ .dw Fcn34 ; Write Random
+ .dw Fcn35 ; Compute File Size
+ .dw Fcn36 ; Set Random Record Field in FCB
+; .dw Fcn37 ; Reset Multiple Drives
+; .dw null ; (Fcn38 not implemented)
+; .dw Fcn39 ; Get Fixed Disk Vector
+; .dw Fcn40 ; Write Random
+TBLSZ .EQU .-fcnTbl
+MAXFCN .EQU TBLSZ/2
+
+;------------------------------------------------
+; bdos0()
+; {
+
+_bdos0: LD (_arg),DE
+ LD A,C
+ LD (_call),A
+ CP #MAXFCN ; Legal Function?
+ LD A,#0xFF ; Prepare Error code
+ LD L,A
+ RET NC ; ..return if Illegal
+ LD (_userSP),SP
+ LD SP,#_Bstack
+ PUSH IX
+ PUSH IY
+ LD B,#0 ; Fcn # to Word
+ LD HL,#_bdosX
+ PUSH HL ; (ret Addr to Stack)
+ LD HL,#fcnTbl
+ ADD HL,BC
+ ADD HL,BC ; Pt to Fcn entry in Table
+ LD A,(HL)
+ INC HL
+ LD H,(HL)
+ LD L,A
+ JP (HL) ; "Call" Function #
+
+_bdosX: POP IY
+ POP IX
+ LD SP,(_userSP)
+ LD DE,(_arg) ; Return Orig contents of DE
+ LD A,(_call)
+ LD C,A ; Return Orig contents of C
+ LD A,L
+ LD B,H ; Strict compatibility
+ OR A
+ RET
+; }
+
+;------------------------------------------------
+; case 0: _exit(); /* Warm Boot */
+
+Fcn0: JP WBoot
+
+;------------------------------------------------
+; case 6: if (arg < 0xfe) /* Direct Console I/O */
+; goto conout;
+; else if (arg == 0xfe)
+; return (ConSt);
+; else if (ConSt) /* 0xff */
+; goto conout;
+; else return (0);
+
+Fcn6: LD A,E ; _arg in DE
+ CP #0x0FE ; < 0FE ?
+ JR C,Fcn2 ; ..jump if Write if Yes
+ PUSH AF
+ CALL BConSt ; Else get Console Status
+ POP AF
+ INC A
+ RET NZ ; ..exit with 0ffh if 0feh
+ LD A,H
+ OR L ; Any char ready?
+ RET Z ; ..exit if Nothing available
+ ;..else fall thru to Fcn1..
+;------------------------------------------------
+; case 1: /* Console Input */
+; conin: read (0, &c, 1);
+; if (c == '\n')
+; c = '\r';
+; return (c);
+
+Fcn1: CALL BConIn ; Get Char from Bios
+Fcn1A: LD H,#0
+ CP #0x0A ; \n?
+ LD L,A ; (prepare for return
+ RET NZ ; ..return if Not
+ LD L,#0x0D ; Else return CR
+ RET
+
+;------------------------------------------------
+; case 3: /* Reader (Aux) Input */
+; conin: read (0, &c, 1);
+; if (c == '\n')
+; c = '\r';
+; return (c);
+
+Fcn3: CALL AuxIn ; Get Char from Bios
+ JR Fcn1A ; ..exit via common code
+
+;------------------------------------------------
+; case 2: /* Console Output */
+; conout: if (arg == '\r')
+; return (0);
+; c = arg;
+; write (1, &c, 1);
+; break;
+
+Fcn2: LD C,E ; _arg in DE, need char in C
+ JP BConOu
+
+;------------------------------------------------
+; case 4: /* Punch (Aux) Output */
+; conout: if (arg == '\r')
+; return (0);
+; c = arg;
+; write (1, &c, 1);
+; break;
+
+Fcn4: LD C,E ; _arg in DE, need char in C
+ JP AuxOut
+
+;------------------------------------------------
+; case 5: if (arg == '\r') /* List (Prntr) Output */
+; return (0);
+; c = arg;
+; write (2, &c, 1);
+; break;
+
+Fcn5: LD A,E ; _arg in DE
+ CP #13 ; \r?
+ RET Z
+ JP List ; ..go to Bios
+
+;------------------------------------------------
+; case 9: ptr = (char *)arg; /* Print '$'-term String */
+; while (*ptr != '$')
+; {
+; if (*ptr != '\r')
+; write (1, ptr, 1);
+; ++ptr;
+; }
+; break;
+ ; Enter: DE -> String (arg)
+Fcn9: LD A,(DE) ; Get char
+ INC DE ; pt to Next
+ CP #'$' ; End?
+ RET Z ; ..quit if Yes
+ LD C,A
+ PUSH DE
+ CALL BConOu
+ POP DE
+ JR Fcn9 ; ..loop Til done
+
+;------------------------------------------------
+; case 10: rdbuf (arg);
+; break;
+; rdbuf (arg)
+; char *arg;
+; {
+; int nread;
+
+; nread = read (0, arg+2, *arg & 0xff);
+
+Fcn10:
+ push de
+ ex de,hl ; hl - buffer
+ ld e,(hl) ; e - max chars
+ inc hl
+ inc hl
+ ld d,#0 ; d - char cnt
+get: push hl
+ push de
+ call BConIn
+ pop de
+ pop hl
+ cp #8
+ jr z,del
+ cp #0x7F
+ jr z,del
+ cp #3
+ jp z,0
+ push hl
+ push de
+ push af
+ ld c,a
+ call BConOu
+ pop af
+ pop de
+ pop hl
+ ld (hl),a
+ cp #CR
+ jr z,eol
+ ld a,e
+ cp d
+ jr z,eol1
+ inc hl
+ inc d
+ jr get
+del: ld a,d
+ or a
+ jr z,get
+ push hl
+ push de
+ ld c,#8
+ call BConOu
+ ld c,#' '
+ call BConOu
+ ld c,#8
+ call BConOu
+ pop de
+ pop hl
+ dec hl
+ dec d
+ jr get
+eol: ld (hl),#0
+eol1: ld a,d
+ pop de
+ inc de
+ ld (de),a
+ ld hl,#0
+ ret
+
+
+;------------------------------------------------
+; case 11: return (ConSt); /* Get Console Status */
+
+Fcn11: JP BConSt
+
+;------------------------------------------------
+; case 12: /* Return Version # */
+
+Fcn12: LD HL,#0x0022 ; Say this is CP/M 2.2
+ RET
+
+;------------------------------------------------
+; case 13: /* Reset Disk Drive */
+; SDma(0x80);
+; break;
+Fcn13:
+ LD BC,#0x80
+ JP BSDma
+
+;------------------------------------------------
+; case 7: /* Get IO Byte */
+; case 8: break; /* Set IO Byte */
+; case 14: break; /* Select Disk
+; case 25: break; /* Return Current Disk */
+; case 28: break; /* Write Protect Disk */
+; case 30: break; /* Set File Attribytes */
+; case 32: break; /* Get/Set User Code */
+Fcn7:
+Fcn8:
+Fcn14:
+Fcn25: ; 0 = Drive A
+Fcn28:
+Fcn30:
+Fcn32: ; Return User 0
+; default: break;
+; }
+; return (0);
+
+Exit0: LD HL,#0
+ RET
+
+;------------------------------------------------
+; case 15: return (openfile (arg)); /* Open File */
+; openfile (blk)
+; {
+; desc = open (getname (arg), 2);
+ ; DE -> arg
+Fcn15: CALL Fcn17 ; Does this file exist?
+ LD A,H
+ AND L
+ INC A ; File Not Found (-1)?
+ RET Z ; ..return -1 if File doesn't exist
+
+Open1: CALL CkSrch ; (Close Search File)
+
+; arg.recno = 0;
+
+ CALL ZeroCR
+ LD 13(IY),#0x80 ; use S1 as file open flag
+
+ JR Exit0 ; Return Dir Code for Entry
+
+;.....
+; Common File Open Routine. Used by Read, Write and Search First.
+; Enter: DE = File Mode
+; HL = Ptr to Null-terminated Path String
+; Exit : A = 0 if Error, HL = -1
+; File Descriptor, A <> 0 if Ok
+
+OpenF: PUSH DE ; Mode
+ PUSH HL ; Path
+ LD HL,#1 ; Fuzix Open Fcn #
+ PUSH HL
+ RST 0x30 ; _open (Path, Mode);
+ POP BC ; Clean Stack
+ POP BC
+ POP BC
+ LD A,H
+ AND L
+ INC A ; FF -> 0?
+ RET ; ..return (HL=-1/A=0 if Err, HL=fd/A<>0 of Ok)
+
+;------------------------------------------------
+; case 16: return (closefile (arg)); /* Close File */
+
+; if (close (arg->desc) == -1)
+
+Fcn16: LD IY,(_arg)
+ LD 13(IY),#0 ; clear file open flag
+
+ LD HL,#11 ; Fuzix sync function #
+ PUSH HL
+ RST 0x30 ; Execute!
+ POP BC ; Clean Stack
+
+ JP Exit0 ; Return OK
+
+;....
+; Close file descriptor
+
+CloseV: PUSH DE
+ LD HL,#2 ; Fuzix Close Fcn #
+ PUSH HL
+ RST 0x30 ; Execute!
+ POP BC ; Clean Stack
+ POP BC
+ RET
+
+;------------------------------------------------
+; case 17: /* Search First */
+
+Fcn17: CALL CkSrch ; Ensure Search File closed
+ LD HL,#'.' ; Open current directory
+ LD (RName),HL ; store name in Secondary work string
+ LD DE,#0 ; Open Read-Only
+ LD HL,#RName
+ CALL OpenF ; _open ('.', 0);
+ RET Z ; HL = -1, A = 0 if Can't Open
+
+ LD (srchFD),HL ; Else Ok, Save File Descriptor
+ LD (curFil),HL ; Duplicate for Reading
+ ;..fall thru to read one entry..
+;------------------------------------------------
+; case 18: return (255); /* Search Next */
+
+;
+; FIXME:
+; We really only need 64bytes of scratch space here
+; 32-63 for the FUZIX entry
+; 0-31 for the directory entry in CP/M format, but we must
+; return A = 0. We also ought to magic up the second half of
+; the entry but it's not clear how. The rest of the 128 bytes
+; are undefined after this call. Given we can't really magic
+; up the second 16 bytes we may only need 48
+;
+;
+Fcn18: LD HL,(dmaadr)
+ LD (dmaSav),HL ; Save "real" DMA
+Fcn18A: LD HL,#dir+16
+ LD (dmaadr),HL ; Set DMA for Dir Op'n
+ LD A,#16 ; Getdirent
+ LD DE,#32 ; Len of Dir entries
+ CALL RdWrt0 ; Read an Entry
+ JR C,#Fcn18E ; Error if Carry Set
+ OR A ; Read Ok?
+ JR Z,Fcn18E ; ..Return HL=-1 if EOF
+ CALL ChkDir ; Else Set Dir to CP/M, Check Match
+ OR A
+ JR NZ,Fcn18A ; ..loop if No Match
+
+ LD A,(_call)
+ CP #15 ; Is this a File Open internal Call?
+ LD HL,#0 ; (set Success, Index 0)
+ JR Z,Fcn18X ; ..exit now if Yes
+
+ LD HL,#dir ; Else
+ LD DE,(dmaSav) ; Move Dir Buffer to "real" DMA
+ LD BC,#128
+ LDIR
+ LD L,B ; Use 0 in BC
+ LD H,C ; to show Index 0 (success)
+ JR Fcn18X ; ..exit
+
+Fcn18E: LD HL,#-1
+Fcn18X: LD DE,(dmaSav)
+ LD (dmaadr),DE ; Restore "real" DMA Addr
+ RET
+
+;------------------------------------------------
+; case 19: return (delete (arg)); /* Delete File */
+
+Fcn19: CALL CkSrch ; Ensure Search file closed
+
+; if (unlink (getname (arg)) == -1)
+ ; DE -> arg
+ CALL GetNam ; Parse to String
+ PUSH HL ; String
+ LD HL,#6 ; UZI Unlink Fcn #
+ PUSH HL
+ RST 0x30 ; Execute!
+ POP BC ; Clean Stack
+ POP BC
+
+; return (255);
+; return (0);
+
+ LD A,H
+ AND L
+ INC A ; FF->0?
+ JP NZ,Exit0 ; return Ok if No
+
+ExitM1: LD HL,#-1
+ RET
+
+
+;------------------------------------------------
+; case 33: /* Read File Random */
+; readrandom (fcb)
+; {
+
+Fcn33: CALL RWprep ; Prepare File for access
+ JP Z,Exit1
+
+ LD IY,(_arg)
+ LD A,33(IY) ; Set Record Count from
+ LD 32(IY),A ; Random Record number
+ LD A,34(IY) ;
+ LD 12(IY),A ;
+
+ CALL DoRead
+ JR RWEx
+
+;....
+DoRead:
+ CALL LSeek ; Seek to Offset (128-byte rec in Block)
+
+ CALL BRead ; Read 1 Sector
+
+ PUSH AF
+ LD DE,(curFil)
+;;;; CALL CloseV ; Close the file
+ LD DE,#0
+ LD (curFil),DE
+ POP AF
+
+ RET
+
+
+;------------------------------------------------
+; case 20: return (readfile (arg)); /* Read File */
+; readfile (arg)
+; {
+; nread = read (blk->desc, dmaadr, 128);
+ ; DE -> arg (FCB)
+Fcn20: CALL RWprep ; Prepare file for access
+ JP Z,Exit1
+
+ CALL DoRead ; Read 1 Sector
+
+; arg.recno++;
+
+ PUSH AF
+ CALL IncCR ; Bump Current Record #
+ POP AF
+
+RWEx: JP C,Exit1 ; ..Error if Carry Set
+
+; if (nread == 0)
+; return (0);
+
+ OR A ; Good Read?
+ JP Z,Exit0 ; exit w/0 if Yes
+
+; else return (1)
+
+ JP Exit1
+
+;------------------------------------------------
+; case 34: /* Write File Random */
+; writerandom (fcb)
+; {
+; /* CAUTION the seek calls MUST be in this order */
+; _seek (f, (int)(fcb+33) % 128, 0); /* byte seek */
+; _seek (f, (int)(fcb+33) / 128, 3); /* block seek */
+
+Fcn34: CALL RWprep ; Prepare file for access
+ JP Z,Exit1
+
+ LD IY,(_arg)
+ LD A,33(IY) ; Set Record Count from
+ LD 32(IY),A ; Random Record number
+ LD A,34(IY) ;
+ LD 12(IY),A ;
+
+ CALL DoWrite
+ JR RWEx
+
+;....
+DoWrite:
+ CALL LSeek ; Seek to Offset (128-byte rec in Block)
+
+ CALL BWrit ; Write 1 Sector
+
+ PUSH AF
+ LD DE,(curFil)
+;;;; CALL CloseV ; Close the file
+ LD DE,#0
+ LD (curFil),DE
+ POP AF
+
+ RET
+
+;------------------------------------------------
+; case 21: return (writefile (arg)); /* Write File */
+; writefile (arg)
+; {
+; if (write (blk->desc, dmaadr, 128) != 128)
+
+ ; DE -> arg (FCB)
+Fcn21: CALL RWprep ; Prepare file for access
+ JP Z,Exit1
+
+Fcn21A: CALL DoWrite ; Write
+
+; arg.recno++;
+
+ PUSH AF
+ CALL IncCR ; Bump Current Record #
+ POP AF
+
+; return (255);
+; return (0);
+
+ JR RWEx ; ..exit via Common R/W Code
+; }
+
+;------------------------------------------------
+; case 22: return (makefile (arg)); /* Create File */
+; makefile (arg)
+; {
+; desc = creat (getname (blk), 0666);
+
+Fcn22: CALL CkSrch ; Ensure Search file closed
+ LD HL,#0q0666 ; Own/Grp/Oth are Read/Execute
+ PUSH HL ; DE -> arg
+ LD HL,#0x502 ; O_CREAT|O_RDWR|O_TRUNC
+ CALL GetNam ; This name string
+ PUSH HL
+ LD HL,#1 ; Fuzix open Fcn #
+ PUSH HL
+ RST 0x30 ; Execute!
+ POP BC ; Clean Stack
+ POP BC
+ POP BC
+ POP BC
+
+; if (desc == -1)
+
+ LD A,H
+ AND L
+ INC A ; FF -> 0?
+
+; return (255);
+
+ RET Z ; ..return -1 if Yes
+
+; arg.recno = 0;
+
+ EX DE,HL
+ CALL CloseV
+ JP Open1
+
+;------------------------------------------------
+; case 23: return (rename (arg)); /* Rename File */
+; rename (arg)
+; {
+; RName = getname (arg);
+;
+; FIXME: should use rename() syscall now
+;
+Fcn23: CALL CkSrch ; Ensure Search file closed
+ PUSH DE ; Save FCB Ptr
+ CALL GetNam ; parse to UZI String
+
+ LD HL,#FName
+ LD DE,#RName
+ LD BC,#12
+ LDIR ; Copy to Rename string
+
+; FName = getname (arg+16);
+
+ POP DE ; DE -> _arg
+ LD HL,#16
+ ADD HL,DE ; Offset to New Name
+ EX DE,HL
+ CALL GetNam ; parse it returning HL -> FName
+
+; if (link (RName, FName) < 0) {
+
+ PUSH HL ; New Name
+ LD HL,#RName ; Old Name
+ PUSH HL
+ LD HL,#5 ; UZI link Fcn #
+ PUSH HL
+ RST 0x30 ; Execute!
+ POP BC ; Clean Stack
+ POP BC
+ POP BC
+
+; return (-1);
+
+ JP C,ExitM1 ; Exit w/Err if Bad
+; }
+; if (unlink (RName) < 0) {
+
+ LD HL,#RName ; Old Name
+ PUSH HL
+ LD HL,#6 ; UZI unlink Fcn #
+ PUSH HL
+ RST 0x30 ; Execute!
+ POP BC ; Clean Stack
+ POP BC
+ JP NC,Exit0 ; exit w/0 if Ok
+
+; unlink (FName);
+ ; Else remove the new iNode
+ LD HL,#FName ; New Name
+ PUSH HL
+ LD HL,#6 ; UZI unlink Fcn #
+ PUSH HL
+ RST 0x30 ; Execute!
+ POP BC ; Clean Stack
+ POP BC
+
+; return (-1);
+
+ JP C,ExitM1 ; return -1 if Bad
+; }
+; return (0);
+
+ JP Exit0 ; else return Ok
+; }
+
+;------------------------------------------------
+; case 24: return (1); /* Return Disk Login Vector */
+
+Fcn24:
+Exit1: LD HL,#1
+ RET
+
+;------------------------------------------------
+; case 26: dmaadr = (char *)arg; /* Set DMA Address */
+; break;
+ ; Enter DE = DMA Address
+Fcn26: LD C,E
+ LD B,D ; Move to Bios Regs
+ JP BSDma ; Set in Bios & return
+
+;------------------------------------------------
+; case 27: return (-1) /* Get Allocation Map */
+; case 29: return (-1) /* Get R/O Vector Address */
+Fcn27:
+Fcn29: LD HL,#-1
+ RET
+
+;------------------------------------------------
+; case 31: return (&dpb); /* Get Disk Param Table Addr */
+
+Fcn31: LD HL,#dpb
+ RET
+; }
+
+;------------------------------------------------
+; case 35: /* Return File Size in FCB */
+; /* Use stat fcn, rounding up to mod-128 */
+; if (_stat (dname, &statbuf) == 0) {
+ ; DE -> fcb
+Fcn35: CALL CkSrch ; Ensure Search file closed
+ CALL GetNam ; parse to UZI String
+ LD DE,#stBuf
+ PUSH DE ; &statbuf
+ PUSH HL ; dname
+ LD HL,#15 ; UZI stat Fcn #
+ PUSH HL
+ RST 0x30 ; Execute!
+ POP BC ; Clean Stk
+ POP BC
+ POP BC
+ LD IY,(_arg)
+ LD A,H
+ OR L ; 0?
+ JR NZ,Fcn35X ; ..jump if Bad
+
+; (int)fcb+33 = ((512 * statbuf.st_size.o_blkno
+; + statbuf.st_size.o_offset)
+; + 127)
+; >> 7;
+; FIXME: offset is now a simple number so this is wrong
+;
+ LD HL,(stBuf+14) ; Get Offset
+ LD DE,#127
+ ADD HL,DE ; round up to next 128-byte block
+ ADD HL,HL ; Shift so H has 128-byte blk #
+ LD E,H ; position in DE
+ LD HL,(stBuf+16) ; Get Block
+ ADD HL,HL ; * 2
+ ADD HL,HL ; * 4 for 128-byte Block Count
+ ADD HL,DE ; Now have CP/M Record Size
+ LD 33(IY),L
+ LD 34(IY),H ; Store in RR fields in FCB
+ LD 35(IY),D ; (D = 0)
+
+; return (0);
+
+ LD L,D
+ LD H,D ; HL = 0
+ RET
+
+; else {
+; (int)fcb+33 = 0;
+
+Fcn35X: LD 33(IY),#0
+ LD 34(IY),#0
+ LD 35(IY),#0
+
+; return (-1);
+
+ LD HL,#-1
+; }
+ RET
+
+;------------------------------------------------
+; case 36: /* Set Random Record Field in FCB */
+
+Fcn36: LD IY,(_arg)
+ LD A,32(IY) ; Fetch RecNo
+ LD 33(IY),A ; place in LSB of RR field (r0)
+ LD A,12(IY)
+ LD 34(IY),A ; set (r1)
+ LD 35(IY),#0 ; Clear Hi byte of RR (r2)
+
+ LD HL,#0 ; Return Ok
+ RET
+
+;===========================================================
+; BDos Support Routines
+;===========================================================
+; char *
+; getname (struct fcb *blk)
+; {
+; int j;
+; static char name[16];
+; char *p;
+
+; p = name;
+ ; Enter: DE -> FCB drive byte
+GetNam: LD IX,#FName ; Dest to string
+ EX DE,HL
+ PUSH HL ; (save)
+ INC HL ; adv to 1st char of FN
+
+; for (j = 0; j < 8; ++j)
+; {
+; if (!blk->name[j] || blk->name[j] == ' ')
+; break;
+
+ LD B,#8
+GetN0: LD A,(HL)
+ INC HL
+ OR A
+ JR Z,GetN1
+ CP #' '
+ JR Z,GetN1
+
+; *p++ = chlower (blk->name[j]);
+
+ CALL ChLower
+ LD 0(IX),A
+ INC IX
+ DJNZ GetN0
+; }
+
+GetN1: POP HL
+ LD DE,#9
+ ADD HL,DE ; Pt to 1st char of FT
+ LD A,(HL)
+ CP #' ' ; Any Type?
+ JR Z,GetNX ; ..quit if Not
+
+; *p++ = '.';
+
+ LD 0(IX),#'.'
+ INC IX
+
+; for (j = 0; j < 3; ++j)
+
+ LD B,#3
+
+; {
+; if (!blk->ext[j] || blk->ext[j] == ' ')
+; break;
+
+GetN2: LD A,(HL)
+ INC HL
+ CP #' '
+ JR Z,GetNX
+
+; *p++ = chlower (blk->ext[j]);
+
+ CALL ChLower
+ LD 0(IX),A
+ INC IX
+ DJNZ GetN2
+
+; }
+; *p = '\0';
+
+GetNX: LD 0(IX),#0
+
+; return (name);
+
+ LD HL,#FName
+ RET
+; }
+
+;
+; FIXME: we need to use lseek for Fuzix
+;
+LSeek: LD BC,#0 ; Push 0 for absolute
+ PUSH BC
+ LD IY, (_arg) ; FCB
+ LD C, 32(IY) ; Pull the offset out of the FCB
+ LD B, 12(IY) ; This is in records (128 bytes)
+ ; And may overflow 2^16 bytes
+ XOR A
+ SLA C ; C x 2, into carry
+ RL B ; B x 2 picking up carry from C
+ RLA ; A x 2 picking up carry from B - now in 64's
+ SLA C
+ RL B
+ RLA ; now in 32's
+ SLA C
+ RL B
+ RLA ; now in 16's
+ SLA C
+ RL B
+ RLA ; now in 8's
+ SLA C
+ RL B
+ RLA ; now in 4's
+ SLA C
+ RL B
+ RLA ; now in 2's
+ SLA C
+ RL B
+ RLA ; now in bytes
+ LD (LSeekData), BC ; low bits
+ LD (LSeekData + 2), A ; high bit (top byte already clear)
+ LD BC, #LSeekData ; push pointer
+ PUSH BC
+ LD HL, (curFil)
+ PUSH HL
+ LD HL,#9 ; _lseek()
+ PUSH HL
+ RST 0x30 ; Syscall
+ POP BC ; Recover stack
+ POP BC
+ POP BC
+ POP BC
+ RET
+
+;.....
+; Perform File Access Preparatory actions:
+; Open file for R/W and Seek to current Record #
+; Enter: DE = Ptr to FCB
+; Exit : A = 0 and HL = -1 if Error, A <> 0 if Ok
+
+RWprep: CALL CkSrch ; Ensure Search file closed
+ LD HL,#13 ; offset to S1 (file open flag)
+ ADD HL,DE
+ LD A,(HL)
+ AND #0x80
+ LD HL,#-1
+ RET Z
+
+ CALL GetNam ; Parse FCB Fn.Ft to String
+
+ CALL COpen
+ RET Z ; ..return -1 on error
+
+ LD (curFil),HL ; store file descriptor for Bios
+ RET
+
+COpen: PUSH HL
+ LD DE,#CName
+chk: LD A,(DE)
+ CP (HL) ; compare filename with cached name
+ JR NZ,differ
+ OR A
+ JR Z,same
+ INC HL
+ INC DE
+ JR chk
+same: POP DE ; if same, just return the cached file descr
+ LD HL,(Cfd)
+ LD A,H
+ AND L
+ INC A
+ RET NZ
+ EX DE,HL
+ JR op1
+differ: LD HL,(Cfd)
+ LD A,H
+ AND L
+ INC A
+ EX DE,HL
+ CALL NZ,CloseV ; close old file
+ POP HL ; restore file name
+ CALL Ccopy
+op1: LD DE,#2 ; open for R/W
+ CALL OpenF
+ LD (Cfd),HL
+ RET
+
+Ccopy: PUSH HL
+ LD DE,#CName
+cpy: LD A,(HL)
+ LD (DE),A
+ INC HL
+ INC DE
+ OR A
+ JR NZ,cpy
+ POP HL
+ RET
+
+;.....
+; Convert UZI Directory Entry at dir+16 to CP/M FCB entry at dir, Zero rest.
+; Ambiguously compare FCB FN.FT at dir to that passed at arg, returning Zero
+; if Match, Non-Zero if mismatch.
+
+ChkDir: LD DE,#dir
+ LD HL,#dir+16+2 ; Pt to 1st char of Name
+ XOR A
+ LD (DE),A ; Zero Drive field
+ INC DE ; Pt to 1st char of FN
+ LD B,#8
+ CALL ChkD0 ; Fix Name
+ LD B,#3
+ CALL ChkD0 ; & Type
+ LD B,#21
+ CALL ZeroDE ; Clear rest of Dir entry
+
+ LD DE,(_arg)
+ INC DE ; Pt to 1st char of FN
+ LD A,(DE)
+ CP #' ' ; Any Name present?
+ JR NZ,ChkFN0 ; ..jump if Yes
+ LD HL,#8
+ ADD HL,DE ; Else offset to 1st char of FT
+ LD A,(HL)
+ CP #' ' ; Type present?
+ LD A,#0x0FF ; (Assume Error)
+ RET Z ; Return w/Err Flag if no Type either
+
+ChkFN0: LD HL,#dir+1 ; Else Compare name/type fields
+ LD B,#11
+ ; Ambiguous FN.FT compare of (HL) to (DE)
+ChkL: LD A,(DE)
+ CP #'?' ; Accept anything?
+ JR Z,ChkL0 ; ..jump if ambiguous
+ XOR (HL)
+ AND #0x7F ; Match?
+ RET NZ ; .Return Non-Zero if Not
+ChkL0: INC HL
+ INC DE
+ DJNZ ChkL ; ..loop til Done
+ XOR A ; return Zero for Match
+ RET
+
+;.....
+; Parse FileSpec addressed by HL into FN.FT Spec addressed by DE.
+
+ChkD0: LD A,(HL) ; Get Char
+ CP #'a'
+ JR C,ChkD1
+ CP #'z'+1
+ JR NC,ChkD1
+ AND #0x5F ; Convert to Uppercase
+ChkD1: OR A ; End of String?
+ JR Z,ChkDE ; ..jump if End
+ INC HL ; (bump Inp Ptr if Not End)
+ CP #'.'
+ JR Z,ChkDE ; ..or Period field separator
+ LD (DE),A ; Store char
+ INC DE ; bump Dest
+ DJNZ ChkD0 ; ..loop til field done
+ChkD2: LD A,(HL) ; Get Next
+ OR A
+ RET Z ; Exit at End of string
+ INC HL ; (adv to next)
+ CP #'.'
+ RET Z ; or field separator
+ JR ChkD2 ; ..loop til end found
+
+ChkDE: LD A,#' ' ; Fill rest w/Spaces
+ChkD3: INC B
+ DEC B ; More in field?
+ RET Z ; ..exit if Not
+ JR ZeroL ; ..else stuff spaces til field ends
+
+;.....
+; Zero area addressed by DE for B Bytes. Uses A,B,DE.
+
+ZeroDE: XOR A
+ZeroL: LD (DE),A
+ INC DE
+ DJNZ ZeroL
+ RET
+
+;.....
+; Close the Directory if we just exitted a SearchF/SearchN sequence
+
+CkSrch: PUSH DE ; Save Regs
+ PUSH HL
+ LD DE,(srchFD) ; Get File Desc
+ LD A,D
+ OR E ; Anything open?
+ CALL NZ,CloseV ; Close file if Yes
+ LD HL,#0
+ LD (srchFD),HL ; Mark as closed
+ POP HL ; (ignore Errors)
+ POP DE
+ RET
+
+;.....
+; Bump current Record # for sequential R/W operations
+
+IncCR: LD IY,(_arg)
+ INC 32(IY) ; Bump Lo byte
+ RET NZ
+ INC 12(IY) ; Bump Hi byte
+ RET
+
+;.....
+; Init Current Record #
+
+ZeroCR: LD IY,(_arg)
+ LD 32(IY),#0 ; Clear Lo Byte
+ LD 12(IY),#0 ; Clear Hi Byte
+ RET
+
+;.....
+; Convert char in A to Lowercase Ascii
+
+ChLower:
+ CP #'A'
+ RET C
+ CP #'Z'+1
+ RET NC
+ OR #0x20 ; Convert to Lcase
+ RET
+
+;= = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+; Bdos data in Text Segment for treating as single module
+
+; struct fcb {
+; char drive;
+; char name[8];
+; char ext[3];
+; char junk1[4];
+; char desc; /* This byte & 1st byte of Name used for file desc */
+; char name2[8];
+; char ext2[3];
+; char junk2[4];
+; char junk3;
+; };
+
+_arg: .dw 00 ; Argument passed to Bdos (char *arg;)
+_call: .db 0 ; Bdos Function # (char call;)
+FName: .ascii ' ' ; Storage for FCB "name" String
+RName: .ascii ' ' ; 2nd Storage for FCB "name" String (rename)
+
+CName: .ascii ' ' ; cached filename
+ .db 0
+Cfd: .dw -1 ; cached file descriptor
+
+curFil: .dw 00 ; Storage for File Descriptor of FCB
+ ; (set by Bdos, Used by Bios)
+stBuf: .ds 30 ; Buffer for stat() results
+
+DOSSIZ .EQU .-EStart
+RESV .EQU 0x100-(DOSSIZ&0x0FF)
+ .ds RESV ; Pad to make Bios start on even mult of 256
+
+;============================================================
+; The Bios Jump table MUST start on an even MOD 256 boundary
+;============================================================
+
+__bios: JP __cold ; 0 Cold Boot
+WBoot: JP Exit ; 1 Warm Boot
+BConSt: JP ConSt ; 2 Console Status
+BConIn: JP ConIn ; 3 Console Input
+BConOu: JP ConOut ; 4 Console Output
+ JP List ; 5 Printer Output
+ JP AuxOut ; 6 Auxiliary Output (Punch)
+ JP AuxIn ; 7 Auxiliary Input (Reader)
+ JP Home ; 8 Home drive head
+ JP SelDsk ; 9 Select Drive
+ JP SetTrk ; 10 Set Track
+ JP SetSec ; 11 Set Sector
+BSDma: JP SetDMA ; 12 Set DMA Address
+BRead: JP Read ; 13 Read Sector
+BWrit: JP Write ; 14 Write Sector
+ JP ListSt ; 15 Printer Status
+ JP SecTrn ; 16 Translate Sector
+
+;------------------------------------------------
+; Cold Entry. Set up CP/M vectors and Stack, Get
+; Current TTY Parms, Save for Exit, and begin
+
+__cold: LD A,#0x0C3
+ LD HL,#__bdos
+ LD SP,HL ; Set CP/M Stack for execution
+ LD (0x0005),A ; Set Bdos Vector
+ LD (0x0006),HL
+ LD HL,#WBoot
+ LD (0x0000),A ; Set Bios Warm Boot Vector
+ LD (0x0001),HL
+
+ LD HL,#ttTermios0 ; & buf
+ LD DE,#TCGETS ; ioctl fcn to Get Parms
+ CALL IoCtl ; Execute ioctl fcn on STDIN
+ LD HL,#ttTermios0
+ LD DE,#ttTermios
+ LD BC, #20
+ LDIR ; Move to Work Area
+ ; Now we need to Change Modes defined in DEVTTY as:
+ ; RAW = 20H (0000040)
+ ; CRMOD = 10H (0000020)
+ ; ECHO = 08H (0000010)
+ ; CBREAK = 02H (0000002)
+ ; COOKED = 00H (0000000)
+ LD HL, #0
+ ; Turn all the input and output magic off
+ LD (ttTermios), HL
+ LD (ttTermios + 2), HL
+ XOR A
+ LD (ttTermios+6), A ; Echo etch off
+ LD A, (ttTermios+7)
+ AND #0xF0 ; canonical processing off
+ LD (ttTermios+7), A
+ LD HL, #1 ; VTIME 0
+ LD (ttTermios+8), HL ; VMIN 1
+ LD HL, #ttTermios
+ LD DE, #TCSETS
+ CALL IoCtl ; Set terminal bits
+
+ CALL 0x0100 ; ..Execute!
+
+;.....
+; 1 - Warm Boot Vector (Exits back to UZI) {exit (0);}
+; TTY Port Settings are restored to original state.
+
+Exit: LD C,#0x0D
+ CALL ConOut
+ LD C,#0x0A
+ CALL ConOut
+ LD HL,#ttTermios0 ; & buf
+ LD DE,#TCSETS ; ioctl fcn to Set Parms
+ CALL IoCtl ; Execute ioctl Fcn on STDIN
+
+ LD HL,#0 ; Exit Good Status
+ PUSH HL
+ PUSH HL ; UZI Fcn 0 (_exit)
+ RST 0x30 ; Execute!
+Spin: JR Spin ; Can't happen!
+
+;.....
+; 2 - Return Console Input Status
+
+ConSt: LD HL,#cnt ; &buf
+ LD DE,#TIOCINQ ; ioctl fcn to read queue count
+ CALL IoCtl ; Execute ioctl on STDIN
+ LD HL,(cnt)
+ LD A,H
+ OR L ; Anything There?
+ RET Z ; ..return Zero if Not
+ OR #0x0FF ; Else signify char waiting
+ RET
+
+;.....
+; 3 - Read Console Input Char {read (stdin, &char, 1);}
+
+ConIn: call ConSt
+ jr z,ConIn
+ LD HL,#1 ; 1 char
+ PUSH HL
+ LD DE,#char ; Addr to put char
+ PUSH DE
+ LD L,#STDIN ; fd
+ PUSH HL
+ LD L,#7 ; UZI Read Fcn
+ChrV0: PUSH HL
+ RST 0x30 ; Execute
+ POP BC
+ POP BC
+ POP BC
+ POP BC
+ LD A,(char)
+ RET
+
+;.....
+; 4 - Write Char in C to Console {write (stdout, &char, 1);}
+
+ConOut: LD A,C
+ LD DE,#char
+ LD (DE),A ; Stash char
+ LD HL,#1 ; 1 char
+ PUSH HL
+ PUSH DE ; Addr to get char
+ LD L,#STDOUT ; fd
+ PUSH HL
+ LD L,#8 ; UZI Write Fcn
+ JR ChrV0 ; ..go to common code
+
+;.....
+
+List: ; Bios Fcn 5
+AuxOut: ; Bios Fcn 6
+AuxIn: ; Bios Fcn 7
+Home: ; Bios Fcn 8
+SetTrk: ; Bios Fcn 10
+SetSec: ; Bios Fcn 11
+ListSt: ; Bios Fcn 15
+SecTrn: XOR A ; Bios Fcn 16. These are No-Ops
+ RET
+
+;.....
+; 9 - Select Disk. Simply return the DPH pointer
+
+SelDsk: LD HL,#dph ; Return DPH Pointer
+ RET
+
+;.....
+; 12 - Set DMA Transfer Address
+
+SetDMA: LD (dmaadr),BC ; Save Address
+ Ret
+
+;.....
+; 13 - Read a "Sector" to DMA Address {read (curFil, dmaadr, 128);}
+
+Read: LD A,#7 ; Set UZI Read Fcn
+ CALL RdWrt ; Do the work
+ RET C ; ..exit if Error
+ OR A ; 0 bytes Read?
+ JR Z,XErr1 ; ..Return Error if Yes (EOF)
+ SUB #128 ; A full 128 bytes Read?
+ RET Z ; return Ok if Yes
+ LD DE,(dmaadr)
+ ADD HL,DE ; Else offset to byte after end
+Feof: LD (HL),#0x1A ; stuff EOF in case of text
+ INC HL
+ INC A
+ JR NZ,Feof
+ RET ; exit with OK status
+
+;.....
+; 14 - Write a "Sector" from DMA Address {write (curFil, dmaadr, 128);}
+
+Write: LD A,#8 ; Set UZI Write Fcn
+ CALL RdWrt ; Do the work
+ RET C ; ..exit if Error
+ SUB #128 ; Good Write?
+ RET Z ; return Ok if Yes
+XErr1: SCF
+ JR XErr ; Else Return Error
+
+; Common Read/Write Support Routine
+
+RdWrt: LD DE,#128 ; 1 "Sector" char
+ ; Entry Point accessed by Search Next (BDos)
+RdWrt0: PUSH DE
+ LD HL,(dmaadr) ; from here
+ PUSH HL
+ LD HL,(curFil) ; to this file
+ PUSH HL
+ LD E,A ; Position R/W Fcn #
+ PUSH DE
+ RST 0x30 ; Execute!
+ POP BC ; Clear Stack
+ POP BC
+ POP BC
+ POP BC
+ LD A,L ; Shuffle possible byte quantity
+ RET NC ; ..return if No Error
+XErr: LD A,#0x01 ; Else Signal Error (keeping Carry)
+ RET
+
+;==========================================================
+; Bios Support Utilities
+;==========================================================
+; Execute ioctl Function on STDIN
+; Enter: HL = Addr of Parm Block
+; DE = ioctl Function to execute
+; Exit : None
+; Uses : AF,BC,DE,HL
+
+IoCtl: PUSH HL ; &buf
+ PUSH DE ; ioctl fcn
+ LD E,#STDIN ; fd
+ PUSH DE
+ LD E,#29 ; Fuzix ioctl Fcn #
+ PUSH DE
+ RST 0x30 ; Execute!
+ POP BC ; Clean Stack
+ POP BC
+ POP BC
+ POP BC
+ RET
+
+;- - - - - - - - - - Data Structures - - - - - - - - -
+
+dph: .dw 0 ; Ptr to Skew Table
+ .dw 0,0,0 ; Scratch Words for BDos use
+ .dw dir ; Ptr to Directory Buffer
+ .dw dpb ; Ptr to DPB
+ .dw 0 ; Ptr to Disk Checksum Buffer
+ .dw 0 ; Ptr to ALV Buffer
+
+
+dpb: .dw 64 ; Dummy Disk Parameter Block
+ .db 4
+ .db 15
+ .dw 0x0FFFF
+ .dw 1023
+ .db 0x0FF,0
+ .db 0,0,0,0
+
+;----------------------- Data -----------------------
+
+dmaadr: .dw 0x0080 ; Read/Write Transfer Addr (char *dmaadr;)
+dmaSav: .dw 0 ; Temp storage of current DMA Address
+srchFD: .dw 0 ; File Descriptor for Searches
+char: .db ' ' ; Byte storage for Conin/Conout
+cnt: .dw 0 ; Count of waiting keys
+LSeekData: .dw 0 ; Used for _lseek() syscalls
+ .dw 0
+ttTermios:
+ .ds 20 ; Working TTY Port Settings
+ttTermios0:
+ .ds 20 ; Initial TTY Port Settings
+
+dir: .ds 128 ; Directory Buffer
+
+ .ds 128
+_Bstack:
+_userSP:.dw 0 ; WRS: important that we write data all the way to the very last byte used
+
+BIOSIZ .EQU .-__bios
+CPMSIZ .EQU .-__bdos
+
+; .end
+