name ('ldrdos') title Loader DOS for ATHENA 1.0 .z80 .tfcond .comment ~ ********************************************************************************** ********************************************************************************** *** ********** *********** ********** ******** ********** *** *********** ************ *********** ********** *********** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ************ *** *** *** *** ********* *** *** *** *********** *** *** *** *** ********* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ************ *********** *** *** *********** ********** *********** ************ ********** *** *** ********** ******** ********** ********************************************************************************** ********************************************************************************** L D R D O S . M A C Version 1.10 Creation Date: 18 January 1983 MODULE: 0.20 Date Last Revised: 6 October 1983 Loader DOS for ATHENA 1.0 Edit History: 18 Jan 83 - Created by Mike Pinkston. 20 Jan 83 - Cleaned things up a bit. 6 Oct 83 - Stripped out all of the useless routines and junk. ~ subttl Global/External points of entry and reference page 60 GLOBAL LDOS EXTERNAL WBOOTF, CONIF, CONOF EXTERNAL HOMF, SELF, TRKF, SECF, DMAF, DRDF, SECTRN subttl MODULE EQUATE STATEMENTS page BOOTV EQU 0100H ; Vector to start of the LOADER module. IOBYTE EQU 0003H ; System I/O device assignment. TBUFF EQU 0080H ; Default DMA buffer location. FALSE EQU 0 ; TRUE EQU -1 ; CR EQU 0DH ; LF EQU 0AH ; TAB EQU 09H ; Tab BACKSP EQU 08H ; Backspace CTRLC EQU 03H ; Control-C CTRLE EQU 05H ; Control-E CTRLP EQU 10H ; Control-P CTRLQ EQU 11H ; Control-Q CTRLR EQU 12H ; Control-R CTRLS EQU 13H ; Control-S CTRLU EQU 15H ; Control-U CTRLX EQU 18H ; Control-X DELDAT EQU 0E5H ; Deleted Data Byte subttl START OF LDOS page ; ldos entry point. data enters as follows: ; C = command ; DE = address or 16 bit data word ; E = 8 bit data word ; ; return from ldos data is as follows: ; A = status or value ; HL = 16 bit value or address ; ; ldos entry saves input data as follows: ; ; DE -> FCB 0 -> OLDDSK ; E -> IDATA 0 -> OUT1 ; 0 -> ODATA sets return to BEXIT on stack ; SP -> OLDSP ; ; saves input data and jumps to command routine LDOS:: LD (FCB),DE ; LD A,E ; move data byte into A LD (IDATA),A ; save it LD HL,0 ; zero out HL LD (ODATA),HL ; save this for the return LD (OLDSP),SP ; save this for the return also LD SP,USRCOD ; point to LDOS stack XOR A ; zero out a LD (OLDDSK),A ; save this as the old disk LD (OUT1),A ; and at OUT1 for disk relog on exit LD HL,BEXIT ; get the return point address PUSH HL ; and stuff it on the stack for exiting LD A,C ; move the command into A CP 025H ; Max cmds +1 - is it too big? RET NC ; return if so LD HL,DISTBL ; point to the dispatch table LD B,0 ; zero out D ADD HL,BC ; add in the dispatch table ADD HL,BC ; once more - over two bytes LD C,(HL) ; pull the jump address INC HL ; into LD B,(HL) ; DE PUSH BC ; LD C,E ; RET ; subttl COMMAND DISPATCH TABLE page ; command dispatch table DISTBL: DEFW WBOOTF## ; 0: System reset DEFW Null.Routine ; 1: Console input DEFW WRTCON ; 2: Console output DEFW Null.Routine ; 3: Reader input DEFW Null.Routine ; 4: Punch output DEFW Null.Routine ; 5: List output DEFW Null.Routine ; 6: Direct console I/O DEFW Null.Routine ; 7: Get I/O Byte DEFW Null.Routine ; 8: Set I/O Byte DEFW PRNBUF ; 9: Print string DEFW Null.Routine ; 10: Read console buffer DEFW Null.Routine ; 11: Get console status DEFW Null.Routine ; 12: Return version number DEFW RESET ; 13: Reset disk system DEFW Null.Routine ; 14: Select disk DEFW OPEN ; 15: Open file DEFW Null.Routine ; 16: Close file DEFW Null.Routine ; 17: Search for first DEFW Null.Routine ; 18: Search for next DEFW Null.Routine ; 19: Delete file DEFW READ ; 20: Read sequential DEFW Null.Routine ; 21: Write sequential DEFW Null.Routine ; 22: Make file DEFW Null.Routine ; 23: Rename file DEFW Null.Routine ; 24: Return login vector DEFW Null.Routine ; 25: Return current disk DEFW DMASET ; 26: Set DMA address DEFW Null.Routine ; 27: Get addr (alloc) DEFW Null.Routine ; 28: Write protect disk DEFW Null.Routine ; 29: Get R/O vector DEFW Null.Routine ; 30: Set file attributes DEFW Null.Routine ; 31: Get addr (disk parms) DEFW Null.Routine ; 32: Set/Get user code DEFW Null.Routine ; 33: Read random DEFW Null.Routine ; 34: Write random DEFW Null.Routine ; 35: Compute file size DEFW Null.Routine ; 36: Set random record DEFW Null.Routine ; 37: Reset drive Null.Routine: RET subttl ERROR MESSAGE SECTION page ; Print the permanent error message PERERR: LD HL,PERMSG ; point to bad sector message CALL DOSERR ; print out with disk and return CP CTRLC ; return with a char - is it ctrlc? JP Z,BOOTV ; if so go reboot RET ; otherwise just go back ; Print the select error message then die SELERR: LD HL,SELMSG ; 'Select' msg JR BOMB ; say too bad then abort ; Print out the error message then reboot BOMB: CALL DOSERR ; Print LDOS err on [dsk #] JP BOOTV ; Reboot system subttl ERROR MESSAGES page ; Error messages DOSMSG: DEFB 'Bdos Err On ' DSKCH: DEFB ' : $' PERMSG: DEFB 'Bad Sector$' SELMSG: DEFB 'Select$' ; Print LDOS error msg with disk # DOSERR: PUSH HL ; save the message pointer CALL CRLF ; Turn up a new line LD A,(CURDSK) ; get the current disk ADD A,'A' ; add in the ascii bias LD (DSKCH),A ; Form drive ID char LD BC,DOSMSG ; Print the CALL PRINT ; 'Bdos Err on' msg POP BC ; restore the message pointer CALL PRINT ; Print specific error message JP CONIF## ; subttl OUTPUT CHARACTER TO CONSOLE page ; Output char in C to console CONOUT: PUSH BC ; save it again CALL CONOF## ; and print the character on the console POP BC ; recover once more LD A,C ; put the byte into A LD HL,COLUMN ; point to the buffer column counter INC (HL) ; Increment column CP ' ' ; is the character a space? RET NC ; return if greater - must be control DEC (HL) ; Decrement column LD A,(HL) ; is column number = 0? OR A ; RET Z ; return if it is LD A,C ; move the char into C CP BACKSP ; was it a backspace? JR NZ,CONOU2 ; if not jump over DEC (HL) ; Decrement column RET ; and return CONOU2: CP LF ; was it a linefeed? RET NZ ; return if not again LD (HL),0 ; Set column to 0 RET ; and return subttl LDOS FUNCTION 2 page ; LDOS function 2: write to the system console char in C WRTCON: LD A,C ; which puts it into A CP TAB ; check for tab JR NZ,CONOUT ; if not jump and print TABOUT: LD C,' ' ; Space gets printed to expand tabs CALL CONOUT ; to LD A,(COLUMN) ; next AND 07H ; tab JR NZ,TABOUT ; stop RET ; Return ; Print CR/LF at console CRLF: LD C,CR ; get a cr CALL CONOUT ; and print it out LD C,LF ; followed by a lf JP CONOUT ; which goes out too subttl PRINT STRING page ; LDOS function 9: Print console buffer until '$' ; entry string address in DE PRNBUF: LD BC,(FCB) ; ; Print string at (BC) until '$' with TAB expansion PRINT: LD A,(BC) ; get the byte at (BC) CP '$' ; is it the end mark? RET Z ; if so we are done INC BC ; otherwise bump the pointer PUSH BC ; and save it in the stack LD C,A ; get the character into C CALL WRTCON ; write it out expanding tabs POP BC ; recover the pointer JR PRINT ; and loop til we hit the stop JR1: LD A,01H ; get a 1 for return GOBAK: LD (ODATA),A ; save result in return info byte RET ; and go back to caller subttl MISCELLANEOUS ROUTINES page ; routine to move C bytes DE to HL MOVE: INC C ; add 1 to move count to start loop MOVLOP: DEC C ; decrement the loop count RET Z ; return if we are done LD A,(DE) ; otherwise get the byte at (DE) LD (HL),A ; and put it at (HL) INC DE ; bump the source pointer INC HL ; and the destination pointer JR MOVLOP ; and loop til you are done subttl DISK IDENTIFICATION DATA SETUP page ; routine to set up disk identification data for access DISKID: LD A,(CURDSK) ; get the current logged disk LD C,A ; and put it into C CALL SELF## ; Select disk - HL comes back with DISP parm LD A,H ; move H into A for the zero test OR L ; or in L - zero says select error RET Z ; so return to LOGDSK with error LD E,(HL) ; otherwise move INC HL ; translation address LD D,(HL) ; into DE INC HL ; bump once more LD (SCRT0),HL ; and save this address at scratch 0 INC HL ; move the pointer INC HL ; two doors down LD (SCRT1),HL ; and save this address at scratch 1 INC HL ; move over INC HL ; two more LD (SCRT2),HL ; saving the result at scratch 2 INC HL ; two more INC HL ; for good measure EX DE,HL ; LD (TRANS),HL ; save the original disp parm header LD HL,DTEMP ; point to the temp DMA area LD C,8 ; Bytes to move CALL MOVE ; Move C bytes DIRBUF to DTEMP (dont know why) LD HL,(DPB) ; get the disk parm block address EX DE,HL ; LD HL,DPBLK ; LD C,15 ; Bytes to move CALL MOVE ; Move disk data into storage area LD HL,(DSIZE) ; get the size of this disk LD A,H ; high byte zero says 1024 byte blocks LD HL,BSIZE ; point to block size byte LD (HL),0FFH ; put in an FF OR A ; set flags JR Z,JRFF ; Return FF if blocks are 1024 bytes LD (HL),0 ; otherwise set in a ZERO JRFF: LD A,0FFH ; and get an FF into A OR A ; set flags RET ; and return subttl HOME DISK DRIVE page ; routine to home the selected disk and reset data words HOMDSK: CALL HOMF## ; home the selected drive LD BC,(TOFS) XOR A ; get a zero LD HL,(SCRT1) ; point to the current track storage LD (HL),A ; put in the zero INC HL ; point to the low half LD (HL),A ; and zero this as well LD HL,(SCRT2) ; point to starting sector count for this track LD (HL),A ; zero it INC HL ; bump pointer LD (HL),A ; both halves of this word RET ; and return subttl READ & WRITE SECTOR page ; routine reads indicated sector and checks status RDSEC: CALL DRDF## ; Read sector JR PERCHK ; jump to check error and return ; routine to write indicated sector and check result PERCHK: OR A ; set flags RET Z ; return if all is well JP PERERR ; HL=Permanent err sub addr subttl GET NEXT DIRECTORY SECTOR LOCATION page ; routine to get the next directory sector location for reading GETDS: LD HL,(COUNT) ; get the directory count LD C,02H ; ready to slide it over 2 bits right CALL SPINHL ; spin hl into position - sector number LD (GROUP),HL ; save a copy at group LD (LDIRPNT),HL ; and another at directory pointer GETD1: LD HL,GROUP ; point to directory sector number LD C,(HL) ; pull it INC HL ; into LD B,(HL) ; BC LD HL,(SCRT2) ; pull current sector address LD E,(HL) ; pull (scratch2) 16 bits wide INC HL ; into LD D,(HL) ; DE LD HL,(SCRT1) ; pull current track address LD A,(HL) ; pull (scratch1) into A INC HL ; into LD H,(HL) ; HL LD L,A ; 16 bits at (scratch1) into HL ; next two blocks set the track and starting sector number for the track ; so the directory disk access will be right GETD2: LD A,C ; subtract SUB E ; track start sector LD A,B ; from SBC A,D ; desired sector number JR NC,GETD3 ; if too far jump PUSH HL ; save current track on stack LD HL,(DPBLK) ; get sectors per track out LD A,E ; subtract SUB L ; value LD E,A ; from LD A,D ; track start sector SBC A,H ; and leave LD D,A ; result in DE POP HL ; restore track count DEC HL ; decrement count JR GETD2 ; and loop until start is less than desired page ; BC has the sector we want, DE the track start sector number, HL the track GETD3: PUSH HL ; save the track count LD HL,(DPBLK) ; get the sectors per track ADD HL,DE ; compute the next track start sector number JR C,GETD4 ; if this exceeds the desired number jump LD A,C ; otherwise SUB L ; subtract LD A,B ; new track start number SBC A,H ; from the desired sector JR C,GETD4 ; if ok then jump over EX DE,HL ; otherwise swap DE and HL POP HL ; recover track number INC HL ; bump it by 1 JR GETD3 ; and loop until done ; point the disk to the right spot for access now that the computing is done GETD4: POP HL ; recover track number PUSH BC ; save desired sector number PUSH DE ; and the track starting sector number PUSH HL ; and the track number again EX DE,HL ; swap DE and HL LD HL,(TOFS) ; get track offset into HL ADD HL,DE ; add in the desired track number LD B,H ; and move it LD C,L ; into BC CALL TRKF## ; Select track POP DE ; recover entry track into DE LD HL,(SCRT1) ; get pointer to current track storage LD (HL),E ; and put INC HL ; the new track LD (HL),D ; back into scratch POP DE ; recover current sector number LD HL,(SCRT2) ; get storage address LD (HL),E ; and save INC HL ; the new current sector number LD (HL),D ; there POP BC ; recover new track sector start number LD A,C ; subtract SUB E ; track sector start number LD C,A ; from LD A,B ; the desired number SBC A,D ; and LD B,A ; leave result in BC LD HL,(TRANS) ; point to translation routine EX DE,HL ; put address into DE CALL SECTRN## ; go do the translation LD C,L ; put the result LD B,H ; into BC JP SECF## ; finish setting up the disk pointers subttl GET EXTENT SECTOR COUNT page ; routine gets extent sector count from storage and figures block number COMBLK: LD HL,BLSHFT ; point to block shift factor LD C,(HL) ; pull it into C LD A,(ESCNT2) ; pull in the extent low byte COMBL1: OR A ; set flags - clear carry RRA ; rotate right through carry DEC C ; decrement count JR NZ,COMBL1 ; loop until done LD B,A ; then save the byte into B LD A,08H ; get bit 3 set SUB (HL) ; subtract block shift from this value LD C,A ; and save it into C LD A,(ESCNT1) ; get the extent high byte COMBL2: DEC C ; decrement count again JR Z,COMBL3 ; if done jump OR A ; otherwise set flags - clear carry RLA ; rotate left through carry JR COMBL2 ; loop until done COMBL3: ADD A,B ; add in the offset RET ; and return ; routine points into the FCB block and returns with group pointer. ; If BSIZE=0 blocks are>1024 and 16 bits come back GETGRP: LD HL,(FCB) ; get the FCB address LD DE,16 ; and a 16 ADD HL,DE ; add in the offset ADD HL,BC ; add in BC as well LD A,(BSIZE) ; get the block size byte OR A ; set flags JR Z,HLPBC ; if zero add HL and BC LD L,(HL) ; pull in this byte LD H,0 ; set upper byte 0 RET ; return with HL set ; routine sets HL = (HL+BC) HLPBC: ADD HL,BC ; add HL and BC LD E,(HL) ; move this address byte into E INC HL ; point to next LD D,(HL) ; pull this one into D EX DE,HL ; swap DE and HL RET ; and go back subttl SECTOR COUNT TO GROUP POINTER CONVERSION page ; routine takes sector count for directory area and gets the group ; pointer out SECGRP: CALL COMBLK ; get the block number LD C,A ; and put it into C LD B,0 ; zero out B CALL GETGRP ; now go get the right group LD (GROUP),HL ; save it at group storage RET ; and go back ; routine checks group and sets flags if zero - no group 0 should be there TSTGRP: LD HL,(GROUP) ; get the group pointer out LD A,L ; move low into A OR H ; or with H to set flags RET ; and go back ; routine appears to figure actual sector number from group pointer COMSEC: LD A,(BLSHFT) ; get the block shift factor LD HL,(GROUP) ; and the group pointer into HL COMS1: ADD HL,HL ; slide it right DEC A ; and decrement count JR NZ,COMS1 ; looping until it has shifted right amount LD (DIRPNT),HL ; then save it in the directory pointer LD A,(BLKMSK) ; now get the block mask out LD C,A ; save it in C LD A,(ESCNT2) ; get the low extent counter AND C ; and it with the block mask OR L ; or in the directory pointer low byte LD L,A ; slide this back into L LD (GROUP),HL ; and save this address at group RET ; then back to sender ; routine to point to information + 12 - the extent byte of the FCB GETEX: LD HL,(FCB) ; get the FCB pointer LD DE,12 ; and get a 12 ADD HL,DE ; add in the offset RET ; return with HL set to extent address subttl DO RECORD COUNT & CURRENT RECORD page ; routine sets HL to current record (CR) and DE to record count (RC) SETPT: LD HL,(FCB) ; get the FCB address LD DE,15 ; get an offset of 15 ADD HL,DE ; HL now points to RC address EX DE,HL ; swap new value into DE LD HL,17 ; now get an offset of 17 ADD HL,DE ; add this in so HL points to CR RET ; return with HL and DE set ; routine appears to get the extent byte from FCB and set up extent pointers FIXEXT: CALL SETPT ; set record pointers LD A,(HL) ; get the current record for sequential I/O LD (ESCNT2),A ; and save it EX DE,HL ; swap DE and HL LD A,(HL) ; pull in the record count for this extent LD (RECCNT),A ; and save it CALL GETEX ; point to extent byte in FCB LD A,(EXTMSK) ; get the extent mask AND (HL) ; and the extent byte extent mask LD (ESCNT1),A ; saving the result RET ; and return ; routine appears to set up record counters FIXREC: CALL SETPT ; set the record pointers LD A,(DCODE) ; get the byte CP 02H ; is it a 2? JR NZ,FIXRC1 ; if not jump past XOR A ; zero out A FIXRC1: LD C,A ; move A into C LD A,(ESCNT2) ; get the byte ADD A,C ; add it to C LD (HL),A ; put the result away at CR in FCB EX DE,HL ; swap DE and HL LD A,(RECCNT) ; get the byte ( record count) LD (HL),A ; place it at RC in FCB RET ; and return subttl SPIN HL RIGHT page ; spin HL by C bits right SPINHL: INC C ; SPH1: DEC C ; RET Z ; OR A ; RR H ; rotate right through carry RR L ; rotate it as well JR SPH1 ; loop until the count quits ; routine to compute a check sum across a buffer of 128 bytes SUM128: LD C,128 ; we will sum 128 bytes in the temp buffer LD HL,(DTEMP) ; get the address of the buffer from DTEMP XOR A ; zero A SUM1: ADD A,(HL) ; add in the byte at (HL) INC HL ; bump pointer to next DEC C ; decrement count JR NZ,SUM1 ; loop if we are not finished RET ; otherwise go back with sum in A ; routine to slide HL left C bits SPINBIT: INC C ; bump for the decrement SPB1: DEC C ; decrement the loop count RET Z ; return when done ADD HL,HL ; slide HL left one bit JR SPB1 ; spin until count zeroes ; routine to point to directory entry stored in buffer PNTDIR: LD HL,(DTEMP) ; get the temp buffer address out LD A,(DIROFF) ; get the offset from storage PNTD1: ADD A,L ; add it to A LD L,A ; and put it into L RET NC ; if no overflow go back INC H ; otherwise add in the overflow RET ; and then return subttl FCB S1 & S2 BYTES MANIPULATIONS page ; routine gets the S2 byte at (FCB + 14) GETS2: LD HL,(FCB) ; get the input FCB address LD DE,14 ; point 14 downstream ADD HL,DE ; set up this address LD A,(HL) ; pull this byte into A RET ; and go back ; routine sets the S2 byte at (FCB + 14) to zero ZS2: CALL GETS2 ; get the address of S2 LD (HL),0 ; and set in a zero RET ; and go back ; routine sets bit 8 of S2 SETS28: CALL GETS2 ; point to S2 OR 80H ; or in the eigth bit LD (HL),A ; and put this value back RET ; and return ; routine subtracts counter from word at scratch0 SUBCS0: LD DE,(COUNT) ; get the counter LD HL,(SCRT0) ; then get scratch0 data word LD A,E ; and subtract SUB (HL) ; data at scratch 0 INC HL ; from LD A,D ; the SBC A,(HL) ; counter RET ; and go back with result set in flags? ; routine checks count against scratch0 and decrements count if ok CHKINC: CALL SUBCS0 ; compare counter and (scratch0) RET C ; if (scratch0)>counter return INC DE ; otherwise bump DE LD (HL),D ; and place it DEC HL ; into LD (HL),E ; (scratch0) RET ; and return subttl MATH ROUTINES page ; routine to compute HL = DE - HL SUBHD: LD A,E ; pull E into A SUB L ; subtract from L LD L,A ; and put it back LD A,D ; then move in the D value SBC A,H ; and subtract with borrow LD H,A ; put back into H RET ; and return ; routine to check directory area with check sum to see if it changed CHKCNG: LD DE,(LDIRPNT) ; point to the directory area LD HL,(CKSIZE) ; get the check size CALL SUBHD ; compute HL = DE - HL RET NC ; return if they were the same PUSH BC ; otherwise save the BC regs CALL SUM128 ; and compute the new check sum LD HL,(CHANGE) ; point to the change storage area EX DE,HL ; swap DE and HL LD HL,(LDIRPNT) ; point to the directory area ADD HL,DE ; add in the change word POP BC ; recover BC INC C ; bump count by one JR Z,CHK1 ; if zero save A at (HL) and return CP (HL) ; if not check A and (HL) RET Z ; return if they are the same CALL SUBCS0 ; otherwise check counter and (scratch0) RET ; return if they are ok CHK1: LD (HL),A ; put A into (HL) RET ; and return subttl DISK RELATED ROUTINES page ; routine to read the next sector from the disk GETNXT: CALL SETEMP ; set the DMA address to (DTEMP) CALL RDSEC ; and read the next sector ; tell AIOS the correct DMA address SETDMA: LD BC,(DMAADR) ; point to the DMA address word JP DMAF## ; and tell AIOS about it ; set the dma address to DTEMP SETEMP: LD BC,(DTEMP) ; get the DMA address from DTEMP JP DMAF## ; Set DMA buffer addr & return ; routine checks count - returns 1 if it is zero ; and the first byte at count if it is nonzero CHKCNT: LD HL,COUNT ; get count LD A,(HL) ; check to INC HL ; see CP (HL) ; if it is zero RET NZ ; return if it is not INC A ; bump up to 1 if it is RET ; and then return subttl DIRECTORY RELATED ROUTINES page ; routine sets counter to -1 for starting loop SETCNT: LD HL,-1 ; start off with a 16 bit -1 LD (COUNT),HL ; and save it at count RET ; return to sender ; routine moves to next directory entry - COUNT says the last one looked at NXTDIR: LD DE,(DIRMAX) ; get the number of directory entries LD HL,(COUNT) ; now get the directory counter INC HL ; add one to it LD (COUNT),HL ; and save it back for later CALL SUBHD ; looking too far? JR NC,GOTDIR ; if no carry still entries left JR SETCNT ; otherwise return with a 16 bit -1 ; routine to get the next directory sector off of the disk GOTDIR: LD A,(COUNT) ; get the directory count low byte AND 03H ; strip off all but low two (4 entries per sec) LD B,5 ; set to spin low 3 over 5 SPINAB: ADD A,A ; move over 1 bit DJNZ SPINAB ; loop until done LD (DIROFF),A ; save this for later OR A ; set flags RET NZ ; return if not zero PUSH BC ; otherwise save BC CALL GETDS ; looks like we set for getting the next dir CALL GETNXT ; and read the next sector POP BC ; recover the BC pair JP CHKCNG ; see if the disk changed and return subttl ALLOCATION BIT MAP ROUTINES page ; routine gets an allocation bit set into position ; and sets pointer to allocation byte for marking blocks ; BC has the group pointer - Exit with BC -> allocation byte ; D -> bit for this group ; A -> allocation byte spun into place GETBIT: LD A,C ; save entry C into A AND 07H ; strip off all but low 3 INC A ; add one to the result - bit number for group LD E,A ; and put this into E LD D,A ; and another into D for exit LD A,C ; get another copy of C RRCA ; spin RRCA ; it RRCA ; right 3 AND 1FH ; strip off high 3 leaving high 5 in place LD C,A ; put this into C LD A,B ; now get entry B ADD A,A ; and spin ADD A,A ; it left ADD A,A ; four ADD A,A ; bits ADD A,A ; OR C ; or in the word at C LD C,A ; and put it back into C LD A,B ; get another copy of B RRCA ; slide RRCA ; over 3 RRCA ; right AND 1FH ; strip off all but low 3 LD B,A ; place this into B, BC is set LD HL,(ALLOCA) ; now get the allocation pointer ADD HL,BC ; add in the offset LD A,(HL) ; and pull in this byte of it GBLOOP: RLCA ; rotate it left through carry DEC E ; decrement the rotate count JR NZ,GBLOOP ; loop until the bit is in place RET ; and then return with it page ; routine to set allocation bit - BC has the group pointer ; enter with E set with current allocation byte SETBIT: PUSH DE ; save DE CALL GETBIT ; get allocation bit into position AND 0FEH ; clear bit zero POP BC ; restore BC - C has bit to set OR C ; or in the allocation bit PUTBAK: RRCA ; spin right DEC D ; decrement count JR NZ,PUTBAK ; loop til it is back in place LD (HL),A ; and then put it back in RET ; and return page ; routine to update allocation map for current directory FIXALL: CALL PNTDIR ; set pointer to DMA + offset LD DE,16 ; get offset of 16 ADD HL,DE ; add this in - point to group bytes PUSH BC ; save BC with set bit in C LD C,17 ; set up to spin 16 times FIXAL1: POP DE ; get data off stack DEC C ; decrement counter RET Z ; return if it is zero PUSH DE ; otherwise save the data again LD A,(BSIZE) ; get the block size OR A ; set the flags JR Z,FIXAL2 ; if zero jump - blocks are > 1024 PUSH BC ; otherwise save the current count PUSH HL ; and the address data LD C,(HL) ; pull in the word at (HL) LD B,0 ; zero out B - BC now has group pointer JR FIXAL3 ; and jump over FIXAL2: DEC C ; these groups are two bytes wide so dec again PUSH BC ; save the new count on the stack LD C,(HL) ; pull in the high group byte INC HL ; point to low LD B,(HL) ; and pull it in so BC has both PUSH HL ; save this address FIXAL3: LD A,C ; put the new data into A OR B ; compare with B JR Z,FIXAL4 ; if it is zero block is not allocated LD HL,(DSIZE) ; get the number of disk blocks LD A,L ; subtract SUB C ; HL LD A,H ; from SBC A,B ; BC CALL NC,SETBIT ; if not too big, go set the bit FIXAL4: POP HL ; recover the group pointer INC HL ; bump it by one POP BC ; recover count JR FIXAL1 ; and spin until done page ; routine to get the allocation vector GETAL: LD HL,(DSIZE) ; get the max number of blocks LD C,03H ; set to CALL SPINHL ; divide by eight INC HL ; add 1 for the map size LD B,H ; and put map size LD C,L ; into BC - number of bytes in allocation map LD HL,(ALLOCA) ; point to allocation storage area CLRALL: LD (HL),0 ; put in a zero INC HL ; bump the pointer DEC BC ; one less group to zero out LD A,B ; put the new count into A OR C ; check for done JR NZ,CLRALL ; if not loop until so LD DE,(AL01) ; if done get directory allocation bits LD HL,(ALLOCA) ; point to allocation stogare LD (HL),E ; put directory allocation bits INC HL ; into that LD (HL),D ; storage area cause they are busy CALL HOMDSK ; home the selected disk LD HL,(SCRT0) ; get the scratch 0 address LD (HL),03H ; save a 3 at (scratch0) INC HL ; point to (scratch0+1) LD (HL),0 ; and zero this out - it has 16 bit 3 now CALL SETCNT ; set a counter to -1 GETA1: LD C,0FFH ; set the exit code in CALL NXTDIR ; set the next directory pointers CALL CHKCNT ; was there one? RET Z ; return if not CALL PNTDIR ; point to the right spot LD A,DELDAT ; get the file deleted mark CP (HL) ; is this what we see? JR Z,GETA1 ; keep looking for a nonempty block LD A,(USRCOD) ; get the user byte CP (HL) ; test this against the first byte JR NZ,GETA2 ; if we don't match mark this block INC HL ; bump pointer to file name entry LD A,(HL) ; pull the byte in SUB '$' ; is this a submit file name $$$.SUB JR NZ,GETA2 ; if not take this block also DEC A ; otherwise set A to FF LD (ODATA),A ; and save it at ODATA for CCP to use GETA2: LD C,01H ; set the bit into C CALL FIXALL ; fix this allocation bit CALL CHKINC ; test for too far - bump count if not JR GETA1 ; spin until done with the scan subttl page ; routine appears to check extent byte CHKEXT: PUSH BC ; get the entry BC safe PUSH AF ; and the accumulator LD A,(EXTMSK) ; get the extent mask CPL ; flip it over LD B,A ; and save it into B LD A,C ; move C into A AND B ; and it with the flipped extent mask LD C,A ; save this into C POP AF ; get the entry A back AND B ; and with flip extent mask SUB C ; subtract C AND 1FH ; mask off high bit POP BC ; recover the entry BC RET ; and go back confused subttl SEARCH FOR A SPECIFIED FILE page ; search for a specified file - set flags to say how it went ; used by all file routines to set up file information SEARCH: LD A,0FFH ; set return code up LD (SCODE1),A ; and save it for later LD HL,SCODE ; point to search code area LD (HL),C ; save entry code away LD HL,(FCB) ; get the entry FCB address LD (SEARA),HL ; save a copy CALL SETCNT ; set the search counter up CALL HOMDSK ; home the selected disk SERCHN: LD C,0 ; CALL NXTDIR ; locate next directory entry CALL CHKCNT ; check to see if one was found JR Z,NOTFND ; jump over if not LD HL,(SEARA) ; get the FCB address out EX DE,HL ; into DE LD A,(DE) ; get the byte at (DE) CP DELDAT ; is it a deleted file? JR Z,SERCH1 ; if so jump over PUSH DE ; save this address for later CALL SUBCS0 ; check count and increment POP DE ; recover the address JR NC,NOTFND ; if no carry we didn't find the file SERCH1: CALL PNTDIR ; point to the indicated directory entry LD A,(SCODE) ; get the search code out LD C,A ; stuff it into C LD B,0 ; zero out the high byte SERCH2: LD A,C ; move it to A OR A ; set the flags - check for zero JR Z,SEREXT ; if so jump over LD A,(DE) ; otherwise get the FCB file name char CP '?' ; is it a wild card name? JR Z,MATCH ; if so jump over LD A,B ; check B CP 0DH ; is it past extent byte? JR Z,MATCH ; if so this is a good file to go back with CP 0CH ; how about at the extent byte? LD A,(DE) ; get the byte again JR Z,SERCH3 ; if so jump over SUB (HL) ; otherwise subtract the two bytes AND 7FH ; strip off the special bits JR NZ,SERCHN ; if not zero this doesnt match so try again JR MATCH ; if it matches press on page SERCH3: PUSH BC ; save BC from harm LD C,(HL) ; pull the byte into C CALL CHKEXT ; check the extent byte POP BC ; recover BC JR NZ,SERCHN ; if not zero search again MATCH: INC DE ; otherwise bump FCB pointer INC HL ; and pointer into memory INC B ; increment the FCB count DEC C ; decrement the C counter JR SERCH2 ; and keep going with the search SEREXT: LD A,(COUNT) ; get the count AND 03H ; strip off the high 5 bits LD (ODATA),A ; save this at ODATA for return LD HL,SCODE1 ; get the return code address LD A,(HL) ; pull it into A RLA ; slide it left one bit through carry RET NC ; if bit 7 not set return XOR A ; otherwise zero out A LD (HL),A ; put it back at SCODE1 RET ; then go back NOTFND: CALL SETCNT ; set the counter for error code LD A,0FFH ; and set the return byte into A JP GOBAK ; then return to sender subttl OPEN SELECTED FILE page ; routine to open selected file OPENF: LD C,0FH ; look at FCB through S1 and S2 bytes CALL SEARCH ; search for the file CALL CHKCNT ; was it found RET Z ; return if not OPEN1: CALL GETEX ; get the extent byte LD A,(HL) ; pull it into A PUSH AF ; save it for later PUSH HL ; save the address as well CALL PNTDIR ; point to the directory area EX DE,HL ; and put the address into DE LD HL,(FCB) ; get the FCB address LD C,32 ; Bytes to move PUSH DE ; save the directory address CALL MOVE ; Move C bytes DE to HL CALL SETS28 ; set bit 7 of S2 POP DE ; recover the directory address LD HL,12 ; set to point downstream ADD HL,DE ; now pointing to extent byte of directory LD C,(HL) ; pull this byte in LD HL,15 ; ready to move down again ADD HL,DE ; now point to LD B,(HL) ; put the extent byte back here POP HL ; recover the FCB address POP AF ; and the entry extent byte LD (HL),A ; put the byte back LD A,C ; move the directory extent byte into A CP (HL) ; are they the same LD A,B ; JR Z,OPEN2 ; if so jump LD A,0 ; otherwise get a zero JR C,OPEN2 ; if it was too big jump over LD A,80H ; now get bit 7 set OPEN2: LD HL,(FCB) ; point to the FCB again LD DE,15 ; ready to move it ADD HL,DE ; now point to record count LD (HL),A ; pull this out RET ; and go back with it page ; routine to check if (HL) is zero - if so move (DE) to (HL) CHKCPY: LD A,(HL) ; test (HL) INC HL ; for OR (HL) ; zero DEC HL ; point back RET NZ ; return if not zero LD A,(DE) ; otherwise get (DE) LD (HL),A ; put it at (HL) INC DE ; bump INC HL ; bothof them LD A,(DE) ; get (DE) LD (HL),A ; and put it at (DE) DEC DE ; restore both DEC HL ; pointers RET ; and return ; routine to close selected file CLOSEF: XOR A ; cheap zero LD (ODATA),A ; put it at return info byte LD (COUNT),A ; and zero out the count LD (COUNT+1),A ; word RET ; subttl NEXT EXTENT SET UP ROUTINE page ; routine sets up for the next extent access for either read or write NEXTEX: XOR A ; get a zero LD (RWFLG1),A ; say we are writing CALL CLOSEF ; close the file CALL CHKCNT ; did it exist RET Z ; return if not LD HL,(FCB) ; get the FCB address LD BC,12 ; set to point to extent byte ADD HL,BC ; now it does LD A,(HL) ; pull the thing in INC A ; bump by one AND 1FH ; strip off bit 7 LD (HL),A ; and put it back JR Z,NEXTE1 ; if it was zero jump over LD B,A ; otherwise save it in B LD A,(EXTMSK) ; get the extent mask AND B ; and use it LD HL,RWFLG1 ; and point to the flag AND (HL) ; and in this byte as well JR Z,NEXTE2 ; if zero jump past JR NEXTE3 ; otherwise off to NEXTE1: LD BC,2 ; ready to bump 2 more ADD HL,BC ; add it in INC (HL) ; increment this byte LD A,(HL) ; then pull it in AND 0FH ; strip off the high 4 bits JR Z,NEXTE5 ; if zero jump NEXTE2: LD C,0FH ; look through S2 byte in FCB CALL SEARCH ; look for the indicated file CALL CHKCNT ; see if it was there JR NZ,NEXTE3 ; if so open the next extent JR NEXTE5 ; if it was FF then return NEXTE3: CALL OPEN1 ; go open the next extent NEXTE4: CALL FIXEXT ; set the pointers up XOR A ; zero A for the return JP GOBAK ; and go back NEXTE5: CALL JR1 ; set to return with problems JP SETS28 ; set bit 7 of S2 to say so subttl READ DISK ROUTINE page ; routine to read the disk DSKRED: LD A,01H ; get a ! LD (DCODE),A ; save it for the disk code byte RRERR: LD A,0FFH ; get a read flag LD (RWFLG2),A ; set it in for the control CALL FIXEXT ; go fix the extent LD A,(ESCNT2) ; get the extent low byte LD HL,RECCNT ; point to the record count CP (HL) ; are they the same? JR C,DSKR1 ; if record count was bigger, jump over CP 80H ; was the count 80 hex? JR NZ,DERR ; if not jump out with an error CALL NEXTEX ; go get the next extent XOR A ; set a=0 LD (ESCNT2),A ; save this as the new extent low byte LD A,(ODATA) ; get the output data OR A ; set the flags JR NZ,DERR ; if it was not zero exit with error DSKR1: CALL SECGRP ; otherwise group from the sector count CALL TSTGRP ; see if it is ok JR Z,DERR ; if not go back CALL COMSEC ; if co compute the right sector CALL GETD1 ; get the directory set right CALL RDSEC ; read the sector JP FIXREC ; fix up the record and return DERR: JP JR1 ; set the error exit and return ; routine to log the selected disk LOGDSK: CALL DISKID ; go get the disk information set LD HL,0001 ; set drive A: as an active disk LD (DLOG),HL ; save the vector into place again JP GETAL ; get the new allocation vector subttl LDOS FUNCTION 14 page ; LDOS function 14: select disk ; enter with selected disk in IDATA LOGIN: LD A,(IDATA) ; get the disk to log LD HL,CURDSK ; point to the current disk storage CP (HL) ; are they the same RET Z ; return if so LD (HL),A ; otherwise make this current JR LOGDSK ; and log it in then return ; routine to select disk indicated in FCB drive code RSELCT: LD A,0FFH ; get an FF LD (OUT1),A ; save it to force disk reset on exit LD HL,(FCB) ; point to the FCB LD A,(HL) ; pull in the new drive code AND 1FH ; strip off the user bits DEC A ; decrement to make it right LD (IDATA),A ; save it for the login routines CP 1EH ; is it too big JR NC,XIT ; if so go back LD A,(CURDSK) ; get the current disk out LD (OUTDSK),A ; save it for the return LD A,(HL) ; pull in the new code again LD (OLDDSK),A ; save the value as the olddisk AND 0E0H ; strip off the drive bits LD (HL),A ; put the user bits back CALL LOGIN ; go log the new disk XIT: LD A,(USRCOD) ; get the user code out LD HL,(FCB) ; point back to the FCB OR (HL) ; set in the new user code bits LD (HL),A ; and put the result back RET ; then return subttl LDOS FUNCTIONS 12,13,15,16 page ; LDOS function 13: routine to reset the system disk RESET: LD HL,0 ; get ready to reset the vectors LD (ROVEC),HL ; set both read only bits LD (DLOG),HL ; and login bits to off XOR A ; zero A LD (CURDSK),A ; and make it the current disk LD HL,TBUFF ; Set DMA addr to 80H LD (DMAADR),HL ; save this as the new DMA value CALL SETDMA ; make it so for real JR LOGDSK ; go log in drive A and return ; LDOS function 15: open file ; enter with FCB address in HL and INFO ; exit with directory code in A OPEN: CALL ZS2 ; set S2 to zero for the new file CALL RSELCT ; select the right disk JP OPENF ; then open the file in the FCB ; LDOS function 20: read sequential ; enter with FCB address ; exit with directory code in A READ: CALL RSELCT ; select the indicated drive JP DSKRED ; and go read the disk ; LDOS function 26: set DMA address ; enter with DMA address set DMASET: LD (DMAADR),DE ; save it into place JP SETDMA ; then make it true subttl LDOS EXIT ROUTINE page ; ldos exit routine BEXIT: LD A,(OUT1) ; get the out flag for drive reset OR A ; set the flags JR Z,REXIT ; if zero we may exit as is LD HL,(FCB) ; otherwise get the FCB address LD (HL),0 ; put a zero into drive select byte LD A,(OLDDSK) ; get the old disk out OR A ; was it zero JR Z,REXIT ; if so exit LD (HL),A ; otherwise set in the old disk byte LD A,(OUTDSK) ; get the disk to relog LD (IDATA),A ; save it for the login CALL LOGIN ; go log in the old disk REXIT: LD SP,(OLDSP) ; get the old stack pointer out LD HL,(ODATA) ; get the output data into place LD A,L ; put L into A LD B,H ; and H into B RET ; then return from LDOS happy subttl LDOS DATA AREA page ; console input buffer data storage area COLUMN: DEFB 0 ; column pointer for buffer OLDSP: DEFB 0,0 ; Caller's Stack ptr REPT 48 DEFB 0 ; 48 (room for stack) ENDM USRCOD: DEFB 0 ; user and disk code lives here CURDSK: DEFB 0 ; current disk holder FCB: DEFW 0 ; Storage for caller's parms ODATA: DEFW 0 ; return information storage ; ldos disk data area follows TINFO: DEFB 0E5H ; ROVEC: DEFW 00000H ; read only vector address storage DLOG: DEFW 00000H ; disk login areax DMAADR: DEFW 00080H ; DMA address defaults to 80H SCRT0: DEFW 00000H ; SCRT1: DEFW 00000H ; holds current track SCRT2: DEFW 00000H ; holds starting sector number of current track DTEMP: DEFW 00000H ; temp DMA address storage DPB: DEFW 00000H ; disk parameter block address CHANGE: DEFW 00000H ; scratch storage for disk change check ALLOCA: DEFW 00000H ; allocation vector area DPBLK: DEFW 00000H ; disk parm block - number of sectors/trk BLSHFT: DEFB 000H ; block shift factor BLKMSK: DEFB 000H ; block mask EXTMSK: DEFB 000H ; null mask DSIZE: DEFW 00000H ; disk size DIRMAX: DEFW 00000H ; directory max AL01: DEFW 00000H ; Allocation storage CKSIZE: DEFW 00000H ; number of entries to check TOFS: DEFW 00000H ; track offset TRANS: DEFW 00000H ; translation routine address holder RWFLG1: DEFB 000H ; flag indicates read or write RWFLG2: DEFB 000H ; flag also used to indicate read or write SCODE1: DEFB 000H DCODE: DEFB 000H ; disk code byte IDATA: DEFB 000H ; input data storage area EXTBLK: DEFB 000H SCODE: DEFB 000H SEARA: DEFB 000H DEFB 000H DEFB 000H DEFB 000H BSIZE: DEFB 000H ; block size byte OUT1: DEFB 000H ; used to force relog of disk on exit OUTDSK: DEFB 000H ; LDOS exit disk OLDDSK: DEFB 000H ; disk entered with RECCNT: DEFB 000H ; record count holder ESCNT1: DEFB 000H ; extent recort count high byte ESCNT2: DEFB 000H ; extent record count low byte DEFB 000H GROUP: DEFB 000H ; group number storage DEFB 000H DIRPNT: DEFB 000H ; directory pointer area DEFB 000H DIROFF: DEFB 000H ; offset into the directory area COUNT: DEFB 000H ; holds 16 bit counter for looping DEFB 000H LDIRPNT: DEFB 003H ; END