| |
| ;************************************************************** |
| ;* |
| ;* 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 ***************** |
| ;* |
| |