subttl comments title DIAG - disk diagnostics (C) 1982-1983 P.J.Blower .comment " DIAG Vers 2.0 July 1983 - complete rewrite of Vers 1.0 - Drv, Trk, Sec & Blk sub-commands improved - Whole sub-command added - Esc & ^C low level escape commands added - force Read made optional - BACK command added - COPY command expanded - Fill command added Alternate Help O Verify Back I P Write Copy J Quit X Display K Read Y Edit List S Z Fill M Translate G Next U " subttl equates .Z80 ASEG ORG 0100h WBOOT EQU 0000h BDOS EQU 0005h FCB EQU 005Ch ;default File Control Block FCBRR EQU FCB+33 ;random record number CPMBUF EQU 0080h ;CP/M command buffer BS EQU 08h CR EQU 0Dh LF EQU 0Ah ESC EQU 1Bh LLF EQU 8Ah ;LF with top bit set ;------------------------------- subttl main program JP INIT FRC: DB 0 ;optional forcing byte for systems with volatile CBIOSs CURRT: DB 0Ch ;video cursor right CURUP: DB 0Bh ;video cursor up KBD: DB 04h ;keyboard cursor left DB 03h ;keyboard cursor right DB 01h ;keyboard cursor up DB 02h ;keyboard cursor down SIGNON: DB "DIAG 820 Pre-release V2.0 Copyright (c) July 1983 P.J.Blower",CR,LF,LF DB "Note that CP/M 128 byte sectors are used throughout,",CR,LF DC " these may not coincide with actual physical sectors." ; current drive parameter table DIRBUF: DW 0 ;addr of DIR buffer BLKSIZ: DW 0 ;Block size = BLKSIZ *128 SYSTRK: DW 0 ;no of system tracks MAXTRK: DW 0 ;max no of tracks BUF1: DW BUFF1 ;buffer.. BUF2: DW CPMBUF ;..addresses PPARAM: ;space for backup parameters DB 0 DW 0 DW 0 DB 7 DW 0 DW 0 PRMLEN EQU $-PPARAM SPARAM: ;temporary function save area SDRIVE: DB 0 STRACK: DW 0 SSECT: DW 0 SBLKSW: DB 0 SBLOCK: DW 0 SBLCKE: DW 0 TPARAM: DS PRMLEN ;temporary function save area CPARAM: CDRIVE: DB 0 ;current drive CTRACK: DW 0 ;current track CSECT: DW 0 ;current sector CBLKSW: DB 7 ;switch block marker CBLOCK: DW 0 ;current block CBLCKE: DW 0 ;current block extension UPARAM: ;upper limit value save area UDRIVE: DB 0 UTRACK: DW 0 USECT: DW 0 UBLKSW: DB 0 UBLOCK: DW 0 UBLCKE: DW 0 DPARAM: ;destination value save area DDRIVE: DB 0 DTRACK: DW 0 DSECT: DW 0 DBLKSW: DB 0 DBLOCK: DW 0 DBLCKE: DW 0 OPARAM: ;destination upper value ODRIVE: DB 0 OTRACK: DW -1 ;force almost OSECT: DW -1 ;..indefinate read or write OBLKSW: DB 0 OBLOCK: DW -1 OBLCKE: DW -1 EPARAM: ;------------------------------- INIT: LD SP,STACK LD HL,(WBOOT+1) ;HL = addr CBIOS WBOOT LD L,06 ;put CBOIS addresses into rest of prog LD BC,3 ;length of JP instruction LD (CONST+1),HL ADD HL,BC LD (CONNIN+1),HL ADD HL,BC LD (CONOUT+1),HL ADD HL,BC LD (LISTP+1),HL LD L,18h LD (HOME+1),HL ADD HL,BC LD (SELDSK+1),HL ADD HL,BC LD (SETTRK+1),HL ADD HL,BC LD (SETSEC+1),HL ADD HL,BC LD (SETDMA+1),HL ADD HL,BC LD (READFF+1),HL ADD HL,BC LD (WRITF+1),HL ADD HL,BC ADD HL,BC LD (SCTRAN+1),HL LD HL,KBD ;put cursor controls into EDIT LD A,(HL) LD (EDIT12+1),A ;left cursor INC HL LD A,(HL) LD (EDIT17+1),A ;right cursor INC HL LD A,(HL) LD (EDIT18+1),A ;up cursor INC HL LD A,(HL) LD (EDIT16+1),A ;down cursor INC HL ;point to SIGNON CALL CPMSG LD A,(CDRIVE) LD C,A CALL CHK1 ;get disk parameters CALL CONIN ;HELP menu - print everything but ignore control bytes & jumps HELP: CALL CPRINT DB LF DC "Single letter commands available in DIAG" LD HL,MENUM LD B,CMDS+1 ;number of avaible commands HELP1: CALL CPMSG ;print msg LD A,(HL) ;get control byte AND 11111110b ;mask OR A ;test it CALL NZ,SUBMSG ;print sub-commands INC HL ;step over to next msg INC HL INC HL DJNZ HELP1 ;main command menu MENU: LD SP,STACK LD HL,COPYBK ;point ESC & ^C to COPYBK routine LD (ABORT+1),HL LD HL,MENU ;save return address PUSH HL XOR A LD (MENU7+1),A ;kill any outstanding repeat function LD (MENU9+1),A ;and CURUP calls CALL HOME ;for safety ;CALL TEST MENU1: CALL CPRINT ;print promt DC "DIAG>>" MENU2: CALL CONIN ;get user response CP " " JP Z,HELP ;jump if space CP CR JR Z,MENU1 ;go to new line LD HL,MENUM ;pointer to menu list LD B,CMDS ;no. of commands available MENU3: CP (HL) ;get first character JR Z,MENU4 ;jump if compare CALL DMSG ;inc HL to end of msg INC HL ;skip past control byte INC HL ;.. & address INC HL DJNZ MENU3 ;search for next JR MENU2 ;failed, go back ;command letter found - print command then call SCMD if necc. then jump MENU4: LD (MENU5+1),HL ;save msg pointer CALL SMSG ;print until space CALL DMSG ;inc HL to end of msg CALL PRINT DC ">" PUSH HL ;save addr LD A,(HL) ;get control byte LD (SRPT1+1),A ;save for SRPT CALL SCMD ;get more Drv,Trk,Sec,Blk commands if necc. POP HL PUSH HL CALL SDEST ;check (& act) if destination required POP HL LD A,(SCMDX+1) ;get used control byte CP (HL) ;different? CALL NZ,CRLF ;yes, call CRLF INC HL ;point to address CALL GETADR ;get it LD (MENU6+1),HL ;save addr JR Z,MENU6 ;not different, skip MENU5: LD HL,0 ;command msg pointer here CALL SMSG ;print command CALL PRINT DC "-" MENU6: LD HL,0 ;addr pointer stored here CALL CALADR ;call addr in HL MENU7: LD A,0 ;repeated function indicator stored here OR A RET Z ;ret if not required LD A,(UBLKSW) ;get block switch marker OR A ;clear carry JR Z,MENUJJ LD DE,TPARAM ;copy.. CALL COPYF1 ;Cparam to Tparam.. CALL COPYF2 ;& Uparam to Cparam CALL KBLK ;translate Blk into Trk & Sec CALL SRPT2 ;restore to original state MENUJJ: LD A,0 ;drive whole marker stored here OR A JR Z,MENU8 LD HL,(MAXTRK) LD (UTRACK),HL XOR A LD (MENUJJ+1),A MENU8: CALL INCSEC ;inc next sector CALL UCOMP ;compare Ctrk,Csec with Utrk,Usec JR Z,DECSEC ;ret & dec sector if limit passed JR C,DECSEC CALL SCAN ;scan for user interrupts MENU9: LD A,0 ;optional CURUP char stored here CALL POUT JR MENU5 ;do some more ;------------------------------- SCAN: EXX ;scan for any user interrupt CONST: CALL 0 ;CONST addr stored here EXX INC A ;waiting char? JR NZ,SCAN1 CALL HOME ;safety call CALL CONN ;yes, collect waiting char CALL CONN ;yes, wait for any i/p or ESC SCAN1: JP CRLF ;------------------------------- ;Decrement sector count DECSEC: LD HL,(CSECT) ;get current sector DEC HL LD (CSECT),HL CALL CHKSEC ;maximum reached? RET C ;no, ret LD HL,(CTRACK) ;get current track DEC HL LD (CTRACK),HL ;load current track EX DE,HL ;make max no. of sectors DEC HL JR INCS1 ;save & ret ;------------------------------- ;Increment sector count INCSEC: LD HL,(CSECT) ;get current sector INC HL LD (CSECT),HL CALL CHKSEC ;maximum reached? RET C ;no, ret LD HL,(CTRACK) ;get current track INC HL LD (CTRACK),HL ;load current track LD HL,0 ;make sector = 0 INCS1: LD (CSECT),HL ;save RET ;------------------------------- JPADDR: CALL GETADR ;get address CALADR: JP (HL) ;jump to it ;------------------------------- ;examine control byte, call sub-commands upon request SCMD: LD (SCMDX+1),A AND 11111110b ;mask for Drv,Trk,Sec,Blk RET Z LD C,A SCMD0: LD A,0 ;last user character stored here CP CR RET Z CP "-" JR NZ,SCMD1 LD A,"u" SCMD1: LD HL,SUBMM ;msg pointer LD B,NSUB ;no. of sub-commands SCMD2: RLC C JR NC,SCMD3 CP (HL) ;same as first char? CALL Z,SMSG ;yes, print SCMD3: CALL DMSG ;lose rest of msg JR Z,SCMDX ;jump if match INC HL INC HL DJNZ SCMD2 ;loop round for more CALL CONIN ;get user response SCMDX: LD A,0 ;save control byte here LD C,2 ;no. of spaces CALL Z,JPADDR ;call sub-command JR SCMD ;------------------------------- ;get repeated function parameters SRPT: LD (MENU7+1),A ;flag repeated function in MENU LD DE,TPARAM ;copy Cparam to Tparam CALL COPYF1 ;do it SRPT1: LD A,0 ;copy of control byte here AND 01110000b CALL SCMD ;get upper limit track & sector values SRPT2: LD HL,UPARAM-1 ;point to Uparam LD DE,DPARAM-1 ;restore original values SRPT4: LD BC,2*PRMLEN ;2x LDDR RET SRPT5: LD HL,DPARAM-1 JR SRPT4 ;------------------------------- ;check if destination required, if so then get parameters SDEST: LD A,(SRPT1+1) ;get control byte BIT 2,A ;destination required RET Z ;no, ret AND 11110100b ;mask for Drv,Trk,Sec,Blk PUSH AF LD A,(SCMDX+1) ;get used command byte CP (HL) ;any change? LD C,15 CALL NZ,CSPINC ;yes, print CRLF and 15 spaces LD A,"t" ;print "to" msg LD (SCMD0+1),A ;..& dispose of any remaining CR CALL CPYWY ;shift Dparam over the Cparam slot LD HL,SPARAM ;copy original current values for safety LD DE,CPARAM CALL COPYF2 POP AF CALL SCMD ;get destination track & sector values SDESTE: LD DE,EPARAM-1 ;save destination values CALL SRPT5 JR SRPT4 ;put back original parameters & ret ;------------------------------- ;get drive number SDRV: AND 01111111b ;lose next Drv command PUSH AF CALL CONIN PUSH AF SUB "A" ;check if range A-P JR C,SDRV1 OR 10000000b ;nobble last char LD (SCMD0+1),A AND 01111111b CP "P"-"A"+1 JR C,SDRV2 ;OK, skip SDRV1: LD A,(CDRIVE) SDRV2: LD (CDRIVE),A ADD A,"A" CALL POUT POP AF CP "W" ;Whole command? JR NZ,SDRV3 ;no, skip LD (SCMD0+1),A ;put "W" in for WHOLE routine SDRV3: CALL NZ,CONIN ;get next char (Whole or SCMD) CALL WHOLE ;was it Whole? JR NZ,SRETW ;no, jump & ret LD (MENUJJ+1),A ;force MENU to put in last track after CHECK XOR A LD (CBLKSW),A ;ensure no block translation LD HL,0 ;zeroise track count LD (CTRACK),HL LD HL,(MAXTRK) JR STRK1 ;continue with sector ;------------------------------- ;get Track no. & put into CTRACK STRK: AND 10101101b ;lose next Trk & Blk commands PUSH AF LD HL,(CTRACK) CALL GETDEC LD (CTRACK),HL XOR A LD (CBLKSW),A ;ensure no block translation CALL WHOLE ;was there a Whole command? JR NZ,SRETW ;no, jump & ret LD HL,(CTRACK) INC HL STRK1: LD (UTRACK),HL ;make Utrk = Ctrk+1 POP AF LD HL,0 ;zeroise sector count LD (CSECT),HL LD (USECT),HL XOR A ;no more commands LD (UBLKSW),A ;force Track interpretation RET ;------------------------------- WHOLE: CALL SPINC LD A,(SCMDX+1) AND 00000010b XOR 00000010b RET NZ LD A,(SCMD0+1) CP "W" ;was last char "W"? RET NZ PUSH AF LD (MENU7+1),A ;make it a repeated function CALL PRINT DC "Whole" SRETW: POP AF RET ;------------------------------- ;get Block no. & block offset SBLK: AND 10001101b PUSH AF PUSH BC LD B,4 ;no. of i/p chars XOR A ;clear SBLK3 LD HL,SBLK3+2 LD (HL),A DEC HL LD (HL),A SBLK1: CALL GETHEX ;get hex i/p JR C,SBLK2 ;jump if non-hex RLD ;put into CBLOCK INC HL RLD DEC HL DJNZ SBLK1 SBLK2: LD A,4 ;test if any hex i/p CP B SBLK3: LD HL,0 ;hex i/p stored here JR Z,SBLK4 ;skip if no hex i/p LD (CBLOCK),HL SBLK4: LD HL,(CBLOCK) CALL Z,B4HEX ;print BLK no. if no meaningful i/p LD A,":" CALL POUT LD A,(SCMD0+1) ;get last char CP CR JR Z,SBLK5 CP " " JR Z,SBLK5 CP "W" JR Z,SBLK5 LD HL,(CBLCKE) ;get previous extension value CALL GETDEC ;get new value JR SBLK6 SBLK5: LD A,"0" CALL POUT CALL POUT LD HL,0 SBLK6: LD (CBLCKE),HL LD (CBLKSW),A POP BC ;get space value CALL WHOLE ;check for Whole command JR NZ,SRETW ;no, jump & ret LD (UBLKSW),A ;force examinatoin of Blk cmnd POP AF LD HL,(CBLOCK) INC HL LD (UBLOCK),HL ;put CBLOCK+1 in UBLOCK LD HL,0 LD (UBLCKE),HL ;put in block boundary XOR A ;no more commands RET ;------------------------------- ;get Sector no. & put into CSECT SSEC: PUSH AF LD HL,(CSECT) CALL GETDEC LD (CSECT),HL XOR A LD (CBLKSW),A ;ensure no block translation POP AF AND 11001101b ;lose next Sec & Blk commands JP SPINC ;------------------------------- ;put Block no. & block offset into CTRACK & CSECT KBLK: LD A,(CBLKSW) OR A RET Z LD HL,(SYSTRK) LD (CTRACK),HL LD HL,(CBLCKE) ;start with Block extension sectors LD (CSECT),HL LD BC,(CBLOCK) JR KBLK2 KBLK1: LD DE,(BLKSIZ) INC DE ;DE= no. of sectors/block CALL ADDSEC DEC BC KBLK2: LD A,B OR C JR NZ,KBLK1 XOR A LD (CBLKSW),A ;force Trk interpretation RET ;------------------------------- ;control byte definitions for SUBCMD ; X000 0000 - Drive ; 0X00 0000 - Track ; 00X0 0000 - Sector ; 000X 0000 - Block ; 0000 X000 - marker to allow repeated function ; 0000 0X00 - marker to allow repeated function inc DRV ; 0000 00X0 - marker to allow "Whole" command ; 0000 000X - no list device allowed MENUM: DB "Help or",CR,LF DC "(space) - prints this menu" DB 0 DW HELP DC "Display - display current buffer" DB 0 DW DISPLY DC "Alternate - switch to alternate buffer" DB 0 DW ALT DC "Edit - edit current buffer (hex or ,ascii)" DB 0 DW EDIT DC "Fill - fill current buffer (hex or ,ascii)" DB 0 DW FILL READM: DC "Read -" DB 11111010b DW READ DC "Next - read next sector" DB 0 DW NEXT DC "Back - read previous sector" DB 0 DW BACK WRITEM: DC "Write -" DB 11111010b DW WRITE DC "Copy -" DB 11111110b DW COPY DC "Verify -" DB 11111010b DW VERIFY DC "List - list directory block usage" DB 10000000b DW LIST DC "Translate - displays sector translation table" DB 10000000b DW TRANS DC "Quit - exit to CP/M" DB 0 DW 0 CMDS EQU 14 ;no. of commands available DB "(HELP Key) - exit to CP/M at any time",CR,LF DB "(Esc) - return to Command mode at any time",CR,LF,LF DB "notation - d= decimal, h= hex",CR,LF DB "Whole - is available under Drv, Trk & Blk",LLF DB 0 ;------------------------------- ;bit pattern in A organised as follows:- ; X000 0000 Drive ; 0X00 0000 Track ; 00X0 0000 Sector ; 000X 0000 Block ; 0000 X000 Repeated functions ; 0000 0X00 Repeated functions including drive SUBMSG: PUSH HL ;save MENU msg pointer PUSH BC LD B,NSUB ;no. of messages LD HL,SUBMM SUBM1: RLCA ;rotate to carry CALL C,PMSG ;print if bit set CALL NC,DMSG ;else send to dummy print routine INC HL ;inc past address INC HL DJNZ SUBM1 POP BC POP HL SRET: AND 11110000b ;mask for SDEST only RET SUBMM: DRVM: DC "Drive A-P" DW SDRV TRKM: DC "Track d" DW STRK SECM: DC "Sector d" DW SSEC BLKM: DC "Block h:d" DW SBLK UNTLM: DC "until Trk Sec Blk" DW SRPT TOMSG: DC "to DvTkScBk" DW SRET NSUB EQU 6 ;no. of commands ;------------------------------- LIST: CALL CRLF RET ;------------------------------- TRANS: CALL CHK ;get parameters CALL PRINT DC "decimal sectors" LD BC,10*256+10 ;0-9, 10 spaces CALL CRLLF CALL SPINC TRANS1: LD C,4 ;spaces CALL SPINC LD A,10 SUB B CALL B2HEX ;print 0 - 9 DJNZ TRANS1 CALL CRLF LD HL,0 TRANS2: LD BC,10*256+3 ;0-9, 3 spaces CALL CSPINC LD A," " CALL B2DEC1 LD C,1 ;1 space CALL PRINT DC ":" TRANS3: CALL SPINC PUSH HL ;CP/M sector no. PUSH BC CALL CXLT0 ;translate CALL B2DEC2 ;print translation POP BC POP DE ;get sect no. INC DE LD HL,(MAXSEC+1) ;sector maximum SCF SBC HL,DE ;limit reached? JP C,CRLF ;yes, finished EX DE,HL ;back to HL DJNZ TRANS3 ;do some more JR TRANS2 ;go start another 10 ;------------------------------- VERIFY: LD A,(CURUP) LD (MENU9+1),A ;do automatic CURUP in SCAN CALL CHECK1 ;get & display disk parameters LD HL,(CSECT) JP READF ;high speed read ;------------------------------- ;Read previous sector BACK: CALL DECSEC JR READ ;------------------------------- ;Read Next sector NEXT: CALL INCSEC ;------------------------------- ; Read or READ: CALL CHECK CALL FORCE ;force BIOS to read every sector every time JR DSPLY0 ;------------------------------- ;part of ^C & ESC routine, invokes EDITF in edit mode CONED: CALL EDITF CALL CRLF ;------------------------------- ;switch to alternate buffer ALT: LD HL,(BUF1) EX DE,HL LD HL,(BUF2) LD (BUF1),HL EX DE,HL LD (BUF2),HL ;------------------------------- DISPLY: CALL CHECK DSPLY0: CALL CRLF LD BC,16*100h+8 ;write 0-F for HEX heading & 8 spaces CALL CSPINC ;CRLF followed by C spaces XOR A DSPLY1: LD C,2 CALL SPINC ;2 spaces CALL B1HEX ;print 0-9,A-F INC A DJNZ DSPLY1 CALL SPINC LD B,16 ;write 0-F for ASCII heading XOR A DSPLY2: CALL B1HEX INC A DJNZ DSPLY2 CALL CRLLF ;display buffer LD HL,(BUF1) LD BC,8*100h+5 ;B= 8 rows, C= 5 spaces LD E,"0" ;row address character DSPLY3: CALL SPINC LD A,E ;char to A CALL POUT ;print it CALL DISPHL ;print row of HEX & ASCII INC E ;next char DJNZ DSPLY3 RET ;------------------------------- ; display 16 characters in HEX & ASCII starting at addr in HL DISPHL: PUSH BC LD C,2 ;2 spaces CALL SPINC LD B,16 ;B= 16 values PUSH BC DEC C ;1 space PUSH HL DSPHL1: LD A,(HL) ;get value CALL SPINC ;print 1 space CALL B2HEX ;print hex display INC HL DJNZ DSPHL1 POP HL POP BC CALL SPINC ;2 spaces DSPHL2: LD A,(HL) ;get value again CALL PASC ;print ASCII only INC HL DJNZ DSPHL2 POP BC JP CRLF ;------------------------------- ; force BIOS to do a sector read away from current sector, ; & hence complete a pysical read or write FORCE: LD HL,(MAXSEC+1) DEC HL SRL H ;divide by 2 RR L EX DE,HL LD HL,(CSECT) ;get current sector LD A,(FRC) ;get FRC byte OR A JR Z,READF ;jump if special forcing not required PUSH HL ;save SBC HL,DE ;take away half total sectors JR NC,FORCE1 ;jump if still +ve ADC HL,DE ;add total sectors ADC HL,DE FORCE1: CALL READF ;do a read POP HL ;now read desired sector ; read a sector via sector translation table READF: PUSH HL CALL CXLT0 ;translate if neccessary READFF: CALL 0 ;read sector POP HL ERRCHK: OR A RET Z PUSH HL CALL PRINT DC "*** Read Error - Sector" POP HL JP B2DEC ;------------------------------- ; write a sector via sector translation table WRITEF: LD BC,(BUF1) ;write from first buffer CALL SETDMA LD HL,(CSECT) CALL CXLT0 ;set sector via sector trans table LD C,1 ;normal sector write WRITF: CALL 0 ;write sector JR ERRCHK ;check for errors ;------------------------------- WRITE: CALL CHECK LD BC,(BUF2) ;read sector into alternate buffer CALL SETDMA LD HL,(CSECT) CALL CXLT0 ;set sector via translation table CALL READF CALL WRITEF ;write sector JP FORCE ;------------------------------- EDIT: LD HL,CONED ;addr for ESC & ^C handling LD (ABORT+1),HL ;modify ESC & ^C to call EDITF CALL DISPLY ;display buffer LD HL,(BUF1) ;copy data buffer LD DE,(BUF2) ;to alternate buffer LD BC,128 LDIR EDIT0: LD A,(CURUP) ;bring cursor over first character LD B,8 CALL CURS1 LD HL,(BUF1) ;first location EDIT1: CALL WEDIT ;get position in line in C LD A,CR CALL POUT LD A,C ;multiply by 3 ADD A,A ADD A,C ADD A,9 ;add 9 LD B,A CALL CURS ;bring cursor to right place ;get value & put in buffer CALL GETHEX ;get single hex digit in A (no carry if OK) JR C,EDIT10 ;not hex, jump RLCA RLCA ;rotate to upper nibble RLCA RLCA LD B,A ;save in B EDIT3: CALL GETHEX ;get other lo nibble JR C,EDIT3 ADD A,B ;add together LD (HL),A ; update ASCII representation EDIT4: LD A,C ;position in line SUB 17 CPL ADD A,A ADD A,15 LD B,A ;shift cursor to ASCII position CALL CURS LD A,(HL) CALL PASC ;print ASCII only ; check if last position in line, if so goto next line EDIT5: LD A,C ;get position in line CP 15 ;last position? JR NZ,EDIT7 EDIT6: CALL CRLF EDIT7: INC HL ;inc buffer pointer ; check if still in buffer space EDIT8: CALL WEDIT ;get new positions JR NC,EDIT9 ;jump if not past beginning INC HL ;inc buffer pointer INC DE CALL CRLF EDIT9: EX DE,HL ;HL= distance into buffer LD BC,128 SBC HL,BC ;past end? EX DE,HL JR C,EDIT1 ;no, jump JR EDIT0 ;yes, start at beginning ;------------------------------- ;entry other than hex EDIT10: CP "," ;ascii entry? JR NZ,EDIT12 ;no, jump CALL POUT EDIT11: CALL CONN ;get full range ASCII i/p AND 7Fh ;remove top bit CP " "-1 ;cntrl char? JR C,EDIT11 ;yes, do again CP 7Fh ;DEL? JR Z,EDIT11 ;yes, do again LD (HL),A ;put in buffer CALL POUT ;print it JR EDIT4 ;print in ASCII representation ;--------------- EDIT12: CP 0 ;left cursor: then cursor back JR Z,EDIT13 CP BS ;backspace: then cursor back JR NZ,EDIT16 ;no, continue ;do backspace EDIT13: LD A,C ;get position in line OR A ;first position? JR NZ,EDIT15 ;no then skip EDIT14: LD A,(CURUP) ;go up a line CALL POUT EDIT15: DEC HL JR EDIT8 ;--------------- EDIT16: CP 0 ;down cursor JR NZ,EDIT17 ;no, continue LD DE,16 ADD HL,DE ;add one line CALL WEDIT ;in range? EX DE,HL LD BC,128 SBC HL,BC ;test for end EX DE,HL ;restore HL DEC HL ;adjust for space JR C,EDIT6 ;in range, jump LD DE,15 ;put back as it was SBC HL,DE ;--------------- EDIT17: CP 0 ;right cursor JR Z,EDIT5 CP " " ;space: then cursor forward JR Z,EDIT5 ;--------------- EDIT18: CP 0 ;up cursor JR NZ,EDIT19 ;no, continue LD DE,16 SBC HL,DE ;subract one line CALL WEDIT ;in range? INC HL ;adjust for backspace JR NC,EDIT14 ;in range, jump LD DE,15 ;put back as it was ADD HL,DE ;--------------- EDIT19: CP CR ;CR: then finish JP NZ,EDIT1 ;nothing valid, go back ;finish with edit - put cursor back underneath display EDITF: CALL WEDIT LD A,E AND 11110000b SRL A SRL A SRL A SRL A CPL ADD A,9 LD B,A EDITF1: CALL CRLF DJNZ EDITF1 RET ;------------------------------- ; get position in buffer in DE, & position in line in C WEDIT: PUSH HL ;current buffer addr OR A ;clear carry EX DE,HL LD HL,(BUF1) ;get buffer start addr EX DE,HL SBC HL,DE EX DE,HL ;DE= position in buffer PUSH AF ;save flags LD A,E AND 00001111b LD C,A ;C= position in line POP AF POP HL RET ;------------------------------- CURS: LD A,(CURRT) CURS1: CALL POUT DJNZ CURS1 RET ;------------------------------- FILL: CALL GETHEX ;get single hex digit in A (no carry if OK) JR C,FILL2 ;not hex, jump RLCA RLCA ;rotate to upper nibble RLCA RLCA LD B,A ;save in B FILL1: CALL GETHEX ;get other lo nibble JR C,FILL1 ADD A,B ;add together JR FILL4 ;entry other than hex FILL2: CP "," ;ascii entry? JR NZ,FILL ;no, jump CALL POUT FILL3: CALL CONN ;get full range ASCII i/p AND 7Fh ;remove top bit CP " "-1 ;cntrl char? JR C,FILL3 ;yes, do again CP 7Fh ;DEL? JR Z,FILL3 ;yes, do again CALL POUT ;print it FILL4: LD HL,(BUF2) LD (HL),A LD D,H LD E,L INC DE LD BC,127 LDIR CALL CRLLF JP ALT ;------------------------------- SOD: PUSH HL PUSH BC PUSH AF CALL PRINT DC " SOD " POP AF POP BC POP HL RET ;Copy from CDRIVE, CTRACK, CSECT until UTRACK, USECT ; to DDRIVE, DTRACK, DSECT COPY: LD HL,(BUF1) ;save buffer pointer LD (BUF3),HL CALL CHECK ;display options LD DE,SPARAM ;save Cparam CALL COPYF1 LD HL,COPYE LD (ABORT+1),HL ;direct ESC & ^C routine to COPYE LD A,(MENU7+1) ;examine repeat function indicator OR A JR Z,COPY1A ;jump if none LD HL,UPARAM ;copy Uparam to Cparam CALL COPYE1 ;do it CALL KBLK ;now that CHECK is done- translate block CALL DECSEC ;adjust to show last physical sector OR A ;clear carry LD HL,(CSECT) ;get upper sector limit LD (COPY9+1),HL LD DE,(SSECT) ;get current sector SBC HL,DE ;+ve? LD BC,(MAXSEC+1) LD (COPY16+1),BC ;save max no. sectors per track LD DE,(CTRACK) ;load upper track limit LD (COPY8+1),DE JR NC,COPY1 ;jump if +ve ADD HL,BC ;add maxsec sectors DEC DE ;minus 1 track COPY1: LD (COPY13+1),HL ;save sector difference PUSH HL POP BC ;save also in BC OR A ;clear carry LD HL,(STRACK) ;get current track EX DE,HL SBC HL,DE ;legal? LD (COPY14+1),HL ;save track difference JR C,COPY2 ;no, skip "until" section ADC HL,BC ;Trk+Sec=0? JR NZ,COPY2 ;no, do it COPY1A: SCF COPY2: PUSH AF JR C,COPY4 ;skip if carry set CALL CPRINT COPY3: DC " last" CALL CHECK COPY4: CALL CRLLF LD C,4 ;4 spaces CALL SPINC LD HL,TOMSG ;"to" msg CALL SMSG LD HL,DPARAM ;copy Dparam to Cparam CALL COPYE1 CALL CHECK POP AF ;get those flags again JP C,COPY17 ;skip next bit LD A,(SDRIVE) ;get current drive LD HL,UDRIVE ;get dest drive CP (HL) ;same drive? JP NZ,COPY12 ;no, go straight to routine ;check that there is enough buffer to absorb the entire read OR A LD HL,(WBOOT+1) LD L,0 LD DE,BUFF2 SBC HL,DE ;HL= available buffer space LD BC,128 LD DE,-1 ;allow for buffer COPY5: SBC HL,BC INC DE ;sector count JR NC,COPY5 LD HL,(COPY13+1) ;sector difference EX DE,HL ;HL= buffer space in sectors LD (COPY10+1),HL OR A SBC HL,DE ;+ve? (buffer space - difference JR C,COPY7 ;no, don't bother checking track LD DE,(COPY16+1) ;sectors per track LD BC,(COPY14+1) ;track difference COPY6: LD A,B OR C ;track difference exhausted? JR Z,COPY12 ;yes, buffer > difference, Ok to continue DEC BC SBC HL,DE JR NC,COPY6 ;loop if more to go ;buffer is insufficient, check whether dest is outside source area COPY7: LD DE,(STRACK) LD HL,(SSECT) CALL COMP ;compare JR NC,COPY12 ;dest < source, continue COPY8: LD DE,0 ;Utrack stored here COPY9: LD HL,0 ;Usect stored here CALL COMP JR C,COPY12 ;dest > source upper limit, continue CALL CRLF CALL CPRINT DC "*** Source sector(s) would be overwritten (buffer holds" COPY10: LD HL,0 ;buffer length stored here CALL B2DEC CALL PRINT DC " Sectors)" COPY11: CALL COPYE JP MENU ;--------------- COPY12: LD HL,COPY3 ;"last" msg again CALL CPMSG LD HL,CPARAM CALL COPYE1 COPY13: LD DE,0 ;sector difference stored here COPY14: LD BC,0 ;track difference stored here COPY15: CALL ADDSEC ;add DE sectors & adjust COPY16: LD DE,0 ;previous maxsec value LD A,B OR C DEC BC ;lose a track JR NZ,COPY15 CALL CHECK COPY17: LD HL,SPARAM ;put back current parameters CALL COPYE1 CALL CRLF CALL CONIN ;scan for ESC & ^C CP "N" JR Z,COPY11 LD A,(CURUP) LD (MENU9+1),A ;do automatic CURUP in SCAN LD HL,COPYRD ;ensure that COPYW routine jumps.. LD (COPYW2+1),HL ;..back to COPYRD LD HL,CPYWX ;addr COPY end EX (SP),HL ;exchange ret addr & COPYE PUSH HL ;restore ret addr ;Read section - adjust MENU to show Read command & jump to COPYR COPYRD: LD HL,BUFF2 ;put new pointer in LD (BUF1),HL LD HL,READM ;put Read msg in MENU routine LD (MENU5+1),HL LD HL,COPYR LD (MENU6+1),HL ;direct menu to jump to COPYR CALL HOME ;for safety CALL CRLF POP HL ;lose return JP MENU5 ;--------------- COPYR: CALL CHECK1 ;get & display disk parameters LD HL,(CSECT) CALL READF ;high speed read ;check buffer overflow OR A ;clear carry LD HL,(BUF1) ;get current DMA addr LD BC,128 ;buffer length ADD HL,BC ;add LD (BUF1),HL ;save next buffer start addr LD (COPYW1+1),HL ;save hi ram EX DE,HL ;DE = next addr LD HL,(WBOOT+1) ;get RAMTOP LD L,0 ;..to the nearest Kbytes SBC HL,BC ;new RAM limit SBC HL,DE ;subtract ram used so far RET NC ;get some more if ram available ;Write section - adjust MENU to show Write command & jump to COPYW COPYWR: CALL CRLF LD HL,WRITEM ;put Write msg in MENU routine LD (MENU5+1),HL LD HL,COPYW LD (MENU6+1),HL ;direct menu to jump to COPYW LD HL,BUFF2 ;start again at ram base LD (BUF1),HL CALL HOME ;for safety CALL CPYWY ;copy dest params into current params slot POP HL ;lose return JP MENU5 ;--------------- COPYW: CALL CHECK1 LD HL,(CSECT) CALL WRITEF ;check buffer overflow OR A ;clear carry LD HL,(BUF1) ;get current DMA addr LD BC,128 ;buffer length ADD HL,BC ;add LD (BUF1),HL ;save ADD HL,BC EX DE,HL ;DE = next addr COPYW1: LD HL,0 ;hi ram saved here SBC HL,DE ;subtract ram used so far RET NC ;not exhausted, get some more CALL SDESTE ;copy back Cparams COPYW2: JP COPYRD ;do another read ;--------------- CPYWX: LD HL,CPYWGG ;point to CPYWGG LD (COPYW2+1),HL ;make COPYW routine jump there when finished CALL COPYWR ;this is a sort of call! CPYWGG: CALL COPYFD ;save Cparam into Pparam COPYE0: LD HL,(BUF3) LD (BUF1),HL EX DE,HL LD HL,(COPYW1+1) LD BC,128 OR A SBC HL,BC LDIR ;read into current buffer JP CRLF ;--------------- COPYE: CALL COPYE0 LD HL,SPARAM ;put back current parameters COPYE1: LD DE,CPARAM JP COPYF2 ;--------------- CPYWY: LD DE,SPARAM ;put dest values into current LD HL,CPARAM LD BC,4*PRMLEN LDIR RET ;--------------- BUF3: DW 0 ;**************************************** ; check validity of track, sector CHK: LD BC,(BUF1) CALL SETDMA ;call SETDMA LD HL,DRVM CALL SMSG LD A,(CDRIVE) ;get current drive LD C,A ;save for SELDSK ADD A,"A" CALL POUT CALL PRINT DC " " CHK1: SELDSK: CALL 0 ;call SELDSK in BIOS LD A,H ;HL= addr Disk Parameter Header OR L ;..if drive exists JP Z,CHKERR ;jump if drive doesn't exist CALL GETADR LD (CXLT+1),HL ;save addr of translation table EX DE,HL LD DE,6 ADD HL,DE ;HL= pointer to addr of DIRBUF CALL GETADR LD (DIRBUF),HL ;save EX DE,HL CALL GETADR ;get addr of disc parameter block CALL GETADR ;get SPT LD (MAXSEC+1),HL ;HL= max no of sectors per track EX DE,HL INC HL LD A,(HL) ;BLM (blocksize*128 LD (BLKSIZ),A ;Blocksize = BLKSIZ * 128 LD B,A INC HL INC HL CALL GETADR ;HL= value of DSM PUSH DE ;save next addr LD D,H LD E,L ;DE=HL= DSM (data storage max in blocks) CHK2: ADD HL,DE ;multiply DSM by BLKSIZ DJNZ CHK2 LD BC,0 MAXSEC: LD DE,0 ;max no of 128 byte sectors per track INC BC SBC HL,DE ;devide DSM*BLKSIZ by MAXSEC JR NC,MAXSEC POP HL ;get next addr LD DE,6 ADD HL,DE ;HL= addr OFF (no of sys tracks) CALL GETADR LD (SYSTRK),HL ;save no of system tracks ADD HL,BC ;HL= max no of tracks LD (MAXTRK),HL ;save RET ;------------------------------- HOME: JP 0 ;addr HOME stored here ;------------------------------- CHECK: CALL HOME CHECK1: CALL CHK CALL KBLK LD HL,TRKM CALL SMSG LD HL,(CTRACK) ;current track PUSH HL ;save CALL B2DEC ;print decimal LD DE,(MAXTRK) OR A ;clear carry SBC HL,DE ;in range? POP BC ;current track JP NC,CHKERR ;no, jump SETTRK: CALL 0 ;call SETTRK CALL PRINT DC " " ;2 spaces LD HL,SECM CALL SMSG LD HL,(CSECT) ;current sector CALL B2DEC ;print decimal CALL CHKSEC ;check sector in range JP NC,CHKERR CALL PRINT DC " " ;2 spaces LD HL,BLKM CALL SMSG LD HL,(SYSTRK) EX DE,HL LD HL,(CTRACK) OR A ;clear carry SBC HL,DE LD (CPMTRK+1),HL ;no. of CP/M tracks LD DE,0 ;zero sector count LD BC,0 ;zero block count JR NC,CPMTRK CALL PRINT DC "-system" JR COPYFD CPMTRK: LD HL,0 ;current count of tracks to go LD A,H OR L JR Z,CHECK4 ;jump if no more DEC HL LD (CPMTRK+1),HL LD HL,(MAXSEC+1) DEC HL ADD HL,DE EX DE,HL ;DE= cumulative sector count LD HL,(BLKSIZ) INC HL EX DE,HL ;divide no. sectors this track CHECK3: SBC HL,DE ;by no. sectors per block INC BC ;increment block count JR NC,CHECK3 DEC BC ADC HL,DE ;put straight again EX DE,HL JR CPMTRK CHECK4: LD HL,(CSECT) ADD HL,DE EX DE,HL LD HL,(BLKSIZ) INC HL EX DE,HL CHECK5: SBC HL,DE INC BC JR NC,CHECK5 ADC HL,DE PUSH HL DEC BC LD H,B LD L,C LD (CBLOCK),HL CALL B4HEX LD A,"h" CALL POUT LD A,":" CALL POUT POP HL DEC HL LD (CBLCKE),HL ;save extension CALL B2DEC ; all done, copy CDRIVE, CBLOCK, CTRACK & CSECT to previous buffer COPYFD: LD DE,PPARAM COPYF1: LD HL,CPARAM COPYF2: LD BC,PRMLEN LDIR RET ; all wrong, copy previuos buffer back to CDRIVE, CBLOCK, CTRACK & CSECT CHKERR: CALL PRINT DC " does not exist" CALL COPYBK JP MENU COPYBK: LD HL,PPARAM LD DE,CPARAM JR COPYF2 ;------------------------------- CHKSEC: LD HL,(MAXSEC+1) EX DE,HL ;DE= MAXSEC LD HL,(CSECT) ;current sector OR A ;clear carry SBC HL,DE ;in range? RET ;Ok if carry set ;------------------------------- ;add no. of sectors in DE then adjust sector count ADDSEC: LD HL,(CSECT) ;get current destination ADD HL,DE ;new sector total LD (CSECT),HL ;save CALL CHKSEC ;subtract maxsec & test in range RET C ;ret if in range LD (CSECT),HL ;save new sector count.. LD HL,(CTRACK) INC HL ;inc track count LD (CTRACK),HL LD DE,0 ;nil sectors JR ADDSEC ;loop round till all sectors done ;------------------------------- UCOMP: LD HL,(USECT) ;upper sector limit LD DE,(UTRACK) ;upper track limit ;Compare DE with CTRACK & HL with CSECT ;return with carry if less, zero if same COMP: PUSH HL ;save sector OR A ;clear carry LD HL,(CTRACK) ;current track EX DE,HL SBC HL,DE ;in range? POP DE ;restore sector RET NZ ;not same track, ret LD HL,(CSECT) EX DE,HL SBC HL,DE ;in range? RET ;------------------------------- SETDMA: JP 0 ;call setdma ;------------------------------- ; get physical sector from BIOS and transform CXLT0: LD B,H LD C,L CXLT: LD DE,0 ;addr of sector translation table LD A,D OR E JR Z,SETSEC SCTRAN: CALL 0 ;call SECTRAN LD B,H LD C,L SETSEC: JP 0 ;call SETSEC (set sector) ;------------------------------- GETADR: LD E,(HL) ;get contents of (HL) into HL INC HL LD D,(HL) INC HL EX DE,HL RET ;DE points to next address ;------------------------------- TEST: CALL CPRINT DC " Drv Trk Sec Mkr Blk ext PSTCUD" LD B,6 LD HL,PPARAM TEST2: PUSH BC CALL CRLF LD A,B ADD A,"0" CALL POUT LD A,(HL) ADD A,"A" CALL POUT CALL TEST0 CALL TEST1 CALL TEST1 LD A,(HL) CALL B2HEX CALL TEST0 CALL TEST1 CALL TEST1 POP BC DJNZ TEST2 RET TEST1: CALL GETADR CALL B2DEC EX DE,HL JR TEST3 TEST0: INC HL TEST3: PUSH HL CALL PRINT DC " " POP HL RET ;------------------------------- ; multiply previous contents of HL by 10 then ; get a decimal number value from GETN & add into HL GETDEC: LD B,4 PUSH HL LD HL,0 GDEC1: CALL GETN JR C,GDEC2 ADD HL,HL ;*2 PUSH HL ADD HL,HL ;*4 ADD HL,HL ;*8 POP DE ADD HL,DE ;*10 LD D,0 LD E,A ADD HL,DE ;HL= previous *10 + present DJNZ GDEC1 GDEC2: POP DE LD A,4 ;check value stored here CP B RET NZ ;new value returned in HL EX DE,HL ;put old value back CALL B2DEC ;print it GDEZ: SCF RET ;------------------------------- GETN: CALL CONIN GETN1: CP "0" RET C CP "9"+1 JR NC,GDEZ CALL POUT SUB "0" RET ;------------------------------- GETHEX: CALL CONIN CP "F"+1 JR NC,GDEZ CP "A" JR C,GETN1 CALL POUT SUB "A"-10 RET ;------------------------------- CONN: EXX CONNIN: CALL 0 ;console input address stored here EXX CP 1EH ;^C JP Z,ABORT0 CP ESC RET NZ ABORT0: PUSH AF ;save flags ABORT: CALL MENU ;local abort routine addr stored here POP AF CP ESC JP Z,MENU JP WBOOT ;------------------------------- CONIN: CALL CONN CP "@" JR C,CON1 ;skip if number AND 5Fh ;make upper case CON1: LD (SCMD0+1),A ;put in SCMD routine RET ;----------------------- ;dummy message routine DMSG: PUSH AF DMSG1: LD A,(HL) ;go though string until bit 7 found INC HL ;inc address OR A JP P,DMSG1 ;loop if bit 7 unset POP AF RET ;----------------------- ;print message until a space is found SMSG: PUSH AF SMSG1: CALL POUTI CP " " JR NZ,SMSG1 POP AF RET ;----------------------- CPRINT: CALL CRLF ;print CRLF followed by string PRINT: EX (SP),HL ;point to message CALL PMSG EX (SP),HL ;restore return addr RET ;----------------------- PASC: AND 7Fh ;print only ASCII chars CP " " JR NC,PASC1 PASC2: LD A,"." PASC1: CP 7Fh JR Z,PASC2 JR POUT ;----------------------- CRLLF: CALL CRLF CRLF: LD A,CR ;print CRLF CALL POUT LD A,LF JR POUT ;----------------------- CPMSG: CALL CRLF PMSG: PUSH AF PMSG1: CALL POUTI ;print char OR A JP P,PMSG1 ;loop if bit 7 unset LD A," " ;print space JR POUT1 ;----------------------- POUTI: LD A,(HL) ;get char INC HL POUT: PUSH AF POUT1: EXX ;output char in A AND 7Fh ;reduce to normal ASCII chars LD C,A ;char to C PUSH BC CONOUT: CALL 0 ;address stored here POP BC LISTSW: LD A,1 ;print/not print switch byte OR A LISTP: CALL Z,0 ;LIST address stored here POP AF EXX RET ;----------------------- B4HEX: LD A,H ;convert HL to hex CALL B2HEX LD A,L B2HEX: PUSH AF ;save RRCA ;rotate hi nibble to low RRCA RRCA RRCA CALL B1HEX POP AF B1HEX: PUSH AF AND 0Fh ;low nibble only ADD A,90h ;convert DAA ADC A,40h DAA JR POUT1 ;print it ;----------------------- ; HL= binary value, print decimal ignoring leading zeros B2DEC: XOR A B2DEC1: LD (BDEC4+1),A ;replace leading zero's B2DEC2: LD A,0Fh LD (BDEC1+1),A ;enable leading zero detection PUSH HL LD DE,1000 ;divide by 1000 XOR A CALL BDEC ;do it & print 2 most sig chars LD DE,10 ;divide by 10 XOR A CALL BDEC ;do it & print next 2 chars LD A,L POP HL JR B1HEX ;print remainder BDEC0: INC A ;inc counter DAA ;adjust to decimal BDEC: SBC HL,DE ;entry point to circular subtract loop JR NC,BDEC0 ADD HL,DE ;undo last subtraction BDEC1: CP 0Fh ;most sig non-zero bit exist? (this changes) CALL NC,B2HEX ;yes, print both digits JR NC,BDEC2 ;yes, kill further leading zero replacement CALL BDEC3 ;no, replace leading zero OR A ;lower digit zero? JR Z,BDEC3 ;yes, jump & replace CALL B1HEX ;print lower digit BDEC2: XOR A LD (BDEC1+1),A ;kill further zero replacement RET BDEC3: PUSH AF BDEC4: LD A," " ;replace leading zero by char stored here CALL POUT POP AF RET ;----------------------- CSPINC: CALL CRLF SPINC: PUSH AF ;print no. of spaces defined by C PUSH BC LD B,C LD A," " SPINC1: CALL POUT DJNZ SPINC1 POP BC POP AF RET ;----------------------- $END EQU $ STACK EQU $END+78h ;stack space BUFF1 EQU STACK ;alternative buffer space BUFF2 EQU BUFF1+128 ;start memory buffer END ;/////////////////////////////////////////////////////////////////////////// ;----------------------------- LD HL,LISTM CALL PRN LD HL,DIRM CALL PRINTY LD HL,(SYSTRK) INC HL LD (CTRACK),HL RET DIRM: DB " Directory",CR,LF DB "Entry User File Ext Size Disc Space Allocation" DB CR,LF,0 DIREM: DB "*********** End of Directory ********",CR,LF,0 DAMM: DB CR,LF,LF," Disc Allocation Map",CR,LF,LF," ",0