IMD 1.16: 13/08/2007 18:26:07 utilities osborne source files *master* 10/9/81  OCCCOPYAH  OCCCOPYAH  OCCTXTA ASTI OCCRSYS ASMOCCAUTOAASM6OCCRAM1 H OCCRTRK ASMOCCTXTA H IOCCDLDA ASM OCCCCP2 ASM!"#$%&'(OCCCCP2 ASMI)*+,-OCCLOOPAASM-./0OCCFMTA H B12345OCCTXT2 H H6789:OCCTXT1 H H;<=>?OCCTXT3 H H@ABCDOCCBOOT ASMEFOCCCCP1 ASMGHIJKLMNOCCCCP1 ASMHOPQRSOCCBDOS1ASMTUVWXYZ[OCCBDOS1ASM\]^_`abcOCCBDOS1ASMdefghijkOCCBDOS1ASMlmnopqrsOCCBDOS1ASMOCCTXT4 H HtuvwxOCCTXT6 ASTJyz{|}OCCWSYS ASM~OCCTXT5 ASTKOCCRAMA H !OCCDTESTASMOCCDTESTASM5OCCCOPY1ASMZ COPY2 ;If no HELP requested LDK DE,HELPM CALL PRINT JMP BOOT HELPM: DB cr,lf,'Copy will copy disks on the OCC-1' DB cr,lf,'disk subsystem. To start, type:' DB cr,lf,' COPY ' DB cr,lf,'Where is Carriage Return' DB cr,lf,'and is S=system, D=data or' DB cr,lf,'A=all. If not entered, a prompting' DB cr,lf,'message will be output.' DB cr,lf,'The source and destination drive' DB cr,lf,'prompts occur.$' COPY2: LDK DE,LOGON CALL PRINT ;Display signon message :2: CALL GETARG ;Determine amount of disk to copy CALL GETSRC ;Get the source drive CALL GETOBJ ;Get the destination drive LDK DE,GOMSG CALL PRINT ;Prompt USER to load diskettes :4 CALL CONIN CPI CR ;wait for JNZ :4 CALL GETDEN ;Check for compatible media JZ :6 ;If media compatible LDK DE,DENMSG ;Display incompatibility message CALL PRINT JMP :12 :6: LDK DE,CRLF CALL PRINT CALL CALBUF ;Compute buffer address :8: ;Main loop for copying disketOCCCOPY1ASMOCCFMT1 ASMGOCCSETA ASMOCCSETA ASMOCCSETA ASMGOCCSYSGAASM7OCCBDOS2ASMOCCBDOS2ASMOCCBDOS2ASMOCCBDOS2ASMOCCBDOS2ASMOCCRAM2 ASM;OCCCOPYA.ASM TITLE 'Disk Copy program.' ; COPY, disk copy utility. * 4D2004-00 MASTER (.ASM) * 2D2004-00 ASSY (.ASM) * 1D2004-00 LISTING (.PRN) * 4D1004-00 MASTER (.CON) * 2D1004-00 ASSY (.CON) ; +-----------------------+ ; | | ; | Disk Copy | ; | | ; +-----------------------+ ; ; Disk copy routine to be used with any standard ; CBIOS implementation on CPM 2.x. VER = '2' LEVEL = '4' ; Updates ; 2-3, 12-28-80 Fixed recovery in RD/WRTSEG to continue ; upon finding error. (rhf) ; 2-4, 2-25-80 Added to code to adjust number of tracks ; to copy for double sided floppies disks. List -F ;skip listing IFfed-out code Link OCCRAM2.asm ;Osborne high-ram definitions BOOT = 0000h ;Warm start address BIOSLOC = BOOT+1 ;BIOS vector start FSELDSK = 27 ;Offset for SELDSK function FSTRK = 30 ;Offset for SETTRK function FSEC = 33 ;Offset for SETSEC function FSDMA = 36 ;Offset for SETDMA function READF = 39 ;Offset for READ SECTOR function WRITEF = 42 ;Offset for WRITE tes CALL CPYTRK ;Copy one track LDK HL,TRACK INC [hl] ;next track LD A,LTRK ;get last trk to copy CMP [hl] JNC :8 ;if not last track ; See if user wants to copy another disk LDK DE,CPLMSG CALL PRINT ;indicate to user we are done :10: LDK DE,RPTMSG CALL PRINT ;ask for another disk copy CALL CONIN ;Get answer CPI 'Y' JZ :2 ;If yes, perform again CPI 'N' JZ :12 ;If NO, exit CPI CR JNZ :10 ;If no valid answer, try again COPYX: ;Exit copy routine :12: LDK DE,RMOUNT ;Display remount message CALL PRINT CALL CONIN JMP BOOT page GETARG: proc ;Get type of copy to perform. LDK HL,TBUFF MOV A,M ;Get # byte on command line ORA A JZ :4 ;If no command line data MOV C,A ;save # bytes :2 INC HL ;Get next byte MOV A,M CALL VCPYT ;validate parameter RZ ;If valid, return DCR C ;If NO, dec # bytes JNZ :2 ;if not end, continue :4 LDK DE,CPYMSG CALL PRINT ;Request parameter input CALL CONIN CALL VCPYT ;validate parameter JNZ :4 SECTOR function SYSSEC = 10 ;ditto for OCC1 SYSTART = 0 ;First system track number BUFF = 4000h ;OCC1 FWA of ROM-accessible memory SCTLN = 256 ;OCC1 hardware-fixed sector length page ORG 100h COPY: JMP COPY1 ;initializer NAME: DB 'COPY .com' DB CR,LF IF * <> 0111h .9 ERR Wrong length of filename and type. ENDIF DB 'Copyright SORCIM Corp. 1981',CR,LF DB 'S/N-' SN: DB '123456' DB ',Ver ' CPSV: DB 'All' DB ' ' MESV: VFD 16\VER,8\'.',16\LEVEL RELTYP: DB 'ST' ;Release type - EV,UD,DP,etc. DB CR,LF,'Z'-40h ;ascii EOF mark ; Distributor is a fixed value in COPYPROG.com DIST: DB 'OSB ' ;Distributor - DGM,OURS,LBA,etc. OPTYP: DB 'Z80 ' ;Type - Z80,8080,8086,NS,VG,etc. ; Total bytes used to here = 83 decimal. IF * <> 0153h .9 ERR Copyright notice section wrong length. ENDIF COPY1: proc ;Main loop for Disk copy routine LXI SP,100h ;Initialize the stack pointer ;Check if help wanted. LD A,TBUFF OR A JZ COPY2 ;If no arguments LD A,TBUFF+2 CMP '?' JN ;If invalid input RET space 4,10 VCPYT: proc ;Validate copy type request and STO A,CFLAG ;Save for latter test CMP 'A' RZ CMP 'S' RZ CMP 'D' RZ CMP 'X' JZ COPYX ;if request to abort RET space 4,10 GETSRC: proc ;Find source drive number LDK DE,SDMSG CALL GDISK ;Get drive number in ASCII STO A,ASCISRC MOV A,C STO A,SOURCE RET space 4,10 GETOBJ proc ;Find destination drive number LDK DE,OBJMSG CALL GDISK ;Get drive number in ASCII STO A,ASCIOBJ LD A,SOURCE CMP C JZ GETOBJ ;If source = destination drive MOV A,C STO A,OBJECT RET space 4,10 GDISK proc ;Get a drive from user PUSH D ;Save message address CALL PRINT CALL CONIN ;Get user's input SUI 'A' POP D JM GDISK ;If invalid CPI 'P'-'A'+1 JP GDISK ;If invalid MOV C,A MVI B,0 PUSH D PUSH B LDK DE,1 ;Only do drive validation CALL SELDSK MOV A,H ORA L POP B POP D JZ GDISK ;If invalid drive MOV A,C ADI 'A' RET space 4,10 GETDEN: proc ;CheGDPB2: ORI 0FFh ;Force non-zero RET .occ1 ENDIF space 4,10 ; Calculate the number of available pages for disk ; buffering. CALBUF: LD HL,SYS+1 ;Find bottom of BDOS MVI L,0 ;force page boundary LDK DE,-BUFF ;Subtract address for start of buffer ADD HL,DE MOV A,H ;H now has # PAGES in buffer STO A,BLEN ;save length ORA A RAR ;divide by 2 MOV H,A MOV A,L RAR MOV L,A LDK DE,BUFF ;Compute address of second buffer ADD HL,DE STO HL,BUFF2 RET space 4,10 ; COPY a track of data from source drive to ; desination drive. ; ; ENTRY B = next sector to copy. ; C = number of sectors to copy. CPYTRK: proc LDK HL,ERRFLG MOV A,M ;Get ERROR FLAG MVI M,0 ;clear it ORA A LDK DE,CRLF ;new line CNZ PRINT LDK DE,TRACKM CALL PRINT ;output track LD A,TRACK CALL ODEC ;output track # ; Start the copy MVI B,0 ;sec = 0 LD A,TRACK ;Get TRACK number OR A LD A,SPT LD HL,SKEWPTR JNZ :4 ;If not the first cylinder LDK A,SYSSEC LDK HL,T128 :PUSH BC ;save sector info LK DE,SCTLN ;DE = length of a sector LD HL,DMMA ;HL = DMA-addr for 1st sector :loop: DEC B ;B = #sectors xferred so far JRZ :2 ;if HL-> next DMA-addr ADD HL,DE ;...else step HL to next sector addr JR :loop ;and repeat :2: MOV B,H ! MOV C,L ;BC = HL CALL SETDMA POP BC ! PUSH BC ;retrieve sector info CALL SETSEC ;set sector to begin xfer POP BC ! PUSH BC ;retrieve sector info CALL XFER ;do disk operation OR A ;ck for error POP BC ;restore stack RZ ;if no error, finished ; D = #of sectors awaiting xfer (from ROM-routine) LK A,MSEC+1 SUB D ;A = #of flawed sector MOV B,A ;...will be 1st sector to xfer MOV C,D ;C = #of sectors to xfer PUSH BC ;save info for next xfer CALL RWERR ;notify user POP BC LK HL,RRTRY ;retry counter DEC [hl] JRNZ :retry ;if going to retry @this sector INC B ! DEC C ;...else give up on this sector RZ ;if flawed sector is last sector ;on this track, no others need be copied ck disks for compatible DPBs .occ1 IF FALSE LD A,SOURCE ;Get source drive CALL GETDPB ;Get disk parameter block RNZ ;If select error PUSH DE ;save pointer to DPB STO HL,SKEWPTR ;save pointer to skew table LD A,[de] ;Get number of SECTORS PER TRACK STO A,SPT LXI H,13 DAD D ;HL -> # of reserved tracks LD A,[hl] ;Get number of reserved tracks MVI C,77-1 CMP 2 JZ :1 ;If single sided MVI C,77*2-1 .occ1 ELSE LK A,10 ;10 sectors/track STO A,SPT LK C,39 ;tracks per disk .occ1 ENDIF :1 MVI B,SYSTART LD A,CFLAG CPI 'A' JZ :2 ;If entire disk ;occ1 LD B,[hl] ;B = first data track LK B,3 ;3rd track is data on OCC1 CPI 'D' JZ :2 ;If data only MOV C,B DCR C MVI B,SYSTART :2: MOV A,C STO A,LTRK MOV A,B STO A,TRACK .occ1 IF FALSE LD A,OBJECT CALL GETDPB ;Get disk PARAMETER BLOCK POP HL ;Restore DPB pointer RNZ ;If select error MVI C,15 ;Compare DPBs :3: LD A,[de] ;Get DPB byte CMP M RNZ ;If not identical INX D INC HL 4: STO A,TSECS ;Store # of secs to copy STO HL,SKEWT :6: LDK A,3 STO A,TRYS ;Set retry counter LDK HL,TSECS MOV C,M ;Get number of sectors left to copy MVI M,0 LD A,BLEN CMP C JNC :8 ;If buffer for sectors left MOV D,A MOV A,C SUB D ;TSECS - BLEN MOV M,A ;TSECS = TSECS - BLEN MOV C,D ;# SECTORS = BLEN :8: PUSH BC ;Save sector information LD A,SOURCE ;Get source drive STO A,DISK LDK DE,1 MOV C,A CALL SELDSK ;Select source drive LD A,TRACK CALL SETTRK ;Set track number POP BC ;Restore sector information PUSH BC ;and save it LDK HL,BUFF CALL RDSEG ;Read the "track" LD A,OBJECT ;Get destination drive STO A,DISK MOV C,A LDK DE,1 CALL SELDSK ;Select destination drive LD A,TRACK CALL SETTRK ;Set track number POP BC ;Restore sector information PUSH BC ;and save it LDK HL,BUFF CALL WRTSEG ;Write the track POP BC ;Restore sector information PUSH BC ;and save it LD HL,BUFF2 CALL RDSEG ;Read the written "track" POP  STO 3,[hl] ;RRTRY := 3 JR :retry ;ignore bad sector, continue .occ1 IF FALSE PUSH BC ;Save sector information MOV B,H MOV C,L CALL SETDMA ;SET DMA address .occ1 IF FALSE LD HL,SKEWT ;Get pointer to skew table POP BC ;Restore sector information MOV E,B ;Compute skewed sector number MVI D,0 ADD HL,DE PUSH BC LD A,FUDGE ADD A,M .occ1 ELSE POP BC PUSH BC ;get & save sector info LD A,FUDGE ADD B ;A = 1st sector # .occ1 ENDIF STO A,SECTOR CALL SETSEC ;Select sector LK B,10 ;#tracks to xfer CALL XFER ;Perform disk transfer ORA A CNZ RWERR ;if transfer error (read or write) .occ1 IF FALSE LD HL,DMA LD HL,DMMA ;OCC1 systext-compatible name LDK DE,SCTLN ADD HL,DE ;Compute new DMA address POP BC ;Restore sector information INC B ;Increment sector DCR C ;decrement sector count JNZ RWSEC .occ1 ELSE POP BC ;restore sector info .occ1 ENDIF RET .occ1 ENDIF space 4,10 RWERR: ;Read or Write error processing routin DCR C JNZ :3 ;If all bytes not checked .occ1 ELSE LK HL,TOCC ;skew table for OCC1 STO HL,SKEWPTR LK A,1 STO A,FUDGE ;no fudging on OCC1 .occ1 ENDIF RET .occ1 IF FALSE space 4,10 ; Get disk parameters. ; Entry A= drive. ; Exit HL= FWA for slew table. ; DE-?DPB GETDPB: MOV C,A ;PUT disk number in C reg LDK DE,0 ;disk off-line (bios will check den) CALL SELDSK ;Select disk MOV A,H ;Check for valid disk ORA L JZ GDPB2 ;If invalid selection MOV C,M ;Get translation table address INX H MOV B,M MOV A,B ORA C JZ GDPB1 ;If no translation table LDAX B ;get first sector translation GDPB1: STO A,FUDGE ;Save for fudge factor LDK BC,5*2-1 ADD HL,BC ;-> DPB LD A,[hl] INC HL LD H,[hl] MOV L,A EX DE,HL ;DE -> DPB LD A,[de] LDK HL,T128 CMP 26*128/128 RZ ;if single density LDK HL,T256 CMP 26*256/128 RZ ;If double density (256) LDK HL,T512 CMP 15*512/128 RZ ;If double density (512) LDK HL,T1024 CMP 8*1024/128 RET BC ;Restore sector information LD A,ERRFLG ORA A CZ COMPSEG ;Verify the "track" JZ :10 ;If compare was good ; Here if possible hard error, update retry ; count and try until found to be permament. LDK HL,TRYS DEC [hl] JNZ :8 ;If retries not exhausted ; Here if hard error, output message and continue ; copying. PUSH BC ;save regs LDK DE,PERMMSG CALL PRINT ;Display permanent error XRA A STO A,ERRFLG ;Clear error flag POP BC ;restore counters :10: MOV A,B ADD C ;SECTOR = SECTOR + # SECTORS MOV B,A LD A,TSECS ORA A JNZ :6 ;If track complete RET page ; Read a segment of a track ; Entry HL -> FWA of buffer ; Internal usages: ; B = 1st sector to read ; C = Number of sectors to read RDSEG: XRA A ;READ FLAG JMP RWSEG WRTSEG: ;Entry same as RDSEG ORI 0FFH ;WRITE FLAG RWSEG: STO A,RDWRTF ;Store flag STO HL,DMMA ;OCC1 systext-compatible name LK BC, (1 shl 8) + (MSEC) ;B = 1st sector, C = #sectors LK A,3 STO A,RRTRY :retry: e. ; ; Entry CBIOS indicated hard error, output info. ; Used only from RD/WRT_SEG. ; B = bad sector ; Caller must perserve registers. proc MOV A,B ;get sector # STO A,SECTOR LD A,RDWRTF LDK DE,READMSG ORA A JZ :2 ;If sector read LDK DE,WRTMSG :2: CALL PRINT LD A,DISK ;Get disk number ADI 'A' MOV E,A CALL CONOUT ;Display drive LDK DE,TRKMSG CALL PRINT ;Display track LD A,TRACK CALL ODEC LDK DE,SECMSG CALL PRINT ;Display sector LD A,SECTOR CALL ODEC ;output 179x status LK DE,STSMSG CALL PRINT LK DE,4 LK HL,R179x ;179x register save area :loop: PUSH DE ! PUSH HL CALL OSP POP HL ! PUSH HL LD A,[hl] ;get "register" to output CALL OHEX POP HL ! POP DE INC HL ;point @ next "reg" DEC DE ;decrement count MOV A,D OR E JRNZ :loop ;repeat 4 times ORI 0FFh ;Set error idicator STO A,ERRFLG RET space 4,10 ; Compare the data in the two buffers. ; ; B = SECTOR number, C = # SECTORS COMPSEG: PUSH BC ;Save sectOUT ;PRINT IT POP BC ;RESTORE BCD digits :6: MOV A,C ;MOVE BCD digits into A JMP OHEX ;output page PRINT MVI C,9 JMP SYS CONIN MVI C,1 CALL SYS CMP 'a' RC ;if not lower case CMP 'z'+1 RNC ;if not lower case SUI 'a'-'A' RET OHEX: ;PRINT number in ACCUMULATOR in HEX proc PUSH PSW ;save IT RRC ;Get HIGH NIBBLE RRC RRC RRC CALL :2 ;PRINT HIGH NIBBLE POP PSW ;RESTORE number :2: ;output nib in 4 lsbs of A ANI 0FH ;MASK for NIBBLE CPI 10 JC :4 ;if decimal ADI 'A'-'9'-1 ;If NO, add offset for A-F :4: ADI '0' ;make it ascii MOV E,A ;to E for conout ; JMP CONOUT CONOUT MVI C,2 ;CONOUT function JMP SYS O2SP: CALL OSP OSP: LK E,' ' JR CONOUT SELDSK: MOV A,C STO A,SDISK ;set ROM-referenced cell RET SETTRK: MOV C,A LDK B,0 ;BC = track # LK DE,BSTTRK+100h JMP GOROM SETSEC: MOV C,B LDK B,0 ;BC = sector # LK DE,BSTSEC+100h JMP GOROM SETDMA: LK DE,BSTDMA+100h JMP GOROM XFER: LK DE,BREAD+100h ;ROM12,13,14,15 DB 20,21,22,23,28,29,30,31 DB 36,37,38,39,44,45,46,47 DB 52,53,54,55 T1024: DB 0, 1, 2, 3, 4, 5, 6, 7 DB 16,17,18,19,20,21,22,23 DB 32,33,34,35,36,37,38,39 DB 48,49,50,51,52,53,54,55 DB 8, 9,10,11,12,13,14,15 DB 24,25,26,27,28,29,30,31 DB 40,41,42,43,44,45,46,47 DB 56,57,58,59,60,61,62,63 .occ1 ENDIF RRTRY: DS 1 ;ReTRY counter for R/WSEG ERRFLG: DB 0 ;Error on last track SOURCE: DS 1 ;source drive OBJECT: DS 1 ;destination drive SKEWPTR DS 2 ;Skew table pointer SKEWT: DS 2 FUDGE: DS 1 ;Skew fudge factor CFLAG: DS 1 ;Copy flag TRACK DS 1 ;Track LTRK DS 1 ;Ending track DISK DS 1 ;Disk number SPT DS 1 ;Sectors per track TSECS DS 1 ;Sectors left to copy from this track ;DMA DS 2 ;DMA address DMMA DS 2 ;OCC1 systext-compatible name BLEN: DS 1 ;Length of the Read/Write buffer (pages) BUFF2: DS 2 ;Address of second buffer SECTOR: DS 1 ;Sector SBYTE: DS 1 DBYTE: DS 1 TRYS: DS 1 ;Number of retries RDWRTF: DS 1 ;Read/Write flag ;BUFF = $ ;see 1st por information LD HL,BUFF2 LDK DE,BUFF COMP1: MVI B,low SCTLN ;Length of sector COMP2: LD A,[de] ;Get source byte XOR M JNZ CMPERR ;If source <> destination byte INX D INC HL DCR B JNZ COMP2 ;If end of a sector DCR C ;Decrement # of sectors JNZ COMP1 POP BC RET space 4,10 ; Display compare error message. CMPERR: LD A,[hl] ;Get destination byte STO A,DBYTE LD A,[de] ;Get source byte STO A,SBYTE POP HL ;Get sector information PUSH HL ;and save it back PUSH BC ;Save buffer information PUSH HL LDK DE,CMSG1 CALL PRINT ;Display 1st portion of message LD A,TRACK CALL ODEC ;Display track LDK DE,CMSG2 CALL PRINT ;Display next part POP HL ;Compute bad sector POP BC PUSH BC MOV A,L SUB C ADD A,H MOV E,A ;Compute skewed sector number MVI D,0 LDK HL,SKEWT ADD HL,DE LD A,FUDGE ADD A,M CALL ODEC ;Output sector number LDK DE,CMSG3 CALL PRINT ;Display byte in sector MOV A,B CALL ODEC ;Display byte MVI E,' ' CALL C 256-byte Read utility MOV B,C ;B = #sectors to xfer LD A,RDWRTF ORA A JZ GOROM ;if read operation LK DE,BWRITE+100h ;ROM write routine ; JMP GOROM GOROM: LD HL,BIOSLOC LK L,BROMJP ;call ROM resident routine JMP [hl] page LOGON DB CR,LF,'OSBORNE COPY Utility Program Version ' DB VER,'.' DW rev (LEVEL) DB '.$' CPYMSG: DB CR,LF,LF,'Do you want to ',ESC,'l','copy',ESC,'m:' DB CR,LF,' SYSTEM tracks only? (type S)' DB CR,LF,' DATA tracks only? (type D)' DB CR,LF,' ALL of the disk? (type A)' DB CR,LF,' Exit back to System (type X) $' SDMSG: DB CR,LF,LF,'Source drive? (A, B ) $' OBJMSG DB CR,LF,'Destination drive? (A, B ) $' GOMSG DB CR,LF,LF,'Put source disk on ' ASCISRC DB 'A' DB CR,LF,'Put destination disk on ' ASCIOBJ DB 'B' DB CR,LF,'Then type $' CRLF DB CR,LF,'$' TRACKM DB CR,'Track $' DENMSG DB CR,LF,LF,'Disk have incompatible formats.$' CPLMSG DB CR,LF,'COPY complete.$' RPTMSG DB CR,LF,LF,'Do you want to COPY agage ; Endx OCOPY.asm DB 16,17,18,19,20,21,22,23 DB 32,33,34,35,36,37,38,39 DB 48,49,50,51,52,53,54,55 DB 8, 9,10,11,12,13,14,15 DB 24,25,26,27,28,29,30,31 DB 40,41,42,43,44,45,46,47 DB 56,57,58,59,60,61,62,63 .occ1 ENDIF RRTRY: DS 1 ;ReTRY counter for R/WSEG ERRFLG: DB 0 ;Error on last track SOURCE: DS 1 ;source drive OBJECT: DS 1 ;destination drive SKEWPTR DS 2 ;Skew table pointer SKEWT: DS 2 FUDGE: DS 1 ;Skew fudge factor CFLAG: DS 1 ;Copy flag TRACK DS 1 ;Track LTRK DS 1 ;Ending track DISK DS 1 ;Disk number SPT DS 1 ;Sectors per track TSECS DS 1 ;Sectors left to copy from this track ;DMA DS 2 ;DMA address DMMA DS 2 ;OCC1 systext-compatible name BLEN: DS 1 ;Length of the Read/Write buffer (pages) BUFF2: DS 2 ;Address of second buffer SECTOR: DS 1 ;Sector SBYTE: DS 1 DBYTE: DS 1 TRYS: DS 1 ;Number of retries RDWRTF: DS 1 ;Read/Write flag ;BUFF = $ ;see 1st pONOUT LD A,SBYTE CALL OHEX ;Output source byte MVI E,' ' CALL CONOUT LD A,DBYTE CALL OHEX POP BC ;Restore sector information ORI 0FFh ;Set error indicator STO A,ERRFLG RET ODEC: ;Output number in decimal ; Entry A= number to convert proc LDK BC,0 ;init dec number :2 SUI 10h ;high nibble JC :4 ;If yes, CARRY is set, output low only MOV D,A ;save HEX number MOV A,C ;Get 2 least SIGNIF. BCD digits ADI 16H ;ADD 16 DAA ;DECIMAL ADJUST MOV C,A ;STORE THEM MOV A,B ;Get OVERFLOW digit ACI 0 ;ADD CARRY (OVERFLOW) MOV B,A ;STORE OVERFLOW digit MOV A,D ;RESTORE HEX number JMP :2 :4 ADI 10H ;RESTORE HEX number to RANGE of 0-0FH ORA A DAA ;MAKE it DECIMAL ADD C ;ADD BCD digits to LOW NIBBLE DAA ;DECIMAL ADJUST MOV C,A ;STORE BACK in C MOV A,B ;Get OVERFLOW digit ACI 0 ;ADD in CARRY (OVERFLOW) JZ :6 ;DON'T PRINT LEADING 0 from OVERFLOW PUSH BC ;save BCD digits ADI '0' ;MAKE OVERFLOW digit into ASCII MOV E,A CALL CONain? (Y/N) $' READMSG DB CR,LF,'Read error on drive $' WRTMSG DB CR,LF,'Write error on drive $' TRKMSG DB ' track $' SECMSG DB ' sector $' STSMSG DB ' stat=$' CMSG1 DB CR,LF,'Compare error Track $' CMSG2 DB ' Sector $' CMSG3 DB ' Byte $' PERMMSG DB CR,LF,BELCHR,'Permanent error.',LF,CR,'$' RMOUNT: DB CR,LF,LF,'Remount system disk on A: and type' DB ' RETURN.$' page ; Data is read and written on the disks sequentially ; but to speed the processing up the actual transfers ; are performed with a skewing factor of two. TOCC: T128: DB 0, 2, 4, 6, 8 ; ,10,12,14,16,18,20,22,24 DB 1, 3, 5, 7, 9 ; ,11,13,15,17,19,21,23,25 .occ1 IF FALSE T256: DB 0, 1, 4, 5, 8, 9 DB 12,13,16,17,20,21 DB 24,25,28,29,32,33 DB 36,37,40,41,44,45 DB 48,49 DB 2, 3, 6, 7,10,11 DB 14,15,18,19,22,23 DB 26,27,30,31,34,35 DB 38,39,42,43,46,47 DB 50,51 T512: DB 0, 1, 2, 3, 8, 9,10,11 DB 16,17,18,19,24,25,26,27 DB 32,33,34,35,40,41,42,43 DB 48,49,50,51,56,57,58,59 DB 4, 5, 6, 7, TITLE 'OCCTXT.AST - Osborne Computer Corp System Text.' SVER = 1 ; Legal values for RSIZE are 48 and 64 ; entered from command line IF *o = 0 ; user forgot 'o=64' parameter RSIZE = 64 ; default = 64k version ELSE RSIZE = *o ;get machine size ENDIF ;SYSTEXT.AST - Installation Parameter Configuration. ; This Systext provides definitions of system configuration, ; and Ascii control characters for the DigiCon Phoenix ; product set. ; ; This text is configured for CP/m. Another text must be ; used to assemble routines for Phoenix. Both are derived ; from IPARAMS.ACD. space 4,10 ; True/False definitions FALSE = 0 TRUE = not FALSE space 4,10 ODEBUG = FALSE ;True for activating debug code SYcpm20 = 4 ;=cpm 2.0 SYcpm14 = 0 SYmpm10 = 5 SYcdos = 3 @CPM SET TRUE space 4,10 ; List output page/line/column definitions MLPP = 66 ;Number of lines per physical page LLP = 60 ;NUMBER OF LINES TO PRINT PER PAGN ; Disk type and speed constants SEEKTM = 2 ;if MPI, 0 ;if Sieme 'A'-40h ;^A, SOH = Tab with fill BTBCHR = 02h ;^B, STX = BACK TAB CHARACTER ABTCHR = 03h ;^C, ETX = ABORT CHARACTER BELCHR = 07h ;^G, BEL = Audible Alarm BKSCHR = 08h ;^H, BS = PHYSICAL BACKSPACE CHARACTER TABCHR = 09h ;^I, HT = Tab LF = 0Ah ;^J, LF = Line Feed VTAB = 0Bh ;^K, VT = Vertical Tab FMFD = 0Ch ;^L, FF = Form Feed CR = 0Dh ;^M, CR = Carriage Return LITCHR = 0Eh ;^N, SO = Literal next character SUSCHR = 0Fh ;^O, SI = SUSPEND CHARACTER RTYCHR = 12h ;^R, DC2 = RE-TYPE LINE CHARACTER CTRU = 15h ;^U, NAK = Cancel Line char BWDCHR = 17h ;^W, ETB = Backspace ONE word CANCHR = 18h ;^X, CAN = CANCEL LINE CHARACTER ESC = 1Bh ;^[, ESC = Escape RUBOUT = 7Fh ; DEL = Rubout (accepted as Backspace) ERC = 7Fh ;illegal key REP = 02 ;repeat key CAPS = 03 ;repeat SHTK = 04 ;Shift key CTRL = 05 ;Control key CLRSRN = 'Z'-40h ;Clear screen MCR = 'L'-40h ;Form Feed (move cursor right) PMCHR = '>' ;Prompt character ERCHR = '?' ;Error character BKS = 08h EOFC = 'Z'-40h  ;H= 0, L=xx if 2.0 or > ;H= 1, L=0 if MPM 1.0 OS#IND = 13 ;Initialize/Reset Disk system OS#DSEL = 14 ;Select disk unit OS#OPEN = 15 ;Open file OS#CLOS = 16 ;Close file OS#SDIR = 17 ;Search directory OS#NDIR = 18 ;next entry in directory OS#DEL = 19 ;Delete a file OS#READ = 20 ;READ next pru from file OS#WRT = 21 ;Write one PRU to file OS#CRE = 22 ;Create a file OS#REN = 23 ;rename a file OS#RLV = 24 ;Return login vector OS#RCD = 25 ;Return current disk OS#SDMA = 26 ;Set DMA address ; CPM 2.x functions OS#DIO = 6 ;Console direct I/O OS#GAA = 27 ;Get adr (alloc) OS#SWP = 28 ;Set write protect for disk OS#GRO = 29 ;Get r/o vector OS#SFA = 30 ;Set File attributes OS#GAD = 31 ;Get disk parms address OS#USC = 32 ;Get/set user code OS#RRD = 33 ;Read random OS#WRD = 34 ;Write random OS#FIZ = 35 ;Get file size in records OS#SRD = 36 ;Set random record OS#RDRV = 37 ;Reset drive OS#ADR = 38 ;Access drive OS#FDR = 39 ;Free drive OS#WRZ = 40 ;Write random with zero fill ; Directms then 2 ; Keyboard debounce KBDTM = 1 ;WAIT 1 MILLISECOND FOR DEBOUNCE ; Assembly Constants ; Port definitions (Memory mapped) H.FDC = 2100h ;Floppy disk H.KEY = 2200h ;Keyboard H.SIO = 2A00h ;Serial I/O H.VIO = 2C00h ;Video memory controls SEKTMO = 0FEh ;Seek Time Out error status ; SIO Interface and definitions H.SXMT = H.SIO+1 ;Transmit address H.SREC = H.SIO+1 ;Receive (read from address) H.SCTRL = H.SIO ;Set control reg (write) H.SSTS = H.SIO ;Status reg (read) ; Defined function codes SI.MRST = 0_10_101_11b ;Master reset SI.S16 = 0_10_101_01b ;Select 16x clock, xmit/rec ;8 data, 1 stop bit ;RTS high (no request) SI.S64 = 0_10_101_10b ;Select 64x clock ;...all else identical to SI.S16 SI.1200 = SI.S16 SI.300 = SI.S64 SI.RRDY = 01 ;Receiver ready SI.TRDY = 02 ;Transmit ready SI.NCTS = 0000_1000b ; ~CTS SI.RERR = 0111_0000b ;Rcvr err: PE, OVRN, FE H.IEEE = 2900h ;IEEE interface CPDRA EQU H.IEEE+0 ;Peripheral/Direction register A  ;Eof character (same as cpm) TAB = 09h INVV = 80h ;Inverse video bit GESC = 'a' ;Graphics escape toggle char ; CP/m definitions CVER = 22 ;CPM version number CBIOSV = SVER ;cbios version NDSKS = 2 K = 1024 MSIZE = RSIZE-(LVMEM/K) BIOS = MSIZE*k-0600h ;Start of CPM jump table BDOS = BIOS-0E00h+6 ;BDOS entry pt CCP = BIOS-1600h ;CCP entry ABTC = 'C'-40h ;Abort character ALVS: = 0Ch ;*** must be validated with CBIOS *** CSVS: = 20h ;*** --------------------------- *** LSECB = 256 ;Length of a sector MTRK = 40 ;Maximum tracks MSEC = 10 ;Maximum Sectors FMTCHR = 0E5h ;Data format char space 4,10 ; Set CP/M symbols. IOBYTE = 3 CDISK = 4 ;Current logged-in CPM disk SYS = 5 ;CPM Monitor call address SYSL = SYS+1 ;(lwa of CP/m) TIMPTR = 40h ;points to where time kept DBUF = 80h ;Default disk buffer OM#LRAM = SYSL SYSR = 0 ;return to system SYSDSK = CDISK ;Contains current system disk # SYSDAT = 10h ;Date in ddmmyy SYSTIM = SYSDAT+3 ;Time in hhmm BIO call definitions BCONST = 2*3 ;Console status BCONIN SET 3*3 BCOUT = 4*3 ;Console output BLIST = 5*3 ;List output BPUNCH = 6*3 ;Punch output BRDR = 7*3 ;Reader input BHOME = 8*3 ;Home disk drive BSDSK = 9*3 ;Select Disk drive BSTTRK = 10*3 ;Set track BSTSEC = 11*3 ;Set sector BSTDMA = 12*3 ;Set DMA BREAD = 13*3 ;READ disk pru BWRITE = 14*3 ;WRITE disk pru BLSTS = 15*3 ;List LST: device status BSECTR = 16*3 ;Sector translate routine ; Extensions added by SORCIM BRRXT = 17*3 ;Ram-Rom exit BROMJP = 18*3 ;Rom jump entry BFMTJP = 19*3 ;Format Track BSBAUD = 20*3 ;Set Serial Port Baud Rate ;BRDXY = 21*3 ;Read XY ;BWTXY = 22*3 ;Write XY ; IEEE-488 calls BIECO = 21*3 ;Control Out BIESI = 22*3 ;Status In BIEGTS = 23*3 ;Go To Standby BIETC = 24*3 ;Take Control BIEOIM = 25*3 ;Output Interface Message BIEODM = 26*3 ;Output Device Message BIEIDM = 27*3 ;Input Device Message BIEPP = 28*3 ;Parallel Poll ;BIOCAL Used to make calls directly to BIO BIOCAL MACRO NAME LXI HL,L%K  CCRA EQU H.IEEE+1 ;Control register A CPDRB EQU H.IEEE+2 ;Peripheral/Direction register B CCRB EQU H.IEEE+3 ;Control register B ROMVEC = 100h ;Start of rom vector BKPI = 0CFh ;Breakpoint interrupt = 1 NMIA = 66h ;NMI address LCMDP = 4*1024 ;L command prom .siz IF RSIZE = 48 MSG 'Assembly for 48 k Mode 1 system' MRAM = 0AD80h ;FWA for Monitor ram FWAVM = 0B000h ;FWA of video memory NVDL = 24 .siz ELSE MSG 'Assembly for 64k Mode 2 system' MRAM = 0ED80h FWAVM = 0F000h NVDL = 24 INTBL = 0EFF0h ;interrupt vector table .siz ENDIF LVMEM = 128*32 ;Length of video memory LVDIS = 128*NVDL ;Length of Display memory VFLO = -22 ;First line video offset VLL = 128 ;Length of one video line VLDL = 52 ;Length of displayed char/line FWASM = FWAVM+VLDL ;Status info LWAMEM = FWAVM+LVMEM-1 @FREQ = 4000 ;freq of processor SCLFRE = @FREQ/25 ;for DELAY routine ; Define Ascii Control Codes. ; Value ;^Control Meaning in Phoenix EOS = 0 ;^@, NUL = End of String TBFCHR =ss ORGP = 100h ;Transient program origin ;EOL does not exist in CP/M EOM = '$' ;End of Message (for system function) EOF = 'Z'-40h ;End of File L$PRU = 128 ;Bytes per floppy sector FCB = 5Ch ;SYSTEM DEFAULT FILE CONTROL BLOCK FCB_NR = FCB+32 ;Next Record = LRN F_EX = 12 ;Extend field F_S2 = 14 ;CPM system usage **used with 2.0 SIZ funct** F_RC = 15 ;Record count (0 to 128) F_NR = 32 ;Next record ordinal in FCB F_RRF = 33 ;r0,r1,r2 (random field) in FCB L_FILEN = 8 ;length of File name L_FILEX = 3 ;length of filename suffix L_FCB = 36 ;length of entire FCB (comp with 2.0) DMA = 80h ;WHERE SECTORS ARE READ TBUFF = DMA ;where CCP puts command line OS#RCO = 1 ;Read character from CRT: OS#WCO = 2 ;Write character to CRT: device OS#RRO = 3 ;Read from reader OS#WPO = 4 ;Write to punch OS#WLO = 5 ;Write character to LST: device OS#OST = 9 ;Write string on CRT: OS#RCB = 10 ;Read Console Buffer OS#CABT = 11 ;Get Console status OS#VER = 12 ;Return version in HL ;H= 0, L=0 if 1.4 PUSH HL ;return LHLD SYSR+1 MVI L,%1 PCHL L%K: ENDM space 4,10 ;SYSCAL ; This macro is to be used when the common deck ; SYS.ACD is used to make file action ; requests. SYSCAL MACRO NAME,ARG .1 IF ' %2' <> ' ' LXI DE,%2 .1 ENDIF CALL %1 ENDM space 4,10 ; SYSTEM This macro provides a mechanism to call the ; system directly. SYSTEM MACRO FUN,ARG .2 IF DEF OS#%1 MVI C,OS#%1 .1 IF ' %2' <> ' ' LXI DE,%2 .1 ENDIF CALL SYS .2 ELSE CALL %1 .2 ENDIF ENDM ; ALIGN Set origin to even boundary. ALIGN MACRO SIZ,O DS (*%2+(%1)-1)/(%1)*(%1)-*%2 ENDM ; DI and EI are macros so all usages can be accounted ; for in the cr. DI MACRO DB 0F3h ENDM EI MACRO DB 0FBh ENDM ; Macros to enable ROM, RAM and DIM code ENAROM MACRO OUT 0 LDK A,0 STO A,ROMRAM ENDM DISROM MACRO OUT 1 LDK A,1 STO A,ROMRAM ENDM BRTBIT: = 80h ;set brt/dim memory BRIGHT DIMBIT: = 00h ;set brt/dim memory DIM ENADIM MACRO OUT 2 ENDM DISDIM MACRO RNZ :2 ;if not done JMP START ;LOOP FOREVER TITLE 'USEFUL PROC' ; +---------------+ ; | | ; | USEFUL PROC"S | ; | | ; +---------------+ CONIN: LDK C,1 CALL SYS ;BDOS CMP 'a' RC ;IF NOT LOWER CASE CMP 'z'+1 RNC SUI 'a'-'A' ;MAKE UPPER CASE RET DRD: LDK DE,0127H ;BIOS READ SEG JMP LD HL,0001H ;ADDRESS 1 LDK L,36H ;ROMJMP JMP [HL] DERR: LDK DE,ERROR CALL PRINT CALL CONIN ;GET ANSWER JMP START PRINT: LDK C,9 JMP SYS ;PRINT STRING ERROR: DB 1AH ;CLEAR SCREEN DB LF,LF,LF,LF,LF,LF DB ' ****ERROR****' DB CR,LF DB ' BAD TRANSFER' DB CR,LF,LF,LF,LF DB '...any character to restart' DB CR,LF,LF DB '$' ; end  DB CR,LF,LF,'$' ; loop writing data out :2: LDK B,MSEC CALL DRD ;read entire track JNZ DERR ;if error LD HL,DMADR LDK DE,TRLTH ADD HL,DE STO HL,DMADR ;reset DMA LD HL,SAVTRK INC HL STO HL,SAVTRK ;update track MOV A,L CMP 3 Jd,3rd bytes = branch adrs) ; (table repeats [3 byte entries]) ; E is table size (no. of entries) ; (table body is followed with ; 2 byte "No-Match" adrs) ; C = char ; A = value to scan for possible match CMP [HL] INC HL ;(2nd byte of this 3 byte entry) JRZ VECTOR ;If match process INC HL ;(3rd byte of this entry) INC HL ;1st byte of next entry DEC E ;Dec count of entries remaining JRNZ LOOKUPB ;Continue thru body of table ; JR VECTOR ;No-Match. hl=points to vector VECTOR: ;entry point. note hl on stack. LD A,[HL] ;1st byte (low order adrs) INC HL LD H,[HL] ;2nd byte (hi order adrs) MOV L,A ;HL=adrs from table MOV A,C JMP [hl] page ; All command processors LCBS: ;Load CBASIC LDK HL,CBSC CALL PCMD LDK DE,CBSP ;CBASIC prompt JR GETARG ;get argument(s) LMBS: ;Load MBASIC LDK HL,MBSC JR NOARG ;no arguments necessary LSC: ;Load Supercalc LDK HL,SCC JR NOARG LWS: ;Load Wordstar LDK HL,WSC CALL PCMD ;clear & fill buffers OUT 3 ENDM ; Endx SYSTEXT eck ; SYS.ACD is used to make file action ; requests. SYSCAL MACRO NAME,ARG .1 IF ' %2' <> ' ' LXI DE,%2 .1 ENDIF CALL %1 ENDM space 4,10 ; SYSTEM This macro provides a mechanism to call the ; system directly. SYSTEM MACRO FUN,ARG .2 IF DEF OS#%1 MVI C,OS#%1 .1 IF ' %2' <> ' ' LXI DE,%2 .1 ENDIF CALL SYS .2 ELSE CALL %1 .2 ENDIF ENDM ; ALIGN Set origin to even boundary. ALIGN MACRO SIZ,O DS (*%2+(%1)-1)/(%1)*(%1)-*%2 ENDM ; DI and EI are macros so all usages can be accounted ; for in the cr. DI MACRO DB 0F3h ENDM EI MACRO DB 0FBh ENDM ; Macros to enable ROM, RAM and DIM code ENAROM MACRO OUT 0 LDK A,0 STO A,ROMRAM ENDM DISROM MACRO OUT 1 LDK A,1 STO A,ROMRAM ENDM BRTBIT: = 80h ;set brt/dim memory BRIGHT DIMBIT: = 00h ;set brt/dim memory DIM ENADIM MACRO OUT 2 ENDM DISDIM MACRO ;OCCAUTOA.ASM TITLE 'AUTOST - Autostart program for OCC-1' * 4D2013-00 MASTER .ASM * 2D2013-00 ASSY .ASM * 1D2013-00 LISTING .PRN * 4D1013-00 MASTER .COM * 2D1013-00 ASSY .COM ; +---------------+ ; | | ; | AUTOSTART | ; | | ; +---------------+ ;REV = A ;DATE = 256 VER = '00' LEVEL = '01' ; AUTOST is the first program called into execution from the ; Cold Boot. Its purpose is to provide a user friendly interface ; to the user and to provide a convenient prompt for the standard ; OCC-1 software. ; ; Author: ; Sorcim Corporation ; Santa Clara, CA ; Richard H. Frank ; Copyright Sorcim Corporation. ; Assembly switches, constants VERS = 22 ;CPM version number LIND = 5 ;indent for screen HLCHR = 'V'-40h ;highlight char V_SD = ')' ;start dim field V_ED = '(' ;end dim field V_SU = 'l' ;start underline V_EU = 'm' ;end underline COMADR = CCP+88h ;address of next char to scan @CASE = 20h ;UPPER case @ILIST = TRUE ;list internal Link OCCRAM2.as LDK DE,WSP ;WS prompt JR GETARG CBSC DB 26, 'CRUN2 ' MBSC DB 26, 'MBASIC ' SCC DB 26, 'SC ' WSC DB 26, 'WS ' space 4,10 PCMD: ;Put command into buffer ; Entry HL= FWA of command LDK DE,CCP+7 LDK BC,10 LDIR ;copy to CCP buffer RET GOCMD: ;go to CCP to execute the command LK DE,PRMPTX ;exit prompt CALL ESTR ;clear screen, reset underline,etc LDK HL,CCP+8 STO HL,COMADR ;reset scan for command LDK C,1 ;set drive to B: LD SP,CSTK JMP CCP ;fire up program page NOARG: ;process command w/o arguments CALL PCMD JR GOCMD space 4,10 GETARG: ;process command with argmuents PUSH DE ;save FWA of command-prompt LK DE,PRMPT2 CALL ESTR ;begin 2nd prompt POP DE CALL ESTR ;output command-name LK DE,PRMPT3 CALL ESTR ;finish 2nd prompt ;prompt is displayed- now prepare input buffer & get ;filename-argument from user LK HL,15 ;max len of a filename + 1 STO HL,CCP+17 ;set max¤t buf lengths SYSTEM RCB,CCP+17 ;input from consol;RDSYS.WIP ;READ OUT SYSTEM TRACKS(0-2) FORM 04000H ;REV A ;DATE = 237 ;VERSION 1 ;DEB ;MADE ORIGINAL PROGRAM LINK OCCRAMA.ASM TRLTH = 100H*10 ; Entry Memory has valid system image. ; Exit system read. ORG 0100H START: LDK HL,4000h STO HL,DMADR ;set DMA LDK A,1 STO A,SDISK ;SET DRIVE TO B STO A,SAVSEC ;set to 1st sector LDK HL,0 STO HL,SAVTRK ;set track LDK DE,BGMES ;MESSAGE CALL PRINT CALL CONIN ;GET RESPONSE CMP 'Y' JZ :2 ;BEGIN TRANSFER IF YES LDK C,0 CMP 'N' JZ SYS ;REBOOT IF NO JMP START ;LOOP BGMES: DB 01AH ;CLEAR SCREEN DB LF,LF,LF,LF,LF,LF DB 'READ SYSTEM TRACKS FROM DRIVE "B"' DB CR,LF DB ' FROM 4000H.' DB CR,LF,LF,LF,LF DB 'To end type "N" to continue type "Y".' DB CR,LF,LF,'$' ; loop writing data out :2: LDK B,MSEC CALL DRD ;read entire track JNZ DERR ;if error LD HL,DMADR LDK DE,TRLTH ADD HL,DE STO HL,DMADR ;reset DMA LD HL,SAVTRK INC HL STO HL,SAVTRK ;update track MOV A,L CMP 3 Jm page ORG ORGP AUTOST: STO SP,CSTK ;save cpm stack LDK SP,STK LDK DE,PRMPT CALL ESTR ;output initial prompt ; Main Loop. AUT1: CALL GCHR ;get command chr CALL NORM MOV C,A LK E,ENTY# LK HL,CMDTBL JR LOOKUPB NULL: ;Null command NXTCMD: ;Exit from command processor JR AUT1 GCHR: ;Get a Command Chr SYSTEM RCO RET NORM: ;Normalize chr in A ; Uses F. CMP 'a' RC ;if UPPER CASE CMP 'z'+1 RNC ;if not lower case alphabetic SUB 'a' xor 'A' RET CMDTBL: ;Command Table ; 3 bytes per entry: Ascii chr, "DW"-vector ; #entries = CMD#S ; last entry is "DW"-vector to "No-match" addr DB 'A' ! DW LCBS ;load CBASIC DB 'B' ! DW LMBS ;Load MBASIC DB 'C' ! DW LSC ;Load Suprcalc DB 'D' ! DW LWS ;Load Wordstar DB 'E' ! DW EXIT ;exit to CPM :end: DW NULL ;null command ; #entries in CMDTBL ENTY# EQU (:end - CMDTBL)/3 LOOKUPB: ;Logic to scan 3 byte branch table ; Not a subroutine; do not CALL. ; Entry HL =1st byte of table (match code) ; (2ne into CCP buf LD A,CCP+18 ;get len of input ADD A,8+1+2 ;add in len of prev info STO A,CCP+7 ;set length of cmd line ;terminate cmd line with blank chr MOV E,A ! LK D,0 ;DE = len of cmd line LK HL,CCP+8 ;HL = FWA of cmd line ADD HL,DE ;HL => LWA+1 of filename STO ' ',[hl] ;terminate filename with space ;erase 2 bytes of 'Read Console Buffer' housekeeping info LK HL,' ' STO HL,CCP+17 ;erase len info from buffer ;NORMALIZE filename LK B,12 ;max len of filename LK DE,CCP+19 :loop: LD A,[de] ;get chr CALL NORM STO A,[de] ;set to UPPER CASE INC DE ;next chr DJNZ :loop ;repeat for max len of a filename JR GOCMD ;execute the command space 4,10 CFCB: ;Clear entire FCB area LDK B,40 LDK HL,FCB FILLZ: XRA A :2: STO A,[hl] INC HL DJNZ :2 RET EXIT: ;Exit to CP/M LK DE,PRMPTX ;exit prompt CALL ESTR LD SP,CSTK RET page ESTR: ;Output ENCODED string to console ; Entry DE=FWA of source OR A ;clear cbit LD A,[de] RRC ;normalize chEC6: = HOURS+3 ; Used to deselect drive when there is NO activity ; on drive for n seconds. See FDSK routine DACTVE: = HOURS+4 ;=0 by FDSK, Used by UPTIM BELCNT: = HOURS+5 ;^G bell timer cell LLIMIT DS 1 ;max #columns in a logical line ; MSG 'LLIMIT = ',LLIMIT,'h.' ; Disk drive current positions LDSEL: DS 2 ;Last selected drive LDTRK = LDSEL+1 ;Last track used for non-selected drive IESTK: DS 2 ;save current stk ptr ; Interrupt stack DS 20*2 ISTK: DS 0 ; Stack entry DS 20*2 BIOSTK: ROMSTK: DS 0 ACIAD: DS 1 ;last command byte written to ACIA R179x: DS 4 ;179x register save area KBDLY: DS 1 ;keyboard debounce-delay cell ;since CP/M CANNOT boot off B:, this cell is used ;to invert the names of the 2 drives: ; =0, all normal, A=A:, B=B: ; =1, all inverted, A=B:, B=A: DSKSWP DS 1 ; Z80 Alternate Register Set ALIGN 10h RAGS: DESAX: DS 2 ;DE` BCSAX: DS 2 ;BC` AFSAX: DS 2 ;AF` HLSAX: DS 2 ;HL` IXSAX: DS 2 ;IX IYSAX: DS 2 ;IY IVSAX: DS 2 ;Interrupt ar PUSH AF PUSH DE AND 07Fh MOV E,A SYSTEM WCO ;output it POP DE INC DE POP AF JRNC ESTR ;if not done RET space 4,10 OSTR: ;Output string (NOT encoded) ; Entry DE=FWA of source LD A,[de] ;next data byte PUSH AF PUSH DE AND 07Fh MOV E,A SYSTEM WCO ;output it POP DE INC DE POP AF AND 80h JRZ OSTR ;if not done RET Page SETXY MACRO Y,X DBE ESC,'=',%1+' ',%2+' ' ENDM BUL = cr ;Bullit graphic PRMPT: DBE 'Z'-40h ;clear screen DBE ESC,'g' ;Start Graphics SETXY 2,11 DBE ESC,'l' ;start underline DBE bul,bul DBE 'Osborne Computer System One' DBE bul,bul SETXY 3,15 DBE 'CPM System Level ' DBE VERS/10+'0', '.' ,VERS mod 10+'0' DBE ESC,'m' ;end underline SETXY 5,3 DBE cr,' Indicate program you want to use by entering' SETXY 7,LIND DBE 'A. CRUN2 - run a compiled CBASIC program.' SETXY 9,LIND DBE 'B. MBASIC - BASIC Interpreter.' SETXY 11,LIND DBE 'C. SUPERCALC - Electronic worksheet.' SETXY 13,LIND DBE 'D. W TITLE 'BMRAM - Debug Monitor RAM Storage.' ;MRAM - Debug Monitor RAM Storage. ; Used to assembly ROM resident and CBIOS ORG MRAM ; Host disk xfer buffer and... ; Format track template holding buffer HSTBUF: DS 256+128 ; Directory Buffer DIRBUF: = HSTBUF+256 TEM DS 6 RNDV = TEM+1 ;random number seed ERCNT = RNDV+1 ;DW ERCNT RTRC = ERCNT+2 ;retry count RTRY = RTRC+1 MPCHR DS 1 ;prompt character ECHOP DS 1 ;=0, list ehco off ROMRAM DS 1 ;0= RAM, 1= ROM DSTSB DS 6 ;Disk status bytes ; Disk operation temps and control DMADR DS 2 ;Address for read/write Disk DMAADR DS 2 ;CBIOS, users DMA ; Note order of xxxSEC,xxxTRK,xxxDSK must be maintained ; along with length (1,2,1). SEKDEL: DS 1 ;Set for seek-restore command in ROM ;depends on disk type. Siemens = 3h, MPI = 0h SAVSEC DS 1 ;last sector requested SAVTRK DS 2 ;last track requested SDISK DS 1 ;Selected disk drive (0,1) ACTSEC = SAVSEC ACTTRK = SAVTRK ACTDSK = SDISK SEKSEC DS 1 SEKTRK DS 2 SEKDSK DS 1 page register ; 8080 Register Save Area. ALIGN 10h REGS: ESAVE: DS 1 ;E Register save location DSAVE: DS 1 ;D Register save location CSAVE: DS 1 ;C Register save location BSAVE: DS 1 ;B Register save location FSAVE: DS 1 ;FLAGS save location ASAVE: DS 1 ;A Register save location LSAVE: DS 1 ;L Register save location HSAVE: DS 1 ;H Register save location PSAVE: DS 2 ;PGM COUNTER save location SSAVE: DS 2 ;USER STACK pointer save location BKPA: DS 2 ;last breakpoint address BKPC: DS 1 ;Contents of bkp ; Interrupt Jump Vector is between EFFO, EFFF. VRTOFF DS 1 ;LAST VERTICAL OFFSET TAKEN FROM COUT PAGE ; Endx MRAM 0, all normal, A=A:, B=B: ; =1, all inverted, A=B:, B=A: DSKSWP DS 1 ; Z80 Alternate Register Set ALIGN 10h RAGS: DESAX: DS 2 ;DE` BCSAX: DS 2 ;BC` AFSAX: DS 2 ;AF` HLSAX: DS 2 ;HL` IXSAX: DS 2 ;IX IYSAX: DS 2 ;IY IVSAX: DS 2 ;Interrupt ORDSTAR - Word processor.' SETXY 15,LIND DBE 'E. Enter normal CP/M command line system.' DBE ESC,'G' SPMPT: SETXY 21,0 DBE ESC,'T' DCE 'Command: ' PRMPT2: DBE EOF DBE lf,lf,lf,lf,lf,lf,lf,lf,lf,lf ;10 Line Feeds DCE ' ' ;06 spaces PRMPTX: DBE ESC,V_ED, ESC,V_EU ! DCE CLRSRN PRMPT3: DBE ' needs a filename to work with.', cr, lf DBE ' Please type the filename to be used:' DBE cr,lf,lf, ESC,V_SD DBE ' type here: ' DBE ESC,V_SU,ESC,V_ED, ' ', ESC,V_EU,ESC,V_SD DBE ' (end with RETURN)' DBE cr,ESC,V_ED DBE ' ',mcr,mcr,mcr,mcr,mcr, mcr,mcr,mcr,mcr,mcr,mcr DBE ESC ! DCE V_SU WSP: DCE 'WORDSTAR' CBSP: DCE 'CRUN2 ' DS 2*30 STK: DS 0 CSTK: DS 2 ; END AUTOST ndicate program you want to use by entering' SETXY 7,LIND DBE 'A. CRUN2 - run a compiled CBASIC program.' SETXY 9,LIND DBE 'B. MBASIC - BASIC Interpreter.' SETXY 11,LIND DBE 'C. SUPERCALC - Electronic worksheet.' SETXY 13,LIND DBE 'D. W HSTSEC DS 1 HSTTRK DS 2 HSTDSK DS 1 TEMSEC DS 1 ;Used in bios only RDFLAG DS 1 ;Read flag ERFLAG DS 1 ;Error reporting WRTYPE DS 1 ;Write operation type ALV: DS ALVS CSV: DS CSVS ; BIOS blocking-deblocking flags HSTACT: DS 1 ;host active flag HSTWRT: DS 1 ;Host written flag UNACNT: DS 1 ;Unalloc rec count UNATRK: DS 2 ;Track UNASEC: DS 1 ;Sector LOGSEC: DS 1 ;Logical sector LDADR DS 2 KEYLCK DS 1 ;Zero if locked keyboard CURS DS 2 ;current cursor position ; Keyboard scan temporaries TKEY DS 1 ;Tem holding key HKCNT DS 1 ;Debounce key LKEY DS 1 ;Last valid keystroke CKEY DS 1 ;Last control key ESCH DS 1 ;ESC holding flag ;PIAAD and PIABD must be kept sequential, PIAAD first ;dependency in VC_HOME of BMKEY.asm PIAAD: DS 1 ;Holds last PIA-A data PIABD: DS 1 ;Holds last PIA-B data ; Calendar month, day year IDAY DS 3 IMONTH = IDAY+1 IYR = IDAY+2 ; Wall clock time cells and disk active ; see UPTIM: in BMKEY.asm HOURS: DS 6 MINS: = HOURS+1 SECS: = HOURS+2 S TITLE 'DISK I/O ROUTINES.' ; DISK INTERFACE DEFINITIONS AND FUNCTIONS. ; 1792 FUNCTIONS. DML = 4 ;= 15 MS DELAY ON FUNCTION LTRKB = 3125 ;LENGTH OF A TRACK ; NOTE: SHUGART SAYS LTRKB = 3125... NRTRYS = 4 ;NUMBER OF RETRIES MSECB = 10H ;MULTI-SECTOR R/W BIT ; FUNCTION CODES D.RES = 00H+0CH ;RESTORE D.SEK = 10H+08H ;SEEK D.STP = 20H ;STEP D.STPI = 40H ;STEP IN D.STPO = 60H ;STEP OUT D.RDS = 80H ;READ SECTOR D.WRTS = 0A0H+DML ;WRITE SECTOR D.RDA = 0C0H+DML ;READ ADDRESS D.RDT = 0E0H+DML ;READ TRACK D.WRTT = 0F0H+DML ;WRITE TRACK D.FINT = 0D0H ;FORCE INTERRUPT ; DISK STATUS AND COMMAND REGISTERS D.CMDR = H.FDC ;DISK COMMAND REG (WRITE) D.STSR = H.FDC ;STATUS REG (READ) D.TRKR = D.CMDR+1 ;TRACK REG D.SECR = D.CMDR+2 ;SECTOR REG D.DATR = D.CMDR+3 ;DATA REG (R/W) ; STATUS DEFINITIONS BS.BSY = 0 ;BUSY DS.BSY = 1 SHL BS.BSY BS.DRQ = 1 DS.INX = 1 SHL BS.DRQ ;INDEX MARK DETECTED DS.DRQ = DS.INX ;DR IS FULL ON READ, EMPTY ON WRITE BS.TK0 = 2 DS.TK0 = 1 SHL  CCRA EQU H.IEEE+1 ;Control register A CPDRB EQU H.IEEE+2 ;Peripheral/Direction register B CCRB EQU H.IEEE+3 ;Control register B ROMVEC = 100h ;Start of rom vector BKPI = 0CFh ;Breakpoint interrupt = 1 NMIA = 66h ;NMI address LCMDP = 4*1024 ;L command prom .siz IF RSIZE = 48 MSG 'Assembly for 48 k Mode 1 system' MRAM = 0AD80h ;FWA for Monitor ram FWAVM = 0B000h ;FWA of video memory NVDL = 24 .siz ELSE MSG 'Assembly for 64k Mode 2 system' MRAM = 0ED80h FWAVM = 0F000h NVDL = 24 INTBL = 0EFF0h ;interrupt vector table .siz ENDIF LVMEM = 128*32 ;Length of video memory LVDIS = 128*NVDL ;Length of Display memory VFLO = -22 ;First line video offset VLL = 128 ;Length of one video line VLDL = 52 ;Length of displayed char/line FWASM = FWAVM+VLDL ;Status info LWAMEM = FWAVM+LVMEM-1 @FREQ = 4000 ;freq of processor SCLFRE = @FREQ/25 ;for DELAY routine ; Define Ascii Control Codes. ; Value ;^Control Meaning in Phoenix EOS = 0 ;^@, NUL = End of String TBFCHR =BS.TK0 DS.LSD = DS.TK0 ;LOST DATA DS.CRC = 08H ;CRC ERROR IN ID FIELD BS.SEK = 4 DS.SEK = 1 SHL BS.SEK ;SEEK ERROR DS.RNF = DS.SEK ;RECORD NOT FOUND DS.HDL = 20H ;HEAD LOADED DS.WTF = DS.HDL ;WRITE FAULT DS.WTP = 40H ;WRITE PROTECTED DS.NRY = 80H ;DRIVE NOT READY ; DISK TIMING COUNTS D.DEL = 20 ;DELAY AFTER FUNCTION LINK BMRAM.ASM PAGE ORG ORGP JMP PSTAR ORG 4000H PSTAR ENAROM ;ENABLE ROM LDK A,0 STO A,SAVTRK LDK A,1 STO A,SDISK STO A,SAVSEC CALL SELDSK CALL SDRV :2: LDK A,D.RDT ;REQUEST READ TRACK DI CALL FDSK ;REQUEST WRITE EI DISROM JNZ SYS ;IF ERROR ENAROM DI ; LOAD BC WITH LENGTH AND SET LDK BC,4000H ;NUMB OF BYTES LDK HL,BUFF ;FWA OF BUFFER ; NOW WRITE OUT DATA :4: CALL XRDT ;XFER TO MEMORY DISROM LDK C,0 JMP SYS ;END XRDT: PROC ;XFER DATA FROM DISK TO MEMORY ; ENTRY BC = LENGTH ; HL = FWA ; EXIT HL = NEXT ADDRESS ; A=0 IF NO ERROR, ELSE STATUS ; USES A,BC,E, HL PRO TITLE 'OCCTXT.AST - Osborne Computer Corp System Text.' SVER = 1 ; Legal values for RSIZE are 48 and 64 ; entered from command line IF *o = 0 ; user forgot 'o=64' parameter RSIZE = 64 ; default = 64k version ELSE RSIZE = *o ;get machine size ENDIF ;SYSTEXT.AST - Installation Parameter Configuration. ; This Systext provides definitions of system configuration, ; and Ascii control characters for the DigiCon Phoenix ; product set. ; ; This text is configured for CP/m. Another text must be ; used to assemble routines for Phoenix. Both are derived ; from IPARAMS.ACD. space 4,10 ; True/False definitions FALSE = 0 TRUE = not FALSE space 4,10 ODEBUG = FALSE ;True for activating debug code SYcpm20 = 4 ;=cpm 2.0 SYcpm14 = 0 SYmpm10 = 5 SYcdos = 3 @CPM SET TRUE space 4,10 ; List output page/line/column definitions MLPP = 66 ;Number of lines per physical page LLP = 60 ;NUMBER OF LINES TO PRINT PER PAGN ; Disk type and speed constants SEEKTM = 2 ;if MPI, 0 ;if Sieme 'A'-40h ;^A, SOH = Tab with fill BTBCHR = 02h ;^B, STX = BACK TAB CHARACTER ABTCHR = 03h ;^C, ETX = ABORT CHARACTER BELCHR = 07h ;^G, BEL = Audible Alarm BKSCHR = 08h ;^H, BS = PHYSICAL BACKSPACE CHARACTER TABCHR = 09h ;^I, HT = Tab LF = 0Ah ;^J, LF = Line Feed VTAB = 0Bh ;^K, VT = Vertical Tab FMFD = 0Ch ;^L, FF = Form Feed CR = 0Dh ;^M, CR = Carriage Return LITCHR = 0Eh ;^N, SO = Literal next character SUSCHR = 0Fh ;^O, SI = SUSPEND CHARACTER RTYCHR = 12h ;^R, DC2 = RE-TYPE LINE CHARACTER CTRU = 15h ;^U, NAK = Cancel Line char BWDCHR = 17h ;^W, ETB = Backspace ONE word CANCHR = 18h ;^X, CAN = CANCEL LINE CHARACTER ESC = 1Bh ;^[, ESC = Escape RUBOUT = 7Fh ; DEL = Rubout (accepted as Backspace) ERC = 7Fh ;illegal key REP = 02 ;repeat key CAPS = 03 ;repeat SHTK = 04 ;Shift key CTRL = 05 ;Control key CLRSRN = 'Z'-40h ;Clear screen MCR = 'L'-40h ;Form Feed (move cursor right) PMCHR = '>' ;Prompt character ERCHR = '?' ;Error character BKS = 08h EOFC = 'Z'-40h C LD A,D.STSR BIT BS.BSY,A JZ :5 ;IF POSSIBLE ERROR BIT BS.DRQ,A JNZ XRDT ;IF NO DATA STO A,[HL] INC HL LD A,D.DATR ;GET FROM CONTROLLER STO A,[HL] INC HL DEC BC MOV A,B OR C JNZ XRDT ;IF NOT DONE XRA A ;INDICATE GOOD XFER RET ; HERE IF ERROR :5: OR 80H ;INDICATE BUSY DROPPED RET SELDSK = 0850H SDRV = 07EEH FDSK = 09ABH BUFF = 05000H ;END  CALL SELDSK CALL SDRV :2: LDK A,D.RDT ;REQUEST READ TRACK DI CALL FDSK ;REQUEST WRITE EI DISROM JNZ SYS ;IF ERROR ENAROM DI ; LOAD BC WITH LENGTH AND SET LDK BC,4000H ;NUMB OF BYTES LDK HL,BUFF ;FWA OF BUFFER ; NOW WRITE OUT DATA :4: CALL XRDT ;XFER TO MEMORY DISROM LDK C,0 JMP SYS ;END XRDT: PROC ;XFER DATA FROM DISK TO MEMORY ; ENTRY BC = LENGTH ; HL = FWA ; EXIT HL = NEXT ADDRESS ; A=0 IF NO ERROR, ELSE STATUS ; USES A,BC,E, HL PROms then 2 ; Keyboard debounce KBDTM = 1 ;WAIT 1 MILLISECOND FOR DEBOUNCE ; Assembly Constants ; Port definitions (Memory mapped) H.FDC = 2100h ;Floppy disk H.KEY = 2200h ;Keyboard H.SIO = 2A00h ;Serial I/O H.VIO = 2C00h ;Video memory controls SEKTMO = 0FEh ;Seek Time Out error status ; SIO Interface and definitions H.SXMT = H.SIO+1 ;Transmit address H.SREC = H.SIO+1 ;Receive (read from address) H.SCTRL = H.SIO ;Set control reg (write) H.SSTS = H.SIO ;Status reg (read) ; Defined function codes SI.MRST = 0_10_101_11b ;Master reset SI.S16 = 0_10_101_01b ;Select 16x clock, xmit/rec ;8 data, 1 stop bit ;RTS high (no request) SI.S64 = 0_10_101_10b ;Select 64x clock ;...all else identical to SI.S16 SI.1200 = SI.S16 SI.300 = SI.S64 SI.RRDY = 01 ;Receiver ready SI.TRDY = 02 ;Transmit ready SI.NCTS = 0000_1000b ; ~CTS SI.RERR = 0111_0000b ;Rcvr err: PE, OVRN, FE H.IEEE = 2900h ;IEEE interface CPDRA EQU H.IEEE+0 ;Peripheral/Direction register A   ;Eof character (same as cpm) TAB = 09h INVV = 80h ;Inverse video bit GESC = 'a' ;Graphics escape toggle char ; CP/m definitions CVER = 22 ;CPM version number CBIOSV = SVER ;cbios version NDSKS = 2 K = 1024 MSIZE = RSIZE-(LVMEM/K) BIOS = MSIZE*k-0600h ;Start of CPM jump table BDOS = BIOS-0E00h+6 ;BDOS entry pt CCP = BIOS-1600h ;CCP entry ABTC = 'C'-40h ;Abort character ALVS: = 0Ch ;*** must be validated with CBIOS *** CSVS: = 20h ;*** --------------------------- *** LSECB = 256 ;Length of a sector MTRK = 40 ;Maximum tracks MSEC = 10 ;Maximum Sectors FMTCHR = 0E5h ;Data format char space 4,10 ; Set CP/M symbols. IOBYTE = 3 CDISK = 4 ;Current logged-in CPM disk SYS = 5 ;CPM Monitor call address SYSL = SYS+1 ;(lwa of CP/m) TIMPTR = 40h ;points to where time kept DBUF = 80h ;Default disk buffer OM#LRAM = SYSL SYSR = 0 ;return to system SYSDSK = CDISK ;Contains current system disk # SYSDAT = 10h ;Date in ddmmyy SYSTIM = SYSDAT+3 ;Time in hhmm BIO call definitions BCONST = 2*3 ;Console status BCONIN SET 3*3 BCOUT = 4*3 ;Console output BLIST = 5*3 ;List output BPUNCH = 6*3 ;Punch output BRDR = 7*3 ;Reader input BHOME = 8*3 ;Home disk drive BSDSK = 9*3 ;Select Disk drive BSTTRK = 10*3 ;Set track BSTSEC = 11*3 ;Set sector BSTDMA = 12*3 ;Set DMA BREAD = 13*3 ;READ disk pru BWRITE = 14*3 ;WRITE disk pru BLSTS = 15*3 ;List LST: device status BSECTR = 16*3 ;Sector translate routine ; Extensions added by SORCIM BRRXT = 17*3 ;Ram-Rom exit BROMJP = 18*3 ;Rom jump entry BFMTJP = 19*3 ;Format Track BSBAUD = 20*3 ;Set Serial Port Baud Rate ;BRDXY = 21*3 ;Read XY ;BWTXY = 22*3 ;Write XY ; IEEE-488 calls BIECO = 21*3 ;Control Out BIESI = 22*3 ;Status In BIEGTS = 23*3 ;Go To Standby BIETC = 24*3 ;Take Control BIEOIM = 25*3 ;Output Interface Message BIEODM = 26*3 ;Output Device Message BIEIDM = 27*3 ;Input Device Message BIEPP = 28*3 ;Parallel Poll ;BIOCAL Used to make calls directly to BIO BIOCAL MACRO NAME LXI HL,L%K ;OCCDLDA.ASM TITLE 'DOWNLD PROGRAM.' ; +-----------------------+ ; | | ; | DOWN LOAD | ; | | ; +-----------------------+ ;REV = A ;DATE = 270 ;DEB LINK OCCRAM1.ASM BOOT = 0000H ;WARM START ADDRESS ORG 100H LD HL,SYS+1 ;START OF BDOS OR DDT MOV SP,HL ;SET STACK TO FWA OF BDOS PAGE ;FILL WITH ZERO'S XRA A LDK DE,PROCEND ;START SBC HL,DE PUSH HL POP BC ;LENTH TO BC LDK HL,PROCEND ;END OF PROGRAM STO A,[HL] ;STORE FIRST BYTE DEC BC PUSH HL POP DE INC DE ;DE = HL+1 LDIR ;OVERLAPPING MOVE TILL BC = ZERO ;SIGN ON LDK HL,IMSG CALL STRCON LDK BC,0 LDK HL,PROCEND ;END OF PROGRAM LOOP: CALL CABORT CALL NUMCON ;OUTPUT COUNT PUSH HL LDK HL,BKS6 ;BACKSPACE CALL STRCON POP HL CALL INRDR ;GET BYTE STO A,[HL] ;STORE INC BC INC HL JMP LOOP END: LDK C,0 JMP SYS PAGE CABORT: ;CHECK CONSOLE STATUS FOR POSSIBLE ABORT PROC PUSHAL LDK C,11 CALL SYS ;GET STATUS ORA A JRZ :1 ;IF NOss ORGP = 100h ;Transient program origin ;EOL does not exist in CP/M EOM = '$' ;End of Message (for system function) EOF = 'Z'-40h ;End of File L$PRU = 128 ;Bytes per floppy sector FCB = 5Ch ;SYSTEM DEFAULT FILE CONTROL BLOCK FCB_NR = FCB+32 ;Next Record = LRN F_EX = 12 ;Extend field F_S2 = 14 ;CPM system usage **used with 2.0 SIZ funct** F_RC = 15 ;Record count (0 to 128) F_NR = 32 ;Next record ordinal in FCB F_RRF = 33 ;r0,r1,r2 (random field) in FCB L_FILEN = 8 ;length of File name L_FILEX = 3 ;length of filename suffix L_FCB = 36 ;length of entire FCB (comp with 2.0) DMA = 80h ;WHERE SECTORS ARE READ TBUFF = DMA ;where CCP puts command line OS#RCO = 1 ;Read character from CRT: OS#WCO = 2 ;Write character to CRT: device OS#RRO = 3 ;Read from reader OS#WPO = 4 ;Write to punch OS#WLO = 5 ;Write character to LST: device OS#OST = 9 ;Write string on CRT: OS#RCB = 10 ;Read Console Buffer OS#CABT = 11 ;Get Console status OS#VER = 12 ;Return version in HL ;H= 0, L=0 if 1.4 PUSH HL ;return LHLD SYSR+1 MVI L,%1 PCHL L%K: ENDM space 4,10 ;SYSCAL ; This macro is to be used when the common deck ; SYS.ACD is used to make file action ; requests. SYSCAL MACRO NAME,ARG .1 IF ' %2' <> ' ' LXI DE,%2 .1 ENDIF CALL %1 ENDM space 4,10 ; SYSTEM This macro provides a mechanism to call the ; system directly. SYSTEM MACRO FUN,ARG .2 IF DEF OS#%1 MVI C,OS#%1 .1 IF ' %2' <> ' ' LXI DE,%2 .1 ENDIF CALL SYS .2 ELSE CALL %1 .2 ENDIF ENDM ; ALIGN Set origin to even boundary. ALIGN MACRO SIZ,O DS (*%2+(%1)-1)/(%1)*(%1)-*%2 ENDM ; DI and EI are macros so all usages can be accounted ; for in the cr. DI MACRO DB 0F3h ENDM EI MACRO DB 0FBh ENDM ; Macros to enable ROM, RAM and DIM code ENAROM MACRO OUT 0 LDK A,0 STO A,ROMRAM ENDM DISROM MACRO OUT 1 LDK A,1 STO A,ROMRAM ENDM BRTBIT: = 80h ;set brt/dim memory BRIGHT DIMBIT: = 00h ;set brt/dim memory DIM ENADIM MACRO OUT 2 ENDM DISDIM MACRO  CHARACTOR LDK C,1 CALL SYS ;GET CHARACTOR CMP 3 ; JNZ :1 ;IF NOT CONTINUE JMP END ;END :1: POPALL RET PAGE INRDR: ;GET ONE BYTE FROM READER PORT PROC PUSHAL LDK C,3 CALL SYS POPALL RET PAGE NUMCON: ;OUTPUT DECIMAL NUMBER ; ENTRY BC= NUMBER ; EXIT NUMBER SENT TO CONSOLE PUSH DE PUSH HL PUSH BC POP HL ;HL = BC LDK DE,TEMN CALL CBD5 LDK HL,TEMN CALL STRCON POP HL POP DE RET TEMN: DB ' $' PAGE STRCON: ;OUTPUT MESSAGE TO CONSOLE ;ENTRY ;HL = FWA OF MESSAGE ;EXIT ;NONE PROC PUSHAL EX DE,HL ;VALUE TO DE LDK C,9 ;STRING CALL SYS ;BDOS POPALL RET PAGE CBD5: ;CONVERT BINARY NUMBER TO ASCII DECIMAL ;WITH LEADING ZERO SUPQRESSION. ;ENTRY ;HL = VALUE TO CONVERT ;DE = FWA TO STORE CONVERSION ;EXIT ;NONE PROC PUSHAL LK BC,-10000 CALL CBO ;CONVERT TEN-THOUSANDS DIGIT CALL CLZ CBD4: CBD: LK BC,-1000 CALL CBO ;CONVERT THOUSAODS!DIGIT CALL CLZ ;CHECK LEADING ZEROS CBD3: LK BC,-100 CA ;H= 0, L=xx if 2.0 or > ;H= 1, L=0 if MPM 1.0 OS#IND = 13 ;Initialize/Reset Disk system OS#DSEL = 14 ;Select disk unit OS#OPEN = 15 ;Open file OS#CLOS = 16 ;Close file OS#SDIR = 17 ;Search directory OS#NDIR = 18 ;next entry in directory OS#DEL = 19 ;Delete a file OS#READ = 20 ;READ next pru from file OS#WRT = 21 ;Write one PRU to file OS#CRE = 22 ;Create a file OS#REN = 23 ;rename a file OS#RLV = 24 ;Return login vector OS#RCD = 25 ;Return current disk OS#SDMA = 26 ;Set DMA address ; CPM 2.x functions OS#DIO = 6 ;Console direct I/O OS#GAA = 27 ;Get adr (alloc) OS#SWP = 28 ;Set write protect for disk OS#GRO = 29 ;Get r/o vector OS#SFA = 30 ;Set File attributes OS#GAD = 31 ;Get disk parms address OS#USC = 32 ;Get/set user code OS#RRD = 33 ;Read random OS#WRD = 34 ;Write random OS#FIZ = 35 ;Get file size in records OS#SRD = 36 ;Set random record OS#RDRV = 37 ;Reset drive OS#ADR = 38 ;Access drive OS#FDR = 39 ;Free drive OS#WRZ = 40 ;Write random with zero fill ; DirectOUT 3 ENDM ; Endx SYSTEXT eck ; SYS.ACD is used to make file action ; requests. SYSCAL MACRO NAME,ARG .1 IF ' %2' <> ' ' LXI DE,%2 .1 ENDIF CALL %1 ENDM space 4,10 ; SYSTEM This macro provides a mechanism to call the ; system directly. SYSTEM MACRO FUN,ARG .2 IF DEF OS#%1 MVI C,OS#%1 .1 IF ' %2' <> ' ' LXI DE,%2 .1 ENDIF CALL SYS .2 ELSE CALL %1 .2 ENDIF ENDM ; ALIGN Set origin to even boundary. ALIGN MACRO SIZ,O DS (*%2+(%1)-1)/(%1)*(%1)-*%2 ENDM ; DI and EI are macros so all usages can be accounted ; for in the cr. DI MACRO DB 0F3h ENDM EI MACRO DB 0FBh ENDM ; Macros to enable ROM, RAM and DIM code ENAROM MACRO OUT 0 LDK A,0 STO A,ROMRAM ENDM DISROM MACRO OUT 1 LDK A,1 STO A,ROMRAM ENDM BRTBIT: = 80h ;set brt/dim memory BRIGHT DIMBIT: = 00h ;set brt/dim memory DIM ENADIM MACRO OUT 2 ENDM DISDIM MACRO  LL CBO CALL CLZ CBD2: LK BC,-10 CALL CBO CALL CLZ MOV A,L ADI '0' STAX D LK A,'0' STA CLZA ;RESET TO IGNORE LEADING ZEROS! POPALL RET ;CBO CONVERT ONE DIGIT TO DECIMAL HEX ; ENTRY (A) = NUMBER CBO: LK A,'0' PUSH DE CBO1: MOV E,L MOV D,H INR A DAD B JC CBO1 ;IF NOT DONE DCR A MOV L,E MOV H,D POP DE RET ;CLZ CHECK FOR LEADING ZEROS AND IF FOUND ; CHANGE TO LEADING SPACES. ; ENTRY (A) = NUMBER CLZ: MOV B,A LDA CLZA CMP B JNZ CLZ4 ;IF 1ST LEADING DIGIT FOUND LDK A,' ' STAX D CLPLUG: INC DE ;PLUG WITH RET IF NO LEADING SPACES RET CLZ4: MOV A,B STAX D INC DE LK A,0FFH STA CLZA ;SET TO IGNORE LEADING ZERO RET CLZA: DB '0' PAGE IMSG: DB CLRSRN ;CLEAR SCREEN DB CR,LF,LF DB 'OCC1 DOWN LOAD' DB CR,LF DB ' PROGRAM' DB CR,LF DB ' REV A 270' DB CR,LF DB '$' HIGHADR DW 0 ;ADDRESS FOR HIGH STP DW 0 BKS6: DB BKSCHR,BKSCHR,BKSCHR,BKSCHR,BKSCHR,BKSCHR DB '$' PROl entry is at ccp, where the command line given * ; * at ccp+8 is executed automatically (normally a null * ; * command with comlen = 00). An initializing program * ; * can be automatically loaded by storing the command * ; * at ccp+8, with the command length at ccp+7. In this * ; * case, the ccp executes the command before prompting * ; * the console for input. Note that the command is exe-* ; * cuted on both warm and cold starts. When the command* ; * line is initialized, a jump to "jmp ccpclear" dis- * ; * ables the automatic command execution. * ; ******************************************************** ; jmp ccpstart ;start ccp with possible initial command jmp ccpclear ;clear the command buffer maxlen: db 127 ;max buffer length comlen: db 0 ;command length (filled in by dos) ; (command executed initially if comlen non zero) combuf: db ' ' ;8 character fill db ' ' ;8 character fill db 'COPYRIGHT (C) 1979, DIGITAL RESEARCH '; 38 ds 128-($-bdos$inr ; openc: ;open comfcb xra a! sta comrec ;clear next record to read lxi d,comfcb! jmp open ; close: ;close the file given by d,e mvi c,closef! jmp bdos$inr ; search: ;search for the file given by d,e mvi c,searf! jmp bdos$inr ; searchn: ;search for the next occurrence of the file given by d,e mvi c,searnf! jmp bdos$inr ; searchcom: ;search for comfcb file lxi d,comfcb! jmp search ; delete: ;delete the file given by d,e mvi c,delf! jmp bdos ; bdos$cond: call bdos! ora a! ret ; diskread: ;read the next record from the file given by d,e mvi c,dreadf! jmp bdos$cond ; diskreadc: ;read the comfcb file lxi d,comfcb! jmp diskread ; diskwrite: ;write the next record to the file given by d,e mvi c,dwritf! jmp bdos$cond ; make: ;create the file given by d,e mvi c,makef! jmp bdos$inr ; renam: ;rename the file given by d,e mvi c,renf! jmp bdos ; getuser: ;return current user code in a mvi e,0ffh ;drop through to setuser ; setuser: mvi c,uCEND DS 0 ;END OF PROGRAM ; END GNORE LEADING ZEROS! POPALL RET ;CBO CONVERT ONE DIGIT TO DECIMAL HEX ; ENTRY (A) = NUMBER CBO: LK A,'0' PUSH DE CBO1: MOV E,L MOV D,H INR A DAD B JC CBO1 ;IF NOT DONE DCR A MOV L,E MOV H,D POP DE RET ;CLZ CHECK FOR LEADING ZEROS AND IF FOUND ; CHANGE TO LEADING SPACES. ; ENTRY (A) = NUMBER CLZ: MOV B,A LDA CLZA CMP B JNZ CLZ4 ;IF 1ST LEADING DIGIT FOUND LDK A,' ' STAX D CLPLUG: INC DE ;PLUG WITH RET IF NO LEADING SPACES RET CLZ4: MOV A,B STAX D INC DE LK A,0FFH STA CLZA ;SET TO IGNORE LEADING ZERO RET CLZA: DB '0' PAGE IMSG: DB CLRSRN ;CLEAR SCREEN DB CR,LF,LF DB 'OCC1 DOWN LOAD' DB CR,LF DB ' PROGRAM' DB CR,LF DB ' REV A 270' DB CR,LF DB '$' HIGHADR DW 0 ;ADDRESS FOR HIGH STP DW 0 BKS6: DB BKSCHR,BKSCHR,BKSCHR,BKSCHR,BKSCHR,BKSCHR DB '$' PROcombuf) ; total buffer length is 128 characters comaddr:dw combuf ;address of next to char to scan staddr: ds 2 ;starting address of current fillfcb request ; diska equ 0004h ;disk address for current disk bdos equ 0005h ;primary bdos entry point buff equ 0080h ;default buffer fcb equ 005ch ;default file control block ; rcharf equ 1 ;read character function pcharf equ 2 ;print character function pbuff equ 9 ;print buffer function rbuff equ 10 ;read buffer function breakf equ 11 ;break key function liftf equ 12 ;lift head function (no operation) initf equ 13 ;initialize bdos function self equ 14 ;select disk function openf equ 15 ;open file function closef equ 16 ;close file function searf equ 17 ;search for file function searnf equ 18 ;search for next file function delf equ 19 ;delete file function dreadf equ 20 ;disk read function dwritf equ 21 ;disk write function makef equ 22 ;file make function renf equ 23 ;rename file function logf equ 24 ;return login vector cself equ 25 ;retserf! jmp bdos ;sets user number ; saveuser: ;save user#/disk# before possible ^c or transient call getuser ;code to a add a! add a! add a! add a ;rot left lxi h,cdisk! ora m ;4b=user, 4b=disk sta diska ;stored away in memory for later ret ; setdiska: lda cdisk! sta diska ;user/disk ret ; translate: ;translate character in register A to upper case cpi 61h! rc ;return if below lower case a cpi 7bh! rnc ;return if above lower case z ani 5fh! ret ;translated to upper case ; readcom: ;read the next command into the command buffer ;check for submit file lda submit! ora a! jz nosub ;scanning a submit file ;change drives to open and read the file lda cdisk! ora a! mvi a,0! cnz select ;have to open again in case xsub present lxi d,subfcb! call open! jz nosub ;skip if no sub lda subrc! dcr a ;read last record(s) first sta subcr ;current record to read lxi d,subfcb! call diskread ;end of file if last record jnz nosub ;disk read is ok, tr title 'console command processor (CCP), ver 2.0' ; assembly language version of the CP/M console command processor ; ; version 2.2 February, 1980 ;TEST ONLY ;REV = D ;DATE = 237 ;DEB ;REORGED IT TO D000 ; ; Copyright (c) 1976, 1977, 1978, 1979, 1980 ; Digital Research ; Box 579, Pacific Grove, ; California, 93950 ; false equ 0000h true equ not false testing equ false ;true if debugging ; ; ; if testing ; org 3400h ;bdosl equ $+800h ;bdos location ; else ; org 000h ;bdosl equ $+800h ;bdos location ; endif ORG 0D000H bdosl equ $+800h tran equ 100h tranm equ $ ccploc equ $ ; ; ******************************************************** ; * base of CCP contains the following code/data * ; * ccp: jmp ccpstart (start with command) * ; * jmp ccpclear (start, clear command) * ; * ccp+6 127 (max command length) * ; * ccp+7 comlen (command length = 00) * ; * ccp+8 ' ... ' (16 blanks) * ; ******************************************************** ; * Normaurn currently selected drive number dmaf equ 26 ;set dma address userf equ 32 ;set user number ; ; special fcb flags rofile equ 9 ;read only file sysfile equ 10 ;system file flag ; ; special characters cr equ 13 ;carriage return lf equ 10 ;line feed la equ 5fh ;left arrow eofile equ 1ah ;end of file ; ; utility procedures printchar: mov e,a! mvi c,pcharf! jmp bdos ; printbc: ;print character, but save b,c registers push b! call printchar! pop b! ret ; crlf: mvi a,cr! call printbc mvi a,lf! jmp printbc ; blank: mvi a,' '! jmp printbc ; print: ;print string starting at b,c until next 00 entry push b! call crlf! pop h ;now print the string prin0: mov a,m! ora a! rz ;stop on 00 inx h! push h ;ready for next call printchar! pop h ;character printed jmp prin0 ;for another character ; initialize: mvi c,initf! jmp bdos ; select: mov e,a! mvi c,self! jmp bdos ; bdos$inr: call bdos! sta dcnt! inr a! ret ; open: ;open the file given by d,e mvi c,openf! jmp  ansfer to combuf lxi d,comlen! lxi h,buff! mvi b,128! call move0 ;line is transferred, close the file with a ;deleted record lxi h,submod! mvi m,0 ;clear fwflag inx h! dcr m ;one less record lxi d,subfcb! call close! jz nosub ;close went ok, return to original drive lda cdisk! ora a! cnz select ;print to the 00 lxi h,combuf! call prin0 call break$key! jz noread call del$sub! jmp ccp ;break key depressed ; nosub: ;no submit file! call del$sub ;translate to upper case, store zero at end call saveuser ;user # save in case control c mvi c,rbuff! lxi d,maxlen! call bdos call setdiska ;no control c, so restore diska noread: ;enter here from submit file ;set the last character to zero for later scans lxi h,comlen! mov b,m ;length is in b readcom0: inx h! mov a,b! ora a ;end of scan? jz readcom1! mov a,m ;get character and translate call translate! mov m,a! dcr b! jmp readcom0 ; readcom1: ;end of scan, h,l address end of command mov ml,a! rnc inr h! ret ; fillfcb0: ;equivalent to fillfcb(0) mvi a,0 ; fillfcb: lxi h,comfcb! call addh! push h! push h ;fcb rescanned at end xra a! sta sdisk ;clear selected disk (in case A:...) lhld comaddr! xchg ;command address in d,e call deblank ;to first non-blank character xchg! shld staddr ;in case of errors xchg! pop h ;d,e has command, h,l has fcb address ;look for preceding file name A: B: ... ldax d! ora a! jz setcur0 ;use current disk if empty command sbi 'A'-1! mov b,a ;disk name held in b if : follows inx d! ldax d! cpi ':'! jz setdsk ;set disk name if : ; setcur: ;set current disk dcx d ;back to first character of command setcur0: lda cdisk! mov m,a! jmp setname ; setdsk: ;set disk to name in register b mov a,b! sta sdisk ;mark as disk selected mov m,b! inx d ;past the : ; setname: ;set the file name field mvi b,8 ;file name length (max) setnam0: call delim! jz padname ;not a delimiter inx h! cpi '*'! jnz setnam1 ;must be ?'s oop while matching ; ;complete match on name, check for blank in fcb ldax d! cpi ' '! jnz intrin3 ;otherwise matched mov a,c! ret ;with intrinsic number in a ; intrin2: ;mismatch, move to end of intrinsic inx h! dcr b! jnz intrin2 ; intrin3: ;try next intrinsic inr c ;to next intrinsic number jmp intrin0 ;for another round ; ccpclear: ;clear the command buffer xra a sta comlen ;drop through to start ccp ccpstart: ;enter here from boot loader lxi sp,stack! push b ;save initial disk number ;(high order 4bits=user code, low 4bits=disk#) mov a,c! rar! rar! rar! rar! ani 0fh ;user code mov e,a! call setuser ;user code selected ;initialize for this user, get $ flag call initialize ;0ffh in accum if $ file present sta submit ;submit flag set if $ file present pop b ;recall user code and disk number mov a,c! ani 0fh ;disk number in accumulator sta cdisk ;clears user code nibble call select ;proper disk is selecte,a ;store a zero lxi h,combuf! shld comaddr ;ready to scan to zero ret ; break$key: ;check for a character ready at the console mvi c,breakf! call bdos ora a! rz mvi c,rcharf! call bdos ;character cleared ora a! ret ; cselect: ;get the currently selected drive number to reg-A mvi c,cself! jmp bdos ; setdmabuff: ;set default buffer dma address lxi d,buff ;(drop through) ; setdma: ;set dma address to d,e mvi c,dmaf! jmp bdos ; del$sub: ;delete the submit file, and set submit flag to false lxi h,submit! mov a,m! ora a! rz ;return if no sub file mvi m,0 ;submit flag is set to false xra a! call select ;on drive a to erase file lxi d,subfcb! call delete lda cdisk! jmp select ;back to original drive ; serialize: ;check serialization lxi d,serial! lxi h,bdosl! mvi b,6 ;check six bytes ser0: ldax d! cmp m! jnz badserial inx d! inx h! dcr b! jnz ser0 ret ;serial number is ok ; comerr: ;error in command string starting at position ;'staddr' and ending  mvi m,'?'! jmp setnam2 ;to dec count ; setnam1: mov m,a ;store character to fcb! inx d setnam2: dcr b ;count down length! jnz setnam0 ; ;end of name, truncate remainder trname: call delim! jz setty ;set type field if delimiter inx d! jmp trname ; padname: inx h! mvi m,' '! dcr b! jnz padname ; setty: ;set the type field mvi b,3! cpi '.'! jnz padty ;skip the type field if no . inx d ;past the ., to the file type field setty0: ;set the field from the command buffer call delim! jz padty! inx h! cpi '*'! jnz setty1 mvi m,'?' ;since * specified! jmp setty2 ; setty1: ;not a *, so copy to type field mov m,a! inx d setty2: ;decrement count and go again dcr b! jnz setty0 ; ;end of type field, truncate trtyp: ;truncate type field call delim! jz efill! inx d! jmp trtyp ; padty: ;pad the type field with blanks inx h! mvi m,' '! dcr b! jnz padty ; efill: ;end of the filename/filetype fill, save command address ;fill the remaind, now check sub files ;check for initial command lda comlen! ora a! jnz ccp0 ;assume typed already ; ccp: ;enter here on each command or error condition lxi sp,stack call crlf ;print d> prompt, where d is disk name call cselect ;get current disk number adi 'A'! call printchar mvi a,'>'! call printchar call readcom ;command buffer filled ccp0: ;(enter here from initialization with command full) lxi d,buff! call setdma ;default dma address at buff call cselect! sta cdisk ;current disk number saved call fillfcb0 ;command fcb filled cnz comerr ;the name cannot be an ambiguous reference lda sdisk! ora a! jnz userfunc ;check for an intrinsic function call intrinsic lxi h,jmptab ;index is in the accumulator mov e,a! mvi d,0! dad d! dad d ;index in d,e mov a,m! inx h! mov h,m! mov l,a! pchl ;pc changes to the proper intrinsic or user function jmptab: dw direct ;directory search dw erase ;file erase dw type ;type file dw save ;save memory image dwith first delimiter call crlf ;space to next line lhld staddr ;h,l address first to print comerr0: ;print characters until blank or zero mov a,m! cpi ' '! jz comerr1; not blank ora a! jz comerr1; not zero, so print it push h! call printchar! pop h! inx h jmp comerr0; for another character comerr1: ;print question mark,and delete sub file mvi a,'?'! call printchar call crlf! call del$sub jmp ccp ;restart with next command ; ; fcb scan and fill subroutine (entry is at fillfcb below) ;fill the comfcb, indexed by A (0 or 16) ;subroutines delim: ;look for a delimiter ldax d! ora a! rz ;not the last element cpi ' '! jc comerr ;non graphic rz ;treat blank as delimiter cpi '='! rz cpi la! rz ;left arrow cpi '.'! rz cpi ':'! rz cpi ';'! rz cpi '<'! rz cpi '>'! rz ret ;delimiter not found ; deblank: ;deblank the input line ldax d! ora a! rz ;treat end of line as blank cpi ' '! rnz! inx d! jmp deblank ; addh: ;add a to h,l add l! mov ing fields for the fcb mvi b,3 efill0: inx h! mvi m,0! dcr b! jnz efill0 xchg! shld comaddr ;set new starting point ; ;recover the start address of the fcb and count ?'s pop h! lxi b,11 ;b=0, c=8+3 scnq: inx h! mov a,m! cpi '?'! jnz scnq0 ;? found, count it in b! inr b scnq0: dcr c! jnz scnq ; ;number of ?'s in c, move to a and return with flags set mov a,b! ora a! ret ; intvec: ;intrinsic function names (all are four characters) db 'DIR ' db 'ERA ' db 'TYPE' db 'SAVE' db 'REN ' db 'USER' intlen equ ($-intvec)/4 ;intrinsic function length serial: db 0,0,0,0,0,0 ; ; intrinsic: ;look for intrinsic functions (comfcb has been filled) lxi h,intvec! mvi c,0 ;c counts intrinsics as scanned intrin0: mov a,c! cpi intlen ;done with scan?! rnc ;no, more to scan lxi d,comfcb+1 ;beginning of name mvi b,4 ;length of match is in b intrin1: ldax d! cmp m ;match? jnz intrin2 ;skip if no match inx d! inx h! dcr b jnz intrin1 ;l w rename ;file rename dw user ;user number dw userfunc;user-defined function badserial: lxi h,di or (hlt shl 8) shld ccploc! lxi h,ccploc! pchl ; ; ;utility subroutines for intrinsic handlers readerr: ;print the read error message lxi b,rdmsg! jmp print rdmsg: db 'READ ERROR',0 ; nofile: ;print no file message lxi b,nofmsg! jmp print nofmsg: db 'NO FILE',0 ; getnumber: ;read a number from the command line call fillfcb0 ;should be number lda sdisk! ora a! jnz comerr ;cannot be prefixed ;convert the byte value in comfcb to binary lxi h,comfcb+1! lxi b,11 ;(b=0, c=11) ;value accumulated in b, c counts name length to zero conv0: mov a,m! cpi ' '! jz conv1 ;more to scan, convert char to binary and add inx h! sui '0'! cpi 10! jnc comerr ;valid? mov d,a ;save value! mov a,b ;mult by 10 ani 1110$0000b! jnz comerr mov a,b ;recover value rlc! rlc! rlc ;*8 add b! jc comerr add b! jc comerr ;*8+*2 = *10 add d! jc ct line hdr dirhdr0:call blank ;after last one mvi a,':'! call printbc dirhdr1: call blank ;compute position of name in buffer mvi b,1 ;start with first character of name dir3: mov a,b! call addhcf ;buff+a+c fetched ani 7fh ;mask flags ;may delete trailing blanks cpi ' '! jnz dir4 ;check for blank type pop psw! push psw ;may be 3rd item cpi 3! jnz dirb ;place blank at end if not mvi a,9! call addhcf ;first char of type ani 7fh! cpi ' '! jz dir5 ;not a blank in the file type field dirb: mvi a,' ' ;restore trailing filename chr dir4: call printbc ;char printed inr b! mov a,b! cpi 12! jnc dir5 ;check for break between names cpi 9! jnz dir3 ;for another char ;print a blank between names call blank! jmp dir3 ; dir5: ;end of current entry pop psw ;discard the directory counter (mod 4) dir6: call break$key ;check for interrupt at keyboard jnz endir ;abort directory search call searchn! jmp dir2 ;for another entry en l! jz save1 ;may be completed dcx h ;sector count = sector count - 1 push h ;save it for next time around lxi h,128! dad d! push h ;next dma address saved call setdma ;current dma address set lxi d,comfcb! call diskwrite pop d! pop h ;dma address, sector count jnz saverr ;may be disk full case jmp save0 ;for another sector ; save1: ;end of dump, close the file lxi d,comfcb! call close inr a; 255 becomes 00 if error jnz retsave ;for another command saverr: ;must be full or read only disk lxi b,fullmsg! call print retsave: ;reset dma buffer call setdmabuff jmp retcom fullmsg: db 'NO SPACE',0 ; ; rename: ;rename a file on a specific disk call fillfcb0! jnz comerr ;must be unambiguous lda sdisk! push psw ;save for later compare call setdisk ;disk selected call searchcom ;is new name already there? jnz renerr3 ;file doesn't exist, move to second half of fcb lxi h,comfcb! lxi d,comfcb+16! mvi b,16! call move0 ;check for = or left aromerr ;+digit mov b,a! dcr c! jnz conv0 ;for another digit ret conv1: ;end of digits, check for all blanks mov a,m! cpi ' '! jnz comerr ;blanks? inx h! dcr c! jnz conv1 mov a,b ;recover value! ret ; movename: ;move 3 characters from h,l to d,e addresses mvi b,3 move0: mov a,m! stax d! inx h! inx d dcr b! jnz move0 ret ; addhcf: ;buff + a + c to h,l followed by fetch lxi h,buff! add c! call addh! mov a,m! ret ; setdisk: ;change disks for this command, if requested xra a! sta comfcb ;clear disk name from fcb lda sdisk! ora a! rz ;no action if not specified dcr a! lxi h,cdisk! cmp m! rz ;already selected jmp select ; resetdisk: ;return to original disk after command lda sdisk! ora a! rz ;no action if not selected dcr a! lxi h,cdisk! cmp m! rz ;same disk lda cdisk! jmp select ; ;individual intrinsics follow direct: ;directory search call fillfcb0 ;comfcb gets file name call setdisk ;change disk drives if requested lxdir: ;end of directory scan pop d ;discard directory counter jmp retcom ; ; erase: call fillfcb0 ;cannot be all ???'s cpi 11 jnz erasefile ;erasing all of the disk lxi b,ermsg! call print! call readcom lxi h,comlen! dcr m! jnz ccp ;bad input inx h! mov a,m! cpi 'Y'! jnz ccp ;ok, erase the entire diskette inx h! shld comaddr ;otherwise error at retcom erasefile: call setdisk lxi d,comfcb! call delete inr a ;255 returned if not found cz nofile ;no file message if so jmp retcom ; ermsg: db 'ALL (Y/N)?',0 ; type: call fillfcb0! jnz comerr ;don't allow ?'s in file name call setdisk! call openc ;open the file jz typerr ;zero flag indicates not found ;file opened, read 'til eof call crlf! lxi h,bptr! mvi m,255 ;read first buffer type0: ;loop on bptr lxi h,bptr! mov a,m! cpi 128 ;end buffer jc type1! push h ;carry if 0,1,...,127 ;read another buffer full call diskreadc! pop h ;recover address of bptr jnz typeof ;hard end of frow lhld comaddr! xchg! call deblank cpi '='! jz ren1 ;ok if = cpi la! jnz renerr2 ren1: xchg! inx h! shld comaddr ;past delimiter ;proper delimiter found call fillfcb0! jnz renerr2 ;check for drive conflict pop psw! mov b,a ;previous drive number lxi h,sdisk! mov a,m! ora a! jz ren2 ;drive name was specified. same one? cmp b! mov m,b! jnz renerr2 ren2: mov m,b ;store the name in case drives switched xra a! sta comfcb! call searchcom ;is old file there? jz renerr1 ; ;everything is ok, rename the file lxi d,comfcb! call renam jmp retcom ; renerr1:; no file on disk call nofile! jmp retcom renerr2:; ambigous reference/name conflict call resetdisk! jmp comerr renerr3:; file already exists lxi b,renmsg! call print! jmp retcom renmsg: db 'FILE EXISTS',0 ; user: ;set user number call getnumber; leaves the value in the accumulator cpi 16! jnc comerr; must be between 0 and 15 mov e,a ;save for setuser call lda comfcb+1! cpi ' '! i h,comfcb+1! mov a,m ;may be empty request cpi ' '! jnz dir1 ;skip fill of ??? if not blank ;set comfcb to all ??? for current disk mvi b,11 ;length of fill ????????.??? dir0: mvi m,'?'! inx h! dcr b! jnz dir0 ;not a blank request, must be in comfcb dir1: mvi e,0! push d ;E counts directory entries call searchcom ;first one has been found cz nofile ;not found message dir2: jz endir ;found, but may be system file lda dcnt ;get the location of the element rrc! rrc! rrc! ani 110$0000b! mov c,a ;c contains base index into buff for dir entry mvi a,sysfile! call addhcf ;value to A ral! jc dir6 ;skip if system file ;c holds index into buffer ;another fcb found, new line? pop d! mov a,e! inr e! push d ;e=0,1,2,3,...new line if mod 4 = 0 ani 11b! push psw ;and save the test jnz dirhdr0 ;header on current line call crlf push b! call cselect! pop b ;current disk in A adi 'A'! call printbc mvi a,':'! call printbc jmp dirhdr1 ;skip currenile xra a! mov m,a ;bptr = 0 type1: ;read character at bptr and print inr m ;bptr = bptr + 1 lxi h,buff! call addh ;h,l addresses char mov a,m! cpi eofile! jz retcom call printchar call break$key! jnz retcom ;abort if break jmp type0 ;for another character ; typeof: ;end of file, check for errors dcr a! jz retcom call readerr typerr: call resetdisk! jmp comerr ; save: call getnumber; value to register a push psw ;save it for later ; ;should be followed by a file to save the memory image call fillfcb0 jnz comerr ;cannot be ambiguous call setdisk ;may be a disk change lxi d,comfcb! push d! call delete ;existing file removed pop d! call make ;create a new file on disk jz saverr ;no directory space xra a! sta comrec; clear next record field pop psw ;#pages to write is in a, change to #sectors mov l,a! mvi h,0! dad h! lxi d,tran ;h,l is sector count, d,e is load address save0: ;check for sector count zero mov a,h! ora jz comerr call setuser ;new user number set jmp endcom ; userfunc: call serialize ;check serialization ;load user function and set up for execution lda comfcb+1! cpi ' '! jnz user0 ;no file name, but may be disk switch lda sdisk! ora a! jz endcom ;no disk name if 0 dcr a! sta cdisk! call setdiska ;set user/disk call select! jmp endcom user0: ;file name is present lxi d,comfcb+9! ldax d! cpi ' '! jnz comerr ;type ' ' push d! call setdisk! pop d! lxi h,comtype ;.com call movename ;file type is set to .com call openc! jz userer ;file opened properly, read it into memory lxi h,tran ;transient program base load0: push h ;save dma address xchg! call setdma lxi d,comfcb! call diskread! jnz load1 ;sector loaded, set new dma address and compare pop h! lxi d,128! dad d lxi d,tranm ;has the load overflowed? mov a,l! sub e! mov a,h! sbb d! jnc loaderr jmp load0 ;for another sector ; load1: pop h! dcr a! jnz loaderr ;end file is 1 call rrrent operation ;none=0, a=1, b=2 ... bptr: ds 1 ;buffer pointer end ccploc AD',0 comtype: db 'COM' ;for com files ; ; retcom: ;reset disk before end of command check call resetdisk ; endcom: ;end of intrinsic command call fillfcb0 ;to check for garbage at end of line lda comfcb+1! sui ' '! lxi h,sdisk! ora m ;0 in accumulator if no disk selected, and blank fcb jnz comerr jmp ccp ; ; ; ; data areas ds 16 ;8 level stack stack: ; ; 'submit' file control block submit: db 0 ;00 if no submit file, ff if submitting subfcb: db 0,'$$$ ' ;file name is $$$ db 'SUB',0,0 ;file type is sub submod: db 0 ;module number subrc: ds 1 ;record count filed ds 16 ;disk map subcr: ds 1 ;current record to read ; ; command file control block comfcb: ds 32 ;fields filled in later comrec: ds 1 ;current record to read/write dcnt: ds 1 ;disk directory count (used for error codes) cdisk: ds 1 ;current disk sdisk: ds 1 ;selected disk for cuNumber : XXXXX.' DB CR,LF DB CR,LF,'If their bytes don"t match you will see :' DB CR,LF DB CR,LF,'*ERROR* Decimal Value = XXXXX' DB CR,LF DB CR,LF,'or the program will stop in an endless loop in the' DB CR,LF,'serial port driver.' DB CR,LF,'If this program is stuck there will be an "S" on' DB CR,LF,'the right hand side of the screen.' DB CR,LF,'This program will stop with ^C if not stuck. DB CR,LF,LF,' ' DB '$' MGOOD: DB CR,'GOOD Cycle Number :' DB '$' MERROR: DB CR,'*ERROR* Decimal Value =' DB '$' MSTUCK: DB BKSCHR,'S$' CRLF: DB CR,LF,'$' STP: DW 0 CYNUM: DW 0 ;CYCLE NUMBER BKS6: DB BKSCHR,BKSCHR,BKSCHR,BKSCHR,BKSCHR,BKSCHR DB '$' TITLE 'USEFUL PROC' ; +---------------+ ; | | ; | USEFUL PROC"S | ; | | ; +---------------+ INCHL: ;INCREMENT THE TWO BYTE VALUE POINTED TO BY HL ;ENTRY ;HL = ADDRESS ;EXIT ;NONE PROC PUSHAL LD Eesetdisk ;back to original disk call fillfcb0! lxi h,sdisk! push h mov a,m! sta comfcb ;drive number set mvi a,16! call fillfcb ;move entire fcb to memory pop h! mov a,m! sta comfcb+16 xra a! sta comrec ;record number set to zero lxi d,fcb! lxi h,comfcb! mvi b,33! call move0 ;move command line to buff lxi h,combuf bmove0: mov a,m! ora a! jz bmove1! cpi ' '! jz bmove1 inx h! jmp bmove0 ;for another scan ;first blank position found bmove1: mvi b,0! lxi d,buff+1! ;ready for the move bmove2: mov a,m! stax d! ora a! jz bmove3 ;more to move inr b! inx h! inx d! jmp bmove2 bmove3: ;b has character count mov a,b! sta buff call crlf ;now go to the loaded program call setdmabuff ;default dma call saveuser ;user code saved ;low memory diska contains user code call tran ;gone to the loaded program lxi sp,stack ;may come back here call setdiska! call select jmp ccp ; userer: ;arrive here on command error call rese;OCCLOOPA.ASM TITLE 'OCC1 LOOP BACK PROGRAM.' ;CPM COMPATABLE * 4D2014-00 MASTER .ASM * 2D2014-00 ASSY .ASM * 1D2014-00 LISTING .PRN * 4D1014-00 MASTER .COM * 2D1014-00 ASSY .COM ; +-----------------------+ ; | | ; | LOOP BACK TEST | ; | | ; +-----------------------+ ;REV = A ;DATE = 278 ;DEB ;RELEASED LINK OCCRAM2.ASM ORG 100H LD HL,SYS+1 ;START OF BDOS OR DDT STO HL,STP ;SET STP FOR  MOV SP,HL ;SET STACK TO FWA OF BDOS PAGE PROC ; CALL PARG ;PROCESS COMMAND LINE ARGUMENTS LDK HL,0 STO HL,CYNUM ;CYCLE NUMB = 0 LDK HL,IMSG CALL STRCON ;INITIAL MESSAGE :LOOP: CALL CABORT ;CHECK FOR CHAR CALL TSPRT ;TEST PORT JRC :LOOP ;IF ERROR LDK HL,CYNUM CALL INCHL ;CYNUM = CYNUM +1 LDK HL,MGOOD CALL STRCON ;PRINT GOOD LD HL,CYNUM CALL NUMCON ;PRINT CYCLE NUMBER JR :LOOP PAGE TSPRT: ;[2] SERIAL PORT TEST ;ENTRY ;NONE ;EXIT ;CBIT = SET IF ERROR ;ERROR ALSO REPORTED TO CRT PROC LDK B,0 :1: PUSH BC ;SAVE MOV E,B,[HL] INC HL LD D,[HL] INC DE STO D,[HL] DEC HL STO E,[HL] POPALL RET PAGE PARG: ;PROCESS COMMAND LINE ARGUMENTS PROC :3: LD A,TBUFF OR A LDK C,-1 ;INDICATE UN-INITIALIZED JZ :4 ;IF NO ARGUMENTS LD A,TBUFF+2 CMP 'A' LDK C,0 ;DEFAULT DRIVE=0 JM :4 ;IF NOT LEGAL DRIVE CMP 'P'+1 JP :4 ;IF NOT LEGAL DRIVE SUB 'A' MOV C,A ;SET DRIVE :4: MOV A,C STO A,SDISK ;SET DRIVE RET CONIN: LDK C,1 CALL SYS ;BDOS CMP 'a' RC ;IF NOT LOWER CaSE CMP 'z'+1 RNC SUI 'a'-'A' ;MAKE UPPER CASE RET CONOUT: ;OUTPUT CHARACTER TO CONSOLE ; ENTRY A=CHARACTER PROC PUSHAL MOV E,A MVI C,2 CALL SYS POPALL RET PAGE STRCON: ;OUTPUT MESSAGE TO CONSOLE ;ENTRY ;HL = FWA OF MESSAGE ;EXIT ;NONE PROC PUSHAL EX DE,HL ;VALUE TO DE LDK C,9 ;STRING CALL SYS ;BDOS POPALL RET NUMCON: ;OUTPUT DECIMAL NUMBER ; ENTRY HL= NUMBER ; EXIT NUMBER SENT TO CONSOLE PUSH DE PUSH HL LDK DE,TEMN CALL CBD5 LDK HL,TEMN CALL STRCOtdisk! jmp comerr ; loaderr:;cannot load the program lxi b,loadmsg! call print jmp retcom loadmsg: db 'BAD LOAD',0 comtype: db 'COM' ;for com files ; ; retcom: ;reset disk before end of command check call resetdisk ; endcom: ;end of intrinsic command call fillfcb0 ;to check for garbage at end of line lda comfcb+1! sui ' '! lxi h,sdisk! ora m ;0 in accumulator if no disk selected, and blank fcb jnz comerr jmp ccp ; ; ; ; data areas ds 16 ;8 level stack stack: ; ; 'submit' file control block submit: db 0 ;00 if no submit file, ff if submitting subfcb: db 0,'$$$ ' ;file name is $$$ db 'SUB',0,0 ;file type is sub submod: db 0 ;module number subrc: ds 1 ;record count filed ds 16 ;disk map subcr: ds 1 ;current record to read ; ; command file control block comfcb: ds 32 ;fields filled in later comrec: ds 1 ;current record to read/write dcnt: ds 1 ;disk directory count (used for error codes) cdisk: ds 1 ;current disk sdisk: ds 1 ;selected disk for cu ;MOVE CHARACTER TO E LDK C,4H LDK HL,MSTUCK CALL STRCON ;SET "S" FOR STUCK CALL IO LDK C,3H ;READER INPUT CALL IO POP BC ;RESTORE CMP B ;ARE THEY THE SAME JRNZ :2 ;IF ERROR LDK A,BKSCHR ;BACKSPACE CALL CONOUT LDK A,' ' CALL CONOUT ;SPACE DJNZ :1 ;LOOP RET :2: LDK HL,MERROR CALL STRCON ;ERROR MESSAGE LDK H,0 MOV L,B CALL NUMCON ;OUTPUT VALUE IN DEC STC RET IO: PUSHAL CALL SYS POPALL RET TITLE 'CONSTANTS' IMSG: DB CLRSRN ;CLEAR SCREEN DB CR,' +-----------------------+' DB CR,LF,' | SERIAL PORT TEST |' DB CR,LF,' | REV A 278 |' DB CR,LF,' +-----------------------+' DB CR,LF DB CR,LF,'This is a serial loop back test. It writes 0-256' DB CR,LF,'to the LST: device and reads the RDR: device. It' DB CR,LF,'then compares the two buffers. If their bytes' DB CR,LF,'match one for one you will see :' DB CR,LF DB CR,LF,'GOOD Cycle N POP HL POP DE RET TEMN: DB ' $' PAGE CBD5: ;CONVERT BINARY NUMBER TO ASCII DECIMAL ;WITH LEADING ZERO SUPQRESSION. ;ENTRY ;HL = VALUE TO CONVERT ;DE = FWA TO STORE CONVERSION ;EXIT ;NONE PROC PUSHAL LK BC,-10000 CALL CBO ;CONVERT TEN-THOUSANDS DIGIT CALL CLZ CBD4: CBD: LK BC,-1000 CALL CBO ;CONVERT THOUSAODS!DIGIT CALL CLZ ;CHECK LEADING ZEROS CBD3: LK BC,-100 CALL CBO CALL CLZ CBD2: LK BC,-10 CALL CBO CALL CLZ MOV A,L ADI '0' STAX D LK A,'0' STA CLZA ;RESET TO IGNORE LEADING ZEROS! POPALL RET SPACE 4,10 ;CBO CONVERT ONE DIGIT TO DECIMAL HEX ; ENTRY (A) = NUMBER CBO: LK A,'0' PUSH DE CBO1: MOV E,L MOV D,H INR A DAD B JC CBO1 ;IF NOT DONE DCR A MOV L,E MOV H,D POP DE RET ;CLZ CHECK FOR LEADING ZEROS AND IF FOUND ; CHANGE TO LEADING SPACES. ; ENTRY (A) = NUMBER CLZ: MOV B,A LDA CLZA CMP B JNZ CLZ4 ;IF 1ST LEADING DIGIT FOUND LDK A,' ' STAX ing CALL RDRV ;reset the drive LDK DE,PNEXT CALL ESTR ;Prompt for more CALL GCHR ;get response CMP 'Y' JNZ SYSR ;if wants to quit LDK DE,PRMT1 CALL ESTR ;request drive once again JR :1 ;enter requesting drive DTRKM: DBE cr DBE lf,lf,' ' DBE ESC,'l' DBE 'T r a c k s' DBE ESC,'m' DBE cr,lf,'0 1 2 3' DBE cr,lf,'0123456789012345678901234567890123456789',cr DCE lf RDYMS: DBE cr,lf,lf,' Press ' DBE ESC,'l','RETURN',ESC,'m' DBE ' when your disk is ready' DCE '.' PNEXT: DBE cr,lf DCE 'Do you want to format another disk (y/n) ? ' PRMT: DBE cr DBE lf,lf,lf DBE ESC,'g' DBE ' ',CTRM,CTRM,CTRM,CTRM,CTRM,' FORMAT Disk Program ' DBE CTRM,CTRM,CTRM,CTRM,CTRM DBE ESC,'G' DBE cr,lf,lf DBE 'Please Select the drive to format (A or B)' DBE cr,lf, ' (Drive "A" is left drive, "B" is right)' PRMT1: DBE cr,lf, ' Your ' DBE ESC,')','Selection',ESC,'(' DBE ' is : ' DCE ' ' DRV: DB 0 page FMTDSK: for time-out err & hang if so: CMP SEKTMO ;ck for Seek Time Out error JRZ :hang ;if disk not in drive (?) :err: LK DE,ERSTR ;if NOT ok :2: CALL ESTR ;notify user LK HL,TRAK INC [hl] ;step IN one track LD A,[hl] ;A=next track CMP MTRK ;verify up thru MTRK-1 JRNZ :loop :exit: LK DE,CR2LF ;cr,lf,lf JMP ESTR page ;Misc useful procs Proc SETDMA: LK DE,BSTDMA+100h ;addr of DMA-setter within ROM JR GOROM SETSEC: LK DE,BSTSEC+100h ;addr of sector-setter within ROM JR GOROM SETTRK: LK DE,BSTTRK+100h ;addr of track-setter JR GOROM XFER: LK DE,BREAD+100h ;multi-sector read ;JR GOROM GOROM: LD HL,BIOSLOC LK L,BROMJP ;do ROM jump JMP [hl] space 4,10 COMPAR: ;compare data from disk with expected data ;Entry track image @ BUFFR ;Exit Z=TRUE => good compare LK BC,MSEC*LSECB ;#bytes to compare LK HL,BUFFR ;FWA of data LK A,FMTCHR ;format character :loop: CPI# ;magic Zilog instruction RNZ ;if bad compare JPE :loop ;if not finished :goodD CLPLUG: INC DE ;PLUG WITH RET IF NO LEADING SPACES RET CLZ4: MOV A,B STAX D INC DE LK A,0FFH STA CLZA ;SET TO IGNORE LEADING ZERO RET CLZA: DB '0' PAGE CABORT: ;CHECK CONSOLE STATUS FOR POSSIBLE ABORT ;ENTRY ;NONE ;EXIT ;WORM BOOT ON  PROC PUSHAL LDK C,11 CALL SYS ;GET STATUS ORA A JZ :1 ;IF NO CHARACTOR LDK C,1 CALL SYS ;GET CHARACTOR CMP 3 ; JNZ :1 ;IF NOT CONTINUE LD HL,STP ;STACK ORIGINAL POS MOV SP,HL JMP 0 ;WORM BOOT :1: POPALL RET ;IN NO CHAR OR NOT  BUFF1 = * BUFF2 = BUFF1+0100H ;256 BYTES ; ENDX  ; ENTRY (A) = NUMBER CBO: LK A,'0' PUSH DE CBO1: MOV E,L MOV D,H INR A DAD B JC CBO1 ;IF NOT DONE DCR A MOV L,E MOV H,D POP DE RET ;CLZ CHECK FOR LEADING ZEROS AND IF FOUND ; CHANGE TO LEADING SPACES. ; ENTRY (A) = NUMBER CLZ: MOV B,A LDA CLZA CMP B JNZ CLZ4 ;IF 1ST LEADING DIGIT FOUND LDK A,' ' STAX  ;Format entire disk ; Entry SDISK set to drive ; Exit all tracks formatted. proc LDK DE,RDYMS CALL ESTR ;output new line CALL GCHR CMP CR JNZ FMT ;if not ready, start again LDK HL,0 STO HL,SAVTRK ;set track to home ; Move format data to high RAM buffer LDK DE,RBUF+2 ;Fwa of data LDK HL,FORDAT LDK BC,LIFMT LDIR ;copy 1st sector LDK A,1 STO A,SECN ;set sector ECHO MSEC-1 ;copy other 9 sectors PUSH DE POP IY LDK HL,FMTSD LDK BC,LISEC LDIR LD A,SECN INC A STO A,SECN STO A,[IY+(FSECN-FMTSD)] ENDM EX DE,HL LDK BC,RBUF+2 SBC HL,BC ;get length STO HL,RBUF ;set length into buffer CALL RDRV ;reset drive ; Display track indicator line LDK DE,DTRKM CALL ESTR ;Display track numbers ; Format entire disk :2: CALL STRK ;Set new track into data LDK BC,RBUF ;point to FWA of buffer CALL BIOS+BFMTJP ;Issue format command LDK C,'F' CMP -1 JNZ :4 ;if drive ready LDK DE,DNRMS CALL ESTR ;indicate drive NOT ready JMP FMT: RET ;return with Z=True space 4,10 FILLC: ;fill with character: ; C= chr to broadcast ; DE= #characters to broadcast ; HL= FWA to start broadcast MOV A,E OR D RZ ;if broadcast 0 bytes STO C,[hl] ;1st byte DEC DE MOV A,E OR D RZ ;if broadcast 1 byte MOV B,D ! MOV C,E ;BC := DE (count) MOV D,H ! MOV E,L ;DE := HL (FWA) INC DE ;DE := HL + 1 LDIR ;overlapping move RET page TRAK: DS 1 ;current TRACK SECN: DB 0 DNRMS: DBE cr,lf,'Disk drive ' DRV1: DBE 0 DBE ': is NOT ready' DCE '.' DNRMS1: DBE cr,lf,' Is a disk installed in drive ? ' DBE ESC,'l','(y/n)',ESC,'m' DCE ' ' space 4,10 STRK: ;Set track numbers into data proc LDK HL,RBUF+1+TRKOFS LDK B,MSEC LDK DE,LISEC LD A,SAVTRK :2: STO A,[hl] ;update track cell ADD HL,DE ;point to next DJNZ :2 ;if not done LDK A,1 STO A,SAVSEC ;set sector to 1st RET space 4,10 RDRV: ;Reset requested disk drive proc LDK A,10 :2: STO A,RTST ;update retries LDK E,0 LD A,DRV MOV C,A ;OCCFMTA.ASM TITLE 'Format program for OCC - One.' * 4D2005-00 MASTER .ASM * 2D2005-00 ASSY .ASM * 1D2005-00 LISTING .PRN * 4D1005-00 MASTER .COM * 2D1005-00 ASSY .COM ; +-----------------------+ ; | | ; | F O R M A T | ; | | ; +-----------------------+ ;REV = A ;DATE = 251 ;DEB ;RELEASED VER = '01' LEVEL = '00' ; Assembly Constants CTRM = 'M'-40h RBUF = 4000h ;Ram buffer containing format ; ;template data for ROM routine ; ;to use for formatting each track BIOSLOC EQU 1 ;get FWA(bios) from here ORG ORGP LINK OCCRAMA.ASM page ; Main loop FMT: LDK SP,STK LDK DE,PRMT CALL ESTR ;Output initial prompt :1: CALL GCHR ;Get response CMP ABTC JZ SYSR ;if ABORT RLC STO A,DRV1 ;set ascii drive RRC SUB 'A' JRC FMT ;if illegal request JRZ :2 ;if A drive CMP 1 JRNZ FMT ;if illegal (A,B only) ; A= drive to function :2: STO A,DRV ;save drive STO A,SDISK CALL FMTDSK ;Format one disk CALL VERIFY ;verify proper formatt ;start again :4: OR A JZ :6 ;if no error LDK C,'E' :6: CALL CO ;output indicator LD A,SAVTRK INC A STO A,SAVTRK ;update track number CMP MTRK JRNZ :2 ;continue till all tracks ; CALL RDRV ;reset drive RET page VERIFY: ;Verify that format worked correctly - ;read in disk 1 track @ a time to ck for errors ;Uses All. Proc LK DE,MSEC*LSECB ;length of 1 track LK HL,BUFFR ;FWA of track buffer LK C, not FMTCHR ;chr to broadcast CALL FILLC ;fill BUFFR with DB chrs LK DE,CRLF CALL ESTR LK A,0 STO A,TRAK ;start @ OUTSIDE track CALL RDRV ;reset the disk... :hang: :loop: LK BC,BUFFR ;verify free-core area CALL SETDMA LK BC,1 ;start @ sector #1 CALL SETSEC LD A,TRAK LK B,0 ! MOV C,A ;BC = trk# CALL SETTRK LK B,MSEC ;read in MSEC sectors CALL XFER ;read in 1 track @ RBUF OR A JRNZ :serr ;if seek-type error CALL COMPAR ;compare with expected JRNZ :err :good: LK DE,VSTR ;if OK JR :2 :serr: ;Seek type error- ckCALL BIOS+BSDSK ;issue SELDSK CMP -1 RNZ ;if drive ready LD A,RTST CMP 10 JNZ :3 ;if NOT first time LDK DE,DNRMS1 CALL ESTR ;ask if drive ready CALL GCHR CMP 'Y' RZ ;if drive ready confirmation JMP SYSR ;else exit :3: LDK DE,DNRMS CALL ESTR ;indicate drive NOT ready LD A,RTST DEC A JNZ :2 ;if not n tries JMP FMT ;start again RTST: DB 0 page FORDAT: ;Format data for IBM 3740. ; **** Gap 5 **** ECHO 40 ! DB 0FFh ENDM ECHO 6 ! DB 0 ENDM DB 0FCh ;Index mark ; **** Gap 1 **** FMTSD: ECHO 16 ! DB 0FFh ENDM DB 0,0,0,0,0,0 DB 0FEh ;Id address mark FTRKN: DB 0 ;track TRKOFS = *-FORDAT DB 0 ;side FSECN: DB 1 ;sector IF LSECB = 128 DB 0 ENDIF IF LSECB = 256 DB 1 ENDIF IF LSECB > 256 .9 DW LSECB, Error unknown sector length ENDIF DB 0F7h ;CRC ; **** Gap 2 **** ECHO 11 ! DB 0FFh ENDM ECHO 6 ! DB 0 ENDM FDAM: DB 0FBh ;Data address mark :noref: EQU FMTCHR ;...this symbol won't create ;180RB = H.IEEE+3 ;Control register B ROMVEC = 100h ;Start of rom vector BKPI = 0CFh ;Breakpoint interrupt = 1 NMIA = 66h ;NMI address LCMDP = 4*1024 ;L command prom MSG 'Assembly for 64k Mode 2 system' MRAM = 0ED80h FWAVM = 0F000h NVDL = 24 INTBL = 0EFF0h ;interrupt vector table LVMEM = 128*32 ;Length of video memory LVDIS = 128*NVDL ;Length of Display memory VFLO = -22 ;First line video offset VLL = 128 ;Length of one video line VLDL = 52 ;Length of displayed char/line FWASM = FWAVM+VLDL ;Status info LWAMEM = FWAVM+LVMEM-1 @FREQ = 4000 ;freq of processor SCLFRE = @FREQ/22 ;for DELAY routine ; Define Ascii Control Codes. ; Value ;^Control Meaning in Phoenix EOS = 0 ;^@, NUL = End of String TBFCHR = 'A'-40h ;^A, SOH = Tab with fill BTBCHR = 02h ;^B, STX = BACK TAB CHARACTER ABTCHR = 03h ;^C, ETX = ABORT CHARACTER BELCHR = 07h ;^G, BEL = Audible Alarm BKSCHR = 08h ;^H, BS = PHYSICAL BACKSPACE CHARACTER TABCHR = 09h ;^I, HT = Tab LF = 0Ah ;^J, LF = Lin useless xrefs... ; **** Data **** ECHO 128 DB :noref ENDM IF LSECB > 128 ECHO 128 DB :noref ENDM ENDIF FCRC2: DB 0F7h ;CRC FMTEND = * LIFMT = *-FORDAT LISEC = *-FMTSD page VSTR: DCE 'V','H'-40h ! DCE 'H'-40h ;used for GOOD verify ERSTR: DCE 'E','H'-40h ! DCE 'H'-40h ;BAD verify ;LFSTR: DBE 'H'-40h ! DCE LF ;^H,LF CR2LF DBE lf CRLF: DBE cr ! DCE lf ESTR: ;Output encoded data to console ; Entry DE= FWA of code ; Entry DE=FWA of source OR A ;clear cbit LD A,[de] RRC ;normalize char PUSH AF AND 07Fh MOV C,A CALL CO ;output it INC DE POP AF JRNC ESTR ;if not done RET CO: ;Output one char to screen ; Entry C= char PUSH HL ! PUSH DE ! PUSH BC MOV E,C SYSTEM WCO ;output to console POP BC ! POP DE ! POP HL RET space 4,10 GCHR: ;Get character NORMALIZED from console ; Exit A=C= char proc PUSH HL ! PUSH DE SYSTEM RCO MOV A,C CMP 'a' JC :4 ;if not lower case CMP 'z'+1 JNC :4 ;if not lower case SUI 'a'-'A' : TITLE 'OCCTXT.AST - Osborne Computer Corp System Text.' SVER = 1 ; Legal values for RSIZE are 48 and 64 ; entered from command line RSIZE = 64 ; default = 64k version ;SYSTEXT.AST - Installation Parameter Configuration. ; This Systext provides definitions of system configuration, ; and Ascii control characters for the DigiCon Phoenix ; product set. ; ; This text is configured for CP/m. Another text must be ; used to assemble routines for Phoenix. Both are derived ; from IPARAMS.ACD. space 4,10 ; True/False definitions FALSE = 0 TRUE = not FALSE space 4,10 ODEBUG = FALSE ;True for activating debug code SYcpm20 = 4 ;=cpm 2.0 SYcpm14 = 0 SYmpm10 = 5 SYcdos = 3 @CPM SET TRUE space 4,10 ; List output page/line/column definitions MLPP = 66 ;Number of lines per physical page LLP = 60 ;NUMBER OF LINES TO PRINT PER PAGN ; Disk type and speed constants SEEKTM = 2 ;if MPI, 0 ;if Siemems then 2 ; Keyboard debounce KBDTM = 5 ;WAIT 1 MILLISECOND FOR DEBOUNCE ; Assemble Feed VTAB = 0Bh ;^K, VT = Vertical Tab FMFD = 0Ch ;^L, FF = Form Feed CR = 0Dh ;^M, CR = Carriage Return LITCHR = 0Eh ;^N, SO = Literal next character SUSCHR = 0Fh ;^O, SI = SUSPEND CHARACTER RTYCHR = 12h ;^R, DC2 = RE-TYPE LINE CHARACTER CTRU = 15h ;^U, NAK = Cancel Line char BWDCHR = 17h ;^W, ETB = Backspace ONE word CANCHR = 18h ;^X, CAN = CANCEL LINE CHARACTER ESC = 1Bh ;^[, ESC = Escape RUBOUT = 7Fh ; DEL = Rubout (accepted as Backspace) ERC = 7Fh ;illegal key REP = 02 ;repeat key CAPS = 03 ;repeat SHTK = 04 ;Shift key CTRL = 05 ;Control key CLRSRN = 'Z'-40h ;Clear screen MCR = 'L'-40h ;Form Feed (move cursor right) PMCHR = '>' ;Prompt character ERCHR = '?' ;Error character BKS = 08h EOFC = 'Z'-40h ;Eof character (same as cpm) TAB = 09h INVV = 80h ;Inverse video bit GESC = 'a' ;Graphics escape toggle char ; CP/m definitions CVER = 22 ;CPM version number CBIOSV = SVER ;cbios version NDSKS = 2 K = 1024 MSIZE = RSIZE-(LVMEM/K) BIOS = MSIZE*k-0A004: POP DE ! POP HL RET DS 30*2 STK: DS 0 ORG RBUF ;FWA of format image DS MTRK*LIFMT ;length of format image BUFFR: DS 0 ;Verify data area ; Endx OFORMAT OD verify ERSTR: DCE 'E','H'-40h ! DCE 'H'-40h ;BAD verify ;LFSTR: DBE 'H'-40h ! DCE LF ;^H,LF CR2LF DBE lf CRLF: DBE cr ! DCE lf ESTR: ;Output encoded data to console ; Entry DE= FWA of code ; Entry DE=FWA of source OR A ;clear cbit LD A,[de] RRC ;normalize char PUSH AF AND 07Fh MOV C,A CALL CO ;output it INC DE POP AF JRNC ESTR ;if not done RET CO: ;Output one char to screen ; Entry C= char PUSH HL ! PUSH DE ! PUSH BC MOV E,C SYSTEM WCO ;output to console POP BC ! POP DE ! POP HL RET space 4,10 GCHR: ;Get character NORMALIZED from console ; Exit A=C= char proc PUSH HL ! PUSH DE SYSTEM RCO MOV A,C CMP 'a' JC :4 ;if not lower case CMP 'z'+1 JNC :4 ;if not lower case SUI 'a'-'A' :y Constants ; Port definitions (Memory mapped) H.FDC = 2100h ;Floppy disk H.KEY = 2200h ;Keyboard H.SIO = 2A00h ;Serial I/O H.VIO = 2C00h ;Video memory controls SEKTMO = 0FEh ;Seek Time Out error status ; SIO Interface and definitions H.SXMT = H.SIO+1 ;Transmit address H.SREC = H.SIO+1 ;Receive (read from address) H.SCTRL = H.SIO ;Set control reg (write) H.SSTS = H.SIO ;Status reg (read) ; Defined function codes SI.MRST = 0_10_101_11b ;Master reset SI.S16 = 0_10_101_01b ;Select 16x clock, xmit/rec ;8 data, 1 stop bit ;RTS high (no request) SI.S64 = 0_10_101_10b ;Select 64x clock ;...all else identical to SI.S16 SI.1200 = SI.S16 SI.300 = SI.S64 SI.RRDY = 01 ;Receiver ready SI.TRDY = 02 ;Transmit ready SI.NCTS = 0000_1000b ; ~CTS SI.RERR = 0111_0000b ;Rcvr err: PE, OVRN, FE H.IEEE = 2900h ;IEEE interface CPDRA = H.IEEE+0 ;Peripheral/Direction register A CCRA = H.IEEE+1 ;Control register A CPDRB = H.IEEE+2 ;Peripheral/Direction register B CCh ;Start of CPM jump table BDOS = BIOS-0E00h+6 ;BDOS entry pt CCP = BIOS-1600h ;CCP entry ABTC = 'C'-40h ;Abort character ALVS: = 0Ch ;*** must be validated with CBIOS *** CSVS: = 20h ;*** --------------------------- *** LSECB = 256 ;Length of a sector MTRK = 40 ;Maximum tracks MSEC = 10 ;Maximum Sectors FMTCHR = 0E5h ;Data format char space 4,10 ; Set CP/M symbols. IOBYTE = 3 iob_default = 80h ;default value for iobyte CDISK = 4 ;Current logged-in CPM disk SYS = 5 ;CPM Monitor call address SYSL = SYS+1 ;(lwa of CP/m) TIMPTR = 40h ;points to where time kept DBUF = 80h ;Default disk buffer OM#LRAM = SYSL SYSR = 0 ;return to system SYSDSK = CDISK ;Contains current system disk # SYSDAT = 10h ;Date in ddmmyy SYSTIM = SYSDAT+3 ;Time in hhmmss ORGP = 100h ;Transient program origin ;EOL does not exist in CP/M EOM = '$' ;End of Message (for system function) EOF = 'Z'-40h ;End of File L$PRU = 128 ;Bytes per floppy sector FCB = 5Ch ;SYSTEM DEFAULT FILSelect Disk drive BSTTRK = 10*3 ;Set track BSTSEC = 11*3 ;Set sector BSTDMA = 12*3 ;Set DMA BREAD = 13*3 ;READ disk pru BWRITE = 14*3 ;WRITE disk pru BLSTS = 15*3 ;List LST: device status BSECTR = 16*3 ;Sector translate routine ; Extensions added by SORCIM BRRXT = 17*3 ;Ram-Rom exit BROMJP = 18*3 ;Rom jump entry BFMTJP = 19*3 ;Format Track BSBAUD = 20*3 ;Set Serial Port Baud Rate ;BRDXY = 21*3 ;Read XY ;BWTXY = 22*3 ;Write XY ; IEEE-488 calls BIECO = 21*3 ;Control Out BIESI = 22*3 ;Status In BIEGTS = 23*3 ;Go To Standby BIETC = 24*3 ;Take Control BIEOIM = 25*3 ;Output Interface Message BIEODM = 26*3 ;Output Device Message BIEIDM = 27*3 ;Input Device Message BIEPP = 28*3 ;Parallel Poll ;BIOCAL Used to make calls directly to BIO BIOCAL MACRO NAME LXI HL,L%K PUSH HL ;return LHLD SYSR+1 MVI L,%1 PCHL L%K: ENDM space 4,10 ;SYSCAL ; This macro is to be used when the common deck ; SYS.ACD is used to make file action ; requests. SYSCAL MACRO NAME,ARG .1 IF TITLE 'OCCTXT.AST - Osborne Computer Corp System Text.' SVER = 1 ; Legal values for RSIZE are 48 and 64 ; entered from command line RSIZE = 64 ; default = 64k version ;SYSTEXT.AST - Installation Parameter Configuration. ; This Systext provides definitions of system configuration, ; and Ascii control characters for the DigiCon Phoenix ; product set. ; ; This text is configured for CP/m. Another text must be ; used to assemble routines for Phoenix. Both are derived ; from IPARAMS.ACD. space 4,10 ; True/False definitions FALSE = 0 TRUE = not FALSE space 4,10 ODEBUG = FALSE ;True for activating debug code SYcpm20 = 4 ;=cpm 2.0 SYcpm14 = 0 SYmpm10 = 5 SYcdos = 3 @CPM SET TRUE space 4,10 ; List output page/line/column definitions MLPP = 66 ;Number of lines per physical page LLP = 60 ;NUMBER OF LINES TO PRINT PER PAGN ; Disk type and speed constants SEEKTM = 2 ;if MPI, 0 ;if Siemems then 2 ; Keyboard debounce KBDTM = 5 ;WAIT 1 MILLISECOND FOR DEBOUNCE ; AssemblE CONTROL BLOCK FCB_NR = FCB+32 ;Next Record = LRN F_EX = 12 ;Extend field F_S2 = 14 ;CPM system usage **used with 2.0 SIZ funct** F_RC = 15 ;Record count (0 to 128) F_NR = 32 ;Next record ordinal in FCB F_RRF = 33 ;r0,r1,r2 (random field) in FCB L_FILEN = 8 ;length of File name L_FILEX = 3 ;length of filename suffix L_FCB = 36 ;length of entire FCB (comp with 2.0) DMA = 80h ;WHERE SECTORS ARE READ TBUFF = DMA ;where CCP puts command line OS#RCO = 1 ;Read character from CRT: OS#WCO = 2 ;Write character to CRT: device OS#RRO = 3 ;Read from reader OS#WPO = 4 ;Write to punch OS#WLO = 5 ;Write character to LST: device OS#OST = 9 ;Write string on CRT: OS#RCB = 10 ;Read Console Buffer OS#CABT = 11 ;Get Console status OS#VER = 12 ;Return version in HL ;H= 0, L=0 if 1.4 ;H= 0, L=xx if 2.0 or > ;H= 1, L=0 if MPM 1.0 OS#IND = 13 ;Initialize/Reset Disk system OS#DSEL = 14 ;Select disk unit OS#OPEN = 15 ;Open file OS#CLOS = 16 ;Close file OS#SDIR = 17 ;Search directory OS#NDIR ' %2' <> ' ' LXI DE,%2 .1 ENDIF CALL %1 ENDM space 4,10 ; SYSTEM This macro provides a mechanism to call the ; system directly. SYSTEM MACRO FUN,ARG .2 IF DEF OS#%1 MVI C,OS#%1 .1 IF ' %2' <> ' ' LXI DE,%2 .1 ENDIF CALL SYS .2 ELSE CALL %1 .2 ENDIF ENDM ; ALIGN Set origin to even boundary. ALIGN MACRO SIZ,O DS (*%2+(%1)-1)/(%1)*(%1)-*%2 ENDM ; DI and EI are macros so all usages can be accounted ; for in the cr. DI MACRO DB 0F3h ENDM EI MACRO DB 0FBh ENDM ; Macros to enable ROM, RAM and DIM code ENAROM MACRO DI OUT 0 LDK A,0 STO A,ROMRAM EI ENDM DISROM MACRO DI OUT 1 LDK A,1 STO A,ROMRAM EI ENDM BRTBIT: = 80h ;set brt/dim memory BRIGHT DIMBIT: = 00h ;set brt/dim memory DIM ENADIM MACRO OUT 2 ENDM DISDIM MACRO OUT 3 ENDM PUSHAL MACRO PUSH AF PUSH BC PUSH DE PUSH HL PUSH IX PUSH IY ENDM POPALL MACRO POP IY POP IX POP HL POP DE POP BC POP AF ENDM ; Endx SYSTEXT y Constants ; Port definitions (Memory mapped) H.FDC = 2100h ;Floppy disk H.KEY = 2200h ;Keyboard H.SIO = 2A00h ;Serial I/O H.VIO = 2C00h ;Video memory controls SEKTMO = 0FEh ;Seek Time Out error status ; SIO Interface and definitions H.SXMT = H.SIO+1 ;Transmit address H.SREC = H.SIO+1 ;Receive (read from address) H.SCTRL = H.SIO ;Set control reg (write) H.SSTS = H.SIO ;Status reg (read) ; Defined function codes SI.MRST = 0_10_101_11b ;Master reset SI.S16 = 0_10_101_01b ;Select 16x clock, xmit/rec ;8 data, 1 stop bit ;RTS high (no request) SI.S64 = 0_10_101_10b ;Select 64x clock ;...all else identical to SI.S16 SI.1200 = SI.S16 SI.300 = SI.S64 SI.RRDY = 01 ;Receiver ready SI.TRDY = 02 ;Transmit ready SI.NCTS = 0000_1000b ; ~CTS SI.RERR = 0111_0000b ;Rcvr err: PE, OVRN, FE H.IEEE = 2900h ;IEEE interface CPDRA = H.IEEE+0 ;Peripheral/Direction register A CCRA = H.IEEE+1 ;Control register A CPDRB = H.IEEE+2 ;Peripheral/Direction register B CC = 18 ;next entry in directory OS#DEL = 19 ;Delete a file OS#READ = 20 ;READ next pru from file OS#WRT = 21 ;Write one PRU to file OS#CRE = 22 ;Create a file OS#REN = 23 ;rename a file OS#RLV = 24 ;Return login vector OS#RCD = 25 ;Return current disk OS#SDMA = 26 ;Set DMA address ; CPM 2.x functions OS#DIO = 6 ;Console direct I/O OS#GAA = 27 ;Get adr (alloc) OS#SWP = 28 ;Set write protect for disk OS#GRO = 29 ;Get r/o vector OS#SFA = 30 ;Set File attributes OS#GAD = 31 ;Get disk parms address OS#USC = 32 ;Get/set user code OS#RRD = 33 ;Read random OS#WRD = 34 ;Write random OS#FIZ = 35 ;Get file size in records OS#SRD = 36 ;Set random record OS#RDRV = 37 ;Reset drive OS#ADR = 38 ;Access drive OS#FDR = 39 ;Free drive OS#WRZ = 40 ;Write random with zero fill ; Direct BIO call definitions BCONST = 2*3 ;Console status BCONIN SET 3*3 BCOUT = 4*3 ;Console output BLIST = 5*3 ;List output BPUNCH = 6*3 ;Punch output BRDR = 7*3 ;Reader input BHOME = 8*3 ;Home disk drive BSDSK = 9*3 ;RB = H.IEEE+3 ;Control register B ROMVEC = 100h ;Start of rom vector BKPI = 0CFh ;Breakpoint interrupt = 1 NMIA = 66h ;NMI address LCMDP = 4*1024 ;L command prom MSG 'Assembly for 64k Mode 2 system' MRAM = 0ED80h FWAVM = 0F000h NVDL = 24 INTBL = 0EFF0h ;interrupt vector table LVMEM = 128*32 ;Length of video memory LVDIS = 128*NVDL ;Length of Display memory VFLO = -22 ;First line video offset VLL = 128 ;Length of one video line VLDL = 52 ;Length of displayed char/line FWASM = FWAVM+VLDL ;Status info LWAMEM = FWAVM+LVMEM-1 @FREQ = 4000 ;freq of processor SCLFRE = @FREQ/22 ;for DELAY routine ; Define Ascii Control Codes. ; Value ;^Control Meaning in Phoenix EOS = 0 ;^@, NUL = End of String TBFCHR = 'A'-40h ;^A, SOH = Tab with fill BTBCHR = 02h ;^B, STX = BACK TAB CHARACTER ABTCHR = 03h ;^C, ETX = ABORT CHARACTER BELCHR = 07h ;^G, BEL = Audible Alarm BKSCHR = 08h ;^H, BS = PHYSICAL BACKSPACE CHARACTER TABCHR = 09h ;^I, HT = Tab LF = 0Ah ;^J, LF = LinE CONTROL BLOCK FCB_NR = FCB+32 ;Next Record = LRN F_EX = 12 ;Extend field F_S2 = 14 ;CPM system usage **used with 2.0 SIZ funct** F_RC = 15 ;Record count (0 to 128) F_NR = 32 ;Next record ordinal in FCB F_RRF = 33 ;r0,r1,r2 (random field) in FCB L_FILEN = 8 ;length of File name L_FILEX = 3 ;length of filename suffix L_FCB = 36 ;length of entire FCB (comp with 2.0) DMA = 80h ;WHERE SECTORS ARE READ TBUFF = DMA ;where CCP puts command line OS#RCO = 1 ;Read character from CRT: OS#WCO = 2 ;Write character to CRT: device OS#RRO = 3 ;Read from reader OS#WPO = 4 ;Write to punch OS#WLO = 5 ;Write character to LST: device OS#OST = 9 ;Write string on CRT: OS#RCB = 10 ;Read Console Buffer OS#CABT = 11 ;Get Console status OS#VER = 12 ;Return version in HL ;H= 0, L=0 if 1.4 ;H= 0, L=xx if 2.0 or > ;H= 1, L=0 if MPM 1.0 OS#IND = 13 ;Initialize/Reset Disk system OS#DSEL = 14 ;Select disk unit OS#OPEN = 15 ;Open file OS#CLOS = 16 ;Close file OS#SDIR = 17 ;Search directory OS#NDIR ' %2' <> ' ' LXI DE,%2 .1 ENDIF CALL %1 ENDM space 4,10 ; SYSTEM This macro provides a mechanism to call the ; system directly. SYSTEM MACRO FUN,ARG .2 IF DEF OS#%1 MVI C,OS#%1 .1 IF ' %2' <> ' ' LXI DE,%2 .1 ENDIF CALL SYS .2 ELSE CALL %1 .2 ENDIF ENDM ; ALIGN Set origin to even boundary. ALIGN MACRO SIZ,O DS (*%2+(%1)-1)/(%1)*(%1)-*%2 ENDM ; DI and EI are macros so all usages can be accounted ; for in the cr. DI MACRO DB 0F3h ENDM EI MACRO DB 0FBh ENDM ; Macros to enable ROM, RAM and DIM code ENAROM MACRO DI OUT 0 LDK A,0 STO A,ROMRAM EI ENDM DISROM MACRO DI OUT 1 LDK A,1 STO A,ROMRAM EI ENDM BRTBIT: = 80h ;set brt/dim memory BRIGHT DIMBIT: = 00h ;set brt/dim memory DIM ENADIM MACRO OUT 2 ENDM DISDIM MACRO OUT 3 ENDM PUSHAL MACRO PUSH AF PUSH BC PUSH DE PUSH HL PUSH IX PUSH IY ENDM POPALL MACRO POP IY POP IX POP HL POP DE POP BC POP AF ENDM ; Endx SYSTEXT e Feed VTAB = 0Bh ;^K, VT = Vertical Tab FMFD = 0Ch ;^L, FF = Form Feed CR = 0Dh ;^M, CR = Carriage Return LITCHR = 0Eh ;^N, SO = Literal next character SUSCHR = 0Fh ;^O, SI = SUSPEND CHARACTER RTYCHR = 12h ;^R, DC2 = RE-TYPE LINE CHARACTER CTRU = 15h ;^U, NAK = Cancel Line char BWDCHR = 17h ;^W, ETB = Backspace ONE word CANCHR = 18h ;^X, CAN = CANCEL LINE CHARACTER ESC = 1Bh ;^[, ESC = Escape RUBOUT = 7Fh ; DEL = Rubout (accepted as Backspace) ERC = 7Fh ;illegal key REP = 02 ;repeat key CAPS = 03 ;repeat SHTK = 04 ;Shift key CTRL = 05 ;Control key CLRSRN = 'Z'-40h ;Clear screen MCR = 'L'-40h ;Form Feed (move cursor right) PMCHR = '>' ;Prompt character ERCHR = '?' ;Error character BKS = 08h EOFC = 'Z'-40h ;Eof character (same as cpm) TAB = 09h INVV = 80h ;Inverse video bit GESC = 'a' ;Graphics escape toggle char ; CP/m definitions CVER = 22 ;CPM version number CBIOSV = SVER ;cbios version NDSKS = 2 K = 1024 MSIZE = RSIZE-(LVMEM/K) BIOS = MSIZE*k-0600 = 18 ;next entry in directory OS#DEL = 19 ;Delete a file OS#READ = 20 ;READ next pru from file OS#WRT = 21 ;Write one PRU to file OS#CRE = 22 ;Create a file OS#REN = 23 ;rename a file OS#RLV = 24 ;Return login vector OS#RCD = 25 ;Return current disk OS#SDMA = 26 ;Set DMA address ; CPM 2.x functions OS#DIO = 6 ;Console direct I/O OS#GAA = 27 ;Get adr (alloc) OS#SWP = 28 ;Set write protect for disk OS#GRO = 29 ;Get r/o vector OS#SFA = 30 ;Set File attributes OS#GAD = 31 ;Get disk parms address OS#USC = 32 ;Get/set user code OS#RRD = 33 ;Read random OS#WRD = 34 ;Write random OS#FIZ = 35 ;Get file size in records OS#SRD = 36 ;Set random record OS#RDRV = 37 ;Reset drive OS#ADR = 38 ;Access drive OS#FDR = 39 ;Free drive OS#WRZ = 40 ;Write random with zero fill ; Direct BIO call definitions BCONST = 2*3 ;Console status BCONIN SET 3*3 BCOUT = 4*3 ;Console output BLIST = 5*3 ;List output BPUNCH = 6*3 ;Punch output BRDR = 7*3 ;Reader input BHOME = 8*3 ;Home disk drive BSDSK = 9*3 ;h ;Start of CPM jump table BDOS = BIOS-0E00h+6 ;BDOS entry pt CCP = BIOS-1600h ;CCP entry ABTC = 'C'-40h ;Abort character ALVS: = 0Ch ;*** must be validated with CBIOS *** CSVS: = 20h ;*** --------------------------- *** LSECB = 256 ;Length of a sector MTRK = 40 ;Maximum tracks MSEC = 10 ;Maximum Sectors FMTCHR = 0E5h ;Data format char space 4,10 ; Set CP/M symbols. IOBYTE = 3 iob_default = 80h ;default value for iobyte CDISK = 4 ;Current logged-in CPM disk SYS = 5 ;CPM Monitor call address SYSL = SYS+1 ;(lwa of CP/m) TIMPTR = 40h ;points to where time kept DBUF = 80h ;Default disk buffer OM#LRAM = SYSL SYSR = 0 ;return to system SYSDSK = CDISK ;Contains current system disk # SYSDAT = 10h ;Date in ddmmyy SYSTIM = SYSDAT+3 ;Time in hhmmss ORGP = 100h ;Transient program origin ;EOL does not exist in CP/M EOM = '$' ;End of Message (for system function) EOF = 'Z'-40h ;End of File L$PRU = 128 ;Bytes per floppy sector FCB = 5Ch ;SYSTEM DEFAULT FILSelect Disk drive BSTTRK = 10*3 ;Set track BSTSEC = 11*3 ;Set sector BSTDMA = 12*3 ;Set DMA BREAD = 13*3 ;READ disk pru BWRITE = 14*3 ;WRITE disk pru BLSTS = 15*3 ;List LST: device status BSECTR = 16*3 ;Sector translate routine ; Extensions added by SORCIM BRRXT = 17*3 ;Ram-Rom exit BROMJP = 18*3 ;Rom jump entry BFMTJP = 19*3 ;Format Track BSBAUD = 20*3 ;Set Serial Port Baud Rate ;BRDXY = 21*3 ;Read XY ;BWTXY = 22*3 ;Write XY ; IEEE-488 calls BIECO = 21*3 ;Control Out BIESI = 22*3 ;Status In BIEGTS = 23*3 ;Go To Standby BIETC = 24*3 ;Take Control BIEOIM = 25*3 ;Output Interface Message BIEODM = 26*3 ;Output Device Message BIEIDM = 27*3 ;Input Device Message BIEPP = 28*3 ;Parallel Poll ;BIOCAL Used to make calls directly to BIO BIOCAL MACRO NAME LXI HL,L%K PUSH HL ;return LHLD SYSR+1 MVI L,%1 PCHL L%K: ENDM space 4,10 ;SYSCAL ; This macro is to be used when the common deck ; SYS.ACD is used to make file action ; requests. SYSCAL MACRO NAME,ARG .1 IF TITLE 'OCCTXT.AST - Osborne Computer Corp System Text.' SVER = 1 ; Legal values for RSIZE are 48 and 64 ; entered from command line RSIZE = 64 ; default = 64k version ;SYSTEXT.AST - Installation Parameter Configuration. ; This Systext provides definitions of system configuration, ; and Ascii control characters for the DigiCon Phoenix ; product set. ; ; This text is configured for CP/m. Another text must be ; used to assemble routines for Phoenix. Both are derived ; from IPARAMS.ACD. space 4,10 ; True/False definitions FALSE = 0 TRUE = not FALSE space 4,10 ODEBUG = FALSE ;True for activating debug code SYcpm20 = 4 ;=cpm 2.0 SYcpm14 = 0 SYmpm10 = 5 SYcdos = 3 @CPM SET TRUE space 4,10 ; List output page/line/column definitions MLPP = 66 ;Number of lines per physical page LLP = 60 ;NUMBER OF LINES TO PRINT PER PAGN ; Disk type and speed constants SEEKTM = 2 ;if MPI, 0 ;if Siemems then 2 ; Keyboard debounce KBDTM = 5 ;WAIT 1 MILLISECOND FOR DEBOUNCE ; Assemble Feed VTAB = 0Bh ;^K, VT = Vertical Tab FMFD = 0Ch ;^L, FF = Form Feed CR = 0Dh ;^M, CR = Carriage Return LITCHR = 0Eh ;^N, SO = Literal next character SUSCHR = 0Fh ;^O, SI = SUSPEND CHARACTER RTYCHR = 12h ;^R, DC2 = RE-TYPE LINE CHARACTER CTRU = 15h ;^U, NAK = Cancel Line char BWDCHR = 17h ;^W, ETB = Backspace ONE word CANCHR = 18h ;^X, CAN = CANCEL LINE CHARACTER ESC = 1Bh ;^[, ESC = Escape RUBOUT = 7Fh ; DEL = Rubout (accepted as Backspace) ERC = 7Fh ;illegal key REP = 02 ;repeat key CAPS = 03 ;repeat SHTK = 04 ;Shift key CTRL = 05 ;Control key CLRSRN = 'Z'-40h ;Clear screen MCR = 'L'-40h ;Form Feed (move cursor right) PMCHR = '>' ;Prompt character ERCHR = '?' ;Error character BKS = 08h EOFC = 'Z'-40h ;Eof character (same as cpm) TAB = 09h INVV = 80h ;Inverse video bit GESC = 'a' ;Graphics escape toggle char ; CP/m definitions CVER = 22 ;CPM version number CBIOSV = SVER ;cbios version NDSKS = 2 K = 1024 MSIZE = RSIZE-(LVMEM/K) BIOS = MSIZE*k-0600 = 18 ;next entry in directory OS#DEL = 19 ;Delete a file OS#READ = 20 ;READ next pru from file OS#WRT = 21 ;Write one PRU to file OS#CRE = 22 ;Create a file OS#REN = 23 ;rename a file OS#RLV = 24 ;Return login vector OS#RCD = 25 ;Return current disk OS#SDMA = 26 ;Set DMA address ; CPM 2.x functions OS#DIO = 6 ;Console direct I/O OS#GAA = 27 ;Get adr (alloc) OS#SWP = 28 ;Set write protect for disk OS#GRO = 29 ;Get r/o vector OS#SFA = 30 ;Set File attributes OS#GAD = 31 ;Get disk parms address OS#USC = 32 ;Get/set user code OS#RRD = 33 ;Read random OS#WRD = 34 ;Write random OS#FIZ = 35 ;Get file size in records OS#SRD = 36 ;Set random record OS#RDRV = 37 ;Reset drive OS#ADR = 38 ;Access drive OS#FDR = 39 ;Free drive OS#WRZ = 40 ;Write random with zero fill ; Direct BIO call definitions BCONST = 2*3 ;Console status BCONIN SET 3*3 BCOUT = 4*3 ;Console output BLIST = 5*3 ;List output BPUNCH = 6*3 ;Punch output BRDR = 7*3 ;Reader input BHOME = 8*3 ;Home disk drive BSDSK = 9*3 ;y Constants ; Port definitions (Memory mapped) H.FDC = 2100h ;Floppy disk H.KEY = 2200h ;Keyboard H.SIO = 2A00h ;Serial I/O H.VIO = 2C00h ;Video memory controls SEKTMO = 0FEh ;Seek Time Out error status ; SIO Interface and definitions H.SXMT = H.SIO+1 ;Transmit address H.SREC = H.SIO+1 ;Receive (read from address) H.SCTRL = H.SIO ;Set control reg (write) H.SSTS = H.SIO ;Status reg (read) ; Defined function codes SI.MRST = 0_10_101_11b ;Master reset SI.S16 = 0_10_101_01b ;Select 16x clock, xmit/rec ;8 data, 1 stop bit ;RTS high (no request) SI.S64 = 0_10_101_10b ;Select 64x clock ;...all else identical to SI.S16 SI.1200 = SI.S16 SI.300 = SI.S64 SI.RRDY = 01 ;Receiver ready SI.TRDY = 02 ;Transmit ready SI.NCTS = 0000_1000b ; ~CTS SI.RERR = 0111_0000b ;Rcvr err: PE, OVRN, FE H.IEEE = 2900h ;IEEE interface CPDRA = H.IEEE+0 ;Peripheral/Direction register A CCRA = H.IEEE+1 ;Control register A CPDRB = H.IEEE+2 ;Peripheral/Direction register B CCh ;Start of CPM jump table BDOS = BIOS-0E00h+6 ;BDOS entry pt CCP = BIOS-1600h ;CCP entry ABTC = 'C'-40h ;Abort character ALVS: = 0Ch ;*** must be validated with CBIOS *** CSVS: = 20h ;*** --------------------------- *** LSECB = 256 ;Length of a sector MTRK = 40 ;Maximum tracks MSEC = 10 ;Maximum Sectors FMTCHR = 0E5h ;Data format char space 4,10 ; Set CP/M symbols. IOBYTE = 3 iob_default = 80h ;default value for iobyte CDISK = 4 ;Current logged-in CPM disk SYS = 5 ;CPM Monitor call address SYSL = SYS+1 ;(lwa of CP/m) TIMPTR = 40h ;points to where time kept DBUF = 80h ;Default disk buffer OM#LRAM = SYSL SYSR = 0 ;return to system SYSDSK = CDISK ;Contains current system disk # SYSDAT = 10h ;Date in ddmmyy SYSTIM = SYSDAT+3 ;Time in hhmmss ORGP = 100h ;Transient program origin ;EOL does not exist in CP/M EOM = '$' ;End of Message (for system function) EOF = 'Z'-40h ;End of File L$PRU = 128 ;Bytes per floppy sector FCB = 5Ch ;SYSTEM DEFAULT FILSelect Disk drive BSTTRK = 10*3 ;Set track BSTSEC = 11*3 ;Set sector BSTDMA = 12*3 ;Set DMA BREAD = 13*3 ;READ disk pru BWRITE = 14*3 ;WRITE disk pru BLSTS = 15*3 ;List LST: device status BSECTR = 16*3 ;Sector translate routine ; Extensions added by SORCIM BRRXT = 17*3 ;Ram-Rom exit BROMJP = 18*3 ;Rom jump entry BFMTJP = 19*3 ;Format Track BSBAUD = 20*3 ;Set Serial Port Baud Rate ;BRDXY = 21*3 ;Read XY ;BWTXY = 22*3 ;Write XY ; IEEE-488 calls BIECO = 21*3 ;Control Out BIESI = 22*3 ;Status In BIEGTS = 23*3 ;Go To Standby BIETC = 24*3 ;Take Control BIEOIM = 25*3 ;Output Interface Message BIEODM = 26*3 ;Output Device Message BIEIDM = 27*3 ;Input Device Message BIEPP = 28*3 ;Parallel Poll ;BIOCAL Used to make calls directly to BIO BIOCAL MACRO NAME LXI HL,L%K PUSH HL ;return LHLD SYSR+1 MVI L,%1 PCHL L%K: ENDM space 4,10 ;SYSCAL ; This macro is to be used when the common deck ; SYS.ACD is used to make file action ; requests. SYSCAL MACRO NAME,ARG .1 IFRB = H.IEEE+3 ;Control register B ROMVEC = 100h ;Start of rom vector BKPI = 0CFh ;Breakpoint interrupt = 1 NMIA = 66h ;NMI address LCMDP = 4*1024 ;L command prom MSG 'Assembly for 64k Mode 2 system' MRAM = 0ED80h FWAVM = 0F000h NVDL = 24 INTBL = 0EFF0h ;interrupt vector table LVMEM = 128*32 ;Length of video memory LVDIS = 128*NVDL ;Length of Display memory VFLO = -22 ;First line video offset VLL = 128 ;Length of one video line VLDL = 52 ;Length of displayed char/line FWASM = FWAVM+VLDL ;Status info LWAMEM = FWAVM+LVMEM-1 @FREQ = 4000 ;freq of processor SCLFRE = @FREQ/22 ;for DELAY routine ; Define Ascii Control Codes. ; Value ;^Control Meaning in Phoenix EOS = 0 ;^@, NUL = End of String TBFCHR = 'A'-40h ;^A, SOH = Tab with fill BTBCHR = 02h ;^B, STX = BACK TAB CHARACTER ABTCHR = 03h ;^C, ETX = ABORT CHARACTER BELCHR = 07h ;^G, BEL = Audible Alarm BKSCHR = 08h ;^H, BS = PHYSICAL BACKSPACE CHARACTER TABCHR = 09h ;^I, HT = Tab LF = 0Ah ;^J, LF = LinE CONTROL BLOCK FCB_NR = FCB+32 ;Next Record = LRN F_EX = 12 ;Extend field F_S2 = 14 ;CPM system usage **used with 2.0 SIZ funct** F_RC = 15 ;Record count (0 to 128) F_NR = 32 ;Next record ordinal in FCB F_RRF = 33 ;r0,r1,r2 (random field) in FCB L_FILEN = 8 ;length of File name L_FILEX = 3 ;length of filename suffix L_FCB = 36 ;length of entire FCB (comp with 2.0) DMA = 80h ;WHERE SECTORS ARE READ TBUFF = DMA ;where CCP puts command line OS#RCO = 1 ;Read character from CRT: OS#WCO = 2 ;Write character to CRT: device OS#RRO = 3 ;Read from reader OS#WPO = 4 ;Write to punch OS#WLO = 5 ;Write character to LST: device OS#OST = 9 ;Write string on CRT: OS#RCB = 10 ;Read Console Buffer OS#CABT = 11 ;Get Console status OS#VER = 12 ;Return version in HL ;H= 0, L=0 if 1.4 ;H= 0, L=xx if 2.0 or > ;H= 1, L=0 if MPM 1.0 OS#IND = 13 ;Initialize/Reset Disk system OS#DSEL = 14 ;Select disk unit OS#OPEN = 15 ;Open file OS#CLOS = 16 ;Close file OS#SDIR = 17 ;Search directory OS#NDIR ' %2' <> ' ' LXI DE,%2 .1 ENDIF CALL %1 ENDM space 4,10 ; SYSTEM This macro provides a mechanism to call the ; system directly. SYSTEM MACRO FUN,ARG .2 IF DEF OS#%1 MVI C,OS#%1 .1 IF ' %2' <> ' ' LXI DE,%2 .1 ENDIF CALL SYS .2 ELSE CALL %1 .2 ENDIF ENDM ; ALIGN Set origin to even boundary. ALIGN MACRO SIZ,O DS (*%2+(%1)-1)/(%1)*(%1)-*%2 ENDM ; DI and EI are macros so all usages can be accounted ; for in the cr. DI MACRO DB 0F3h ENDM EI MACRO DB 0FBh ENDM ; Macros to enable ROM, RAM and DIM code ENAROM MACRO DI OUT 0 LDK A,0 STO A,ROMRAM EI ENDM DISROM MACRO DI OUT 1 LDK A,1 STO A,ROMRAM EI ENDM BRTBIT: = 80h ;set brt/dim memory BRIGHT DIMBIT: = 00h ;set brt/dim memory DIM ENADIM MACRO OUT 2 ENDM DISDIM MACRO OUT 3 ENDM PUSHAL MACRO PUSH BC PUSH DE PUSH HL ENDM POPALL MACRO POP HL POP DE POP BC ENDM ; Endx SYSTEXT  port ilow equ base+1 ;low iopb address ihigh equ base+2 ;high iopb address bsw equ 0ffh ;boot switch recal equ 3h ;recalibrate selected drive readf equ 4h ;disk read function stack equ 100h ;use end of boot for stack ; rstart: lxi sp,stack;in case of call to mon80 ; clear disk status in rtype in rbyte ; check if boot switch is off coldstart: in bsw ani 02h ;switch on? jnz coldstart ; clear the controller out reset ;logic cleared ; ; mvi b,ntrks ;number of tracks to read lxi h,iopb0 ; start: ; ; read first/next track into cpmb mov a,l out ilow mov a,h out ihigh wait0: in dstat ani 4 jz wait0 ; ; check disk status in rtype ani 11b cpi 2 ; if testing cnc rmon80 ;go to monitor if 11 or 10 endif if not testing jnc rstart ;retry the load endif ; in rbyte ;i/o complete, check status ; if not ready, then go to mon80 ral cc rmon80 ;not ready bit set rar ;restore ani 11110b ;overrun/addr err/seek/crc/xxxx ; if testing cnz rmon80 ;g title 'console command processor (CCP), ver 2.0' ; assembly language version of the CP/M console command processor ; ; version 2.2 February, 1980 ; ; Copyright (c) 1976, 1977, 1978, 1979, 1980 ; Digital Research ; Box 579, Pacific Grove, ; California, 93950 ; false equ 0000h true equ not false testing equ false ;true if debugging ; ; if testing org 3400h bdosl equ $+800h ;bdos location else org 000h bdosl equ $+800h ;bdos location endif tran equ 100h tranm equ $ ccploc equ $ ; ; ******************************************************** ; * Base of CCP contains the following code/data * ; * ccp: jmp ccpstart (start with command) * ; * jmp ccpclear (start, clear command) * ; * ccp+6 127 (max command length) * ; * ccp+7 comlen (command length = 00) * ; * ccp+8 ' ... ' (16 blanks) * ; ******************************************************** ; * Normal entry is at ccp, where the command line given * ; * at ccp+8 is executed automatically (normally a nuo to monitor endif if not testing jnz rstart ;retry the load endif ; ; lxi d,iopbl ;length of iopb dad d ;addressing next iopb dcr b ;count down tracks jnz start ; ; ; jmp to boot to print initial message, and set up jmps jmp boot ; ; parameter blocks iopb0: db 80h ;iocw, no update db readf ;read function db bdos0 ;# sectors to read on track 0 db 0 ;track 0 db 2 ;start with sector 2 on track 0 dw cpmb ;start at base of bdos iopbl equ $-iopb0 ; iopb1: db 80h db readf db bdos1 ;sectors to read on track 1 db 1 ;track 1 db 1 ;sector 1 dw cpmb+bdos0*128 ;base of second read ; end z wait0 ; ; check disk status in rtype ani 11b cpi 2 ; if testing cnc rmon80 ;go to monitor if 11 or 10 endif if not testing jnc rstart ;retry the load endif ; in rbyte ;i/o complete, check status ; if not ready, then go to mon80 ral cc rmon80 ;not ready bit set rar ;restore ani 11110b ;overrun/addr err/seek/crc/xxxx ; if testing cnz rmon80 ;gll * ; * command with comlen = 00). An initializing program * ; * can be automatically loaded by storing the command * ; * at ccp+8, with the command length at ccp+7. In this * ; * case, the ccp executes the command before prompting * ; * the console for input. Note that the command is exe-* ; * cuted on both warm and cold starts. When the command* ; * line is initialized, a jump to "jmp ccpclear" dis- * ; * ables the automatic command execution. * ; ******************************************************** ; jmp ccpstart ;start ccp with possible initial command jmp ccpclear ;clear the command buffer maxlen: db 127 ;max buffer length comlen: db 0 ;command length (filled in by dos) ; (command executed initially if comlen non zero) combuf: db ' ' ;8 character fill db ' ' ;8 character fill db 'COPYRIGHT (C) 1979, DIGITAL RESEARCH '; 38 ds 128-($-combuf) ; total buffer length is 128 characters comaddr:dw combuf ;address of next to char to scan st title 'mds cold start loader at 3000h' ; ; MDS-800 Cold Start Loader for CP/M 2.0 ; ; Version 2.0 August, 1979 ; false equ 0 true equ not false testing equ false ;if true, then go to mon80 on errors ; if testing bias equ 03400h endif if not testing bias equ 0000h endif cpmb equ bias ;base of dos load bdos equ 806h+bias ;entry to dos for calls bdose equ 1880h+bias ;end of dos load boot equ 1600h+bias ;cold start entry point rboot equ boot+3 ;warm start entry point ; org 03000h ;loaded down from hardware boot at 3000h ; bdosl equ bdose-cpmb ntrks equ 2 ;number of tracks to read bdoss equ bdosl/128 ;number of sectors in dos bdos0 equ 25 ;number of bdos sectors on track 0 bdos1 equ bdoss-bdos0 ;number of sectors on track 1 ; mon80 equ 0f800h ;intel monitor base rmon80 equ 0ff0fh ;restart location for mon80 base equ 078h ;'base' used by controller rtype equ base+1 ;result type rbyte equ base+3 ;result byte reset equ base+7 ;reset controller ; dstat equ base ;disk statusaddr: ds 2 ;starting address of current fillfcb request ; diska equ 0004h ;disk address for current disk bdos equ 0005h ;primary bdos entry point buff equ 0080h ;default buffer fcb equ 005ch ;default file control block ; rcharf equ 1 ;read character function pcharf equ 2 ;print character function pbuff equ 9 ;print buffer function rbuff equ 10 ;read buffer function breakf equ 11 ;break key function liftf equ 12 ;lift head function (no operation) initf equ 13 ;initialize bdos function self equ 14 ;select disk function openf equ 15 ;open file function closef equ 16 ;close file function searf equ 17 ;search for file function searnf equ 18 ;search for next file function delf equ 19 ;delete file function dreadf equ 20 ;disk read function dwritf equ 21 ;disk write function makef equ 22 ;file make function renf equ 23 ;rename file function logf equ 24 ;return login vector cself equ 25 ;return currently selected drive number dmaf equ 26 ;set dma address userf equ 32 ;set user number ; ; sl getuser ;code to a add a! add a! add a! add a ;rot left lxi h,cdisk! ora m ;4b=user, 4b=disk sta diska ;stored away in memory for later ret ; setdiska: lda cdisk! sta diska ;user/disk ret ; translate: ;translate character in register A to upper case cpi 61h! rc ;return if below lower case a cpi 7bh! rnc ;return if above lower case z ani 5fh! ret ;translated to upper case ; readcom: ;read the next command into the command buffer ;check for submit file lda submit! ora a! jz nosub ;scanning a submit file ;change drives to open and read the file lda cdisk! ora a! mvi a,0! cnz select ;have to open again in case xsub present lxi d,subfcb! call open! jz nosub ;skip if no sub lda subrc! dcr a ;read last record(s) first sta subcr ;current record to read lxi d,subfcb! call diskread ;end of file if last record jnz nosub ;disk read is ok, transfer to combuf lxi d,comlen! lxi h,buff! mvi b,128! call move0 ;line is transferred, close the: ;print characters until blank or zero mov a,m! cpi ' '! jz comerr1; not blank ora a! jz comerr1; not zero, so print it push h! call printchar! pop h! inx h jmp comerr0; for another character comerr1: ;print question mark,and delete sub file mvi a,'?'! call printchar call crlf! call del$sub jmp ccp ;restart with next command ; ; fcb scan and fill subroutine (entry is at fillfcb below) ;fill the comfcb, indexed by A (0 or 16) ;subroutines delim: ;look for a delimiter ldax d! ora a! rz ;not the last element cpi ' '! jc comerr ;non graphic rz ;treat blank as delimiter cpi '='! rz cpi la! rz ;left arrow cpi '.'! rz cpi ':'! rz cpi ';'! rz cpi '<'! rz cpi '>'! rz ret ;delimiter not found ; deblank: ;deblank the input line ldax d! ora a! rz ;treat end of line as blank cpi ' '! rnz! inx d! jmp deblank ; addh: ;add a to h,l add l! mov l,a! rnc inr h! ret ; fillfcb0: ;equivalent to fillfcb(0) mvi a,0 ; fillfcb: lxi h,comfcbpecial fcb flags rofile equ 9 ;read only file sysfile equ 10 ;system file flag ; ; special characters cr equ 13 ;carriage return lf equ 10 ;line feed la equ 5fh ;left arrow eofile equ 1ah ;end of file ; ; utility procedures printchar: mov e,a! mvi c,pcharf! jmp bdos ; printbc: ;print character, but save b,c registers push b! call printchar! pop b! ret ; crlf: mvi a,cr! call printbc mvi a,lf! jmp printbc ; blank: mvi a,' '! jmp printbc ; print: ;print string starting at b,c until next 00 entry push b! call crlf! pop h ;now print the string prin0: mov a,m! ora a! rz ;stop on 00 inx h! push h ;ready for next call printchar! pop h ;character printed jmp prin0 ;for another character ; initialize: mvi c,initf! jmp bdos ; select: mov e,a! mvi c,self! jmp bdos ; bdos$inr: call bdos! sta dcnt! inr a! ret ; open: ;open the file given by d,e mvi c,openf! jmp bdos$inr ; openc: ;open comfcb xra a! sta comrec ;clear next record to read lxi d,comfcb! jmp open file with a ;deleted record lxi h,submod! mvi m,0 ;clear fwflag inx h! dcr m ;one less record lxi d,subfcb! call close! jz nosub ;close went ok, return to original drive lda cdisk! ora a! cnz select ;print to the 00 lxi h,combuf! call prin0 call break$key! jz noread call del$sub! jmp ccp ;break key depressed ; nosub: ;no submit file! call del$sub ;translate to upper case, store zero at end call saveuser ;user # save in case control c mvi c,rbuff! lxi d,maxlen! call bdos call setdiska ;no control c, so restore diska noread: ;enter here from submit file ;set the last character to zero for later scans lxi h,comlen! mov b,m ;length is in b readcom0: inx h! mov a,b! ora a ;end of scan? jz readcom1! mov a,m ;get character and translate call translate! mov m,a! dcr b! jmp readcom0 ; readcom1: ;end of scan, h,l address end of command mov m,a ;store a zero lxi h,combuf! shld comaddr ;ready to scan to zero ret ; break$key: ;check for ! call addh! push h! push h ;fcb rescanned at end xra a! sta sdisk ;clear selected disk (in case A:...) lhld comaddr! xchg ;command address in d,e call deblank ;to first non-blank character xchg! shld staddr ;in case of errors xchg! pop h ;d,e has command, h,l has fcb address ;look for preceding file name A: B: ... ldax d! ora a! jz setcur0 ;use current disk if empty command sbi 'A'-1! mov b,a ;disk name held in b if : follows inx d! ldax d! cpi ':'! jz setdsk ;set disk name if : ; setcur: ;set current disk dcx d ;back to first character of command setcur0: lda cdisk! mov m,a! jmp setname ; setdsk: ;set disk to name in register b mov a,b! sta sdisk ;mark as disk selected mov m,b! inx d ;past the : ; setname: ;set the file name field mvi b,8 ;file name length (max) setnam0: call delim! jz padname ;not a delimiter inx h! cpi '*'! jnz setnam1 ;must be ?'s mvi m,'?'! jmp setnam2 ;to dec count ; setnam1: mov m,a ;store character to fcb! inx d setna ; close: ;close the file given by d,e mvi c,closef! jmp bdos$inr ; search: ;search for the file given by d,e mvi c,searf! jmp bdos$inr ; searchn: ;search for the next occurrence of the file given by d,e mvi c,searnf! jmp bdos$inr ; searchcom: ;search for comfcb file lxi d,comfcb! jmp search ; delete: ;delete the file given by d,e mvi c,delf! jmp bdos ; bdos$cond: call bdos! ora a! ret ; diskread: ;read the next record from the file given by d,e mvi c,dreadf! jmp bdos$cond ; diskreadc: ;read the comfcb file lxi d,comfcb! jmp diskread ; diskwrite: ;write the next record to the file given by d,e mvi c,dwritf! jmp bdos$cond ; make: ;create the file given by d,e mvi c,makef! jmp bdos$inr ; renam: ;rename the file given by d,e mvi c,renf! jmp bdos ; getuser: ;return current user code in a mvi e,0ffh ;drop through to setuser ; setuser: mvi c,userf! jmp bdos ;sets user number ; saveuser: ;save user#/disk# before possible ^c or transient cala character ready at the console mvi c,breakf! call bdos ora a! rz mvi c,rcharf! call bdos ;character cleared ora a! ret ; cselect: ;get the currently selected drive number to reg-A mvi c,cself! jmp bdos ; setdmabuff: ;set default buffer dma address lxi d,buff ;(drop through) ; setdma: ;set dma address to d,e mvi c,dmaf! jmp bdos ; del$sub: ;delete the submit file, and set submit flag to false lxi h,submit! mov a,m! ora a! rz ;return if no sub file mvi m,0 ;submit flag is set to false xra a! call select ;on drive a to erase file lxi d,subfcb! call delete lda cdisk! jmp select ;back to original drive ; serialize: ;check serialization lxi d,serial! lxi h,bdosl! mvi b,6 ;check six bytes ser0: ldax d! cmp m! jnz badserial inx d! inx h! dcr b! jnz ser0 ret ;serial number is ok ; comerr: ;error in command string starting at position ;'staddr' and ending with first delimiter call crlf ;space to next line lhld staddr ;h,l address first to print comerr0m2: dcr b ;count down length! jnz setnam0 ; ;end of name, truncate remainder trname: call delim! jz setty ;set type field if delimiter inx d! jmp trname ; padname: inx h! mvi m,' '! dcr b! jnz padname ; setty: ;set the type field mvi b,3! cpi '.'! jnz padty ;skip the type field if no . inx d ;past the ., to the file type field setty0: ;set the field from the command buffer call delim! jz padty! inx h! cpi '*'! jnz setty1 mvi m,'?' ;since * specified! jmp setty2 ; setty1: ;not a *, so copy to type field mov m,a! inx d setty2: ;decrement count and go again dcr b! jnz setty0 ; ;end of type field, truncate trtyp: ;truncate type field call delim! jz efill! inx d! jmp trtyp ; padty: ;pad the type field with blanks inx h! mvi m,' '! dcr b! jnz padty ; efill: ;end of the filename/filetype fill, save command address ;fill the remaining fields for the fcb mvi b,3 efill0: inx h! mvi m,0! dcr b! jnz efill0 xchg! shld comaddr ;se ; ccp: ;enter here on each command or error condition lxi sp,stack call crlf ;print d> prompt, where d is disk name call cselect ;get current disk number adi 'A'! call printchar mvi a,'>'! call printchar call readcom ;command buffer filled ccp0: ;(enter here from initialization with command full) lxi d,buff! call setdma ;default dma address at buff call cselect! sta cdisk ;current disk number saved call fillfcb0 ;command fcb filled cnz comerr ;the name cannot be an ambiguous reference lda sdisk! ora a! jnz userfunc ;check for an intrinsic function call intrinsic lxi h,jmptab ;index is in the accumulator mov e,a! mvi d,0! dad d! dad d ;index in d,e mov a,m! inx h! mov h,m! mov l,a! pchl ;pc changes to the proper intrinsic or user function jmptab: dw direct ;directory search dw erase ;file erase dw type ;type file dw save ;save memory image dw rename ;file rename dw user ;user number dw userfunc;user-defined function badserial: lomfcb to all ??? for current disk mvi b,11 ;length of fill ????????.??? dir0: mvi m,'?'! inx h! dcr b! jnz dir0 ;not a blank request, must be in comfcb dir1: mvi e,0! push d ;E counts directory entries call searchcom ;first one has been found cz nofile ;not found message dir2: jz endir ;found, but may be system file lda dcnt ;get the location of the element rrc! rrc! rrc! ani 110$0000b! mov c,a ;c contains base index into buff for dir entry mvi a,sysfile! call addhcf ;value to A ral! jc dir6 ;skip if system file ;c holds index into buffer ;another fcb found, new line? pop d! mov a,e! inr e! push d ;e=0,1,2,3,...new line if mod 4 = 0 ani 11b! push psw ;and save the test jnz dirhdr0 ;header on current line call crlf push b! call cselect! pop b ;current disk in A adi 'A'! call printbc mvi a,':'! call printbc jmp dirhdr1 ;skip current line hdr dirhdr0:call blank ;after last one mvi a,':'! call printbc dirhdr1: call blank t new starting point ; ;recover the start address of the fcb and count ?'s pop h! lxi b,11 ;b=0, c=8+3 scnq: inx h! mov a,m! cpi '?'! jnz scnq0 ;? found, count it in b! inr b scnq0: dcr c! jnz scnq ; ;number of ?'s in c, move to a and return with flags set mov a,b! ora a! ret ; intvec: ;intrinsic function names (all are four characters) db 'DIR ' db 'ERA ' db 'TYPE' db 'SAVE' db 'REN ' db 'USER' intlen equ ($-intvec)/4 ;intrinsic function length serial: db 0,0,0,0,0,0 ; ; intrinsic: ;look for intrinsic functions (comfcb has been filled) lxi h,intvec! mvi c,0 ;c counts intrinsics as scanned intrin0: mov a,c! cpi intlen ;done with scan?! rnc ;no, more to scan lxi d,comfcb+1 ;beginning of name mvi b,4 ;length of match is in b intrin1: ldax d! cmp m ;match? jnz intrin2 ;skip if no match inx d! inx h! dcr b jnz intrin1 ;loop while matching ; ;complete match on name, check for blank in fcb ldax d! cpi ' '! jnz intrixi h,di or (hlt shl 8) shld ccploc! lxi h,ccploc! pchl ; ; ;utility subroutines for intrinsic handlers readerr: ;print the read error message lxi b,rdmsg! jmp print rdmsg: db 'READ ERROR',0 ; nofile: ;print no file message lxi b,nofmsg! jmp print nofmsg: db 'NO FILE',0 ; getnumber: ;read a number from the command line call fillfcb0 ;should be number lda sdisk! ora a! jnz comerr ;cannot be prefixed ;convert the byte value in comfcb to binary lxi h,comfcb+1! lxi b,11 ;(b=0, c=11) ;value accumulated in b, c counts name length to zero conv0: mov a,m! cpi ' '! jz conv1 ;more to scan, convert char to binary and add inx h! sui '0'! cpi 10! jnc comerr ;valid? mov d,a ;save value! mov a,b ;mult by 10 ani 1110$0000b! jnz comerr mov a,b ;recover value rlc! rlc! rlc ;*8 add b! jc comerr add b! jc comerr ;*8+*2 = *10 add d! jc comerr ;+digit mov b,a! dcr c! jnz conv0 ;for another digit ret conv1: ;end of digits, check f ;compute position of name in buffer mvi b,1 ;start with first character of name dir3: mov a,b! call addhcf ;buff+a+c fetched ani 7fh ;mask flags ;may delete trailing blanks cpi ' '! jnz dir4 ;check for blank type pop psw! push psw ;may be 3rd item cpi 3! jnz dirb ;place blank at end if not mvi a,9! call addhcf ;first char of type ani 7fh! cpi ' '! jz dir5 ;not a blank in the file type field dirb: mvi a,' ' ;restore trailing filename chr dir4: call printbc ;char printed inr b! mov a,b! cpi 12! jnc dir5 ;check for break between names cpi 9! jnz dir3 ;for another char ;print a blank between names call blank! jmp dir3 ; dir5: ;end of current entry pop psw ;discard the directory counter (mod 4) dir6: call break$key ;check for interrupt at keyboard jnz endir ;abort directory search call searchn! jmp dir2 ;for another entry endir: ;end of directory scan pop d ;discard directory counter jmp retcom ; ; erase: call fillfn3 ;otherwise matched mov a,c! ret ;with intrinsic number in a ; intrin2: ;mismatch, move to end of intrinsic inx h! dcr b! jnz intrin2 ; intrin3: ;try next intrinsic inr c ;to next intrinsic number jmp intrin0 ;for another round ; ccpclear: ;clear the command buffer xra a sta comlen ;drop through to start ccp ccpstart: ;enter here from boot loader lxi sp,stack! push b ;save initial disk number ;(high order 4bits=user code, low 4bits=disk#) mov a,c! rar! rar! rar! rar! ani 0fh ;user code mov e,a! call setuser ;user code selected ;initialize for this user, get $ flag call initialize ;0ffh in accum if $ file present sta submit ;submit flag set if $ file present pop b ;recall user code and disk number mov a,c! ani 0fh ;disk number in accumulator sta cdisk ;clears user code nibble call select ;proper disk is selected, now check sub files ;check for initial command lda comlen! ora a! jnz ccp0 ;assume typed already or all blanks mov a,m! cpi ' '! jnz comerr ;blanks? inx h! dcr c! jnz conv1 mov a,b ;recover value! ret ; movename: ;move 3 characters from h,l to d,e addresses mvi b,3 move0: mov a,m! stax d! inx h! inx d dcr b! jnz move0 ret ; addhcf: ;buff + a + c to h,l followed by fetch lxi h,buff! add c! call addh! mov a,m! ret ; setdisk: ;change disks for this command, if requested xra a! sta comfcb ;clear disk name from fcb lda sdisk! ora a! rz ;no action if not specified dcr a! lxi h,cdisk! cmp m! rz ;already selected jmp select ; resetdisk: ;return to original disk after command lda sdisk! ora a! rz ;no action if not selected dcr a! lxi h,cdisk! cmp m! rz ;same disk lda cdisk! jmp select ; ;individual intrinsics follow direct: ;directory search call fillfcb0 ;comfcb gets file name call setdisk ;change disk drives if requested lxi h,comfcb+1! mov a,m ;may be empty request cpi ' '! jnz dir1 ;skip fill of ??? if not blank ;set ccb0 ;cannot be all ???'s cpi 11 jnz erasefile ;erasing all of the disk lxi b,ermsg! call print! call readcom lxi h,comlen! dcr m! jnz ccp ;bad input inx h! mov a,m! cpi 'Y'! jnz ccp ;ok, erase the entire diskette inx h! shld comaddr ;otherwise error at retcom erasefile: call setdisk lxi d,comfcb! call delete inr a ;255 returned if not found cz nofile ;no file message if so jmp retcom ; ermsg: db 'ALL (Y/N)?',0 ; type: call fillfcb0! jnz comerr ;don't allow ?'s in file name call setdisk! call openc ;open the file jz typerr ;zero flag indicates not found ;file opened, read 'til eof call crlf! lxi h,bptr! mvi m,255 ;read first buffer type0: ;loop on bptr lxi h,bptr! mov a,m! cpi 128 ;end buffer jc type1! push h ;carry if 0,1,...,127 ;read another buffer full call diskreadc! pop h ;recover address of bptr jnz typeof ;hard end of file xra a! mov m,a ;bptr = 0 type1: ;read character at bptr and print inr m ;bptr = bptr + 1 ! inx h! shld comaddr ;past delimiter ;proper delimiter found call fillfcb0! jnz renerr2 ;check for drive conflict pop psw! mov b,a ;previous drive number lxi h,sdisk! mov a,m! ora a! jz ren2 ;drive name was specified. same one? cmp b! mov m,b! jnz renerr2 ren2: mov m,b ;store the name in case drives switched xra a! sta comfcb! call searchcom ;is old file there? jz renerr1 ; ;everything is ok, rename the file lxi d,comfcb! call renam jmp retcom ; renerr1:; no file on disk call nofile! jmp retcom renerr2:; ambigous reference/name conflict call resetdisk! jmp comerr renerr3:; file already exists lxi b,renmsg! call print! jmp retcom renmsg: db 'FILE EXISTS',0 ; user: ;set user number call getnumber; leaves the value in the accumulator cpi 16! jnc comerr; must be between 0 and 15 mov e,a ;save for setuser call lda comfcb+1! cpi ' '! jz comerr call setuser ;new user number set jmp endcom ; userfunc: call serialize ;check seriali loadmsg: db 'BAD LOAD',0 comtype: db 'COM' ;for com files ; ; retcom: ;reset disk before end of command check call resetdisk ; endcom: ;end of intrinsic command call fillfcb0 ;to check for garbage at end of line lda comfcb+1! sui ' '! lxi h,sdisk! ora m ;0 in accumulator if no disk selected, and blank fcb jnz comerr jmp ccp ; ; ; ; data areas ds 16 ;8 level stack stack: ; ; 'submit' file control block submit: db 0 ;00 if no submit file, ff if submitting subfcb: db 0,'$$$ ' ;file name is $$$ db 'SUB',0,0 ;file type is sub submod: db 0 ;module number subrc: ds 1 ;record count filed ds 16 ;disk map subcr: ds 1 ;current record to read ; ; command file control block comfcb: ds 32 ;fields filled in later comrec: ds 1 ;current record to read/write dcnt: ds 1 ;disk directory count (used for error codes) cdisk: ds 1 ;current disk sdisk: ds 1 ;selected disk for current operation ;none=0, a=1, b=2 ... bptr: ds 1 ;buffer pointer end ccploc  lxi h,buff! call addh ;h,l addresses char mov a,m! cpi eofile! jz retcom call printchar call break$key! jnz retcom ;abort if break jmp type0 ;for another character ; typeof: ;end of file, check for errors dcr a! jz retcom call readerr typerr: call resetdisk! jmp comerr ; save: call getnumber; value to register a push psw ;save it for later ; ;should be followed by a file to save the memory image call fillfcb0 jnz comerr ;cannot be ambiguous call setdisk ;may be a disk change lxi d,comfcb! push d! call delete ;existing file removed pop d! call make ;create a new file on disk jz saverr ;no directory space xra a! sta comrec; clear next record field pop psw ;#pages to write is in a, change to #sectors mov l,a! mvi h,0! dad h! lxi d,tran ;h,l is sector count, d,e is load address save0: ;check for sector count zero mov a,h! ora l! jz save1 ;may be completed dcx h ;sector count = sector count - 1 push h ;save it for next timzation ;load user function and set up for execution lda comfcb+1! cpi ' '! jnz user0 ;no file name, but may be disk switch lda sdisk! ora a! jz endcom ;no disk name if 0 dcr a! sta cdisk! call setdiska ;set user/disk call select! jmp endcom user0: ;file name is present lxi d,comfcb+9! ldax d! cpi ' '! jnz comerr ;type ' ' push d! call setdisk! pop d! lxi h,comtype ;.com call movename ;file type is set to .com call openc! jz userer ;file opened properly, read it into memory lxi h,tran ;transient program base load0: push h ;save dma address xchg! call setdma lxi d,comfcb! call diskread! jnz load1 ;sector loaded, set new dma address and compare pop h! lxi d,128! dad d lxi d,tranm ;has the load overflowed? mov a,l! sub e! mov a,h! sbb d! jnc loaderr jmp load0 ;for another sector ; load1: pop h! dcr a! jnz loaderr ;end file is 1 call resetdisk ;back to original disk call fillfcb0! lxi h,sdisk! push h mov a,m! sta comfcb ;drive nue around lxi h,128! dad d! push h ;next dma address saved call setdma ;current dma address set lxi d,comfcb! call diskwrite pop d! pop h ;dma address, sector count jnz saverr ;may be disk full case jmp save0 ;for another sector ; save1: ;end of dump, close the file lxi d,comfcb! call close inr a; 255 becomes 00 if error jnz retsave ;for another command saverr: ;must be full or read only disk lxi b,fullmsg! call print retsave: ;reset dma buffer call setdmabuff jmp retcom fullmsg: db 'NO SPACE',0 ; ; rename: ;rename a file on a specific disk call fillfcb0! jnz comerr ;must be unambiguous lda sdisk! push psw ;save for later compare call setdisk ;disk selected call searchcom ;is new name already there? jnz renerr3 ;file doesn't exist, move to second half of fcb lxi h,comfcb! lxi d,comfcb+16! mvi b,16! call move0 ;check for = or left arrow lhld comaddr! xchg! call deblank cpi '='! jz ren1 ;ok if = cpi la! jnz renerr2 ren1: xchgmber set mvi a,16! call fillfcb ;move entire fcb to memory pop h! mov a,m! sta comfcb+16 xra a! sta comrec ;record number set to zero lxi d,fcb! lxi h,comfcb! mvi b,33! call move0 ;move command line to buff lxi h,combuf bmove0: mov a,m! ora a! jz bmove1! cpi ' '! jz bmove1 inx h! jmp bmove0 ;for another scan ;first blank position found bmove1: mvi b,0! lxi d,buff+1! ;ready for the move bmove2: mov a,m! stax d! ora a! jz bmove3 ;more to move inr b! inx h! inx d! jmp bmove2 bmove3: ;b has character count mov a,b! sta buff call crlf ;now go to the loaded program call setdmabuff ;default dma call saveuser ;user code saved ;low memory diska contains user code call tran ;gone to the loaded program lxi sp,stack ;may come back here call setdiska! call select jmp ccp ; userer: ;arrive here on command error call resetdisk! jmp comerr ; loaderr:;cannot load the program lxi b,loadmsg! call print jmp retcom title 'Bdos Interface, Bdos, Version 2.2 Feb, 1980' ;***************************************************************** ;***************************************************************** ;** ** ;** B a s i c D i s k O p e r a t i n g S y s t e m ** ;** I n t e r f a c e M o d u l e ** ;** ** ;***************************************************************** ;***************************************************************** ; ; Copyright (c) 1978, 1979, 1980 ; Digital Research ; Box 579, Pacific Grove ; California ; ; ; 20 january 1980 ; ; on equ 0ffffh off equ 00000h test equ off ; if test org 0dc00h else org 0800h endif ; bios value defined at end of module ; ssize equ 24 ;24 level stack ; ; low memory locations reboot equ 0000h ;reboot system ioloc equ 0003h ;i/o byte location bdosa equ 0006h ;addrelse lxi h,goback ;return here after all functions push h ;jmp goback equivalent to ret mov a,c! cpi nfuncs! rnc ;skip if invalid # mov c,e ;possible output character to C lxi h,functab! mov e,a! mvi d,0 ;DE=func, HL=.ciotab dad d! dad d! mov e,m! inx h! mov d,m ;DE=functab(func) lhld info ;info in DE for later xchg xchg! pchl ;dispatched ; ; dispatch table for functions functab: dw wbootf, func1, func2, func3 dw punchf, listf, func6, func7 dw func8, func9, func10,func11 diskf equ ($-functab)/2 ;disk funcs dw func12,func13,func14,func15 dw func16,func17,func18,func19 dw func20,func21,func22,func23 dw func24,func25,func26,func27 dw func28,func29,func30,func31 dw func32,func33,func34,func35 dw func36,func37,func38,func39 ; dw func40 ; nfuncs equ ($-functab)/2 ; ; ; error subroutines persub: ;report permanent error lxi h,permsg! call errflg ;to report the error cpi ctlc! jz reboot ;reboot if response is ctlc ret ;and ignore the error ; selsub: ;report pute character position/write console char from C ;compcol = true if computing column position lda compcol! ora a! jnz compout ;write the character, then compute the column ;write console character from C push b! call conbrk ;check for screen stop function pop b! push b ;recall/save character call conoutf ;externally, to console pop b! push b ;recall/save character ;may be copying to the list device lda listcp! ora a! cnz listf ;to printer, if so pop b ;recall the character compout: mov a,c ;recall the character ;and compute column position lxi h,column ;A = char, HL = .column cpi rubout! rz ;no column change if nulls inr m ;column = column + 1 cpi ' '! rnc ;return if graphic ;not graphic, reset column position dcr m ;column = column - 1 mov a,m! ora a! rz ;return if at zero ;not at zero, may be backspace or end line mov a,c ;character back to A cpi ctlh! jnz notbacksp ;backspace character dcr m ;column = column - 1 ret notbacss field of jmp BDOS ; ; bios access constants bootf set bios+3*0 ;cold boot function wbootf set bios+3*1 ;warm boot function constf set bios+3*2 ;console status function coninf set bios+3*3 ;console input function conoutf set bios+3*4 ;console output function listf set bios+3*5 ;list output function punchf set bios+3*6 ;punch output function readerf set bios+3*7 ;reader input function homef set bios+3*8 ;disk home function seldskf set bios+3*9 ;select disk function settrkf set bios+3*10 ;set track function setsecf set bios+3*11 ;set sector function setdmaf set bios+3*12 ;set dma function readf set bios+3*13 ;read disk function writef set bios+3*14 ;write disk function liststf set bios+3*15 ;list status function sectran set bios+3*16 ;sector translate ; ; equates for non graphic characters ctlc equ 03h ;control c ctle equ 05h ;physical eol ctlh equ 08h ;backspace ctlp equ 10h ;prnt toggle ctlr equ 12h ;repeat line ctls equ 13h ;stop/start screen ctlu equ 15h ;line delete ctlx equ select error lxi h,selmsg! jmp wait$err ;wait console before boot ; rodsub: ;report write to read/only disk lxi h,rodmsg! jmp wait$err ;wait console ; rofsub: ;report read/only file lxi h,rofmsg ;drop through to wait for console ; wait$err: ;wait for response before boot call errflg! jmp reboot ; ; error messages dskmsg: db 'Bdos Err On ' dskerr: db ' : $' ;filled in by errflg permsg: db 'Bad Sector$' selmsg: db 'Select$' rofmsg: db 'File ' rodmsg: db 'R/O$' ; ; errflg: ;report error to console, message address in HL push h! call crlf ;stack mssg address, new line lda curdsk! adi 'A'! sta dskerr ;current disk name lxi b,dskmsg! call print ;the error message pop b! call print ;error mssage tail ;jmp conin ;to get the input character ;(drop through to conin) ;ret ; ; ; console handlers conin: ;read console character to A lxi h,kbchar! mov a,m! mvi m,0! ora a! rnz ;no previous keyboard character ready jmp coninf ;get character externally ;ret ; conechksp: ;not a backspace character, eol? cpi lf! rnz ;return if not ;end of line, column = 0 mvi m,0 ;column = 0 ret ; ctlout: ;send C character with possible preceding up-arrow mov a,c! call echoc ;cy if not graphic (or special case) jnc tabout ;skip if graphic, tab, cr, lf, or ctlh ;send preceding up arrow push psw! mvi c,ctl! call conout ;up arrow pop psw! ori 40h ;becomes graphic letter mov c,a ;ready to print ;(drop through to tabout) ; tabout: ;expand tabs to console mov a,c! cpi tab! jnz conout ;direct to conout if not ;tab encountered, move to next tab position tab0: mvi c,' '! call conout ;another blank lda column! ani 111b ;column mod 8 = 0 ? jnz tab0 ;back for another if not ret ; ; backup: ;back-up one screen position call pctlh! mvi c,' '! call conoutf ; (drop through to pctlh) ; pctlh: ;send ctlh to console without affecting column count mvi c,ctlh! jmp conoutf ;ret ; crlfp: ;print #, cr, lf for ctlx, ctlu, ctlr f18h ;=ctl-u ctlz equ 1ah ;end of file rubout equ 7fh ;char delete tab equ 09h ;tab char cr equ 0dh ;carriage return lf equ 0ah ;line feed ctl equ 5eh ;up arrow ; db 0,0,0,0,0,0 ; ; enter here from the user's program with function number in c, ; and information address in d,e jmp bdose ;past parameter block ; ; ************************************************ ; *** relative locations 0009 - 000e *** ; ************************************************ pererr: dw persub ;permanent error subroutine selerr: dw selsub ;select error subroutine roderr: dw rodsub ;ro disk error subroutine roferr: dw rofsub ;ro file error subroutine ; ; bdose: ;arrive here from user programs xchg! shld info! xchg ;info=DE, DE=info mov a,e! sta linfo ;linfo = low(info) - don't equ lxi h,0! shld aret ;return value defaults to 0000 ;save user's stack pointer, set to local stack dad sp! shld entsp ;entsp = stackptr lxi sp,lstack ;local stack setup xra a! sta fcbdsk! sta resel ;fcbdsk,resel=fa: ;read character with echo call conin! call echoc! rc ;echo character? ;character must be echoed before return push psw! mov c,a! call tabout! pop psw ret ;with character in A ; echoc: ;echo character if graphic ;cr, lf, tab, or backspace cpi cr! rz ;carriage return? cpi lf! rz ;line feed? cpi tab! rz ;tab? cpi ctlh! rz ;backspace? cpi ' '! ret ;carry set if not graphic ; conbrk: ;check for character ready lda kbchar! ora a! jnz conb1 ;skip if active kbchar ;no active kbchar, check external break call constf! ani 1! rz ;return if no char ready ;character ready, read it call coninf ;to A cpi ctls! jnz conb0 ;check stop screen function ;found ctls, read next character call coninf ;to A cpi ctlc! jz reboot ;ctlc implies re-boot ;not a reboot, act as if nothing has happened xra a! ret ;with zero in accumulator conb0: ;character in accum, save it sta kbchar conb1: ;return with true set in accumulator mvi a,1! ret ; conout: ;comunctions ;then move to strtcol (starting column) mvi c,'#'! call conout call crlf ;column = 0, move to position strtcol crlfp0: lda column! lxi h,strtcol cmp m! rnc ;stop when column reaches strtcol mvi c,' '! call conout ;print blank jmp crlfp0 ;; ; crlf: ;carriage return line feed sequence mvi c,cr! call conout! mvi c,lf! jmp conout ;ret ; print: ;print message until M(BC) = '$' ldax b! cpi '$'! rz ;stop on $ ;more to print inx b! push b! mov c,a ;char to C call tabout ;another character printed pop b! jmp print ; read: ;read to info address (max length, current length, buffer) lda column! sta strtcol ;save start for ctl-x, ctl-h lhld info mov c,m! inx h! push h! mvi b,0 ;B = current buffer length, ;C = maximum buffer length, ;HL= next to fill - 1 readnx: ;read next character, BC, HL active push b! push h ;blen, cmax, HL saved readn0: call conin ;next char in A ani 7fh ;mask parity bit pop h! pop b ;reactivate counters  rep0: mov a,b! ora a! jz rep1 ;count len to 00 inx h! mov c,m ;next to print dcr b! push b! push h ;count length down call ctlout ;character echoed pop h! pop b ;recall remaining count jmp rep0 ;for the next character rep1: ;end of repeat, recall lengths ;original BC still remains pushed push h ;save next to fill lda compcol! ora a ;>0 if computing length jz readn0 ;for another char if so ;column position computed for ctlh lxi h,column! sub m ;diff > 0 sta compcol ;count down below ;move back compcol-column spaces backsp: ;move back one more space call backup ;one space lxi h,compcol! dcr m jnz backsp jmp readn0 ;for next character notr: ;not a ctlr, place into buffer rdecho: inx h! mov m,a ;character filled to mem inr b ;blen = blen + 1 rdech1: ;look for a random control character push b! push h ;active values saved mov c,a ;ready to print call ctlout ;may be up-arrow C pop h! pop  I/O System ; ;***************************************************************** ;***************************************************************** ; ; common values shared between bdosi and bdos usrcode:db 0 ;current user number curdsk: db 0 ;current disk number info: ds 2 ;information address aret: ds 2 ;address value to return lret equ aret ;low(aret) ; ;***************************************************************** ;***************************************************************** ;** ** ;** B a s i c D i s k O p e r a t i n g S y s t e m ** ;** ** ;***************************************************************** ;***************************************************************** ; dvers equ 22h ;version 2.2 ; module addresses ; ; literal constants true equ 0ffh ;constant true false equ 000h ;constant false enddir equ 0ffffh ;end of directory byt cpi cr! jz readen ;end of line? cpi lf! jz readen ;also end of line cpi ctlh! jnz noth ;backspace? ;do we have any characters to back over? mov a,b! ora a! jz readnx ;characters remain in buffer, backup one dcr b ;remove one character lda column! sta compcol ;col > 0 ;compcol > 0 marks repeat as length compute jmp linelen ;uses same code as repeat noth: ;not a backspace cpi rubout! jnz notrub ;rubout char? ;rubout encountered, rubout if possible mov a,b! ora a! jz readnx ;skip if len=0 ;buffer has characters, resend last char mov a,m! dcr b! dcx h ;A = last char ;blen=blen-1, next to fill - 1 decremented jmp rdech1 ;act like this is an echo ; notrub: ;not a rubout character, check end line cpi ctle! jnz note ;physical end line? ;yes, save active counters and force eol push b! push h! call crlf xra a! sta strtcol ;start position = 00 jmp readn0 ;for another character note: ;not end of line, list toggle? b! mov a,m ;recall char cpi ctlc ;set flags for reboot test mov a,b ;move length to A jnz notc ;skip if not a control c cpi 1 ;control C, must be length 1 jz reboot ;reboot if blen = 1 ;length not one, so skip reboot notc: ;not reboot, are we at end of buffer? cmp c! jc readnx ;go for another if not readen: ;end of read operation, store blen pop h! mov m,b ;M(current len) = B mvi c,cr! jmp conout ;return carriage ;ret func1: ;return console character with echo call conech jmp sta$ret ; func2: equ tabout ;write console character with tab expansion ; func3: ;return reader character call readerf jmp sta$ret ; ;func4: equated to punchf ;write punch character ; ;func5: equated to listf ;write list character ;write to list device ; func6: ;direct console i/o - read if 0ffh mov a,c! inr a! jz dirinp ;0ffh => 00h, means input mode inr a! jz constf ;0feH in C for status ;direct output function jmp conoutf ; dirinp: e equ 1 ;number of bytes for "byte" type word equ 2 ;number of bytes for "word" type ; ; fixed addresses in low memory tfcb equ 005ch ;default fcb location tbuff equ 0080h ;default buffer location ; ; fixed addresses referenced in bios module are ; pererr (0009), selerr (000c), roderr (000f) ; ; error message handlers ; ;per$error: ;report permanent error to user ; lxi h,pererr jmp goerr ; ;rod$error: ; ;report read/only disk error ; lxi h,roderr jmp goerr ; ; ;rof$error: ; ;report read/only file error ; ; lxi h,roferr ;jmp goerr ; sel$error: ;report select error lxi h,selerr ; ; ; goerr: ;HL = .errorhandler, call subroutine mov e,m! inx h! mov d,m ;address of routine in DE xchg! pchl ;to subroutine ; ; ; ; local subroutines for bios interface ; move: ;move data length of length C from source DE to ;destination given by HL inr c ;in case it is zero move0: dcr c! rz ;more to move ldax d! mov m,a ;one byte moved inx d! inx cpi ctlp! jnz notp ;skip if not ctlp ;list toggle - change parity push h ;save next to fill - 1 lxi h,listcp ;HL=.listcp flag mvi a,1! sub m ;True-listcp mov m,a ;listcp = not listcp pop h! jmp readnx ;for another char notp: ;not a ctlp, line delete? cpi ctlx! jnz notx pop h ;discard start position ;loop while column > strtcol backx: lda strtcol! lxi h,column cmp m! jnc read ;start again dcr m ;column = column - 1 call backup ;one position jmp backx notx: ;not a control x, control u? ;not control-X, control-U? cpi ctlu! jnz notu ;skip if not ;delete line (ctlu) call crlfp ;physical eol pop h ;discard starting position jmp read ;to start all over notu: ;not line delete, repeat line? cpi ctlr! jnz notr linelen: ;repeat line, or compute line len (ctlh) ;if compcol > 0 push b! call crlfp ;save line length pop b! pop h! push h! push b ;bcur, cmax active, beginning buff at HL  call constf ;status check ora a! jz retmon ;skip, return 00 if not ready ;character is ready, get it call coninf ;to A jmp sta$ret ; func7: ;return io byte lda ioloc jmp sta$ret ; func8: ;set i/o byte lxi h,ioloc mov m,c ret ;jmp goback ; func9: ;write line until $ encountered xchg ;was lhld info mov c,l! mov b,h ;BC=string address jmp print ;out to console ; func10: equ read ;read a buffered console line ; func11: ;check console status call conbrk ;(drop through to sta$ret) sta$ret: ;store the A register to aret sta aret func$ret: ; ret ;jmp goback (pop stack for non cp/m functions) ; setlret1: ;set lret = 1 mvi a,1! jmp sta$ret ; ; ; ; ; data areas ; compcol:db 0 ;true if computing column position strtcol:db 0 ;starting column position after read column: db 0 ;column position listcp: db 0 ;listing toggle kbchar: db 0 ;initial key char = 00 entsp: ds 2 ;entry stack pointer ds ssize*2 ;stack size lstack: ; end of Basic h ;to next byte jmp move0 ; selectdisk: ;select the disk drive given by curdsk, and fill ;the base addresses curtrka - alloca, then fill ;the values of the disk parameter block lda curdsk! mov c,a ;current disk# to c ;lsb of e = 0 if not yet logged - in call seldskf ;HL filled by call ;HL = 0000 if error, otherwise disk headers mov a,h! ora l! rz ;return with 0000 in HL and z flag ;disk header block address in hl mov e,m! inx h! mov d,m! inx h ;DE=.tran shld cdrmaxa! inx h! inx h ;.cdrmax shld curtrka! inx h! inx h ;HL=.currec shld curreca! inx h! inx h ;HL=.buffa ;DE still contains .tran xchg! shld tranv ;.tran vector lxi h,buffa ;DE= source for move, HL=dest mvi c,addlist! call move ;addlist filled ;now fill the disk parameter block lhld dpbaddr! xchg ;DE is source lxi h,sectpt ;HL is destination mvi c,dpblist! call move ;data filled ;now set single/double map mode lhld maxall ;largest allocation number mov a,h ;00 indicates < 255 lxi ther try seek1: ;look while arecord >= (t:=currec + sectpt) push ctrkh lhld sectpt! dad crech ;HL = currec+sectpt jc seek2 ;can be > FFFFH mov a,arecl! sub tcrecl! mov a,arech! sbb tcrech jc seek2 ;skip if t > arecord ;currec = t xchg ;curtrk = curtrk + 1 pop ctrkh! inx ctrkh jmp seek1 ;for another try seek2: pop ctrkh ;arrive here with updated values in each register push arech! push crech! push ctrkh ;to stack for later ;stack contains (lowest) BC=arecord, DE=currec, HL=curtrk xchg! lhld offset! dad d ;HL = curtrk+offset mov b,h! mov c,l! call settrkf ;track set up ;note that BC - curtrk is difference to move in bios pop d ;recall curtrk lhld curtrka! mov m,e! inx h! mov m,d ;curtrk updated ;now compute sector as arecord-currec pop crech ;recall currec lhld curreca! mov m,crecl! inx h! mov m,crech pop arech ;BC=arecord, DE=currec mov a,arecl! sub crecl! mov arecl,a mov a,arech! sbb crech! mov arech,a lhld tranv! xchg ;BC=sector#, DE=. d,dskmap! dad d ;HL =.diskmap dad b ;index by a single byte value lda single ;single byte/map entry? ora a! jz getdmd ;get disk map single byte mov l,m! mvi h,0! ret ;with HL=00bb getdmd: dad b ;HL=.fcb(dm+i*2) ;double precision value returned mov e,m! inx h! mov d,m! xchg! ret ; index: ;compute disk block number from current fcb call dm$position ;0...15 in register A mov c,a! mvi b,0! call getdm ;value to HL shld arecord! ret ; allocated: ;called following index to see if block allocated lhld arecord! mov a,l! ora h! ret ; atran: ;compute actual record address, assuming index called lda blkshf ;shift count to reg A lhld arecord atran0: dad h! dcr a! jnz atran0 ;shl(arecord,blkshf) shld arecord1 ;save shifted block # lda blkmsk! mov c,a ;mask value to C lda vrecord! ana c ;masked value in A ora l! mov l,a ;to HL shld arecord ;arecord=HL or (vrecord and blkmsk) ret ; getexta: ;get current extent field address to A lhld info! lxi d,extnum! h,single! mvi m,true ;assume a=00 ora a! jz retselect ;high order of maxall not zero, use double dm mvi m,false retselect: mvi a,true! ora a! ret ;select disk function ok ; home: ;move to home position, then offset to start of dir call homef ;move to track 00, sector 00 reference ;lxi h,offset ;mov c,m ;inx h ;mov b,m ;call settrkf ; ;first directory position selected xra a ;constant zero to accumulator lhld curtrka! mov m,a! inx h! mov m,a ;curtrk=0000 lhld curreca! mov m,a! inx h! mov m,a ;currec=0000 ;curtrk, currec both set to 0000 ret ; rdbuff: ;read buffer and check condition call readf ;current drive, track, sector, dma jmp diocomp ;check for i/o errors ; wrbuff: ;write buffer and check condition ;write type (wrtype) is in register C ;wrtype = 0 => normal write operation ;wrtype = 1 => directory write operation ;wrtype = 2 => start of new block call writef ;current drive, track, sector, dma diocomp: ;check for disk errors ora a! rz ; lxi h,ptran call sectran ;HL = tran(sector) mov c,l! mov b,h ;BC = tran(sector) jmp setsecf ;sector selected ;ret ; ; file control block (fcb) constants empty equ 0e5h ;empty directory entry lstrec equ 127 ;last record# in extent recsiz equ 128 ;record size fcblen equ 32 ;file control block size dirrec equ recsiz/fcblen ;directory elts / record dskshf equ 2 ;log2(dirrec) dskmsk equ dirrec-1 fcbshf equ 5 ;log2(fcblen) ; extnum equ 12 ;extent number field maxext equ 31 ;largest extent number ubytes equ 13 ;unfilled bytes field modnum equ 14 ;data module number maxmod equ 15 ;largest module number fwfmsk equ 80h ;file write flag is high order modnum namlen equ 15 ;name length reccnt equ 15 ;record count field dskmap equ 16 ;disk map field lstfcb equ fcblen-1 nxtrec equ fcblen ranrec equ nxtrec+1;random record field (2 bytes) ; ; reserved file indicators rofile equ 9 ;high order of first type char invis equ 10 ;invisible file in dir command ; equ 11 ;reserved ; ; utility functions fdad d ;HL=.fcb(extnum) ret ; getfcba: ;compute reccnt and nxtrec addresses for get/setfcb lhld info! lxi d,reccnt! dad d! xchg ;DE=.fcb(reccnt) lxi h,(nxtrec-reccnt)! dad d ;HL=.fcb(nxtrec) ret ; getfcb: ;set variables from currently addressed fcb call getfcba ;addresses in DE, HL mov a,m! sta vrecord ;vrecord=fcb(nxtrec) xchg! mov a,m! sta rcount ;rcount=fcb(reccnt) call getexta ;HL=.fcb(extnum) lda extmsk ;extent mask to a ana m ;fcb(extnum) and extmsk sta extval ret ; setfcb: ;place values back into current fcb call getfcba ;addresses to DE, HL lda seqio cpi 02! jnz setfcb1! xra a ;check ranfill setfcb1: mov c,a ;=1 if sequential i/o lda vrecord! add c! mov m,a ;fcb(nxtrec)=vrecord+seqio xchg! lda rcount! mov m,a ;fcb(reccnt)=rcount ret ; hlrotr: ;hl rotate right by amount C inr c ;in case zero hlrotr0: dcr c! rz ;return when zero mov a,h! ora a! rar! mov h,a ;high byte mov a,l! rar! mov l,a ;low byte jmp hlrotr0 ; ; compute$cs: ;coererr ; jmp goerr ; seekdir: ;seek the record containing the current dir entry lhld dcnt ;directory counter to HL mvi c,dskshf! call hlrotr ;value to HL shld arecord! shld drec ;ready for seek ; jmp seek ; ;ret ; ; seek: ;seek the track given by arecord (actual record) ;local equates for registers arech equ b! arecl equ c ;arecord = BC crech equ d! crecl equ e ;currec = DE ctrkh equ h! ctrkl equ l ;curtrk = HL tcrech equ h! tcrecl equ l ;tcurrec = HL ;load the registers from memory lxi h,arecord! mov arecl,m! inx h! mov arech,m lhld curreca ! mov crecl,m! inx h! mov crech,m lhld curtrka ! mov a,m! inx h! mov ctrkh,m! mov ctrkl,a ;loop while arecord < currec seek0: mov a,arecl! sub crecl! mov a,arech! sbb crech jnc seek1 ;skip if arecord >= currec ;currec = currec - sectpt push ctrkh! lhld sectpt mov a,crecl! sub l! mov crecl,a mov a,crech! sbb h! mov crech,a pop ctrkh ;curtrk = curtrk - 1 dcx ctrkh jmp seek0 ;for anoor file access ; dm$position: ;compute disk map position for vrecord to HL lxi h,blkshf! mov c,m ;shift count to C lda vrecord ;current virtual record to A dmpos0: ora a! rar! dcr c! jnz dmpos0 ;A = shr(vrecord,blkshf) = vrecord/2**(sect/block) mov b,a ;save it for later addition mvi a,8! sub m ;8-blkshf to accumulator mov c,a ;extent shift count in register c lda extval ;extent value ani extmsk dmpos1: ;blkshf = 3,4,5,6,7, C=5,4,3,2,1 ;shift is 4,3,2,1,0 dcr c! jz dmpos2 ora a! ral! jmp dmpos1 dmpos2: ;arrive here with A = shl(ext and extmsk,7-blkshf) add b ;add the previous shr(vrecord,blkshf) value ;A is one of the following values, depending upon alloc ;bks blkshf ;1k 3 v/8 + extval * 16 ;2k 4 v/16+ extval * 8 ;4k 5 v/32+ extval * 4 ;8k 6 v/64+ extval * 2 ;16k 7 v/128+extval * 1 ret ;with dm$position in A ; getdm: ;return disk map value from position given by BC lhld info ;base address of file control block lximpute checksum for current directory buffer mvi c,recsiz ;size of directory buffer lhld buffa ;current directory buffer xra a ;clear checksum value computecs0: add m! inx h! dcr c ;cs=cs+buff(recsiz-C) jnz computecs0 ret ;with checksum in A ; hlrotl: ;rotate the mask in HL by amount in C inr c ;may be zero hlrotl0: dcr c! rz ;return if zero dad h! jmp hlrotl0 ; set$cdisk: ;set a "1" value in curdsk position of BC push b ;save input parameter lda curdsk! mov c,a ;ready parameter for shift lxi h,1 ;number to shift call hlrotl ;HL = mask to integrate pop b ;original mask mov a,c! ora l! mov l,a mov a,b! ora h! mov h,a ;HL = mask or rol(1,curdsk) ret ; nowrite: ;return true if dir checksum difference occurred lhld rodsk! lda curdsk! mov c,a! call hlrotr mov a,l! ani 1b! ret ;non zero if nowrite ; set$ro: ;set current disk to read only lxi h,rodsk! mov c,m! inx h! mov b,m call set$cdisk ;sets bit to 1 shld rodsk ;high water mark in directory goes tpdate the ;directory element if C=true, or check for = if not ;drec < chksiz? lhld drec! xchg! lhld chksiz! call subdh ;DE-HL rnc ;skip checksum if past checksum vector size ;drec < chksiz, so continue push b ;save init flag call compute$cs ;check sum value to A lhld checka ;address of check sum vector xchg lhld drec ;value of drec dad d ;HL = .check(drec) pop b ;recall true=0ffh or false=00 to C inr c ;0ffh produces zero flag jz initial$cs ;not initializing, compare cmp m ;compute$cs=check(drec)? rz ;no message if ok ;checksum error, are we beyond ;the end of the disk? call compcdr rnc ;no message if so call set$ro ;read/only disk set ret initial$cs: ;initializing the checksum mov m,a! ret ; ; wrdir: ;write the current directory entry, set checksum call newchecksum ;initialize entry call setdir ;directory dma mvi c,1 ;indicates a write directory operation call wrbuff ;write the buffer jmp setdata ;to dacbit: ;given allocation vector position BC, return with byte ;containing BC shifted so that the least significant ;bit is in the low order accumulator position. HL is ;the address of the byte for possible replacement in ;memory upon return, and D contains the number of shifts ;required to place the returned value back into position mov a,c! ani 111b! inr a! mov e,a! mov d,a ;d and e both contain the number of bit positions to shift mov a,c! rrc! rrc! rrc! ani 11111b! mov c,a ;C shr 3 to C mov a,b! add a! add a! add a! add a! add a ;B shl 5 ora c! mov c,a ;bbbccccc to C mov a,b! rrc! rrc! rrc! ani 11111b! mov b,a ;BC shr 3 to BC lhld alloca ;base address of allocation vector dad b! mov a,m ;byte to A, hl = .alloc(BC shr 3) ;now move the bit to the low order position of A rotl: rlc! dcr e! jnz rotl! ret ; ; setallocbit: ;BC is the bit position of ALLOC to set or reset. The ;value of the bit is in register E. push d! call getallocbit ;shifted val A, count in D ani 111o max lhld dirmax! inx h! xchg ;DE = directory max lhld cdrmaxa ;HL = .cdrmax mov m,e! inx h! mov m,d ;cdrmax = dirmax ret ; check$rodir: ;check current directory element for read/only status call getdptra ;address of element ; check$rofile: ;check current buff(dptr) or fcb(0) for r/o status lxi d,rofile! dad d ;offset to ro bit mov a,m! ral! rnc ;return if not set lxi h,roferr! jmp goerr ; ; jmp rof$error ;exit to read only disk message ; ; check$write: ;check for write protected disk call nowrite! rz ;ok to write if not rodsk lxi h,roderr! jmp goerr ; jmp rod$error ;read only disk error ; getdptra: ;compute the address of a directory element at ;positon dptr in the buffer lhld buffa! lda dptr ; addh: ;HL = HL + A add l! mov l,a! rnc ;overflow to H inr h! ret ; ; getmodnum: ;compute the address of the module number ;bring module number to accumulator ;(high order bit is fwf (file write flag) lhld info! lxi d,modnum! dad d ;HL=.fcb(modta dma address ;ret ; rd$dir: ;read a directory entry into the directory buffer call setdir ;directory dma call rdbuff ;directory record loaded ; jmp setdata to data dma address ;ret ; setdata: ;set data dma address lxi h,dmaad! jmp setdma ;to complete the call ; setdir: ;set directory dma address lxi h,buffa ;jmp setdma to complete call ; setdma: ;HL=.dma address to set (i.e., buffa or dmaad) mov c,m! inx h! mov b,m ;parameter ready jmp setdmaf ; ; dir$to$user: ;copy the directory entry to the user buffer ;after call to search or searchn by user code lhld buffa! xchg ;source is directory buffer lhld dmaad ;destination is user dma address mvi c,recsiz ;copy entire record jmp move ;ret ; end$of$dir: ;return zero flag if at end of directory, non zero ;if not at end (end of dir if dcnt = 0ffffh) lxi h,dcnt! mov a,m ;may be 0ffh inx h! cmp m ;low(dcnt) = high(dcnt)? rnz ;non zero returned if different ;high and low the same, = 0ffh?1$1110b ;mask low bit to zero (may be set) pop b! ora c ;low bit of C is masked into A ; jmp rotr ;to rotate back into proper position ;ret rotr: ;byte value from ALLOC is in register A, with shift count ;in register C (to place bit back into position), and ;target ALLOC position in registers HL, rotate and replace rrc! dcr d! jnz rotr ;back into position mov m,a ;back to ALLOC ret ; scandm: ;scan the disk map addressed by dptr for non-zero ;entries, the allocation vector entry corresponding ;to a non-zero entry is set to the value of C (0,1) call getdptra ;HL = buffa + dptr ;HL addresses the beginning of the directory entry lxi d,dskmap! dad d ;hl now addresses the disk map push b ;save the 0/1 bit to set mvi c,fcblen-dskmap+1 ;size of single byte disk map + 1 scandm0: ;loop once for each disk map entry pop d ;recall bit parity dcr c! rz ;all done scanning? ;no, get next entry for scan push d ;replace bit parity lda single! ora a! jz scandm1 ;singlnum) mov a,m! ret ;A=fcb(modnum) ; clrmodnum: ;clear the module number field for user open/make call getmodnum! mvi m,0 ;fcb(modnum)=0 ret ; setfwf: call getmodnum ;HL=.fcb(modnum), A=fcb(modnum) ;set fwf (file write flag) to "1" ori fwfmsk! mov m,a ;fcb(modnum)=fcb(modnum) or 80h ;also returns non zero in accumulator ret ; ; compcdr: ;return cy if cdrmax > dcnt lhld dcnt! xchg ;DE = directory counter lhld cdrmaxa ;HL=.cdrmax mov a,e! sub m ;low(dcnt) - low(cdrmax) inx h ;HL = .cdrmax+1 mov a,d! sbb m ;hig(dcnt) - hig(cdrmax) ;condition dcnt - cdrmax produces cy if cdrmax>dcnt ret ; setcdr: ;if not (cdrmax > dcnt) then cdrmax = dcnt+1 call compcdr rc ;return if cdrmax > dcnt ;otherwise, HL = .cdrmax+1, DE = dcnt inx d! mov m,d! dcx h! mov m,e ret ; subdh: ;compute HL = DE - HL mov a,e! sub l! mov l,a! mov a,d! sbb h! mov h,a ret ; newchecksum: mvi c,true ;drop through to compute new checksum checksum: ;compute current checksum record and u inr a ;0ffh becomes 00 if so ret ; set$end$dir: ;set dcnt to the end of the directory lxi h,enddir! shld dcnt! ret ; read$dir: ;read next directory entry, with C=true if initializing lhld dirmax! xchg ;in preparation for subtract lhld dcnt! inx h! shld dcnt ;dcnt=dcnt+1 ;continue while dirmax >= dcnt (dirmax-dcnt no cy) call subdh ;DE-HL jnc read$dir0 ;yes, set dcnt to end of directory jmp set$end$dir ; ; ret ; read$dir0: ;not at end of directory, seek next element ;initialization flag is in C lda dcnt! ani dskmsk ;low(dcnt) and dskmsk mvi b,fcbshf ;to multiply by fcb size read$dir1: add a! dcr b! jnz read$dir1 ;A = (low(dcnt) and dskmsk) shl fcbshf sta dptr ;ready for next dir operation ora a! rnz ;return if not a new record push b ;save initialization flag C call seek$dir ;seek proper record call rd$dir ;read the directory record pop b ;recall initialization flag jmp checksum ;checksum the directory elt ;ret ; ; getalloe byte scan operation push b ;save counter push h ;save map address mov c,m! mvi b,0 ;BC=block# jmp scandm2 scandm1: ;double byte scan operation dcr c ;count for double byte push b ;save counter mov c,m! inx h! mov b,m ;BC=block# push h ;save map address scandm2: ;arrive here with BC=block#, E=0/1 mov a,c! ora b ;skip if = 0000 jz scanm3 lhld maxall ;check invalid index mov a,l! sub c! mov a,h! sbb b ;maxall - block# cnc set$alloc$bit ; ;bit set to 0/1 scanm3: ; pop h! inx h ;to next bit position pop b ;recall counter jmp scandm0 ;for another item ; initialize: ;initialize the current disk ;lret = false ;set to true if $ file exists ;compute the length of the allocation vector - 2 lhld maxall! mvi c,3 ;perform maxall/8 ;number of bytes in alloc vector is (maxall/8)+1 call hlrotr! inx h ;HL = maxall/8+1 mov b,h! mov c,l ;count down BC til zero lhld alloca ;base of allocation vector ;fill the allocatg ;a previous call on search which sets searcha and ;searchl mvi c,false! call read$dir ;read next dir element call end$of$dir! jz search$fin ;skip to end if so ;not end of directory, scan for match lhld searcha! xchg ;DE=beginning of user fcb ldax d ;first character cpi empty ;keep scanning if empty jz searchnext ;not empty, may be end of logical directory push d ;save search address call compcdr ;past logical end? pop d ;recall address jnc search$fin ;artificial stop searchnext: call getdptra ;HL = buffa+dptr lda searchl! mov c,a ;length of search to c mvi b,0 ;b counts up, c counts down searchloop: mov a,c! ora a! jz endsearch ldax d! cpi '?'! jz searchok ;? matches all ;scan next character if not ubytes mov a,b! cpi ubytes! jz searchok ;not the ubytes field, extent field? cpi extnum ;may be extent field ldax d ;fcb character jz searchext ;skip to search extent sub m! ani 7fh ;mask-out flags/extent modulus jnz searcht=maxall? jnc retblock0 ;return block 0000 if so inx d! push b! push d ;left, right pushed mov b,d! mov c,e ;ready right for call call getallocbit rar! jnc retblock ;return block number if zero pop d! pop b ;restore left and right pointers jmp lefttst ;for another attempt retblock: ral! inr a ;bit back into position and set to 1 ;d contains the number of shifts required to reposition call rotr ;move bit back to position and store pop h! pop d ;HL returned value, DE discarded ret retblock0: ;cannot find an available bit, return 0000 mov a,c ; ora b! jnz lefttst ;also at beginning lxi h,0000h! ret ; copy$fcb: ;copy the entire file control block mvi c,0! mvi e,fcblen ;start at 0, to fcblen-1 ; jmp copy$dir ; ; copy$dir: ;copy fcb information starting at C for E bytes ;into the currently addressed directory entry push d ;save length for later mvi b,0 ;double index to BC lhld info ;HL = source for data dad b! xchg ;DE=.fcb(C), sourion vector with zeros initial0: mvi m,0! inx h ;alloc(i)=0 dcx b ;count length down mov a,b! ora c! jnz initial0 ;set the reserved space for the directory lhld dirblk! xchg lhld alloca ;HL=.alloc() mov m,e! inx h! mov m,d ;sets reserved directory blks ;allocation vector initialized, home disk call home ;cdrmax = 3 (scans at least one directory record) lhld cdrmaxa! mvi m,3! inx h! mvi m,0 ;cdrmax = 0000 call set$end$dir ;dcnt = enddir ;read directory entries and check for allocated storage initial2: mvi c,true! call read$dir call end$of$dir! rz ;return if end of directory ;not end of directory, valid entry? call getdptra ;HL = buffa + dptr mvi a,empty! cmp m jz initial2 ;go get another item ;not empty, user code the same? lda usrcode cmp m! jnz pdollar ;same user code, check for '$' submit inx h! mov a,m ;first character sui '$' ;dollar file? jnz pdollar ;dollar file found, mark in lret dcr a! sta lret ;lret = 255 pdollarhn ;skip if not matched jmp searchok ;matched character searchext: ;A has fcb character ;attempt an extent # match push b ;save counters mov c,m ;directory character to c call compext ;compare user/dir char pop b ;recall counters jnz searchn ;skip if no match searchok: ;current character matches inx d! inx h! inr b! dcr c jmp searchloop endsearch: ;entire name matches, return dir position lda dcnt! ani dskmsk! sta lret ;lret = low(dcnt) and 11b lxi h,dirloc! mov a,m! ral! rnc ;dirloc=0ffh? ;yes, change it to 0 to mark as found xra a! mov m,a ;dirloc=0 ret search$fin: ;end of directory, or empty name call set$end$dir ;may be artifical end mvi a,255! jmp sta$ret ; ; ; delete: ;delete the currently addressed file call check$write ;write protected? mvi c,extnum! call search ;search through file type delete0: ;loop while directory matches call end$of$dir! rz ;stop if end ;set each non zero disk map ce for copy call getdptra ;HL=.buff(dptr), destination pop b ;DE=source, HL=dest, C=length call move ;data moved seek$copy: ;enter from close to seek and copy current element call seek$dir ;to the directory element jmp wrdir ;write the directory element ;ret ; ; rename: ;rename the file described by the first half of ;the currently addressed file control block. the ;new name is contained in the last half of the ;currently addressed file conrol block. the file ;name and type are changed, but the reel number ;is ignored. the user number is identical call check$write ;may be write protected ;search up to the extent field mvi c,extnum! call search ;copy position 0 lhld info! mov a,m ;HL=.fcb(0), A=fcb(0) lxi d,dskmap! dad d;HL=.fcb(dskmap) mov m,a ;fcb(dskmap)=fcb(0) ;assume the same disk drive for new named file rename0: call end$of$dir! rz ;stop at end of dir ;not end of directory, rename next element call check$rodir ;may be read-only file mvi c,dsk: ;now scan the disk map for allocated blocks mvi c,1 ;set to allocated call scandm call setcdr ;set cdrmax to dcnt jmp initial2 ;for another entry ; copy$dirloc: ;copy directory location to lret following ;delete, rename, ... ops lda dirloc! jmp sta$ret ; ; ret ; ; compext: ;compare extent# in A with that in C, return nonzero ;if they do not match push b ;save C's original value push psw! lda extmsk! cma! mov b,a ;B has negated form of extent mask mov a,c! ana b! mov c,a ;low bits removed from C pop psw! ana b ;low bits removed from A sub c! ani maxext ;set flags pop b ;restore original values ret ; search: ;search for directory element of length C at info mvi a,0ffh! sta dirloc ;changed if actually found lxi h,searchl! mov m,c ;searchl = C lhld info! shld searcha ;searcha = info call set$end$dir ;dcnt = enddir call home ;to start at the beginning ;(drop through to searchn) ; ; searchn: ;search for the next directory element, assuminentry to 0 ;in the allocation vector ;may be r/o file call check$rodir ;ro disk error if found call getdptra ;HL=.buff(dptr) mvi m,empty mvi c,0! call scandm ;alloc elts set to 0 call wrdir ;write the directory call searchn ;to next element jmp delete0 ;for another record ; get$block: ;given allocation vector position BC, find the zero bit ;closest to this position by searching left and right. ;if found, set the bit to one and return the bit position ;in hl. if not found (i.e., we pass 0 on the left, or ;maxall on the right), return 0000 in hl mov d,b! mov e,c ;copy of starting position to de lefttst: mov a,c! ora b! jz righttst ;skip if left=0000 ;left not at position zero, bit zero? dcx b! push d! push b ;left,right pushed call getallocbit rar! jnc retblock ;return block number if zero ;bit is one, so try the right pop b! pop d ;left, right restored righttst: lhld maxall ;value of maximum allocation# mov a,e! sub l! mov a,d! sbb h ;rigmap! mvi e,extnum! call copy$dir ;element renamed, move to next call searchn jmp rename0 ; indicators: ;set file indicators for current fcb mvi c,extnum! call search ;through file type indic0: call end$of$dir! rz ;stop at end of dir ;not end of directory, continue to change mvi c,0! mvi e,extnum ;copy name call copy$dir call searchn jmp indic0 ; open: ;search for the directory entry, copy to fcb mvi c,namlen! call search call end$of$dir! rz ;return with lret=255 if end ;not end of directory, copy fcb information open$copy: ;(referenced below to copy fcb info) call getexta! mov a,m! push psw! push h ;save extent# call getdptra! xchg ;DE = .buff(dptr) lhld info ;HL=.fcb(0) mvi c,nxtrec ;length of move operation push d ;save .buff(dptr) call move ;from .buff(dptr) to .fcb(0) ;note that entire fcb is copied, including indicators call setfwf ;sets file write flag pop d! lxi h,extnum! dad d ;HL=.buff(dptr+extnum) mov c,m ;C = directory extent numbeould be identical at this point ldax d! cmp m! jnz mergerr ;low same? inx d! inx h ;to high byte ldax d! cmp m! jnz mergerr ;high same? ;merge operation ok for this pair dcr c ;extra count for double byte dmset: inx d! inx h ;to next byte position dcr c! jnz merge0 ;for more ;end of disk map merge, check record count ;DE = .buff(dptr)+32, HL = .fcb(32) lxi b,-(fcblen-extnum)! dad b! xchg! dad b ;DE = .fcb(extnum), HL = .buff(dptr+extnum) ldax d ;current user extent number ;if fcb(ext) >= buff(fcb) then ;buff(ext) := fcb(ext), buff(rec) := fcb(rec) cmp m! jc endmerge ;fcb extent number >= dir extent number mov m,a ;buff(ext) = fcb(ext) ;update directory record count field lxi b,(reccnt-extnum)! dad b! xchg! dad b ;DE=.buff(reccnt), HL=.fcb(reccnt) mov a,m! stax d ;buff(reccnt)=fcb(reccnt) endmerge: mvi a,true! sta fcb$copied ;mark as copied jmp seek$copy ;ok to "wrdir" here - 1.4 compat ; ret ; mergerr: ;elements did not merge erflow to zero ;otherwise, ok to continue with new module open$reel0: mvi c,namlen! call search ;next extent found? call end$of$dir! jnz open$reel1 ;end of file encountered lda rmf! inr a ;0ffh becomes 00 if read jz open$r$err ;sets lret = 1 ;try to extend the current file call make ;cannot be end of directory call end$of$dir jz open$r$err ;with lret = 1 jmp open$reel2 open$reel1: ;not end of file, open call open$copy open$reel2: call getfcb ;set parameters xra a! jmp sta$ret ;lret = 0 ; ; ret ;with lret = 0 open$r$err: ;cannot move to next extent of this file call setlret1 ;lret = 1 jmp setfwf ;ensure that it will not be closed ;ret ; seqdiskread: ;sequential disk read operation mvi a,1! sta seqio ;drop through to diskread ; diskread: ;(may enter from seqdiskread) mvi a,true! sta rmf ;read mode flag = true (open$reel) ;read the next record from the current fcb call getfcb ;sets parameters for the read lda r lxi h,reccnt! dad d ;HL=.buff(dptr+reccnt) mov b,m ;B holds directory record count pop h! pop psw! mov m,a ;restore extent number ;HL = .user extent#, B = dir rec cnt, C = dir extent# ;if user ext < dir ext then user := 128 records ;if user ext = dir ext then user := dir records ;if user ext > dir ext then user := 0 records mov a,c! cmp m! mov a,b ;ready dir reccnt jz open$rcnt ;if same, user gets dir reccnt mvi a,0! jc open$rcnt ;user is larger mvi a,128 ;directory is larger open$rcnt: ;A has record count to fill lhld info! lxi d,reccnt! dad d! mov m,a ret ; mergezero: ;HL = .fcb1(i), DE = .fcb2(i), ;if fcb1(i) = 0 then fcb1(i) := fcb2(i) mov a,m! inx h! ora m! dcx h! rnz ;return if = 0000 ldax d! mov m,a! inx d! inx h ;low byte copied ldax d! mov m,a! dcx d! dcx h ;back to input form ret ; close: ;locate the directory element and re-write it xra a! sta lret! sta dcnt! sta dcnt+1 ; call nowrite! rnz ;skip close if r/o disk ;check file write flag - 0 icorrectly lxi h,lret! dcr m ;=255 non zero flag set ret ; make: ;create a new file by creating a directory entry ;then opening the file call check$write ;may be write protected lhld info! push h ;save fcb address, look for e5 lxi h,efcb! shld info ;info = .empty mvi c,1! call search ;length 1 match on empty entry call end$of$dir ;zero flag set if no space pop h ;recall info address shld info ;in case we return here rz ;return with error condition 255 if not found xchg ;DE = info address ;clear the remainder of the fcb lxi h,namlen! dad d ;HL=.fcb(namlen) mvi c,fcblen-namlen ;number of bytes to fill xra a ;clear accumulator to 00 for fill make0: mov m,a! inx h! dcr c! jnz make0 lxi h,ubytes! dad d ;HL = .fcb(ubytes) mov m,a ;fcb(ubytes) = 0 call setcdr ;may have extended the directory ;now copy entry to the directory call copy$fcb ;and set the file write flag to "1" jmp setfwf ;ret ; open$reel: ;close the current extent, and open the next one ;if vrecord! lxi h,rcount! cmp m ;vrecord-rcount ;skip if rcount > vrecord jc recordok ;not enough records in the extent ;record count must be 128 to continue cpi 128 ;vrecord = 128? jnz diskeof ;skip if vrecord<>128 call open$reel ;go to next extent if so xra a! sta vrecord ;vrecord=00 ;now check for open ok lda lret! ora a! jnz diskeof ;stop at eof recordok: ;arrive with fcb addressing a record to read call index ;error 2 if reading unwritten data ;(returns 1 to be compatible with 1.4) call allocated ;arecord=0000? jz diskeof ;record has been allocated, read it call atran ;arecord now a disk address call seek ;to proper track,sector call rdbuff ;to dma address jmp setfcb ;replace parameter ; ret ; diskeof: jmp setlret1 ;lret = 1 ;ret ; seqdiskwrite: ;sequential disk write mvi a,1! sta seqio ;drop through to diskwrite ; diskwrite: ;(may enter here from seqdiskwrite above) mvi a,false! sta rmf ;read mode flag ;write recordndicates written call getmodnum ;fcb(modnum) in A ani fwfmsk! rnz ;return if bit remains set mvi c,namlen! call search ;locate file call end$of$dir! rz ;return if not found ;merge the disk map at info with that at buff(dptr) lxi b,dskmap! call getdptra dad b! xchg ;DE is .buff(dptr+16) lhld info! dad b ;DE=.buff(dptr+16), HL=.fcb(16) mvi c,(fcblen-dskmap) ;length of single byte dm merge0: lda single! ora a! jz merged ;skip to double ;this is a single byte map ;if fcb(i) = 0 then fcb(i) = buff(i) ;if buff(i) = 0 then buff(i) = fcb(i) ;if fcb(i) <> buff(i) then error mov a,m! ora a! ldax d! jnz fcbnzero ;fcb(i) = 0 mov m,a ;fcb(i) = buff(i) fcbnzero: ora a! jnz buffnzero ;buff(i) = 0 mov a,m! stax d ;buff(i)=fcb(i) buffnzero: cmp m! jnz mergerr ;fcb(i) = buff(i)? jmp dmset ;if merge ok merged: ;this is a double byte merge operation call mergezero ;buff = fcb if buff 0000 xchg! call mergezero! xchg ;fcb = buff if fcb 0000 ;they shpossible. RMF is true if in read mode xra a! sta fcb$copied ;set true if actually copied call close ;close current extent ;lret remains at enddir if we cannot open the next ext call end$of$dir! rz ;return if end ;increment extent number lhld info! lxi b,extnum! dad b ;HL=.fcb(extnum) mov a,m! inr a! ani maxext! mov m,a ;fcb(extnum)=++1 jz open$mod ;move to next module if zero ;may be in the same extent group mov b,a! lda extmsk! ana b ;if result is zero, then not in the same group lxi h,fcb$copied ;true if the fcb was copied to directory ana m ;produces a 00 in accumulator if not written jz open$reel0 ;go to next physical extent ;result is non zero, so we must be in same logical ext jmp open$reel1 ;to copy fcb information open$mod: ;extent number overflow, go to next module lxi b,(modnum-extnum)! dad b ;HL=.fcb(modnum) inr m ;fcb(modnum)=++1 ;module number incremented, check for overflow mov a,m! ani maxmod ;mask high order bits jz open$r$err ;cannot ov to currently selected file call check$write ;in case write protected lhld info ;HL = .fcb(0) call check$rofile ;may be a read-only file call getfcb ;to set local parameters lda vrecord! cpi lstrec+1 ;vrecord-128 ;skip if vrecord > lstrec ;vrecord = 128, cannot open next extent jnc setlret1 ;lret=1 ; diskwr0: ;can write the next record, so continue call index call allocated mvi c,0 ;marked as normal write operation for wrbuff jnz diskwr1 ;not allocated ;the argument to getblock is the starting ;position for the disk search, and should be ;the last allocated block for this file, or ;the value 0 if no space has been allocated call dm$position sta dminx ;save for later lxi b,0000h ;may use block zero ora a! jz nopblock ;skip if no previous block ;previous block exists at A mov c,a! dcx b ;previous block # in BC call getdm ;previous block # to HL mov b,h! mov c,l ;BC=prev block# nopblock: ;BC = 0000, or previous block # call getlock or new record# dcr c! dcr c! jnz noupdate push psw ;save vrecord value call getmodnum ;HL=.fcb(modnum), A=fcb(modnum) ;reset the file write flag to mark as written fcb ani (not fwfmsk) and 0ffh ;bit reset mov m,a ;fcb(modnum) = fcb(modnum) and 7fh pop psw ;restore vrecord noupdate: ;check for end of extent, if found attempt to open ;next extent in preparation for next write cpi lstrec ;vrecord=lstrec? jnz diskwr3 ;skip if not ;may be random access write, if so we are done ;change next lda seqio! cpi 1! jnz diskwr3 ;skip next extent open op ;update current fcb before going to next extent call setfcb call open$reel ;rmf=false ;vrecord remains at lstrec causing eof if ;no more directory space is available lxi h,lret! mov a,m! ora a! jnz nospace ;space available, set vrecord=255 dcr a! sta vrecord ;goes to 00 next time nospace: mvi m,0 ;lret = 00 for returned value diskwr3: jmp setfcb ;replace parameters ;ret ; rseek: ;ranl,5 ;cannot create new extent #5 lda lret! inr a! jz badseek ;no dir space ;file make operation successful seekok: pop b ;discard r/w flag xra a! jmp sta$ret ;with zero set badseek: ;fcb no longer contains a valid fcb, mark ;with 1100$000b in modnum field so that it ;appears as overflow with file write flag set push h ;save error flag call getmodnum ;HL = .modnum mvi m,1100$0000b pop h ;and drop through seekerr: pop b ;discard r/w flag mov a,l! sta lret ;lret=#, nonzero ;setfwf returns non-zero accumulator for err jmp setfwf ;flag set, so subsequent close ok ;ret ; randiskread: ;random disk read operation mvi c,true ;marked as read operation call rseek cz diskread ;if seek successful ret ; randiskwrite: ;random disk write operation mvi c,false ;marked as write operation call rseek cz diskwrite ;if seek successful ret ; compute$rr: ;compute random record position for getfilesize/setrandom xchg! dad d ;DE=.buf(dptr) or .fcb($block ;block # to HL ;arrive here with block# or zero mov a,l! ora h! jnz blockok ;cannot find a block to allocate mvi a,2! jmp sta$ret ;lret=2 blockok: ;allocated block number is in HL shld arecord xchg ;block number to DE lhld info! lxi b,dskmap! dad b ;HL=.fcb(dskmap) lda single! ora a ;set flags for single byte dm lda dminx ;recall dm index jz allocwd ;skip if allocating word ;allocating a byte value call addh! mov m,e ;single byte alloc jmp diskwru ;to continue allocwd: ;allocate a word value mov c,a! mvi b,0 ;double(dminx) dad b! dad b ;HL=.fcb(dminx*2) mov m,e! inx h! mov m,d ;double wd diskwru: ;disk write to previously unallocated block mvi c,2 ;marked as unallocated write diskwr1: ;continue the write operation of no allocation error ;C = 0 if normal write, 2 if to prev unalloc block lda lret! ora a! rnz ;stop if non zero returned value push b ;save write flag call atran ;arecord set lda seqio! dcr a! dcr adom access seek operation, C=0ffh if read mode ;fcb is assumed to address an active file control block ;(modnum has been set to 1100$0000b if previous bad seek) xra a! sta seqio ;marked as random access operation rseek1: push b ;save r/w flag lhld info! xchg ;DE will hold base of fcb lxi h,ranrec! dad d ;HL=.fcb(ranrec) mov a,m! ani 7fh! push psw ;record number mov a,m! ral ;cy=lsb of extent# inx h! mov a,m! ral! ani 11111b ;A=ext# mov c,a ;C holds extent number, record stacked mov a,m! rar! rar! rar! rar! ani 1111b ;mod# mov b,a ;B holds module#, C holds ext# pop psw ;recall sought record # ;check to insure that high byte of ran rec = 00 inx h! mov l,m ;l=high byte (must be 00) inr l! dcr l! mvi l,6 ;zero flag, l=6 ;produce error 6, seek past physical eod jnz seekerr ;otherwise, high byte = 0, A = sought record lxi h,nxtrec! dad d ;HL = .fcb(nxtrec) mov m,a ;sought rec# stored away ;arrive here with B=mod#, C=ext#, DE=.fcb, rec stored ;the r/w fl0), HL = .f(nxtrec/reccnt) mov c,m! mvi b,0 ;BC = 0000 0000 ?rrr rrrr lxi h,extnum! dad d! mov a,m! rrc! ani 80h ;A=e000 0000 add c! mov c,a! mvi a,0! adc b! mov b,a ;BC = 0000 000? errrr rrrr mov a,m! rrc! ani 0fh! add b! mov b,a ;BC = 000? eeee errrr rrrr lxi h,modnum! dad d! mov a,m ;A=XXX? mmmm add a! add a! add a! add a ;cy=? A=mmmm 0000 push psw! add b! mov b,a ;cy=?, BC = mmmm eeee errr rrrr push psw ;possible second carry pop h ;cy = lsb of L mov a,l ;cy = lsb of A pop h ;cy = lsb of L ora l ;cy/cy = lsb of A ani 1 ;A = 0000 000? possible carry-out ret ; getfilesize: ;compute logical file size for current fcb mvi c,extnum call search ;zero the receiving ranrec field lhld info! lxi d,ranrec! dad d! push h ;save position mov m,d! inx h! mov m,d! inx h! mov m,d;=00 00 00 getsize: call end$of$dir jz setsize ;current fcb addressed by dptr call getdptra! lxi d,reccnt ;ready for compute size call compute$rr ;A=0000 000? BC = mmmm eeee errr r! jnz diskwr11 ; pop b! push b! mov a,c! dcr a! dcr a ; jnz diskwr11 ;old allocation push h ;arecord in hl ret from atran lhld buffa! mov d,a ;zero buffa & fill fill0: mov m,a! inx h! inr d! jp fill0 ; call setdir! lhld arecord1 ; mvi c,2 ; fill1: shld arecord! push b! call seek! pop b ; call wrbuff ;write fill record ; lhld arecord! ;restore last record mvi c,0 ;change allocate flag lda blkmsk! mov b,a! ana l! cmp b!inx h ; jnz fill1 ;cont until cluster is zeroed pop h! shld arecord! call setdata diskwr11: ; call seek ;to proper file position pop b! push b ;restore/save write flag (C=2 if new block) call wrbuff ;written to disk pop b ;C = 2 if a new block was allocated, 0 if not ;increment record count if rcount<=vrecord lda vrecord! lxi h,rcount! cmp m ;vrecord-rcount jc diskwr2 ;rcount <= vrecord mov m,a! inr m ;rcount = vrecord+1 mvi c,2 ;mark as record count incremented diskwr2: ;A has vrecord, C=2 if new bag is still stacked. compare fcb values lxi h,extnum! dad d! mov a,c ;A=seek ext# sub m! jnz ranclose ;tests for = extents ;extents match, check mod# lxi h,modnum! dad d! mov a,b ;B=seek mod# ;could be overflow at eof, producing module# ;of 90H or 10H, so compare all but fwf sub m! ani 7fh! jz seekok ;same? ranclose: push b! push d ;save seek mod#,ext#, .fcb call close ;current extent closed pop d! pop b ;recall parameters and fill mvi l,3 ;cannot close error #3 lda lret! inr a! jz badseek lxi h,extnum! dad d! mov m,c ;fcb(extnum)=ext# lxi h,modnum! dad d! mov m,b ;fcb(modnum)=mod# call open ;is the file present? lda lret! inr a! jnz seekok ;open successful? ;cannot open the file, read mode? pop b ;r/w flag to c (=0ffh if read) push b ;everyone expects this item stacked mvi l,4 ;seek to unwritten extent #4 inr c ;becomes 00 if read operation jz badseek ;skip to error if read operation ;write operation, make new extent call make mvi rrr ;compare with memory, larger? pop h! push h ;recall, replace .fcb(ranrec) mov e,a ;save cy mov a,c! sub m! inx h ;ls byte mov a,b! sbb m! inx h ;middle byte mov a,e! sbb m ;carry if .fcb(ranrec) > directory jc getnextsize ;for another try ;fcb is less or equal, fill from directory mov m,e! dcx h! mov m,b! dcx h! mov m,c getnextsize: call searchn jmp getsize setsize: pop h ;discard .fcb(ranrec) ret ; setrandom: ;set random record from the current file control block lhld info! lxi d,nxtrec ;ready params for computesize call compute$rr ;DE=info, A=cy, BC=mmmm eeee errr rrrr lxi h,ranrec! dad d ;HL = .fcb(ranrec) mov m,c! inx h! mov m,b! inx h! mov m,a ;to ranrec ret ; select: ;select disk info for subsequent input or output ops lhld dlog! lda curdsk! mov c,a! call hlrotr push h! xchg ;save it for test below, send to seldsk call selectdisk! pop h ;recall dlog vector cz sel$error ;returns true if select ok ;is the disk logged in? mov a,l! rarchn jmp dir$to$user ;copy directory entry to user ;ret ;jmp goback ; func19: ;delete a file call reselect call delete jmp copy$dirloc ;ret ;jmp goback ; func20: ;read a file call reselect jmp seqdiskread ; ;jmp goback ; func21: ;write a file call reselect jmp seqdiskwrite ; ;jmp goback ; func22: ;make a file call clrmodnum call reselect jmp make ;ret ;jmp goback ; func23: ;rename a file call reselect call rename jmp copy$dirloc ;ret ;jmp goback ; func24: ;return the login vector lhld dlog! jmp sthl$ret ; ; ret ;jmp goback ; func25: ;return selected disk number lda curdsk! jmp sta$ret ; ; ret ;jmp goback ; func26: ;set the subsequent dma address to info xchg ;was lhld info shld dmaad ;dmaad = info jmp setdata ;to data dma address ;ret ;jmp goback ; func27: ;return the login vector address lhld alloca! jmp sthl$ret ; ; ret ;jmp goback ; func28: equ set$ro ; ;write protect current disk ;rek select ; (data must be adjacent, do not insert variables) ; (address of translate vector, not used) cdrmaxa:ds word ;pointer to cur dir max value curtrka:ds word ;current track address curreca:ds word ;current record address buffa: ds word ;pointer to directory dma address dpbaddr:ds word ;current disk parameter block address checka: ds word ;current checksum vector address alloca: ds word ;current allocation vector address addlist equ $-buffa ;address list size ; ; sectpt - offset obtained from disk parm block at dpbaddr ; (data must be adjacent, do not insert variables) sectpt: ds word ;sectors per track blkshf: ds byte ;block shift factor blkmsk: ds byte ;block mask extmsk: ds byte ;extent mask maxall: ds word ;maximum allocation number dirmax: ds word ;largest directory number dirblk: ds word ;reserved allocation bits for directory chksiz: ds word ;size of checksum vector offset: ds word ;offset tracks at beginning dpblist equ $-sectpt ;size of area ; ; local variables tranv: dar! rc ;return if bit is set ;disk not logged in, set bit and initialize lhld dlog! mov c,l! mov b,h ;call ready call set$cdisk! shld dlog ;dlog=set$cdisk(dlog) jmp initialize ;ret ; curselect: lda linfo! lxi h,curdsk! cmp m! rz ;skip if linfo=curdsk mov m,a ;curdsk=info jmp select ;ret ; reselect: ;check current fcb to see if reselection necessary mvi a,true! sta resel ;mark possible reselect lhld info! mov a,m ;drive select code ani 1$1111b ;non zero is auto drive select dcr a ;drive code normalized to 0..30, or 255 sta linfo ;save drive code cpi 30! jnc noselect ;auto select function, save curdsk lda curdsk! sta olddsk ;olddsk=curdsk mov a,m! sta fcbdsk ;save drive code ani 1110$0000b! mov m,a ;preserve hi bits call curselect noselect: ;set user code lda usrcode ;0...31 lhld info! ora m! mov m,a ret ; ; individual function handlers func12: ;return version number mvi a,dvers! jmp sta$ret ;lret = dvers (high = 00) ; ret ;jmp goback ; t ;jmp goback ; func29: ;return r/o bit vector lhld rodsk! jmp sthl$ret ; ; ret ;jmp goback ; func30: ;set file indicators call reselect call indicators jmp copy$dirloc ;lret=dirloc ;ret ;jmp goback ; func31: ;return address of disk parameter block lhld dpbaddr sthl$ret: shld aret ret ;jmp goback func32: ;set user code lda linfo! cpi 0ffh! jnz setusrcode ;interrogate user code instead lda usrcode! jmp sta$ret ;lret=usrcode ; ret ;jmp goback setusrcode: ani 1fh! sta usrcode ret ;jmp goback ; func33: ;random disk read operation call reselect jmp randiskread ;to perform the disk read ;ret ;jmp goback ; func34: ;random disk write operation call reselect jmp randiskwrite ;to perform the disk write ;ret ;jmp goback ; func35: ;return file size (0-65536) call reselect jmp getfilesize ;ret ;jmp goback ; func36: equ setrandom ; ;set random record ;ret ;jmp goback func37: ; lhld info mov a,l! cma! mov e,a! mov a,h! cs word ;address of translate vector fcb$copied: ds byte ;set true if copy$fcb called rmf: ds byte ;read mode flag for open$reel dirloc: ds byte ;directory flag in rename, etc. seqio: ds byte ;1 if sequential i/o linfo: ds byte ;low(info) dminx: ds byte ;local for diskwrite searchl:ds byte ;search length searcha:ds word ;search address tinfo: ds word ;temp for info in "make" single: ds byte ;set true if single byte allocation map resel: ds byte ;reselection flag olddsk: ds byte ;disk on entry to bdos fcbdsk: ds byte ;disk named in fcb rcount: ds byte ;record count in current fcb extval: ds byte ;extent number and extmsk vrecord:ds word ;current virtual record arecord:ds word ;current actual record arecord1: ds word ;current actual block# * blkmsk ; ; local variables for directory access dptr: ds byte ;directory pointer 0,1,2,3 dcnt: ds word ;directory counter 0,1,...,dirmax drec: ds word ;directory record 0,1,...,dirmax/4 ; bios equ ($ and 0ff00h)+100h ;next module end func13: ;reset disk system - initialize to disk 0 lxi h,0! shld rodsk! shld dlog xra a! sta curdsk ;note that usrcode remains unchanged lxi h,tbuff! shld dmaad ;dmaad = tbuff call setdata ;to data dma address jmp select ;ret ;jmp goback ; func14: equ curselect ; ;select disk info ;ret ;jmp goback ; func15: ;open file call clrmodnum ;clear the module number call reselect jmp open ;ret ;jmp goback ; func16: ;close file call reselect jmp close ;ret ;jmp goback ; func17: ;search for first occurrence of a file mvi c,0 ;length assuming '?' true xchg ;was lhld info mov a,m! cpi '?' ;no reselect if ? jz qselect ;skip reselect if so ;normal search call getexta! mov a,m! cpi '?' ; cnz clrmodnum ;module number zeroed call reselect mvi c,namlen qselect: call search jmp dir$to$user ;copy directory entry to user ;ret ;jmp goback ; func18: ;search for next occurrence of a file name lhld searcha! shld info call reselect! call sema lhld dlog! ana h! mov d,a! mov a,l! ana e mov e,a! lhld rodsk! xchg! shld dlog mov a,l! ana e! mov l,a mov a,h! ana d! mov h,a shld rodsk! ret ; ; goback: ;arrive here at end of processing to return to user lda resel! ora a! jz retmon ;reselection may have taken place lhld info! mvi m,0 ;fcb(0)=0 lda fcbdsk! ora a! jz retmon ;restore disk number mov m,a ;fcb(0)=fcbdsk lda olddsk! sta linfo! call curselect ; ; return from the disk monitor retmon: lhld entsp! sphl ;user stack restored lhld aret! mov a,l! mov b,h ;BA = HL = aret ret func38: equ func$ret func39 equ func$ret func40: ;random disk write with zero fill of unallocated block call reselect mvi a,2! sta seqio mvi c,false call rseek1 cz diskwrite ;if seek successful ret ; ; ; data areas ; ; initialized data efcb: db empty ;0e5=available dir entry rodsk: dw 0 ;read only disk vector dlog: dw 0 ;logged-in disks dmaad: dw tbuff ;initial dma address ; ; curtrka - alloca are set upon dis TITLE 'OCCTXT.AST - Osborne Computer Corp System Text.' SVER = 1 ; Legal values for RSIZE are 48 and 64 ; entered from command line RSIZE = 64 ; default = 64k version ;SYSTEXT.AST - Installation Parameter Configuration. ; This Systext provides definitions of system configuration, ; and Ascii control characters for the DigiCon Phoenix ; product set. ; ; This text is configured for CP/m. Another text must be ; used to assemble routines for Phoenix. Both are derived ; from IPARAMS.ACD. space 4,10 ; True/False definitions FALSE = 0 TRUE = not FALSE space 4,10 ODEBUG = FALSE ;True for activating debug code SYcpm20 = 4 ;=cpm 2.0 SYcpm14 = 0 SYmpm10 = 5 SYcdos = 3 @CPM SET TRUE space 4,10 ; List output page/line/column definitions MLPP = 66 ;Number of lines per physical page LLP = 60 ;NUMBER OF LINES TO PRINT PER PAGN ; Disk type and speed constants SEEKTM = 2 ;if MPI, 0 ;if Siemems then 2 ; Keyboard debounce KBDTM = 5 ;WAIT 1 MILLISECOND FOR DEBOUNCE ; Assemble Feed VTAB = 0Bh ;^K, VT = Vertical Tab FMFD = 0Ch ;^L, FF = Form Feed CR = 0Dh ;^M, CR = Carriage Return LITCHR = 0Eh ;^N, SO = Literal next character SUSCHR = 0Fh ;^O, SI = SUSPEND CHARACTER RTYCHR = 12h ;^R, DC2 = RE-TYPE LINE CHARACTER CTRU = 15h ;^U, NAK = Cancel Line char BWDCHR = 17h ;^W, ETB = Backspace ONE word CANCHR = 18h ;^X, CAN = CANCEL LINE CHARACTER ESC = 1Bh ;^[, ESC = Escape RUBOUT = 7Fh ; DEL = Rubout (accepted as Backspace) ERC = 7Fh ;illegal key REP = 02 ;repeat key CAPS = 03 ;repeat SHTK = 04 ;Shift key CTRL = 05 ;Control key CLRSRN = 'Z'-40h ;Clear screen MCR = 'L'-40h ;Form Feed (move cursor right) PMCHR = '>' ;Prompt character ERCHR = '?' ;Error character BKS = 08h EOFC = 'Z'-40h ;Eof character (same as cpm) TAB = 09h INVV = 80h ;Inverse video bit GESC = 'a' ;Graphics escape toggle char ; CP/m definitions CVER = 22 ;CPM version number CBIOSV = SVER ;cbios version NDSKS = 2 K = 1024 MSIZE = RSIZE-(LVMEM/K) BIOS = MSIZE*k-0A00 = 18 ;next entry in directory OS#DEL = 19 ;Delete a file OS#READ = 20 ;READ next pru from file OS#WRT = 21 ;Write one PRU to file OS#CRE = 22 ;Create a file OS#REN = 23 ;rename a file OS#RLV = 24 ;Return login vector OS#RCD = 25 ;Return current disk OS#SDMA = 26 ;Set DMA address ; CPM 2.x functions OS#DIO = 6 ;Console direct I/O OS#GAA = 27 ;Get adr (alloc) OS#SWP = 28 ;Set write protect for disk OS#GRO = 29 ;Get r/o vector OS#SFA = 30 ;Set File attributes OS#GAD = 31 ;Get disk parms address OS#USC = 32 ;Get/set user code OS#RRD = 33 ;Read random OS#WRD = 34 ;Write random OS#FIZ = 35 ;Get file size in records OS#SRD = 36 ;Set random record OS#RDRV = 37 ;Reset drive OS#ADR = 38 ;Access drive OS#FDR = 39 ;Free drive OS#WRZ = 40 ;Write random with zero fill ; Direct BIO call definitions BCONST = 2*3 ;Console status BCONIN SET 3*3 BCOUT = 4*3 ;Console output BLIST = 5*3 ;List output BPUNCH = 6*3 ;Punch output BRDR = 7*3 ;Reader input BHOME = 8*3 ;Home disk drive BSDSK = 9*3 ;y Constants ; Port definitions (Memory mapped) H.FDC = 2100h ;Floppy disk H.KEY = 2200h ;Keyboard H.SIO = 2A00h ;Serial I/O H.VIO = 2C00h ;Video memory controls SEKTMO = 0FEh ;Seek Time Out error status ; SIO Interface and definitions H.SXMT = H.SIO+1 ;Transmit address H.SREC = H.SIO+1 ;Receive (read from address) H.SCTRL = H.SIO ;Set control reg (write) H.SSTS = H.SIO ;Status reg (read) ; Defined function codes SI.MRST = 0_10_101_11b ;Master reset SI.S16 = 0_10_101_01b ;Select 16x clock, xmit/rec ;8 data, 1 stop bit ;RTS high (no request) SI.S64 = 0_10_101_10b ;Select 64x clock ;...all else identical to SI.S16 SI.1200 = SI.S16 SI.300 = SI.S64 SI.RRDY = 01 ;Receiver ready SI.TRDY = 02 ;Transmit ready SI.NCTS = 0000_1000b ; ~CTS SI.RERR = 0111_0000b ;Rcvr err: PE, OVRN, FE H.IEEE = 2900h ;IEEE interface CPDRA = H.IEEE+0 ;Peripheral/Direction register A CCRA = H.IEEE+1 ;Control register A CPDRB = H.IEEE+2 ;Peripheral/Direction register B CCh ;Start of CPM jump table BDOS = BIOS-0E00h+6 ;BDOS entry pt CCP = BIOS-1600h ;CCP entry ABTC = 'C'-40h ;Abort character ALVS: = 0Ch ;*** must be validated with CBIOS *** CSVS: = 20h ;*** --------------------------- *** LSECB = 256 ;Length of a sector MTRK = 40 ;Maximum tracks MSEC = 10 ;Maximum Sectors FMTCHR = 0E5h ;Data format char space 4,10 ; Set CP/M symbols. IOBYTE = 3 iob_default = 80h ;default value for iobyte CDISK = 4 ;Current logged-in CPM disk SYS = 5 ;CPM Monitor call address SYSL = SYS+1 ;(lwa of CP/m) TIMPTR = 40h ;points to where time kept DBUF = 80h ;Default disk buffer OM#LRAM = SYSL SYSR = 0 ;return to system SYSDSK = CDISK ;Contains current system disk # SYSDAT = 10h ;Date in ddmmyy SYSTIM = SYSDAT+3 ;Time in hhmmss ORGP = 100h ;Transient program origin ;EOL does not exist in CP/M EOM = '$' ;End of Message (for system function) EOF = 'Z'-40h ;End of File L$PRU = 128 ;Bytes per floppy sector FCB = 5Ch ;SYSTEM DEFAULT FILSelect Disk drive BSTTRK = 10*3 ;Set track BSTSEC = 11*3 ;Set sector BSTDMA = 12*3 ;Set DMA BREAD = 13*3 ;READ disk pru BWRITE = 14*3 ;WRITE disk pru BLSTS = 15*3 ;List LST: device status BSECTR = 16*3 ;Sector translate routine ; Extensions added by SORCIM BRRXT = 17*3 ;Ram-Rom exit BROMJP = 18*3 ;Rom jump entry BFMTJP = 19*3 ;Format Track BSBAUD = 20*3 ;Set Serial Port Baud Rate ;BRDXY = 21*3 ;Read XY ;BWTXY = 22*3 ;Write XY ; IEEE-488 calls BIECO = 21*3 ;Control Out BIESI = 22*3 ;Status In BIEGTS = 23*3 ;Go To Standby BIETC = 24*3 ;Take Control BIEOIM = 25*3 ;Output Interface Message BIEODM = 26*3 ;Output Device Message BIEIDM = 27*3 ;Input Device Message BIEPP = 28*3 ;Parallel Poll ;BIOCAL Used to make calls directly to BIO BIOCAL MACRO NAME LXI HL,L%K PUSH HL ;return LHLD SYSR+1 MVI L,%1 PCHL L%K: ENDM space 4,10 ;SYSCAL ; This macro is to be used when the common deck ; SYS.ACD is used to make file action ; requests. SYSCAL MACRO NAME,ARG .1 IFRB = H.IEEE+3 ;Control register B ROMVEC = 100h ;Start of rom vector BKPI = 0CFh ;Breakpoint interrupt = 1 NMIA = 66h ;NMI address LCMDP = 4*1024 ;L command prom MSG 'Assembly for 64k Mode 2 system' MRAM = 0ED80h FWAVM = 0F000h NVDL = 24 INTBL = 0EFF0h ;interrupt vector table LVMEM = 128*32 ;Length of video memory LVDIS = 128*NVDL ;Length of Display memory VFLO = -22 ;First line video offset VLL = 128 ;Length of one video line VLDL = 52 ;Length of displayed char/line FWASM = FWAVM+VLDL ;Status info LWAMEM = FWAVM+LVMEM-1 @FREQ = 4000 ;freq of processor SCLFRE = @FREQ/22 ;for DELAY routine ; Define Ascii Control Codes. ; Value ;^Control Meaning in Phoenix EOS = 0 ;^@, NUL = End of String TBFCHR = 'A'-40h ;^A, SOH = Tab with fill BTBCHR = 02h ;^B, STX = BACK TAB CHARACTER ABTCHR = 03h ;^C, ETX = ABORT CHARACTER BELCHR = 07h ;^G, BEL = Audible Alarm BKSCHR = 08h ;^H, BS = PHYSICAL BACKSPACE CHARACTER TABCHR = 09h ;^I, HT = Tab LF = 0Ah ;^J, LF = LinE CONTROL BLOCK FCB_NR = FCB+32 ;Next Record = LRN F_EX = 12 ;Extend field F_S2 = 14 ;CPM system usage **used with 2.0 SIZ funct** F_RC = 15 ;Record count (0 to 128) F_NR = 32 ;Next record ordinal in FCB F_RRF = 33 ;r0,r1,r2 (random field) in FCB L_FILEN = 8 ;length of File name L_FILEX = 3 ;length of filename suffix L_FCB = 36 ;length of entire FCB (comp with 2.0) DMA = 80h ;WHERE SECTORS ARE READ TBUFF = DMA ;where CCP puts command line OS#RCO = 1 ;Read character from CRT: OS#WCO = 2 ;Write character to CRT: device OS#RRO = 3 ;Read from reader OS#WPO = 4 ;Write to punch OS#WLO = 5 ;Write character to LST: device OS#OST = 9 ;Write string on CRT: OS#RCB = 10 ;Read Console Buffer OS#CABT = 11 ;Get Console status OS#VER = 12 ;Return version in HL ;H= 0, L=0 if 1.4 ;H= 0, L=xx if 2.0 or > ;H= 1, L=0 if MPM 1.0 OS#IND = 13 ;Initialize/Reset Disk system OS#DSEL = 14 ;Select disk unit OS#OPEN = 15 ;Open file OS#CLOS = 16 ;Close file OS#SDIR = 17 ;Search directory OS#NDIR  ' %2' <> ' ' LXI DE,%2 .1 ENDIF CALL %1 ENDM space 4,10 ; SYSTEM This macro provides a mechanism to call the ; system directly. SYSTEM MACRO FUN,ARG .2 IF DEF OS#%1 MVI C,OS#%1 .1 IF ' %2' <> ' ' LXI DE,%2 .1 ENDIF CALL SYS .2 ELSE CALL %1 .2 ENDIF ENDM ; ALIGN Set origin to even boundary. ALIGN MACRO SIZ,O DS (*%2+(%1)-1)/(%1)*(%1)-*%2 ENDM ; DI and EI are macros so all usages can be accounted ; for in the cr. DI MACRO DB 0F3h ENDM EI MACRO DB 0FBh ENDM ; Macros to enable ROM, RAM and DIM code ENAROM MACRO DI OUT 0 LDK A,0 STO A,ROMRAM EI ENDM DISROM MACRO DI OUT 1 LDK A,1 STO A,ROMRAM EI ENDM BRTBIT: = 80h ;set brt/dim memory BRIGHT DIMBIT: = 00h ;set brt/dim memory DIM ENADIM MACRO OUT 2 ENDM DISDIM MACRO OUT 3 ENDM PUSHAL MACRO PUSH BC PUSH DE PUSH HL ENDM POPALL MACRO POP HL POP DE POP BC ENDM ; Endx SYSTEXT Master reset SI.S16 = 0_10_101_01b ;Select 16x clock, xmit/rec ;8 data, 1 stop bit ;RTS high (no request) SI.S64 = 0_10_101_10b ;Select 64x clock ;...all else identical to SI.S16 SI.1200 = SI.S16 SI.300 = SI.S64 SI.RRDY = 01 ;Receiver ready SI.TRDY = 02 ;Transmit ready SI.NCTS = 0000_1000b ; ~CTS SI.RERR = 0111_0000b ;Rcvr err: PE, OVRN, FE H.IEEE = 2900h ;IEEE interface CPDRA = H.IEEE+0 ;Peripheral/Direction register A CCRA = H.IEEE+1 ;Control register A CPDRB = H.IEEE+2 ;Peripheral/Direction register B CCRB = H.IEEE+3 ;Control register B ROMVEC = 100h ;Start of rom vector BKPI = 0CFh ;Breakpoint interrupt = 1 NMIA = 66h ;NMI address LCMDP = 4*1024 ;L command prom MSG 'Assembly for 64k Mode 2 system' MRAM = 0ED80h MRAMEXT = 0ED70h FWAVM = 0F000h NVDL = 24 INTBL = 0EFF0h ;interrupt vector table LVMEM = 128*32 ;Length of video memory LVDIS = 128*NVDL ;Length of Display memory VFLO = -22 ;First line video offset VLL = 128 ;Length of one vidta format char ; Set CP/M symbols. IOBYTE = 3 iob_default = 80h ;default value for iobyte CDISK = 4 ;Current logged-in CPM disk SYS = 5 ;CPM Monitor call address SYSL = SYS+1 ;(lwa of CP/m) TIMPTR = 40h ;points to where time kept DBUF = 80h ;Default disk buffer OM#LRAM = SYSL SYSR = 0 ;return to system SYSDSK = CDISK ;Contains current system disk # SYSDAT = 10h ;Date in ddmmyy SYSTIM = SYSDAT+3 ;Time in hhmmss ORGP = 100h ;Transient program origin ;EOL ;does not exist in CP/M EOM = '$' ;End of Message (for system function) EOF = 'Z'-40h ;End of File L$PRU = 128 ;Bytes per floppy sector FCB = 5Ch ;SYSTEM DEFAULT FILE CONTROL BLOCK FCB_NR = FCB+32 ;Next Record = LRN F_EX = 12 ;Extend field F_S2 = 14 ;CPM system usage **used with 2.0 SIZ funct** F_RC = 15 ;Record count (0 to 128) F_NR = 32 ;Next record ordinal in FCB F_RRF = 33 ;r0,r1,r2 (random field) in FCB L_FILEN = 8 ;length of File name L_FILEX = 3 ;length of filename suffix L_FCB = 36 ;length of eneo line VLDL = 52 ;Length of displayed char/line FWASM = FWAVM+VLDL ;Status info LWAMEM = FWAVM+LVMEM-1 @FREQ = 4000 ;freq of processor SCLFRE = @FREQ/22 ;for DELAY routine ; Define Ascii Control Codes. ; Value ;^Control Meaning in Phoenix ;--------------------------------------------------------- EOS = 0 ;^@, NUL = End of String TBFCHR = 'A'-40h ;^A, SOH = Tab with fill BTBCHR = 02h ;^B, STX = BACK TAB CHARACTER ABTCHR = 03h ;^C, ETX = ABORT CHARACTER BELCHR = 07h ;^G, BEL = Audible Alarm BKSCHR = 08h ;^H, BS = PHYSICAL BACKSPACE CHARACTER TABCHR = 09h ;^I, HT = Tab LF = 0Ah ;^J, LF = Line Feed VTAB = 0Bh ;^K, VT = Vertical Tab FMFD = 0Ch ;^L, FF = Form Feed CR = 0Dh ;^M, CR = Carriage Return LITCHR = 0Eh ;^N, SO = Literal next character SUSCHR = 0Fh ;^O, SI = SUSPEND CHARACTER RTYCHR = 12h ;^R, DC2 = RE-TYPE LINE CHARACTER CTRU = 15h ;^U, NAK = Cancel Line char BWDCHR = 17h ;^W, ETB = Backspace ONE word CANCHR = 18h ;^X, CAN = CANCEL LINE CHARACTER ESC =tire FCB (comp with 2.0) DMA = 80h ;WHERE SECTORS ARE READ TBUFF = DMA ;where CCP puts command line OS#RCO = 1 ;Read character from CRT: OS#WCO = 2 ;Write character to CRT: device OS#RRO = 3 ;Read from reader OS#WPO = 4 ;Write to punch OS#WLO = 5 ;Write character to LST: device OS#OST = 9 ;Write string on CRT: OS#RCB = 10 ;Read Console Buffer OS#CABT = 11 ;Get Console status OS#VER = 12 ;Return version in HL ;H= 0, L=0 if 1.4 ;H= 0, L=xx if 2.0 or > ;H= 1, L=0 if MPM 1.0 OS#IND = 13 ;Initialize/Reset Disk system OS#DSEL = 14 ;Select disk unit OS#OPEN = 15 ;Open file OS#CLOS = 16 ;Close file OS#SDIR = 17 ;Search directory OS#NDIR = 18 ;next entry in directory OS#DEL = 19 ;Delete a file OS#READ = 20 ;READ next pru from file OS#WRT = 21 ;Write one PRU to file OS#CRE = 22 ;Create a file OS#REN = 23 ;rename a file OS#RLV = 24 ;Return login vector OS#RCD = 25 ;Return current disk OS#SDMA = 26 ;Set DMA address ; CPM 2.x functions OS#DIO = 6 ;OCCTXT6.AST TITLE 'Osborne Computer Corp System Text.' SVER = 1 RSIZE = 64 ; default = 64k version ; True/False definitions FALSE = 0 TRUE = not FALSE space 4,10 ODEBUG = FALSE ;True for activating debug code SYcpm20 = 4 ;=cpm 2.0 SYcpm14 = 0 SYmpm10 = 5 SYcdos = 3 @CPM SET TRUE space 4,10 ; List output page/line/column definitions MLPP = 66 ;Number of lines per physical page LLP = 60 ;NUMBER OF LINES TO PRINT PER PAGN ; Disk type and speed constants SEEKTM = 2 ;if MPI, 0 ;if Siemems then 2 ; Assembly Constants ; Port definitions (Memory mapped) H.FDC = 2100h ;Floppy disk H.KEY = 2200h ;Keyboard H.SIO = 2A00h ;Serial I/O H.VIO = 2C00h ;Video memory controls SEKTMO = 0FEh ;Seek Time Out error status ; SIO Interface and definitions H.SXMT = H.SIO+1 ;Transmit address H.SREC = H.SIO+1 ;Receive (read from address) H.SCTRL = H.SIO ;Set control reg (write) H.SSTS = H.SIO ;Status reg (read) ; Defined function codes SI.MRST = 0_10_101_11b ; 1Bh ;^[, ESC = Escape RUBOUT = 7Fh ; DEL = Rubout (accepted as Backspace) ERC = 7Fh ;illegal key REP = 02 ;repeat key CAPS = 03 ;repeat SHTK = 04 ;Shift key CTRL = 05 ;Control key CLRSRN = 'Z'-40h ;Clear screen MCR = 'L'-40h ;Form Feed (move cursor right) PMCHR = '>' ;Prompt character ERCHR = '?' ;Error character BKS = 08h ;BACKSPACE EOFC = 'Z'-40h ;Eof character (same as cpm) TAB = 09h ;TAB INVV = 80h ;Inverse video bit GESC = 'a' ;Graphics escape toggle char PAGE ; CP/M DEFINITIONS CVER = 22 ;CPM version number CBIOSV = SVER ;cbios version NDSKS = 2 K = 1024 MSIZE = RSIZE-(LVMEM/K) BIOS = MSIZE*k-0A00h ;Start of CPM jump table BDOS = BIOS-0E00h+6 ;BDOS entry pt CCP = BIOS-1600h ;CCP entry ABTC = 'C'-40h ;Abort character ALVS: = 0Ch ;*** must be validated with CBIOS *** CSVS: = 20h ;*** --------------------------- *** ;DRIVE DEFINITIONS LSECB = 256 ;Length of a sector MTRK = 40 ;Maximum tracks MSEC = 10 ;Maximum Sectors FMTCHR = 0E5h ;Da! ;Console direct I/O OS#GAA = 27 ;Get adr (alloc) OS#SWP = 28 ;Set write protect for disk OS#GRO = 29 ;Get r/o vector OS#SFA = 30 ;Set File attributes OS#GAD = 31 ;Get disk parms address OS#USC = 32 ;Get/set user code OS#RRD = 33 ;Read random OS#WRD = 34 ;Write random OS#FIZ = 35 ;Get file size in records OS#SRD = 36 ;Set random record OS#RDRV = 37 ;Reset drive OS#ADR = 38 ;Access drive OS#FDR = 39 ;Free drive OS#WRZ = 40 ;Write random with zero fill ; Direct BIO call definitions BCONST = 2*3 ;Console status BCONIN SET 3*3 BCOUT = 4*3 ;Console output BLIST = 5*3 ;List output BPUNCH = 6*3 ;Punch output BRDR = 7*3 ;Reader input BHOME = 8*3 ;Home disk drive BSDSK = 9*3 ;Select Disk drive BSTTRK = 10*3 ;Set track BSTSEC = 11*3 ;Set sector BSTDMA = 12*3 ;Set DMA BREAD = 13*3 ;READ disk pru BWRITE = 14*3 ;WRITE disk pru BLSTS = 15*3 ;List LST: device status BSECTR = 16*3 ;Sector translate routine ; Extensions added by SORCIM BRRXT = 17*3 ;Ram-Rom exit brt/dim memory BRIGHT DIMBIT: = 00h ;set brt/dim memory DIM ENADIM MACRO OUT 2 ENDM DISDIM MACRO OUT 3 ENDM PUSHAL MACRO PUSH BC PUSH DE PUSH HL ENDM POPALL MACRO POP HL POP DE POP BC ENDM ; Endx SYSTEXT VICED ONCE KROW_M = 38H ;ROW NUMBER MASK KCOL_M = 7H ;COL NUMBER MASK RPTCTR = 1 ;REPEAT COUNT ROW0_M = 81H ;MASK FOR ADDRESSING ROW 0 DB_CT = 1 ;DEBOUNCE COUNT IRPTCT = 24 ;INITIAL REPEAT COUNT (400MS) SRPTCT = 6 ;SECOND REPEAT COUNT (100MS) TOT_ROW = 7 ;TOTAL ROWS CTL_KY = 2 ;COLUMN NUMBER OF CTL,ALPHA AND SHIFT KEYS ALPH_KY = 3 SHFT_KY = 4 SLD_RCT = 3 ;REPEAT COUNT FOR SLIDE KEYS (50MS APPROX) ; DI and EI are macros so all usages can be accounted ; for in the cr. DI MACRO DB 0F3h ENDM EI MACRO DB 0FBh ENDM ; Macros to enable ROM, RAM and DIM code ENAROM MACRO DI OUT 0 LDK A,0 STO A,ROMRAM EI ENDM DISROM MACRO DI OUT 1 LDK A,1 STO A,ROMRAM EI ENDM BRTBIT: = 80h ;set;OCCTXT5.AST TITLE 'Osborne Computer Corp System Text.' ;NOTE ;FOR USE ONLY WITH OLD CBIOS SVER = 1 RSIZE = 64 ; default = 64k version ; True/False definitions FALSE = 0 TRUE = not FALSE space 4,10 ODEBUG = FALSE ;True for activating debug code SYcpm20 = 4 ;=cpm 2.0 SYcpm14 = 0 SYmpm10 = 5 SYcdos = 3 @CPM SET TRUE space 4,10 ; List output page/line/column definitions MLPP = 66 ;Number of lines per physical page LLP = 60 ;NUMBER OF LINES TO PRINT PER PAGN ; Disk type and speed constants SEEKTM = 2 ;if MPI, 0 ;if Siemems then 2 ; Assembly Constants ; Port definitions (Memory mapped) H.FDC = 2100h ;Floppy disk H.KEY = 2200h ;Keyboard H.SIO = 2A00h ;Serial I/O H.VIO = 2C00h ;Video memory controls SEKTMO = 0FEh ;Seek Time Out error status ; SIO Interface and definitions H.SXMT = H.SIO+1 ;Transmit address H.SREC = H.SIO+1 ;Receive (read from address) H.SCTRL = H.SIO ;Set control reg (write) H.SSTS = H.SIO ;Status reg (read) ; Def BROMJP = 18*3 ;Rom jump entry BFMTJP = 19*3 ;Format Track BSBAUD = 20*3 ;Set Serial Port Baud Rate ;BRDXY = 21*3 ;Read XY ;BWTXY = 22*3 ;Write XY ; IEEE-488 calls BIECO = 21*3 ;Control Out BIESI = 22*3 ;Status In BIEGTS = 23*3 ;Go To Standby BIETC = 24*3 ;Take Control BIEOIM = 25*3 ;Output Interface Message BIEODM = 26*3 ;Output Device Message BIEIDM = 27*3 ;Input Device Message BIEPP = 28*3 ;Parallel Poll ;BIOCAL Used to make calls directly to BIO BIOCAL MACRO NAME LXI HL,L%K PUSH HL ;return LHLD SYSR+1 MVI L,%1 PCHL L%K: ENDM space 4,10 ;SYSCAL ; This macro is to be used when the common deck ; SYS.ACD is used to make file action ; requests. SYSCAL MACRO NAME,ARG .1 IF ' %2' <> ' ' LXI DE,%2 .1 ENDIF CALL %1 ENDM space 4,10 ; SYSTEM This macro provides a mechanism to call the ; system directly. SYSTEM MACRO FUN,ARG .2 IF DEF OS#%1 MVI C,OS#%1 .1 IF ' %2' <> ' ' LXI DE,%2 .1 ENDIF CALL SYS .2 ELSE CALL %1 .2 ENDIF END;WRTSYS.WIP ;WRITE OUT SYSTEM TRACKS(0-2) FORM 04000H ;REV A ;DATE = 237 ;VERSION 1 ;DEB ;MADE ORIGINAL PROGRAM LINK OCCRAMA.ASM ; Entry Memory has valid system image. ; Exit system written. ORG 0100H TRLTH = 100H*10 ;TEN SECTORS = ONE TRACK START: LDK HL,4000h STO HL,DMADR ;set DMA LDK A,1 STO A,SDISK ;SET DRIVE TO B STO A,SAVSEC ;set to 1st sector LDK HL,0 STO HL,SAVTRK ;set track LDK DE,BGMES ;MESSAGE CALL PRINT CALL CONIN ;GET RESPONSE CMP 'Y' JZ :2 ;BEGIN TRANSFER IF YES LDK C,0 CMP 'N' JZ SYS ;REBOOT IF NO JMP START ;LOOP BGMES: DB 01AH ;CLEAR SCREEN DB LF,LF,LF,LF,LF,LF DB 'WRITE SYSTEM TRACKS TO DRIVE "B"' DB CR,LF DB ' FROM 4000H.' DB CR,LF,LF,LF,LF DB 'To end type "N" to continue type "Y".' DB CR,LF,LF,'$' ; loop writing data out :2: LDK B,MSEC CALL DWRT ;write entire track JNZ DERR ;if error LD HL,DMADR LDK DE,TRLTH ADD HL,DE STO HL,DMADR ;reset DMA LD HL,SAVTRK INC HL STO HL,SAVTRK ;ined function codes SI.MRST = 0_10_101_11b ;Master reset SI.S16 = 0_10_101_01b ;Select 16x clock, xmit/rec ;8 data, 1 stop bit ;RTS high (no request) SI.S64 = 0_10_101_10b ;Select 64x clock ;...all else identical to SI.S16 SI.1200 = SI.S16 SI.300 = SI.S64 SI.RRDY = 01 ;Receiver ready SI.TRDY = 02 ;Transmit ready SI.NCTS = 0000_1000b ; ~CTS SI.RERR = 0111_0000b ;Rcvr err: PE, OVRN, FE H.IEEE = 2900h ;IEEE interface CPDRA = H.IEEE+0 ;Peripheral/Direction register A CCRA = H.IEEE+1 ;Control register A CPDRB = H.IEEE+2 ;Peripheral/Direction register B CCRB = H.IEEE+3 ;Control register B ROMVEC = 100h ;Start of rom vector BKPI = 0CFh ;Breakpoint interrupt = 1 NMIA = 66h ;NMI address LCMDP = 4*1024 ;L command prom MSG 'Assembly for 64k Mode 2 system' MRAM = 0ED80h MRAMEXT = 0ED70h FWAVM = 0F000h NVDL = 24 INTBL = 0EFF0h ;interrupt vector table LVMEM = 128*32 ;Length of video memory LVDIS = 128*NVDL ;Length of Display memory VFLO = -22 ;First lM ; ALIGN Set origin to even boundary. ALIGN MACRO SIZ,O DS (*%2+(%1)-1)/(%1)*(%1)-*%2 ENDM ;KEYBOARD CONSTANTS KL_LEN = 3 ;KEY LIST LENTH KLE_LEN = 2 ;KEY LIST ENTRY LENTH KL_USED = 7 ;KEYLIST ENTRY USED KY_SRVD = 6 ;KEY SERVICED ONCE KROW_M = 38H ;ROW NUMBER MASK KCOL_M = 7H ;COL NUMBER MASK RPTCTR = 1 ;REPEAT COUNT ROW0_M = 81H ;MASK FOR ADDRESSING ROW 0 DB_CT = 1 ;DEBOUNCE COUNT IRPTCT = 24 ;INITIAL REPEAT COUNT (400MS) SRPTCT = 6 ;SECOND REPEAT COUNT (100MS) TOT_ROW = 7 ;TOTAL ROWS CTL_KY = 2 ;COLUMN NUMBER OF CTL,ALPHA AND SHIFT KEYS ALPH_KY = 3 SHFT_KY = 4 SLD_RCT = 3 ;REPEAT COUNT FOR SLIDE KEYS (50MS APPROX) ; DI and EI are macros so all usages can be accounted ; for in the cr. DI MACRO DB 0F3h ENDM EI MACRO DB 0FBh ENDM ; Macros to enable ROM, RAM and DIM code ENAROM MACRO DI OUT 0 LDK A,0 STO A,ROMRAM EI ENDM DISROM MACRO DI OUT 1 LDK A,1 STO A,ROMRAM EI ENDM BRTBIT: = 80h ;setupdate track MOV A,L CMP 3 JRNZ :2 ;if not done JMP START ;LOOP FOREVER TITLE 'USEFUL PROC' ; +---------------+ ; | | ; | USEFUL PROC"S | ; | | ; +---------------+ CONIN: LDK C,1 CALL SYS ;BDOS CMP 'a' RC ;IF NOT LOWER CASE CMP 'z'+1 RNC SUI 'a'-'A' ;MAKE UPPER CASE RET DWRT: LDK DE,012AH ;BIOS WRITE SEG JMP LD HL,0001H ;ADDRESS 1 LDK L,36H ;ROMJMP JMP [HL] DERR: LDK DE,ERROR CALL PRINT CALL CONIN ;GET ANSWER JMP START PRINT: LDK C,9 JMP SYS ;PRINT STRING ERROR: DB 1AH ;CLEAR SCREEN DB LF,LF,LF,LF,LF,LF DB ' ****ERROR****' DB CR,LF DB ' BAD TRANSFER' DB CR,LF,LF,LF,LF DB '...any character to restart' DB CR,LF,LF DB '$' ; end type "N" to continue type "Y".' DB CR,LF,LF,'$' ; loop writing data out :2: LDK B,MSEC CALL DWRT ;write entire track JNZ DERR ;if error LD HL,DMADR LDK DE,TRLTH ADD HL,DE STO HL,DMADR ;reset DMA LD HL,SAVTRK INC HL STO HL,SAVTRK ;"ine video offset VLL = 128 ;Length of one video line VLDL = 52 ;Length of displayed char/line FWASM = FWAVM+VLDL ;Status info LWAMEM = FWAVM+LVMEM-1 @FREQ = 4000 ;freq of processor SCLFRE = @FREQ/22 ;for DELAY routine ; Define Ascii Control Codes. ; Value ;^Control Meaning in Phoenix ;--------------------------------------------------------- EOS = 0 ;^@, NUL = End of String TBFCHR = 'A'-40h ;^A, SOH = Tab with fill BTBCHR = 02h ;^B, STX = BACK TAB CHARACTER ABTCHR = 03h ;^C, ETX = ABORT CHARACTER BELCHR = 07h ;^G, BEL = Audible Alarm BKSCHR = 08h ;^H, BS = PHYSICAL BACKSPACE CHARACTER TABCHR = 09h ;^I, HT = Tab LF = 0Ah ;^J, LF = Line Feed VTAB = 0Bh ;^K, VT = Vertical Tab FMFD = 0Ch ;^L, FF = Form Feed CR = 0Dh ;^M, CR = Carriage Return LITCHR = 0Eh ;^N, SO = Literal next character SUSCHR = 0Fh ;^O, SI = SUSPEND CHARACTER RTYCHR = 12h ;^R, DC2 = RE-TYPE LINE CHARACTER CTRU = 15h ;^U, NAK = Cancel Line char BWDCHR = 17h ;^W, ETB = Backspace ONE word CANCHh of filename suffix L_FCB = 36 ;length of entire FCB (comp with 2.0) DMA = 80h ;WHERE SECTORS ARE READ TBUFF = DMA ;where CCP puts command line OS#RCO = 1 ;Read character from CRT: OS#WCO = 2 ;Write character to CRT: device OS#RRO = 3 ;Read from reader OS#WPO = 4 ;Write to punch OS#WLO = 5 ;Write character to LST: device OS#OST = 9 ;Write string on CRT: OS#RCB = 10 ;Read Console Buffer OS#CABT = 11 ;Get Console status OS#VER = 12 ;Return version in HL ;H= 0, L=0 if 1.4 ;H= 0, L=xx if 2.0 or > ;H= 1, L=0 if MPM 1.0 OS#IND = 13 ;Initialize/Reset Disk system OS#DSEL = 14 ;Select disk unit OS#OPEN = 15 ;Open file OS#CLOS = 16 ;Close file OS#SDIR = 17 ;Search directory OS#NDIR = 18 ;next entry in directory OS#DEL = 19 ;Delete a file OS#READ = 20 ;READ next pru from file OS#WRT = 21 ;Write one PRU to file OS#CRE = 22 ;Create a file OS#REN = 23 ;rename a file OS#RLV = 24 ;Return login vector OS#RCD = 25 ;Return current disk OS#SDMA = 26 ;Set F CALL SYS .2 ELSE CALL %1 .2 ENDIF ENDM ; ALIGN Set origin to even boundary. ALIGN MACRO SIZ,O DS (*%2+(%1)-1)/(%1)*(%1)-*%2 ENDM ;KEYBOARD CONSTANTS KL_LEN = 3 ;KEY LIST LENTH KLE_LEN = 2 ;KEY LIST ENTRY LENTH KL_USED = 7 ;KEYLIST ENTRY USED KY_SRVD = 6 ;KEY SERVICED ONCE KROW_M = 38H ;ROW NUMBER MASK KCOL_M = 7H ;COL NUMBER MASK RPTCTR = 1 ;REPEAT COUNT ROW0_M = 81H ;MASK FOR ADDRESSING ROW 0 DB_CT = 1 ;DEBOUNCE COUNT IRPTCT = 24 ;INITIAL REPEAT COUNT (400MS) SRPTCT = 6 ;SECOND REPEAT COUNT (100MS) TOT_ROW = 7 ;TOTAL ROWS CTL_KY = 2 ;COLUMN NUMBER OF CTL,ALPHA AND SHIFT KEYS ALPH_KY = 3 SHFT_KY = 4 SLD_RCT = 3 ;REPEAT COUNT FOR SLIDE KEYS (50MS APPROX) ; DI and EI are macros so all usages can be accounted ; for in the cr. DI MACRO DB 0F3h ENDM EI MACRO DB 0FBh ENDM ; Macros to enable ROM, RAM and DIM code ENAROM MACRO DI OUT 0 LDK A,0 STO A,ROMRAM EI ENDM DISROM MACRO DI OUT 1 LDK A,1 SR = 18h ;^X, CAN = CANCEL LINE CHARACTER ESC = 1Bh ;^[, ESC = Escape RUBOUT = 7Fh ; DEL = Rubout (accepted as Backspace) ERC = 7Fh ;illegal key REP = 02 ;repeat key CAPS = 03 ;repeat SHTK = 04 ;Shift key CTRL = 05 ;Control key CLRSRN = 'Z'-40h ;Clear screen MCR = 'L'-40h ;Form Feed (move cursor right) PMCHR = '>' ;Prompt character ERCHR = '?' ;Error character BKS = 08h ;BACKSPACE EOFC = 'Z'-40h ;Eof character (same as cpm) TAB = 09h ;TAB INVV = 80h ;Inverse video bit GESC = 'a' ;Graphics escape toggle char PAGE ; CP/M DEFINITIONS CVER = 22 ;CPM version number CBIOSV = SVER ;cbios version NDSKS = 2 K = 1024 MSIZE = RSIZE-(LVMEM/K) BIOS = MSIZE*k-0600h ;Start of CPM jump table BDOS = BIOS-0E00h+6 ;BDOS entry pt CCP = BIOS-1600h ;CCP entry ABTC = 'C'-40h ;Abort character ALVS: = 0Ch ;*** must be validated with CBIOS *** CSVS: = 20h ;*** --------------------------- *** ;DRIVE DEFINITIONS LSECB = 256 ;Length of a sector MTRK = 40 ;Maximum tracks DMA address ; CPM 2.x functions OS#DIO = 6 ;Console direct I/O OS#GAA = 27 ;Get adr (alloc) OS#SWP = 28 ;Set write protect for disk OS#GRO = 29 ;Get r/o vector OS#SFA = 30 ;Set File attributes OS#GAD = 31 ;Get disk parms address OS#USC = 32 ;Get/set user code OS#RRD = 33 ;Read random OS#WRD = 34 ;Write random OS#FIZ = 35 ;Get file size in records OS#SRD = 36 ;Set random record OS#RDRV = 37 ;Reset drive OS#ADR = 38 ;Access drive OS#FDR = 39 ;Free drive OS#WRZ = 40 ;Write random with zero fill ; Direct BIO call definitions BCONST = 2*3 ;Console status BCONIN SET 3*3 BCOUT = 4*3 ;Console output BLIST = 5*3 ;List output BPUNCH = 6*3 ;Punch output BRDR = 7*3 ;Reader input BHOME = 8*3 ;Home disk drive BSDSK = 9*3 ;Select Disk drive BSTTRK = 10*3 ;Set track BSTSEC = 11*3 ;Set sector BSTDMA = 12*3 ;Set DMA BREAD = 13*3 ;READ disk pru BWRITE = 14*3 ;WRITE disk pru BLSTS = 15*3 ;List LST: device status BSECTR = 16*3 ;Sector translate routine ; ExtensioTO A,ROMRAM EI ENDM BRTBIT: = 80h ;set brt/dim memory BRIGHT DIMBIT: = 00h ;set brt/dim memory DIM ENADIM MACRO OUT 2 ENDM DISDIM MACRO OUT 3 ENDM PUSHAL MACRO PUSH BC PUSH DE PUSH HL ENDM POPALL MACRO POP HL POP DE POP BC ENDM ; Endx SYSTEXT TCTR = 1 ;REPEAT COUNT ROW0_M = 81H ;MASK FOR ADDRESSING ROW 0 DB_CT = 1 ;DEBOUNCE COUNT IRPTCT = 24 ;INITIAL REPEAT COUNT (400MS) SRPTCT = 6 ;SECOND REPEAT COUNT (100MS) TOT_ROW = 7 ;TOTAL ROWS CTL_KY = 2 ;COLUMN NUMBER OF CTL,ALPHA AND SHIFT KEYS ALPH_KY = 3 SHFT_KY = 4 SLD_RCT = 3 ;REPEAT COUNT FOR SLIDE KEYS (50MS APPROX) ; DI and EI are macros so all usages can be accounted ; for in the cr. DI MACRO DB 0F3h ENDM EI MACRO DB 0FBh ENDM ; Macros to enable ROM, RAM and DIM code ENAROM MACRO DI OUT 0 LDK A,0 STO A,ROMRAM EI ENDM DISROM MACRO DI OUT 1 LDK A,1 SMSEC = 10 ;Maximum Sectors FMTCHR = 0E5h ;Data format char ; Set CP/M symbols. IOBYTE = 3 iob_default = 80h ;default value for iobyte CDISK = 4 ;Current logged-in CPM disk SYS = 5 ;CPM Monitor call address SYSL = SYS+1 ;(lwa of CP/m) TIMPTR = 40h ;points to where time kept DBUF = 80h ;Default disk buffer OM#LRAM = SYSL SYSR = 0 ;return to system SYSDSK = CDISK ;Contains current system disk # SYSDAT = 10h ;Date in ddmmyy SYSTIM = SYSDAT+3 ;Time in hhmmss ORGP = 100h ;Transient program origin ;EOL ;does not exist in CP/M EOM = '$' ;End of Message (for system function) EOF = 'Z'-40h ;End of File L$PRU = 128 ;Bytes per floppy sector FCB = 5Ch ;SYSTEM DEFAULT FILE CONTROL BLOCK FCB_NR = FCB+32 ;Next Record = LRN F_EX = 12 ;Extend field F_S2 = 14 ;CPM system usage **used with 2.0 SIZ funct** F_RC = 15 ;Record count (0 to 128) F_NR = 32 ;Next record ordinal in FCB F_RRF = 33 ;r0,r1,r2 (random field) in FCB L_FILEN = 8 ;length of File name L_FILEX = 3 ;lengtns added by SORCIM BRRXT = 17*3 ;Ram-Rom exit BROMJP = 18*3 ;Rom jump entry BFMTJP = 19*3 ;Format Track BSBAUD = 20*3 ;Set Serial Port Baud Rate ;BRDXY = 21*3 ;Read XY ;BWTXY = 22*3 ;Write XY ; IEEE-488 calls BIECO = 21*3 ;Control Out BIESI = 22*3 ;Status In BIEGTS = 23*3 ;Go To Standby BIETC = 24*3 ;Take Control BIEOIM = 25*3 ;Output Interface Message BIEODM = 26*3 ;Output Device Message BIEIDM = 27*3 ;Input Device Message BIEPP = 28*3 ;Parallel Poll ;BIOCAL Used to make calls directly to BIO BIOCAL MACRO NAME LXI HL,L%K PUSH HL ;return LHLD SYSR+1 MVI L,%1 PCHL L%K: ENDM space 4,10 ;SYSCAL ; This macro is to be used when the common deck ; SYS.ACD is used to make file action ; requests. SYSCAL MACRO NAME,ARG .1 IF ' %2' <> ' ' LXI DE,%2 .1 ENDIF CALL %1 ENDM space 4,10 ; SYSTEM This macro provides a mechanism to call the ; system directly. SYSTEM MACRO FUN,ARG .2 IF DEF OS#%1 MVI C,OS#%1 .1 IF ' %2' <> ' ' LXI DE,%2 .1 ENDI# TITLE 'BMRAM - Debug Monitor RAM Storage.' ;MRAM - Debug Monitor RAM Storage. ; Used to assembly ROM resident and CBIOS ORG MRAM ; Host disk xfer buffer and... ; Format track template holding buffer HSTBUF: IF DEF FMTDAT RMTDAT DS 0 DS (FMTSD-FMTDAT) RFMTSD DS 0 DS (FTRKN-FMTSD) RFTRKN DS 0 DS (FSECN-FTRKN) RFSECN DS 0 DS (FDAM-FSECN) RDAM DS 0 DS LSECB+1 RCRC2 DS 0 DS FMTEND-FCRC2 DS (256+128)-(*-HSTBUF) ELSE DS 256+128 ENDIF ; Directory Buffer DIRBUF: = HSTBUF+256 TEM DS 6 RNDV = TEM+1 ;random number seed ERCNT = RNDV+1 ;DW ERCNT RTRC = ERCNT+2 ;retry count RTRY = RTRC+1 MPCHR DS 1 ;prompt character ECHOP DS 1 ;=0, list ehco off ROMRAM DS 1 ;0= RAM, 1= ROM DSTSB DS 6 ;Disk status bytes ; Disk operation temps and control DMADR DS 2 ;Address for read/write Disk DMAADR DS 2 ;CBIOS, users DMA ; Note order of xxxSEC,xxxTRK,xxxDSK must be maintained ; along with length (1,2,1). SEKDEL: DS 1 ;Set for seek-restore command in ROM ;depends oA:, B=B: ; =1, all inverted, A=B:, B=A: DSKSWP DS 1 ; Z80 Alternate Register Set ALIGN 10h RAGS: DESAX: DS 2 ;DE` BCSAX: DS 2 ;BC` AFSAX: DS 2 ;AF` HLSAX: DS 2 ;HL` IXSAX: DS 2 ;IX IYSAX: DS 2 ;IY IVSAX: DS 2 ;Interrupt page register ; 8080 Register Save Area. ALIGN 10h REGS: ESAVE: DS 1 ;E Register save location DSAVE: DS 1 ;D Register save location CSAVE: DS 1 ;C Register save location BSAVE: DS 1 ;B Register save location FSAVE: DS 1 ;FLAGS save location ASAVE: DS 1 ;A Register save location LSAVE: DS 1 ;L Register save location HSAVE: DS 1 ;H Register save location PSAVE: DS 2 ;PGM COUNTER save location SSAVE: DS 2 ;USER STACK pointer save location IF (high *) <> (high ROMSTK) .9 DW *, STK, must be equal ENDIF ;TEMP: DS 1 ;TEMPORARY MONITOR CELL BKPA: DS 2 ;last breakpoint address BKPC: DS 1 ;Contents of bkp IF * >= LWAMEM MSG 'RAM temporary area too large ',* .9 ERROR ENDIF IF * > INTBL .9 ERROR MSG 'RAM overflow into Int Vector ' TITLE 'Osborne One Disk Test program.' ;REV = B ;ORG IS REV A ;DATE = 231 ; +-----------------------+ ; | | ; | DISKTEST | ; | | ; +-----------------------+ ; ; PRMCHR = ':' ;prompt char ROMJMP = BIOS+BROMJP ;Rom callable routine jump adr BUFFS = 4000h ;FWA after rom, must be in RAM RCO = 1 ;Read console WCO = 2 ;Write console RCB = 10 ;Read console (buufered) GCNS = 11 ;get console status RDS = 13 ;Reset disk system ; Assembly Constants DELCNT = 5000 ;Delay count MRTRY = 5 ;Maximum retries of r/w op Link bmram.asm page org 0100h RESTRT: PROC LDK SP,STK LK A,MSEC STO A,MSECD ;max #sectors/track CALL PARG ;process command args LDK HL,IMSG CALL OSTR ;initial message LD A,SDISK ;pass active disk CALL SDRV ;select CALL CERF ;clear all errors JMP DSKTR ; Here if restart DSKT1: LDK SP,STK LDK HL,IMSG CALL OSTR ;output message DSKTR: CALL RDRV ;reset drive ; Master loop of Diagnostic :2: CALL GNC ;GET COn disk type. Siemens = 3h, MPI = 0h SAVSEC DS 1 ;last sector requested SAVTRK DS 2 ;last track requested SDISK DS 1 ;Selected disk drive (0,1) ACTSEC = SAVSEC ACTTRK = SAVTRK ACTDSK = SDISK SEKSEC DS 1 SEKTRK DS 2 SEKDSK DS 1 HSTSEC DS 1 HSTTRK DS 2 HSTDSK DS 1 TEMSEC DS 1 ;Used in bios only RDFLAG DS 1 ;Read flag ERFLAG DS 1 ;Error reporting WRTYPE DS 1 ;Write operation type ALV: DS ALVS CSV: DS CSVS ; BIOS blocking-deblocking flags HSTACT: DS 1 ;host active flag HSTWRT: DS 1 ;Host written flag UNACNT: DS 1 ;Unalloc rec count UNATRK: DS 2 ;Track UNASEC: DS 1 ;Sector LOGSEC: DS 1 ;Logical sector LDADR DS 2 KEYLCK DS 1 ;Zero if locked keyboard CURS DS 2 ;current cursor position ; Keyboard scan temporaries TKEY DS 1 ;Tem holding key HKCNT DS 1 ;Debounce key LKEY DS 1 ;Last valid keystroke CKEY DS 1 ;Last control key ESCH DS 1 ;ESC holding flag ;PIAAD and PIABD must be kept sequential, PIAAD first ;dependency in VC_HOME of BMKEY.asm PIAAD: DS 1 ;Holds last, * ENDIF ; Interrupt Jump Vector is between EFFO, EFFF. PAGE ; Endx MRAM SAX: DS 2 ;BC` AFSAX: DS 2 ;AF` HLSAX: DS 2 ;HL` IXSAX: DS 2 ;IX IYSAX: DS 2 ;IY IVSAX: DS 2 ;Interrupt page register ; 8080 Register Save Area. ALIGN 10h REGS: ESAVE: DS 1 ;E Register save location DSAVE: DS 1 ;D Register save location CSAVE: DS 1 ;C Register save location BSAVE: DS 1 ;B Register save location FSAVE: DS 1 ;FLAGS save location ASAVE: DS 1 ;A Register save location LSAVE: DS 1 ;L Register save location HSAVE: DS 1 ;H Register save location PSAVE: DS 2 ;PGM COUNTER save location SSAVE: DS 2 ;USER STACK pointer save location IF (high *) <> (high ROMSTK) .9 DW *, STK, must be equal ENDIF ;TEMP: DS 1 ;TEMPORARY MONITOR CELL BKPA: DS 2 ;last breakpoint address BKPC: DS 1 ;Contents of bkp IF * >= LWAMEM MSG 'RAM temporary area too large ',* .9 ERROR ENDIF IF * > INTBL .9 ERROR MSG 'RAM overflow into Int Vector 'MMAND CALL GOTO ;Go to processor JMP :2 ;continue GOTO: JMP [hl] EXIT: JMP 0 ;warm boot IMSG: DB 1AH ;CLEAR SCREEN DB CR,LF,LF DB ' Disk Diagnostic - DTEST' DB CR,LF DB ' REV B 231 TEST ONLY' DB CR,LF,LF,LF DB 'Legal commands for disk test are:',CR,LF DB '0 - Confidence Test.',CR,LF DB '1 - Seek test.',CR,LF DB '2 - Read/Display track,sector.',CR,LF DB '3 - Read test.',CR,LF DB '4 - Write verify read test.',CR,LF DB '5 - Random seek, read/write.',CR,LF DB CR,LF DB 'C - Clear error count.',CR,LF DB 'D - Select drive.',CR,LF DB 'E - Display and clear error count.',CR,LF DB 'H - Help me.',CR,LF DB 'R - Restart test.',CR,LF DB 'S - Select STEP rate for disk.',cr,lf DB 'X - Exit back to CPM.',CR,LF DC LF PAGE GNC: ;GET COMMAND CHARACTER ; ENTRY NONE ; EXIT A= NEXT COMMAND CHARACTER ; HL=ADDRESS FOR COMMAND PROC LDK HL,CMDP CALL NLSTR ;PROMPT USER :1: MVI C,RCO ;GET COMMAND CALL BDOS CMP ' ' JZ :1 ;IF LEADING  PIA-A data PIABD: DS 1 ;Holds last PIA-B data ; Calendar month, day year IDAY DS 3 IMONTH = IDAY+1 IYR = IDAY+2 ; Wall clock time cells and disk active ; see UPTIM: in BMKEY.asm HOURS: DS 6 MINS: = HOURS+1 SECS: = HOURS+2 SEC6: = HOURS+3 ; Used to deselect drive when there is NO activity ; on drive for n seconds. See FDSK routine DACTVE: = HOURS+4 ;=0 by FDSK, Used by UPTIM BELCNT: = HOURS+5 ;^G bell timer cell LLIMIT DS 1 ;max #columns in a logical line ; MSG 'LLIMIT = ',LLIMIT,'h.' ; Disk drive current positions LDSEL: DS 2 ;Last selected drive LDTRK = LDSEL+1 ;Last track used for non-selected drive IESTK: DS 2 ;save current stk ptr ; Interrupt stack DS 20*2 ISTK: DS 0 ; Stack entry DS 20*2 BIOSTK: ROMSTK: DS 0 ACIAD: DS 1 ;last command byte written to ACIA R179x: DS 4 ;179x register save area KBDLY: DS 1 ;keyboard debounce-delay cell ;since CP/M CANNOT boot off B:, this cell is used ;to invert the names of the 2 drives: ; =0, all normal, A=$BLANK CMP '0' JM :10 ;IF NOT NUMBER CMP '9'+1 JP :10 ;IF NOT NUMBER CMP '5'+1 JP GNC ;IF ILLEGAL OPTION SUB '0' RLC MOV E,A LDK D,0 LDK HL,TNUMC ADD HL,DE LD E,[HL] INC HL LD D,[HL] ;GET ADDRESS IN DE EX DE,HL RET CMDP: PRMSIZ: DB ' ' PRMDRV: DB 'A' DC ':' ; HERE IF NOT NUMBER COMMAND ; CHECK FOR ALPHA :10: CALL NORM ;NORMALIZE CHAR :11: CMP '?' JNZ :13 ;IF NOT HELP :12: LDK HL,IMSG CALL OSTR JMP GNC ;TRY AGAIN :13: CMP 'H' JZ :12 ;IF HELP ; TEST FOR SELECT DRIVE LDK HL,SDRVA CMP 'D' RZ ;IF SELECT DRIVE ; TEST FOR RESET CMP 'R' JZ RESTRT ;IF RESTART ; TEST FOR SELECT CMP 'S' LDK HL,SELSTP RZ ;IF SELECT MODE ; TEST FOR OUTPUT TOTAL ERRORS CMP 'E' LDK HL,OTEC RZ ;IF OUTPUT ERRORS CMP 'X' JZ EXIT ;IF EXIT BACK TO CPM CMP 'C' LDK HL,CERF RZ ;IF CLEAR ERROR COUNT LDK HL,DSKT1 RET TNUMC: ;COMMAND JUMP TABLE DW TCONF ;CONFIDENCE TEST DW TSEK DW TRDIS DW TREAD DW TWRT  HL,DSKB2 STO HL,BUFADR ;SET READ BUFFER DIFFERENT CALL RSEC ;NOW READ BACK ; NOW COMPARE BUFFERS FOR EQUAL LD HL,LSECD MOV B,H MOV C,L LDK DE,DSKB1 LDK HL,DSKB2 :7: LD A,[DE] CMP [HL] JZ :9 ;IF EQUAL ; *** OUTPUT DATA ERROR *** CALL WERR ;INDICATE WRITE ERROR :9: INC DE INC HL DEC BC MOV A,B OR C JNZ :7 ;IF NOT DONE ; CHECK FOR ALL SECTORS LD A,MSECD MOV C,A LD A,SAVSEC CMP C JNZ :6 ;IF NOT ALL SECTORS ; CHECK FOR ALL TRACKS LDK A,1 STO A,SAVSEC ;RESET SECTOR # LD A,SAVTRK CMP MTRK-1 JNZ :5 ;IF NOT DONE LDK HL,TWRTM CALL OSTR RET TWRTM: DB CR,LF DC 'WRITE VERIFY READ TEST COMPLETE.' TPAT: DB 0F0H DC 'TEST PATTERN IS : ' TRKM: DB CR DC 'TRACK # - ' SPACE 4,10 WSEC: ;WRITE A SECTOR PROC LDK HL,RTRYC STO 0,[HL] ;CLEAR RETRY COUNT :2: CALL SDMA ;SET DMA LD A,SAVTRK CALL DOSEEK LDK DE,BWRITE+ROMVEC LDK B,1 ;ONE SECTOR CALL ROMJMP ;PERFORM WRITE OR A RZ ;IF GOOD READ :3: STO A,STSBYT ;FOR OERR VE KNOWN SDRVA: LDK HL,SDRVM CALL NLSTR ;SEND FORMAT REQUEST MVI C,RCO ;READ CONSOLE CALL BDOS CALL NORM SUI 'A' JM SDRVA ;IF ILLEGAL CMP 'D'+1-'A' JP SDRVA ;IF ILLEGAL :2: STO A,SDISK ;SET ACTIVE DISK ADI 'A' STO A,PRMDRV ;SET INTO PROMPT ; TRY AND DETERMINE DENSITY OF DEVICE LDK HL,LSECB STO HL,LSECD ;SET LENGTH TO 256 JMP SELFMT SDRVM: DB 'SPECIFY DRIVE (A: - B:) :',BKSCHR DC BKSCHR SELFMT: ;SELECT DISK MODE FORMAT ; FORMAT ALWAYS 256 BYTE SECTORS. ; SET MAX SECTORS/TRACK ; NOW SET MAX SECTOR BUFFER SIZE ; SET BUFFER LENGTH LD A,SDISK MOV C,A STO A,SDISK SELDSK: LD HL,SYSR+1 LDK L,BSDSK LDK E,0 ;FORCE RESET JMP [HL] ;SELECT DRIVE SPACE 4,10 SELSTP: ;SELECT STEP RATE FOR DISK LDK HL,SEKTMG CALL NLSTR ;PROMPT USER CALL GNUM ;GET NUMBER RC ;IF ERROR AND 3 ;MAXIMUM STO A,SEKDEL ;SET DELAY RET SEKTMG: DB 'SELECT STEP RATE FOR DRIVES:' DB CR,LF,' 0 = 5 MILLISECOND (MPI)' DB CR,LF,' 1 = 10 MILLISECOND' DB CR, DW TRSEK PAGE TCONF: ;CONFIDENCE TEST LDK A,MTRK-1 CALL DOSEEK ;SET TO MAX TRACK LDK A,0 CALL DOSEEK ;SET TO 1ST TRACK CALL TWRT ;PERFORM WRITE/READ TEST CALL TSEK ;PERFORM SEEK READ TEST CALL TREAD ;PERFORM READ TEST CALL TRSEK ;PERFORM RANDOM SEEK CALL OTEC ;DISPLAY ERROR COUNTERS RET SPACE 4,10 TSEK: ;SEEK AND READ TEST PROC LDK A,1 STO A,SAVSEC ;SET BEGINNING SECTOR LDK A,3 :1: STO A,CTRK CALL DOSEEK ;PERFORM SEEK CALL RSEC ;READ LD A,CTRK MOV C,A LDK A,MTRK+2 SUB C CALL DOSEEK ;PERFORM SEEK CALL RSEC ;READ DATA LD A,CTRK INC A CMP MTRK JNZ :1 ;IF NOT DONE LDK HL,TSEKM CALL OSTR RET TSEKM: DB CR,LF DC 'SEEK TEST COMPLETE.' CTRK: DB 0 ;CURRENT TRACK SPACE 4,10 TRSEK: ;TEST RANDOM SEEK WITH READ PROC LD A,MSECD STO A,NSECS ;SET TO FULL TRACK LDK A,100 :2: STO A,CTRK ;MAX TRIES CALL GNRN ;GET NEXT TRACK STO A,SAVTRK ;SET TRACK CALL RSEC ;READ CALL GNRN ;GET NEXT RANDOM NUMBER OR 1 ;DISALLOW 0  CALL USOFE ;UPDATE SOFT ERRORS CMP MRTRY JNZ :2 ;IF NOT MAX ; HERE ON ANY TYPE OF WRITE ERROR WERR: PUSH HL LD HL,WERCNT INC HL STO HL,WERCNT POP HL JMP OERR ;UPDATE ERRORS SPACE 4,10 TREAD: ;TEST READING SECTORS PROC CALL RDRV ;RESET DRIVE LD A,MSECD STO A,NSECS ;READ WHOLE TRACK LDK A,0 :2: INC A STO A,SAVTRK CALL RSEC ;READ NEXT TRACK LD A,SAVTRK CMP MTRK-1 JNZ :2 ;IF NOT DONE LDK HL,TRDM CALL OSTR RET TRDM: DB CR,LF DC 'READ TEST COMPLETE.' SPACE 4,10 RSEC: ;READ SECTOR ; EXIT CBIT SET IF READ ERROR PROC LDK HL,RTRYC STO 0,[HL] ;CLEAR RETRY COUNT :2: CALL SDMA ;SET DMA LD A,SAVTRK CALL DOSEEK ;SEEK TO TRACK LDK DE,BREAD+ROMVEC LDK B,1 ;ONE SECTOR CALL ROMJMP ;PERFORM READ OR A RZ ;IF GOOD READ :3: STO A,STSBYT ;FOR OERR CALL USOFE ;UPDATE SOFT ERRORS CMP MRTRY JNZ :2 ;IF NOT MAX LD HL,RERCNT INC HL STO HL,RERCNT CALL OERR ;UPDATE ERRORS STC ;INDICATE ERROR RET RTRYC: DB 0 ;RETRY COUNLF,' 2 = 20 MILLISECOND (SIEMEMS)' DB CR,LF,' 3 = 30 MILLISECOND' DB CR,LF,'ENTER NUMBER (0-3) ' DC ': ' PAGE RDRV: ;RESET DRIVE PERFORMING ON ; EXIT A = 0, TRANSFER OK LDK HL,0 STO HL,SAVTRK ;TRACK TO 0 LK A,1 STO A,SAVSEC ;SET SECTOR = 1 LDK A,5 STO A,NSECS LDK HL,DSKB1 STO HL,BUFADR ;SET FWA OF LOAD LDK A,0 STO A,BUFADE CALL RCAL ;RECALIBRATE DRIVE 0 RET PAGE ; OUTPUT TOTAL ERROR COUNT ; EXIT ERROR COUNT CELLS CLEARED OTEC: LDK HL,OTMSG CALL NLSTR LDK HL,OTRD CALL NLSTR LD HL,RERCNT CALL NOUT ;OUTPUT COUNTER LDK HL,OTWT CALL NLSTR LD HL,WERCNT CALL NOUT ;OUTPUT WRITE ERRORS LDK HL,OTSK CALL NLSTR LD HL,SERCNT CALL NOUT ;OUTPUT SEEK ERRORS LDK HL,OSFE CALL NLSTR LD HL,SOFCNT CALL NOUT ;OUTPUT SOFT ERRORS RET CERF: ;CLEAR ALL ERROR COUNTERS PROC LDK HL,0 STO HL,RERCNT ;CLEAR READ ERROR COUNT STO HL,WERCNT ;CLEAR WRT ERROR COUNT STO HL,SERCNT ;CLEAR SEEK STO HL,SOFCNT ;CLEAR SOFT ERRORS RET OTM LDK B,0 MOV C,A CALL DELAY ;DELAY LD A,CTRK DEC A JNZ :2 ;IF NOT DONE LDK HL,TRSEKM CALL OSTR RET TRSEKM: DB CR,LF DC 'RANDOM SEEK, READ/WRITE TEST COMPLETE.' SPACE 4,10 TWRT: ;TEST WRITING SECTORS ; NOTE: THIS TEST TAKES ABOUT 8 MINUTES TO RUN TO ; COMPLETION ON A FLOPPY DISK. PROC CALL RDRV ;RESET DRIVE LDK A,1 STO A,NSECS ;SET # OF SECTORS TO 1 ; GET TEST PATTERN LDK HL,TPAT CALL GNUM ;GET TEST PATTERN LD A,TPAT LD HL,LSECD EX DE,HL ;LENGTH TO DE LDK HL,DSKB1 MOV C,A ; FILL WRITE BUFFER WITH TEST PATTERN :2: STO C,[HL] INC HL DEC DE MOV A,D OR E JNZ :2 ;IF NOT DONE WITH FILL LDK A,-1 ; WRITE TRACK LOOP :5: INC A STO A,SAVTRK STO A,DSKB1 ;SET TRACK AS FIRST BYTE LDK HL,TRKM CALL OSTR ;OUTPUT TRACK MESSAGE LD HL,SAVTRK CALL NOUT ;OUTPUT TRACK LD A,SAVSEC DEC A ; WRITE SECTOR LOOP :6: INC A STO A,DSKB1+1 ;SET SECTOR AS 2ND BYTE STO A,SAVSEC LDK HL,DSKB1 STO HL,BUFADR CALL WSEC ;WRITE SECTOR LDKT MVI C,GCNS CALL BDOS OR A JNZ DSKTR ;IF ABORT SOFCNT: DW 0 ;SOFT ERROR COUNT SERCNT: DW 0 ;TOTAL SEEK ERRORS RERCNT: DW 0 ;READ ERROR COUNT WERCNT: DW 0 ;WRITE ERROR COUNT LSECD: DW 0 ;LENGTH OF SECTOR SPACE 4,10 TRDIS: ;TEST READ AND DISPLAY PROC LDK A,1 STO A,NSECS LDK HL,TRKN LD A,[HL] STO A,SAVTRK CALL GNUM ;GET TRACK JC :2 ;IF ERROR STO A,SAVTRK :2: LDK HL,SECN LD A,[HL] STO A,SAVSEC CALL GNUM ;GET SECTOR TO READ JC :4 ;IF ERROR STO A,SAVSEC :4: LDK HL,DSKB1 STO HL-BUFADR CALL RSEC LDK HL,DSKB1 LD HL,LSECD EX DE,HL ;LENGTH LDK HL,DSKB1 CALL DMEM ;DISPLAY BUFFER LDK HL,TDMSG CALL NLSTR CALL GANS ;GET ANSWER JNC TRDIS ;IF AGAIN RET TDMSG: DC 'AGAIN ? (Y) : ' TRKN: DB 0 DC 'ENTER TRACK TO USE (0..39): ' SECN: DB 1 DC 'ENTER SECTOR TO USE (1..10): ' PAGE ; SELECT DRIVE ; ENTRY A= -1, REQUEST DISK, ELSE USE A AS DISK DRIVE ; CHECK FOR REPEAT OF PREVIOUS FORMAT, 2**7 ON. SDRV: PROC CMP -1 JNZ :2 ;IF DRI%SG: DC 'ERROR COUNTERS ARE: ' OTRD: DC ' READ ERRORS : ' OTWT: DC ' WRITE ERRORS : ' OTSK: DC ' SEEK ERRORS : ' OSFE: DC ' SOFT ERRORS : ' SPACE 4,10 USOFE: ;UPDATE SOFT ERROR COUNT ; EXIT RTRYC UPDATED LD HL,SOFCNT INC HL STO HL,SOFCNT LD A,RTRYC INC A STO A,RTRYC RET SPACE 4,10 OERR: ;OUTPUT ERROR ; ENTRY STSBYT = STATUS BYTE PROC CALL CKABT ;CHECK FOR ABORT PUSH HL ! PUSH DE ! PUSH BC LDK HL,ERMSG CALL NLSTR ;INDICATE ERROR LD A,STSBYT CALL HOUT ;OUTPUT IN HEX ; LDK HL,STMSG ; CALL OSTR :4: POP BC ! POP DE ! POP HL RET ERMSG: DC ' ERROR, STATUS= ' STSBYT: DB 0 ;STATUS SPACE 4,10 OSTS: ;OUTPUT STATUS TO TERMINAL ; ENTRY A= STATUS BYTE PUSH BC MOV B,A ;SAVE STATUS STO A,[DE] ;SAVE IN TABLE INC DE ;UPDATE POINTER MOV C,A LD A,OUTF OR A JZ :3 ;IF NO DIRECT OUTPUT MOV A,C ;STATUS BYTE CALL HOUT ;OUTPUT BYTE IN HEX :3: POP BC RET ; DDTYP - DETERMINE FLOPPY DISK TYPE. ; ALWAYS 256 BYTE SECTORS. ; EXIT A = 0  ANI 0FH CALL OHN ;OUTPUT LOW RET SPACE 4,10 ; OUTPUT HEX NIBBLE. OHN: ADI 90H DAA ACI 40H DAA JMP CONOUT SPACE 4,10 NLSTR: ;OUTPUT NEWLINE FOLLOWED BY MESSAGE PUSH HL CALL CRLF POP HL ; JMP OSTR OSTR: ;OUTPUT MESSAGE TO CONSOLE ; ENTRY HL= FWA OF MESSAGE LD A,[HL] OR A ;CHECK SIGN BIT PUSH AF AND 07FH ;STRIP END INDICATOR MOV C,A CALL CONOUT ;OUTPUT CHAR POP AF INC HL JP OSTR ;IF NOT DONE RET SPACE 4,10 GANS: ;GET ANSWER TO 'Y' QUESTION ; EXIT CBIT SET IF NO. MVI C,RCO CALL BDOS CALL NORM CMP 'Y' STC RNZ ;IF NO CONFIRMATION CLC RET SPACE 4,10 CRLF: ;OUTPUT NEW LINE ; MUST PERSERVE ALL REGISTERS LDK A,CR CALL CONOUT LDK A,LF JMP CONOUT SPACE 4,10 O2SP: ;OUTPUT 2 SPACES CALL OSP OSP: ;OUTPUT ONE SPACE LDK A,' ' ; JMP CONOUT SPACE 4,10 CONOUT: ;OUTPUT CHARACTER TO CONSOLE ; ENTRY A=CHARACTER ; EXIT DE=DE,BC=BC,HL=HL PUSH HL ! PUSH DE ! PUSH BC MOV E,A MVI C,WCO CALL BDOS POP BC ! POP DE ! POP HL  POP BC INR C ;UPDATE COUNTER JC :20 ;IF END FOUND, OUTPUT REMAINING ASCII INX HL ;IF MORE TO GO, POINT TO NEXT LOC TO DISPLAY MOV A,L ;GET LOW ORDER BITS OF NEW ADDRESS ANI NEWLN ;CHECK FOR EOL JNZ :10 ;IF NOT AT END OF LINE ; NOW CHECK TO SEE IF PAD NEED TO ALIGN ASCII TEXT ; C= COUNT OF BYTES THIS LINE :20: MOV A,C ANI NEWLN JZ :21 ;IF AT END OF LINE MOV L,A ;PAD LENGTH :20A: MOV A,L INR L ANI NEWLN JZ :21 ;IF ALL DONE CALL O2SP ;OUTPUT TWO SPACES ; CALL OSP ;OUTPUT THIRD SPACE JMP :20A ;NEXT ; NOW OUTPUT THE ASCII EQUIVALENT OF DATA, FILTERING ; OUT NON-DISPLAYABLE CHARACTERS :21: POP H ;RESTORE FWA CALL O2SP ;OUTPUT 2 SPACES CALL OSP ;OUTPUT 3RD SPACE :22: LD A,[HL] ANI 7FH ;IGNORE PARITY FOR ASCII ECHO MVI C,'_' ;USE FOR ILLEGAL CHARACTER CPI ' ' JC :24 ;IF ILLEGAL CONTROL CHARACTER CPI 7FH JNC :24 ;IF NOT DISPLAYABLE CHARACTER MOV C,A ;OK CHARACTER :24: MOV A,C CALL CONOUT CALL CDEHL INX HL RC ;IF ALL DONE MOV A,L ANI  NRDYM1: DB CR,LF,'DRIVE ' NRDYM2: DB 'X' DC ' NOT READY.' DOSEEK: ; SEEK TO SPECIFIED TRACK ; ENTRY A = TRACK. ; ; EXIT CBIT SET IF SEEK ERROR PROC STO A,SAVTRK RET SPACE 4,10 RCAL: ;RECALIBRATE DRIVE LDK HL,0 STO HL,SAVTRK INC L MOV A,L STO A,SAVSEC ;SET TO 1ST TRACK, SECTOR JMP SELFMT ;SELECT DRIVE SPACE 4,10 SDMA: ;SEND DMA TO CONTROLLER PUSH HL ;SAVE USER'S HL LD HL,BUFADR STO HL,DMADR ;SET DMA ADDRESS POP HL RET PAGE PARG: ;PROCESS COMMAND LINE ARGUMENTS PROC :3: LD A,TBUFF OR A LDK C,-1 ;INDICATE UN-INITIALIZED JZ :4 ;IF NO ARGUMENTS LD A,TBUFF+2 CMP 'A' LDK C,0 ;DEFAULT DRIVE=0 JM :4 ;IF NOT LEGAL DRIVE CMP 'P'+1 JP :4 ;IF NOT LEGAL DRIVE SUB 'A' MOV C,A ;SET DRIVE :4: MOV A,C STO A,SDISK ;SET DRIVE CALL CERF ;CLEAR ERROR FLAGS RET SPACE 4,10 GNRN: ;GET NEXT RANDOM NUMBER ; EXIT A= NUMBER LDK HL,RNDV MOV A,R ;GET REFRESH REG XOR [HL] ;MAKE MAGIC NUMBER AND 3FH ;CLIP UPPER BITS ADD A,16 RET PAGE NOUT: ;OUTPUT DECIMAL NUMBER ; ENTRY HL= NUMBER ; EXIT NUMBER SENT TO CONSOLE LDK DE,TEMN CALL CBD5 LDK HL,TEMN CALL OSTR RET TEMN: DC ' ' SPACE 4,10 ;CBD CONVERT BINARY NUMBER TO ASCII DECIMAL ; WITH LEADING ZERO SUPPRESSION. ; ENTRY HL= VALUE TO CONVERT. ; DE-> FWA TO STORE CONVERSION. CBD5: LK BC,-10000 CALL CBO ;CONVERT TEN-THOUSANDS DIGIT CALL CLZ CBD4: CBD: LK BC,-1000 CALL CBO ;CONVERT THOUSANDS DIGIT CALL CLZ ;CHECK LEADING ZEROS CBD3: LK BC,-100 CALL CBO CALL CLZ CBD2: LK BC,-10 CALL CBO CALL CLZ MOV A,L ADI '0' STAX D LK A,'0' STA CLZA ;RESET TO IGNORE LEADING ZEROS RET SPACE 4,10 ;CBO CONVERT ONE DIGIT TO DECIMAL HEX ; ENTRY (A) = NUMBER CBO: LK A,'0' PUSH DE CBO1: MOV E,L MOV D,H INR A DAD B JC CBO1 ;IF NOT DONE DCR A MOV L,E MOV H,D POP DE RET ;CLZ CHECK FOR LEADING ZEROS AND IF FOUND ; CHANGE TO LEADING SPACES. ; ENTRY (A) = NUMBER CNEWLN JNZ :22 ;IF NOT FULL LINE ; CHECK FOR OUTPUT OF 128 BYTES, IF SO OUTPUT ONE ; BLANK LINE DCR B JNZ :5 ;IF NOT 128 CALL CRLF ;OUTPUT BLANK LINE JMP :4 ;RESET COUNTER CDEHL: ;COMPARE DE TO HL MOV A,D CMP H RNZ MOV A,E CMP L RNZ STC RET PAGE GNUM: ;GET NUMBER FROM USER ; ENTRY HL-> CURRENT VALUE, FOLLOWED BY ; PROMPT MESSAGE STRING ; EXIT CBIT SET IF NO # ; A= LOW OF # PROC PUSH HL INC HL CALL NLSTR ;OUTPUT PROMPT STRING LDK A,'(' CALL CONOUT POP HL PUSH HL LD A,[HL] CALL HOUT ;OUTPUT CURRENT VALUE LDK A,')' CALL CONOUT LDK A,' ' CALL CONOUT ;SPACE CALL RNUM ;READ NUMBER POP HL MOV A,E JC :5 ;IF NO NUMBER STO E,[HL] ;RESET :5: PUSH AF ;SAVE FLAGS CALL CRLF POP AF ;FLAGS AND LOW OF NUMBER RET SPACE 4,10 RNUM: ;READ NUMBER ; EXIT DE= NUMBER ; CBIT SET IF ILLEGAL OR NONE PROC MVI C,RCB LXI D,TEMB CALL BDOS LDK HL,TEMB+1 LD A,[HL] OR A STC RZ ;IF NO DATA MOV C,A INC HL :3: LD A,[HL] ;N CMP MTRK JRC :EXIT ;IF IN RANGE SUB MTRK ;...BRING INTO RANGE :EXIT: STO A,[HL] RET SPACE 4,10 ; DELAY N MILLISECONDS ; ENTRY BC= LENGTH DELAY: PROC :1: LDK A,DELCNT/26 :2: DCX B INX B DCR A JNZ :2 ;IF 1 MILLISECOND NOT ELAPSED DEC BC MOV A,B OR C JNZ :1 ;IF TIME INTERVAL NOT ELAPSED RET SPACE 4,10 PAGE CKABT: ;CHECK FOR ABORT ; EXIT TO MASTER LOOP IF CONSOLE ACTIVE PUSH HL ! PUSH DE ! PUSH BC MVI C,GCNS CALL BDOS POP BC ! POP DE ! POP HL OR A RZ ;IF NO ABORT MVI C,RCO ;TRASH CHAR CALL BDOS JMP DSKTR ;ANEW SPACE 4,10 NORM: ;NORMALIZE CHARACTER UPPER ;ENTRY A= CHAR ;EXIT A= NORMALIZED CHAR CMP 'A' RM ;IF NOT LOWER CASE CMP 'Z'+1 RNC ;IF NOT LOWER CASE SUI 'A'-'A' RET SPACE 4,10 HOUT: ;OUTPUT HEX FOLLOWED BY SPACE CALL HNOUT LDK A,' ' JMP CONOUT ;ONE SPACE HNOUT: ;OUTPUT HEX DATA TO TERMINAL ; ENTRY A= BYTE TO OUTPUT MOV B,A ;SAVE BYTE RRC ! RRC ! RRC ! RRC ANI 0FH CALL OHN ;OUTPUT HIGH MOV A,B LZ: MOV B,A LDA CLZA CMP B JNZ CLZ4 ;IF 1ST LEADING DIGIT FOUND LK A,' ' STAX D CLPLUG: INC DE ;PLUG WITH RET IF NO LEADING SPACES RET CLZ4: MOV A,B STAX D INC DE LK A,0FFH STA CLZA ;SET TO IGNORE LEADING ZERO RET CLZA: DB '0' PAGE DMEM: ;DISPLAY MEMORY ON CONSOLE PROC ; DISPLAY MAIN LOOP ; DISPLAY BYTES TO USER. ; DE= LENGTH ; HL= FWA NEWLN = 16-1 ;LENGTH OF LINE DEC DE ;LENGTH-1 FOR COMPUTE OF LWA EX DE,HL ADD HL,DE EX DE,HL ;SET LWA :4: MVI B,8 ;8 LINES THEN BLANK LINE :5: CALL CRLF ;ECHO CARRIAGE RETURN/LINE FEED MOV A,H SUI HIGH(DSKB1) CALL HNOUT ;HIGH BYTE OF ADR MOV A,L CALL HOUT ;LOW BYTE WITH SPACE FOLLOWING ; CALL O2SP ; CALL OSP ;ONE MORE SPACE PUSH HL ;SAVE STARTING ADR MVI C,0 ;INITIALIZE COUNTER :10: PUSH BC ;SAVE COUNTER MOV A,L RRC CNC OSP ;OUTPUT SPACE IF EVEN BYTE LD A,[HL] ;GET CONTENTS OF NEXT MEMORY LOCATION CALL HNOUT ;DISPLAY CONTENTS CALL CKABT CALL CDEHL ;CHECK FOR END OF REQUEST &EXT CHAR CALL NORM STO A,[HL] ;REPLACE AS UPPER INC HL DEC C JNZ :3 ;IF NOT ALL NORMALIZED STO 0,[HL] ;SET EOS LDK HL,TEMB+2 CALL NUMBR ;CONVERT NUMBER RET TEMB: DB 30 DS 30 SPACE 4,10 ; NUMIN CONVERTS A NUMBER TO ITS VALUE (GARBAGE RESULTS IF ; VALUE EXCEEDS 2**16-1 = 65535). AN OPTIONAL POST-RADIX ; MAY BE SPECIFIED: ; B 2 BINARY ; Q 8 OCTAL ; N 10 DECIMAL ; H 16 HEXADECIMAL ; IF THE STRING CONTAINS NO EXPLICIT RADIX (LAST CHARACTER ; IS NOT ONE OF THE ABOVE), THE CALLER MAY SPECIFY THE ; DEFAULT RADIX. THE CELL NUMBAS SHOULD CONTAIN ASCII ; B, Q, OR H. IF NUMBAS IS NOT DEFINED, OR HAS ANOTHER ; VALUE THEN N (= DECIMAL) IS ASSUMED. AN ERROR IS ; REPORTED IF A DIGIT GREATER THAN THE RADIX APPEARS, OR ; TIE RADIX APPEARS, OR IF THE FIELD IS EMPTY. "DIGIT" MEANS ; IF THE FIELD IS EMPTY. "DIGIT" MEANS '0'..'9' OR ; 'A' .. 'Z'. ;NUMBR CONVERT ASCII NUMBER TO VALUE. ; ENTRY HL-> FIRST CHARACTER OF POSSIBLE NUMBER ; NUMBAS= DEFAULT RADIX SPECIFIER. ER OCT: LD A,[HL] SUB '0' JM DEC8 ;IF CHARACTER BELOW DIGITS CMP 7+1 JP DEC8 ;IF DIGIT EXCEEDS RADIX OR E MOV E,A DEC C INC HL ;NEXT DIGIT JZ DEC5 ;IF ALL DIGITS PROCESSED EX DE,HL ADD HL,HL ADD HL,HL ADD HL,HL EX DE,HL JMP OCT ; CONVERT HEXADECIMAL NUMBER HEX: LD A,[HL] CMP '0' JM DEC8 ;IF NOT NUMBER CMP '9'+1 JM HEX1 ;IF HEX CMP 'A' XOR 020H JM DEC8 ;IF NOT NUMBER CMP ('F'+1) XOR 020H JP DEC8 ;IF NOT NUMBER SUI 7 HEX1: AND 0FH OR E MOV E,A INC HL DEC C ;DECREMENT DIGIT PROCESSED COUNT JZ DEC5 ;IF ALL DIGITS PROCESSED EX DE,HL ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL EX DE,HL JMP HEX ; CONVERT BINARY NUMBER BINN: LDK DE,0 ;INITIALIZE RESULTS BINN1: LD A,[HL] SUI '0' JM DEC8 ;IF ILLEGAL NUMBER CMP 1+1 JP DEC8 ;IF ILLEGAL NUMBER EX DE,HL ADD HL,HL ;LEFT SHIFT 1 OR L ;ADD IN THIS BIT MOV L,A EX DE,HL INC HL ;NEXT USER CHARACTER DEC C ;COUNT-1 JNZ BINN1 ;IF NO;OCCCOPY1.ASM TITLE 'Disk Copy program.' * 4D2004-00 MASTER .ASM * 2D2004-00 ASSY .ASM * 1D2004-00 LISTING .PRN * 4D1004-00 MASTER .COM * 2D1004-00 ASSY .COM ; +-----------------------+ ; | | ; | Disk Copy | ; | | ; +-----------------------+ ;REV = A ;DATE = 280 ;DEB ;RELEASED ; November 23, 1980. VER = '2' LEVEL = '4' ; Updates ; 2-3, 12-28-80 Fixed recovery in RD/WRTSEG to continue ; upon finding error. (rhf) ; 2-4, 2-25-80 Added to code to adjust number of tracks ; to copy for double sided floppies disks. List -F ;skip listing IFfed-out code LINK OCCRAM2.ASM BOOT = 0000h ;Warm start address BIOSLOC = BOOT+1 ;BIOS vector start FSELDSK = 27 ;Offset for SELDSK function FSTRK = 30 ;Offset for SETTRK function FSEC = 33 ;Offset for SETSEC function FSDMA = 36 ;Offset for SETDMA function READF = 39 ;Offset for READ SECTOR function WRITEF = 42 ;Offset for WRITE SECTOR function SYSSEC = 10 ;ditto for OCC1 SYSTART = 0 ;First system track number B ; IF 2**7 SET, PRE-RADIX SPECIFIED. ; IF NUMBAS NOT DEFINED, DECIMAL ASSUMED. ; EXIT CBIT SET IF ERROR ; DE= RESULT VALUE. ; HL-> LAST CHARACTER OF NUMBER. ; PRESERVES BC. NUMBR: PUSH BC PUSH HL LDK DE,0 MOV B,D MOV C,D NUMB1: LD A,[HL] ;GET NEXT SOURCE CHARACTER CMP '0' JC NUMB2 ;IF BELOW DIGITS, END OF FIELD CMP '9'+1 JC NUMB3 ;IF DECIMAL DIGIT CMP 'A' XOR 020H JC NUMB2 ;IF BELOW ALPHA, END OF FIELD CMP ('Z'+1) XOR 020H JNC NUMB2 ;IF NOT ALFABETIC, END OF FIELD NUMB3: INC C ;COUNT "DIGITS" INC HL JMP NUMB1 ;LOOP THRU ALPHANUMERICS ; C= LENGTH OF NUMBER FIELD ; HL-> TERMINATING CHAR NUMB2: DEC HL LD B,[HL] ;B= LAST CHARACTER EX [SP],HL ;STACK ^LAST, HL-> FIRST MOV A,C OR A JZ DEC8 ;IF EMPTY NUMBER, ERROR MOV A,B ;CHECK FOR POST-RADIX DEC C JZ NUMB5 ;IF SINGLE DIGIT CMP 'B' XOR 020H JZ BINN ;IF BINARY CMP 'N' XOR 020H JZ DEC ;IF DECIMAL CMP 'Q' XOR 020H JZ OCT ;IF OCTAL CMP 'H' XOR 020H JZ HEX ;IF HEX T ALL PROCESSED JMP DEC5 PAGE ; TEMPORARIES AND BUFFER OUTF: DB 0 ;CONSOLE OUTPUT FLAG BUFADR: DW 0 ;LSBS OF ADDRESS BUFADE: DB 0 ; DISK ACCESS INFORMATION. ; THIS AREA IS ORGANIZED INTO THE FOLLOWING GROUPS ; DISK DRIVE ; TRACK NUMBER ; SECTOR NUMBER ; DRIVE TYPE ACTTYP: DS 1 ;ACTUAL DISK'S TYPE MSECD: DS 1 ;MAX SECTORS PER TRACK NSECS: DS 1 ;ACTUAL # OF SECTORS TO XFER DS 2*30 STK: DS 0 ; DISK BUFFER ORG BUFFS DSKB1: DS 0 DSKB2: = *+256+1 ;2ND DISK BUFFER ;END L,HL ADD HL,HL ADD HL,HL ADD HL,HL EX DE,HL JMP HEX ; CONVERT BINARY NUMBER BINN: LDK DE,0 ;INITIALIZE RESULTS BINN1: LD A,[HL] SUI '0' JM DEC8 ;IF ILLEGAL NUMBER CMP 1+1 JP DEC8 ;IF ILLEGAL NUMBER EX DE,HL ADD HL,HL ;LEFT SHIFT 1 OR L ;ADD IN THIS BIT MOV L,A EX DE,HL INC HL ;NEXT USER CHARACTER DEC C ;COUNT-1 JNZ BINN1 ;IF NOUFF = 4000h ;OCC1 FWA of ROM-accessible memory SCTLN = 256 ;OCC1 hardware-fixed sector length page ORG 100h COPY: JMP COPY1 ;initializer NAME: DB 'COPY .com' DB CR,LF IF * <> 0111h .9 ERR Wrong length of filename and type. ENDIF DB 'Copyright SORCIM Corp. 1981',CR,LF DB 'S/N-' SN: DB '123456' DB ',Ver ' CPSV: DB 'All' DB ' ' MESV: VFD 16\VER,8\'.',16\LEVEL RELTYP: DB 'ST' ;Release type - EV,UD,DP,etc. DB CR,LF,'Z'-40h ;ascii EOF mark ; Distributor is a fixed value in COPYPROG.com DIST: DB 'OSB ' ;Distributor - DGM,OURS,LBA,etc. OPTYP: DB 'Z80 ' ;Type - Z80,8080,8086,NS,VG,etc. ; Total bytes used to here = 83 decimal. IF * <> 0153h .9 ERR Copyright notice section wrong length. ENDIF COPY1: proc ;Main loop for Disk copy routine ;Initialize the stack pointer LD HL,06H ;LWA OF TPA MOV SP,HL ;SET STACK ;Check if help wanted. LD A,TBUFF OR A JZ COPY2 ;If no arguments LD A,TBUFF+2 CMP '?' JNZ COPY2 ;If no HELP requested LDK DE,HELPM C NUMB5: INC C ; CONVERT DECIMAL NUMBER DEC: MOV A,C CMP 2 JM DEC4 ;ONE DIGIT CONVERSION JZ DEC3 ;TWO DIGITS CMP 4 JM DEC2 ;THREE DIGITS JZ DEC1 ;FOUR DIGITS LDK BC,10000 ;FIVE DIGIT CONVERSION CALL DECC DEC1: LDK BC,1000 CALL DECC DEC2: LDK BC,100 CALL DECC DEC3: LDK BC,10 CALL DECC DEC4: LDK BC,1 CALL DECC ; SET EXIT CONDITIONS FOR GOOD NUMBER DEC5: CLC ;CLEAR ERROR INDICATOR DEC7: POP HL ;REPLY HL-> LAST POP BC RET ; SET EXIT CONDITIONS FOR BAD NUMBER DECE: POP BC ;DISCARD DECC RETURN ADDRESS DEC8: STC ;INDICATE ERROR IN FIELD JMP DEC7 ; PROCESS CONVERSION OF DECIMAL DIGIT ; BC= UNIT VALUE ; HL-> CHARACTER ; EXIT TO DECE IF ERROR ; DE UPDATED DECC: LD A,[HL] SUB '0' JM DECE ;IF CHARACTER BELOW DIGITS CMP 9+1 JP DECE ;IF CHARACTER ABOVE DIGITS INC HL OR A RZ ;IF DIGIT = 0 EX DE,HL DECC1: ADD HL,BC DEC A JNZ DECC1 ;IF NOT DONE EX DE,HL RET ; CONVERT OCTAL NUMB'ALL PRINT JMP BOOT HELPM: DB cr,lf,'Copy will copy disks on the OCC-1' DB cr,lf,'disk subsystem. To start, type:' DB cr,lf,' COPY ' DB cr,lf,'Where is Carriage Return' DB cr,lf,'and is S=system, D=data or' DB cr,lf,'A=all. If not entered, a prompting' DB cr,lf,'message will be output.' DB cr,lf,'The source and destination drive' DB cr,lf,'prompts occur.$' COPY2: LDK DE,LOGON CALL PRINT ;Display signon message :2: CALL GETARG ;Determine amount of disk to copy CALL GETSRC ;Get the source drive CALL GETOBJ ;Get the destination drive LDK DE,GOMSG CALL PRINT ;Prompt USER to load diskettes :4 CALL CONIN CPI CR ;wait for JNZ :4 CALL GETDEN ;Check for compatible media ;for OCC1 just initialize track ;parameters--SPT,LTRK,and start trk :6: LDK DE,CRLF CALL PRINT CALL CALBUF ;Compute buffer address :8: ;Main loop for copying diskettes CALL CPYTRK ;Copy one track LDK HL,TRACK INC [hl] ;next trac, and last track ; for copy ; Internal register use: ; B = start track ; C = ending track LDK E,0 ;Prepare to actually select disks LD A,SOURCE MOV C,A CALL SELDSK LDK E,0 LD A,OBJECT MOV C,A CALL SELDSK LK A,10 ;10 sectors/track STO A,SPT LK C,39 ;tracks per disk :1 MVI B,SYSTART LD A,CFLAG CPI 'A' JZ :2 ;If entire disk LK B,3 ;3rd track is data on OCC1 CPI 'D' JZ :2 ;If data only ;here if system tracks only MOV C,B DCR C MVI B,SYSTART :2: MOV A,C STO A,LTRK MOV A,B STO A,TRACK LK HL,TOCC ;skew table for OCC1 STO HL,SKEWPTR LK A,1 STO A,FUDGE ;no fudging on OCC1 RET space 4,10 ; Calculate the number of available pages for disk ; buffering. CALBUF: LD HL,SYS+1 ;Find bottom of BDOS MVI L,0 ;force page boundary LDK DE,-BUFF ;Subtract address for start of buffer ADD HL,DE MOV A,H ;H now has # PAGES in buffer STO A,BLEN ;save length ORA A RAR ;divide by 2 MOV H,A MOV PRINT ;Display permanent error XRA A STO A,ERRFLG ;Clear error flag POP BC ;restore counters :10: MOV A,B ADD C ;SECTOR = SECTOR + # SECTORS MOV B,A LD A,TSECS ORA A JNZ :6 ;If track complete RET page ; Read a segment of a track ; Entry HL -> FWA of buffer ; Internal usages: ; B = 1st sector to read ; C = Number of sectors to read RDSEG: XRA A ;READ FLAG JMP RWSEG WRTSEG: ;Entry same as RDSEG ORI 0FFH ;WRITE FLAG RWSEG: STO A,RDWRTF ;Store flag STO HL,DMMA ;OCC1 systext-compatible name LK BC, (1 shl 8) + (MSEC) ;B = 1st sector, C = #sectors LK A,3 STO A,RRTRY :retry: PUSH BC ;save sector info LK DE,SCTLN ;DE = length of a sector LD HL,DMMA ;HL = DMA-addr for 1st sector :loop: DEC B ;B = #sectors xferred so far JRZ :2 ;if HL-> next DMA-addr ADD HL,DE ;...else step HL to next sector addr JR :loop ;and repeat :2: MOV B,H ! MOV C,L ;BC = HL CALL SETDMA POP BC ! PUSH BC ;retrieve sector info CALL SETSEC ;set sector to k LD A,LTRK ;get last trk to copy CMP [hl] JNC :8 ;if not last track ; See if user wants to copy another disk LDK DE,CPLMSG CALL PRINT ;indicate to user we are done :10: LDK DE,RPTMSG CALL PRINT ;ask for another disk copy CALL CONIN ;Get answer CPI 'Y' JZ :2 ;If yes, perform again CPI 'N' JZ :12 ;If NO, exit CPI CR JNZ :10 ;If no valid answer, try again COPYX: ;Exit copy routine :12: LDK DE,RMOUNT ;Display remount message CALL PRINT CALL CONIN JMP BOOT page GETARG: proc ;Get type of copy to perform. LDK HL,TBUFF MOV A,M ;Get # byte on command line ORA A JZ :4 ;If no command line data MOV C,A ;save # bytes :2 INC HL ;Get next byte MOV A,M CALL VCPYT ;validate parameter RZ ;If valid, return DCR C ;If NO, dec # bytes JNZ :2 ;if not end, continue :4 LDK DE,CPYMSG CALL PRINT ;Request parameter input CALL CONIN CALL VCPYT ;validate parameter JNZ :4 ;If invalid input RET space 4,10 VCPYT: proc ;Validate copy typeA,L RAR MOV L,A LDK DE,BUFF ;Compute address of second buffer ADD HL,DE STO HL,BUFF2 RET space 4,10 ; COPY a track of data from source drive to ; desination drive. ; ; ENTRY B = next sector to copy. ; C = number of sectors to copy. CPYTRK: proc LDK HL,ERRFLG MOV A,M ;Get ERROR FLAG MVI M,0 ;clear it ORA A LDK DE,CRLF ;new line CNZ PRINT LDK DE,TRACKM CALL PRINT ;output track LD A,TRACK CALL ODEC ;output track # ; Start the copy MVI B,0 ;sec = 0 LD A,TRACK ;Get TRACK number OR A LD A,SPT LD HL,SKEWPTR JNZ :4 ;If not the first cylinder LDK A,SYSSEC LDK HL,T128 :4: STO A,TSECS ;Store # of secs to copy STO HL,SKEWT :6: LDK A,3 STO A,TRYS ;Set retry counter LDK HL,TSECS MOV C,M ;Get number of sectors left to copy MVI M,0 LD A,BLEN CMP C JNC :8 ;If buffer for sectors left MOV D,A MOV A,C SUB D ;TSECS - BLEN MOV M,A ;TSECS = TSECS - BLEN MOV C,D ;# SECTORS = BLEN :8: PUSH BC ;Save sector information LDbegin xfer POP BC ! PUSH BC ;retrieve sector info CALL XFER ;do disk operation OR A ;ck for error POP BC ;restore stack RZ ;if no error, finished ; D = #of sectors awaiting xfer (from ROM-routine) LK A,MSEC+1 SUB D ;A = #of flawed sector MOV B,A ;...will be 1st sector to xfer MOV C,D ;C = #of sectors to xfer PUSH BC ;save info for next xfer CALL RWERR ;notify user POP BC LK HL,RRTRY ;retry counter DEC [hl] JRNZ :retry ;if going to retry @this sector INC B ! DEC C ;...else give up on this sector RZ ;if flawed sector is last sector ;on this track, no others need be copied STO 3,[hl] ;RRTRY := 3 JR :retry ;ignore bad sector, continue space 4,10 RWERR: ;Read or Write error processing routine. ; ; Entry CBIOS indicated hard error, output info. ; Used only from RD/WRT_SEG. ; B = bad sector ; Caller must perserve registers. proc MOV A,B ;get sector # STO A,SECTOR LD A,RDWRTF LDK DE,READMSG ORA A JZ :2 ;If sector read request and STO A,CFLAG ;Save for latter test CMP 'A' RZ CMP 'S' RZ CMP 'D' RZ CMP 'X' JZ COPYX ;if request to abort RET space 4,10 GETSRC: proc ;Find source drive number LDK DE,SDMSG CALL GDISK ;Get drive number in ASCII STO A,ASCISRC MOV A,C STO A,SOURCE RET space 4,10 GETOBJ proc ;Find destination drive number LDK DE,OBJMSG CALL GDISK ;Get drive number in ASCII STO A,ASCIOBJ LD A,SOURCE CMP C JZ GETOBJ ;If source = destination drive MOV A,C STO A,OBJECT RET space 4,10 GDISK proc ;Get a drive from user PUSH D ;Save message address CALL PRINT CALL CONIN ;Get user's input SUI 'A' POP D JM GDISK ;If invalid CPI 'P'-'A'+1 JP GDISK ;If invalid MOV C,A MVI B,0 PUSH D PUSH B LDK DE,1 ;Only do drive validation CALL SELDSK MOV A,H ORA L POP B POP D JZ GDISK ;If invalid drive MOV A,C ADI 'A' RET space 4,10 GETDEN: proc ;Check disks for compatible DPBs ;OCC1 set up sectors/track, start track A,SOURCE ;Get source drive STO A,DISK LDK DE,1 MOV C,A CALL SELDSK ;Select source drive LD A,TRACK CALL SETTRK ;Set track number POP BC ;Restore sector information PUSH BC ;and save it LDK HL,BUFF CALL RDSEG ;Read the "track" LD A,OBJECT ;Get destination drive STO A,DISK MOV C,A LDK DE,1 CALL SELDSK ;Select destination drive LD A,TRACK CALL SETTRK ;Set track number POP BC ;Restore sector information PUSH BC ;and save it LDK HL,BUFF CALL WRTSEG ;Write the track POP BC ;Restore sector information PUSH BC ;and save it LD HL,BUFF2 CALL RDSEG ;Read the written "track" POP BC ;Restore sector information LD A,ERRFLG ORA A CZ COMPSEG ;Verify the "track" JZ :10 ;If compare was good ; Here if possible hard error, update retry ; count and try until found to be permament. LDK HL,TRYS DEC [hl] JNZ :8 ;If retries not exhausted ; Here if hard error, output message and continue ; copying. PUSH BC ;save regs LDK DE,PERMMSG CALL ( LDK DE,WRTMSG :2: CALL PRINT LD A,DISK ;Get disk number ADI 'A' MOV E,A CALL CONOUT ;Display drive LDK DE,TRKMSG CALL PRINT ;Display track LD A,TRACK CALL ODEC LDK DE,SECMSG CALL PRINT ;Display sector LD A,SECTOR CALL ODEC ;output 179x status LK DE,STSMSG CALL PRINT LK DE,4 LK HL,R179x ;179x register save area :loop: PUSH DE ! PUSH HL CALL OSP POP HL ! PUSH HL LD A,[hl] ;get "register" to output CALL OHEX POP HL ! POP DE INC HL ;point @ next "reg" DEC DE ;decrement count MOV A,D OR E JRNZ :loop ;repeat 4 times ORI 0FFh ;Set error idicator STO A,ERRFLG RET space 4,10 ; Compare the data in the two buffers. ; ; B = SECTOR number, C = # SECTORS COMPSEG: PUSH BC ;Save sector information LD HL,BUFF2 LDK DE,BUFF COMP1: MVI B,low SCTLN ;Length of sector COMP2: LD A,[de] ;Get source byte XOR M JNZ CMPERR ;If source <> destination byte INX D INC HL DCR B JNZ COMP2 ;If end of a sector DCR C ;Decrement  RET OHEX: ;PRINT number in ACCUMULATOR in HEX proc PUSH PSW ;save IT RRC ;Get HIGH NIBBLE RRC RRC RRC CALL :2 ;PRINT HIGH NIBBLE POP PSW ;RESTORE number :2: ;output nib in 4 lsbs of A ANI 0FH ;MASK for NIBBLE CPI 10 JC :4 ;if decimal ADI 'A'-'9'-1 ;If NO, add offset for A-F :4: ADI '0' ;make it ascii MOV E,A ;to E for conout ; JMP CONOUT CONOUT MVI C,2 ;CONOUT function JMP SYS O2SP: CALL OSP OSP: LK E,' ' JR CONOUT SELDSK: ;select disk ; ENTRY C=disk number ; if (E and 1) = 0 then actually call ROM select MOV A,E AND 1 LK DE,BSDSK + 100H ;prepare for ROM jump CZ GOROM ;go to ROM if (E and 1)=0 MOV A,C ;otherwise, set SDISK STO A,SDISK ;set ROM-referenced cell RET SETTRK: STO A,SAVTRK ;HIGH RAM RET SETSEC: MOV A,B STO A,SAVSEC ;HIGH RAM RET SETDMA: STO BC,DMADR ;HIGH RAM RET XFER: LK DE,BREAD+100h ;ROM 256-byte Read utility MOV B,C ;B = #sectors to xfer LD A,RDWRTF ORA A JZ GOROM ;if readh of the Read/Write buffer (pages) BUFF2: DS 2 ;Address of second buffer SECTOR: DS 1 ;Sector SBYTE: DS 1 DBYTE: DS 1 TRYS: DS 1 ;Number of retries RDWRTF: DS 1 ;Read/Write flag ;BUFF = $ ;see 1st page ; Endx OCCCOPY1.ASM # of sectors JNZ COMP1 POP BC RET space 4,10 ; Display compare error message. CMPERR: LD A,[hl] ;Get destination byte STO A,DBYTE LD A,[de] ;Get source byte STO A,SBYTE POP HL ;Get sector information PUSH HL ;and save it back PUSH BC ;Save buffer information PUSH HL LDK DE,CMSG1 CALL PRINT ;Display 1st portion of message LD A,TRACK CALL ODEC ;Display track LDK DE,CMSG2 CALL PRINT ;Display next part POP HL ;Compute bad sector POP BC PUSH BC MOV A,L SUB C ADD A,H MOV E,A ;Compute skewed sector number MVI D,0 LDK HL,SKEWT ADD HL,DE LD A,FUDGE ADD A,M CALL ODEC ;Output sector number LDK DE,CMSG3 CALL PRINT ;Display byte in sector MOV A,B CALL ODEC ;Display byte MVI E,' ' CALL CONOUT LD A,SBYTE CALL OHEX ;Output source byte MVI E,' ' CALL CONOUT LD A,DBYTE CALL OHEX POP BC ;Restore sector information ORI 0FFh ;Set error indicator STO A,ERRFLG RET ODEC: ;Output number in decimal ; Entry A= number t operation LK DE,BWRITE+100h ;ROM write routine ; JMP GOROM GOROM: LD HL,BIOSLOC LK L,BROMJP ;call ROM resident routine JMP [hl] LOGON DB CR,LF,'OSBORNE COPY Utility Program Version ' DB VER,'.' DW rev (LEVEL) DB '.$' CPYMSG: DB CR,LF,LF,'Do you want to ',ESC,'l','copy',ESC,'m:' DB CR,LF,' SYSTEM tracks only? (type S)' DB CR,LF,' DATA tracks only? (type D)' DB CR,LF,' ALL of the disk? (type A)' DB CR,LF,' Exit back to System (type X) $' SDMSG: DB CR,LF,LF,'Source drive? (A, B ) $' OBJMSG DB CR,LF,'Destination drive? (A, B ) $' GOMSG DB CR,LF,LF,'Put source disk on ' ASCISRC DB 'A' DB CR,LF,'Put destination disk on ' ASCIOBJ DB 'B' DB CR,LF,'Then type $' CRLF DB CR,LF,'$' TRACKM DB CR,'Track $' DENMSG DB CR,LF,LF,'Disk have incompatible formats.$' CPLMSG DB CR,LF,'COPY complete.$' RPTMSG DB CR,LF,LF,'Do you want to COPY again? (Y/N) $' READMSG DB CR,LF,'Read error on drive $' WRTMSG DB CR,LF,'Write error on drive $' TRKMo convert proc LDK BC,0 ;init dec number :2 SUI 10h ;high nibble JC :4 ;If yes, CARRY is set, output low only MOV D,A ;save HEX number MOV A,C ;Get 2 least SIGNIF. BCD digits ADI 16H ;ADD 16 DAA ;DECIMAL ADJUST MOV C,A ;STORE THEM MOV A,B ;Get OVERFLOW digit ACI 0 ;ADD CARRY (OVERFLOW) MOV B,A ;STORE OVERFLOW digit MOV A,D ;RESTORE HEX number JMP :2 :4 ADI 10H ;RESTORE HEX number to RANGE of 0-0FH ORA A DAA ;MAKE it DECIMAL ADD C ;ADD BCD digits to LOW NIBBLE DAA ;DECIMAL ADJUST MOV C,A ;STORE BACK in C MOV A,B ;Get OVERFLOW digit ACI 0 ;ADD in CARRY (OVERFLOW) JZ :6 ;DON'T PRINT LEADING 0 from OVERFLOW PUSH BC ;save BCD digits ADI '0' ;MAKE OVERFLOW digit into ASCII MOV E,A CALL CONOUT ;PRINT IT POP BC ;RESTORE BCD digits :6: MOV A,C ;MOVE BCD digits into A JMP OHEX ;output page PRINT MVI C,9 JMP SYS CONIN MVI C,1 CALL SYS CMP 'a' RC ;if not lower case CMP 'z'+1 RNC ;if not lower case SUI 'a'-'A' SG DB ' track $' SECMSG DB ' sector $' STSMSG DB ' stat=$' CMSG1 DB CR,LF,'Compare error Track $' CMSG2 DB ' Sector $' CMSG3 DB ' Byte $' PERMMSG DB CR,LF,BELCHR,'Permanent error.',LF,CR,'$' RMOUNT: DB CR,LF,LF,'Remount system disk on A: and type' DB ' RETURN.$' page ; Data is read and written on the disks sequentially ; but to speed the processing up the actual transfers ; are performed with a skewing factor of two. TOCC: T128: DB 0, 2, 4, 6, 8 ; ,10,12,14,16,18,20,22,24 DB 1, 3, 5, 7, 9 ; ,11,13,15,17,19,21,23,25 RRTRY: DS 1 ;ReTRY counter for R/WSEG ERRFLG: DB 0 ;Error on last track SOURCE: DS 1 ;source drive OBJECT: DS 1 ;destination drive SKEWPTR DS 2 ;Skew table pointer SKEWT: DS 2 FUDGE: DS 1 ;Skew fudge factor CFLAG: DS 1 ;Copy flag TRACK DS 1 ;Track LTRK DS 1 ;Ending track DISK DS 1 ;Disk number SPT DS 1 ;Sectors per track TSECS DS 1 ;Sectors left to copy from this track ;DMA DS 2 ;DMA address DMMA DS 2 ;OCC1 systext-compatible name BLEN: DS 1 ;Lengt);OCCFMT1.ASM TITLE 'FORMAT PROGRAM FOR OCC - ONE.' * 4D2005-00 MASTER .ASM * 2D2005-00 ASSY .ASM * 1D2005-00 LISTING .PRN * 4D1005-00 MASTER .COM * 2D1005-00 ASSY .COM ; +-----------------------+ ; | | ; | F O R M A T | ; | | ; +-----------------------+ ;REV = A1 ;DATE = 251 ;DEB ; ADDED RETRY TO VERIFY VER = '01' LEVEL = '00' ; ASSEMBLY CONSTANTS CTRM = 'M'-40H RBUF = 4000H ;RAM BUFFER CONTAINING FORMAT ; ;TEMPLATE DATA FOR ROM ROUTINE ; ;TO USE FOR FORMATTING EACH TRACK BIOSLOC = 1 ;GET FWA(BIOS) FROM HERE ORG ORGP LINK OCCRAM2.ASM page ; Main loop FMT: LDK SP,STK LDK DE,PRMT CALL ESTR ;Output initial prompt :1: CALL GCHR ;Get response CMP ABTC JZ SYSR ;if ABORT RLC STO A,DRV1 ;set ascii drive RRC SUB 'A' JRC FMT ;if illegal request JRZ :2 ;if A drive CMP 1 JRNZ FMT ;if illegal (A,B only) ; A= drive to function :2: STO A,DRV ;save drive STO A,SDISK CALL FMTDSK ;Format one disk CALL VERIFY ;verify p ;indicate drive NOT ready JMP FMT ;start again :4: OR A JZ :6 ;if no error LDK C,'E' :6: CALL CO ;output indicator LD A,SAVTRK INC A STO A,SAVTRK ;update track number CMP MTRK JRNZ :2 ;continue till all tracks CALL RDRV ;reset drive CALL RDRV RET page VERIFY: ;Verify that format worked correctly - ;read in disk 1 track @ a time to ck for errors ;Uses All. Proc LK DE,MSEC*LSECB ;length of 1 track LK HL,BUFFR ;FWA of track buffer LK C, not FMTCHR ;chr to broadcast CALL FILLC ;fill BUFFR with DB chrs LK DE,CRLF CALL ESTR LK A,0 STO A,TRAK ;start @ OUTSIDE track CALL RDRV ;reset the disk... CALL RDRV ;SET RETRY COUNT LDK A,5 ;FIVE READ RETRYS STO A,RETRY :hang: :loop: LK DE,MSEC*LSECB ;length of 1 track LK HL,BUFFR ;FWA of track buffer LDK C,0 ;ZERO BUFFER CALL FILLC ;fill BUFFR with DB chrs LDK BC,BUFFR CALL SETDMA LK BC,1 ;start @ sector #1 CALL SETSEC LD A,TRAK LK B,0 ! MOV C,A ;BC = trk# CAL TRAK: DS 1 ;current TRACK SECN: DB 0 DNRMS: DBE cr,lf,'Disk drive ' DRV1: DBE 0 DBE ': is NOT ready' DCE '.' DNRMS1: DBE cr,lf,' Is a disk installed in drive ? ' DBE ESC,'l','(y/n)',ESC,'m' DCE ' ' space 4,10 STRK: ;Set track numbers into data proc LDK HL,RBUF+1+TRKOFS LDK B,MSEC LDK DE,LISEC LD A,SAVTRK :2: STO A,[hl] ;update track cell ADD HL,DE ;point to next DJNZ :2 ;if not done LDK A,1 STO A,SAVSEC ;set sector to 1st RET space 4,10 RDRV: ;Reset requested disk drive proc LDK A,10 :2: STO A,RTST ;update retries LDK E,0 LD A,DRV MOV C,A CALL BIOS+BSDSK ;issue SELDSK CMP -1 RNZ ;if drive ready LD A,RTST CMP 10 JNZ :3 ;if NOT first time LDK DE,DNRMS1 CALL ESTR ;ask if drive ready CALL GCHR CMP 'Y' RZ ;if drive ready confirmation JMP SYSR ;else exit :3: LDK DE,DNRMS CALL ESTR ;indicate drive NOT ready LD A,RTST DEC A JNZ :2 ;if not n tries JMP FMT ;start again RTST: DB 0 page FORDAT: ;Format data froper formatting CALL RDRV ;reset the drive CALL RDRV LDK DE,PNEXT CALL ESTR ;Prompt for more CALL GCHR ;get response CMP 'Y' JNZ SYSR ;if wants to quit LDK DE,PRMT1 CALL ESTR ;request drive once again JR :1 ;enter requesting drive DTRKM: DBE cr DBE lf,lf,' ' DBE ESC,'l' DBE 'T r a c k s' DBE ESC,'m' DBE cr,lf,'0 1 2 3' DBE cr,lf,'0123456789012345678901234567890123456789',cr DCE lf RDYMS: DBE cr,lf,lf,' Press ' DBE ESC,'l','RETURN',ESC,'m' DBE ' when your disk is ready' DCE '.' PNEXT: DBE cr,lf DCE 'Do you want to format another disk (y/n) ? ' PRMT: DBE cr DBE lf,lf,lf DBE ESC,'g' DBE ' ',CTRM,CTRM,CTRM,CTRM,CTRM,' FORMAT Disk Program ' DBE CTRM,CTRM,CTRM,CTRM,CTRM DBE ESC,'G' DBE cr,lf,lf DBE 'Please Select the drive to format (A or B)' DBE cr,lf, ' (Drive "A" is left drive, "B" is right)' PRMT1: DBE cr,lf, ' Your ' DBE ESC,')','Selection',ESC,'(' DBE ' is : ' DCE ' ' DL SETTRK ;OUTPUT RETRY COUNT LD A,RETRY ;LOAD A WITH COUNT ADD A,30H ;MAKE ASCI MOV C,A ;MOVE TO C CALL CO ;OUTPUT NUMBER LDK C,08H ;BACK SPACE CALL CO LK B,MSEC ;read in MSEC sectors CALL XFER ;read in 1 track @ RBUF OR A JRNZ :serr ;if seek-type error CALL COMPAR ;compare with expected JRNZ :err :GOOD: LDK A,5 STO A,RETRY ;RESET RETRYS LK DE,VSTR ;if OK JR :2 :serr: ;Seek type error- ck for time-out err & hang if so: CMP SEKTMO ;ck for Seek Time Out error JRZ :hang ;if disk not in drive (?) :err: LD A,RETRY ;CHECK FOR RETRY SUB 1 ;DECREMENT BY ONE STO A,RETRY ;SAVE NEW COUNT JNZ :loop ;loop LDK A,5 ;RESET RETRYS TO FIVE STO A,RETRY LK DE,ERSTR ;if NOT ok :2: CALL ESTR ;notify user LK HL,TRAK INC [hl] ;step IN one track LD A,[hl] ;A=next track CMP MTRK ;verify up thru MTRK-1 JRNZ :loop :exit: LK DE,CR2LF ;cr,lf,lf JMP ESTR page ;Misc useful procs Proc SETDMA: LK DE,BSTDMA+100h ;addr of DMA-setteror IBM 3740. ; **** Gap 5 **** ECHO 40 ! DB 0FFh ENDM ECHO 6 ! DB 0 ENDM DB 0FCh ;Index mark ; **** Gap 1 **** FMTSD: ECHO 16 ! DB 0FFh ENDM DB 0,0,0,0,0,0 DB 0FEh ;Id address mark FTRKN: DB 0 ;track TRKOFS = *-FORDAT DB 0 ;side FSECN: DB 1 ;sector IF LSECB = 128 DB 0 ENDIF IF LSECB = 256 DB 1 ENDIF IF LSECB > 256 .9 DW LSECB, Error unknown sector length ENDIF DB 0F7h ;CRC ; **** Gap 2 **** ECHO 11 ! DB 0FFh ENDM ECHO 6 ! DB 0 ENDM FDAM: DB 0FBh ;Data address mark :noref: = FMTCHR ;...this symbol won't create ;180 useless xrefs... ; **** Data **** ECHO 128 DB :noref ENDM IF LSECB > 128 ECHO 128 DB :noref ENDM ENDIF FCRC2: DB 0F7h ;CRC FMTEND = * LIFMT = *-FORDAT LISEC = *-FMTSD page VSTR: DCE 'V','H'-40h ! DCE 'H'-40h ;used for GOOD verify ERSTR: DCE 'E','H'-40h ! DCE 'H'-40h ;BAD verify ;LFSTR: DBE 'H'-40h ! DCE LF ;^H,LF CR2LF DBE lf CRLF: DBE cr ! DCE lf RETRY DB 0 ESTR: ;Output RV: DB 0 page FMTDSK: ;Format entire disk ; Entry SDISK set to drive ; Exit all tracks formatted. proc LDK DE,RDYMS CALL ESTR ;output new line CALL GCHR CMP CR JNZ FMT ;if not ready, start again LDK HL,0 STO HL,SAVTRK ;set track to home ; Move format data to high RAM buffer LDK DE,RBUF+2 ;Fwa of data LDK HL,FORDAT LDK BC,LIFMT LDIR ;copy 1st sector LDK A,1 STO A,SECN ;set sector ECHO MSEC-1 ;copy other 9 sectors PUSH DE POP IY LDK HL,FMTSD LDK BC,LISEC LDIR LD A,SECN INC A STO A,SECN STO A,[IY+(FSECN-FMTSD)] ENDM EX DE,HL LDK BC,RBUF+2 SBC HL,BC ;get length STO HL,RBUF ;set length into buffer CALL RDRV ;reset drive CALL RDRV ; Display track indicator line LDK DE,DTRKM CALL ESTR ;Display track numbers ; Format entire disk :2: CALL STRK ;Set new track into data LDK BC,RBUF ;point to FWA of buffer CALL BIOS+BFMTJP ;Issue format command LDK C,'F' CMP -1 JNZ :4 ;if drive ready LDK DE,DNRMS CALL ESTR within ROM JR GOROM SETSEC: LK DE,BSTSEC+100h ;addr of sector-setter within ROM JR GOROM SETTRK: LK DE,BSTTRK+100h ;addr of track-setter JR GOROM XFER: LK DE,BREAD+100h ;multi-sector read ;JR GOROM GOROM: LD HL,BIOSLOC LK L,BROMJP ;do ROM jump JMP [hl] space 4,10 COMPAR: ;compare data from disk with expected data ;Entry track image @ BUFFR ;Exit Z=TRUE => good compare LK BC,MSEC*LSECB ;#bytes to compare LK HL,BUFFR ;FWA of data LK A,FMTCHR ;format character :loop: CPI# ;magic Zilog instruction RNZ ;if bad compare JPE :loop ;if not finished :good: RET ;return with Z=True space 4,10 FILLC: ;fill with character: ; C= chr to broadcast ; DE= #characters to broadcast ; HL= FWA to start broadcast MOV A,E OR D RZ ;if broadcast 0 bytes STO C,[hl] ;1st byte DEC DE MOV A,E OR D RZ ;if broadcast 1 byte MOV B,D ! MOV C,E ;BC := DE (count) MOV D,H ! MOV E,L ;DE := HL (FWA) INC DE ;DE := HL + 1 LDIR ;overlapping move RET page *encoded data to console ; Entry DE= FWA of code ; Entry DE=FWA of source OR A ;clear cbit LD A,[de] RRC ;normalize char PUSH AF AND 07Fh MOV C,A CALL CO ;output it INC DE POP AF JRNC ESTR ;if not done RET CO: ;Output one char to screen ; Entry C= char PUSH HL ! PUSH DE ! PUSH BC MOV E,C SYSTEM WCO ;output to console POP BC ! POP DE ! POP HL RET space 4,10 GCHR: ;Get character NORMALIZED from console ; Exit A=C= char proc PUSH HL ! PUSH DE SYSTEM RCO MOV A,C CMP 'a' JC :4 ;if not lower case CMP 'z'+1 JNC :4 ;if not lower case SUI 'a'-'A' :4: POP DE ! POP HL RET DS 30*2 STK: DS 0 ORG RBUF ;FWA of format image DS MTRK*LIFMT ;length of format image BUFFR: DS 0 ;Verify data area ; Endx OFORMAT DCE 'H'-40h ;BAD verify ;LFSTR: DBE 'H'-40h ! DCE LF ;^H,LF CR2LF DBE lf CRLF: DBE cr ! DCE lf RETRY DB 0 ESTR: ;Output Z'-40h ;ascii EOF mark ; Distributor is a fixed value in COPYPROG.com DIST: DB 'OSB1' ;Distributor - DGM,OURS,LBA,etc. OPTYP: DB ' Z80' ;Type - Z80,8080,8086,NS,VG,etc. ; Total bytes used to here = 83 decimal. IF * <> 0153h .9 ERR Copyright notice section wrong length. ENDIF page SETUP: PUSH AF ! PUSH BC ! PUSH DE ! PUSH HL LDK HL,0 ADD HL,SP STO HL,SYSTK ;save CPM stack LDK SP,STK LD A,LLIMIT STO A,NLIMIT LDK A,52 STO A,LLIMIT ;force limit to 52 CALL CKTIM ;ck for valid system clock LDK DE,PRMPT CALL ESTR ;output initial screen NXTCMD: ;Exit from command processor CALL OCCD ;re-setup variables in big prompt LK DE,SPMPT ;short prompt CALL ESTR NULL: ;Null command CALL GCHR ;get command chr CALL NORM MOV C,A LK E,ENTY# LK HL,CMDTBL JR LOOKUPB GCHR: ;Get a Command Chr CALL SECOND ;update date & time on screen SYSTEM CABT ;console stat OR A JRZ GCHR ;no char yet SYSTEM RCO RET NORM: ;Normalize chr in A ; Uses F.  ;if date&time are bad, don't output CALL GMON ;month CALL GDAY ;day CALL OUTPUT ;print exit message :bad: LD A,NLIMIT STO A,LLIMIT ;force new limit LD HL,SYSTK ;restore stack MOV SP,HL POP HL ! POP DE ! POP BC ! POP AF RET ;exit to CPM space 4,10 SDAT: ;Set date and time LK A,0FFh ;A = TRUE STO A,GOODAT ;Indicate date&time are valid JMP DATTIM page SECOND: ;update date&time on screen proc LD A,GOODAT ;ck for valid date & time OR A RZ ;if bad LD HL,TIMPTR LDK DE,PDAY LDK B,3 LDK C,'/' CALL PDIG ;display date LDK DE,OTIM LDK B,3 LDK C,':' JMP PDIG ;display time OCCD: ;Output current configuration data ; Entry LLIMIT set to current line limit ; ACIAD set to current baud rate Proc CALL SECOND ;update time & date ; Check baud rate and display LD A,ACIAD BIT 0,A ;0 => 300, 1 => 1200 LDK HL,U300 LDK DE,U1200 JRZ :4 ;if 300 baud EX DE,HL ;switch for 1200 baud :4: ;HL -> data to get bullit ; ;DE -> data to be cle CMP 'a' RC ;if UPPER CASE CMP 'z'+1 RNC ;if not lower case alphabetic SUB 'a' xor 'A' RET page CMDTBL: ;Command Table ; 3 bytes per entry: Ascii chr, "DW"-vector ; #entries = ENTY# ; last entry is "DW"-vector to "No-match" addr DB 'A' ! DW BAUD12 ;set 1200 baud DB 'B' ! DW BAUD3 ;set 300 baud DB 'C' ! DW SCR52 ;set 52 columns DB 'D' ! DW SCR128 ;set 128 columns DB 'E' ! DW SNCOL ;Set number of cols DB 'F' ! DW SDAT ;Set date, time DB 'X' ! DW EXIT ;exit to CP/M :end: DW NULL ;null command ; #entries in CMDTBL ENTY# EQU (:end - CMDTBL)/3 LOOKUPB: ;Logic to scan 3 byte branch table ; Not a subroutine; do not CALL. ; Entry HL =1st byte of table (match code) ; (2nd,3rd bytes = branch adrs) ; (table repeats [3 byte entries]) ; E is table size (no. of entries) ; (table body is followed with ; 2 byte "No-Match" adrs) ; C = char ; A = value to scan for possible match CMP [HL] INC HL ;(2nd byte of this 3 byte entry) JRZ VECTOR ;If match processared LDK A, ' ' STO A,[de] LDK A,HLCHR STO A,[hl] ; Now show current length of line LD HL,NLIMIT LDK H,0 ;set current lenght LDK DE,USCRN2 CALL CBD3 ;convert number LDK DE,USCRNL CALL OSTR ;output number of columns RET USCRNL: DB ESC,'=',12+' ',17+' ' USCRN2: DB 'xxx' ; DB ESC,'=',21+' ',9+' ' DB EOS space 4,10 GNUM: ;Get number from user LDK DE,LBUFF SYSTEM RCB ;read buffer LDK HL,LBUFF+2 PUSH HL ;save FWA of data LD A,LBUFF+1 MOV E,A LDK D,0 ADD HL,DE STO 0,[hl] ;set eos POP HL CALL NUMBR ;convert number JC NXTCMD ;if error MOV A,D OR A JNZ NXTCMD ;if too big MOV A,E CMP 128+1 JP NXTCMD ;if too big RET CKTIM: ;initialize GOODAT if system TIME is correct CALL VERTIM RZ ;ret if time is bad CALL VERDAT RZ STO A,GOODAT ;time & date must be correct! RET GOODAT: DB 0 ;if zero, BAD DATE & TIME NLIMIT: DB 0 LBUFF: DB 80,0 DS 40 Page SETXY MACRO Y,X DBE ESC,'=',%1+' ',%2+' ' ENDM PRMPT: DBE 'Z'-40;OCCSETA.ASM Title 'OSETUP.asm - Osborne One Setup Program.' * 4D2012-00 MASTER .ASM * 2D2012-00 ASSY .ASM * 1D2012-00 MASTER .ASM * 4D1012-00 MASTER .COM * 2D1012-00 ASSY .COM ; +-----------------------+ ; | | ; | S e t u p | ; | Program to show the | ; | configuration present | ; | | ; +-----------------------+ ;REV = A ;DATE = 256 VER = '00' LEVEL = '01' ; Assembly switches, constants LIND = 14 ;indent for screen HLCHR = 'V'-40h ;highlight char @CASE = 20h ;UPPER case @ILIST = TRUE ;list internal @SYS = SYcpm20 ;CP/M 2.x @CPU = 1 ;Z80 CPU 'Z80' ; Link odtime.asm ; Link numin.acd ; Link numout.acd Link OCCRAM2.asm ORG ORGP PROG: JMP SETUP NAME: DB 'SETUP .COM' DB CR,LF IF * <> 0111h .9 ERR Wrong length of filename and type. ENDIF DB 'Copyright SORCIM Corp. 1981',CR,LF DB 'S/N-' SN: DB '999999' DB ',Ver ' CPSV: DB '2.x' DB ' ' MESV: VFD 16\VER,8\'.',16\LEVEL RELTYP: DB 'ST' ;Release type - EV,UD,DP,etc.ST = standard DB CR,LF,' INC HL ;(3rd byte of this entry) INC HL ;1st byte of next entry DEC E ;Dec count of entries remaining JRNZ LOOKUPB ;Continue thru body of table ; JR VECTOR ;No-Match. hl=points to vector VECTOR: ;entry point. note hl on stack. LD A,[HL] ;1st byte (low order adrs) INC HL LD H,[HL] ;2nd byte (hi order adrs) MOV L,A ;HL=adrs from table MOV A,C JMP [hl] page BAUD12: ;Set 1200 baud Proc LK C,SI.1200 :baud: LDK HL,NXTCMD PUSH HL ;return LD HL,SYSR+1 LDK L,BSBAUD JMP [hl] ;perform baud rate set BAUD3: ;Set 300 baud LK C,SI.300 JR :baud SCR52: ;Set 52 column screen LK A,52 :scr: ;A = Lenght of line STO A,NLIMIT JMP NXTCMD SCR128: ;Set 128 column screen LK A,128 JR :scr SNCOL: ;Set user defined column length ; Minimum is 3, maximum is 128 LK DE,COLPMT CALL ESTR ;prompt CALL GNUM ;get number STO A,NLIMIT ;reset limit JMP NXTCMD EXIT: ;Exit to CP/M LK E,'Z'-40h ;clear screen SYSTEM WCO LD A,GOODAT OR A JRZ :bad+h ;clear screen DBE ESC,'g' ;Start Graphics SETXY 2,11 DBE ESC,'l' ;start underline DBE 'Configuration Setup Program' DBE ESC,'m' ;end underline SETXY 4,5 DBE cr,' Baud Rate for serial port' SETXY 5,LIND DBE 'A. ' U1200 = FWAVM+(5*128)+LIND+3 DBE ' ' DBE '1200 Baud' SETXY 6,LIND DBE 'B. ' U300 = FWAVM+(6*128)+LIND+3 DBE ' ' DBE ' 300 Baud' SETXY 8,5 DBE cr,' Screen Width' SETXY 9,LIND DBE 'C. 52 Columns' SETXY 10,LIND DBE 'D. 128 Columns' SETXY 11,LIND DBE 'E. ? Columns, user defined' SETXY 12,17 DBE ' , Current setting.' SETXY 14,5 DBE cr,' System Clock' SETXY 15,LIND DBE 'F. Set Date and Time' SETXY 16,LIND+4 DBE '??/??/?? ??:??:??' PDAY = FWAVM+(16*128)+LIND+4 OTIM = PDAY+12 SETXY 17,5 DBE cr,' Exit Setup Program' DBE ESC,'G' ;End Graphics SETXY 18,LIND DBE 'X. Exit' SPMPT: SETXY 22,0 DBE ESC,'T','K'-40h,ESC,'T' ;clr lines 21,22 DCE 'Command (A-F,X): ' COLPMT: SETXY 21,0 DCE 'Input screen wi HL,OPNTR EX DE,HL LD HL,PNTR LOOP: CALL GDIGT ;get next number INC A RZ ;if not valid number DEC A RLC ! RLC ! RLC ! RLC AND 0F0h MOV B,A ;save 1st digit INC HL CALL GDIGT ;get next number INC A CZ NLEAD ;if no leading zeros DEC A OR B ;merge with 1st digit INC HL STO HL,TPNTR INC HL STO A,[DE] INC DE DEC C JNZ LOOP ;if more, then again CALL VERDAT ;verify valid date RET VERDAT: ; Convert to Julian date and back again to ; check for valid date ; Enter: OPNTR is FWA of date ; Exit: Zero flag is set if invalid date LD HL,OPNTR CALL KDATE ;convert to K date CALL GDATE ;convert to Gregorian date ; Test for valid date entered. If valid, then ; old Gregorian date at ODATE is ; equal the new Gregorian date at NDATE ; month LD HL,NDATE EX DE,HL LD HL,ODATE MOV A,L CMP E JNZ ERR10 ;if not valid date ; day LD HL,NDATE+2 EX DE,HL LD HL,ODATE+2 MOV A,L CMP E JNZ ERR10 ;if notthe Osborne One. The time ' DBE 'will be' DBE cr,lf DBE 'valid until the machine is turned off.' DBE cr,lf DBE 'The date must be entered daily (to update the date)' DBE cr,lf DBE 'even if the machine is left turned on.' DBE cr,lf DBE 'There are three forms of entry:' DBE cr,lf,lf DBE 'DATETIME - display current date and time.' DBE cr,lf DBE 'DATETIME - prompt for new date and time.' DBE cr,lf DBE 'DATETIME? - display this message.' DCE '.' .occ1 ENDIF page ENDAT: ; Enter the date with prompting ; Enter: PROM1 is prompt message ; Exit: date entered at TBUFF ; A=0 if no entry CALL FILL ;fill buffer with zeros LDK A,10 STO A,TBUFF LDK DE,PROM1 PUSH AF CALL ESTR POP AF SYSTEM RCB,TBUFF LD A,TBUFF+1 OR A RZ ;if no entry LDK HL,TBUFF+2 STO HL,PNTR RET ENTIM: ; Enter the time with prompting ; Enter: PROM2 is prompt ; Exit: time is input at TBUFF CALL FILL LDK A,10 STO A,TBUFF LDK DE,PROM2 PUSHdth (1-128): ' DS 2*30 STK: DS 0 ; Endx OSETUP.asm TITLE 'ODTIME.asm for OCC-1 using Kdates' ; +---------------------+ ; ! ! ; ! DATETIME ! ; ! OSBORNE ONE ! ; ! ! ; +---------------------+ ; ; Author R.H. Frank ; Sorcim ; ; 12-01-79 Changed name. ; 07-12-80 Use cpm save-restore to speed up exit. ; 10-17-80 CMI Re-write for Julian dates. ; 02-28-81 CMI fix seconds prob. add copyright. ; 03-02-81 CMI Changed to suit OSB-1. ; and added help message. ; ; DEFINITIONS CNST: EQU 27759 ;kdays before mar 1 76 MCNST: EQU -27759 ;for subtraction ;Dummy for csysface.acd FILLZ EQU 0 ; Link ARITH.asm ; Link CSYSFACE.acd ; Link KDATE.acd ; Uses Kdates. Kdate started Thur. March 1st 1900 and is ; sequential since then. CNST is the days ; from 3/1/1900 to 3/1/1976 and is added to the calculation. ; 76 is subtracted from the year to compensate. ; Algorithm 199 from Communications of the ACM ; 199-P 1-0 OPNTR: EQU 0040h ;pointer i valid date ; year LD HL,NDATE+4 EX DE,HL LD HL,ODATE+4 MOV A,L CMP E JNZ ERR10 ;if invalid date LDK A,1 OR A RET ERR10: XOR A RET PAGE VALTIM: ; Validate time (hh:mm) ; and place in Pascal compatible location ; Enter: TPNTR is FWA of input buffer ; OPNTR+3 is FWA of output buffer ; Exit: Time in output buffer as 2 BCD bytes ; Zero flag set if invalid time ; C= counter B= temporary storage ; HL= input pointer ; DE= output pointer LD HL,TPNTR EX DE,HL LD HL,OPNTR INC HL INC HL INC HL EX DE,HL LDK C,2 LOOP1: CALL GDIGT INC A RZ ;if invalid number DEC A RLC ! RLC ! RLC ! RLC AND 0F0h MOV B,A INC HL CALL GDIGT INC A CZ NLEAD ;if no leading zeros DEC A OR B ;merge with 1st digit STO A,[DE] INC DE INC HL INC HL DEC C JNZ LOOP1 ;if another byte CALL VERTIM RET VERTIM: ; Verify time (as 4 BCD nybbles at OPNTR+3=>OPNTR+4) ; Hours must be <=23 Minutes must be <=59 ; Enter: time a AF CALL ESTR POP AF SYSTEM RCB,TBUFF LD A,TBUFF+1 OR A RZ ;if no entry LDK HL,TBUFF+2 STO HL,TPNTR LDK A,0 LD HL,OPNTR LDK BC,5 ADD HL,BC ;seconds STO A,[HL] RET page GDAY: ; Get the day of the week ; given a valid Julian (Kdate) date ; Enter: KDAY is the Julian date ; Exit: ODAY is the day of the week ; Uses DOW table for lookup LD HL,KDAY EX DE,HL LDK BC,7 CALL DIVD ;divide Kday by 7 INC HL EX DE,HL LDK BC,9 CALL PMULT ;multiply day by 9 chars./day LDK DE,DOW ADD HL,DE DEC HL ; HL is now the LWA of day ; Move day to ODAY LDK D,9 LDK BC,ODAY+8 LOOP2: LD A,[HL] STO A,[BC] DEC HL DEC BC DEC D JNZ LOOP2 ;if another char. RET GMON: ; Get the month of the year given a valid month ; of 1-12 ; Enter: MONTH is 1-12 ; Exit: DMO is the month (3 bytes) ; Uses MOY as lookup table LD HL,MONTH EX DE,HL LDK BC,3 CALL PMULT ;multiply month by 3 chars./month LDK BC,MOY ADD HL,BC DEC HL ; Hn OSBORNE - 1 DATTIM: CALL ERRD ;prompt & verify date CALL ERRT ;prompt & verify time ; CALL GDAY ;get day of week ; CALL GMON ;get month of year ; CALL CKHOL ;check if holiday ; CALL OUTPUT ;print date and time to user JMP NXTCMD ; ENDx page ERRD: ;Enter: when invalid date entered ; Exit: when a valid date is entered or ; on a ^C (jmp to CPM) CALL ENDAT JZ ERRD ;if no entry CALL VALDAT JZ ERRD ;if not valid date RET ERRT: ;Enter: when invalid time entered in main loop ; Exit: when a valid time is entered or ; on a ^C (jmp to CPM) CALL ENTIM JZ ERRT ;if no entry CALL VALTIM JZ ERRT ;if invalid date in ram RET TITLE 'SUBROUTINES' VALDAT: ; Check for date in buffer ; and store at Pascal compatible location ; Enter: PNTR is FWA of input buffer ; OPNTR is FWA of output buffer ; Exit: Zero flag set if invalid date ; Month C=3, Day C=2, Year C=1 ; C= counter B=temporary storage ; HL= input pointer ; DE= output pointer LDK C,3 LDt ONTR+3 ; Exit: Zero flag set if invalid time ; hours LD HL,OPNTR INC HL INC HL INC HL LD A,[HL] LDK HL,23h LDK D,0 MOV E,A CALL NEGDE ADD HL,DE MOV A,H OR A JM ERR11 ;if >23 hours ; minutes ,hours already OK LD HL,OPNTR INC HL INC HL INC HL INC HL LD A,[HL] LDK HL,59h LDK D,0 MOV E,A CALL NEGDE ADD HL,DE MOV A,H OR A JM ERR11 ;if >59 minutes LDK A,1 OR A RET ERR11: XOR A RET PAGE OUTPUT: ; Setup output line and print it ; Day LDK BC,DAY# LD HL,OPNTR INC HL CALL CONV1 ; Year INC HL LDK BC,OYR CALL CONV1 ; Hour INC HL LDK BC,OHOUR CALL CONV1 ; Minutes INC HL LDK BC,OMIN CALL CONV1 ; Seconds INC HL LDK BC,OSEC CALL CONV1 ; Print it LDK DE,DISP1 CALL OSTR RET page .occ1 IF FALSE HELP: ; Print help message. LDK DE,HELPM PUSH AF CALL ESTR POP AF JMP EXIT HELPM: DBE 'DATETIME is a SORCIM program to provide ' DBE 'the date' DBE cr,lf DBE 'and time for ,L now points to LWA of current month ; Move to DMO LDK D,3 LDK BC,DMO+2 LOOP3: LD A,[HL] STO A,[BC] DEC HL DEC BC DEC D JNZ LOOP3 ;if another char. RET NLEAD: ; No leading zero entered ; Enter: number in B in MSNybble ; Exit: number in B in LSNybble with MSNybble zero ; A contains a 1 for the dec upon return MOV A,B RRC ! RRC ! RRC ! RRC AND 0Fh MOV B,A LDK A,1 DEC HL ;reset pointer RET CONV: ; Convert 2 BCD nybbles in A into ; one 16 bit binary integer in HL ; Enter: 2 nybbles in A ; Exit: value in HL PUSH AF RRC ! RRC ! RRC ! RRC AND 0Fh MOV C,A LDK B,0 LDK DE,10 CALL PMULT ;multiply by 10 POP AF AND 0Fh LDK B,0 MOV C,A ;add ones to value ADD HL,BC RET CONV1: ; Convert BCD to Ascii and place in output buffer ; Enter: HL=> input byte ; BC=> output buffer ; Exit: HL and BC intact LD A,[HL] AND 0F0h RRC ! RRC ! RRC ! RRC AND 0Fh ADD A,'0' ;convert to ascii STO A,[BC] INC BC LDATE: ; beginning of new date NMO: DW 0 ;new month NDY: DW 0 ;new day NYR: DW 0 ;new year K1: DW 0 ;temporary storage MONTH1: DW 0 ;temporary month YEAR1: DW 0 ;temporary year page DISP1: ; Final display to user of date and time ; DB ESC,'=',21+' ',0+' ' ;SETXY 21,0 ; DB ESC,'T',lf,ESC,'T' ;clear lines 21,22 DB 'Today is ' ODAY: DB ' ' DB ' ' DMO: DB ' ' DB ' ' DAY#: DB ' ' DB ' ' DB '19' OYR: DB '00' DB ' at ' OHOUR: DB ' ' DB ':' OMIN: DB '00' DB ':' OSEC: DB '00' DB EOS PROM1: ; Enter date prompt SETXY 21,0 DBE ESC,'T' ;clr to EOL DCE 'Enter the date as (MM/DD/YY) ' PROM2: ; Enter time prompt SETXY 21,0 DBE ESC,'T' DCE 'Enter the time as (HH:MM) ' .occ1 IF FALSE PROM3: ; Date in ram invalid DBE cr,lf DBE 'Date currently in Ram is invalid - ' DCE 'Please re-enter date ' PJUL: DBE cr,lf DBE 'HAPPY 4th of JULY - Wheres your fireworks?' DBE cr DCE lf PDEC1: DBE cr,lf DBE 'MERRY CHRISTMAS TO YOU !!!' DB30 ;If partial subtraction successful ADD HL,BC DEC DE :30: SRL B RR C DEC A JRNZ :20 ;If more quotient bits to develop RL C RL B RET .cpu ENDIF .cpu IF @CPU = 0 MOV H,B MOV L,C XOR A :10: INC A ;count normalization shifts ADD HL,HL JNC :10 ;If divisor not yet normalized ; for main loop, BC = divisor, DE = what's left of dividend, ; HL = quotient so far MOV C,L MOV B,H LK HL,0 :20: PUSH AF MOV A,B ;shift divisor right RAR MOV B,A MOV A,C RAR MOV C,A ADD HL,HL ;shift quotient left MOV A,E ;dividend= dividend-divisor SUB C MOV E,A MOV A,D SBC A,B MOV D,A JNC :30 ;If partial subtraction successful EX DE,HL ;restore dividend ADD HL,BC EX DE,HL JMP :40 :30: INC HL ;subtract was okay :40: POP AF DEC A CLC ;clear out carry JNZ :20 ;If more quotient bits to develop EX DE,HL RET .cpu ENDIF space 4,20 ;DIVD Two's complement divide. ; (fortran like divide..) ; Entry BC = divisor. ; DE = dividend ; Exit HL = remainder.D A,[HL] AND 0Fh ADD A,'0' STO A,[BC] RET SPACE 1,18 FNC: ; Find next non-space,non-tab character ; or terminate on 0h character (no more chars.) ; Enter: HL points to FWA buffer ; Exit: HL points to next character ; character in A ; A= zero if no more chars. LD A,[HL] ORA A RZ ;if no more characters CMP TABCHR JZ NEXT ;if it is a tab CMP ' ' JZ NEXT ;if it is a space RET NEXT: INC HL JMP FNC ;for more chars. SPACE 1,17 GDIGT: ; Get next number ; Validate that it is 0-9 ; Convert to BCD ; Enter: HL is FWA of character ; Exit: character returned in A ; Error: 0FFh in A (not 0-9) LD A,[HL] CMP '0' JC ERR1 ;if <0 ascii CMP '9'+1 JNC ERR1 ;if >9 ascii SUI '0' ;convert to BCD RET ERR1: LDK A,0FFh RET FILL: ; This routine fills the input buffer at 080h ; with 32 zeros; ; Enter: nothing XOR A LDK HL,TBUFF LDK C,32 LOOP4: STO A,[HL] INC HL DEC C JNZ LOOP4 ;if not finished RET .occ1 IF FALSE CKHOLE cr DCE lf PDEC2: DBE cr,lf DBE 'HAPPY NEW YEAR !!!' DBE cr DCE lf .occ1 ENDIF PAGE DOW: ; Day of the week table ; March 1, 1900 was a THURSDAY ; length is 9 bytes each DB 'Wednesday' DB 'Thursday ' DB 'Friday ' DB 'Saturday ' DB 'Sunday ' DB 'Monday ' DB 'Tuesday ' MOY: ; Month of the year table ; 1st is January and length is 3 bytes DB 'Jan' DB 'Feb' DB 'Mar' DB 'Apr' DB 'May' DB 'Jun' DB 'Jul' DB 'Aug' DB 'Sep' DB 'Oct' DB 'Nov' DB 'Dec' ; Endx Datetime.asm TITLE 'Arithmetic routines' PMULT: ;Two's complement integer multiply routine ; Entry BC = multiplicand. ; DE = multiplier ; Exit HL = product proc .cpu IF @CPU = 1 LK HL,0 :10: SRL D JRNZ :20 RR E JRZ :50 JR :30 :20: RR E :30: JRNC :40 ADD HL,BC :40: SLA C RL B JR :10 :50: RNC ;If next add unnecessary ADD HL,BC RET .cpu ENDIF .cpu IF @CPU = 0 EX DE,HL ;make HL multiplicand LDK DE,0 ;and DE product MOV A,C ;A= lower 8 bits of mul ; DE = quotient DIVD: proc XOR A ;make sure divisor isn't 0 or -32768 OR C JNZ :10 ;If divisor of 0 not possible OR B JZ :99 ;if divide by 0, error XOR 80h JZ :99 ;if divide by -0, error :10: MOV A,B ;check divisor sign OR A JM :50 ;if divisor is negative :20: OR D JP DIVPOS ;if divident also positive ; divide negative by positive ; then set realquotient= -quotient ; realremainder= - remainder ; -7 DIV 3 = -2, -7 MOD 3 = -1 ; -6 DIV 3 = -2, -6 MOD 3 = 0 CALL DIVNEG ;divide -DE by BC CALL NEGDE ;2s complement of quotient JMP :85 ;go complement remainder ; divide by negative. make divisor positive. ; then set realquotient= -quotient ; 7 DIV -3 = -2, 7 MOD -3 = +1 ; 6 DIV -3 = -2, 6 MOD -3 = 0 :50: CALL NEGBC ;make divisor positive MOV A,D ;check dividend sign OR A JM :80 ;if dividend is negative OR E JZ :80 ;If dividend is zero CALL DIVPOS JMP NEGDE ;set quotient negative ; divide negative or zero by negative ; then set realremainder=: ; Check if its near a holiday and if so, print ; out appropriate message. ; Enter: MONTH is month in hex ; DAY is day in hex ; Exit: If near a holiday, print message else nothing ; ; New Years - month=12 and day>25 ; 4th of July - month=7 and day<=4 ; Christmas - month=12 and day<=25 ; LD HL,MONTH MOV A,L CMP 7 JZ JUL ;if July CMP 12 JRZ DCMB ;if December RET ;no holiday JUL: LD HL,DAY MOV A,L CMP 5 RNC ;if not near 4th LDK DE,PJUL PUSH AF CALL ESTR POP AF RET DCMB: LD HL,DAY MOV A,L CMP 26 JC XMAS ;if near Xmas LDK DE,PDEC2 CALL ESTR ;near New Years RET XMAS: LDK DE,PDEC1 PUSH AF CALL ESTR POP AF RET .occ1 ENDIF TITLE 'DATA STORAGE AREA' SYSTK: DW 0 ;old CPM stack PNTR: DW 0 ;input character pointer (in TBUFF) TPNTR: DW 0 ;time pointer (in TBUFF) ; Kdate values ODATE: ; beginning of old date MONTH: DW 0 ;month in hex DAY: DW 0 ;day in hex YEAR: DW 0 ;last 2 digits of year KDAY: DW 0 ;K date value Ntiplier :10: MOV C,B ;set up next 8 bits for next time around LDK B,8 ;B= shift count :20: RAR JNC :30 EX DE,HL ;add in the partial product ADD HL,DE EX DE,HL :30: ADD HL,HL ;shift multiplicand left DEC B JNZ :20 MOV A,C ;get high order byte of multiplier OR A JNZ :10 EX DE,HL ;reply HL= product RET .cpu ENDIF space 4,20 DIVNEG: ;Divide negative of a number. ; DE:= (-DE) div BC ; HL:= (-DE) mod BC ; Entry BC= divisor > 0 ; DE= dividend < 0 ; Exit BC= divisor preserved. ; HL= remainder >= 0 ; DE= - quotient >= 0 proc CALL NEGDE ;2s complement dividend ;DIVPOS Divide two positive integers. ; DE:= DE div BC ; HL:= DE mod BC ; Entry BC= divisor > 0 ; DE= dividend >= 0 ; Exit BC= divisor preserved. ; HL= remainder >= 0 ; DE= quotient >= 0 DIVPOS: .cpu IF @CPU = 1 EX DE,HL ;HL= dividend LDK A,1 :10: INC A ;count normalization shifts SLA C RL B JP :10 ;If BC not yet normalized LDK DE,0 :20: STC RL E RL D ;DE= 2 * DE SBC HL,BC JRNC :- -remainder ; -7 DIV -3 = 2, -7 MOD -3 = -1 :80: CALL DIVNEG ;divide -DE by BC ; complement remainder ; DE= true quotient ; HL= - remainder :85: EX DE,HL CALL NEGDE ;DE= - remainder EX DE,HL RET :99: POP HL ;return_address PUSH DE ;Tos= dividend JMP DIVZER NEGDE: ;2s complement DE MOV A,E CMA MOV E,A MOV A,D CMA MOV D,A INX DE RET ; NEGBC: ;2s complement BC MOV A,C CMA MOV C,A MOV A,B CMA MOV B,A INX BC RET ; DIVZER: ;ERROR OCCURED BECAUSE OF DIVIDING BY ZERO ; ABORT HERE JMP EXIT ; ; ENDx TITLE 'CSYSFACE - CP/m system interface routines.' ;CSYSFACE - CP/m system interface routines. ; 79/07/13. ; 79/09/03. ; 79/09/30, Added cromemco switch, rhf. ; 79/10/25, Fix bug in CCSTS to use RCO to clear status. ; 80-01-04, rhf, Changed OSTR to use std cpm WCO function ; 80-01-07, rhf, Changed WCON-cpm and ICOND-transparent ; 80-01-11, rhf, Temporary fix for OPEN on cpm 2.0 ; 80-01-18, rhf, Added FSIZE to work on 1.4 and 2.0 ; 80-03-24,  directly RCO: ; NOTE: this routine is used by GOTOXY proc MOV C,A PUSH HL PUSH BC .sys IF @SYS = SYcpm20 MOV A,C CMP 0F0h JC :3 ;If not F0 or > ANI 7Fh :3: MOV E,A SYSTEM DIO .sys else BIOCAL COUT ;bypass cp/m .sys endif POP BC POP HL RET RCON: ;Read specified length from console ; Entry BC= FWA of buffer ; Buffer format is: ; 1/ML (maximum length) ; 1/CL (Current length) ; ML/buffer area ; Exit data in buffer LDK C,OS#RCB JMP SYS CCSTS: ;Get status of user's console ; Entry NONE ; Exit A= char if char there else A=0. SYSTEM CABT RAR JNC CCSTS1 ;If NOT ready SYSTEM RCO ;eat char (can't stop echo) RET CCSTS1: XRA A RET OCRLF: ;Output carriage return/line feed ; Entry NONE ; Exit sent LDK DE,TCRLF JMP OSTR ;output cr/lf TCRLF: DB CR,LF,EOS NLPBUF: ;Output new line followed by buffer ; Entry DE-> buffer ; Exit sent PUSH DE ;save FWA CALL OCRLF ;Output new line POP DE ; JMP PBUF OSTR: ;Output St;Set exit conditions ; Here if file > 1 extend :2: CALL OPEXT ;open next extend MOV A,L CMP 128 JNC :2 ;if still full ; Here if done ; BC = total LDK HL,F_EX ADD HL,DE STO 0,[hl] ;set extend to 0 PUSH BC SYSTEM OPEN ;reopen extend 0 POP DE .sys endif ; Now divide by 2 (pascal pru = 256) ; Cbit cleared :20: INC DE ;round up one (cpm files) OR A ;clear cbit MOV A,D RAR MOV D,A MOV A,E RAR ;bring in Cbit MOV E,A RET ;DE = length OPEXT: ;Open next extend for file ; Entry DE -> FCB ; BC = current length ; Exit DE=DE ; BC = new length ; L = length this extend LDK HL,F_EX ADD HL,DE INC [hl] ;next extend PUSH BC PUSH DE SYSTEM OPEN POP DE POP BC LDK HL,F_RC ADD HL,DE LD A,[hl] PUSH AF PUSH BC PUSH DE SYSTEM CLOS ;close extend POP DE POP BC POP AF MOV L,A ADD C MOV C,A RNC ;if no oveflow INC B RET IFCB: ;Initialize DMA and clear FCB area ; Entry HL = DMA ; DE ->FWA of FCB ; Exit DE = DE PUSH Drhf, Psr 125, Fixed to FSIZE carry bit. ; , rhf, Psr 124, reserve F0-FF on output ; , rhf, GETMEM forced to round down to 100h ; , rhf, Fix CDOS to use 1.4 for filesize ; 80-12-15, phm, Use CPm 2 Random Address in FCB. ; 81-03-02, cmi, Added ESTR for encoded output. ; 81-03-22, phm, Document GDSKN & SDSKN. ; 81-05-15, seh, change JRNC into JNC inside ESTR. GETMEM: ;Return in HL LWA of available memory LD HL,SYS+1 DEC H ;-256 LDK L,0 ;insure rounded to 100h RET GETDAT: ;Return date into cells -> DE LD HL,SYSDAT LDK B,3 LDK C,'/' ;separator GETD3: CALL PDIG ;convert LDK A,EOS STO A,[de] ;set EOS (ala OASIS) RET GETTIM: ;Return time into cells -> DE LD HL,SYSTIM LDK B,3 LDK C,':' JMP GETD3 SETCON: ;Set console translation definitions ; Entry B= enable mask, And with current ; C= disable mask, Xor with results of B ; B=C=0, returns current status MOV A,B OR C LD A,CONTRL RZ ;if status AND B ;bits to change XOR C ;toggle STO A,CONring to console PBUF: ;Output line to console device ; NOTE: Uses standard CPM function WCO, thus all non cpm ; control codes will set ^x as character. ; Entry DE-> fwa of buffer ; String terminated by EOS (0) ; Exit Sent EX DE,HL PBUF2: LD E,[hl] MOV A,E ORA A RZ ;If all done PUSH HL ! PUSH BC SYSTEM WCO ;output to terminal POP BC ! POP HL INC HL JMP PBUF2 ;continue space 4,10 ESTR: ;Output ENCODED string to console ; Entry DE=FWA of source OR A ;clear cbit LD A,[de] RRC ;normalize char PUSH AF AND 07Fh MOV C,A CALL CO ;output it INC DE POP AF JNC ESTR ;if not done RET CO: ;Output one char to screen ; Entry C= char PUSH HL ! PUSH DE ! PUSH BC MOV E,A SYSTEM WCO ;output to console POP BC ! POP DE ! POP HL RET PAGE OPEN: ;Open a currently defined file (Sequential). ; Entry DE-> FWA of FCB ; HL= FWA for DMA ; Exit A =-1 if ERROR CALL IFCB ;Clear FCB and set DMA LDK C,OS#OPEN .sys IF @SYS <> SYcpm20 JMP SYS .syE EX DE,HL ;DMA to DE SYSTEM SDMA POP HL PUSH HL LDK DE,LFILEN+LFILEX+1 ADD HL,DE LDK DE,LFCB-(LFILEN+LFILEX+1) CALL FILLZ ;clear it out POP DE RET PAGE CLOSE: ;Close aa currently OPENED file ; Entry DE-> FWA of FCB ; HL= FWA of Buffer ; Exit A=-1 if file not present CALL RDMA1 LDK C,OS#CLOS JMP SYS SDIR: ;Search directory for a file ; Entry DE-> FWA of FCB ; HL-> DMA address ; Exit A=-1 if NO find CALL RDMA1 LDK C,OS#SDIR JMP SYS DELFIL: ;Delete an existing file from directory ; Entry DE-> FWA of FCB ; HL->DMA address ; Exit NO conditions. CALL RDMA1 LDK C,OS#DEL JMP SYS RENAME: ;Rename an existing file. ; Entry DE-> 16/Old name, ; 16/New name. ; HL->DMA address ; Exit A=-1 if file does not exist. CALL RDMA1 LDK C,OS#REN JMP SYS GDSKN: ;Get current disk number. ; Entry NONE ; Exit A=Current disk being used. ; (A: = 0 .. P: = 15) LDK DE,0 LDK C,OS#RCD JMP SYS SDSKN: ;Select disk number. ; Entry C=number. ; (TRL RET CONTRL: VFD 1\1 ;Echo on VFD 1\0 ;No fold to UPPER VFD 1\0 ;No fold to lower VFD 1\0 ;Stop on any control chr VFD 1\0 ;Ignore control chr (throw away) VFD 1\1 ;Display control chr as ^[chr+40h] ; VFD 1\0 ;**NOT USED by OASIS** VFD 1\0 ;No column display VFD 1\0 ;**Reserved for OASIS** COLN: DB 0 ;=Current column LINE: DB 0 ;=Current line PAGE RCIO: ;Read character with ECHO CALL RCI ;get char JMP RCO ;echo always ;WCON Write one character to console ; Entry C= character ; Exit C=C ; character sent WCONA: MOV C,A ;entry if char in A WCON: PUSH HL PUSH BC MOV E,C SYSTEM WCO POP BC ;perserve C POP HL MOV A,C RET ICON$D: ;Input to A from keyboard directly RCI: ; NOTE: does not use CP/m function. proc .sys IF @SYS = SYcdos SYSTEM DCO .sys else .sys IF @SYS = SYcpm20 :3: LDK E,0FFh SYSTEM DIO OR A JZ :3 ;If no data .sys else BIOCAL CONIN .sys endif ;cpm20 .sys endif ;cdos MOV C,A RET OCON$D: ;Output A to consoles else PUSH DE CALL SYS POP HL LDK DE,F_S2 ADD HL,DE STO 0,[hl] ;clear S2 field RET .sys endif OPENR: ;Open a currently defined file (Random usage). ; Entry Same as OPEN CALL RDMA1 ;Set DMA LDK C,OS#OPEN JMP SYS CREATE: ;Create a new file. ; Entry DE-> FWA of FCB ; HL = FWA for DMA ; Exit A=-1 if NO space. CALL IFCB LDK C,OS#CRE JMP SYS FSIZE: ;Get size of file in Pascal blocks ; Entry DE-> FWA of FCB ; Exit HL = Length of file. proc .sys If (@SYS = SYcpm2) or (@SYS = SYmpm1) PUSH DE ;save fcb again SYSTEM FIZ ;get filesize POP HL LDK DE,F_S2 ADD HL,DE STO 0,[hl] ;clear S2 **temporary** LDK DE,F_RRF-F_S2 ADD HL,DE XRA A ;clear values LD E,[hl] ;get lsb of size STO A,[hl] ;clear it INC HL LD D,[hl] ;get msb of size STO A,[hl] ;clear it .sys endif .sys IF (@SYS = SYcpm14) or (@SYS = SYcdos) LDK BC,0 ;length = 0 LDK HL,F_RC ADD HL,DE LD A,[hl] MOV C,A CMP 128 JNC :2 ;If file > 1 extend MOV E,A LDK D,0 JMP :20 .A: = 1 .. P: = 16) ; Exit NONE. DCR C ;Actual #= #-1 MOV E,C ;E= disk # LDK C,OS#DSEL JMP SYS ;SDMA: ;Set DMA address for all disk I/O operations. ; Entry DE=DMA address ; Exit NONE. ; NOTE: THIS ROUTINE SHOULD NEVER BE CALLED. ; AND IS THUS NOT DEFINED.. PAGE RDPRU: ;Read one PRU of data from file specified. ; Entry DE-> FWA of FCB ; HL-> FWA of DMA ; Exit A= 0, If successful ; 1, If read passed EOF ; 2, If reading unwritten PRU CALL RDMA1 RDPRU1: .sys IF (@SYS >= SYcpm2) LDK C,OS#RRD RDPRU2: PUSH DE CALL SYS LDK DE,F_RRF POP HL ADD HL,DE ;HL-> Random Address field LD E,[hl] INC HL LD D,[hl] INC DE ;advance Random Address STO D,[hl] DEC HL STO E,[hl] RET .sys ELSE LDK C,OS#READ JMP SYS .sys endif WTPRU: ;Write one PRU of data to file. ; Entry DE-> FWA of FCB ; HL-> FWA of DMA ; Exit A= 0, If Successful ; 1, If extend error ; 2, If disk full ; 255, If no more dir space CALL RDMA1 WTPRU1: .sys IF @SYS >= SYcpm2 L,DAY ADD HL,DE ;k=k+d LDK DE,CNST ADD HL,DE ;k=k+constant STO HL,KDAY RET LESS:; Here if m<=2 LD HL,MONTH1 LDK DE,9 ADD HL,DE ;m=m+9 STO HL,MONTH1 LD HL,YEAR1 DEC HL ;y=y-1 STO HL,YEAR1 JMP RET PAGE GDATE: ; Converts a Kdate to the MDY of a Gdate ; Enter: Kdate is the no. to convert in 16 bit binary ; K2 and K1 are temporary storage ; Exit: NMO is the month,NDY is the day and NYR is the year ; as 16 bit binary LD HL,KDAY ; STO HL,K LDK BC,MCNST ADD HL,BC ;k=k-constant(27759) ; STO HL,K EX DE,HL LDK BC,4 CALL PMULT ;k1=k*4 DEC HL ;k1=k1-1 STO HL,K1 EX DE,HL LDK BC,1461 CALL DIVD ;y=k1/1461 EX DE,HL STO HL,NYR LD HL,K1 STO HL,NDY ;d=k1 LD HL,NYR EX DE,HL LDK BC,1461 CALL PMULT ;k1=y*1461 EX DE,HL LD HL,NDY CALL NEGDE ADD HL,DE ;d=d-k1 LDK BC,4 ADD HL,BC ;d=d+4 EX DE,HL CALL DIVD ;d=d/4 EX DE,HL STO HL,NDY EX DE,HL LDK BC,5 CALL PMULT ;m=d*5 DEC  CMP '0' JC NUMB2 ;If below digits, end of field CMP '9'+1 JC NUMB3 ;If decimal digit CMP 'a' xor @CASE JC NUMB2 ;If below alpha, end of field CMP ('z'+1) xor @CASE JNC NUMB2 ;if not alfabetic, end of field NUMB3: INC C ;count "digits" INC HL JMP NUMB1 ;loop thru alphanumerics ; C= length OF number Field ; HL-> terminating char NUMB2: DEC HL LD B,[hl] ;B= last character EX [sp],HL ;stack ^Last, HL-> First MOV A,C OR A JZ DEC8 ;If empty number, ERROR .base IF DEF NUMBAS LD A,NUMBAS OR A JM NUMB6 ;If pre-radix appeared .base ENDIF MOV A,B ;check for post-radix DEC C JZ NUMB5 ;If single digit CMP 'b' xor @CASE JZ BINN ;If Binary CMP 'n' xor @CASE JZ DEC ;If Decimal CMP 'q' xor @CASE JZ OCT ;If Octal CMP 'h' xor @CASE JZ HEX ;If HEX NUMB5: INC C .base IF DEF NUMBAS NUMB6: LD A,NUMBAS AND 7Fh CMP 'b' xor @CASE JZ BINN ;If Binary CMP 'q' xor @CASE JZ OCT ;If Octal CMP 'h' xor @CASE JZ HEX ;If HEX .bas LDK C,OS#WRD JMP RDPRU2 .sys ELSE LDK C,OS#WRT JMP SYS .sys endif space 4,10 RDCPRU: ;Read C reg PRUs into ram. ; Entry DE -> FCB ; HL = DMA ; C = Number of PRUs ; Exit A= 0, if xfer complete, else CPM error CALL RDMA PUSH HL ! PUSH DE ! PUSH BC CALL RDPRU1 ;read next pru of data POP BC ! POP DE ! POP HL OR A RNZ ;If error DEC C JNZ RDCPRU ;if not done RET WTCPRU: ;Write C reg PRUs to disk ; Entry/Exit same as RDCPRU CALL RDMA PUSH HL ! PUSH DE ! PUSH BC CALL WTPRU1 ;read next pru of data POP BC ! POP DE ! POP HL OR A RNZ ;If error DEC C JNZ RDCPRU ;if not done RET RDMA: ;Reset DMA ; Entry HL=DMA ; Exit BC=BC, DE=DE, ; HL=DMA+L$PRU PUSH BC PUSH DE PUSH HL ;DMA EX DE,HL SYSTEM SDMA POP HL ;DMA LDK BC,L$PRU ADD HL,BC ;Advance DMA POP DE POP BC RET RDMA1: ;Set DMA only ; Entry HL= DMA ; Perserves DE PUSH DE EX DE,HL SYSTEM SDMA POP DE RET ; ENDx SYSI ;End of system interface module PAGE KDAHL DEC HL DEC HL ;m=m-3 EX DE,HL LDK BC,153 CALL DIVD ;m=m/153 EX DE,HL STO HL,NMO LD HL,NDY EX DE,HL LDK BC,5 CALL PMULT ;d=d*5 DEC HL DEC HL DEC HL ;d=d-3 STO HL,NDY LD HL,NMO EX DE,HL LDK BC,153 CALL PMULT ;k1=m*153 EX DE,HL CALL NEGDE LD HL,NDY ADD HL,DE ;d=d-k1 LDK BC,5 ADD HL,BC ;d=d+5 EX DE,HL CALL DIVD ;d=d/5 EX DE,HL STO HL,NDY ; If m<10 then gosub LESS1 LD HL,NMO MOV A,L CMP 10 JC LESS1 ;if <10 LDK BC,-9 ADD HL,BC ;m=m-9 STO HL,NMO LD HL,NYR INC HL ;y=y+1 STO HL,NYR RET1: LD HL,NYR LDK BC,76 ADD HL,BC ;y=y+76 STO HL,NYR RET LESS1: LD HL,NMO LDK BC,3 ADD HL,BC STO HL,NMO JMP RET1 IF @ILIST = 0 ;Ctext NUMIN LIST -L,-R ENDIF TITLE 'NUMIN.ACD - Numeric Input Conversion routines.' ;NUMIN.ACD - Numeric Input Conversion routines. ; 79/07/07 ; 80/04/16 Default radix, phm. ; NUMIN converts a number to its value (garbage results if ; value exce ENDIF ; Convert Decimal number DEC: MOV A,C CMP 2 JM DEC4 ;ONE digit Conversion JZ DEC3 ;TWO digitS CMP 4 JM DEC2 ;THREE digitS JZ DEC1 ;FOUR digitS LDK BC,10000 ;FIVE digit Conversion CALL DECC DEC1: LDK BC,1000 CALL DECC DEC2: LDK BC,100 CALL DECC DEC3: LDK BC,10 CALL DECC DEC4: LDK BC,1 CALL DECC ; Set exit conditions for GOOD number DEC5: CLC ;clear error indicator DEC7: POP HL ;reply HL-> Last POP BC RET ; Set exit conditions for BAD number DECE: POP BC ;discard DECC return address DEC8: STC ;Indicate error in Field JMP DEC7 ; process conversion of Decimal digit ; BC= unit value ; HL-> character ; Exit to DECE if error ; DE updated DECC: LD A,[hl] SUB '0' JM DECE ;If character below digits CMP 9+1 JP DECE ;If character above digits INC HL OR A RZ ;If digit = 0 EX DE,HL DECC1: ADD HL,BC DEC A JNZ DECC1 ;If not DONE EX DE,HL RET ; Convert Octal number OCT: TE: ; Convert Gregorian date to Kdate ; Enter: Mo,Dy,Yr as 3 bytes of 2 BCD nybbles each ; at location pointed to by HL ; Exit: Kdate is stored at KDAY as a 16 bit binary ; number ; First convert date to binary and store at MONTH,DAY,YEAR LD A,[HL] PUSH HL CALL CONV STO HL,MONTH STO HL,MONTH1 POP HL INC HL LD A,[HL] PUSH HL CALL CONV STO HL,DAY POP HL INC HL LD A,[HL] CALL CONV STO HL,YEAR STO HL,YEAR1 ; Actual conversion ; if m<=2 then gosub LESS LD HL,MONTH1 MOV A,L CMP 3 JC LESS ;if month<=2 SUI 3 ;m=m-3 MOV L,A STO HL,MONTH1 RET: LD HL,YEAR1 MOV A,L SUI 76 ;y=y-76 MOV L,A STO HL,YEAR1 EX DE,HL LDK BC,1461 CALL PMULT ;k1=y*1461 EX DE,HL LDK BC,4 CALL DIVD ;k1=k1/4 EX DE,HL STO HL,K1 LD HL,MONTH1 EX DE,HL LDK BC,153 CALL PMULT ;k=m*153 LDK BC,2 ADD HL,BC ;k=k+2 EX DE,HL LDK BC,5 CALL DIVD ;k=k/5 LD HL,K1 ADD HL,DE ;k=k+k1 EX DE,HL LD Heeds 2**16-1 = 65535). An optional post-radix ; may be specified: ; B 2 Binary ; Q 8 Octal ; N 10 Decimal ; H 16 Hexadecimal ; If the string contains no explicit radix (last character ; is not one of the above), the caller may specify the ; default radix. The cell NUMBAS should contain Ascii ; B, Q, or H (in lower case, depending on @CASE). If NUMBAS ; is not DEFined, or has another value, then N (= decimal) ; is assumed. An error is reported if a digit greater than ; the radix appears, or if the field is empty. "Digit" means ; '0'..'9' or 'A'..'Z' (depending on @CASE). ;NUMBR Convert Ascii number to value. ; Entry HL-> first character of possible number ; NUMBAS= default radix specifier. ; If 2**7 set, pre-radix specified. ; If NUMBAS not DEFined, decimal assumed. ; Exit Cbit set if error ; DE= result value. ; HL-> last character of number. ; Preserves BC. NUMBR: PUSH BC PUSH HL LDK DE,0 MOV B,D MOV C,D NUMB1: LD A,[hl] ;GET NEXT SOURCE Character / LD A,[hl] SUB '0' JM DEC8 ;If character below digits CMP 7+1 JP DEC8 ;If digit exceeds radix OR E MOV E,A DEC C INC HL ;next digit JZ DEC5 ;If all digits processed EX DE,HL ADD HL,HL ADD HL,HL ADD HL,HL EX DE,HL JMP OCT ; Convert Hexadecimal number HEX: LD A,[hl] CMP '0' JM DEC8 ;If not number CMP '9'+1 JM HEX1 ;If HEX CMP 'a' xor @CASE JM DEC8 ;If not number CMP ('f'+1) xor @CASE JP DEC8 ;If not number SUI 7 HEX1: AND 0Fh OR E MOV E,A INC HL DEC C ;decrement digit processed count JZ DEC5 ;If all digits processed EX DE,HL ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL EX DE,HL JMP HEX ; Convert Binary number BINN: LDK DE,0 ;initialize results BINN1: LD A,[hl] SUI '0' JM DEC8 ;If illegal number CMP 1+1 JP DEC8 ;If illegal number EX DE,HL ADD HL,HL ;left shift 1 OR L ;add in this bit MOV L,A EX DE,HL INC HL ;next user character DEC C ;count-1 JNZ BINN1 ;if not all pr RZ ;if end PUSH HL CALL OCHR POP HL INC HL JMP OSTR SEL: ;SELECT DISK GIVEN BY REGISTER A STO A,SDISK ;set for rom MOV C,A LDK DE,SELDSK JMP ROMJMP TRK: ;SET UP TRACK LDK B,0 ;upper half always 0 LDK DE,SETTRK ;OFFSET FOR SETTRK ENTRY JMP ROMJMP SEC: ;SET UP SECTOR NUMBER LDK DE,SETSEC JMP ROMJMP SDMA: ;SET DMA ADDRESS TO VALUE OF B,C LDK DE,SETDMA JMP ROMJMP READ: ;PERFORM READ OPERATION LDK DE,READF LDK B,MSEC JMP ROMJMP WRITE: ;PERFORM WRITE OPERATON LDK DE,WRITF LDK B,MSEC JMP ROMJMP page GETPUT: ;Get or PUT cpm onto disk requested ; ENTRY RW= 0, if read ; RW= 1, if WRITE proc LDK HL,LDBUF ;where CP/M is left after movcpm STO HL,LDMA LDK A,-1 ;TRK=-1 STO A,TRACK :2: ;Main loop for reading or writing next Track LDK HL,TRACK INC [hl] ;TRACK = TRACK + 1 LD A,OST CMP [hl] JZ :14 ;if all OS tracks, then done ; Reset to next track to sector LD C,[hl] CALL TRK ;set next track LDK C,1 CALL SEC ;setocessed JMP DEC5 IF @ILIST = 0 LIST * ENDIF ; Endx NUMIN IF @ILIST = 0 LIST -L,-R ENDIF TITLE 'NUMOUT.ACD - Numeric Output Conversion routines.' ;NUMOUT.ACD - Numeric Output Conversion routines. ; 79/05/30 ;CBD Convert Binary number to Ascii Decimal ; with leading zero suppression. ; Entry HL= value to convert. ; DE-> Fwa to store conversion. CBD5: LK BC,-10000 CALL CBO ;convert Ten-Thousands digit CALL CLZ CBD4: CBD: LK BC,-1000 CALL CBO ;convert Thousands digit CALL CLZ ;CHECK LEADING ZEROS CBD3: LK BC,-100 CALL CBO CALL CLZ CBD2: LK BC,-10 CALL CBO CALL CLZ MOV A,L ADI '0' STAX D LK A,'0' STA CLZA ;RESET TO IGNORE LEADING ZEROS RET ;CBO Convert ONE digit TO Decimal HEX ; ENTRY (A) = number CBO: LK A,'0' PUSH DE CBO1: MOV E,L MOV D,H INR A DAD B JC CBO1 ;IF not DONE DCR A MOV L,E MOV H,D POP DE RET ;CLZ Check for leading zeros and if found ; change to ;OCCSYSGA.ASM TITLE 'SYSGEN - SYSTEM GENERATION PROGRAM 8/79' * 4D2006-00 MASTER .ASM * 2D2006-00 ASSY .ASM * 1D2006-00 LISTING .PRN * 4D1006-00 MASTER .COM * 2D1006-00 ASSY .COM ; +---------------+ ; | | ; | SYSGEN | ; | | ; +---------------+ ;REV = A ;DATE = 251 ;DEB ;RELEASED VERS = 22 ;X.X ; COPYRIGHT (C) DIGITAL RESEARCH ; 1976, 1977, 1978, 1979, 1980, 1981 ; Modified for OSBORNE One machine by ; R.H. Frank ; 3-23-81 LINK OCCRAM2.ASM ; Assembly Constants and definitions. NTRKS = 3 ;No. of OS reserved tracks NDISKS = 2 ;Maximum supported disk drives ; WARNING: This buffer (LDBUF) must be above ROM space ; MINIMUM address is 4000h LDBUF = 4000h ;Disk xfer buffer CONI = 1 ;CONSOLE INPUT FUNCTION CONO = 2 ;CONSOLE OUTPUT FUNCTION SELF = 14 ;SELECT DISK MAXTRY = 10 ;MAXIMUM NUMBER OF RETRIES ON EACH READ/WRITE ROMJMP = 0EA36h ;64k machine vector address SELDSK = 100h+27 ;ROMVEC+24 FOR DISK SELECT SETTRK = 100h+30 ;ROMVEC+27 FOR SET T to 1st sector :4: ;Perform read or write LD HL,LDMA MOV B,H MOV C,L CALL SDMA ;Set new DMA XRA A STO A,RETRY ;clear retries :6: ;try read or write LD A,RETRY CPI MAXTRY JC :8 ;if NOT at limit of retries LDK HL,ERRMSG CALL OSTR ;indicate error CALL GCHR CMP CR JNZ RESYSR ;if wants to exit CALL CRLF ;confirm ok to process JMP :4 :8: ;check for read or write and execute INR A STO A,RETRY ;RETRY=RETRY+1 LD A,RW ;READ OR WRITE? ORA A JZ :9 ;if read CALL WRITE ;write next track OR A JNZ SYSR ;if error JMP :11 ;next :9: CALL READ ;read next track :10: ORA A JZ :11 ;if no error JMP :6 ; Update track dma and continue :11: LD HL,LDMA LDK DE,MSEC*LSECB ADD HL,DE STO HL,LDMA JMP :2 ;next :14: ;End of read of writing entire system tracks RET page START: proc LDK SP,STK ;set local stk LDK HL,SIGNON CALL OSTR ;initial sign on LDK A,3 STO A,OST ;number of system tracks GETSYS: LDK HL,ASKGET ;GET SYSTEMleading SPACES. ; ENTRY (A) = number CLZ: MOV B,A LDA CLZA CMP B JNZ CLZ4 ;If 1st Leading digit found LK A,' ' STAX D CLPLUG: INC DE ;plug with RET if no leading spaces RET CLZ4: MOV A,B STAX D INC DE LK A,0FFH STA CLZA ;SET TO IGNORE LEADING ZERO RET CLZA: DB '0' ;PDIG Convert packed binary to ASCII ; Entry HL-> Packed-decimal digits (BCD). ; DE-> Output buffer ; B = Number of digits ; C = Separator character to insert between pairs. ; Exit HL= HL + B ; DE= DE + (2*B) + (B-1) PDIG: LD A,[hl] RLC ! RLC ! RLC ! RLC AND 0Fh ADD A,'0' STO A,[de] ! INC DE LD A,[hl] ;reload byte AND 0Fh ADD A,'0' STO A,[de] ! INC DE INC HL ;advance input cursor DCR B ;decrement digit-pair count RZ ;if all done MOV A,C STO A,[de] ! INC DE JMP PDIG LIST * ; Endx NUMOUT  not DONE DCR A MOV L,E MOV H,D POP DE RET ;CLZ Check for leading zeros and if found ; change to RACK FUNCTION SETSEC = 100h+33 ;ROMVEC+30 FOR SET SECTOR FUNCTION SETDMA = 100h+36 ;ROMVEC+33 FOR SET DMA ADDRESS READF = 100h+39 ;ROMVEC+36 FOR READ FUNCTION WRITF = 100h+42 ;ROMVEC+39 FOR WRITE FUNCTION ORG ORGP page JMP START DB 'Osborne One SYSGEN program.',cr,lf DB ' V e r s i o n 1.00',cr,lf DB 'COPYRIGHT (C) 1978, DIGITAL RESEARCH',cr,lf DB 1 ;left shift back for JMP inst DB 'Z'-40h page ; UTILITY SUBROUTINES GCHR: ;READ CONSOLE CHARACTER TO REGISTER A LDK C,CONI CALL SYS ;get char ; Normalize char to UPPER case CMP 'A' or 20h RC ;if below lower case CMP ('Z' or 20h)+1 RNC ;if above 'z' AND 5Fh RET OCHR: ;Output character to console ; Entry A= char MOV E,A LDK C,CONO JMP SYS CRLF: ;SEND CARRIAGE RETURN, LINE FEED LDK A,CR CALL OCHR LDK A,LF JMP OCHR OLSTR: ;Output string to console with leading CR,LF ; Entry HL-> Fwa of message proc PUSH HL CALL CRLF ;new line POP HL ; Output string OSTR: LD A,[hl] OR A 0? CALL OLSTR CALL GCHR CMP ABTC JZ SYSR ;if request to quit SUI 'A' ;NORMALIZE DRIVE NUMBER CPI NDISKS JC GETC ;IF valid drive CALL BADSK ;indicate invalid drive JMP GETSYS ;try again GETC: ;SELECT DISK GIVEN BY REGISTER A ADI 'A' STO A,GDISK ;TO SET MESSAGE SUI 'A' CALL SEL ;TO SELECT THE DRIVE ; GETSYS, SET RW TO READ AND GET THE SYSTEM CALL CRLF LDK HL,GETMSG CALL OSTR CALL GCHR CPI CR JNZ RESYSR ;if request to quit CALL CRLF ;confirm XRA A STO A,RW ;set flag for READ CALL GETPUT ;Get system into memory LD A,LDBUF CMP 0C3h JZ :12 ;if 1st byte is JMP LDK HL,BSYSD CALL OSTR ;indicate ill-formed disk JMP SYSR ;exit via abort :12: LDK HL,RDONE CALL OSTR ;complete message to user PUTSYS: ;Now put the system on user requested disk LDK HL,ASKPUT CALL OLSTR CALL GCHR CPI CR JZ RESYSR SUI 'A' CPI NDISKS JC PUTC ;if valid drive ; INVALID DRIVE NAME CALL BADSK JMP PUTSYS ;TO TRY AGAIN PUTC: ;SET DISK FROequ 13h ;stop/start screen ctlu equ 15h ;line delete ctlx equ 18h ;=ctl-u ctlz equ 1ah ;end of file rubout equ 7fh ;char delete tab equ 09h ;tab char cr equ 0dh ;carriage return lf equ 0ah ;line feed ctl equ 5eh ;up arrow ; db 0,0,0,0,0,0 ; ; enter here from the user's program with function number in c, ; and information address in d,e jmp bdose ;past parameter block ; ; ************************************************ ; *** relative locations 0009 - 000e *** ; ************************************************ pererr: dw persub ;permanent error subroutine selerr: dw selsub ;select error subroutine roderr: dw rodsub ;ro disk error subroutine roferr: dw rofsub ;ro file error subroutine ; ; bdose: ;arrive here from user programs xchg! shld info! xchg ;info=DE, DE=info mov a,e! sta linfo ;linfo = low(info) - don't equ lxi h,0! shld aret ;return value defaults to 0000 ;save user's stack pointer, set to local stack dad sp! shld entsp ;entsp = stackptr lxi sp,lstack ;lM REGISTER C ADI 'A' STO A,PDISK ;MESSAGE SET SUI 'A' CALL SEL ;SELECT DEST DRIVE ; PUT SYSTEM, SET RW TO WRITE LDK HL,PUTMSG CALL OLSTR ;confirm prompt CALL GCHR CPI CR JNZ RESYSR ;if abort CALL CRLF LDK HL,RW MVI M,1 CALL GETPUT ;TO PUT SYSTEM BACK ON DISKETTE LDK HL,DONE CALL OSTR JMP PUTSYS ;FOR ANOTHER PUT OPERATION RESYSR: LDK A,0 CALL SEL CALL CRLF JMP SYSR BADSK: ;BAD DISK NAME LDK HL,QDISK CALL OSTR ;indicate invalid RET page ; Data and all message constants SIGNON: DB cr,lf,lf,' ' DB ESC,'l','SYSGEN',ESC,'m' DB ' Operating System Duplication Program.' DB cr,lf DB ESC,')',' OSBORNE COMPUTER SYSTEM ONE',ESC,'(' DB 0 ASKGET: DB ' ',ESC,'l','Source',ESC,'m' DB ' Drive (',ESC,'lA or B',ESC,'m', ') ', 0 GETMSG: DB ' ',ESC,'l','Source',ESC,'m' DB ' is on drive ' GDISK: DB ' ' ;drive name DB ', Then type ',ESC,'l','RETURN',ESC,'m',0 ASKPUT: DB ' ------------------------------------------------' DB cr,lf,' title 'Bdos Interface, Bdos, Version 2.2 Feb, 1980' ;***************************************************************** ;***************************************************************** ;** ** ;** B a s i c D i s k O p e r a t i n g S y s t e m ** ;** I n t e r f a c e M o d u l e ** ;** ** ;***************************************************************** ;***************************************************************** ; ; Copyright (c) 1978, 1979, 1980 ; Digital Research ; Box 579, Pacific Grove ; California ; ; ; 20 january 1980 ;REV = D ;DATE = 238 ;DEB ;REORGED TO D800H ; ; on equ 0ffffh off equ 00000h test equ off ; ;if test ; org 0dc00h ; else ; org 0800h endif ORG 0D800H ; bios value defined at end of module ; ssize equ 24 ;24 level stack ; ; low memory locations reboot equ 0000h ;reboot systocal stack setup xra a! sta fcbdsk! sta resel ;fcbdsk,resel=false lxi h,goback ;return here after all functions push h ;jmp goback equivalent to ret mov a,c! cpi nfuncs! rnc ;skip if invalid # mov c,e ;possible output character to C lxi h,functab! mov e,a! mvi d,0 ;DE=func, HL=.ciotab dad d! dad d! mov e,m! inx h! mov d,m ;DE=functab(func) lhld info ;info in DE for later xchg xchg! pchl ;dispatched ; ; dispatch table for functions functab: dw wbootf, func1, func2, func3 dw punchf, listf, func6, func7 dw func8, func9, func10,func11 diskf equ ($-functab)/2 ;disk funcs dw func12,func13,func14,func15 dw func16,func17,func18,func19 dw func20,func21,func22,func23 dw func24,func25,func26,func27 dw func28,func29,func30,func31 dw func32,func33,func34,func35 dw func36,func37,func38,func39 ; dw func40 ; nfuncs equ ($-functab)/2 ; ; ; error subroutines persub: ;report permanent error lxi h,permsg! call errflg ;to report the error cpi ctlc! jz reboot ;reboot if r ' DB ESC,'l','DESTINATION',ESC,'m' DB ' (A,B or RETURN to exit) ',0 PUTMSG: DB ' ' DB ESC,'lDESTINATION',ESC,'m' DB ' is on drive ' PDISK: DS 1 ;FILLED IN AT PUT FUNCTION DB ', Then type ',ESC,'lRETURN',ESC,'m',0 ERRMSG: DB 'PERMANENT ERROR, TYPE RETURN TO IGNORE',0 RDONE: DB ' Read of SYSTEM is ',ESC,'lcomplete',ESC,'m.' DB cr,lf,lf,0 DONE: DB ' System has been copied successfully.',0 QDISK: DB ' ',ESC,')INVALID',ESC,'(',0 BSYSD: DB ' System Disk is ',ESC,'lunauthorized',ESC,'m, ABORT.' DB 0 space 4,10 ; Variables OST: DS 1 ;number of system tracks TRACK: DS 1 ;CURRENT TRACK SECTOR: DS 1 ;CURRENT SECTOR RW: DS 1 ;READ IF 0, WRITE IF 1 LDMA: DS 2 ;CURRENT DMA ADDRESS RETRY: DS 1 ;NUMBER OF TRIES ON THIS SECTOR DS 30*2 STK: DS 0 IF * > LDBUF .err MSG 'Error SYSGEN too large ', * ENDIF ; ENDx SYSGEN me DB ', Then type ',ESC,'l','RETURN',ESC,'m',0 ASKPUT: DB ' ------------------------------------------------' DB cr,lf,'em ioloc equ 0003h ;i/o byte location bdosa equ 0006h ;address field of jmp BDOS ; ; bios access constants bootf set bios+3*0 ;cold boot function wbootf set bios+3*1 ;warm boot function constf set bios+3*2 ;console status function coninf set bios+3*3 ;console input function conoutf set bios+3*4 ;console output function listf set bios+3*5 ;list output function punchf set bios+3*6 ;punch output function readerf set bios+3*7 ;reader input function homef set bios+3*8 ;disk home function seldskf set bios+3*9 ;select disk function settrkf set bios+3*10 ;set track function setsecf set bios+3*11 ;set sector function setdmaf set bios+3*12 ;set dma function readf set bios+3*13 ;read disk function writef set bios+3*14 ;write disk function liststf set bios+3*15 ;list status function sectran set bios+3*16 ;sector translate ; ; equates for non graphic characters ctlc equ 03h ;control c ctle equ 05h ;physical eol ctlh equ 08h ;backspace ctlp equ 10h ;prnt toggle ctlr equ 12h ;repeat line ctls 1esponse is ctlc ret ;and ignore the error ; selsub: ;report select error lxi h,selmsg! jmp wait$err ;wait console before boot ; rodsub: ;report write to read/only disk lxi h,rodmsg! jmp wait$err ;wait console ; rofsub: ;report read/only file lxi h,rofmsg ;drop through to wait for console ; wait$err: ;wait for response before boot call errflg! jmp reboot ; ; error messages dskmsg: db 'Bdos Err On ' dskerr: db ' : $' ;filled in by errflg permsg: db 'Bad Sector$' selmsg: db 'Select$' rofmsg: db 'File ' rodmsg: db 'R/O$' ; ; errflg: ;report error to console, message address in HL push h! call crlf ;stack mssg address, new line lda curdsk! adi 'A'! sta dskerr ;current disk name lxi b,dskmsg! call print ;the error message pop b! call print ;error mssage tail ;jmp conin ;to get the input character ;(drop through to conin) ;ret ; ; ; console handlers conin: ;read console character to A lxi h,kbchar! mov a,m! mvi m,0! ora a! rnz ;no previous keyboard charactespace character dcr m ;column = column - 1 ret notbacksp: ;not a backspace character, eol? cpi lf! rnz ;return if not ;end of line, column = 0 mvi m,0 ;column = 0 ret ; ctlout: ;send C character with possible preceding up-arrow mov a,c! call echoc ;cy if not graphic (or special case) jnc tabout ;skip if graphic, tab, cr, lf, or ctlh ;send preceding up arrow push psw! mvi c,ctl! call conout ;up arrow pop psw! ori 40h ;becomes graphic letter mov c,a ;ready to print ;(drop through to tabout) ; tabout: ;expand tabs to console mov a,c! cpi tab! jnz conout ;direct to conout if not ;tab encountered, move to next tab position tab0: mvi c,' '! call conout ;another blank lda column! ani 111b ;column mod 8 = 0 ? jnz tab0 ;back for another if not ret ; ; backup: ;back-up one screen position call pctlh! mvi c,' '! call conoutf ; (drop through to pctlh) ; pctlh: ;send ctlh to console without affecting column count mvi c,ctlh! jmp con another character note: ;not end of line, list toggle? cpi ctlp! jnz notp ;skip if not ctlp ;list toggle - change parity push h ;save next to fill - 1 lxi h,listcp ;HL=.listcp flag mvi a,1! sub m ;True-listcp mov m,a ;listcp = not listcp pop h! jmp readnx ;for another char notp: ;not a ctlp, line delete? cpi ctlx! jnz notx pop h ;discard start position ;loop while column > strtcol backx: lda strtcol! lxi h,column cmp m! jnc read ;start again dcr m ;column = column - 1 call backup ;one position jmp backx notx: ;not a control x, control u? ;not control-X, control-U? cpi ctlu! jnz notu ;skip if not ;delete line (ctlu) call crlfp ;physical eol pop h ;discard starting position jmp read ;to start all over notu: ;not line delete, repeat line? cpi ctlr! jnz notr linelen: ;repeat line, or compute line len (ctlh) ;if compcol > 0 push b! call crlfp ;save line length pop b! pop r ready jmp coninf ;get character externally ;ret ; conech: ;read character with echo call conin! call echoc! rc ;echo character? ;character must be echoed before return push psw! mov c,a! call tabout! pop psw ret ;with character in A ; echoc: ;echo character if graphic ;cr, lf, tab, or backspace cpi cr! rz ;carriage return? cpi lf! rz ;line feed? cpi tab! rz ;tab? cpi ctlh! rz ;backspace? cpi ' '! ret ;carry set if not graphic ; conbrk: ;check for character ready lda kbchar! ora a! jnz conb1 ;skip if active kbchar ;no active kbchar, check external break call constf! ani 1! rz ;return if no char ready ;character ready, read it call coninf ;to A cpi ctls! jnz conb0 ;check stop screen function ;found ctls, read next character call coninf ;to A cpi ctlc! jz reboot ;ctlc implies re-boot ;not a reboot, act as if nothing has happened xra a! ret ;with zero in accumulator conb0: ;character in accum, save it sta kbchar conb1: ;returnoutf ;ret ; crlfp: ;print #, cr, lf for ctlx, ctlu, ctlr functions ;then move to strtcol (starting column) mvi c,'#'! call conout call crlf ;column = 0, move to position strtcol crlfp0: lda column! lxi h,strtcol cmp m! rnc ;stop when column reaches strtcol mvi c,' '! call conout ;print blank jmp crlfp0 ;; ; crlf: ;carriage return line feed sequence mvi c,cr! call conout! mvi c,lf! jmp conout ;ret ; print: ;print message until M(BC) = '$' ldax b! cpi '$'! rz ;stop on $ ;more to print inx b! push b! mov c,a ;char to C call tabout ;another character printed pop b! jmp print ; read: ;read to info address (max length, current length, buffer) lda column! sta strtcol ;save start for ctl-x, ctl-h lhld info mov c,m! inx h! push h! mvi b,0 ;B = current buffer length, ;C = maximum buffer length, ;HL= next to fill - 1 readnx: ;read next character, BC, HL active push b! push h ;blen, cmax, HL saved readn0: call conin ;next char in A anh! push h! push b ;bcur, cmax active, beginning buff at HL rep0: mov a,b! ora a! jz rep1 ;count len to 00 inx h! mov c,m ;next to print dcr b! push b! push h ;count length down call ctlout ;character echoed pop h! pop b ;recall remaining count jmp rep0 ;for the next character rep1: ;end of repeat, recall lengths ;original BC still remains pushed push h ;save next to fill lda compcol! ora a ;>0 if computing length jz readn0 ;for another char if so ;column position computed for ctlh lxi h,column! sub m ;diff > 0 sta compcol ;count down below ;move back compcol-column spaces backsp: ;move back one more space call backup ;one space lxi h,compcol! dcr m jnz backsp jmp readn0 ;for next character notr: ;not a ctlr, place into buffer rdecho: inx h! mov m,a ;character filled to mem inr b ;blen = blen + 1 rdech1: ;look for a random control character push b! push h ;active values saved mov c,a ;r with true set in accumulator mvi a,1! ret ; conout: ;compute character position/write console char from C ;compcol = true if computing column position lda compcol! ora a! jnz compout ;write the character, then compute the column ;write console character from C push b! call conbrk ;check for screen stop function pop b! push b ;recall/save character call conoutf ;externally, to console pop b! push b ;recall/save character ;may be copying to the list device lda listcp! ora a! cnz listf ;to printer, if so pop b ;recall the character compout: mov a,c ;recall the character ;and compute column position lxi h,column ;A = char, HL = .column cpi rubout! rz ;no column change if nulls inr m ;column = column + 1 cpi ' '! rnc ;return if graphic ;not graphic, reset column position dcr m ;column = column - 1 mov a,m! ora a! rz ;return if at zero ;not at zero, may be backspace or end line mov a,c ;character back to A cpi ctlh! jnz notbacksp ;backi 7fh ;mask parity bit pop h! pop b ;reactivate counters cpi cr! jz readen ;end of line? cpi lf! jz readen ;also end of line cpi ctlh! jnz noth ;backspace? ;do we have any characters to back over? mov a,b! ora a! jz readnx ;characters remain in buffer, backup one dcr b ;remove one character lda column! sta compcol ;col > 0 ;compcol > 0 marks repeat as length compute jmp linelen ;uses same code as repeat noth: ;not a backspace cpi rubout! jnz notrub ;rubout char? ;rubout encountered, rubout if possible mov a,b! ora a! jz readnx ;skip if len=0 ;buffer has characters, resend last char mov a,m! dcr b! dcx h ;A = last char ;blen=blen-1, next to fill - 1 decremented jmp rdech1 ;act like this is an echo ; notrub: ;not a rubout character, check end line cpi ctle! jnz note ;physical end line? ;yes, save active counters and force eol push b! push h! call crlf xra a! sta strtcol ;start position = 00 jmp readn0 ;for2eady to print call ctlout ;may be up-arrow C pop h! pop b! mov a,m ;recall char cpi ctlc ;set flags for reboot test mov a,b ;move length to A jnz notc ;skip if not a control c cpi 1 ;control C, must be length 1 jz reboot ;reboot if blen = 1 ;length not one, so skip reboot notc: ;not reboot, are we at end of buffer? cmp c! jc readnx ;go for another if not readen: ;end of read operation, store blen pop h! mov m,b ;M(current len) = B mvi c,cr! jmp conout ;return carriage ;ret func1: ;return console character with echo call conech jmp sta$ret ; func2: equ tabout ;write console character with tab expansion ; func3: ;return reader character call readerf jmp sta$ret ; ;func4: equated to punchf ;write punch character ; ;func5: equated to listf ;write list character ;write to list device ; func6: ;direct console i/o - read if 0ffh mov a,c! inr a! jz dirinp ;0ffh => 00h, means input mode inr a! jz constf ;0feH in C for su 000h ;constant false enddir equ 0ffffh ;end of directory byte equ 1 ;number of bytes for "byte" type word equ 2 ;number of bytes for "word" type ; ; fixed addresses in low memory tfcb equ 005ch ;default fcb location tbuff equ 0080h ;default buffer location ; ; fixed addresses referenced in bios module are ; pererr (0009), selerr (000c), roderr (000f) ; ; error message handlers ; ;per$error: ;report permanent error to user ; lxi h,pererr jmp goerr ; ;rod$error: ; ;report read/only disk error ; lxi h,roderr jmp goerr ; ; ;rof$error: ; ;report read/only file error ; ; lxi h,roferr ;jmp goerr ; sel$error: ;report select error lxi h,selerr ; ; ; goerr: ;HL = .errorhandler, call subroutine mov e,m! inx h! mov d,m ;address of routine in DE xchg! pchl ;to subroutine ; ; ; ; local subroutines for bios interface ; move: ;move data length of length C from source DE to ;destination given by HL inr c ;in case it is zero move0: dcr c! r, dma diocomp: ;check for disk errors ora a! rz ; lxi h,pererr ; jmp goerr ; seekdir: ;seek the record containing the current dir entry lhld dcnt ;directory counter to HL mvi c,dskshf! call hlrotr ;value to HL shld arecord! shld drec ;ready for seek ; jmp seek ; ;ret ; ; seek: ;seek the track given by arecord (actual record) ;local equates for registers arech equ b! arecl equ c ;arecord = BC crech equ d! crecl equ e ;currec = DE ctrkh equ h! ctrkl equ l ;curtrk = HL tcrech equ h! tcrecl equ l ;tcurrec = HL ;load the registers from memory lxi h,arecord! mov arecl,m! inx h! mov arech,m lhld curreca ! mov crecl,m! inx h! mov crech,m lhld curtrka ! mov a,m! inx h! mov ctrkh,m! mov ctrkl,a ;loop while arecord < currec seek0: mov a,arecl! sub crecl! mov a,arech! sbb crech jnc seek1 ;skip if arecord >= currec ;currec = currec - sectpt push ctrkh! lhld sectpt mov a,crecl! sub l! mov crecl,a mov a,crech! sbb h! mov crech,a pop cttatus ;direct output function jmp conoutf ; dirinp: call constf ;status check ora a! jz retmon ;skip, return 00 if not ready ;character is ready, get it call coninf ;to A jmp sta$ret ; func7: ;return io byte lda ioloc jmp sta$ret ; func8: ;set i/o byte lxi h,ioloc mov m,c ret ;jmp goback ; func9: ;write line until $ encountered xchg ;was lhld info mov c,l! mov b,h ;BC=string address jmp print ;out to console ; func10: equ read ;read a buffered console line ; func11: ;check console status call conbrk ;(drop through to sta$ret) sta$ret: ;store the A register to aret sta aret func$ret: ; ret ;jmp goback (pop stack for non cp/m functions) ; setlret1: ;set lret = 1 mvi a,1! jmp sta$ret ; ; ; ; ; data areas ; compcol:db 0 ;true if computing column position strtcol:db 0 ;starting column position after read column: db 0 ;column position listcp: db 0 ;listing toggle kbchar: db 0 ;initial key char = 00 entsp: ds 2 ;entryz ;more to move ldax d! mov m,a ;one byte moved inx d! inx h ;to next byte jmp move0 ; selectdisk: ;select the disk drive given by curdsk, and fill ;the base addresses curtrka - alloca, then fill ;the values of the disk parameter block lda curdsk! mov c,a ;current disk# to c ;lsb of e = 0 if not yet logged - in call seldskf ;HL filled by call ;HL = 0000 if error, otherwise disk headers mov a,h! ora l! rz ;return with 0000 in HL and z flag ;disk header block address in hl mov e,m! inx h! mov d,m! inx h ;DE=.tran shld cdrmaxa! inx h! inx h ;.cdrmax shld curtrka! inx h! inx h ;HL=.currec shld curreca! inx h! inx h ;HL=.buffa ;DE still contains .tran xchg! shld tranv ;.tran vector lxi h,buffa ;DE= source for move, HL=dest mvi c,addlist! call move ;addlist filled ;now fill the disk parameter block lhld dpbaddr! xchg ;DE is source lxi h,sectpt ;HL is destination mvi c,dpblist! call move ;data filled ;now set single/double map mode lhld maxall ;rkh ;curtrk = curtrk - 1 dcx ctrkh jmp seek0 ;for another try seek1: ;look while arecord >= (t:=currec + sectpt) push ctrkh lhld sectpt! dad crech ;HL = currec+sectpt jc seek2 ;can be > FFFFH mov a,arecl! sub tcrecl! mov a,arech! sbb tcrech jc seek2 ;skip if t > arecord ;currec = t xchg ;curtrk = curtrk + 1 pop ctrkh! inx ctrkh jmp seek1 ;for another try seek2: pop ctrkh ;arrive here with updated values in each register push arech! push crech! push ctrkh ;to stack for later ;stack contains (lowest) BC=arecord, DE=currec, HL=curtrk xchg! lhld offset! dad d ;HL = curtrk+offset mov b,h! mov c,l! call settrkf ;track set up ;note that BC - curtrk is difference to move in bios pop d ;recall curtrk lhld curtrka! mov m,e! inx h! mov m,d ;curtrk updated ;now compute sector as arecord-currec pop crech ;recall currec lhld curreca! mov m,crecl! inx h! mov m,crech pop arech ;BC=arecord, DE=currec mov a,arecl! sub crecl! mov arecl,a mov a,ar stack pointer ds ssize*2 ;stack size lstack: ; end of Basic I/O System ; ;***************************************************************** ;***************************************************************** ; ; common values shared between bdosi and bdos usrcode:db 0 ;current user number curdsk: db 0 ;current disk number info: ds 2 ;information address aret: ds 2 ;address value to return lret equ aret ;low(aret) ; ;***************************************************************** ;***************************************************************** ;** ** ;** B a s i c D i s k O p e r a t i n g S y s t e m ** ;** ** ;***************************************************************** ;***************************************************************** ; dvers equ 22h ;version 2.2 ; module addresses ; ; literal constants true equ 0ffh ;constant true false eqlargest allocation number mov a,h ;00 indicates < 255 lxi h,single! mvi m,true ;assume a=00 ora a! jz retselect ;high order of maxall not zero, use double dm mvi m,false retselect: mvi a,true! ora a! ret ;select disk function ok ; home: ;move to home position, then offset to start of dir call homef ;move to track 00, sector 00 reference ;lxi h,offset ;mov c,m ;inx h ;mov b,m ;call settrkf ; ;first directory position selected xra a ;constant zero to accumulator lhld curtrka! mov m,a! inx h! mov m,a ;curtrk=0000 lhld curreca! mov m,a! inx h! mov m,a ;currec=0000 ;curtrk, currec both set to 0000 ret ; rdbuff: ;read buffer and check condition call readf ;current drive, track, sector, dma jmp diocomp ;check for i/o errors ; wrbuff: ;write buffer and check condition ;write type (wrtype) is in register C ;wrtype = 0 => normal write operation ;wrtype = 1 => directory write operation ;wrtype = 2 => start of new block call writef ;current drive, track, sector3ech! sbb crech! mov arech,a lhld tranv! xchg ;BC=sector#, DE=.tran call sectran ;HL = tran(sector) mov c,l! mov b,h ;BC = tran(sector) jmp setsecf ;sector selected ;ret ; ; file control block (fcb) constants empty equ 0e5h ;empty directory entry lstrec equ 127 ;last record# in extent recsiz equ 128 ;record size fcblen equ 32 ;file control block size dirrec equ recsiz/fcblen ;directory elts / record dskshf equ 2 ;log2(dirrec) dskmsk equ dirrec-1 fcbshf equ 5 ;log2(fcblen) ; extnum equ 12 ;extent number field maxext equ 31 ;largest extent number ubytes equ 13 ;unfilled bytes field modnum equ 14 ;data module number maxmod equ 15 ;largest module number fwfmsk equ 80h ;file write flag is high order modnum namlen equ 15 ;name length reccnt equ 15 ;record count field dskmap equ 16 ;disk map field lstfcb equ fcblen-1 nxtrec equ fcblen ranrec equ nxtrec+1;random record field (2 bytes) ; ; reserved file indicators rofile equ 9 ;high order of first type char invis equ 10 ;invisible fet current extent field address to A lhld info! lxi d,extnum! dad d ;HL=.fcb(extnum) ret ; getfcba: ;compute reccnt and nxtrec addresses for get/setfcb lhld info! lxi d,reccnt! dad d! xchg ;DE=.fcb(reccnt) lxi h,(nxtrec-reccnt)! dad d ;HL=.fcb(nxtrec) ret ; getfcb: ;set variables from currently addressed fcb call getfcba ;addresses in DE, HL mov a,m! sta vrecord ;vrecord=fcb(nxtrec) xchg! mov a,m! sta rcount ;rcount=fcb(reccnt) call getexta ;HL=.fcb(extnum) lda extmsk ;extent mask to a ana m ;fcb(extnum) and extmsk sta extval ret ; setfcb: ;place values back into current fcb call getfcba ;addresses to DE, HL lda seqio cpi 02! jnz setfcb1! xra a ;check ranfill setfcb1: mov c,a ;=1 if sequential i/o lda vrecord! add c! mov m,a ;fcb(nxtrec)=vrecord+seqio xchg! lda rcount! mov m,a ;fcb(reccnt)=rcount ret ; hlrotr: ;hl rotate right by amount C inr c ;in case zero hlrotr0: dcr c! rz ;return when zero mov a,h! ora a! rar! mov h,a ;high byte mov a,l (file write flag) lhld info! lxi d,modnum! dad d ;HL=.fcb(modnum) mov a,m! ret ;A=fcb(modnum) ; clrmodnum: ;clear the module number field for user open/make call getmodnum! mvi m,0 ;fcb(modnum)=0 ret ; setfwf: call getmodnum ;HL=.fcb(modnum), A=fcb(modnum) ;set fwf (file write flag) to "1" ori fwfmsk! mov m,a ;fcb(modnum)=fcb(modnum) or 80h ;also returns non zero in accumulator ret ; ; compcdr: ;return cy if cdrmax > dcnt lhld dcnt! xchg ;DE = directory counter lhld cdrmaxa ;HL=.cdrmax mov a,e! sub m ;low(dcnt) - low(cdrmax) inx h ;HL = .cdrmax+1 mov a,d! sbb m ;hig(dcnt) - hig(cdrmax) ;condition dcnt - cdrmax produces cy if cdrmax>dcnt ret ; setcdr: ;if not (cdrmax > dcnt) then cdrmax = dcnt+1 call compcdr rc ;return if cdrmax > dcnt ;otherwise, HL = .cdrmax+1, DE = dcnt inx d! mov m,d! dcx h! mov m,e ret ; subdh: ;compute HL = DE - HL mov a,e! sub l! mov l,a! mov a,d! sbb h! mov h,a ret ; newchecksum: mvi c,true ;drop through to compute ile in dir command ; equ 11 ;reserved ; ; utility functions for file access ; dm$position: ;compute disk map position for vrecord to HL lxi h,blkshf! mov c,m ;shift count to C lda vrecord ;current virtual record to A dmpos0: ora a! rar! dcr c! jnz dmpos0 ;A = shr(vrecord,blkshf) = vrecord/2**(sect/block) mov b,a ;save it for later addition mvi a,8! sub m ;8-blkshf to accumulator mov c,a ;extent shift count in register c lda extval ;extent value ani extmsk dmpos1: ;blkshf = 3,4,5,6,7, C=5,4,3,2,1 ;shift is 4,3,2,1,0 dcr c! jz dmpos2 ora a! ral! jmp dmpos1 dmpos2: ;arrive here with A = shl(ext and extmsk,7-blkshf) add b ;add the previous shr(vrecord,blkshf) value ;A is one of the following values, depending upon alloc ;bks blkshf ;1k 3 v/8 + extval * 16 ;2k 4 v/16+ extval * 8 ;4k 5 v/32+ extval * 4 ;8k 6 v/64+ extval * 2 ;16k 7 v/128+extval * 1 ret ;with dm$position in A ; getdm: ;return disk map value from position g! rar! mov l,a ;low byte jmp hlrotr0 ; ; compute$cs: ;compute checksum for current directory buffer mvi c,recsiz ;size of directory buffer lhld buffa ;current directory buffer xra a ;clear checksum value computecs0: add m! inx h! dcr c ;cs=cs+buff(recsiz-C) jnz computecs0 ret ;with checksum in A ; hlrotl: ;rotate the mask in HL by amount in C inr c ;may be zero hlrotl0: dcr c! rz ;return if zero dad h! jmp hlrotl0 ; set$cdisk: ;set a "1" value in curdsk position of BC push b ;save input parameter lda curdsk! mov c,a ;ready parameter for shift lxi h,1 ;number to shift call hlrotl ;HL = mask to integrate pop b ;original mask mov a,c! ora l! mov l,a mov a,b! ora h! mov h,a ;HL = mask or rol(1,curdsk) ret ; nowrite: ;return true if dir checksum difference occurred lhld rodsk! lda curdsk! mov c,a! call hlrotr mov a,l! ani 1b! ret ;non zero if nowrite ; set$ro: ;set current disk to read only lxi h,rodsk! mov c,m! inx h! mov b,m call set$cdisk ;snew checksum checksum: ;compute current checksum record and update the ;directory element if C=true, or check for = if not ;drec < chksiz? lhld drec! xchg! lhld chksiz! call subdh ;DE-HL rnc ;skip checksum if past checksum vector size ;drec < chksiz, so continue push b ;save init flag call compute$cs ;check sum value to A lhld checka ;address of check sum vector xchg lhld drec ;value of drec dad d ;HL = .check(drec) pop b ;recall true=0ffh or false=00 to C inr c ;0ffh produces zero flag jz initial$cs ;not initializing, compare cmp m ;compute$cs=check(drec)? rz ;no message if ok ;checksum error, are we beyond ;the end of the disk? call compcdr rnc ;no message if so call set$ro ;read/only disk set ret initial$cs: ;initializing the checksum mov m,a! ret ; ; wrdir: ;write the current directory entry, set checksum call newchecksum ;initialize entry call setdir ;directory dma mvi c,1 ;indicates a write directory operaiven by BC lhld info ;base address of file control block lxi d,dskmap! dad d ;HL =.diskmap dad b ;index by a single byte value lda single ;single byte/map entry? ora a! jz getdmd ;get disk map single byte mov l,m! mvi h,0! ret ;with HL=00bb getdmd: dad b ;HL=.fcb(dm+i*2) ;double precision value returned mov e,m! inx h! mov d,m! xchg! ret ; index: ;compute disk block number from current fcb call dm$position ;0...15 in register A mov c,a! mvi b,0! call getdm ;value to HL shld arecord! ret ; allocated: ;called following index to see if block allocated lhld arecord! mov a,l! ora h! ret ; atran: ;compute actual record address, assuming index called lda blkshf ;shift count to reg A lhld arecord atran0: dad h! dcr a! jnz atran0 ;shl(arecord,blkshf) shld arecord1 ;save shifted block # lda blkmsk! mov c,a ;mask value to C lda vrecord! ana c ;masked value in A ora l! mov l,a ;to HL shld arecord ;arecord=HL or (vrecord and blkmsk) ret ; getexta: ;gets bit to 1 shld rodsk ;high water mark in directory goes to max lhld dirmax! inx h! xchg ;DE = directory max lhld cdrmaxa ;HL = .cdrmax mov m,e! inx h! mov m,d ;cdrmax = dirmax ret ; check$rodir: ;check current directory element for read/only status call getdptra ;address of element ; check$rofile: ;check current buff(dptr) or fcb(0) for r/o status lxi d,rofile! dad d ;offset to ro bit mov a,m! ral! rnc ;return if not set lxi h,roferr! jmp goerr ; ; jmp rof$error ;exit to read only disk message ; ; check$write: ;check for write protected disk call nowrite! rz ;ok to write if not rodsk lxi h,roderr! jmp goerr ; jmp rod$error ;read only disk error ; getdptra: ;compute the address of a directory element at ;positon dptr in the buffer lhld buffa! lda dptr ; addh: ;HL = HL + A add l! mov l,a! rnc ;overflow to H inr h! ret ; ; getmodnum: ;compute the address of the module number ;bring module number to accumulator ;(high order bit is fwf4tion call wrbuff ;write the buffer jmp setdata ;to data dma address ;ret ; rd$dir: ;read a directory entry into the directory buffer call setdir ;directory dma call rdbuff ;directory record loaded ; jmp setdata to data dma address ;ret ; setdata: ;set data dma address lxi h,dmaad! jmp setdma ;to complete the call ; setdir: ;set directory dma address lxi h,buffa ;jmp setdma to complete call ; setdma: ;HL=.dma address to set (i.e., buffa or dmaad) mov c,m! inx h! mov b,m ;parameter ready jmp setdmaf ; ; dir$to$user: ;copy the directory entry to the user buffer ;after call to search or searchn by user code lhld buffa! xchg ;source is directory buffer lhld dmaad ;destination is user dma address mvi c,recsiz ;copy entire record jmp move ;ret ; end$of$dir: ;return zero flag if at end of directory, non zero ;if not at end (end of dir if dcnt = 0ffffh) lxi h,dcnt! mov a,m ;may be 0ffh inx h! cmp m ;low(dcnt) = high(dcnt)? rnz ; push d! call getallocbit ;shifted val A, count in D ani 1111$1110b ;mask low bit to zero (may be set) pop b! ora c ;low bit of C is masked into A ; jmp rotr ;to rotate back into proper position ;ret rotr: ;byte value from ALLOC is in register A, with shift count ;in register C (to place bit back into position), and ;target ALLOC position in registers HL, rotate and replace rrc! dcr d! jnz rotr ;back into position mov m,a ;back to ALLOC ret ; scandm: ;scan the disk map addressed by dptr for non-zero ;entries, the allocation vector entry corresponding ;to a non-zero entry is set to the value of C (0,1) call getdptra ;HL = buffa + dptr ;HL addresses the beginning of the directory entry lxi d,dskmap! dad d ;hl now addresses the disk map push b ;save the 0/1 bit to set mvi c,fcblen-dskmap+1 ;size of single byte disk map + 1 scandm0: ;loop once for each disk map entry pop d ;recall bit parity dcr c! rz ;all done scanning? ;no, get next entry for scan push dile found, mark in lret dcr a! sta lret ;lret = 255 pdollar: ;now scan the disk map for allocated blocks mvi c,1 ;set to allocated call scandm call setcdr ;set cdrmax to dcnt jmp initial2 ;for another entry ; copy$dirloc: ;copy directory location to lret following ;delete, rename, ... ops lda dirloc! jmp sta$ret ; ; ret ; ; compext: ;compare extent# in A with that in C, return nonzero ;if they do not match push b ;save C's original value push psw! lda extmsk! cma! mov b,a ;B has negated form of extent mask mov a,c! ana b! mov c,a ;low bits removed from C pop psw! ana b ;low bits removed from A sub c! ani maxext ;set flags pop b ;restore original values ret ; search: ;search for directory element of length C at info mvi a,0ffh! sta dirloc ;changed if actually found lxi h,searchl! mov m,c ;searchl = C lhld info! shld searcha ;searcha = info call set$end$dir ;dcnt = enddir call home ;to start at the beginning ;(drop through to searchn) non zero returned if different ;high and low the same, = 0ffh? inr a ;0ffh becomes 00 if so ret ; set$end$dir: ;set dcnt to the end of the directory lxi h,enddir! shld dcnt! ret ; read$dir: ;read next directory entry, with C=true if initializing lhld dirmax! xchg ;in preparation for subtract lhld dcnt! inx h! shld dcnt ;dcnt=dcnt+1 ;continue while dirmax >= dcnt (dirmax-dcnt no cy) call subdh ;DE-HL jnc read$dir0 ;yes, set dcnt to end of directory jmp set$end$dir ; ; ret ; read$dir0: ;not at end of directory, seek next element ;initialization flag is in C lda dcnt! ani dskmsk ;low(dcnt) and dskmsk mvi b,fcbshf ;to multiply by fcb size read$dir1: add a! dcr b! jnz read$dir1 ;A = (low(dcnt) and dskmsk) shl fcbshf sta dptr ;ready for next dir operation ora a! rnz ;return if not a new record push b ;save initialization flag C call seek$dir ;seek proper record call rd$dir ;read the directory record pop b ;recall initialization flag  ;replace bit parity lda single! ora a! jz scandm1 ;single byte scan operation push b ;save counter push h ;save map address mov c,m! mvi b,0 ;BC=block# jmp scandm2 scandm1: ;double byte scan operation dcr c ;count for double byte push b ;save counter mov c,m! inx h! mov b,m ;BC=block# push h ;save map address scandm2: ;arrive here with BC=block#, E=0/1 mov a,c! ora b ;skip if = 0000 jz scanm3 lhld maxall ;check invalid index mov a,l! sub c! mov a,h! sbb b ;maxall - block# cnc set$alloc$bit ; ;bit set to 0/1 scanm3: ; pop h! inx h ;to next bit position pop b ;recall counter jmp scandm0 ;for another item ; initialize: ;initialize the current disk ;lret = false ;set to true if $ file exists ;compute the length of the allocation vector - 2 lhld maxall! mvi c,3 ;perform maxall/8 ;number of bytes in alloc vector is (maxall/8)+1 call hlrotr! inx h ;HL = maxall/8+1 mov b,h! mov c,l ;count down BC til z; ; searchn: ;search for the next directory element, assuming ;a previous call on search which sets searcha and ;searchl mvi c,false! call read$dir ;read next dir element call end$of$dir! jz search$fin ;skip to end if so ;not end of directory, scan for match lhld searcha! xchg ;DE=beginning of user fcb ldax d ;first character cpi empty ;keep scanning if empty jz searchnext ;not empty, may be end of logical directory push d ;save search address call compcdr ;past logical end? pop d ;recall address jnc search$fin ;artificial stop searchnext: call getdptra ;HL = buffa+dptr lda searchl! mov c,a ;length of search to c mvi b,0 ;b counts up, c counts down searchloop: mov a,c! ora a! jz endsearch ldax d! cpi '?'! jz searchok ;? matches all ;scan next character if not ubytes mov a,b! cpi ubytes! jz searchok ;not the ubytes field, extent field? cpi extnum ;may be extent field ldax d ;fcb character jz searchext ;skip to search extent jmp checksum ;checksum the directory elt ;ret ; ; getallocbit: ;given allocation vector position BC, return with byte ;containing BC shifted so that the least significant ;bit is in the low order accumulator position. HL is ;the address of the byte for possible replacement in ;memory upon return, and D contains the number of shifts ;required to place the returned value back into position mov a,c! ani 111b! inr a! mov e,a! mov d,a ;d and e both contain the number of bit positions to shift mov a,c! rrc! rrc! rrc! ani 11111b! mov c,a ;C shr 3 to C mov a,b! add a! add a! add a! add a! add a ;B shl 5 ora c! mov c,a ;bbbccccc to C mov a,b! rrc! rrc! rrc! ani 11111b! mov b,a ;BC shr 3 to BC lhld alloca ;base address of allocation vector dad b! mov a,m ;byte to A, hl = .alloc(BC shr 3) ;now move the bit to the low order position of A rotl: rlc! dcr e! jnz rotl! ret ; ; setallocbit: ;BC is the bit position of ALLOC to set or reset. The ;value of the bit is in register E.ero lhld alloca ;base of allocation vector ;fill the allocation vector with zeros initial0: mvi m,0! inx h ;alloc(i)=0 dcx b ;count length down mov a,b! ora c! jnz initial0 ;set the reserved space for the directory lhld dirblk! xchg lhld alloca ;HL=.alloc() mov m,e! inx h! mov m,d ;sets reserved directory blks ;allocation vector initialized, home disk call home ;cdrmax = 3 (scans at least one directory record) lhld cdrmaxa! mvi m,3! inx h! mvi m,0 ;cdrmax = 0000 call set$end$dir ;dcnt = enddir ;read directory entries and check for allocated storage initial2: mvi c,true! call read$dir call end$of$dir! rz ;return if end of directory ;not end of directory, valid entry? call getdptra ;HL = buffa + dptr mvi a,empty! cmp m jz initial2 ;go get another item ;not empty, user code the same? lda usrcode cmp m! jnz pdollar ;same user code, check for '$' submit inx h! mov a,m ;first character sui '$' ;dollar file? jnz pdollar ;dollar f5 sub m! ani 7fh ;mask-out flags/extent modulus jnz searchn ;skip if not matched jmp searchok ;matched character searchext: ;A has fcb character ;attempt an extent # match push b ;save counters mov c,m ;directory character to c call compext ;compare user/dir char pop b ;recall counters jnz searchn ;skip if no match searchok: ;current character matches inx d! inx h! inr b! dcr c jmp searchloop endsearch: ;entire name matches, return dir position lda dcnt! ani dskmsk! sta lret ;lret = low(dcnt) and 11b lxi h,dirloc! mov a,m! ral! rnc ;dirloc=0ffh? ;yes, change it to 0 to mark as found xra a! mov m,a ;dirloc=0 ret search$fin: ;end of directory, or empty name call set$end$dir ;may be artifical end mvi a,255! jmp sta$ret ; ; ; delete: ;delete the currently addressed file call check$write ;write protected? mvi c,extnum! call search ;search through file type delete0: ;loop while directory matches  lhld info ;HL = source for data dad b! xchg ;DE=.fcb(C), source for copy call getdptra ;HL=.buff(dptr), destination pop b ;DE=source, HL=dest, C=length call move ;data moved seek$copy: ;enter from close to seek and copy current element call seek$dir ;to the directory element jmp wrdir ;write the directory element ;ret ; ; rename: ;rename the file described by the first half of ;the currently addressed file control block. the ;new name is contained in the last half of the ;currently addressed file conrol block. the file ;name and type are changed, but the reel number ;is ignored. the user number is identical call check$write ;may be write protected ;search up to the extent field mvi c,extnum! call search ;copy position 0 lhld info! mov a,m ;HL=.fcb(0), A=fcb(0) lxi d,dskmap! dad d;HL=.fcb(dskmap) mov m,a ;fcb(dskmap)=fcb(0) ;assume the same disk drive for new named file rename0: call end$of$dir! rz ;stop at end of dir ;not end of directory, rename nextrite! rnz ;skip close if r/o disk ;check file write flag - 0 indicates written call getmodnum ;fcb(modnum) in A ani fwfmsk! rnz ;return if bit remains set mvi c,namlen! call search ;locate file call end$of$dir! rz ;return if not found ;merge the disk map at info with that at buff(dptr) lxi b,dskmap! call getdptra dad b! xchg ;DE is .buff(dptr+16) lhld info! dad b ;DE=.buff(dptr+16), HL=.fcb(16) mvi c,(fcblen-dskmap) ;length of single byte dm merge0: lda single! ora a! jz merged ;skip to double ;this is a single byte map ;if fcb(i) = 0 then fcb(i) = buff(i) ;if buff(i) = 0 then buff(i) = fcb(i) ;if fcb(i) <> buff(i) then error mov a,m! ora a! ldax d! jnz fcbnzero ;fcb(i) = 0 mov m,a ;fcb(i) = buff(i) fcbnzero: ora a! jnz buffnzero ;buff(i) = 0 mov a,m! stax d ;buff(i)=fcb(i) buffnzero: cmp m! jnz mergerr ;fcb(i) = buff(i)? jmp dmset ;if merge ok merged: ;this is a double byte merge operation call mergezero ;buff = fcb if buff 0000 call end$of$dir! rz ;stop if end ;set each non zero disk map entry to 0 ;in the allocation vector ;may be r/o file call check$rodir ;ro disk error if found call getdptra ;HL=.buff(dptr) mvi m,empty mvi c,0! call scandm ;alloc elts set to 0 call wrdir ;write the directory call searchn ;to next element jmp delete0 ;for another record ; get$block: ;given allocation vector position BC, find the zero bit ;closest to this position by searching left and right. ;if found, set the bit to one and return the bit position ;in hl. if not found (i.e., we pass 0 on the left, or ;maxall on the right), return 0000 in hl mov d,b! mov e,c ;copy of starting position to de lefttst: mov a,c! ora b! jz righttst ;skip if left=0000 ;left not at position zero, bit zero? dcx b! push d! push b ;left,right pushed call getallocbit rar! jnc retblock ;return block number if zero ;bit is one, so try the right pop b! pop d ;left, right restored righttst: lhld maxall ;val element call check$rodir ;may be read-only file mvi c,dskmap! mvi e,extnum! call copy$dir ;element renamed, move to next call searchn jmp rename0 ; indicators: ;set file indicators for current fcb mvi c,extnum! call search ;through file type indic0: call end$of$dir! rz ;stop at end of dir ;not end of directory, continue to change mvi c,0! mvi e,extnum ;copy name call copy$dir call searchn jmp indic0 ; open: ;search for the directory entry, copy to fcb mvi c,namlen! call search call end$of$dir! rz ;return with lret=255 if end ;not end of directory, copy fcb information open$copy: ;(referenced below to copy fcb info) call getexta! mov a,m! push psw! push h ;save extent# call getdptra! xchg ;DE = .buff(dptr) lhld info ;HL=.fcb(0) mvi c,nxtrec ;length of move operation push d ;save .buff(dptr) call move ;from .buff(dptr) to .fcb(0) ;note that entire fcb is copied, including indicators call setfwf ;sets file write flag pop d! lxi h,extnum! da xchg! call mergezero! xchg ;fcb = buff if fcb 0000 ;they should be identical at this point ldax d! cmp m! jnz mergerr ;low same? inx d! inx h ;to high byte ldax d! cmp m! jnz mergerr ;high same? ;merge operation ok for this pair dcr c ;extra count for double byte dmset: inx d! inx h ;to next byte position dcr c! jnz merge0 ;for more ;end of disk map merge, check record count ;DE = .buff(dptr)+32, HL = .fcb(32) lxi b,-(fcblen-extnum)! dad b! xchg! dad b ;DE = .fcb(extnum), HL = .buff(dptr+extnum) ldax d ;current user extent number ;if fcb(ext) >= buff(fcb) then ;buff(ext) := fcb(ext), buff(rec) := fcb(rec) cmp m! jc endmerge ;fcb extent number >= dir extent number mov m,a ;buff(ext) = fcb(ext) ;update directory record count field lxi b,(reccnt-extnum)! dad b! xchg! dad b ;DE=.buff(reccnt), HL=.fcb(reccnt) mov a,m! stax d ;buff(reccnt)=fcb(reccnt) endmerge: mvi a,true! sta fcb$copied ;mark as copied jmp seek$copy ;ok to "wrdir" here -ue of maximum allocation# mov a,e! sub l! mov a,d! sbb h ;right=maxall? jnc retblock0 ;return block 0000 if so inx d! push b! push d ;left, right pushed mov b,d! mov c,e ;ready right for call call getallocbit rar! jnc retblock ;return block number if zero pop d! pop b ;restore left and right pointers jmp lefttst ;for another attempt retblock: ral! inr a ;bit back into position and set to 1 ;d contains the number of shifts required to reposition call rotr ;move bit back to position and store pop h! pop d ;HL returned value, DE discarded ret retblock0: ;cannot find an available bit, return 0000 mov a,c ; ora b! jnz lefttst ;also at beginning lxi h,0000h! ret ; copy$fcb: ;copy the entire file control block mvi c,0! mvi e,fcblen ;start at 0, to fcblen-1 ; jmp copy$dir ; ; copy$dir: ;copy fcb information starting at C for E bytes ;into the currently addressed directory entry push d ;save length for later mvi b,0 ;double index to BC d d ;HL=.buff(dptr+extnum) mov c,m ;C = directory extent number lxi h,reccnt! dad d ;HL=.buff(dptr+reccnt) mov b,m ;B holds directory record count pop h! pop psw! mov m,a ;restore extent number ;HL = .user extent#, B = dir rec cnt, C = dir extent# ;if user ext < dir ext then user := 128 records ;if user ext = dir ext then user := dir records ;if user ext > dir ext then user := 0 records mov a,c! cmp m! mov a,b ;ready dir reccnt jz open$rcnt ;if same, user gets dir reccnt mvi a,0! jc open$rcnt ;user is larger mvi a,128 ;directory is larger open$rcnt: ;A has record count to fill lhld info! lxi d,reccnt! dad d! mov m,a ret ; mergezero: ;HL = .fcb1(i), DE = .fcb2(i), ;if fcb1(i) = 0 then fcb1(i) := fcb2(i) mov a,m! inx h! ora m! dcx h! rnz ;return if = 0000 ldax d! mov m,a! inx d! inx h ;low byte copied ldax d! mov m,a! dcx d! dcx h ;back to input form ret ; close: ;locate the directory element and re-write it xra a! sta lret! sta dcnt! sta dcnt+1 ; call now6 1.4 compat ; ret ; mergerr: ;elements did not merge correctly lxi h,lret! dcr m ;=255 non zero flag set ret ; make: ;create a new file by creating a directory entry ;then opening the file call check$write ;may be write protected lhld info! push h ;save fcb address, look for e5 lxi h,efcb! shld info ;info = .empty mvi c,1! call search ;length 1 match on empty entry call end$of$dir ;zero flag set if no space pop h ;recall info address shld info ;in case we return here rz ;return with error condition 255 if not found xchg ;DE = info address ;clear the remainder of the fcb lxi h,namlen! dad d ;HL=.fcb(namlen) mvi c,fcblen-namlen ;number of bytes to fill xra a ;clear accumulator to 00 for fill make0: mov m,a! inx h! dcr c! jnz make0 lxi h,ubytes! dad d ;HL = .fcb(ubytes) mov m,a ;fcb(ubytes) = 0 call setcdr ;may have extended the directory ;now copy entry to the directory call copy$fcb ;and set the file write flag to "1" jmp setfwf ;ret ; opene current fcb call getfcb ;sets parameters for the read lda vrecord! lxi h,rcount! cmp m ;vrecord-rcount ;skip if rcount > vrecord jc recordok ;not enough records in the extent ;record count must be 128 to continue cpi 128 ;vrecord = 128? jnz diskeof ;skip if vrecord<>128 call open$reel ;go to next extent if so xra a! sta vrecord ;vrecord=00 ;now check for open ok lda lret! ora a! jnz diskeof ;stop at eof recordok: ;arrive with fcb addressing a record to read call index ;error 2 if reading unwritten data ;(returns 1 to be compatible with 1.4) call allocated ;arecord=0000? jz diskeof ;record has been allocated, read it call atran ;arecord now a disk address call seek ;to proper track,sector call rdbuff ;to dma address jmp setfcb ;replace parameter ; ret ; diskeof: jmp setlret1 ;lret = 1 ;ret ; seqdiskwrite: ;sequential disk write mvi a,1! sta seqio ;drop through to diskwrite ; diskwrite: ;(may enter here from seqdiskwri write flag call atran ;arecord set lda seqio! dcr a! dcr a! jnz diskwr11 ; pop b! push b! mov a,c! dcr a! dcr a ; jnz diskwr11 ;old allocation push h ;arecord in hl ret from atran lhld buffa! mov d,a ;zero buffa & fill fill0: mov m,a! inx h! inr d! jp fill0 ; call setdir! lhld arecord1 ; mvi c,2 ; fill1: shld arecord! push b! call seek! pop b ; call wrbuff ;write fill record ; lhld arecord! ;restore last record mvi c,0 ;change allocate flag lda blkmsk! mov b,a! ana l! cmp b!inx h ; jnz fill1 ;cont until cluster is zeroed pop h! shld arecord! call setdata diskwr11: ; call seek ;to proper file position pop b! push b ;restore/save write flag (C=2 if new block) call wrbuff ;written to disk pop b ;C = 2 if a new block was allocated, 0 if not ;increment record count if rcount<=vrecord lda vrecord! lxi h,rcount! cmp m ;vrecord-rcount jc diskwr2 ;rcount <= vrecord mov m,a! inr m ;rcount = vrecord+1 mvi c,2 ;mark as re$reel: ;close the current extent, and open the next one ;if possible. RMF is true if in read mode xra a! sta fcb$copied ;set true if actually copied call close ;close current extent ;lret remains at enddir if we cannot open the next ext call end$of$dir! rz ;return if end ;increment extent number lhld info! lxi b,extnum! dad b ;HL=.fcb(extnum) mov a,m! inr a! ani maxext! mov m,a ;fcb(extnum)=++1 jz open$mod ;move to next module if zero ;may be in the same extent group mov b,a! lda extmsk! ana b ;if result is zero, then not in the same group lxi h,fcb$copied ;true if the fcb was copied to directory ana m ;produces a 00 in accumulator if not written jz open$reel0 ;go to next physical extent ;result is non zero, so we must be in same logical ext jmp open$reel1 ;to copy fcb information open$mod: ;extent number overflow, go to next module lxi b,(modnum-extnum)! dad b ;HL=.fcb(modnum) inr m ;fcb(modnum)=++1 ;module number incremented, check for overflow mov ate above) mvi a,false! sta rmf ;read mode flag ;write record to currently selected file call check$write ;in case write protected lhld info ;HL = .fcb(0) call check$rofile ;may be a read-only file call getfcb ;to set local parameters lda vrecord! cpi lstrec+1 ;vrecord-128 ;skip if vrecord > lstrec ;vrecord = 128, cannot open next extent jnc setlret1 ;lret=1 ; diskwr0: ;can write the next record, so continue call index call allocated mvi c,0 ;marked as normal write operation for wrbuff jnz diskwr1 ;not allocated ;the argument to getblock is the starting ;position for the disk search, and should be ;the last allocated block for this file, or ;the value 0 if no space has been allocated call dm$position sta dminx ;save for later lxi b,0000h ;may use block zero ora a! jz nopblock ;skip if no previous block ;previous block exists at A mov c,a! dcx b ;previous block # in BC call getdm ;previous block # to HL mov b,h! mov c,l ;BC=prev bloccord count incremented diskwr2: ;A has vrecord, C=2 if new block or new record# dcr c! dcr c! jnz noupdate push psw ;save vrecord value call getmodnum ;HL=.fcb(modnum), A=fcb(modnum) ;reset the file write flag to mark as written fcb ani (not fwfmsk) and 0ffh ;bit reset mov m,a ;fcb(modnum) = fcb(modnum) and 7fh pop psw ;restore vrecord noupdate: ;check for end of extent, if found attempt to open ;next extent in preparation for next write cpi lstrec ;vrecord=lstrec? jnz diskwr3 ;skip if not ;may be random access write, if so we are done ;change next lda seqio! cpi 1! jnz diskwr3 ;skip next extent open op ;update current fcb before going to next extent call setfcb call open$reel ;rmf=false ;vrecord remains at lstrec causing eof if ;no more directory space is available lxi h,lret! mov a,m! ora a! jnz nospace ;space available, set vrecord=255 dcr a! sta vrecord ;goes to 00 next time nospace: mvi m,0 ;lret = 00 for returned value di,m! ani maxmod ;mask high order bits jz open$r$err ;cannot overflow to zero ;otherwise, ok to continue with new module open$reel0: mvi c,namlen! call search ;next extent found? call end$of$dir! jnz open$reel1 ;end of file encountered lda rmf! inr a ;0ffh becomes 00 if read jz open$r$err ;sets lret = 1 ;try to extend the current file call make ;cannot be end of directory call end$of$dir jz open$r$err ;with lret = 1 jmp open$reel2 open$reel1: ;not end of file, open call open$copy open$reel2: call getfcb ;set parameters xra a! jmp sta$ret ;lret = 0 ; ; ret ;with lret = 0 open$r$err: ;cannot move to next extent of this file call setlret1 ;lret = 1 jmp setfwf ;ensure that it will not be closed ;ret ; seqdiskread: ;sequential disk read operation mvi a,1! sta seqio ;drop through to diskread ; diskread: ;(may enter from seqdiskread) mvi a,true! sta rmf ;read mode flag = true (open$reel) ;read the next record from thk# nopblock: ;BC = 0000, or previous block # call get$block ;block # to HL ;arrive here with block# or zero mov a,l! ora h! jnz blockok ;cannot find a block to allocate mvi a,2! jmp sta$ret ;lret=2 blockok: ;allocated block number is in HL shld arecord xchg ;block number to DE lhld info! lxi b,dskmap! dad b ;HL=.fcb(dskmap) lda single! ora a ;set flags for single byte dm lda dminx ;recall dm index jz allocwd ;skip if allocating word ;allocating a byte value call addh! mov m,e ;single byte alloc jmp diskwru ;to continue allocwd: ;allocate a word value mov c,a! mvi b,0 ;double(dminx) dad b! dad b ;HL=.fcb(dminx*2) mov m,e! inx h! mov m,d ;double wd diskwru: ;disk write to previously unallocated block mvi c,2 ;marked as unallocated write diskwr1: ;continue the write operation of no allocation error ;C = 0 if normal write, 2 if to prev unalloc block lda lret! ora a! rnz ;stop if non zero returned value push b ;save7skwr3: jmp setfcb ;replace parameters ;ret ; rseek: ;random access seek operation, C=0ffh if read mode ;fcb is assumed to address an active file control block ;(modnum has been set to 1100$0000b if previous bad seek) xra a! sta seqio ;marked as random access operation rseek1: push b ;save r/w flag lhld info! xchg ;DE will hold base of fcb lxi h,ranrec! dad d ;HL=.fcb(ranrec) mov a,m! ani 7fh! push psw ;record number mov a,m! ral ;cy=lsb of extent# inx h! mov a,m! ral! ani 11111b ;A=ext# mov c,a ;C holds extent number, record stacked mov a,m! rar! rar! rar! rar! ani 1111b ;mod# mov b,a ;B holds module#, C holds ext# pop psw ;recall sought record # ;check to insure that high byte of ran rec = 00 inx h! mov l,m ;l=high byte (must be 00) inr l! dcr l! mvi l,6 ;zero flag, l=6 ;produce error 6, seek past physical eod jnz seekerr ;otherwise, high byte = 0, A = sought record lxi h,nxtrec! dad d ;HL = .fcb(nxtrec) mov m,a ;sought rec# stored away ;aror getfilesize/setrandom xchg! dad d ;DE=.buf(dptr) or .fcb(0), HL = .f(nxtrec/reccnt) mov c,m! mvi b,0 ;BC = 0000 0000 ?rrr rrrr lxi h,extnum! dad d! mov a,m! rrc! ani 80h ;A=e000 0000 add c! mov c,a! mvi a,0! adc b! mov b,a ;BC = 0000 000? errrr rrrr mov a,m! rrc! ani 0fh! add b! mov b,a ;BC = 000? eeee errrr rrrr lxi h,modnum! dad d! mov a,m ;A=XXX? mmmm add a! add a! add a! add a ;cy=? A=mmmm 0000 push psw! add b! mov b,a ;cy=?, BC = mmmm eeee errr rrrr push psw ;possible second carry pop h ;cy = lsb of L mov a,l ;cy = lsb of A pop h ;cy = lsb of L ora l ;cy/cy = lsb of A ani 1 ;A = 0000 000? possible carry-out ret ; getfilesize: ;compute logical file size for current fcb mvi c,extnum call search ;zero the receiving ranrec field lhld info! lxi d,ranrec! dad d! push h ;save position mov m,d! inx h! mov m,d! inx h! mov m,d;=00 00 00 getsize: call end$of$dir jz setsize ;current fcb addressed by dptr call getdptra! lxi d,reccnt ;ready for compus! jmp sta$ret ;lret = dvers (high = 00) ; ret ;jmp goback ; func13: ;reset disk system - initialize to disk 0 lxi h,0! shld rodsk! shld dlog xra a! sta curdsk ;note that usrcode remains unchanged lxi h,tbuff! shld dmaad ;dmaad = tbuff call setdata ;to data dma address jmp select ;ret ;jmp goback ; func14: equ curselect ; ;select disk info ;ret ;jmp goback ; func15: ;open file call clrmodnum ;clear the module number call reselect jmp open ;ret ;jmp goback ; func16: ;close file call reselect jmp close ;ret ;jmp goback ; func17: ;search for first occurrence of a file mvi c,0 ;length assuming '?' true xchg ;was lhld info mov a,m! cpi '?' ;no reselect if ? jz qselect ;skip reselect if so ;normal search call getexta! mov a,m! cpi '?' ; cnz clrmodnum ;module number zeroed call reselect mvi c,namlen qselect: call search jmp dir$to$user ;copy directory entry to user ;ret ;jmp goback ; func18: ;search for next occurrence orive here with B=mod#, C=ext#, DE=.fcb, rec stored ;the r/w flag is still stacked. compare fcb values lxi h,extnum! dad d! mov a,c ;A=seek ext# sub m! jnz ranclose ;tests for = extents ;extents match, check mod# lxi h,modnum! dad d! mov a,b ;B=seek mod# ;could be overflow at eof, producing module# ;of 90H or 10H, so compare all but fwf sub m! ani 7fh! jz seekok ;same? ranclose: push b! push d ;save seek mod#,ext#, .fcb call close ;current extent closed pop d! pop b ;recall parameters and fill mvi l,3 ;cannot close error #3 lda lret! inr a! jz badseek lxi h,extnum! dad d! mov m,c ;fcb(extnum)=ext# lxi h,modnum! dad d! mov m,b ;fcb(modnum)=mod# call open ;is the file present? lda lret! inr a! jnz seekok ;open successful? ;cannot open the file, read mode? pop b ;r/w flag to c (=0ffh if read) push b ;everyone expects this item stacked mvi l,4 ;seek to unwritten extent #4 inr c ;becomes 00 if read operation jz badseek ;skip to error if read opete size call compute$rr ;A=0000 000? BC = mmmm eeee errr rrrr ;compare with memory, larger? pop h! push h ;recall, replace .fcb(ranrec) mov e,a ;save cy mov a,c! sub m! inx h ;ls byte mov a,b! sbb m! inx h ;middle byte mov a,e! sbb m ;carry if .fcb(ranrec) > directory jc getnextsize ;for another try ;fcb is less or equal, fill from directory mov m,e! dcx h! mov m,b! dcx h! mov m,c getnextsize: call searchn jmp getsize setsize: pop h ;discard .fcb(ranrec) ret ; setrandom: ;set random record from the current file control block lhld info! lxi d,nxtrec ;ready params for computesize call compute$rr ;DE=info, A=cy, BC=mmmm eeee errr rrrr lxi h,ranrec! dad d ;HL = .fcb(ranrec) mov m,c! inx h! mov m,b! inx h! mov m,a ;to ranrec ret ; select: ;select disk info for subsequent input or output ops lhld dlog! lda curdsk! mov c,a! call hlrotr push h! xchg ;save it for test below, send to seldsk call selectdisk! pop h ;recall dlog vector cz sel$error ;f a file name lhld searcha! shld info call reselect! call searchn jmp dir$to$user ;copy directory entry to user ;ret ;jmp goback ; func19: ;delete a file call reselect call delete jmp copy$dirloc ;ret ;jmp goback ; func20: ;read a file call reselect jmp seqdiskread ; ;jmp goback ; func21: ;write a file call reselect jmp seqdiskwrite ; ;jmp goback ; func22: ;make a file call clrmodnum call reselect jmp make ;ret ;jmp goback ; func23: ;rename a file call reselect call rename jmp copy$dirloc ;ret ;jmp goback ; func24: ;return the login vector lhld dlog! jmp sthl$ret ; ; ret ;jmp goback ; func25: ;return selected disk number lda curdsk! jmp sta$ret ; ; ret ;jmp goback ; func26: ;set the subsequent dma address to info xchg ;was lhld info shld dmaad ;dmaad = info jmp setdata ;to data dma address ;ret ;jmp goback ; func27: ;return the login vector address lhld alloca! jmp sthl$ret ; ; ret ;jmp gobackration ;write operation, make new extent call make mvi l,5 ;cannot create new extent #5 lda lret! inr a! jz badseek ;no dir space ;file make operation successful seekok: pop b ;discard r/w flag xra a! jmp sta$ret ;with zero set badseek: ;fcb no longer contains a valid fcb, mark ;with 1100$000b in modnum field so that it ;appears as overflow with file write flag set push h ;save error flag call getmodnum ;HL = .modnum mvi m,1100$0000b pop h ;and drop through seekerr: pop b ;discard r/w flag mov a,l! sta lret ;lret=#, nonzero ;setfwf returns non-zero accumulator for err jmp setfwf ;flag set, so subsequent close ok ;ret ; randiskread: ;random disk read operation mvi c,true ;marked as read operation call rseek cz diskread ;if seek successful ret ; randiskwrite: ;random disk write operation mvi c,false ;marked as write operation call rseek cz diskwrite ;if seek successful ret ; compute$rr: ;compute random record position freturns true if select ok ;is the disk logged in? mov a,l! rar! rc ;return if bit is set ;disk not logged in, set bit and initialize lhld dlog! mov c,l! mov b,h ;call ready call set$cdisk! shld dlog ;dlog=set$cdisk(dlog) jmp initialize ;ret ; curselect: lda linfo! lxi h,curdsk! cmp m! rz ;skip if linfo=curdsk mov m,a ;curdsk=info jmp select ;ret ; reselect: ;check current fcb to see if reselection necessary mvi a,true! sta resel ;mark possible reselect lhld info! mov a,m ;drive select code ani 1$1111b ;non zero is auto drive select dcr a ;drive code normalized to 0..30, or 255 sta linfo ;save drive code cpi 30! jnc noselect ;auto select function, save curdsk lda curdsk! sta olddsk ;olddsk=curdsk mov a,m! sta fcbdsk ;save drive code ani 1110$0000b! mov m,a ;preserve hi bits call curselect noselect: ;set user code lda usrcode ;0...31 lhld info! ora m! mov m,a ret ; ; individual function handlers func12: ;return version number mvi a,dver8 ; func28: equ set$ro ; ;write protect current disk ;ret ;jmp goback ; func29: ;return r/o bit vector lhld rodsk! jmp sthl$ret ; ; ret ;jmp goback ; func30: ;set file indicators call reselect call indicators jmp copy$dirloc ;lret=dirloc ;ret ;jmp goback ; func31: ;return address of disk parameter block lhld dpbaddr sthl$ret: shld aret ret ;jmp goback func32: ;set user code lda linfo! cpi 0ffh! jnz setusrcode ;interrogate user code instead lda usrcode! jmp sta$ret ;lret=usrcode ; ret ;jmp goback setusrcode: ani 1fh! sta usrcode ret ;jmp goback ; func33: ;random disk read operation call reselect jmp randiskread ;to perform the disk read ;ret ;jmp goback ; func34: ;random disk write operation call reselect jmp randiskwrite ;to perform the disk write ;ret ;jmp goback ; func35: ;return file size (0-65536) call reselect jmp getfilesize ;ret ;jmp goback ; func36: equ setrandom ; ;set random record ;ret ;jmp goblist equ $-sectpt ;size of area ; ; local variables tranv: ds word ;address of translate vector fcb$copied: ds byte ;set true if copy$fcb called rmf: ds byte ;read mode flag for open$reel dirloc: ds byte ;directory flag in rename, etc. seqio: ds byte ;1 if sequential i/o linfo: ds byte ;low(info) dminx: ds byte ;local for diskwrite searchl:ds byte ;search length searcha:ds word ;search address tinfo: ds word ;temp for info in "make" single: ds byte ;set true if single byte allocation map resel: ds byte ;reselection flag olddsk: ds byte ;disk on entry to bdos fcbdsk: ds byte ;disk named in fcb rcount: ds byte ;record count in current fcb extval: ds byte ;extent number and extmsk vrecord:ds word ;current virtual record arecord:ds word ;current actual record arecord1: ds word ;current actual block# * blkmsk ; ; local variables for directory access dptr: ds byte ;directory pointer 0,1,2,3 dcnt: ds word ;directory counter 0,1,...,dirmax drec: ds word ;directory record 0,1,...,dirmax/4 ;OCCRAM2.ASM TITLE 'Debug Monitor RAM Storage.' ; Used to assembly ROM resident and CBIOS ORG MRAMEXT KEYLST: DS KL_LEN*KLE_LEN ORG MRAM ; Host disk xfer buffer and... ; Format track template holding buffer HSTBUF: DS 256+128 ; Directory Buffer DIRBUF: = HSTBUF+256 TEM DS 6 RNDV = TEM+1 ;random number seed ERCNT = RNDV+1 ;DW ERCNT RTRC = ERCNT+2 ;retry count RTRY = RTRC+1 MPCHR DS 1 ;prompt character ECHOP DS 1 ;=0, list ehco off ROMRAM DS 1 ;0= RAM, 1= ROM DSTSB DS 6 ;Disk status bytes ; Disk operation temps and control DMADR DS 2 ;Address for read/write Disk DMAADR DS 2 ;CBIOS, users DMA ; Note order of xxxSEC,xxxTRK,xxxDSK must be maintained ; along with length (1,2,1). SEKDEL: DS 1 ;Set for seek-restore command in ROM ;depends on disk type. Siemens = 3h, MPI = 0h SAVSEC DS 1 ;last sector requested SAVTRK DS 2 ;last track requested SDISK DS 1 ;Selected disk drive (0,1) ACTSEC = SAVSEC ACTTRK = SAVTRK ACTDSK = SDISK SEKSEC DS 1 SEKTRK DS 2 SEKDSback func37: ; lhld info mov a,l! cma! mov e,a! mov a,h! cma lhld dlog! ana h! mov d,a! mov a,l! ana e mov e,a! lhld rodsk! xchg! shld dlog mov a,l! ana e! mov l,a mov a,h! ana d! mov h,a shld rodsk! ret ; ; goback: ;arrive here at end of processing to return to user lda resel! ora a! jz retmon ;reselection may have taken place lhld info! mvi m,0 ;fcb(0)=0 lda fcbdsk! ora a! jz retmon ;restore disk number mov m,a ;fcb(0)=fcbdsk lda olddsk! sta linfo! call curselect ; ; return from the disk monitor retmon: lhld entsp! sphl ;user stack restored lhld aret! mov a,l! mov b,h ;BA = HL = aret ret func38: equ func$ret func39 equ func$ret func40: ;random disk write with zero fill of unallocated block call reselect mvi a,2! sta seqio mvi c,false call rseek1 cz diskwrite ;if seek successful ret ; ; ; data areas ; ; initialized data efcb: db empty ;0e5=available dir entry rodsk: dw 0 ;read only disk vector dlog: dw 0 ;logged-in disks dmaad: dw tb ; bios equ ($ and 0ff00h)+100h ;next module end OCCSETA ASMGOCCSYSGAASM7OCCBDOS2$$$OCCBDOS2$$$OCCBDOS2$$$OCCBDOS2$$$OCCBDOS2$$$K DS 1 HSTSEC DS 1 HSTTRK DS 2 HSTDSK DS 1 TEMSEC DS 1 ;Used in bios only RDFLAG DS 1 ;Read flag ERFLAG DS 1 ;Error reporting WRTYPE DS 1 ;Write operation type ALV: DS ALVS CSV: DS CSVS ; BIOS blocking-deblocking flags HSTACT: DS 1 ;host active flag HSTWRT: DS 1 ;Host written flag UNACNT: DS 1 ;Unalloc rec count UNATRK: DS 2 ;Track UNASEC: DS 1 ;Sector LOGSEC: DS 1 ;Logical sector LDADR DS 2 KEYLCK DS 1 ;Zero if locked keyboard CURS DS 2 ;current cursor position ; Keyboard scan temporaries TKEY DS 1 ;Tem holding key HKCNT DS 1 ;Debounce key LKEY DS 1 ;Last valid keystroke CKEY DS 1 ;Last control key ESCH DS 1 ;ESC holding flag ;PIAAD and PIABD must be kept sequential, PIAAD first ;dependency in VC_HOME of BMKEY.asm PIAAD: DS 1 ;Holds last PIA-A data PIABD: DS 1 ;Holds last PIA-B data ; Calendar month, day year IDAY DS 3 IMONTH = IDAY+1 IYR = IDAY+2 ; Wall clock time cells and disk active ; see UPTIM: in BMKEY.asm HOURS: DS 6 MINS: = HOURS+1 SECS: = HOUuff ;initial dma address ; ; curtrka - alloca are set upon disk select ; (data must be adjacent, do not insert variables) ; (address of translate vector, not used) cdrmaxa:ds word ;pointer to cur dir max value curtrka:ds word ;current track address curreca:ds word ;current record address buffa: ds word ;pointer to directory dma address dpbaddr:ds word ;current disk parameter block address checka: ds word ;current checksum vector address alloca: ds word ;current allocation vector address addlist equ $-buffa ;address list size ; ; sectpt - offset obtained from disk parm block at dpbaddr ; (data must be adjacent, do not insert variables) sectpt: ds word ;sectors per track blkshf: ds byte ;block shift factor blkmsk: ds byte ;block mask extmsk: ds byte ;extent mask maxall: ds word ;maximum allocation number dirmax: ds word ;largest directory number dirblk: ds word ;reserved allocation bits for directory chksiz: ds word ;size of checksum vector offset: ds word ;offset tracks at beginning dp9RS+2 SEC6: = HOURS+3 ; Used to deselect drive when there is NO activity ; on drive for n seconds. See FDSK routine DACTVE: = HOURS+4 ;=0 by FDSK, Used by UPTIM BELCNT: = HOURS+5 ;^G bell timer cell LLIMIT DS 1 ;max #columns in a logical line ; MSG 'LLIMIT = ',LLIMIT,'h.' ; Disk drive current positions LDSEL: DS 2 ;Last selected drive LDTRK = LDSEL+1 ;Last track used for non-selected drive IESTK: DS 2 ;save current stk ptr ; Interrupt stack DS 20*2 ISTK: DS 0 ; Stack entry DS 20*2 BIOSTK: ROMSTK: DS 0 ACIAD: DS 1 ;last command byte written to ACIA R179x: DS 4 ;179x register save area KBDLY: DS 1 ;keyboard debounce-delay cell ;since CP/M CANNOT boot off B:, this cell is used ;to invert the names of the 2 drives: ; =0, all normal, A=A:, B=B: ; =1, all inverted, A=B:, B=A: DSKSWP DS 1 ; Z80 Alternate Register Set ALIGN 10h RAGS: DESAX: DS 2 ;DE` BCSAX: DS 2 ;BC` AFSAX: DS 2 ;AF` HLSAX: DS 2 ;HL` IXSAX: DS 2 ;IX IYSAX: DS 2 ;IY IVSAX: DS 2 ;Interrupt page register ; 8080 Register Save Area. ALIGN 10h REGS: ESAVE: DS 1 ;E Register save location DSAVE: DS 1 ;D Register save location CSAVE: DS 1 ;C Register save location BSAVE: DS 1 ;B Register save location FSAVE: DS 1 ;FLAGS save location ASAVE: DS 1 ;A Register save location LSAVE: DS 1 ;L Register save location HSAVE: DS 1 ;H Register save location PSAVE: DS 2 ;PGM COUNTER save location SSAVE: DS 2 ;USER STACK pointer save location BKPA: DS 2 ;last breakpoint address BKPC: DS 1 ;Contents of bkp VRTOFF DS 1 ;LAST VERTICAL OFFSET TAKEN FROM COUT ; Interrupt Jump Vector is between EFF8, EFFF. ; Endx MRAM s: ; =0, all normal, A=A:, B=B: ; =1, all inverted, A=B:, B=A: DSKSWP DS 1 ; Z80 Alternate Register Set ALIGN 10h RAGS: DESAX: DS 2 ;DE` BCSAX: DS 2 ;BC` AFSAX: DS 2 ;AF` HLSAX: DS 2 ;HL` IXSAX: DS 2 ;IX IYSAX: DS 2 ;IY IVSAX: DS 2 ;Int:;<=>?@ABCDEFGHIJKL