;************************************************************** ;* ;* C P / M version 2 . 2 ;* ;* Reconstructed from memory image on February 27, 1981 ;* ;* by Clark A. Calkins ;* ;************************************************************** ; ; Set memory limit here. This is the amount of contigeous ; ram starting from 0000. CP/M will reside at the end of this space. ; MEM EQU 62 ;for a 62k system (TS802 TEST - WORKS OK). ; IOBYTE EQU 3 ;i/o definition byte. TDRIVE EQU 4 ;current drive name and user number. ENTRY EQU 5 ;entry point for the cp/m bdos. TFCB EQU 5CH ;default file control block. TBUFF EQU 80H ;i/o buffer and command line storage. TBASE EQU 100H ;transiant program storage area. ; ; Set control character equates. ; CNTRLC EQU 3 ;control-c CNTRLE EQU 05H ;control-e BS EQU 08H ;backspace TAB EQU 09H ;tab LF EQU 0AH ;line feed FF EQU 0CH ;form feed CR EQU 0DH ;carriage return CNTRLP EQU 10H ;control-p CNTRLR EQU 12H ;control-r CNTRLS EQU 13H ;control-s CNTRLU EQU 15H ;control-u CNTRLX EQU 18H ;control-x CNTRLZ EQU 1AH ;control-z (end-of-file mark) DEL EQU 7FH ;rubout ; ; Set origin for CP/M ; ORG (MEM-7)*1024 ; CBASE JMP COMMAND ;execute command processor (ccp). JMP CLEARBUF ;entry to empty input buffer before starting ccp. ; ; Standard cp/m ccp input buffer. Format is (max length), ; (actual length), (char #1), (char #2), (char #3), etc. ; INBUFF DB 127 ;length of input buffer. DB 0 ;current length of contents. DB 'Copyright' DB ' 1979 (c) by Digital Research ' DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 INPOINT DW INBUFF+2;input line pointer NAMEPNT DW 0 ;input line pointer used for error message. Points to ; ;start of name in error. ; ; Routine to print (A) on the console. All registers used. ; PRINT MOV E,A ;setup bdos call. MVI C,2 JMP ENTRY ; ; Routine to print (A) on the console and to save (BC). ; PRINTB PUSH B CALL PRINT POP B RET ; ; Routine to send a carriage return, line feed combination ; to the console. ; CRLF MVI A,CR CALL PRINTB MVI A,LF JMP PRINTB ; ; Routine to send one space to the console and save (BC). ; SPACE MVI A,' ' JMP PRINTB ; ; Routine to print character string pointed to be (BC) on the ; console. It must terminate with a null byte. ; PLINE PUSH B CALL CRLF POP H PLINE2 MOV A,M ORA A RZ INX H PUSH H CALL PRINT POP H JMP PLINE2 ; ; Routine to reset the disk system. ; RESDSK MVI C,13 JMP ENTRY ; ; Routine to select disk (A). ; DSKSEL MOV E,A MVI C,14 JMP ENTRY ; ; Routine to call bdos and save the return code. The zero ; flag is set on a return of 0ffh. ; ENTRY1 CALL ENTRY STA RTNCODE ;save return code. INR A ;set zero if 0ffh returned. RET ; ; Routine to open a file. (DE) must point to the FCB. ; OPEN MVI C,15 JMP ENTRY1 ; ; Routine to open file at (FCB). ; OPENFCB XRA A ;clear the record number byte at fcb+32 STA FCB+32 LXI D,FCB JMP OPEN ; ; Routine to close a file. (DE) points to FCB. ; CLOSE MVI C,16 JMP ENTRY1 ; ; Routine to search for the first file with ambigueous name ; (DE). ; SRCHFST MVI C,17 JMP ENTRY1 ; ; Search for the next ambigeous file name. ; SRCHNXT MVI C,18 JMP ENTRY1 ; ; Search for file at (FCB). ; SRCHFCB LXI D,FCB JMP SRCHFST ; ; Routine to delete a file pointed to by (DE). ; DELETE MVI C,19 JMP ENTRY ; ; Routine to call the bdos and set the zero flag if a zero ; status is returned. ; ENTRY2 CALL ENTRY ORA A ;set zero flag if appropriate. RET ; ; Routine to read the next record from a sequential file. ; (DE) points to the FCB. ; RDREC MVI C,20 JMP ENTRY2 ; ; Routine to read file at (FCB). ; READFCB LXI D,FCB JMP RDREC ; ; Routine to write the next record of a sequential file. ; (DE) points to the FCB. ; WRTREC MVI C,21 JMP ENTRY2 ; ; Routine to create the file pointed to by (DE). ; CREATE MVI C,22 JMP ENTRY1 ; ; Routine to rename the file pointed to by (DE). Note that ; the new name starts at (DE+16). ; RENAM MVI C,23 JMP ENTRY ; ; Get the current user code. ; GETUSR MVI E,0FFH ; ; Routne to get or set the current user code. ; If (E) is FF then this is a GET, else it is a SET. ; GETSETUC:MVI C,32 JMP ENTRY ; ; Routine to set the current drive byte at (TDRIVE). ; SETCDRV CALL GETUSR ;get user number ADD A ;and shift into the upper 4 bits. ADD A ADD A ADD A LXI H,CDRIVE;now add in the current drive number. ORA M STA TDRIVE ;and save. RET ; ; Move currently active drive down to (TDRIVE). ; MOVECD LDA CDRIVE STA TDRIVE RET ; ; Routine to convert (A) into upper case ascii. Only letters ; are affected. ; UPPER CPI 'a' ;check for letters in the range of 'a' to 'z'. RC CPI '{' RNC ANI 5FH ;convert it if found. RET ; ; Routine to get a line of input. We must check to see if the ; user is in (BATCH) mode. If so, then read the input from file ; ($$$.SUB). At the end, reset to console input. ; GETINP LDA BATCH ;if =0, then use console input. ORA A JZ GETINP1 ; ; Use the submit file ($$$.sub) which is prepared by a ; SUBMIT run. It must be on drive (A) and it will be deleted ; if and error occures (like eof). ; LDA CDRIVE ;select drive 0 if need be. ORA A MVI A,0 ;always use drive A for submit. CNZ DSKSEL ;select it if required. LXI D,BATCHFCB CALL OPEN ;look for it. JZ GETINP1 ;if not there, use normal input. LDA BATCHFCB+15;get last record number+1. DCR A STA BATCHFCB+32 LXI D,BATCHFCB CALL RDREC ;read last record. JNZ GETINP1 ;quit on end of file. ; ; Move this record into input buffer. ; LXI D,INBUFF+1 LXI H,TBUFF ;data was read into buffer here. MVI B,128 ;all 128 characters may be used. CALL HL2DE ;(HL) to (DE), (B) bytes. LXI H,BATCHFCB+14 MVI M,0 ;zero out the 's2' byte. INX H ;and decrement the record count. DCR M LXI D,BATCHFCB;close the batch file now. CALL CLOSE JZ GETINP1 ;quit on an error. LDA CDRIVE ;re-select previous drive if need be. ORA A CNZ DSKSEL ;don't do needless selects. ; ; Print line just read on console. ; LXI H,INBUFF+2 CALL PLINE2 CALL CHKCON ;check console, quit on a key. JZ GETINP2 ;jump if no key is pressed. ; ; Terminate the submit job on any keyboard input. Delete this ; file such that it is not re-started and jump to normal keyboard ; input section. ; CALL DELBATCH;delete the batch file. JMP CMMND1 ;and restart command input. ; ; Get here for normal keyboard input. Delete the submit file ; incase there was one. ; GETINP1 CALL DELBATCH;delete file ($$$.sub). CALL SETCDRV ;reset active disk. MVI C,10 ;get line from console device. LXI D,INBUFF CALL ENTRY CALL MOVECD ;reset current drive (again). ; ; Convert input line to upper case. ; GETINP2 LXI H,INBUFF+1 MOV B,M ;(B)=character counter. GETINP3 INX H MOV A,B ;end of the line? ORA A JZ GETINP4 MOV A,M ;convert to upper case. CALL UPPER MOV M,A DCR B ;adjust character count. JMP GETINP3 GETINP4 MOV M,A ;add trailing null. LXI H,INBUFF+2 SHLD INPOINT ;reset input line pointer. RET ; ; Routine to check the console for a key pressed. The zero ; flag is set is none, else the character is returned in (A). ; CHKCON MVI C,11 ;check console. CALL ENTRY ORA A RZ ;return if nothing. MVI C,1 ;else get character. CALL ENTRY ORA A ;clear zero flag and return. RET ; ; Routine to get the currently active drive number. ; GETDSK MVI C,25 JMP ENTRY ; ; Set the stabdard dma address. ; STDDMA LXI D,TBUFF ; ; Routine to set the dma address to (DE). ; DMASET MVI C,26 JMP ENTRY ; ; Delete the batch file created by SUBMIT. ; DELBATCH:LXI H,BATCH ;is batch active? MOV A,M ORA A RZ MVI M,0 ;yes, de-activate it. XRA A CALL DSKSEL ;select drive 0 for sure. LXI D,BATCHFCB;and delete this file. CALL DELETE LDA CDRIVE ;reset current drive. JMP DSKSEL ; ; Check to two strings at (PATTRN1) and (PATTRN2). They must be ; the same or we halt.... ; VERIFY LXI D,PATTRN1;these are the serial number bytes. LXI H,PATTRN2;ditto, but how could they be different? MVI B,6 ;6 bytes each. VERIFY1 LDAX D CMP M JNZ HALT ;jump to halt routine. INX D INX H DCR B JNZ VERIFY1 RET ; ; Print back file name with a '?' to indicate a syntax error. ; SYNERR CALL CRLF ;end current line. LHLD NAMEPNT ;this points to name in error. SYNERR1 MOV A,M ;print it until a space or null is found. CPI ' ' JZ SYNERR2 ORA A JZ SYNERR2 PUSH H CALL PRINT POP H INX H JMP SYNERR1 SYNERR2 MVI A,'?' ;add trailing '?'. CALL PRINT CALL CRLF CALL DELBATCH;delete any batch file. JMP CMMND1 ;and restart from console input. ; ; Check character at (DE) for legal command input. Note that the ; zero flag is set if the character is a delimiter. ; CHECK LDAX D ORA A RZ CPI ' ' ;control characters are not legal here. JC SYNERR RZ ;check for valid delimiter. CPI '=' RZ CPI '_' RZ CPI '.' RZ CPI ':' RZ CPI ';' RZ CPI '<' RZ CPI '>' RZ RET ; ; Get the next non-blank character from (DE). ; NONBLANK:LDAX D ORA A ;string ends with a null. RZ CPI ' ' RNZ INX D JMP NONBLANK ; ; Add (HL)=(HL)+(A) ; ADDHL ADD L MOV L,A RNC ;take care of any carry. INR H RET ; ; Convert the first name in (FCB). ; CONVFST MVI A,0 ; ; Format a file name (convert * to '?', etc.). On return, ; (A)=0 is an unambigeous name was specified. Enter with (A) equal to ; the position within the fcb for the name (either 0 or 16). ; CONVERT LXI H,FCB CALL ADDHL PUSH H PUSH H XRA A STA CHGDRV ;initialize drive change flag. LHLD INPOINT ;set (HL) as pointer into input line. XCHG CALL NONBLANK;get next non-blank character. XCHG SHLD NAMEPNT ;save pointer here for any error message. XCHG POP H LDAX D ;get first character. ORA A JZ CONVRT1 SBI 'A'-1 ;might be a drive name, convert to binary. MOV B,A ;and save. INX D ;check next character for a ':'. LDAX D CPI ':' JZ CONVRT2 DCX D ;nope, move pointer back to the start of the line. CONVRT1 LDA CDRIVE MOV M,A JMP CONVRT3 CONVRT2 MOV A,B STA CHGDRV ;set change in drives flag. MOV M,B INX D ; ; Convert the basic file name. ; CONVRT3 MVI B,08H CONVRT4 CALL CHECK JZ CONVRT8 INX H CPI '*' ;note that an '*' will fill the remaining JNZ CONVRT5 ;field with '?'. MVI M,'?' JMP CONVRT6 CONVRT5 MOV M,A INX D CONVRT6 DCR B JNZ CONVRT4 CONVRT7 CALL CHECK ;get next delimiter. JZ GETEXT INX D JMP CONVRT7 CONVRT8 INX H ;blank fill the file name. MVI M,' ' DCR B JNZ CONVRT8 ; ; Get the extension and convert it. ; GETEXT MVI B,03H CPI '.' JNZ GETEXT5 INX D GETEXT1 CALL CHECK JZ GETEXT5 INX H CPI '*' JNZ GETEXT2 MVI M,'?' JMP GETEXT3 GETEXT2 MOV M,A INX D GETEXT3 DCR B JNZ GETEXT1 GETEXT4 CALL CHECK JZ GETEXT6 INX D JMP GETEXT4 GETEXT5 INX H MVI M,' ' DCR B JNZ GETEXT5 GETEXT6 MVI B,3 GETEXT7 INX H MVI M,0 DCR B JNZ GETEXT7 XCHG SHLD INPOINT ;save input line pointer. POP H ; ; Check to see if this is an ambigeous file name specification. ; Set the (A) register to non zero if it is. ; LXI B,11 ;set name length. GETEXT8 INX H MOV A,M CPI '?' ;any question marks? JNZ GETEXT9 INR B ;count them. GETEXT9 DCR C JNZ GETEXT8 MOV A,B ORA A RET ; ; CP/M command table. Note commands can be either 3 or 4 characters long. ; NUMCMDS EQU 6 ;number of commands CMDTBL DB 'DIR ' DB 'ERA ' DB 'TYPE' DB 'SAVE' DB 'REN ' DB 'USER' ; ; The following six bytes must agree with those at (PATTRN2) ; or cp/m will HALT. Why? ; PATTRN1 DB 0,22,0,0,0,0;(* serial number bytes *). ; ; Search the command table for a match with what has just ; been entered. If a match is found, then we jump to the ; proper section. Else jump to (UNKNOWN). ; On return, the (C) register is set to the command number ; that matched (or NUMCMDS+1 if no match). ; SEARCH LXI H,CMDTBL MVI C,0 SEARCH1 MOV A,C CPI NUMCMDS ;this commands exists. RNC LXI D,FCB+1 ;check this one. MVI B,4 ;max command length. SEARCH2 LDAX D CMP M JNZ SEARCH3 ;not a match. INX D INX H DCR B JNZ SEARCH2 LDAX D ;allow a 3 character command to match. CPI ' ' JNZ SEARCH4 MOV A,C ;set return register for this command. RET SEARCH3 INX H DCR B JNZ SEARCH3 SEARCH4 INR C JMP SEARCH1 ; ; Set the input buffer to empty and then start the command ; processor (ccp). ; CLEARBUF:XRA A STA INBUFF+1;second byte is actual length. ; ;************************************************************** ;* ;* ;* C C P - C o n s o l e C o m m a n d P r o c e s s o r ;* ;************************************************************** ;* COMMAND LXI SP,CCPSTACK;setup stack area. PUSH B ;note that (C) should be equal to: MOV A,C ;(uuuudddd) where 'uuuu' is the user number RAR ;and 'dddd' is the drive number. RAR RAR RAR ANI 0FH ;isolate the user number. MOV E,A CALL GETSETUC;and set it. CALL RESDSK ;reset the disk system. STA BATCH ;clear batch mode flag. POP B MOV A,C ANI 0FH ;isolate the drive number. STA CDRIVE ;and save. CALL DSKSEL ;...and select. LDA INBUFF+1 ORA A ;anything in input buffer already? JNZ CMMND2 ;yes, we just process it. ; ; Entry point to get a command line from the console. ; CMMND1 LXI SP,CCPSTACK;set stack straight. CALL CRLF ;start a new line on the screen. CALL GETDSK ;get current drive. ADI 'a' CALL PRINT ;print current drive. MVI A,'>' CALL PRINT ;and add prompt. CALL GETINP ;get line from user. ; ; Process command line here. ; CMMND2 LXI D,TBUFF CALL DMASET ;set standard dma address. CALL GETDSK STA CDRIVE ;set current drive. CALL CONVFST ;convert name typed in. CNZ SYNERR ;wild cards are not allowed. LDA CHGDRV ;if a change in drives was indicated, ORA A ;then treat this as an unknown command JNZ UNKNOWN ;which gets executed. CALL SEARCH ;else search command table for a match. ; ; Note that an unknown command returns ; with (A) pointing to the last address ; in our table which is (UNKNOWN). ; LXI H,CMDADR;now, look thru our address table for command (A). MOV E,A ;set (DE) to command number. MVI D,0 DAD D DAD D ;(HL)=(CMDADR)+2*(command number). MOV A,M ;now pick out this address. INX H MOV H,M MOV L,A PCHL ;now execute it. ; ; CP/M command address table. ; CMDADR DW DIRECT,ERASE,TYPE,SAVE DW RENAME,USER,UNKNOWN ; ; Halt the system. Reason for this is unknown at present. ; HALT LXI H,76F3H ;'DI HLT' instructions. SHLD CBASE LXI H,CBASE PCHL ; ; Read error while TYPEing a file. ; RDERROR LXI B,RDERR JMP PLINE RDERR DB 'Read error',0 ; ; Required file was not located. ; NONE LXI B,NOFILE JMP PLINE NOFILE DB 'No file',0 ; ; Decode a command of the form 'A>filename number{ filename}. ; Note that a drive specifier is not allowed on the first file ; name. On return, the number is in register (A). Any error ; causes 'filename?' to be printed and the command is aborted. ; DECODE CALL CONVFST ;convert filename. LDA CHGDRV ;do not allow a drive to be specified. ORA A JNZ SYNERR LXI H,FCB+1 ;convert number now. LXI B,11 ;(B)=sum register, (C)=max digit count. DECODE1 MOV A,M CPI ' ' ;a space terminates the numeral. JZ DECODE3 INX H SUI '0' ;make binary from ascii. CPI 10 ;legal digit? JNC SYNERR MOV D,A ;yes, save it in (D). MOV A,B ;compute (B)=(B)*10 and check for overflow. ANI 0E0H JNZ SYNERR MOV A,B RLC RLC RLC ;(A)=(B)*8 ADD B ;.......*9 JC SYNERR ADD B ;.......*10 JC SYNERR ADD D ;add in new digit now. DECODE2 JC SYNERR MOV B,A ;and save result. DCR C ;only look at 11 digits. JNZ DECODE1 RET DECODE3 MOV A,M ;spaces must follow (why?). CPI ' ' JNZ SYNERR INX H DECODE4 DCR C JNZ DECODE3 MOV A,B ;set (A)=the numeric value entered. RET ; ; Move 3 bytes from (HL) to (DE). Note that there is only ; one reference to this at (A2D5h). ; MOVE3 MVI B,3 ; ; Move (B) bytes from (HL) to (DE). ; HL2DE MOV A,M STAX D INX H INX D DCR B JNZ HL2DE RET ; ; Compute (HL)=(TBUFF)+(A)+(C) and get the byte that's here. ; EXTRACT LXI H,TBUFF ADD C CALL ADDHL MOV A,M RET ; ; Check drive specified. If it means a change, then the new ; drive will be selected. In any case, the drive byte of the ; fcb will be set to null (means use current drive). ; DSELECT XRA A ;null out first byte of fcb. STA FCB LDA CHGDRV ;a drive change indicated? ORA A RZ DCR A ;yes, is it the same as the current drive? LXI H,CDRIVE CMP M RZ JMP DSKSEL ;no. Select it then. ; ; Check the drive selection and reset it to the previous ; drive if it was changed for the preceeding command. ; RESETDR LDA CHGDRV ;drive change indicated? ORA A RZ DCR A ;yes, was it a different drive? LXI H,CDRIVE CMP M RZ LDA CDRIVE ;yes, re-select our old drive. JMP DSKSEL ; ;************************************************************** ;* ;* D I R E C T O R Y C O M M A N D ;* ;************************************************************** ; DIRECT CALL CONVFST ;convert file name. CALL DSELECT ;select indicated drive. LXI H,FCB+1 ;was any file indicated? MOV A,M CPI ' ' JNZ DIRECT2 MVI B,11 ;no. Fill field with '?' - same as *.*. DIRECT1 MVI M,'?' INX H DCR B JNZ DIRECT1 DIRECT2 MVI E,0 ;set initial cursor position. PUSH D CALL SRCHFCB ;get first file name. CZ NONE ;none found at all? DIRECT3 JZ DIRECT9 ;terminate if no more names. LDA RTNCODE ;get file's position in segment (0-3). RRC RRC RRC ANI 60H ;(A)=position*32 MOV C,A MVI A,10 CALL EXTRACT ;extract the tenth entry in fcb. RAL ;check system file status bit. JC DIRECT8 ;we don't list them. POP D MOV A,E ;bump name count. INR E PUSH D ANI 03H ;at end of line? PUSH PSW JNZ DIRECT4 CALL CRLF ;yes, end this line and start another. PUSH B CALL GETDSK ;start line with ('A:'). POP B ADI 'A' CALL PRINTB MVI A,':' CALL PRINTB JMP DIRECT5 DIRECT4 CALL SPACE ;add seperator between file names. MVI A,':' CALL PRINTB DIRECT5 CALL SPACE MVI B,1 ;'extract' each file name character at a time. DIRECT6 MOV A,B CALL EXTRACT ANI 7FH ;strip bit 7 (status bit). CPI ' ' ;are we at the end of the name? JNZ DRECT65 POP PSW ;yes, don't print spaces at the end of a line. PUSH PSW CPI 3 JNZ DRECT63 MVI A,9 ;first check for no extension. CALL EXTRACT ANI 7FH CPI ' ' JZ DIRECT7 ;don't print spaces. DRECT63 MVI A,' ' ;else print them. DRECT65 CALL PRINTB INR B ;bump to next character psoition. MOV A,B CPI 12 ;end of the name? JNC DIRECT7 CPI 9 ;nope, starting extension? JNZ DIRECT6 CALL SPACE ;yes, add seperating space. JMP DIRECT6 DIRECT7 POP PSW ;get the next file name. DIRECT8 CALL CHKCON ;first check console, quit on anything. JNZ DIRECT9 CALL SRCHNXT ;get next name. JMP DIRECT3 ;and continue with our list. DIRECT9 POP D ;restore the stack and return to command level. JMP GETBACK ; ;************************************************************** ;* ;* E R A S E C O M M A N D ;* ;************************************************************** ; ERASE CALL CONVFST ;convert file name. CPI 11 ;was '*.*' entered? JNZ ERASE1 LXI B,YESNO ;yes, ask for confirmation. CALL PLINE CALL GETINP LXI H,INBUFF+1 DCR M ;must be exactly 'y'. JNZ CMMND1 INX H MOV A,M CPI 'Y' JNZ CMMND1 INX H SHLD INPOINT ;save input line pointer. ERASE1 CALL DSELECT ;select desired disk. LXI D,FCB CALL DELETE ;delete the file. INR A CZ NONE ;not there? JMP GETBACK ;return to command level now. YESNO DB 'All (y/n)?',0 ; ;************************************************************** ;* ;* T Y P E C O M M A N D ;* ;************************************************************** ; TYPE CALL CONVFST ;convert file name. JNZ SYNERR ;wild cards not allowed. CALL DSELECT ;select indicated drive. CALL OPENFCB ;open the file. JZ TYPE5 ;not there? CALL CRLF ;ok, start a new line on the screen. LXI H,NBYTES;initialize byte counter. MVI M,0FFH ;set to read first sector. TYPE1 LXI H,NBYTES TYPE2 MOV A,M ;have we written the entire sector? CPI 128 JC TYPE3 PUSH H ;yes, read in the next one. CALL READFCB POP H JNZ TYPE4 ;end or error? XRA A ;ok, clear byte counter. MOV M,A TYPE3 INR M ;count this byte. LXI H,TBUFF ;and get the (A)th one from the buffer (TBUFF). CALL ADDHL MOV A,M CPI CNTRLZ ;end of file mark? JZ GETBACK CALL PRINT ;no, print it. CALL CHKCON ;check console, quit if anything ready. JNZ GETBACK JMP TYPE1 ; ; Get here on an end of file or read error. ; TYPE4 DCR A ;read error? JZ GETBACK CALL RDERROR ;yes, print message. TYPE5 CALL RESETDR ;and reset proper drive JMP SYNERR ;now print file name with problem. ; ;************************************************************** ;* ;* S A V E C O M M A N D ;* ;************************************************************** ; SAVE CALL DECODE ;get numeric number that follows SAVE. PUSH PSW ;save number of pages to write. CALL CONVFST ;convert file name. JNZ SYNERR ;wild cards not allowed. CALL DSELECT ;select specified drive. LXI D,FCB ;now delete this file. PUSH D CALL DELETE POP D CALL CREATE ;and create it again. JZ SAVE3 ;can't create? XRA A ;clear record number byte. STA FCB+32 POP PSW ;convert pages to sectors. MOV L,A MVI H,0 DAD H ;(HL)=number of sectors to write. LXI D,TBASE ;and we start from here. SAVE1 MOV A,H ;done yet? ORA L JZ SAVE2 DCX H ;nope, count this and compute the start PUSH H ;of the next 128 byte sector. LXI H,128 DAD D PUSH H ;save it and set the transfer address. CALL DMASET LXI D,FCB ;write out this sector now. CALL WRTREC POP D ;reset (DE) to the start of the last sector. POP H ;restore sector count. JNZ SAVE3 ;write error? JMP SAVE1 ; ; Get here after writing all of the file. ; SAVE2 LXI D,FCB ;now close the file. CALL CLOSE INR A ;did it close ok? JNZ SAVE4 ; ; Print out error message (no space). ; SAVE3 LXI B,NOSPACE CALL PLINE SAVE4 CALL STDDMA ;reset the standard dma address. JMP GETBACK NOSPACE DB 'No space',0 ; ;************************************************************** ;* ;* R E N A M E C O M M A N D ;* ;************************************************************** ; RENAME CALL CONVFST ;convert first file name. JNZ SYNERR ;wild cards not allowed. LDA CHGDRV ;remember any change in drives specified. PUSH PSW CALL DSELECT ;and select this drive. CALL SRCHFCB ;is this file present? JNZ RENAME6 ;yes, print error message. LXI H,FCB ;yes, move this name into second slot. LXI D,FCB+16 MVI B,16 CALL HL2DE LHLD INPOINT ;get input pointer. XCHG CALL NONBLANK;get next non blank character. CPI '=' ;only allow an '=' or '_' seperator. JZ RENAME1 CPI '_' JNZ RENAME5 RENAME1 XCHG INX H ;ok, skip seperator. SHLD INPOINT ;save input line pointer. CALL CONVFST ;convert this second file name now. JNZ RENAME5 ;again, no wild cards. POP PSW ;if a drive was specified, then it MOV B,A ;must be the same as before. LXI H,CHGDRV MOV A,M ORA A JZ RENAME2 CMP B MOV M,B JNZ RENAME5 ;they were different, error. RENAME2 MOV M,B; reset as per the first file specification. XRA A STA FCB ;clear the drive byte of the fcb. RENAME3 CALL SRCHFCB ;and go look for second file. JZ RENAME4 ;doesn't exist? LXI D,FCB CALL RENAM ;ok, rename the file. JMP GETBACK ; ; Process rename errors here. ; RENAME4 CALL NONE ;file not there. JMP GETBACK RENAME5 CALL RESETDR ;bad command format. JMP SYNERR RENAME6 LXI B,EXISTS;destination file already exists. CALL PLINE JMP GETBACK EXISTS DB 'File exists',0 ; ;************************************************************** ;* ;* U S E R C O M M A N D ;* ;************************************************************** ; USER CALL DECODE ;get numeric value following command. CPI 16 ;legal user number? JNC SYNERR MOV E,A ;yes but is there anything else? LDA FCB+1 CPI ' ' JZ SYNERR ;yes, that is not allowed. CALL GETSETUC;ok, set user code. JMP GETBACK1 ; ;************************************************************** ;* ;* T R A N S I A N T P R O G R A M C O M M A N D ;* ;************************************************************** ; UNKNOWN CALL VERIFY ;check for valid system (why?). LDA FCB+1 ;anything to execute? CPI ' ' JNZ UNKWN1 LDA CHGDRV ;nope, only a drive change? ORA A JZ GETBACK1;neither??? DCR A STA CDRIVE ;ok, store new drive. CALL MOVECD ;set (TDRIVE) also. CALL DSKSEL ;and select this drive. JMP GETBACK1;then return. ; ; Here a file name was typed. Prepare to execute it. ; UNKWN1 LXI D,FCB+9 ;an extension specified? LDAX D CPI ' ' JNZ SYNERR ;yes, not allowed. UNKWN2 PUSH D CALL DSELECT ;select specified drive. POP D LXI H,COMFILE ;set the extension to 'COM'. CALL MOVE3 CALL OPENFCB ;and open this file. JZ UNKWN9 ;not present? ; ; Load in the program. ; LXI H,TBASE ;store the program starting here. UNKWN3 PUSH H XCHG CALL DMASET ;set transfer address. LXI D,FCB ;and read the next record. CALL RDREC JNZ UNKWN4 ;end of file or read error? POP H ;nope, bump pointer for next sector. LXI D,128 DAD D LXI D,CBASE ;enough room for the whole file? MOV A,L SUB E MOV A,H SBB D JNC UNKWN0 ;no, it can't fit. JMP UNKWN3 ; ; Get here after finished reading. ; UNKWN4 POP H DCR A ;normal end of file? JNZ UNKWN0 CALL RESETDR ;yes, reset previous drive. CALL CONVFST ;convert the first file name that follows LXI H,CHGDRV;command name. PUSH H MOV A,M ;set drive code in default fcb. STA FCB MVI A,16 ;put second name 16 bytes later. CALL CONVERT ;convert second file name. POP H MOV A,M ;and set the drive for this second file. STA FCB+16 XRA A ;clear record byte in fcb. STA FCB+32 LXI D,TFCB ;move it into place at(005Ch). LXI H,FCB MVI B,33 CALL HL2DE LXI H,INBUFF+2;now move the remainder of the input UNKWN5 MOV A,M ;line down to (0080h). Look for a non blank. ORA A ;or a null. JZ UNKWN6 CPI ' ' JZ UNKWN6 INX H JMP UNKWN5 ; ; Do the line move now. It ends in a null byte. ; UNKWN6 MVI B,0 ;keep a character count. LXI D,TBUFF+1;data gets put here. UNKWN7 MOV A,M ;move it now. STAX D ORA A JZ UNKWN8 INR B INX H INX D JMP UNKWN7 UNKWN8 MOV A,B ;now store the character count. STA TBUFF CALL CRLF ;clean up the screen. CALL STDDMA ;set standard transfer address. CALL SETCDRV ;reset current drive. CALL TBASE ;and execute the program. ; ; Transiant programs return here (or reboot). ; LXI SP,BATCH ;set stack first off. CALL MOVECD ;move current drive into place (TDRIVE). CALL DSKSEL ;and reselect it. JMP CMMND1 ;back to comand mode. ; ; Get here if some error occured. ; UNKWN9 CALL RESETDR ;inproper format. JMP SYNERR UNKWN0 LXI B,BADLOAD;read error or won't fit. CALL PLINE JMP GETBACK BADLOAD DB 'Bad load',0 COMFILE DB 'COM' ;command file extension. ; ; Get here to return to command level. We will reset the ; previous active drive and then either return to command ; level directly or print error message and then return. ; GETBACK CALL RESETDR ;reset previous drive. GETBACK1:CALL CONVFST ;convert first name in (FCB). LDA FCB+1 ;if this was just a drive change request, SUI ' ' ;make sure it was valid. LXI H,CHGDRV ORA M JNZ SYNERR JMP CMMND1 ;ok, return to command level. ; ; ccp stack area. ; DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 CCPSTACK:EQU $ ;end of ccp stack area. ; ; Batch (or SUBMIT) processing information storage. ; BATCH DB 0 ;batch mode flag (0=not active). BATCHFCB:DB 0,'$$$ SUB',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; ; File control block setup by the CCP. ; FCB DB 0,' ',0,0,0,0,0,' ',0,0,0,0,0 RTNCODE DB 0 ;status returned from bdos call. CDRIVE DB 0 ;currently active drive. CHGDRV DB 0 ;change in drives flag (0=no change). NBYTES DW 0 ;byte counter used by TYPE. ; ; Room for expansion? ; DB 0,0,0,0,0,0,0,0,0,0,0,0,0 ; ; Note that the following six bytes must match those at ; (PATTRN1) or cp/m will HALT. Why? ; PATTRN2 DB 0,22,0,0,0,0;(* serial number bytes *). ; ;************************************************************** ;* ;* B D O S E N T R Y ;* ;************************************************************** ; FBASE JMP FBASE1 ; ; Bdos error table. ; BADSCTR DW ERROR1 ;bad sector on read or write. BADSLCT DW ERROR2 ;bad disk select. RODISK DW ERROR3 ;disk is read only. ROFILE DW ERROR4 ;file is read only. ; ; Entry into bdos. (DE) or (E) are the parameters passed. The ; function number desired is in register (C). ; FBASE1 XCHG ;save the (DE) parameters. SHLD PARAMS XCHG MOV A,E ;and save register (E) in particular. STA EPARAM LXI H,0 SHLD STATUS ;clear return status. DAD SP SHLD USRSTACK;save users stack pointer. LXI SP,STKAREA;and set our own. XRA A ;clear auto select storage space. STA AUTOFLAG STA AUTO LXI H,GOBACK;set return address. PUSH H MOV A,C ;get function number. CPI NFUNCTS ;valid function number? RNC MOV C,E ;keep single register function here. LXI H,FUNCTNS;now look thru the function table. MOV E,A MVI D,0 ;(DE)=function number. DAD D DAD D ;(HL)=(start of table)+2*(function number). MOV E,M INX H MOV D,M ;now (DE)=address for this function. LHLD PARAMS ;retrieve parameters. XCHG ;now (DE) has the original parameters. PCHL ;execute desired function. ; ; BDOS function jump table. ; NFUNCTS EQU 41 ;number of functions in followin table. ; FUNCTNS DW WBOOT,GETCON,OUTCON,GETRDR,PUNCH,LIST,DIRCIO,GETIOB DW SETIOB,PRTSTR,RDBUFF,GETCSTS,GETVER,RSTDSK,SETDSK,OPENFIL DW CLOSEFIL,GETFST,GETNXT,DELFILE,READSEQ,WRTSEQ,FCREATE DW RENFILE,GETLOG,GETCRNT,PUTDMA,GETALOC,WRTPRTD,GETROV,SETATTR DW GETPARM,GETUSER,RDRANDOM,WTRANDOM,FILESIZE,SETRAN,LOGOFF,RTN DW RTN,WTSPECL ; ; Bdos error message section. ; ERROR1 LXI H,BADSEC ;bad sector message. CALL PRTERR ;print it and get a 1 char responce. CPI CNTRLC ;re-boot request (control-c)? JZ 0 ;yes. RET ;no, return to retry i/o function. ; ERROR2 LXI H,BADSEL ;bad drive selected. JMP ERROR5 ; ERROR3 LXI H,DISKRO ;disk is read only. JMP ERROR5 ; ERROR4 LXI H,FILERO ;file is read only. ; ERROR5 CALL PRTERR JMP 0 ;always reboot on these errors. ; BDOSERR DB 'Bdos Err On ' BDOSDRV DB ' : $' BADSEC DB 'Bad Sector$' BADSEL DB 'Select$' FILERO DB 'File ' DISKRO DB 'R/O$' ; ; Print bdos error message. ; PRTERR PUSH H ;save second message pointer. CALL OUTCRLF ;send (cr)(lf). LDA ACTIVE ;get active drive. ADI 'A' ;make ascii. STA BDOSDRV ;and put in message. LXI B,BDOSERR;and print it. CALL PRTMESG POP B ;print second message line now. CALL PRTMESG ; ; Get an input character. We will check our 1 character ; buffer first. This may be set by the console status routine. ; GETCHAR LXI H,CHARBUF;check character buffer. MOV A,M ;anything present already? MVI M,0 ;...either case clear it. ORA A RNZ ;yes, use it. JMP CONIN ;nope, go get a character responce. ; ; Input and echo a character. ; GETECHO CALL GETCHAR ;input a character. CALL CHKCHAR ;carriage control? RC ;no, a regular control char so don't echo. PUSH PSW ;ok, save character now. MOV C,A CALL OUTCON ;and echo it. POP PSW ;get character and return. RET ; ; Check character in (A). Set the zero flag on a carriage ; control character and the carry flag on any other control ; character. ; CHKCHAR CPI CR ;check for carriage return, line feed, backspace, RZ ;or a tab. CPI LF RZ CPI TAB RZ CPI BS RZ CPI ' ' ;other control char? Set carry flag. RET ; ; Check the console during output. Halt on a control-s, then ; reboot on a control-c. If anything else is ready, clear the ; zero flag and return (the calling routine may want to do ; something). ; CKCONSOL:LDA CHARBUF ;check buffer. ORA A ;if anything, just return without checking. JNZ CKCON2 CALL CONST ;nothing in buffer. Check console. ANI 01H ;look at bit 0. RZ ;return if nothing. CALL CONIN ;ok, get it. CPI CNTRLS ;if not control-s, return with zero cleared. JNZ CKCON1 CALL CONIN ;halt processing until another char CPI CNTRLC ;is typed. Control-c? JZ 0 ;yes, reboot now. XRA A ;no, just pretend nothing was ever ready. RET CKCON1 STA CHARBUF ;save character in buffer for later processing. CKCON2 MVI A,1 ;set (A) to non zero to mean something is ready. RET ; ; Output (C) to the screen. If the printer flip-flop flag ; is set, we will send character to printer also. The console ; will be checked in the process. ; OUTCHAR LDA OUTFLAG ;check output flag. ORA A ;anything and we won't generate output. JNZ OUTCHR1 PUSH B CALL CKCONSOL;check console (we don't care whats there). POP B PUSH B CALL CONOUT ;output (C) to the screen. POP B PUSH B LDA PRTFLAG ;check printer flip-flop flag. ORA A CNZ LIST ;print it also if non-zero. POP B OUTCHR1 MOV A,C ;update cursors position. LXI H,CURPOS CPI DEL ;rubouts don't do anything here. RZ INR M ;bump line pointer. CPI ' ' ;and return if a normal character. RNC DCR M ;restore and check for the start of the line. MOV A,M ORA A RZ ;ingnore control characters at the start of the line. MOV A,C CPI BS ;is it a backspace? JNZ OUTCHR2 DCR M ;yes, backup pointer. RET OUTCHR2 CPI LF ;is it a line feed? RNZ ;ignore anything else. MVI M,0 ;reset pointer to start of line. RET ; ; Output (A) to the screen. If it is a control character ; (other than carriage control), use ^x format. ; SHOWIT MOV A,C CALL CHKCHAR ;check character. JNC OUTCON ;not a control, use normal output. PUSH PSW MVI C,'^' ;for a control character, preceed it with '^'. CALL OUTCHAR POP PSW ORI '@' ;and then use the letter equivelant. MOV C,A ; ; Function to output (C) to the console device and expand tabs ; if necessary. ; OUTCON MOV A,C CPI TAB ;is it a tab? JNZ OUTCHAR ;use regular output. OUTCON1 MVI C,' ' ;yes it is, use spaces instead. CALL OUTCHAR LDA CURPOS ;go until the cursor is at a multiple of 8 ANI 07H ;position. JNZ OUTCON1 RET ; ; Echo a backspace character. Erase the prevoius character ; on the screen. ; BACKUP CALL BACKUP1 ;backup the screen 1 place. MVI C,' ' ;then blank that character. CALL CONOUT BACKUP1 MVI C,BS ;then back space once more. JMP CONOUT ; ; Signal a deleted line. Print a '#' at the end and start ; over. ; NEWLINE MVI C,'#' CALL OUTCHAR ;print this. CALL OUTCRLF ;start new line. NEWLN1 LDA CURPOS ;move the cursor to the starting position. LXI H,STARTING CMP M RNC ;there yet? MVI C,' ' CALL OUTCHAR ;nope, keep going. JMP NEWLN1 ; ; Output a (cr) (lf) to the console device (screen). ; OUTCRLF MVI C,CR CALL OUTCHAR MVI C,LF JMP OUTCHAR ; ; Print message pointed to by (BC). It will end with a '$'. ; PRTMESG LDAX B ;check for terminating character. CPI '$' RZ INX B PUSH B ;otherwise, bump pointer and print it. MOV C,A CALL OUTCON POP B JMP PRTMESG ; ; Function to execute a buffered read. ; RDBUFF LDA CURPOS ;use present location as starting one. STA STARTING LHLD PARAMS ;get the maximum buffer space. MOV C,M INX H ;point to first available space. PUSH H ;and save. MVI B,0 ;keep a character count. RDBUF1 PUSH B PUSH H RDBUF2 CALL GETCHAR ;get the next input character. ANI 7FH ;strip bit 7. POP H ;reset registers. POP B CPI CR ;en of the line? JZ RDBUF17 CPI LF JZ RDBUF17 CPI BS ;how about a backspace? JNZ RDBUF3 MOV A,B ;yes, but ignore at the beginning of the line. ORA A JZ RDBUF1 DCR B ;ok, update counter. LDA CURPOS ;if we backspace to the start of the line, STA OUTFLAG ;treat as a cancel (control-x). JMP RDBUF10 RDBUF3 CPI DEL ;user typed a rubout? JNZ RDBUF4 MOV A,B ;ignore at the start of the line. ORA A JZ RDBUF1 MOV A,M ;ok, echo the prevoius character. DCR B ;and reset pointers (counters). DCX H JMP RDBUF15 RDBUF4 CPI CNTRLE ;physical end of line? JNZ RDBUF5 PUSH B ;yes, do it. PUSH H CALL OUTCRLF XRA A ;and update starting position. STA STARTING JMP RDBUF2 RDBUF5 CPI CNTRLP ;control-p? JNZ RDBUF6 PUSH H ;yes, flip the print flag filp-flop byte. LXI H,PRTFLAG MVI A,1 ;PRTFLAG=1-PRTFLAG SUB M MOV M,A POP H JMP RDBUF1 RDBUF6 CPI CNTRLX ;control-x (cancel)? JNZ RDBUF8 POP H RDBUF7 LDA STARTING;yes, backup the cursor to here. LXI H,CURPOS CMP M JNC RDBUFF ;done yet? DCR M ;no, decrement pointer and output back up one space. CALL BACKUP JMP RDBUF7 RDBUF8 CPI CNTRLU ;cntrol-u (cancel line)? JNZ RDBUF9 CALL NEWLINE ;start a new line. POP H JMP RDBUFF RDBUF9 CPI CNTRLR ;control-r? JNZ RDBUF14 RDBUF10 PUSH B ;yes, start a new line and retype the old one. CALL NEWLINE POP B POP H PUSH H PUSH B RDBUF11 MOV A,B ;done whole line yet? ORA A JZ RDBUF12 INX H ;nope, get next character. MOV C,M DCR B ;count it. PUSH B PUSH H CALL SHOWIT ;and display it. POP H POP B JMP RDBUF11 RDBUF12 PUSH H ;done with line. If we were displaying LDA OUTFLAG ;then update cursor position. ORA A JZ RDBUF2 LXI H,CURPOS;because this line is shorter, we must SUB M ;back up the cursor (not the screen however) STA OUTFLAG ;some number of positions. RDBUF13 CALL BACKUP ;note that as long as (OUTFLAG) is non LXI H,OUTFLAG;zero, the screen will not be changed. DCR M JNZ RDBUF13 JMP RDBUF2 ;now just get the next character. ; ; Just a normal character, put this in our buffer and echo. ; RDBUF14 INX H MOV M,A ;store character. INR B ;and count it. RDBUF15 PUSH B PUSH H MOV C,A ;echo it now. CALL SHOWIT POP H POP B MOV A,M ;was it an abort request? CPI CNTRLC ;control-c abort? MOV A,B JNZ RDBUF16 CPI 1 ;only if at start of line. JZ 0 RDBUF16 CMP C ;nope, have we filled the buffer? JC RDBUF1 RDBUF17 POP H ;yes end the line and return. MOV M,B MVI C,CR JMP OUTCHAR ;output (cr) and return. ; ; Function to get a character from the console device. ; GETCON CALL GETECHO ;get and echo. JMP SETSTAT ;save status and return. ; ; Function to get a character from the tape reader device. ; GETRDR CALL READER ;get a character from reader, set status and return. JMP SETSTAT ; ; Function to perform direct console i/o. If (C) contains (FF) ; then this is an input request. If (C) contains (FE) then ; this is a status request. Otherwise we are to output (C). ; DIRCIO MOV A,C ;test for (FF). INR A JZ DIRC1 INR A ;test for (FE). JZ CONST JMP CONOUT ;just output (C). DIRC1 CALL CONST ;this is an input request. ORA A JZ GOBACK1 ;not ready? Just return (directly). CALL CONIN ;yes, get character. JMP SETSTAT ;set status and return. ; ; Function to return the i/o byte. ; GETIOB LDA IOBYTE JMP SETSTAT ; ; Function to set the i/o byte. ; SETIOB LXI H,IOBYTE MOV M,C RET ; ; Function to print the character string pointed to by (DE) ; on the console device. The string ends with a '$'. ; PRTSTR XCHG MOV C,L MOV B,H ;now (BC) points to it. JMP PRTMESG ; ; Function to interigate the console device. ; GETCSTS CALL CKCONSOL ; ; Get here to set the status and return to the cleanup ; section. Then back to the user. ; SETSTAT STA STATUS RTN RET ; ; Set the status to 1 (read or write error code). ; IOERR1 MVI A,1 JMP SETSTAT ; OUTFLAG DB 0 ;output flag (non zero means no output). STARTING:DB 2 ;starting position for cursor. CURPOS DB 0 ;cursor position (0=start of line). PRTFLAG DB 0 ;printer flag (control-p toggle). List if non zero. CHARBUF DB 0 ;single input character buffer. ; ; Stack area for BDOS calls. ; USRSTACK:DW 0 ;save users stack pointer here. ; DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 STKAREA EQU $ ;end of stack area. ; USERNO DB 0 ;current user number. ACTIVE DB 0 ;currently active drive. PARAMS DW 0 ;save (DE) parameters here on entry. STATUS DW 0 ;status returned from bdos function. ; ; Select error occured, jump to error routine. ; SLCTERR LXI H,BADSLCT ; ; Jump to (HL) indirectly. ; JUMPHL MOV E,M INX H MOV D,M ;now (DE) contain the desired address. XCHG PCHL ; ; Block move. (DE) to (HL), (C) bytes total. ; DE2HL INR C ;is count down to zero? DE2HL1 DCR C RZ ;yes, we are done. LDAX D ;no, move one more byte. MOV M,A INX D INX H JMP DE2HL1 ;and repeat. ; ; Select the desired drive. ; SELECT LDA ACTIVE ;get active disk. MOV C,A CALL SELDSK ;select it. MOV A,H ;valid drive? ORA L ;valid drive? RZ ;return if not. ; ; Here, the BIOS returned the address of the parameter block ; in (HL). We will extract the necessary pointers and save them. ; MOV E,M ;yes, get address of translation table into (DE). INX H MOV D,M INX H SHLD SCRATCH1 ;save pointers to scratch areas. INX H INX H SHLD SCRATCH2 ;ditto. INX H INX H SHLD SCRATCH3 ;ditto. INX H INX H XCHG ;now save the translation table address. SHLD XLATE LXI H,DIRBUF ;put the next 8 bytes here. MVI C,8 ;they consist of the directory buffer CALL DE2HL ;pointer, parameter block pointer, LHLD DISKPB ;check and allocation vectors. XCHG LXI H,SECTORS ;move parameter block into our ram. MVI C,15 ;it is 15 bytes long. CALL DE2HL LHLD DSKSIZE ;check disk size. MOV A,H ;more than 256 blocks on this? LXI H,BIGDISK MVI M,0FFH ;set to samll. ORA A JZ SELECT1 MVI M,0 ;wrong, set to large. SELECT1 MVI A,0FFH ;clear the zero flag. ORA A RET ; ; Routine to home the disk track head and clear pointers. ; HOMEDRV CALL HOME ;home the head. XRA A LHLD SCRATCH2;set our track pointer also. MOV M,A INX H MOV M,A LHLD SCRATCH3;and our sector pointer. MOV M,A INX H MOV M,A RET ; ; Do the actual disk read and check the error return status. ; DOREAD CALL READ JMP IORET ; ; Do the actual disk write and handle any bios error. ; DOWRITE CALL WRITE IORET ORA A RZ ;return unless an error occured. LXI H,BADSCTR;bad read/write on this sector. JMP JUMPHL ; ; Routine to select the track and sector that the desired ; block number falls in. ; TRKSEC LHLD FILEPOS ;get position of last accessed file MVI C,2 ;in directory and compute sector #. CALL SHIFTR ;sector #=file-position/4. SHLD BLKNMBR ;save this as the block number of interest. SHLD CKSUMTBL;what's it doing here too? ; ; if the sector number has already been set (BLKNMBR), enter ; at this point. ; TRKSEC1 LXI H,BLKNMBR MOV C,M ;move sector number into (BC). INX H MOV B,M LHLD SCRATCH3;get current sector number and MOV E,M ;move this into (DE). INX H MOV D,M LHLD SCRATCH2;get current track number. MOV A,M ;and this into (HL). INX H MOV H,M MOV L,A TRKSEC2 MOV A,C ;is desired sector before current one? SUB E MOV A,B SBB D JNC TRKSEC3 PUSH H ;yes, decrement sectors by one track. LHLD SECTORS ;get sectors per track. MOV A,E SUB L MOV E,A MOV A,D SBB H MOV D,A ;now we have backed up one full track. POP H DCX H ;adjust track counter. JMP TRKSEC2 TRKSEC3 PUSH H ;desired sector is after current one. LHLD SECTORS ;get sectors per track. DAD D ;bump sector pointer to next track. JC TRKSEC4 MOV A,C ;is desired sector now before current one? SUB L MOV A,B SBB H JC TRKSEC4 XCHG ;not yes, increment track counter POP H ;and continue until it is. INX H JMP TRKSEC3 ; ; here we have determined the track number that contains the ; desired sector. ; TRKSEC4 POP H ;get track number (HL). PUSH B PUSH D PUSH H XCHG LHLD OFFSET ;adjust for first track offset. DAD D MOV B,H MOV C,L CALL SETTRK ;select this track. POP D ;reset current track pointer. LHLD SCRATCH2 MOV M,E INX H MOV M,D POP D LHLD SCRATCH3;reset the first sector on this track. MOV M,E INX H MOV M,D POP B MOV A,C ;now subtract the desired one. SUB E ;to make it relative (1-# sectors/track). MOV C,A MOV A,B SBB D MOV B,A LHLD XLATE ;translate this sector according to this table. XCHG CALL SECTRN ;let the bios translate it. MOV C,L MOV B,H JMP SETSEC ;and select it. ; ; Compute block number from record number (SAVNREC) and ; extent number (SAVEXT). ; GETBLOCK:LXI H,BLKSHFT;get logical to physical conversion. MOV C,M ;note that this is base 2 log of ratio. LDA SAVNREC ;get record number. GETBLK1 ORA A ;compute (A)=(A)/2^BLKSHFT. RAR DCR C JNZ GETBLK1 MOV B,A ;save result in (B). MVI A,8 SUB M MOV C,A ;compute (C)=8-BLKSHFT. LDA SAVEXT GETBLK2 DCR C ;compute (A)=SAVEXT*2^(8-BLKSHFT). JZ GETBLK3 ORA A RAL JMP GETBLK2 GETBLK3 ADD B RET ; ; Routine to extract the (BC) block byte from the fcb pointed ; to by (PARAMS). If this is a big-disk, then these are 16 bit ; block numbers, else they are 8 bit numbers. ; Number is returned in (HL). ; EXTBLK LHLD PARAMS ;get fcb address. LXI D,16 ;block numbers start 16 bytes into fcb. DAD D DAD B LDA BIGDISK ;are we using a big-disk? ORA A JZ EXTBLK1 MOV L,M ;no, extract an 8 bit number from the fcb. MVI H,0 RET EXTBLK1 DAD B ;yes, extract a 16 bit number. MOV E,M INX H MOV D,M XCHG ;return in (HL). RET ; ; Compute block number. ; COMBLK CALL GETBLOCK MOV C,A MVI B,0 CALL EXTBLK SHLD BLKNMBR RET ; ; Check for a zero block number (unused). ; CHKBLK LHLD BLKNMBR MOV A,L ;is it zero? ORA H RET ; ; Adjust physical block (BLKNMBR) and convert to logical ; sector (LOGSECT). This is the starting sector of this block. ; The actual sector of interest is then added to this and the ; resulting sector number is stored back in (BLKNMBR). This ; will still have to be adjusted for the track number. ; LOGICAL LDA BLKSHFT ;get log2(physical/logical sectors). LHLD BLKNMBR ;get physical sector desired. LOGICL1 DAD H ;compute logical sector number. DCR A ;note logical sectors are 128 bytes long. JNZ LOGICL1 SHLD LOGSECT ;save logical sector. LDA BLKMASK ;get block mask. MOV C,A LDA SAVNREC ;get next sector to access. ANA C ;extract the relative position within physical block. ORA L ;and add it too logical sector. MOV L,A SHLD BLKNMBR ;and store. RET ; ; Set (HL) to point to extent byte in fcb. ; SETEXT LHLD PARAMS LXI D,12 ;it is the twelth byte. DAD D RET ; ; Set (HL) to point to record count byte in fcb and (DE) to ; next record number byte. ; SETHLDE LHLD PARAMS LXI D,15 ;record count byte (#15). DAD D XCHG LXI H,17 ;next record number (#32). DAD D RET ; ; Save current file data from fcb. ; STRDATA CALL SETHLDE MOV A,M ;get and store record count byte. STA SAVNREC XCHG MOV A,M ;get and store next record number byte. STA SAVNXT CALL SETEXT ;point to extent byte. LDA EXTMASK ;get extent mask. ANA M STA SAVEXT ;and save extent here. RET ; ; Set the next record to access. If (MODE) is set to 2, then ; the last record byte (SAVNREC) has the correct number to access. ; For sequential access, (MODE) will be equal to 1. ; SETNREC CALL SETHLDE LDA MODE ;get sequential flag (=1). CPI 2 ;a 2 indicates that no adder is needed. JNZ STNREC1 XRA A ;clear adder (random access?). STNREC1 MOV C,A LDA SAVNREC ;get last record number. ADD C ;increment record count. MOV M,A ;and set fcb's next record byte. XCHG LDA SAVNXT ;get next record byte from storage. MOV M,A ;and put this into fcb as number of records used. RET ; ; Shift (HL) right (C) bits. ; SHIFTR INR C SHIFTR1 DCR C RZ MOV A,H ORA A RAR MOV H,A MOV A,L RAR MOV L,A JMP SHIFTR1 ; ; Compute the check-sum for the directory buffer. Return ; integer sum in (A). ; CHECKSUM:MVI C,128 ;length of buffer. LHLD DIRBUF ;get its location. XRA A ;clear summation byte. CHKSUM1 ADD M ;and compute sum ignoring carries. INX H DCR C JNZ CHKSUM1 RET ; ; Shift (HL) left (C) bits. ; SHIFTL INR C SHIFTL1 DCR C RZ DAD H ;shift left 1 bit. JMP SHIFTL1 ; ; Routine to set a bit in a 16 bit value contained in (BC). ; The bit set depends on the current drive selection. ; SETBIT PUSH B ;save 16 bit word. LDA ACTIVE ;get active drive. MOV C,A LXI H,1 CALL SHIFTL ;shift bit 0 into place. POP B ;now 'or' this with the original word. MOV A,C ORA L MOV L,A ;low byte done, do high byte. MOV A,B ORA H MOV H,A RET ; ; Extract the write protect status bit for the current drive. ; The result is returned in (A), bit 0. ; GETWPRT LHLD WRTPRT ;get status bytes. LDA ACTIVE ;which drive is current? MOV C,A CALL SHIFTR ;shift status such that bit 0 is the MOV A,L ;one of interest for this drive. ANI 01H ;and isolate it. RET ; ; Function to write protect the current disk. ; WRTPRTD LXI H,WRTPRT;point to status word. MOV C,M ;set (BC) equal to the status. INX H MOV B,M CALL SETBIT ;and set this bit according to current drive. SHLD WRTPRT ;then save. LHLD DIRSIZE ;now save directory size limit. INX H ;remember the last one. XCHG LHLD SCRATCH1;and store it here. MOV M,E ;put low byte. INX H MOV M,D ;then high byte. RET ; ; Check for a read only file. ; CHKROFL CALL FCB2HL ;set (HL) to file entry in directory buffer. CKROF1 LXI D,9 ;look at bit 7 of the ninth byte. DAD D MOV A,M RAL RNC ;return if ok. LXI H,ROFILE;else, print error message and terminate. JMP JUMPHL ; ; Check the write protect status of the active disk. ; CHKWPRT CALL GETWPRT RZ ;return if ok. LXI H,RODISK;else print message and terminate. JMP JUMPHL ; ; Routine to set (HL) pointing to the proper entry in the ; directory buffer. ; FCB2HL LHLD DIRBUF ;get address of buffer. LDA FCBPOS ;relative position of file. ; ; Routine to add (A) to (HL). ; ADDA2HL ADD L MOV L,A RNC INR H ;take care of any carry. RET ; ; Routine to get the 's2' byte from the fcb supplied in ; the initial parameter specification. ; GETS2 LHLD PARAMS ;get address of fcb. LXI D,14 ;relative position of 's2'. DAD D MOV A,M ;extract this byte. RET ; ; Clear the 's2' byte in the fcb. ; CLEARS2 CALL GETS2 ;this sets (HL) pointing to it. MVI M,0 ;now clear it. RET ; ; Set bit 7 in the 's2' byte of the fcb. ; SETS2B7 CALL GETS2 ;get the byte. ORI 80H ;and set bit 7. MOV M,A ;then store. RET ; ; Compare (FILEPOS) with (SCRATCH1) and set flags based on ; the difference. This checks to see if there are more file ; names in the directory. We are at (FILEPOS) and there are ; (SCRATCH1) of them to check. ; MOREFLS LHLD FILEPOS ;we are here. XCHG LHLD SCRATCH1;and don't go past here. MOV A,E ;compute difference but don't keep. SUB M INX H MOV A,D SBB M ;set carry if no more names. RET ; ; Call this routine to prevent (SCRATCH1) from being greater ; than (FILEPOS). ; CHKNMBR CALL MOREFLS ;SCRATCH1 too big? RC INX D ;yes, reset it to (FILEPOS). MOV M,D DCX H MOV M,E RET ; ; Compute (HL)=(DE)-(HL) ; SUBHL MOV A,E ;compute difference. SUB L MOV L,A ;store low byte. MOV A,D SBB H MOV H,A ;and then high byte. RET ; ; Set the directory checksum byte. ; SETDIR MVI C,0FFH ; ; Routine to set or compare the directory checksum byte. If ; (C)=0ffh, then this will set the checksum byte. Else the byte ; will be checked. If the check fails (the disk has been changed), ; then this disk will be write protected. ; CHECKDIR:LHLD CKSUMTBL XCHG LHLD ALLOC1 CALL SUBHL RNC ;ok if (CKSUMTBL) > (ALLOC1), so return. PUSH B CALL CHECKSUM;else compute checksum. LHLD CHKVECT ;get address of checksum table. XCHG LHLD CKSUMTBL DAD D ;set (HL) to point to byte for this drive. POP B INR C ;set or check ? JZ CHKDIR1 CMP M ;check them. RZ ;return if they are the same. CALL MOREFLS ;not the same, do we care? RNC CALL WRTPRTD ;yes, mark this as write protected. RET CHKDIR1 MOV M,A ;just set the byte. RET ; ; Do a write to the directory of the current disk. ; DIRWRITE:CALL SETDIR ;set checksum byte. CALL DIRDMA ;set directory dma address. MVI C,1 ;tell the bios to actually write. CALL DOWRITE ;then do the write. JMP DEFDMA ; ; Read from the directory. ; DIRREAD CALL DIRDMA ;set the directory dma address. CALL DOREAD ;and read it. ; ; Routine to set the dma address to the users choice. ; DEFDMA LXI H,USERDMA;reset the default dma address and return. JMP DIRDMA1 ; ; Routine to set the dma address for directory work. ; DIRDMA LXI H,DIRBUF ; ; Set the dma address. On entry, (HL) points to ; word containing the desired dma address. ; DIRDMA1 MOV C,M INX H MOV B,M ;setup (BC) and go to the bios to set it. JMP SETDMA ; ; Move the directory buffer into user's dma space. ; MOVEDIR LHLD DIRBUF ;buffer is located here, and XCHG LHLD USERDMA; put it here. MVI C,128 ;this is its length. JMP DE2HL ;move it now and return. ; ; Check (FILEPOS) and set the zero flag if it equals 0ffffh. ; CKFILPOS:LXI H,FILEPOS MOV A,M INX H CMP M ;are both bytes the same? RNZ INR A ;yes, but are they each 0ffh? RET ; ; Set location (FILEPOS) to 0ffffh. ; STFILPOS:LXI H,0FFFFH SHLD FILEPOS RET ; ; Move on to the next file position within the current ; directory buffer. If no more exist, set pointer to 0ffffh ; and the calling routine will check for this. Enter with (C) ; equal to 0ffh to cause the checksum byte to be set, else we ; will check this disk and set write protect if checksums are ; not the same (applies only if another directory sector must ; be read). ; NXENTRY LHLD DIRSIZE ;get directory entry size limit. XCHG LHLD FILEPOS ;get current count. INX H ;go on to the next one. SHLD FILEPOS CALL SUBHL ;(HL)=(DIRSIZE)-(FILEPOS) JNC NXENT1 ;is there more room left? JMP STFILPOS;no. Set this flag and return. NXENT1 LDA FILEPOS ;get file position within directory. ANI 03H ;only look within this sector (only 4 entries fit). MVI B,5 ;convert to relative position (32 bytes each). NXENT2 ADD A ;note that this is not efficient code. DCR B ;5 'ADD A's would be better. JNZ NXENT2 STA FCBPOS ;save it as position of fcb. ORA A RNZ ;return if we are within buffer. PUSH B CALL TRKSEC ;we need the next directory sector. CALL DIRREAD POP B JMP CHECKDIR ; ; Routine to to get a bit from the disk space allocation ; map. It is returned in (A), bit position 0. On entry to here, ; set (BC) to the block number on the disk to check. ; On return, (D) will contain the original bit position for ; this block number and (HL) will point to the address for it. ; CKBITMAP:MOV A,C ;determine bit number of interest. ANI 07H ;compute (D)=(E)=(C and 7)+1. INR A MOV E,A ;save particular bit number. MOV D,A ; ; compute (BC)=(BC)/8. ; MOV A,C RRC ;now shift right 3 bits. RRC RRC ANI 1FH ;and clear bits 7,6,5. MOV C,A MOV A,B ADD A ;now shift (B) into bits 7,6,5. ADD A ADD A ADD A ADD A ORA C ;and add in (C). MOV C,A ;ok, (C) ha been completed. MOV A,B ;is there a better way of doing this? RRC RRC RRC ANI 1FH MOV B,A ;and now (B) is completed. ; ; use this as an offset into the disk space allocation ; table. ; LHLD ALOCVECT DAD B MOV A,M ;now get correct byte. CKBMAP1 RLC ;get correct bit into position 0. DCR E JNZ CKBMAP1 RET ; ; Set or clear the bit map such that block number (BC) will be marked ; as used. On entry, if (E)=0 then this bit will be cleared, if it equals ; 1 then it will be set (don't use anyother values). ; STBITMAP:PUSH D CALL CKBITMAP;get the byte of interest. ANI 0FEH ;clear the affected bit. POP B ORA C ;and now set it acording to (C). ; ; entry to restore the original bit position and then store ; in table. (A) contains the value, (D) contains the bit ; position (1-8), and (HL) points to the address within the ; space allocation table for this byte. ; STBMAP1 RRC ;restore original bit position. DCR D JNZ STBMAP1 MOV M,A ;and stor byte in table. RET ; ; Set/clear space used bits in allocation map for this file. ; On entry, (C)=1 to set the map and (C)=0 to clear it. ; SETFILE CALL FCB2HL ;get address of fcb LXI D,16 DAD D ;get to block number bytes. PUSH B MVI C,17 ;check all 17 bytes (max) of table. SETFL1 POP D DCR C ;done all bytes yet? RZ PUSH D LDA BIGDISK ;check disk size for 16 bit block numbers. ORA A JZ SETFL2 PUSH B ;only 8 bit numbers. set (BC) to this one. PUSH H MOV C,M ;get low byte from table, always MVI B,0 ;set high byte to zero. JMP SETFL3 SETFL2 DCR C ;for 16 bit block numbers, adjust counter. PUSH B MOV C,M ;now get both the low and high bytes. INX H MOV B,M PUSH H SETFL3 MOV A,C ;block used? ORA B JZ SETFL4 LHLD DSKSIZE ;is this block number within the MOV A,L ;space on the disk? SUB C MOV A,H SBB B CNC STBITMAP;yes, set the proper bit. SETFL4 POP H ;point to next block number in fcb. INX H POP B JMP SETFL1 ; ; Construct the space used allocation bit map for the active ; drive. If a file name starts with '$' and it is under the ; current user number, then (STATUS) is set to minus 1. Otherwise ; it is not set at all. ; BITMAP LHLD DSKSIZE ;compute size of allocation table. MVI C,3 CALL SHIFTR ;(HL)=(HL)/8. INX H ;at lease 1 byte. MOV B,H MOV C,L ;set (BC) to the allocation table length. ; ; Initialize the bitmap for this drive. Right now, the first ; two bytes are specified by the disk parameter block. However ; a patch could be entered here if it were necessary to setup ; this table in a special mannor. For example, the bios could ; determine locations of 'bad blocks' and set them as already ; 'used' in the map. ; LHLD ALOCVECT;now zero out the table now. BITMAP1 MVI M,0 INX H DCX B MOV A,B ORA C JNZ BITMAP1 LHLD ALLOC0 ;get initial space used by directory. XCHG LHLD ALOCVECT;and put this into map. MOV M,E INX H MOV M,D ; ; End of initialization portion. ; CALL HOMEDRV ;now home the drive. LHLD SCRATCH1 MVI M,3 ;force next directory request to read INX H ;in a sector. MVI M,0 CALL STFILPOS;clear initial file position also. BITMAP2 MVI C,0FFH ;read next file name in directory CALL NXENTRY ;and set checksum byte. CALL CKFILPOS;is there another file? RZ CALL FCB2HL ;yes, get its address. MVI A,0E5H CMP M ;empty file entry? JZ BITMAP2 LDA USERNO ;no, correct user number? CMP M JNZ BITMAP3 INX H MOV A,M ;yes, does name start with a '$'? SUI '$' JNZ BITMAP3 DCR A ;yes, set atatus to minus one. STA STATUS BITMAP3 MVI C,1 ;now set this file's space as used in bit map. CALL SETFILE CALL CHKNMBR ;keep (SCRATCH1) in bounds. JMP BITMAP2 ; ; Set the status (STATUS) and return. ; STSTATUS:LDA FNDSTAT JMP SETSTAT ; ; Check extents in (A) and (C). Set the zero flag if they ; are the same. The number of 16k chunks of disk space that ; the directory extent covers is expressad is (EXTMASK+1). ; No registers are modified. ; SAMEXT PUSH B PUSH PSW LDA EXTMASK ;get extent mask and use it to CMA ;to compare both extent numbers. MOV B,A ;save resulting mask here. MOV A,C ;mask first extent and save in (C). ANA B MOV C,A POP PSW ;now mask second extent and compare ANA B ;with the first one. SUB C ANI 1FH ;(* only check buts 0-4 *) POP B ;the zero flag is set if they are the same. RET ;restore (BC) and return. ; ; Search for the first occurence of a file name. On entry, ; register (C) should contain the number of bytes of the fcb ; that must match. ; FINDFST MVI A,0FFH STA FNDSTAT LXI H,COUNTER;save character count. MOV M,C LHLD PARAMS ;get filename to match. SHLD SAVEFCB ;and save. CALL STFILPOS;clear initial file position (set to 0ffffh). CALL HOMEDRV ;home the drive. ; ; Entry to locate the next occurence of a filename within the ; directory. The disk is not expected to have been changed. If ; it was, then it will be write protected. ; FINDNXT MVI C,0 ;write protect the disk if changed. CALL NXENTRY ;get next filename entry in directory. CALL CKFILPOS;is file position = 0ffffh? JZ FNDNXT6 ;yes, exit now then. LHLD SAVEFCB ;set (DE) pointing to filename to match. XCHG LDAX D CPI 0E5H ;empty directory entry? JZ FNDNXT1 ;(* are we trying to reserect erased entries? *) PUSH D CALL MOREFLS ;more files in directory? POP D JNC FNDNXT6 ;no more. Exit now. FNDNXT1 CALL FCB2HL ;get address of this fcb in directory. LDA COUNTER ;get number of bytes (characters) to check. MOV C,A MVI B,0 ;initialize byte position counter. FNDNXT2 MOV A,C ;are we done with the compare? ORA A JZ FNDNXT5 LDAX D ;no, check next byte. CPI '?' ;don't care about this character? JZ FNDNXT4 MOV A,B ;get bytes position in fcb. CPI 13 ;don't care about the thirteenth byte either. JZ FNDNXT4 CPI 12 ;extent byte? LDAX D JZ FNDNXT3 SUB M ;otherwise compare characters. ANI 7FH JNZ FINDNXT ;not the same, check next entry. JMP FNDNXT4 ;so far so good, keep checking. FNDNXT3 PUSH B ;check the extent byte here. MOV C,M CALL SAMEXT POP B JNZ FINDNXT ;not the same, look some more. ; ; So far the names compare. Bump pointers to the next byte ; and continue until all (C) characters have been checked. ; FNDNXT4 INX D ;bump pointers. INX H INR B DCR C ;adjust character counter. JMP FNDNXT2 FNDNXT5 LDA FILEPOS ;return the position of this entry. ANI 03H STA STATUS LXI H,FNDSTAT MOV A,M RAL RNC XRA A MOV M,A RET ; ; Filename was not found. Set appropriate status. ; FNDNXT6 CALL STFILPOS;set (FILEPOS) to 0ffffh. MVI A,0FFH ;say not located. JMP SETSTAT ; ; Erase files from the directory. Only the first byte of the ; fcb will be affected. It is set to (E5). ; ERAFILE CALL CHKWPRT ;is disk write protected? MVI C,12 ;only compare file names. CALL FINDFST ;get first file name. ERAFIL1 CALL CKFILPOS;any found? RZ ;nope, we must be done. CALL CHKROFL ;is file read only? CALL FCB2HL ;nope, get address of fcb and MVI M,0E5H ;set first byte to 'empty'. MVI C,0 ;clear the space from the bit map. CALL SETFILE CALL DIRWRITE;now write the directory sector back out. CALL FINDNXT ;find the next file name. JMP ERAFIL1 ;and repeat process. ; ; Look through the space allocation map (bit map) for the ; next available block. Start searching at block number (BC-1). ; The search procedure is to look for an empty block that is ; before the starting block. If not empty, look at a later ; block number. In this way, we return the closest empty block ; on either side of the 'target' block number. This will speed ; access on random devices. For serial devices, this should be ; changed to look in the forward direction first and then start ; at the front and search some more. ; ; On return, (DE)= block number that is empty and (HL) =0 ; if no empry block was found. ; FNDSPACE:MOV D,B ;set (DE) as the block that is checked. MOV E,C ; ; Look before target block. Registers (BC) are used as the lower ; pointer and (DE) as the upper pointer. ; FNDSPA1 MOV A,C ;is block 0 specified? ORA B JZ FNDSPA2 DCX B ;nope, check previous block. PUSH D PUSH B CALL CKBITMAP RAR ;is this block empty? JNC FNDSPA3 ;yes. use this. ; ; Note that the above logic gets the first block that it finds ; that is empty. Thus a file could be written 'backward' making ; it very slow to access. This could be changed to look for the ; first empty block and then continue until the start of this ; empty space is located and then used that starting block. ; This should help speed up access to some files especially on ; a well used disk with lots of fairly small 'holes'. ; POP B ;nope, check some more. POP D ; ; Now look after target block. ; FNDSPA2 LHLD DSKSIZE ;is block (DE) within disk limits? MOV A,E SUB L MOV A,D SBB H JNC FNDSPA4 INX D ;yes, move on to next one. PUSH B PUSH D MOV B,D MOV C,E CALL CKBITMAP;check it. RAR ;empty? JNC FNDSPA3 POP D ;nope, continue searching. POP B JMP FNDSPA1 ; ; Empty block found. Set it as used and return with (HL) ; pointing to it (true?). ; FNDSPA3 RAL ;reset byte. INR A ;and set bit 0. CALL STBMAP1 ;update bit map. POP H ;set return registers. POP D RET ; ; Free block was not found. If (BC) is not zero, then we have ; not checked all of the disk space. ; FNDSPA4 MOV A,C ORA B JNZ FNDSPA1 LXI H,0 ;set 'not found' status. RET ; ; Move a complete fcb entry into the directory and write it. ; FCBSET MVI C,0 MVI E,32 ;length of each entry. ; ; Move (E) bytes from the fcb pointed to by (PARAMS) into ; fcb in directory starting at relative byte (C). This updated ; directory buffer is then written to the disk. ; UPDATE PUSH D MVI B,0 ;set (BC) to relative byte position. LHLD PARAMS ;get address of fcb. DAD B ;compute starting byte. XCHG CALL FCB2HL ;get address of fcb to update in directory. POP B ;set (C) to number of bytes to change. CALL DE2HL UPDATE1 CALL TRKSEC ;determine the track and sector affected. JMP DIRWRITE ;then write this sector out. ; ; Routine to change the name of all files on the disk with a ; specified name. The fcb contains the current name as the ; first 12 characters and the new name 16 bytes into the fcb. ; CHGNAMES:CALL CHKWPRT ;check for a write protected disk. MVI C,12 ;match first 12 bytes of fcb only. CALL FINDFST ;get first name. LHLD PARAMS ;get address of fcb. MOV A,M ;get user number. LXI D,16 ;move over to desired name. DAD D MOV M,A ;keep same user number. CHGNAM1 CALL CKFILPOS;any matching file found? RZ ;no, we must be done. CALL CHKROFL ;check for read only file. MVI C,16 ;start 16 bytes into fcb. MVI E,12 ;and update the first 12 bytes of directory. CALL UPDATE CALL FINDNXT ;get te next file name. JMP CHGNAM1 ;and continue. ; ; Update a files attributes. The procedure is to search for ; every file with the same name as shown in fcb (ignoring bit 7) ; and then to update it (which includes bit 7). No other changes ; are made. ; SAVEATTR:MVI C,12 ;match first 12 bytes. CALL FINDFST ;look for first filename. SAVATR1 CALL CKFILPOS;was one found? RZ ;nope, we must be done. MVI C,0 ;yes, update the first 12 bytes now. MVI E,12 CALL UPDATE ;update filename and write directory. CALL FINDNXT ;and get the next file. JMP SAVATR1 ;then continue until done. ; ; Open a file (name specified in fcb). ; OPENIT MVI C,15 ;compare the first 15 bytes. CALL FINDFST ;get the first one in directory. CALL CKFILPOS;any at all? RZ OPENIT1 CALL SETEXT ;point to extent byte within users fcb. MOV A,M ;and get it. PUSH PSW ;save it and address. PUSH H CALL FCB2HL ;point to fcb in directory. XCHG LHLD PARAMS ;this is the users copy. MVI C,32 ;move it into users space. PUSH D CALL DE2HL CALL SETS2B7 ;set bit 7 in 's2' byte (unmodified). POP D ;now get the extent byte from this fcb. LXI H,12 DAD D MOV C,M ;into (C). LXI H,15 ;now get the record count byte into (B). DAD D MOV B,M POP H ;keep the same extent as the user had originally. POP PSW MOV M,A MOV A,C ;is it the same as in the directory fcb? CMP M MOV A,B ;if yes, then use the same record count. JZ OPENIT2 MVI A,0 ;if the user specified an extent greater than JC OPENIT2 ;the one in the directory, then set record count to 0. MVI A,128 ;otherwise set to maximum. OPENIT2 LHLD PARAMS ;set record count in users fcb to (A). LXI D,15 DAD D ;compute relative position. MOV M,A ;and set the record count. RET ; ; Move two bytes from (DE) to (HL) if (and only if) (HL) ; point to a zero value (16 bit). ; Return with zero flag set it (DE) was moved. Registers (DE) ; and (HL) are not changed. However (A) is. ; MOVEWORD:MOV A,M ;check for a zero word. INX H ORA M ;both bytes zero? DCX H RNZ ;nope, just return. LDAX D ;yes, move two bytes from (DE) into MOV M,A ;this zero space. INX D INX H LDAX D MOV M,A DCX D ;don't disturb these registers. DCX H RET ; ; Get here to close a file specified by (fcb). ; CLOSEIT XRA A ;clear status and file position bytes. STA STATUS STA FILEPOS STA FILEPOS+1 CALL GETWPRT ;get write protect bit for this drive. RNZ ;just return if it is set. CALL GETS2 ;else get the 's2' byte. ANI 80H ;and look at bit 7 (file unmodified?). RNZ ;just return if set. MVI C,15 ;else look up this file in directory. CALL FINDFST CALL CKFILPOS;was it found? RZ ;just return if not. LXI B,16 ;set (HL) pointing to records used section. CALL FCB2HL DAD B XCHG LHLD PARAMS ;do the same for users specified fcb. DAD B MVI C,16 ;this many bytes are present in this extent. CLOSEIT1:LDA BIGDISK ;8 or 16 bit record numbers? ORA A JZ CLOSEIT4 MOV A,M ;just 8 bit. Get one from users fcb. ORA A LDAX D ;now get one from directory fcb. JNZ CLOSEIT2 MOV M,A ;users byte was zero. Update from directory. CLOSEIT2:ORA A JNZ CLOSEIT3 MOV A,M ;directories byte was zero, update from users fcb. STAX D CLOSEIT3:CMP M ;if neither one of these bytes were zero, JNZ CLOSEIT7 ;then close error if they are not the same. JMP CLOSEIT5 ;ok so far, get to next byte in fcbs. CLOSEIT4:CALL MOVEWORD;update users fcb if it is zero. XCHG CALL MOVEWORD;update directories fcb if it is zero. XCHG LDAX D ;if these two values are no different, CMP M ;then a close error occured. JNZ CLOSEIT7 INX D ;check second byte. INX H LDAX D CMP M JNZ CLOSEIT7 DCR C ;remember 16 bit values. CLOSEIT5:INX D ;bump to next item in table. INX H DCR C ;there are 16 entries only. JNZ CLOSEIT1;continue if more to do. LXI B,0FFECH;backup 20 places (extent byte). DAD B XCHG DAD B LDAX D CMP M ;directory's extent already greater than the JC CLOSEIT6 ;users extent? MOV M,A ;no, update directory extent. LXI B,3 ;and update the record count byte in DAD B ;directories fcb. XCHG DAD B MOV A,M ;get from user. STAX D ;and put in directory. CLOSEIT6:MVI A,0FFH ;set 'was open and is now closed' byte. STA CLOSEFLG JMP UPDATE1 ;update the directory now. CLOSEIT7:LXI H,STATUS;set return status and then return. DCR M RET ; ; Routine to get the next empty space in the directory. It ; will then be cleared for use. ; GETEMPTY:CALL CHKWPRT ;make sure disk is not write protected. LHLD PARAMS ;save current parameters (fcb). PUSH H LXI H,EMPTYFCB;use special one for empty space. SHLD PARAMS MVI C,1 ;search for first empty spot in directory. CALL FINDFST ;(* only check first byte *) CALL CKFILPOS;none? POP H SHLD PARAMS ;restore original fcb address. RZ ;return if no more space. XCHG LXI H,15 ;point to number of records for this file. DAD D MVI C,17 ;and clear all of this space. XRA A GETMT1 MOV M,A INX H DCR C JNZ GETMT1 LXI H,13 ;clear the 's1' byte also. DAD D MOV M,A CALL CHKNMBR ;keep (SCRATCH1) within bounds. CALL FCBSET ;write out this fcb entry to directory. JMP SETS2B7 ;set 's2' byte bit 7 (unmodified at present). ; ; Routine to close the current extent and open the next one ; for reading. ; GETNEXT XRA A STA CLOSEFLG;clear close flag. CALL CLOSEIT ;close this extent. CALL CKFILPOS RZ ;not there??? LHLD PARAMS ;get extent byte. LXI B,12 DAD B MOV A,M ;and increment it. INR A ANI 1FH ;keep within range 0-31. MOV M,A JZ GTNEXT1 ;overflow? MOV B,A ;mask extent byte. LDA EXTMASK ANA B LXI H,CLOSEFLG;check close flag (0ffh is ok). ANA M JZ GTNEXT2 ;if zero, we must read in next extent. JMP GTNEXT3 ;else, it is already in memory. GTNEXT1 LXI B,2 ;Point to the 's2' byte. DAD B INR M ;and bump it. MOV A,M ;too many extents? ANI 0FH JZ GTNEXT5 ;yes, set error code. ; ; Get here to open the next extent. ; GTNEXT2 MVI C,15 ;set to check first 15 bytes of fcb. CALL FINDFST ;find the first one. CALL CKFILPOS;none available? JNZ GTNEXT3 LDA RDWRTFLG;no extent present. Can we open an empty one? INR A ;0ffh means reading (so not possible). JZ GTNEXT5 ;or an error. CALL GETEMPTY;we are writing, get an empty entry. CALL CKFILPOS;none? JZ GTNEXT5 ;error if true. JMP GTNEXT4 ;else we are almost done. GTNEXT3 CALL OPENIT1 ;open this extent. GTNEXT4 CALL STRDATA ;move in updated data (rec #, extent #, etc.) XRA A ;clear status and return. JMP SETSTAT ; ; Error in extending the file. Too many extents were needed ; or not enough space on the disk. ; GTNEXT5 CALL IOERR1 ;set error code, clear bit 7 of 's2' JMP SETS2B7 ;so this is not written on a close. ; ; Read a sequential file. ; RDSEQ MVI A,1 ;set sequential access mode. STA MODE RDSEQ1 MVI A,0FFH ;don't allow reading unwritten space. STA RDWRTFLG CALL STRDATA ;put rec# and ext# into fcb. LDA SAVNREC ;get next record to read. LXI H,SAVNXT;get number of records in extent. CMP M ;within this extent? JC RDSEQ2 CPI 128 ;no. Is this extent fully used? JNZ RDSEQ3 ;no. End-of-file. CALL GETNEXT ;yes, open the next one. XRA A ;reset next record to read. STA SAVNREC LDA STATUS ;check on open, successful? ORA A JNZ RDSEQ3 ;no, error. RDSEQ2 CALL COMBLK ;ok. compute block number to read. CALL CHKBLK ;check it. Within bounds? JZ RDSEQ3 ;no, error. CALL LOGICAL ;convert (BLKNMBR) to logical sector (128 byte). CALL TRKSEC1 ;set the track and sector for this block #. CALL DOREAD ;and read it. JMP SETNREC ;and set the next record to be accessed. ; ; Read error occured. Set status and return. ; RDSEQ3 JMP IOERR1 ; ; Write the next sequential record. ; WTSEQ MVI A,1 ;set sequential access mode. STA MODE WTSEQ1 MVI A,0 ;allow an addition empty extent to be opened. STA RDWRTFLG CALL CHKWPRT ;check write protect status. LHLD PARAMS CALL CKROF1 ;check for read only file, (HL) already set to fcb. CALL STRDATA ;put updated data into fcb. LDA SAVNREC ;get record number to write. CPI 128 ;within range? JNC IOERR1 ;no, error(?). CALL COMBLK ;compute block number. CALL CHKBLK ;check number. MVI C,0 ;is there one to write to? JNZ WTSEQ6 ;yes, go do it. CALL GETBLOCK;get next block number within fcb to use. STA RELBLOCK;and save. LXI B,0 ;start looking for space from the start ORA A ;if none allocated as yet. JZ WTSEQ2 MOV C,A ;extract previous block number from fcb DCX B ;so we can be closest to it. CALL EXTBLK MOV B,H MOV C,L WTSEQ2 CALL FNDSPACE;find the next empty block nearest number (BC). MOV A,L ;check for a zero number. ORA H JNZ WTSEQ3 MVI A,2 ;no more space? JMP SETSTAT WTSEQ3 SHLD BLKNMBR ;save block number to access. XCHG ;put block number into (DE). LHLD PARAMS ;now we must update the fcb for this LXI B,16 ;newly allocated block. DAD B LDA BIGDISK ;8 or 16 bit block numbers? ORA A LDA RELBLOCK ;(* update this entry *) JZ WTSEQ4 ;zero means 16 bit ones. CALL ADDA2HL ;(HL)=(HL)+(A) MOV M,E ;store new block number. JMP WTSEQ5 WTSEQ4 MOV C,A ;compute spot in this 16 bit table. MVI B,0 DAD B DAD B MOV M,E ;stuff block number (DE) there. INX H MOV M,D WTSEQ5 MVI C,2 ;set (C) to indicate writing to un-used disk space. WTSEQ6 LDA STATUS ;are we ok so far? ORA A RNZ PUSH B ;yes, save write flag for bios (register C). CALL LOGICAL ;convert (BLKNMBR) over to loical sectors. LDA MODE ;get access mode flag (1=sequential, DCR A ;0=random, 2=special?). DCR A JNZ WTSEQ9 ; ; Special random i/o from function #40. Maybe for M/PM, but the ; current block, if it has not been written to, will be zeroed ; out and then written (reason?). ; POP B PUSH B MOV A,C ;get write status flag (2=writing unused space). DCR A DCR A JNZ WTSEQ9 PUSH H LHLD DIRBUF ;zero out the directory buffer. MOV D,A ;note that (A) is zero here. WTSEQ7 MOV M,A INX H INR D ;do 128 bytes. JP WTSEQ7 CALL DIRDMA ;tell the bios the dma address for directory access. LHLD LOGSECT ;get sector that starts current block. MVI C,2 ;set 'writing to unused space' flag. WTSEQ8 SHLD BLKNMBR ;save sector to write. PUSH B CALL TRKSEC1 ;determine its track and sector numbers. POP B CALL DOWRITE ;now write out 128 bytes of zeros. LHLD BLKNMBR ;get sector number. MVI C,0 ;set normal write flag. LDA BLKMASK ;determine if we have written the entire MOV B,A ;physical block. ANA L CMP B INX H ;prepare for the next one. JNZ WTSEQ8 ;continue until (BLKMASK+1) sectors written. POP H ;reset next sector number. SHLD BLKNMBR CALL DEFDMA ;and reset dma address. ; ; Normal disk write. Set the desired track and sector then ; do the actual write. ; WTSEQ9 CALL TRKSEC1 ;determine track and sector for this write. POP B ;get write status flag. PUSH B CALL DOWRITE ;and write this out. POP B LDA SAVNREC ;get number of records in file. LXI H,SAVNXT;get last record written. CMP M JC WTSEQ10 MOV M,A ;we have to update record count. INR M MVI C,2 ; ;* This area has been patched to correct disk update problem ;* when using blocking and de-blocking in the BIOS. ; WTSEQ10 NOP ;was 'dcr c' NOP ;was 'dcr c' LXI H,0 ;was 'jnz wtseq99' ; ; * End of patch. ; PUSH PSW CALL GETS2 ;set 'extent written to' flag. ANI 7FH ;(* clear bit 7 *) MOV M,A POP PSW ;get record count for this extent. WTSEQ99 CPI 127 ;is it full? JNZ WTSEQ12 LDA MODE ;yes, are we in sequential mode? CPI 1 JNZ WTSEQ12 CALL SETNREC ;yes, set next record number. CALL GETNEXT ;and get next empty space in directory. LXI H,STATUS;ok? MOV A,M ORA A JNZ WTSEQ11 DCR A ;yes, set record count to -1. STA SAVNREC WTSEQ11 MVI M,0 ;clear status. WTSEQ12 JMP SETNREC ;set next record to access. ; ; For random i/o, set the fcb for the desired record number ; based on the 'r0,r1,r2' bytes. These bytes in the fcb are ; used as follows: ; ; fcb+35 fcb+34 fcb+33 ; | 'r-2' | 'r-1' | 'r-0' | ; |7 0 | 7 0 | 7 0| ; |0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0| ; | overflow | | extra | extent | record # | ; | ______________| |_extent|__number___|_____________| ; also 's2' ; ; On entry, register (C) contains 0ffh if this is a read ; and thus we can not access unwritten disk space. Otherwise, ; another extent will be opened (for writing) if required. ; POSITION:XRA A ;set random i/o flag. STA MODE ; ; Special entry (function #40). M/PM ? ; POSITN1 PUSH B ;save read/write flag. LHLD PARAMS ;get address of fcb. XCHG LXI H,33 ;now get byte 'r0'. DAD D MOV A,M ANI 7FH ;keep bits 0-6 for the record number to access. PUSH PSW MOV A,M ;now get bit 7 of 'r0' and bits 0-3 of 'r1'. RAL INX H MOV A,M RAL ANI 1FH ;and save this in bits 0-4 of (C). MOV C,A ;this is the extent byte. MOV A,M ;now get the extra extent byte. RAR RAR RAR RAR ANI 0FH MOV B,A ;and save it in (B). POP PSW ;get record number back to (A). INX H ;check overflow byte 'r2'. MOV L,M INR L DCR L MVI L,6 ;prepare for error. JNZ POSITN5 ;out of disk space error. LXI H,32 ;store record number into fcb. DAD D MOV M,A LXI H,12 ;and now check the extent byte. DAD D MOV A,C SUB M ;same extent as before? JNZ POSITN2 LXI H,14 ;yes, check extra extent byte 's2' also. DAD D MOV A,B SUB M ANI 7FH JZ POSITN3;same, we are almost done then. ; ; Get here when another extent is required. ; POSITN2 PUSH B PUSH D CALL CLOSEIT ;close current extent. POP D POP B MVI L,3 ;prepare for error. LDA STATUS INR A JZ POSITN4 ;close error. LXI H,12 ;put desired extent into fcb now. DAD D MOV M,C LXI H,14 ;and store extra extent byte 's2'. DAD D MOV M,B CALL OPENIT ;try and get this extent. LDA STATUS ;was it there? INR A JNZ POSITN3 POP B ;no. can we create a new one (writing?). PUSH B MVI L,4 ;prepare for error. INR C JZ POSITN4 ;nope, reading unwritten space error. CALL GETEMPTY;yes we can, try to find space. MVI L,5 ;prepare for error. LDA STATUS INR A JZ POSITN4 ;out of space? ; ; Normal return location. Clear error code and return. ; POSITN3 POP B ;restore stack. XRA A ;and clear error code byte. JMP SETSTAT ; ; Error. Set the 's2' byte to indicate this (why?). ; POSITN4 PUSH H CALL GETS2 MVI M,0C0H POP H ; ; Return with error code (presently in L). ; POSITN5 POP B MOV A,L ;get error code. STA STATUS JMP SETS2B7 ; ; Read a random record. ; READRAN MVI C,0FFH ;set 'read' status. CALL POSITION;position the file to proper record. CZ RDSEQ1 ;and read it as usual (if no errors). RET ; ; Write to a random record. ; WRITERAN:MVI C,0 ;set 'writing' flag. CALL POSITION;position the file to proper record. CZ WTSEQ1 ;and write as usual (if no errors). RET ; ; Compute the random record number. Enter with (HL) pointing ; to a fcb an (DE) contains a relative location of a record ; number. On exit, (C) contains the 'r0' byte, (B) the 'r1' ; byte, and (A) the 'r2' byte. ; ; On return, the zero flag is set if the record is within ; bounds. Otherwise, an overflow occured. ; COMPRAND:XCHG ;save fcb pointer in (DE). DAD D ;compute relative position of record #. MOV C,M ;get record number into (BC). MVI B,0 LXI H,12 ;now get extent. DAD D MOV A,M ;compute (BC)=(record #)+(extent)*128. RRC ;move lower bit into bit 7. ANI 80H ;and ignore all other bits. ADD C ;add to our record number. MOV C,A MVI A,0 ;take care of any carry. ADC B MOV B,A MOV A,M ;now get the upper bits of extent into RRC ;bit positions 0-3. ANI 0FH ;and ignore all others. ADD B ;add this in to 'r1' byte. MOV B,A LXI H,14 ;get the 's2' byte (extra extent). DAD D MOV A,M ADD A ;and shift it left 4 bits (bits 4-7). ADD A ADD A ADD A PUSH PSW ;save carry flag (bit 0 of flag byte). ADD B ;now add extra extent into 'r1'. MOV B,A PUSH PSW ;and save carry (overflow byte 'r2'). POP H ;bit 0 of (L) is the overflow indicator. MOV A,L POP H ;and same for first carry flag. ORA L ;either one of these set? ANI 01H ;only check the carry flags. RET ; ; Routine to setup the fcb (bytes 'r0', 'r1', 'r2') to ; reflect the last record used for a random (or other) file. ; This reads the directory and looks at all extents computing ; the largerst record number for each and keeping the maximum ; value only. Then 'r0', 'r1', and 'r2' will reflect this ; maximum record number. This is used to compute the space used ; by a random file. ; RANSIZE MVI C,12 ;look thru directory for first entry with CALL FINDFST ;this name. LHLD PARAMS ;zero out the 'r0, r1, r2' bytes. LXI D,33 DAD D PUSH H MOV M,D ;note that (D)=0. INX H MOV M,D INX H MOV M,D RANSIZ1 CALL CKFILPOS;is there an extent to process? JZ RANSIZ3 ;no, we are done. CALL FCB2HL ;set (HL) pointing to proper fcb in dir. LXI D,15 ;point to last record in extent. CALL COMPRAND;and compute random parameters. POP H PUSH H ;now check these values against those MOV E,A ;already in fcb. MOV A,C ;the carry flag will be set if those SUB M ;in the fcb represent a larger size than INX H ;this extent does. MOV A,B SBB M INX H MOV A,E SBB M JC RANSIZ2 MOV M,E ;we found a larger (in size) extent. DCX H ;stuff these values into fcb. MOV M,B DCX H MOV M,C RANSIZ2 CALL FINDNXT ;now get the next extent. JMP RANSIZ1 ;continue til all done. RANSIZ3 POP H ;we are done, restore the stack and RET ;return. ; ; Function to return the random record position of a given ; file which has been read in sequential mode up to now. ; SETRAN LHLD PARAMS ;point to fcb. LXI D,32 ;and to last used record. CALL COMPRAND;compute random position. LXI H,33 ;now stuff these values into fcb. DAD D MOV M,C ;move 'r0'. INX H MOV M,B ;and 'r1'. INX H MOV M,A ;and lastly 'r2'. RET ; ; This routine select the drive specified in (ACTIVE) and ; update the login vector and bitmap table if this drive was ; not already active. ; LOGINDRV:LHLD LOGIN ;get the login vector. LDA ACTIVE ;get the default drive. MOV C,A CALL SHIFTR ;position active bit for this drive PUSH H ;into bit 0. XCHG CALL SELECT ;select this drive. POP H CZ SLCTERR ;valid drive? MOV A,L ;is this a newly activated drive? RAR RC LHLD LOGIN ;yes, update the login vector. MOV C,L MOV B,H CALL SETBIT SHLD LOGIN ;and save. JMP BITMAP ;now update the bitmap. ; ; Function to set the active disk number. ; SETDSK LDA EPARAM ;get parameter passed and see if this LXI H,ACTIVE;represents a change in drives. CMP M RZ MOV M,A ;yes it does, log it in. JMP LOGINDRV ; ; This is the 'auto disk select' routine. The firsst byte ; of the fcb is examined for a drive specification. If non ; zero then the drive will be selected and loged in. ; AUTOSEL MVI A,0FFH ;say 'auto-select activated'. STA AUTO LHLD PARAMS ;get drive specified. MOV A,M ANI 1FH ;look at lower 5 bits. DCR A ;adjust for (1=A, 2=B) etc. STA EPARAM ;and save for the select routine. CPI 1EH ;check for 'no change' condition. JNC AUTOSL1 ;yes, don't change. LDA ACTIVE ;we must change, save currently active STA OLDDRV ;drive. MOV A,M ;and save first byte of fcb also. STA AUTOFLAG;this must be non-zero. ANI 0E0H ;whats this for (bits 6,7 are used for MOV M,A ;something)? CALL SETDSK ;select and log in this drive. AUTOSL1 LDA USERNO ;move user number into fcb. LHLD PARAMS ;(* upper half of first byte *) ORA M MOV M,A RET ;and return (all done). ; ; Function to return the current cp/m version number. ; GETVER MVI A,022h ;version 2.2 JMP SETSTAT ; ; Function to reset the disk system. ; RSTDSK LXI H,0 ;clear write protect status and log SHLD WRTPRT ;in vector. SHLD LOGIN XRA A ;select drive 'A'. STA ACTIVE LXI H,TBUFF ;setup default dma address. SHLD USERDMA CALL DEFDMA JMP LOGINDRV;now log in drive 'A'. ; ; Function to open a specified file. ; OPENFIL CALL CLEARS2 ;clear 's2' byte. CALL AUTOSEL ;select proper disk. JMP OPENIT ;and open the file. ; ; Function to close a specified file. ; CLOSEFIL:CALL AUTOSEL ;select proper disk. JMP CLOSEIT ;and close the file. ; ; Function to return the first occurence of a specified file ; name. If the first byte of the fcb is '?' then the name will ; not be checked (get the first entry no matter what). ; GETFST MVI C,0 ;prepare for special search. XCHG MOV A,M ;is first byte a '?'? CPI '?' JZ GETFST1 ;yes, just get very first entry (zero length match). CALL SETEXT ;get the extension byte from fcb. MOV A,M ;is it '?'? if yes, then we want CPI '?' ;an entry with a specific 's2' byte. CNZ CLEARS2 ;otherwise, look for a zero 's2' byte. CALL AUTOSEL ;select proper drive. MVI C,15 ;compare bytes 0-14 in fcb (12&13 excluded). GETFST1 CALL FINDFST ;find an entry and then move it into JMP MOVEDIR ;the users dma space. ; ; Function to return the next occurence of a file name. ; GETNXT LHLD SAVEFCB ;restore pointers. note that no SHLD PARAMS ;other dbos calls are allowed. CALL AUTOSEL ;no error will be returned, but the CALL FINDNXT ;results will be wrong. JMP MOVEDIR ; ; Function to delete a file by name. ; DELFILE CALL AUTOSEL ;select proper drive. CALL ERAFILE ;erase the file. JMP STSTATUS;set status and return. ; ; Function to execute a sequential read of the specified ; record number. ; READSEQ CALL AUTOSEL ;select proper drive then read. JMP RDSEQ ; ; Function to write the net sequential record. ; WRTSEQ CALL AUTOSEL ;select proper drive then write. JMP WTSEQ ; ; Create a file function. ; FCREATE CALL CLEARS2 ;clear the 's2' byte on all creates. CALL AUTOSEL ;select proper drive and get the next JMP GETEMPTY;empty directory space. ; ; Function to rename a file. ; RENFILE CALL AUTOSEL ;select proper drive and then switch CALL CHGNAMES;file names. JMP STSTATUS ; ; Function to return the login vector. ; GETLOG LHLD LOGIN JMP GETPRM1 ; ; Function to return the current disk assignment. ; GETCRNT LDA ACTIVE JMP SETSTAT ; ; Function to set the dma address. ; PUTDMA XCHG SHLD USERDMA ;save in our space and then get to JMP DEFDMA ;the bios with this also. ; ; Function to return the allocation vector. ; GETALOC LHLD ALOCVECT JMP GETPRM1 ; ; Function to return the read-only status vector. ; GETROV LHLD WRTPRT JMP GETPRM1 ; ; Function to set the file attributes (read-only, system). ; SETATTR CALL AUTOSEL ;select proper drive then save attributes. CALL SAVEATTR JMP STSTATUS ; ; Function to return the address of the disk parameter block ; for the current drive. ; GETPARM LHLD DISKPB GETPRM1 SHLD STATUS RET ; ; Function to get or set the user number. If (E) was (FF) ; then this is a request to return the current user number. ; Else set the user number from (E). ; GETUSER LDA EPARAM ;get parameter. CPI 0FFH ;get user number? JNZ SETUSER LDA USERNO ;yes, just do it. JMP SETSTAT SETUSER ANI 1FH ;no, we should set it instead. keep low STA USERNO ;bits (0-4) only. RET ; ; Function to read a random record from a file. ; RDRANDOM:CALL AUTOSEL ;select proper drive and read. JMP READRAN ; ; Function to compute the file size for random files. ; WTRANDOM:CALL AUTOSEL ;select proper drive and write. JMP WRITERAN ; ; Function to compute the size of a random file. ; FILESIZE:CALL AUTOSEL ;select proper drive and check file length JMP RANSIZE ; ; Function #37. This allows a program to log off any drives. ; On entry, set (DE) to contain a word with bits set for those ; drives that are to be logged off. The log-in vector and the ; write protect vector will be updated. This must be a M/PM ; special function. ; LOGOFF LHLD PARAMS ;get drives to log off. MOV A,L ;for each bit that is set, we want CMA ;to clear that bit in (LOGIN) MOV E,A ;and (WRTPRT). MOV A,H CMA LHLD LOGIN ;reset the login vector. ANA H MOV D,A MOV A,L ANA E MOV E,A LHLD WRTPRT XCHG SHLD LOGIN ;and save. MOV A,L ;now do the write protect vector. ANA E MOV L,A MOV A,H ANA D MOV H,A SHLD WRTPRT ;and save. all done. RET ; ; Get here to return to the user. ; GOBACK LDA AUTO ;was auto select activated? ORA A JZ GOBACK1 LHLD PARAMS ;yes, but was a change made? MVI M,0 ;(* reset first byte of fcb *) LDA AUTOFLAG ORA A JZ GOBACK1 MOV M,A ;yes, reset first byte properly. LDA OLDDRV ;and get the old drive and select it. STA EPARAM CALL SETDSK GOBACK1 LHLD USRSTACK;reset the users stack pointer. SPHL LHLD STATUS ;get return status. MOV A,L ;force version 1.4 compatability. MOV B,H RET ;and go back to user. ; ; Function #40. This is a special entry to do random i/o. ; For the case where we are writing to unused disk space, this ; space will be zeroed out first. This must be a M/PM special ; purpose function, because why would any normal program even ; care about the previous contents of a sector about to be ; written over. ; WTSPECL CALL AUTOSEL ;select proper drive. MVI A,2 ;use special write mode. STA MODE MVI C,0 ;set write indicator. CALL POSITN1 ;position the file. CZ WTSEQ1 ;and write (if no errors). RET ; ;************************************************************** ;* ;* BDOS data storage pool. ;* ;************************************************************** ; EMPTYFCB:DB 0E5H ;empty directory segment indicator. WRTPRT DW 0 ;write protect status for all 16 drives. LOGIN DW 0 ;drive active word (1 bit per drive). USERDMA DW 080H ;user's dma address (defaults to 80h). ; ; Scratch areas from parameter block. ; SCRATCH1:DW 0 ;relative position within dir segment for file (0-3). SCRATCH2:DW 0 ;last selected track number. SCRATCH3:DW 0 ;last selected sector number. ; ; Disk storage areas from parameter block. ; DIRBUF DW 0 ;address of directory buffer to use. DISKPB DW 0 ;contains address of disk parameter block. CHKVECT DW 0 ;address of check vector. ALOCVECT:DW 0 ;address of allocation vector (bit map). ; ; Parameter block returned from the bios. ; SECTORS DW 0 ;sectors per track from bios. BLKSHFT DB 0 ;block shift. BLKMASK DB 0 ;block mask. EXTMASK DB 0 ;extent mask. DSKSIZE DW 0 ;disk size from bios (number of blocks-1). DIRSIZE DW 0 ;directory size. ALLOC0 DW 0 ;storage for first bytes of bit map (dir space used). ALLOC1 DW 0 OFFSET DW 0 ;first usable track number. XLATE DW 0 ;sector translation table address. ; ; CLOSEFLG:DB 0 ;close flag (=0ffh is extent written ok). RDWRTFLG:DB 0 ;read/write flag (0ffh=read, 0=write). FNDSTAT DB 0 ;filename found status (0=found first entry). MODE DB 0 ;I/o mode select (0=random, 1=sequential, 2=special random). EPARAM DB 0 ;storage for register (E) on entry to bdos. RELBLOCK:DB 0 ;relative position within fcb of block number written. COUNTER DB 0 ;byte counter for directory name searches. SAVEFCB DW 0,0 ;save space for address of fcb (for directory searches). BIGDISK DB 0 ;if =0 then disk is > 256 blocks long. AUTO DB 0 ;if non-zero, then auto select activated. OLDDRV DB 0 ;on auto select, storage for previous drive. AUTOFLAG:DB 0 ;if non-zero, then auto select changed drives. SAVNXT DB 0 ;storage for next record number to access. SAVEXT DB 0 ;storage for extent number of file. SAVNREC DW 0 ;storage for number of records in file. BLKNMBR DW 0 ;block number (physical sector) used within a file or logical sector. LOGSECT DW 0 ;starting logical (128 byte) sector of block (physical sector). FCBPOS DB 0 ;relative position within buffer for fcb of file of interest. FILEPOS DW 0 ;files position within directory (0 to max entries -1). ; ; Disk directory buffer checksum bytes. One for each of the ; 16 possible drives. ; CKSUMTBL:DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; ; Extra space ? ; DB 0,0,0,0 ; ;************************************************************** ;* ;* B I O S J U M P T A B L E ;* ;************************************************************** ; BOOT JMP 0 ;NOTE WE USE FAKE DESTINATIONS WBOOT JMP 0 CONST JMP 0 CONIN JMP 0 CONOUT JMP 0 LIST JMP 0 PUNCH JMP 0 READER JMP 0 HOME JMP 0 SELDSK JMP 0 SETTRK JMP 0 SETSEC JMP 0 SETDMA JMP 0 READ JMP 0 WRITE JMP 0 PRSTAT JMP 0 SECTRN JMP 0 ; ;* ;****************** E N D O F C P / M ***************** ;*