IMD 1.18: 22/01/1996 7:51:56 micro cornucopia kaypro user group disk 37 utility primer     37-DISK DOC CRC COMCRCKLISTCRC DBUGABSTDOC DBUGABSTDOC] !"#$%DBUGINSTDOCJ&'()*+,-./DEBUG COM0123DEBUG Z80456789:;<=>?@ABCDEBUG Z80DEFGHIJKLMNOPQRSDEBUG Z80TUVWXYZ[\]^_`abcDEBUG Z80defgDU-V86 ASMhijklmnopqrstuvwDU-V86 ASMxyz{|}~DU-V86 ASMDU-V86 ASM@DU-V86 COM; Kaypro Disk 37 Utilities Primer Disk 37 Contains three utilities. We've included assembler source code for DEBUG and DU-V86. These programs, along with the associated DOC files, will give you an excellent opportunity to see what makes the utilities tick. DEBUG In issue #25 of Micro Cornucopia, Richard Amyx wrote an article ----- titled "Why I Wrote a Debugger." DEBUG is a beta test copy of his Z-80 debugger discussDU-V86 DOCdVFILER COM>VFILER DOCVFILERSCASMed in the article. DBUGINST.DOC will instruct you in the use of DEBUG and DBUGABST.DOC contains an abstract of the program. The top of the relocated code has been dropped from E000H to C000H to protect the CCP. DU-V86 The latest version of the disk utility DU allows you to view and edit ------ any byte on a disk. DU-V86 is compatible with CP/M versions 1.4, 2.x, and 3.x. VFILER Similar to SWEEP, VFILER performs the same basic file manipulations, ------   but adds screen oriented displays.*#"ɯ2i2}! "!"]]2 s : ~# ++FILE NOT FOUND++$: F*}’">͇†]<  CANNOT CLOSE CRCFILE$CRCKLISTCRCSK FULL: CRCFILE$  w# !]͐ DONE$!e S!]Q !eZ @ --> FILE: XXXXXXXX.XXX CRC = oS) \<‘@ ++OPEN FAILED++ !" !" * | ʹ) \!~2 #" Ý: ) > T : ) @ ++FILE READ Eth file - ! ~T #M M 2 2 2 2 ! >2 3o: : @ ***No CRC Files found***$> ʉ ʉw#: <2 P6: .6@ Can not parse string ! ~ʽT #ñM ! > 6 #6^#6! ~T #! 4M #x] #e w~ T #>2 @ - s M@ File not found ! 4 >2 o: G: „: G: „@ *Match* 2 ! 4@ <-- is, was --> : ) > T : ) M 2 ! 4<2 ~# @ Not a space between CRC values<2 G-CATALOG??? --> FILE: DU-V86 .DOC CRC = 66 0E --> FILE: CRC .COM CRC = B2 07 --> FILE: VFILER .COM CRC = C2 ED --> FILE: DEBUG .Z80 CRC = 5F A8 --> FILE: DEBUG .COM CRC = 1A 7C --> FILE: DBUGINST.DOC CRC = F4 19 --> FILE: DBUGABST.DOC CRC = 5C 94 --> FILE: DU-V86 .COM CRC = 88 A0 --> FILE: 37-DISK .DOC CRC = BC 2F --> FILE: DU-V86 .ASM CRC = 33 16 --> FILE: VFILER .DOC CRC = FE FB --> FILE: VFILERSC.ASM CRC = B3 14RROR++ <* |): o% |g}o" 2  ; 0T ~T #~A > T > _h : F{͇2h2|: ʲ !\   :\2 ! \  \ ! \  \! \  \<7=Ɓo&   ]  2h2|2   ~$#~# x  : F} *}= ">͇1 ].”#””͡”››tK››͸›* =: =͔="  ʳ ª~#.  ¿~  #~  .+~#0:0~#!A.O.Gy~#0M0 K MMɷ:m2 FSÄCRCKLIST$$$››tK›, › **}|!"*{z*~]*"Ü  DISK FULL: CRCFILE$!"*~#CRC.COM 5.0 6/18/82CRCKFILE???!9" 1 M @ CRC Ver 5.0 CTL-S pauses, CTL-C aborts :] O@ ++Searching for CRCKLIST file++@ Now searching for "CRCKFILE" file++ !  1 .) F!  ͡]͸! ͔0CRCKLIST???CRCKFILE???!9" M à*,*.}|ډ!".*,{z{** u*.".G*.",!".***,}>*.#".ɯ22)! ",". <  NO FILECRC FILE$!   >. @ Checking wi match - : ̓ : J M @ Quantity of lines failed parse test - : ̓ : ~ M @ Quantity of file(s) not found - : ̓ * d͔  ͔ 0T  Җ Wyʩ 0T 0ztiil   AMYX DEBUG ABSTRACT This abstract is provided to help you find your way through AMYX DEBUG without having to tough out all the logic on your own. It is not intended to be a byte-by-byte theory of program operation. The source code is adequately commented to reveal all the nitty- gritty details. Major program divisions are indicated by cen- tered headings; the routines or subroutines they include are in- dicated by the capitalized routine names followed by a colon, in thenitiali- zation module; the highest relocation address is actually set at RELTOP-2 by calculations located at the very end of the program code. RELTOP is currently set at E000H, the bottom of the CCP for the Kaypro 4-84; RELTOP can be changed to any reasonable val- ue without making any other changes in program code. DEBUG object code has successfully been transported directly to an older Kaypro II. GENERAL PROGRAM ORGANIZATION Briefly, DEBUG is organized as follows: Rd be in the screen cursor positioning subroutine POSCUR. This subroutine can simply be rewritten in its entirety as re- quired. The Kaypro cursor positions indicated by equates in the data section of the program are as follows (screen "home" at line 0, column 0): COMCUR: cursor position for "Command?" prompt. 3720H = line 23, column 0 ERRCUR: cursor position for error messages. 3742H = line 23, column 34 BRKCUR: cursor position for bre(Calls ONLYRG and DISRG) ONLYRG: Display just the contents of a CPU register DISREG: Display the register indirects DISBRK: Display breakpoint addresses GETWRD:/GETBYT: Convert ASCII command buffer data to hex values NXTCHR: Adjust command buffer pointer and counter when decoding a command COMCHK: Check for bad command COMCH1: Print error message and cancel command SETBRK: Install breakpoint in user program code MATCH:  usual assembler label fashion. Once DEBUG is activated, it is, in essence, just a menu-driven program, with the "menu" selection made through the entry of a command letter. When the command is decoded, a jump is made to the selected routine. When the command action is finished, the program returns to the "Command?" prompt and waits for another command entry. DEVELOPMENT/MODIFICATION While AMYX DEBUG was developed on (and for) a Kaypro 4-84 Plus 88, it was deliberatelyELOCATION AND INITIALIZATION BEGIN DEBUG CODE BIOS equates Character equates Cursor positions Messages Variables --MAIN PROGRAM-- ENTRY: Entry ENTRY0: Screen display COMAND: Command entry COMDEC: Command decode COMMAND ACTION MPAGE: Page memory forward or back DSPLAY: Display selected page of memory MODMEM: Modify memory BRAKPT: Set breakpoint from keyboard entry SSTEP: Single-step CLRBRK: Clear all breakpoints Fakpoint display. 3520H = line 21, column 0 MODCUR: cursor position for modify memory prompt. 3729H = line 23, column 0 MEMCUR: cursor position at beginning of memory display. 2D26H = line 13, column 6 The only other Kaypro specifics are the clear screen character (26D) and the cursor "home" character (30D). The highest address of relocated code is specified in the equate RELTOP:, located just at the end of the relocation and i Search breakpoint table for address LOAD: Fill memory with specified byte REPBRK: Repair breakpoint set in user program code GENERAL SUBROUTINES/PRIMITIVES GENRET: Return for several subroutines KBD: Strobe keyboard for one character CHROUT: Print one character on screen MSGOUT: Print string on screen HX2ASC: Convert one hex byte to ASCII and output to screen ASC2HX: Convert one ASCII character to a hex value NEWLIN: Drop cursor one screen line  designed to make maximum use of standard CP/M calls so that it could easily be transported to any CP/M based computer with an 80 x 24 screen display and a means of dis- crete screen cursor positioning. The program source code uses only standard Z80 mnemonics (except for the 'TITLE' peculiar to Micro Cornucopia's CROWECPM) and should be compatible with any standard Z80 assembler. The use of a macro assembler is not required. The only real "customization" necessary for non-Kaypro computers shoulIXBRK: Clear specified breakpoint JUMP: Jump to specified address FILL: Fill memory with specified byte REGMOD: Modify register contents MOVE: Block move SEARCH: String search HARITH: Hex arithmetic DBEND: Return to user program DEBUG-SPECIFIC SUBROUTINES DISPAF:/DPRIM: Display contents of CPU AF and AF' registers DISPBC:-DISPPC: Display contents of all other CPU registers DISMEM: Display contents of 128-byte page of memory REGRET:    POSCUR: Place screen cursor at specified position BEEP: Beep keyboard DELAY: Delay approximately three seconds TABLES STACK RELOCATION AND INITIALIZATION Relocates the DEBUG code from CODBEG: through CODEND: with the highest program byte one address below the address specified by the equate RELTOP:. All breakpoints are cleared; the jump to the relocated CODBEG: address is written into the DDT call address at 38H; a loaded-and-relocated confirming message  COMAND: for command entry. GENERAL PROGRAM FLOW Once DEBUG is invoked, the opening screen display is complete, and the "Command?" prompt is written, the general program flow is as follows: --Check for immediate commands. Immediate commands are acted upon as soon as their character is entered. --Enter characters into the command buffer. All charac- ters are checked to be sure they're valid as they are entered. Valid characters are added to the command buf- fer (CL:) to see if any breakpoints are set and, if so, installs them in the user program code. A check is then made to see whether entry to DEBUG came from an RST 38H coded into the user program or from a breakpoint. This test is made by decrementing the user PC (SYSPC:) and testing that address against entries in the breakpoint table (BRKTBL:). (The RST 38H places the next program address on the stack as its "return" point.) If no match is found, DEBUG execution jumps di- rectly to ENTRY0: to write AND: is where the program lands upon completion of the opening display or at the end of any command action. KEYIN: and KEYIN1: begin the keyboard strobe loop to pick up characters. Input characters are checked first to see whether they're immediate commands. If so, they are acted upon immediately; if not, the first character is checked to be sure it is a valid command. The command character is also stored in COMFLG: as an indicator that a command is in progress. Characters after the first one aris printed on the screen, together with the addresses that the relocated DEBUG oc- cupies; and system control is returned without a warm boot. DEBUG CODE Actual DEBUG code, starting at CODBEG:, follows the relocation and initialization section. The only action that takes place at CODBEG: is a jump to ENTRY:, where the main program starts. Be- tween CODBEG: and ENTRY: are located BIOS equates, character values, cursor positions, messages, and variable allocations. OMBUF:); invalid characters are signaled by a key- board beep and a stationary cursor. All commands (except immediate commands) are terminated with a carriage re- turn, which is NOT added to the buffer. --Decode the command. As soon as the first character in the command buffer is checked to see what it is, a jump is made to the indicated command section. The remainder of the command line is decoded in the command sections according to the command's requirements. After decoding, all commands athe screen display. If entry is from a breakpoint, the user code is repaired (see SETBRK: and REPBRK:) and the decremented PC is retained as the actual PC to resume ex- ecution of the user program. OPENING SCREEN DISPLAY At ENTRY0: the screen is cleared of whatever the user might have had on it; at ENTRY1: the writing of the DEBUG display begins. When the screen display is complete, the program jumps to COMRET: (located after the last command processing section), then to e checked in CKCHAR: to be sure that they're a hex digit, a regis- ter specifier, a period, a space, a backspace, or a carriage re- turn. Valid characters are added to the buffer in AD2BUF:, with the buffer pointer and character counter adjusted appropriately. Spaces are valid characters from the keyboard, but are not added to the command buffer. Invalid characters cause a jump to BADCHR: for a keyboard beep and cursor backspace. AD2BUF: and BADCHR: terminate with a jump to KEYIN1: for the next c MAIN PROGRAM ENTRY: is where the action starts. First, the user SP is saved and the DEBUG stack address is loaded into SP; then the user's register contents are pushed onto the DEBUG stack. The user PC, placed on the user's stack by the RST 38H DEBUG call, is stored, then popped off the user's stack and discarded. The user's cur- sor location is also saved in case the user's program includes direct screen manipulation. DEBUG then checks the breakpoint table (BRKTBre checked (COMCHK:) to be sure the correct number of characters was entered (i.e., a coarse check to be sure that all addresses and byte values are complete). --Carry out the command. --Return to the command line. All command action is ter- minated by a jump to COMRET:, where the command entry line is tidied up and flags are cleared, followed by a jump to COMAND:, where the buffer counters are reset and the "Command?" prompt is written. COMMAND PROCESSING MODULE COM  haracter. Entry of a carriage return (which is not added to the command buffer), sends program execution to COMDEC:, where the command is decoded. COMMAND DECODE COMDEC: begins decoding of the command. As soon as the command, the first character in the command buffer, is identified, program execution jumps to the indicated section. Command data are de- coded within each command routine, mainly by GETWRD: and GETBYT:, subroutines that translate the command buffer ASCs. If the next executable instruction is a conditional jump, the contents of the F register must be tested. If the jump condition is true, then a breakpoint is set at the jump address. Second, if the jump condition is not true or if the instruction is not a jump, then the length of the instruction at the current user PC must be determined. The length of the instruction is de- termined by comparing the byte at the PC, and the one following, if necessary, with the contents of tables TAB21:, TAB22:ng a breakpoint involves two steps. First, the break- point table (BRKTBL:) is checked to see if there's room for an- other breakpoint (indicated by a 0000 entry). The first 0000 en- try encountered is filled in with the address specified by the user. If no empty space is found, the keyboard beeps, the mes- sage "BREAKPOINTS FULL!" is displayed, and the program returns to COMAND:. Second, the breakpoint is installed in the user program code through a call to SETBRK:. reakpoint is repaired using REPBRK:. After the breakpoint is repaired, its entry in the breakpoint table is set to zero. When all breakpoints have been cleared, the breakpoint display line is rewritten to show its cleared status. CLEAR A SPECIFIED BREAKPOINT FIXBRK: clears one breakpoint specified by the user. The user's entry is checked against the breakpoint table, and a BAD COMMAND! message is posted if there is no match. If the specified address is ok, its table entryII characters into hexadecimal values. PAGE MEMORY FORWARD OR BACK (IMMEDIATE COMMAND) MPAGE: adds or subtracts 80H from the value saved in MEMSEL:, the area of memory selected for display, saves the new value in MEMSEL:, then rewrites the page of memory on display through a call to DISMEM:. MODIFY MEMORY MODMEM: begins the routine for modifying memory. This routine is initiated by the usual command format. Use of the modify routine is indicated by , TAB31:, TAB32:, and TAB42: (located at the very end of the program code). Bytes in tables TAB21 and TAB31 are single-byte indicators of two- and three-byte instructions respectively. If the byte at the PC is DD, ED, or FD, then the bytes in tables TAB22, TAB32, and TAB42 indicate two-, three-, and four-byte instructions re- spectively. A byte at the PC not found in the tables is assumed to be a one-byte instruction. Third, after the length of the instruction has been determined, the user PC a SINGLE-STEP SSTEP: begins the single-stepping routine. The gist of single- stepping is to emulate CPU action through software one step ahead of the CPU, setting a breakpoint at the executable instruction following the one shown at the PC. Single-stepping is effected through four separate actions. First, the code at the user PC must be discretely tested for jumps (SSTEP:-SSTE17:). If the next executable instruction is an unconditional jump, a breakpoint is set internally at the jump addres is set to zero, the breakpoint is repaired using REPBRK:, and the breakpoint display line is rewritten to show its new status. JUMP TO SPECIFIED ADDRESS JUMP: directs execution of the user program to the specified ad- dress. The specified address is loaded into the user's PC stor- age area; DEBUG then executes a "Go" through a jump to DBEND: (control is returned to the user program). FILL MEMORY AREA WITH SPECIFIED BYTE FILL: simply fills an area of msetting the byte MODFLG: to 1, after which the addresses to be modified are automatically incremented and dis- played with a "Modify: AAAA" prompt, and all the user enters is the new byte for the address. This subsequent modification is carried out through a loop from MODME1: to the end of MODME2:, and is terminated by the entry of a period in place of a byte value. SET BREAKPOINT FROM KEYBOARD ENTRY BRAKPT: begins the routine to set a breakpoint from keyboard en- try. Settiddress is incremented accordingly to establish the address of the next executable instruction. Fourth, the address of the next instruction is stored in the first breakpoint table location (overwriting anything that might have been there) and a breakpoint is installed at that address using SETBRK:. CLEAR ALL BREAKPOINTS (IMMEDIATE COMMAND) CLRBRK: starts the routine for clearing all six breakpoints. The breakpoint table is tested for entries. Where a nonzero entry is found, the b  emory from a specified starting address through a specified ending address with a specified byte. FILL: sets up the starting and ending addresses; the actual load- ing of memory is done in the subroutine LOAD:. MODIFY REGISTER CONTENTS REGMOD: starts the routine to modify the contents of a specified register. What's actually changed is the user register value stored on the DEBUG stack. The new value is loaded into the specified register only when a Jump or Go is executed om SEARC8: through the end of SEARC5: If the string is not found, the program jumps to SEARC6: and outputs a STRING NOT FOUND message. If the string is found, the program jumps to SEARC7:, where the address of the first character of the find is loaded into MEMSEL: as the address of the page of memory to be displayed, and the memory display is re- written. Additionally, if the string is found, the search can be resumed for further searching in the specified area of memory by entering S followed byen display is rewritten to show the new register status. BLOCK MOVE MOVE: uses the LDIR instruction to copy the contents of a block of memory specified by starting and ending addresses to a loca- tion whose starting address is specified. The routine calculates the number of bytes to move from the starting and ending address- es, registering an error if start and end have been entered backward. STRING SEARCH SEARCH: searches an area of m next command. RETURN TO USER PROGRAM DBEND: is the "end" of the DEBUG program; that is, a jump to DBEND: from either a Go or a Jump command (or a Go internally ex- ecuted in single-stepping) returns control to the user program. The user's cursor position is restored, the user's PC is returned from storage to the bottom of the user stack, all registers are popped from the DEBUG stack, the user stack pointer is loaded in- to the SP register, and a RETurn is executed. and the contents of the stack are popped back into the registers. First, the register identifier in the command buffer is "hashed" by adding the ASCII values of the two characters together. Be- cause the user SP and PC are not stored on the DEBUG stack, a discrete test is made for them first. If the register to be mod- ified is neither SP nor PC, the test for which regular register is made by using a CPIR instruction to compare the hashed identi- fier value with the possible hashed identifier va a period. In this case, the search starts again with the first address following the last character of the last find. The little subroutine ENDTST: following the string search routine is unique to SEARCH: and was placed here to keep it from getting lost in the chaff of other little subroutines. PERFORM HEX ARITHMETIC HARITH: just performs sixteen-bit hexadecimal addition or sub- traction and prints the results on the command line following the input. The result of a emory specified by a starting and ending address for a string defined by hex values. The length of the input string is limited only by the size of the command buf- fer. Because the command requires two bytes (ASCII characters) per hex byte of data, the maximum string length turns out to be 11 bytes. Because the length of the string is variable, its end must be marked by a period. If no period is found when decoding the command buffer, an error is registered. The search algorithm itself runs fr DEBUG-SPECIFIC SUBROUTINES DISPAF:/DPRIM: prints the AF and AF' register displays on the screen. On entry, IX contains the DEBUG SP. A, E, and HL are destroyed. DISPBC:-DISPPC: print the displays for the indicated registers by loading HL with the register contents from the DEBUG stack and then jumping to REGRET:. On entry, IX contains the DEBUG SP. DISMEM: prints the 128-byte page of memory in the display. On entry, the address of the selected area of memory for display is lues, which are in table REGTAB: (at the end of the program). If no match is found, the program jumps to BADREG: and outputs the BAD COMMAND! message. If a match is found in the table, the contents of the C register at the match are used to calculate the location of the register on the stack from the current DEBUG SP. If the register to be modified is SP or PC, dummy displacements of FFH and FEH respect- ively are used to so indicate. The indicated change is made in memory, then the whole screcalculation remains on display until any key is pressed. RETURN TO COMMAND LINE COMRET: is the location to which all commands jump when their action is completed. In COMRET:, the command line is blanked and the flags MODFLG: (which indicates that a modify memory command is in process) and COMRET: (used in testing for immediate com- mands to indicate that any command is in process) are turned off (set to zero). The program then jumps back to COMAND: to await input of the   in HL. A and BC are destroyed. REGRET: is the common return from all the register displays. It makes two subroutine calls to ONLYRG: and DISREG:. ONLYRG: prints the contents of a Z80 register to the CRT screen. On entry, the hex word to be output is in HL. A and E are destroyed. DISREG: prints the sixteen indirect address contents for the reg- ister displays and the memory display. On entry, the address for which the indirects are to be displayed is in HL. A, BC, and E are destroyed. ere a breakpoint is requested is in DE. The byte of code at the breakpoint address is transferred to its position in CODSAV:; the breakpoint address in the user program is then loaded with FFH (RST 38H) to effect the breakpoint. A is destroyed. MATCH: searches the breakpoint table for a match with a break- point address requested to be cleared through FIXBRK:. On entry, the address to be tested is in DE. A, B, and HL are destroyed. LOAD: fills an area of memory from a specified starting ad be sure that the command contained the proper number of characters (the command buffer counter (CBUFCT:) should be zero after any command is decoded). If the command is good, only A is destroyed. If the command is bad, the subroutine continues on to COMCH1:, which beeps the keyboard and prints the BAD COMMAND! message. COMCH1: is also called any time a command error is detected for whatever reason (e.g., a missing period in the Search command entry). All registers are destroyed. SETBRK: ists hex value; e.g., "F" becomes 0FH. On entry, the ASCII character is in A; on exit, A contains the hex value. NEWLIN: outputs a carriage return-line feed sequence to the CRT screen to drop the cursor a line. E is destroyed. POSCUR: positions the screen cursor in accordance with Kaypro protocol. On entry, the hexadecimal row,column cursor position is in the BC register (B=row; C=column). E is destroyed. BEEP: sends an ASCII 07H to the keyboard. All registers are preserved. DELAY: does DISBRK: prints the breakpoint line on the display. Nothing is transferred in through CPU registers; uses A, B, DE, and HL. GETWRD:/GETBYT: decode a two-byte hex word or a hex byte from the ASCII representations in the command buffer. On entry, the cur- rent address of the command buffer pointer (COMBUF:) is in HL. GETWRD: returns the hex word in DE; GETBYT: returns the byte as a "low" byte in E. NXTCHR: advances the command buffer pointer (COMBUF:) and incre- ments the command buffer counter dress through a specified ending address with a specified byte. On entry, the start address is in DE, the end address is in HL, and the byte is in A. All registers are preserved. REPBRK: is just the inverse of SETBRK:. Here the breakpoint (FFH) in the user program is replaced with its proper code byte from the CODSAV: table. On entry, the breakpoint address is in DE. A is destroyed. GENERAL SUBROUTINES/PRIMITIVES GENRET: is a common return from any subroutine that push where breakpoints set either from user keyboard entry or internally during single-stepping are established. There are two tables involved with breakpoints, BRKTBL:, which contains the breakpoint addresses, and CODSAV:, which contains the bytes of user program code saved when a breakpoint is installed. These two tables are linked by a counter, BRKCNT:, to keep the bytes of saved code in proper relationship to the addresses where they be- long. On entry to SETBRK: the address of the user program wh nothing for approximately three seconds; used to hold error messages on the screen. The inner loop takes approximately one-half second to complete; hence, the total delay can be ad- justed in half-second increments by changing the value loaded in- to B in the first line. A, B, and HL are destroyed. TABLES REGTAB: contains the "hashed" identifiers (i.e., the sum of the ASCII values of the characters; A'=AF', B'= BC', etc.) for all the registers but SP and PC. Us(CBUFCT:). On entry, the com- mand buffer pointer is in HL; the current buffer character, which is in A, is preserved. NXTCHR: is used primarily by GETWRD: and GETBYT:, but is also called directly from the main command rou- tines for modifying register contents (REGMOD:), for hex arithme- tic (HARITH:) as a way of picking up a non-hex character (the arithmetic operator) discretely, and by SEARCH: as a way of dis- carding the period ending the command. COMCHK: checks any command after decoding toes BC, DE, and HL. KBD: uses the standard BDOS call to strobe the keyboard for one character, shifting the character to upper case if necessary. CHROUT: uses the standard BDOS call to output one character to the CRT screen. MSGOUT: uses the standard BDOS call to output a string to the CRT screen. HX2ASC: converts a hex byte to its ASCII representation and out- puts it to the CRT screen. The byte to be converted is in A on entry; E is destroyed. ASC2HX: converts one ASCII character to i  ed by the modify register (REG- MOD:) routine. Tables TAB21:-TAB42: are used in conjuction with the single- stepping routine (SSTEP:) to determine the length of any instruc- tion. The end of each of these tables is identified by TNNEND with an equate at location; the beginning and ending labels of the tables are used to calculate the table lengths for the CPIR instruction in the single-step routine. I set the tables up this way just in case I missed a byte or the information I was working from  ### structions. If the first instruction byte is DD, ED, or FD, and the second byte is any of TAB42:, then the instruction is four bytes long. STACK A 64-byte (32-word) stack (LOCSP:) is located at the very end of the program code. Inasmuch as ten stack words are always in use by DEBUG for the user register contents, the stack is effectively 22 words deep. This is probably adequately conservative. Al- though I haven't checked to see how deep the subroutines are gocertain about what you're doing, be sure that you have a cur- rent backup copy of the program you're testing, and, for your first few attempts, you might want to leave the diskette drive doors open if your program will allow. LOADING DEBUG To load DEBUG into memory, type DEBUG . DEBUG will load and automatically relocate to high memory, then print a confirming message on the screen. You should reload DEBUG any time you leave it to modify your source code or run any other program. INwas incorrect or incomplete. If a user finds an error in these tables, he can correct it by changing, adding to, or delet- ing from the tables without having to be concerned with any changes in program code per se. TAB21: contains one-byte indicators of two-byte instructions; that is, if the byte is any of TAB21:, then the instruction is two bytes long. TAB22: contains the second byte of two-byte indicators of two- byte instructions. If the first instruction byte is DD, ED, or FD, and the sec AMYX DEBUG INSTRUCTIONS GENERAL AMYX DEBUG is a debugger for Z80 CP/M that lets you examine and modify the contents of both the CPU registers and memory, search memory for specified contents, set breakpoints at specified pro- gram addresses, and single-step through a program. You can also perform hexadecimal addition and subtraction while DEBUG is ac- tive without affecting either DEBUG or the program being tested. Approximately 3000 bytes long, DEBUG is completely relo- ing, the limit is probably no more than eight, including PUSHes. I have not, in any event, found the end of TAB42 corrupted at any time. THE FINAL EQUATES AND CALCULATIONS The final equates and calculations CODEND, CODSIZ, DESTIN, and O all pertain to the relocation of DEBUG and the offset applied to jumps and calls in its relocated position. The program can be modified anywhere between CODBEG: and CODEND without the need for special attention to relocation or offsets. VOKING DEBUG DEBUG is invoked by inserting the one-byte instruction RST 38H into the program being tested where you want DEBUG to become ac- tive. Once you are in DEBUG, you can set and clear breakpoints as you wish without affecting the program addresses. If you think you will want to change the place where you initiate DEBUG, place NOPs in your code where you want the other calls to be. You can then exchange any NOP for an RST 38H without affecting subsequent addresses. ond byte is any of TAB22:, then the instruction is two bytes long. TAB31: contains one-byte indicators of three-byte instructions; that is, if the byte is any of TAB31:, then the instruction is three bytes long. TAB32: contains the second byte of two-byte indicators of three- byte instructions. If the first instruction byte is DD, ED, or FD, and the second byte is any of TAB32:, then the instruction is three bytes long. TAB42: contains the second byte of two-byte indicators of four- byte incatable, with its highest relocated address determined by the equate REL- TOP. If you want to change the location of DEBUG in memory, set RELTOP as you wish and reassemble. If you are making your first excursions into using a debugger for assembly programs, you should be aware that while a debugger is a powerful tool for program testing, it also has the potential to cause disaster. With DEBUG, you can absolutely cream your pro- gram, DEBUG itself, and the operating system. If you are at all un   THE DISPLAY The upper portion of the display shows the contents of the Z80 registers as indicated. The hex numbers to the right of the register contents (to the right of the ->) are the contents of the memory address in the register and the contents of the fif- teen next higher addresses. To the right of the hex display are the ASCII representations of the hex values. Hex values below 20 (space) and above 7E (tilde) are shown as periods. The eight characters to the right of the AF an REGULAR COMMANDS Regular commands require the entry of some kind of data, and are not acted upon until the carriage return is pressed. A command being entered can be cancelled at any time by using the immediate command Q. All addresses or byte values must be specified in hexadecimal, and all hex digits must be entered. Spaces between elements in regular commands may be used optionally. The backspace key may also be used. Command entry error checking is made as follows: nd being entered, in always active. All other immediate commands are active only when no other command is in progress. Q Cancel (Quit) command being entered Cancels the command being entered, blanks the command line, and returns the Command? prompt. Immediate command Q is always active. + Page memory display forward Moves the page of memory on display forward 128 bytes. (The +/= key invokes the command; it's not necessary to shift up for the +.) - Page memory display backward roof, its intent is to try to be reasonably sure that typographic errors won't lead to disaster. B AAAA Set breakpoint at address AAAA. The breakpoint is set in the us- er program code and the address is shown on the breakpoint dis- play. A breakpoint can be set only if there is space for it (0000 in the breakpoint display); a maximum of six breakpoints are allowed. Trying to set a breakpoint when there is no space will result in a keyboard beep and the message BREAKPOINTS FULL! Breakpoind AF' registers show the status of the flags in the F(lag) register. A dash in- dicates a 0 in the F register (no flag set); a letter indicates a 1 (flag set), as follows: S (bit 7) Sign Z (bit 6) Zero x (bit 5) unused H (bit 4) Half-carry x (bit 3) unused P (bit 2) Parity/Overflow N (bit 1) Negative C (bit 0) Carry (See a Z80 reference book for a full explanation of how the flags are used and how to interpret their st Commands are checked for a maximum length of 32 bytes. If the maximum length is exceeded, the keyboard beeps and the character is ignored. The first letter of any command is tested to be sure it is a val- id command. All characters are tested to be sure they are valid in the com- mand string: hex numbers, register indentifiers, +/- arithmetic operators, and the terminating period. If an incorrect character is entered, the keyboard beeps and the character is ignored. Command execution  Moves the page of memory on display back 128 bytes. G Continue from breakpoint (Go) Continues user program execution beginning at the PC on display. O Single-step Executes the instruction located at the PC on display, then re- turns to DEBUG. All CALLs are executed in full. Single-step preempts use of the first breakpoint address. Z Clear (Zero) all breakpoints Clears all set breakpoints. An available breakpoint is indicated by 0000 in the breakpoint display. ts remain set until specifically cancelled by the F or Z commands, or until DEBUG is reloaded. D AAAA Display memory at address AAAA. The page of memory on display is updated to show AAAA. Note that AAAA is placed near the middle of the memory page display so that memory contents below as well as above AAAA are shown. F AAAA Clear (Fix) the breakpoint at AAAA. The breakpoint at AAAA is repaired and the breakpoint display is updated to show an avail- able (0000) breakpoint. Trying tatus.) The lower portion of the display shows the contents of one 128- byte page of memory. The line labeled "Breakpoints:" below the memory display shows the addresses of breakpoints that are set. 0000 means no break- point is set. The last line of the display is used for command entry and error messages. IMMEDIATE COMMANDS Immediate commands are single-character commands that are acted upon as soon as their key is pressed. Immediate command Q, can- cel commaerror checking is made as follows: Each command is tested to be sure that the correct number of characters is present (so that you don't miss a byte and inadver- tently enter an unreasonable address). Command execution errors are indicated by a keyboard beep and the message "BAD COMMAND!" to the right of the command. The error message is held for about three seconds, then the command line is blanked and the "Command?" prompt is returned. While this level of error checking is by no means foolp  o clear a nonexistent breakpoint will result in a BAD COMMAND message. H BBBB +/- BBBB Perform hexadecimal addition or subtraction. The arithmetic takes place as indicated, and the answer remains on display until any key is pressed. J AAAA Jump to address AAAA. User program execution is recommenced at address AAAA. L SSSS EEEE BB Fill (Load) memory with byte BB starting at address SSSS and end- ing at address EEEE. Display does not change. M AAAA BB Modify the contento period will result in a BAD COMMAND! message). When the string is found, the memory page on display is changed to show the find address. If the string is not found, the key- board will beep and the message STRING NOT FOUND will be dis- played in the error message location. S. Recommence search starting at the address immediately following the last find and continuing on to address EEEE. (S. cannot be used if any other command has been issued following the initia- tion of a search.) T SB!F4JaLqRʗTS-H߻*+(B ";-0B"; 7ͷK-yw0B";>26)7ͷ)7#|¾}¾ ͬa!~.s*;-!2$^#~( :$<2$#B7ͷr+sG 5*Vz s *  #~(+zv*  #~(+zv* * N qv qv Av Av Qv Qv yv yv ]8 Av]0 Av]( qv] qv (s of address AAAA to byte BB. After the ini- tial command entry, the memory page display is changed to show AAAA (as with the command D). The entry prompt then changes to Modify: AAAA where AAAA is the next address; you enter only the new byte val- ue. The memory page display is updated as each byte is entered. This entry loop continues until the Modify command is terminated by entering a period in place of BB. R RP BBBB Replace the contents of register pair RP with bytes BBBB. s1 !! ! 6#lͷͷͷ!!|¾}¾ͷ!|¾}¾ͷ!86!!"9{AMYX DEBUG Version 1.0$Test version for Micro C 3/6/85$Loaded and relocated to $H - $H.$8AF : $BC : $DE : $HL : $AF': $BC': $DE': $HL': $IX : $IY : $SP : $PC : $Command? $BAD COMMAND!$Breakpoints: $BREAKPOINTS FULL!$STRING NOT FOUND.$Modify: $ $IHLSPXY=> $for Micro C 3/6/85$Loaded and reed to $H - $H.$8C : $DE : $s1*N#FCs {SSS EEEE DDDD Move (Transfer) the memory contents starting at address SSSS and ending at address EEEE to destination DDDD. The display does not change. ### #~0O /OB#Ô#^z!-((( {!E; z!((( {! z(( {! [#^#V!s#r2$G8!2$^#~(V͇:$<2$#!~ 5-_:$ 26+6͇ 5-S8S%S'K-*'[%y~F ~  !# =!:=2#-y S S s *  s#rͬñ2S%" [%R2#* -*%~. * K) "%DS%S'*~.(_:The register pairs are specified as follows: Regular registers: AF BC DE HL IX IY SP PC Prime registers: A' B' D' H' The display is rewritten upon entry of new register pair contents. S SSSS EEEE BBBBBBBB. Search memory for string BBBBBBBB starting at address SSSS and ending at address EEEE. The length of the search string is lim- ited by the length of the command buffer (32 characters), so its maximum length is 11 bytes. The search string must be terminated by a period (ns{ ͬBͬ6ͬ!2$^#VzG#:$<2$[_ :$O! ~Sͬ$ͷs * n*ͷ޼0ͷ6ͷ<ͷBͷHͷ NͷTͷZͷ&`ͷ/fͷ5*; 7lͷ2!͜QO:7yӶ+ʆ= >+Æ-ʆG8OmZ27D-M-B-F-J-L-R-T-S-H-"00,.(Q-(M+(I'(E h ? ,:촷(&=2+h:0#A0G0!۴( ͬhO:2&-C  ͷ|rary. * ; * May not be reproduced or distributed * ; * for profit. * ; * * ; ********************************************* ; ;**************************************************************************** ; ; TEST VERSION FOR MICRO C USE ONLY 3/6/85. ; ----------NOT FOR DISTRIBUTION---------- ; ;**************************************************************************** ; ; ORG 100H ; ;-------------------------------------------------------------------------UMP LD (HL),0C3H ; CODE AT 38H LD HL,DESTIN ; PUT DEBUG ADDRESS LD (39H),HL ; IN CP/M JUMP TO DDT LOCATION CALL NEWLIN+O ; DROP A LINE LD SP,(SYSSP) ; GET SYSTEM SP BACK RET ; RELOCATED; RETURN TO CCP ; WITHOUT WARM BOOT ; ; RELTOP: EQU 0C000H ; TOP OF RELOCATED CODE ; MOVED: DEFM 'AMYX DEBUG Version 1.0$' XMES: DEFM 'Test version for Micro C 3/6/85$' INVOK0: DEFM 'Loaded and relocated to $' INVOK1: DEFM 'H - $' INVOK2: DEFM 'H.$' ; ;-----------------------------------------¾}¾͋:=2K}͋|¾ ͬ}¾-ͬ>ͬ~¾ ͬ# y  ͬ  ͬB~ 80._ͬ# y  ͬ߷ ͷ!#~¾+~¾ ͬ##W~ ''''G~ 澀_O:=2y#:촷B7vͷ:$O! (w6!2$~ #~#:$<2$#R0:$O! ~`8 Ø ØG????׾x׾ 007_ͬ:007 ͬ ͬͬ=ͬXͬYͬͬ!+| ɇhiko &(.068> #)+9@ABDEFGHIJMPQ--- ; RELOCATION AND INITIALIZATION ROUTINE ;---------------------------------------------------------------------------- ; LD (SYSSP),SP ; SAVE SYSTEM SP LD SP,LOCSP ; USE LOCAL STACK HERE LD HL,CODBEG ; SOURCE LD DE,DESTIN ; DESTINATION LD BC,CODSIZ ; BYTE COUNT LDIR ; WHOOSH! LD HL,BRKTBL+O ; ZERO ALL BREAKPOINTS LD B,12 ; 12 BYTES FOR 6 ADDRESSES BRKZER: LD (HL),0 INC HL DJNZ BRKZER CALL NEWLIN+O ; DROP CURSOR A LINE LD DE,MOVED ; POINT TO MOVED MESSAGE CALL MSGOUT+O  TITLE 'AMYX DEBUG 3/6/85 1346' ; ; ********************************************* ; * * ; * AMYX DEBUG * ; * * ; * An assembly language debugger for Z80 * ; * CP/M. Written by * ; * * ; * Richard Amyx * ; * 994 North Second Street * ; * San Jose, CA 95112 * ; * (408) 287-8572 * ; * * ; * Given to Micro Cornucopia for inclu- * ; * sion in its public domain lib----------------------------------- ; BEGIN ACTUAL DEBUG CODE ;---------------------------------------------------------------------------- ; CODBEG: JP ENTRY+O ; BEGINNING OF CODE TO RELOCATED ; ; ; BIOS EQUATES ; WBOOT EQU 0 BDOS EQU 5 CONIN EQU 1 ; CHARACTER IN FROM KEYBOARD CONOUT EQU 2 ; SINGLE CHARACTER TO CONSOLE PSTR EQU 9 ; PRINT STRING ON CONSOLE LININ EQU 10 ; READ CONSOLE BUFFER ; ; ; CHARACTERS ; BELL EQU 7 BS EQU 8 LF EQU 10 CR EQU 13 SPACE EQU 20H CLRSCR EQRVWXYZ^`abghijorxyz!"*12:45FNV^fnpqrstuw~!"*6CKS[s{ØG????׾x׾ 007_ͬ:007 ͬ ͬ; PRINT IT CALL NEWLIN+O ; DROP CURSOR A LINE LD DE,XMES CALL MSGOUT+O CALL NEWLIN+O LD DE,INVOK0 ; PRINT FIRST PART OF INVOKE CALL MSGOUT+O ; MESSAGE LD HL,DESTIN ; PRINT START ADDRESS LD A,H CALL HX2ASC+O LD A,L CALL HX2ASC+O LD DE,INVOK1 ; PRINT SECOND PART OF INVOKE MESSAGE CALL MSGOUT+O LD HL,RELTOP ; TOP OF RELOCATED CODE LD A,H ; PRINT ADDRESS CALL HX2ASC+O LD A,L CALL HX2ASC+O LD DE,INVOK2 ; PRINT LAST PART OF INVOKE MESSAGE CALL MSGOUT+O LD HL,38H ; PUT J  U 26 ; KAYPRO ^Z CLEAR SCREEN CHAR HOME: EQU 30 ; KAYPRO HOME CURSOR CHAR ; ; ; KAYPRO CURSOR POSITIONS ; COMCUR: EQU 3720H ; COMMAND LINE HEX CURSOR POSITION ERRCUR: EQU 3742H ; ERROR MESSAGE HEX CURSOR POSITION BRKCUR: EQU 3520H ; BREAKPOINT LINE CURSOR POSITION MODCUR: EQU 3729H ; MEMORY MODIFY INPUT CURSOR MEMCUR: EQU 2D26H ; MEMORY DISPLAY CURSOR POS ; ; ; MESSAGES ; MAF: DEFM 'AF : $' MBC: DEFM 'BC : $' MDE: DEFM 'DE : $' MHL: DEFM 'HL : $' MAFP: DEFM 'AF'': $' MBCP: DEFE OF USER PC LD B,(HL) ; PC HIGH TO B LD (SYSPC+O),BC ; SAVE USER PC HERE LD (TMPWRD+O),SP ; PRESERVE CURRENT LOCAL SP LD SP,(SYSSP+O) ; DISCARD DEBUG-CAUSED USER POP HL ; PC FROM BOTTOM OF SYSTEM STACK LD (SYSSP+O),SP ; SAVE ADJUSTED SP LD SP,(TMPWRD+O) ; RESTORE CURRENT LOCAL SP LD E,27 ; SAVE CALL CHROUT+O ; USER LD E,'B' ; CURSOR CALL CHROUT+O ; POSITION LD E,'6' CALL CHROUT+O ; ; ; CHECK FOR BREAKPOINTS AND INSTALL IF SET ; LD HL,BRKTBL+O ; POINT TO BREAKPOIFW 0 ; ALL-PURPOSE END ADDRESS BYTCNT: DEFW 0 ; ALL-PURPOSE BYTE COUNT SCHSTR: DEFS 11 ; 11-BYTE SEARCH STRING BUFFER MODFLG: DEFB 0 ; MODIFY MEMORY FLAG COMFLG: DEFB 0 ; NZ = COMMAND IN PROGRESS ; ;---------------------------------------------------------------------------- ; START MAIN PROGRAM ;---------------------------------------------------------------------------- ; ENTRY: LD (SYSSP+O),SP ; SAVE CALLING PROGRAM SP LD SP,LOCSP+O ; SET UP THE LOCAL STACK PUSH AF ; NOW SAVE EVERYT LD (DE),A ; THEN TO PROGRAM ADDRESS LD (SYSPC+O),DE ; SAVE THE ADJUSTED PC ; ; ; BEGIN OPENING SCREEN DISPLAY ; ENTRY0: LD E,CLRSCR ; CLEAR SCREEN CALL CHROUT+O ENTRY1: LD DE,MAF+O ; POINT TO AF MESSAGE CALL MSGOUT+O ; SEND IT LD (TMPWRD+O),SP ; PUT LOCAL SP IN IX--NOTHING SHOULD BE LD IX,(TMPWRD+O) ; PUSHED OR CALLED AT THIS POINT. CALL DISPAF+O ; DISPLAY AF HEX VALUES AND ; FLAG INDICATIONS CALL NEWLIN+O ; MOVE CURSOR TO NEXT LINE LD DE,MBC+O ; POINT TO BC MESSAGE CALL MM 'BC'': $' MDEP: DEFM 'DE'': $' MHLP: DEFM 'HL'': $' MIX: DEFM 'IX : $' MIY: DEFM 'IY : $' MSP: DEFM 'SP : $' MPC: DEFM 'PC : $' MCOM: DEFM 'Command? $' BADCOM: DEFM 'BAD COMMAND!$' BRKMSG: DEFM 'Breakpoints: $' BRKFUL: DEFM 'BREAKPOINTS FULL!$' NOTFND: DEFM 'STRING NOT FOUND.$' MODMSG: DEFM 'Modify: $' BLANK: DEFM ' $' ;26 SPACES COMCHR: DEFM 'IHLSPXY' ; OK COM BUFF CHARS PTR: DEFM '=> $' ; ; ; VARIABLES ; SYSSP: DEFW 0 ; SYSTEM STACK POINTER SYSPC: DEFNT TABLE LD B,6 ; NUMBER OF BREAKPOINTS TO CHECK XOR A ; ZERO THE LD (BRKCNT+O),A ; BREAKPOINT COUNTER CHKBR1: LD E,(HL) ; LOW BKPT BYTE IN E INC HL ; POINT TO HIGH BYTE LD D,(HL) ; HIGH BKPT BYTE IN D LD A,D ; SEE IF CONTENTS OF TABLE ENTRY OR E ; IS ZERO CALL NZ,SETBRK+O ; IF NOT, INSTALL IT INC HL ; POINT TO NEXT LOW BYTE LD A,(BRKCNT+O) ; BUMP BREAKPOINT INC A ; COUNTER LD (BRKCNT+O),A DJNZ CHKBR1 ; GO UNTIL SIX CHECKED ; ; ; FIX BREAKPOINT ON ENTRY FOR CORRECTHING IN LOCAL STACK. PUSH BC ; AF = LOCSP-17 & 18; BC = LOCSP-19 & 20 PUSH DE ; DE = LOCSP-15 & 16 { HIGH BYTE IS AT } PUSH HL ; HL = LOCSP-13 & 14 { LOWER ADDRESS } EX AF,AF' ; EXCHANGE REGISTERS EXX PUSH AF ; AF' = LOCSP-11 & 12 PUSH BC ; BC' = LOCSP-9 & 10 PUSH DE ; DE' = LOCSP-7 & 8 PUSH HL ; HL' = LOCSP-5 & 6 PUSH IX ; IX = LOCSP-3 & 4 PUSH IY ; IY = LOCSP-1 & 2 LD HL,(SYSSP+O) ; SYSTEM SP TO HL LD C,(HL) ; PC LOW AT SP TO C INC HL ; SP+1 IS HIGH BYTSGOUT+O ; SEND IT CALL DISPBC+O ; DISPLAY HEX VALUES, INDIRECTS, ; AND ASCII REPRESENTATIONS. CALL NEWLIN+O ; DROP CURSOR FOR NEXT LINE LD DE,MDE+O ; POINT TO DE MESSAGE CALL MSGOUT+O ; SEND IT CALL DISPDE+O ; DISPLAY DE VALUES CALL NEWLIN+O ; CURSOR TO NEXT LINE LD DE,MHL+O ; POINT TO HL MESSAGE CALL MSGOUT+O ; SEND IT CALL DISPHL+O ; DISPLAY HL VALUES CALL NEWLIN+O ; CURSOR TO NEXT LINE LD DE,MAFP+O ; POINT TO AF' MESSAGE CALL MSGOUT+O ; SEND IT CALL DISAFP+O ; DISPLAY AF' W 0 ; SYSTEM PROGRAM COUNTER MEMSEL: DEFW 0100H ; DEFAULT MEM DISPLAY ADDRESS CBUFCT: DEFB 0 ; COMMAND BUFFER COUNTER COMBUF: DEFS 32 ; 32-CHARACTER COMMAND BUFFER TMPWRD: DEFW 0 ; TEMPORARY STORAGE AS NEEDED LINECT: DEFB 8 ; LINE COUNTER FOR DISMEM CURSOR: DEFW 0 ; CURSOR SAVE FOR DISMEM BRKTBL: DEFS 12 ; SPACE FOR 6 BREAKPOINT ADDRESSES CODSAV: DEFS 6 ; THE CODE REPLACED BY BREAKPOINTS BRKCNT: DEFB 0 ; COUNTER FOR BREAKPOINT TABLES START: DEFW 0 ; ALL-PURPOSE START ADDRESS ENDADD: DE PC & SP DISPLAYS, ; AND FOR CORRECT EXECUTION ON GO. FIRST, CHECK TO SEE ; WHETHER ENTRY TO DEBUG CAME FROM A BREAKPOINT OR A ; CODED RST 38H. ; LD DE,(SYSPC+O) ; GET THE SYSTEM PC DEC DE ; ONE LESS IF FROM BKPT CALL MATCH+O ; SEE IF IT'S A BREAKPOINT JR NZ,ENTRY0 ; IT ISN'T, SO GO TO DISPLAY LD B,0 ; IT IS--FIRST FIX CODE LD A,(BRKCNT+O) ; PUT BREAKPOINT COUNTER LD C,A ; IN BC LD HL,CODSAV+O ; POINT TO SAVED CODE TABLE ADD HL,BC ; ADD OFFSET LD A,(HL) ; SAVED CODE TO A  VALUES CALL NEWLIN+O ; CURSOR TO NEXT LINE LD DE,MBCP+O ; POINT TO BC' MESSAGE CALL MSGOUT+O ; SEND IT CALL DISBCP+O ; DISPLAY BC' VALUES CALL NEWLIN+O ; CURSOR TO NEXT LINE LD DE,MDEP+O ; POINT TO DE' MESSAGE CALL MSGOUT+O ; SEND IT CALL DISDEP+O ; DISPLAY DE' VALUES CALL NEWLIN+O ; CURSOR TO NEXT LINE LD DE,MHLP+O ; POINT TO HL' MESSAGE CALL MSGOUT+O ; SEND IT CALL DISHLP+O ; DISPLAY HL' VALUES CALL NEWLIN+O ; CURSOR TO NEXT LINE LD DE,MIX+O ; POINT TO IX MESSAGE CALL MSGOUT+ ; M OK; ADD TO BUFFER CP 'B' ; SET BREAKPOINT? JP Z,AD2BUF+O ; B OK; ADD TO BUFFER CP 'F' ; FIX (CLEAR) ONE BKPT? JP Z,AD2BUF+O ; F OK; ADD TO BUFFER CP 'J' ; JUMP TO ADDRESS? JP Z,AD2BUF+O ; J OK; ADD TO BUFFER CP 'L' ; FILL MEMORY? JP Z,AD2BUF+O ; L OK; ADD TO BUFFER CP 'R' ; MODIFY REGISTER? JP Z,AD2BUF+O ; R OK; ADD TO BUFFER CP 'T' ; BLOCK MOVE? JP Z,AD2BUF+O ; T OK; ADD TO BUFFER CP 'S' ; SEARCH STRING? JP Z,AD2BUF+O ; S OK; ADD TO BUFFER CP 'H' ; HEX ARITHMETIC? ; ; ; CHECK FOR IMMEDIATE COMMANDS ; CP 'Q' ; CANCEL COMMAND? (ALWAYS ACTIVE) JP Z,COMRET+O ; YES, WIPE LINE & START OVER LD C,A ; SAVE CHAR IN C LD A,(COMFLG+O) ; IS A COMMAND IN PROGRESS? OR A ; NZ = YES LD A,C ; GET CHAR BACK JP NZ,CKCHAR+O ; YES, SKIP TO CHAR CHECK CP '+' ; PAGE MEMORY FORWARD? JP Z,MPAGE+O ; YES, GO TO MEMORY PAGE CP '=' ; TREAT SAME AS + JR NZ,CFM ; NOT '='--CHECK FOR - LD A,'+' ; MAKE = A + FOR 2ND COMPARE JP MPAGE+O ; AND GO TO PAGE MEM JP KEYIN1+O ; GO FOR NEW CHARACTER CK1: CP 58 ; 0 TO 9? JR NC,CK2 ; ABOVE 9, KEEP GOING JR AD2BUF ; BETWEEK 0 & 9, ADD TO BUFFER CK2: CP 'A' ; BETWEEN 9 AND A? JR NC,CK3 ; ABOVE A, KEEP GOING JR BADCHR ; BETWEEN 9 & A, NO GOOD CK3: CP 'G' ; ABOVE F? JR NC,CK4 ; YES, ONE MORE CHECK JR AD2BUF ; NO, PUT IT IN BUFFER CK4: PUSH HL ; SAVE BUFFER TABLE LOCATION LD HL,COMCHR+O ; POINT TO CHARACTER TABLE LD BC,7 ; 7 CHARS TO CHECK CPIR ; IHLSPXY? POP HL ; BUFFER TABLE LOCATION O ; SEND IT CALL DISPIX+O ; DISPLAY IX VALUES CALL NEWLIN+O ; CURSOR TO NEXT LINE LD DE,MIY+O ; POINT TO IY MESSAGE CALL MSGOUT+O ; SEND IT CALL DISPIY+O ; DISPLAY IY VALUES CALL NEWLIN+O ; CURSOR TO NEXT LINE LD DE,MSP+O ; POINT TO SP MESSAGE CALL MSGOUT+O ; SEND IT CALL DISPSP+O ; DISPLAY SP VALUES CALL NEWLIN+O ; CURSOR TO NEXT LINE LD DE,MPC+O ; POINT TO PC MESSAGE CALL MSGOUT+O ; SEND IT CALL DISPPC+O ; DISPLAY PC CONTENTS LD HL,(MEMSEL+O) ; GET ADDRESS OF MEMORY FOR DISPLAY JP Z,AD2BUF+O ; H OK; ADD TO BUFFER JP BADCHR+O ; BEEP AND IGNORE ; ; ; CHECK CHARACTERS BEFORE ADDING TO BUFFER ; MUST BE HEX DIGIT, REGISTER IDENTIFIER, ; PERIOD, BS, OR CR. SPACE OK BUT NOT ADDED ; TO BUFFER. ; CKCHAR: OR A ; CLEAR CARRY FLAG CP '0' ; BELOW 0? JR NC,CK1 ; 0 OR ABOVE, KEEP GOING CP '.' ; IS IT A PERIOD JR Z,AD2BUF ; YES, ADD IT TO BUFFER CP '-' ; IS IT MINUS SIGN? JR Z,AD2BUF ; YES, ADD IT TO BUFFER CP '+' ; IS IT PLUS SIGN? JR Z,AD2BUF ; YES, ACFM: CP '-' ; PAGE MEMORY BACKWARD? JP Z,MPAGE+O ; YES, GO TO MEMORY PAGE CP 'G' ; CONTINUE FROM BREAKPOINT? JP Z,DBEND+O ; YES, GO TO END CP 'O' ; SINGLE-STEP? JP Z,SSTEP+O ; SIMON SAYS TAKE ONE STEP CP 'Z' ; CLEAR ALL BREAKPOINTS? JP Z,CLRBRK+O ; YES, GO DO IT. ; ; ; NOT AN IMMEDIATE COMMAND--PUT CHAR IN COMMAND BUFFER IF OK ; LD (COMFLG+O),A ; INDICATES COMMAND IN PROCESS CP 'D' ; DISPLAY MEMORY? JP Z,AD2BUF+O ; D OK; ADD TO BUFFER CP 'M' ; MODIFY MEMORY? JP Z,AD2BUF+OBACK JR Z,AD2BUF ; YES, ADD TO BUFFER BADCHR: CALL BEEP+O ; BEEP KEYBOARD LD E,BS ; PRINT A CALL CHROUT+O ; BACKSPACE JP KEYIN1+O ; THEN GO FOR NEW CHAR ; ; ; COMMAND OR CHARACTER OK--ADD IT TO COMMAND BUFFER ; AD2BUF: LD C,A ; SAVE CHAR IN C LD A,(CBUFCT+O) ; GET BUFFER COUNT INC A ; BUMP COUNT CP 33 ; BUFFER FULL? JR NC,BADCHR ; YES, BEEP & IGNORE LD (CBUFCT+O),A ; SAVE COUNT LD A,C ; GET CHAR BACK INC HL ; BUMP BUFFER POINTER LD (HL),A ; PUT CHAR IN BUFFER JP K CALL DISMEM+O ; GO DISPLAY 128 BYTES OF MEMORY CALL NEWLIN+O ; CURSOR TO NEXT LINE CALL DISBRK+O ; DISPLAY BREAKPOINTS JP COMRET+O ; AND HEAD FOR COMMAND ENTRY ; ; ; BEGIN COMMAND PROCESSING MODULE ; COMAND: LD BC,COMCUR ; GET HEX COMMAND CURSOR POS CALL POSCUR+O ; POSITION CURSOR LD DE,MCOM+O ; POINT AT "Command?" MESSAGE CALL MSGOUT+O KEYIN: XOR A ; CLEAR A LD (CBUFCT+O),A ; ZERO COUNTER LD HL,COMBUF+O-1 ; SET BUFFER POINTER FOR 1ST CHAR KEYIN1: CALL KBD+O ; GET A CHARACTERDD IT TO BUFFER CP 39 ; IS IT AN APOSTROPHE? JR Z,AD2BUF ; YES, ADD IT TO BUFFER CP 32 ; IS IT SPACE? JP Z,KEYIN1+O ; OK, BUT DON'T PUT IN BUFFER CP 13 ; IS IT CR? JP Z,COMDEC+O ; YES, END ENTRY, DECODE COMMAND CP 8 ; IS IT BACKSPACE? JR NZ,BADCHR ; NO, IT'S A BOO-BOO LD A,(CBUFCT+O) ; GET BUFFER COUNT OR A ; IS THE BUFFER EMPTY? JR Z,BADCHR ; YES, DON'T BACK UP ANY MORE DEC A ; DECREMENT BUFFER COUNTER LD (CBUFCT+O),A ; SAVE NEW VALUE DEC HL ; DECREMENT BUFFER POINTER   EYIN1+O ; GO FOR NEXT CHARACTER ; ; ; COMMAND DECODE ; COMDEC: LD A,(MODFLG+O) ; ARE WE DOING MODIFY MEMORY? OR A ; (1 = YES) JP NZ,MODME2+O ; YES, GO STRAIGHT TO IT LD HL,COMBUF+O ; POINT TO COMMAND BUFFER LD A,(HL) ; GET COMMAND LD E,A ; SAVE IT IN E LD A,(CBUFCT+O) ; GET BUFFER COUNT DEC A ; SUBTRACT ONE CHARACTER LD (CBUFCT+O),A ; SAVE NEW COUNT INC HL ; POINT TO NEXT BUFFER CHARACTER LD A,E ; GET COMMAND BACK CP 'D' ; IS IT DISPLAY? JP Z,DSPLAY+O ; YES, GO DO IT DDR LD (HL),A ; LOAD MODIFIED BYTE LD BC,30H ; ADJUST FOR SBC HL,BC ; CENTER OF DISPLAY LD (MEMSEL+O),HL ; SAVE THE ADDRESS AS SELECTED MEMORY CALL DISMEM+O ; PRINT NEW MEMORY DISPLAY LD A,1 ; SET MODIFY LD (MODFLG+O),A ; MEMORY FLAG MODME1: LD BC,MODCUR ; MODIFY CURSOR POSITION CALL POSCUR+O ; POSITION CURSOR LD DE,BLANK+O ; BLANK LAST CALL MSGOUT+O ; ENTRY LD BC,MODCUR ; PUT CURSOR BACK TO CALL POSCUR+O ; MODIFY POSITION POP HL ; GET LAST MODIFIED ADDR INC HL ; UPDAT2: LD (MEMSEL+O),HL ; PUT NEW ADDR IN MEMSEL CALL DISMEM+O ; GO DISPLAY IT JP COMRET+O ; ; ; DISPLAY SELECTED ADDRESS, WITH THAT ADDRESS IN CENTER ; OF DISPLAY. ; DSPLAY: CALL GETWRD+O ; GET DISPLAY ADDR FROM COM BUF CALL COMCHK+O ; BE SURE COMMAND WAS OK JP NZ,COMRET+O ; GET OUT IF BAD COMMAND EX DE,HL ; PUT ADDRESS IN HL LD BC,30H ; PUT SELECTED ADDR IN SBC HL,BC ; MIDDLE OF DISPLAY LD (MEMSEL+O),HL ; SAVE IT AS CURRENT CALL DISMEM+O ; DISPLAY IT JP COMRET+O ; DONE; GO BD A,(BRKCNT+O) ; NOT ZERO, SO INC A ; BUMP BREAKPOINT COUNTER AND LD (BRKCNT+O),A ; SET UP TABLE POINTER FOR INC HL ; NEXT LOW BYTE, DJNZ BRAKP1 ; THEN GO CHECK IT. CALL BEEP+O ; NO ROOM IF LOOP FINISHES POP DE ; RESTORE STACK LD BC,ERRCUR ; GET ERROR CURSOR POSITION CALL POSCUR+O ; PLACE CURSOR LD DE,BRKFUL+O ; POINT TO BREAKPOINT FULL MSG CALL MSGOUT+O ; PRINT IT CALL DELAY+O ; WAIT THREE SECONDS JP COMRET+O ; AND GO FOR NEXT COMMAND BRAKP2: POP DE ; GET BKPT ADDR BACK LD CP 'M' ; IS IT MODIFY MEMORY? JP Z,MODMEM+O ; YES, GO DO IT CP 'B' ; IS IT SET BREAKPOINT? JP Z,BRAKPT+O ; YES, GO DO IT CP 'F' ; IS IT CLEAR ONE BKPT? JP Z,FIXBRK+O ; YES, GO DO IT CP 'J' ; IS IT JUMP TO ADDRESS? JP Z,JUMP+O ; YES, GO DO IT CP 'L' ; IS IT FILL MEMORY? JP Z,FILL+O ; YES, GO DO IT CP 'R' ; IS IT MODIFY REGISTER? JP Z,REGMOD+O ; YES, GO DO IT CP 'T' ; IS IT BLOCK MOVE JP Z,MOVE+O ; YES, GO DO IT CP 'S' ; IS IT SEARCH STRING? JP Z,SEARCH+O ; YES, GO DO ITE IT PUSH HL ; SAVE IT AGAIN LD A,H ; PRINT CALL HX2ASC+O ; IT LD A,L CALL HX2ASC+O LD E,SPACE ; PRINT A CALL CHROUT+O ; SPACE JP KEYIN+O ; GET NEXT BYTE FROM KBD MODME2: LD HL,COMBUF+O ; POINT TO COMMAND BUFFER LD A,(HL) ; GET FIRST CHARACTER CP '.' ; END OF COMMAND? JP Z,COMRET+O ; YUP CALL GETBYT+O ; DECODE BYTE POP HL ; GET ADDR TO MODIFY LD (HL),E ; CHANGE IT PUSH HL ; SAVE ADDR AGAIN LD HL,(MEMSEL+O) ; GET DISPLAY MEM ADDR CALL DISMEM+O ; UPDATE DISPLAY ACK ; ; ; MODIFY SELECTED BYTE OF MEMORY ; MODMEM: LD BC,COMCUR ; COMMAND LINE CURSOR POS CALL POSCUR+O ; POSITION CURSOR LD DE,MODMSG+O ; POINT TO MODIFY MESSAGE CALL MSGOUT+O ; PRINT IT CALL GETWRD+O ; GET ADDRESS TO MODIFY PUSH DE ; SAVE IT A SEC CALL GETBYT+O ; GET NEW BYTE LD C,E ; SAVE IT IN C POP DE ; GET ADDR BACK CALL COMCHK+O ; BE SURE COMMAND WAS OK JP NZ,COMRET+O ; GET OUT IF NOT LD A,C ; GET BYTE BACK EX DE,HL ; PUT ADDR IN HL PUSH HL ; SAVE UNADJUSTED A (HL),D ; HIGH BKPT BYTE TO TABLE DEC HL ; MOVE TO LOW BYTE ADDR LD (HL),E ; LOW BKPT BYTE TO TABLE CALL SETBRK+O ; INSTALL BKPT AND SAVE CODE LD BC,BRKCUR ; POSITION CURSOR FOR CALL POSCUR+O ; BREAKPOINT DISPLAY CALL DISBRK+O ; WRITE IT JP COMRET+O ; DONE; GO FOR NEW COMMAND. ; ; ; SINGLE-STEP ROUTINE ; FIRST, CHECK FOR JUMP INSTRUCTIONS ; SSTEP: LD HL,(SYSPC+O) ; GET USER PC ADDR LD D,(HL) ; SAVE (PC) IN D LD A,D ; BRING IT TO A CP 0C3H ; JP UNCONDITIONAL? JR NZ,SSTE CP 'H' ; IS IT HEX ARITHMETIC? JP Z,HARITH+O ; YES, GO DO IT ; ;---------------------------------------------------------------------------- ; COMMAND ACTION ;---------------------------------------------------------------------------- ; ; PAGE MEMORY FORWARD OR BACK ; MPAGE: LD HL,(MEMSEL+O) ; CURRENTLY SELECTED MEMORY TO HL LD BC,80H ; ONE PAGE TO BC CP '+' ; IS IT FORWARD? JR Z,MPAGE1 ; YES, ADD TO ADDRESS SBC HL,BC ; IF NO, MUST BE BACK JR MPAGE2 MPAGE1: ADD HL,BC MPAGEJR MODME1 ; GO FOR NEXT BYTE ; ; ; SET BREAKPOINT FROM KEYBOARD ENTRY ; BRAKPT: CALL GETWRD+O ; GET BREAKPOINT ADDRESS CALL COMCHK+O ; MAKE SURE COMMAND WAS OK JP NZ,COMRET+O ; GET OUT IF NOT LD HL,BRKTBL+O ; SEE IF THERE'S A ZERO ENTRY LD B,6 ; WE CAN USE IN THE XOR A ; BREAKPOINT TABLE LD (BRKCNT+O),A PUSH DE ; SAVE THE BKPT ADDR BRAKP1: LD E,(HL) ; GET A LOW BYTE INC HL ; POINT TO HIGH BYTE LD A,(HL) ; PUT IT IN A OR E ; IS IT EMPTY? JR Z,BRAKP2 ; YES, GO FILL IT L  01 ; NO, GO ON JP SETSS2+O ; SET BKPT AND GO SSTE01: LD (TMPWRD+O),SP ; GET SP--NOTHING ON STACK CP 0E9H ; JP (HL)? (DISP=12) JR NZ,SSTE02 ; NO, GO ON LD HL,(TMPWRD+O) ; SP TO HL LD BC,12 ; HL DISPLACEMENT ADD HL,BC ; HERE'S USER HL JP SETSS3+O ; SET BKPT AND GO SSTE02: CP 0DDH ; JP (IX)? (DISP=2) JR NZ,SSTE03 ; NO, GO ON INC HL ; NEXT USER PROGRAM ADDR LD A,(HL) ; GET THE BYTE CP 0E9H ; ONE MORE TEST JR Z,SSTE18 ; YES, JP (IX). DEC HL ; NOT A JUMP--RETURN HL LD A,D ; ; C FLAG SET? JP NZ,SSTEP0+O ; YES, NO JUMP JP SETJR+O ; GET ADDR FOR JR SSTE15: CP 28H ; JR Z? JR NZ,SSTE16 ; NO, GO ON BIT 6,C ; Z FLAG SET? JP Z,SSTEP0+O ; NO, NO JUMP JP SETJR+O ; GET ADDR FOR JR SSTE16: CP 20H ; JR NZ? JR NZ,SSTE17 ; NO, GO ON BIT 6,C ; Z FLAG SET? JP NZ,SSTEP0+O ; YES, NO JUMP JR SETJR ; GET ADDR FOR JR SSTE17: CP 10H ; DJNZ? JR NZ,SSTEP0 ; NO JUMP OR B ; B=0? JR Z,SSTEP0 ; YES, NO JUMP SETJR: PUSH BC ; IN CASE OF DJNZ LD B,0 ; ZERO B INC 2+O ; SET BKPT AND GO SSTE07: CP 0DAH ; JP C? JR NZ,SSTE08 ; NO, GO ON BIT 0,C ; C FLAG SET JP Z,SSTEP0+O ; NO, NO JUMP JP SETSS2+O ; SET BKPT AND GO SSTE08: CP 0E2H ; JP PO? JR NZ,SSTE09 ; NO, GO ON BIT 2,C ; P FLAG SET JP NZ,SSTEP0+O ; YES, NO JUMP JP SETSS2+O ; SET BKPT AND GO SSTE09: CP 0EAH ; JP PE? JR NZ,SSTE10 ; NO, GO ON BIT 2,C ; P FLAG SET? JP Z,SSTEP0+O ; NO, NO JUMP JP SETSS2+O ; SET BKPT AND GO SSTE10: CP 0F2H ; JP P? JR NZ,SSTE11 ; NO, GO ON BIT 7,C ; S NT TO 2ND 2-BYTE TABLE LD BC,LENT22 ; NO. BYTES TO CHECK CPIR ; SEE IF (PC+1) IS 2-BYTE INST JR NZ,SSTEP1 ; NOT 2 BYTES; TRY 3 ENDTWO: LD B,2 ; IT'S A 2-BYTE INST JP SETSS+O ; GO SET IT UP SSTEP1: LD A,D ; GET (PC) BACK LD HL,TAB31+O ; POINT TO 1ST 3-BYTE TABLE LD BC,LENT31 ; NO. BYTES TO CHECK CPIR ; SEE IF (PC) IS 3-BYTE INST JR Z,ENDTHR ; YES, A 3-BYTE INST CP 0DDH ; NOT SURE YET--IS IT DD? JR Z,CK32 ; IT'S DD--TEST NEXT BYTE CP 0EDH ; NOT SURE YET--IS IT ED? JR Z,CK32 GET BYTE BACK IN A JP SSTEP0+O ; GO TO REGULAR S-S SSTE18: LD HL,(TMPWRD+O) ; SP TO HL LD BC,2 ; IX DISPLACEMENT ADD HL,BC ; HERE'S USER IX JP SETSS3+O ; SET BKPT AND GO SSTE03: CP 0FDH ; JP (IY)? (DISP=0) JR NZ,SSTE04 ; NO, GO ON INC HL ; NEXT USER PROGRAM ADDR LD A,(HL) ; GET THE BYTE CP 0E9H ; ONE MORE TEST JR Z,SSTE19 ; YES, JP (IY). DEC HL ; NO JUMP--RETURN HL LD A,D ; GET BYTE BACK IN A JP SSTEP0+O ; GO TO REGULAR S-S SSTE19: LD HL,(TMPWRD+O) ; SP TO HL--IT IS USER HL ; NEXT INSTRUCTION ADDR LD A,(HL) ; REL JUMP TO A CP 82H ; NEGATIVE JUMP? JR NC,JRNEG ; YES, NEGATIVE LD C,A ; POSITIVE JUMP ADD HL,BC ; JUMP ADDR-1 NOW IN HL JR SETJR1 JRNEG: CPL ; MAKE RELATIVE ADD A,1 ; JUMP POSITIVE LD C,A ; POSITIVE AMOUNT TO C SBC HL,BC ; THEN SUBTRACT SETJR1: INC HL ; ADJ FOR -2 IN JR POP BC ; GET BC BACK EX DE,HL ; PUT JUMP ADDR IN DE JP SETSS4 ; SET BKPT AND GO ; ; NO JUMPS, SO FIND OUT HOW LONG THE INSTRUCTION IS ; SSTEP0: INC HL FLAG SET? JP NZ,SSTEP0+O ; YES, NO JUMP JP SETSS2+O ; SET BKPT AND GO SSTE11: CP 0FAH ; JP M? JR NZ,SSTE12 ; NO, GO ON BIT 7,C ; S FLAG SET? JP Z,SSTEP0+O ; NO, NO JUMP JP SETSS2+O ; SET BKPT AND GO SSTE12: CP 18H ; JR UNCONDITIONAL? JR NZ,SSTE13 ; NO, GO ON JP SETJR+O ; GET ADDR FOR JR SSTE13: CP 38H ; JR C? JR NZ,SSTE14 ; NO, GO ON BIT 0,C ; C FLAG SET? JP Z,SSTEP0+O ; NO, NO JUMP JP SETJR+O ; GET ADDR FOR JR SSTE14: CP 30H ; JR NC? JR NZ,SSTE15 ; NO, GO ON BIT 0,C  ; IT'S ED--TEST NEXT BYTE CP 0FDH ; NOT SURE YET--IS IT FD? JR NZ,SSTEP2 ; NOT FD--TRY 4-BYTE INST CK32: LD A,E ; 2ND INST BYTE TO A LD HL,TAB32+O ; POINT TO 2ND 3-BYTE TABLE LD BC,LENT32 ; NO. BYTES TO CHECK CPIR ; SEE IF (PC+1) IS 3-BYTE INST JR NZ,SSTEP2 ; NOT 3 BYTES; TRY 4 ENDTHR: LD B,3 ; IT'S A 3-BYTE INST JP SETSS+O ; GO SET IT UP SSTEP2: LD A,D ; GET (PC) BACK CP 0DDH ; LOOKING FOR 4-BYTE INST JR Z,CK42 ; IT'S DD--TEST NEXT BYTE CP 0EDH ; IS IT ED? JR Z,CK42 ; IY JP SETSS3+O ; SET BKPT AND GO SSTE04: LD IX,(TMPWRD+O) ; SP TO IX--SET UP FOR JR LD C,(IX+18) ; FLAGS TO C CP 0C2H ; JP NZ? JR NZ,SSTE05 ; NO, GO ON BIT 6,C ; Z FLAG SET? JP NZ,SSTEP0+O ; YES, NO JUMP JP SETSS2+O ; SET BKPT AND GO SSTE05: CP 0CAH ; JP Z? JR NZ,SSTE06 ; NO, GO ON BIT 6,C ; Z FLAG SET? JP Z,SSTEP0+O ; NO, NO JUMP JP SETSS2+O ; SET BKPT AND GO SSTE06: CP 0D2H ; JP NC? JR NZ,SSTE07 ; NO, GO ON BIT 0,C ; C FLAG SET? JP NZ,SSTEP0+O ; YES, NO JUMP JP SETSS; NEXT ADDRESS LD E,(HL) ; SAVE (PC+1) IN E LD A,D ; GET (PC) BACK LD HL,TAB21+O ; POINT TO 1ST 2-BYTE TABLE LD BC,LENT21 ; NO. BYTES TO CHECK CPIR ; SEE IF (PC) IS 2-BYTE INST JR Z,ENDTWO ; YES, A 2-BYTE INST CP 0DDH ; NOT SURE YET--IS IT DD? JR Z,CK22 ; IT'S DD--TEST NEXT BYTE CP 0EDH ; NOT SURE YET--IS IT ED? JR Z,CK22 ; IT'S ED--TEST NEXT BYTE CP 0FDH ; NOT SURE YET-IS IT FD? JR NZ,SSTEP1 ; NOT FD--TRY 3-BYTE INST CK22: LD A,E ; 2ND INST BYTE TO A LD HL,TAB22+O ; POI  IT'S ED--TEST NEXT BYTE CP 0FDH ; IS IT FD? JR NZ,ONEBYT ; NO, MUST BE 1-BYTE INST CK42: LD A,E ; 2ND INST BYTE TO A LD HL,TAB42+O ; POINT TO 4-BYTE TABLE LD BC,LENT42 ; NO. BYTES TO CHECK CPIR ; SEE IF (PC+1) IS 4-BYTE INSTR JR NZ,ONEBYT ; NOT 4; MUST BE 1 LD B,4 ; IT'S A 4-BYTE INST JR SETSS ; GO SET IT UP ONEBYT: LD B,1 ; IT'S A 1-BYTE INST ; WE'VE FOUND OUT WHERE THE NEXT INSTRUCTION IS. ; NOW INSTALL IT AS A BREAKPOINT, USING THE ; FIRST BREAKPOINT TABLE ADDRESS. SETSDE LD BC,BRKCUR ; GET BKPT LINE CURSOR POS CALL POSCUR+O ; POSITION CURSOR CALL DISBRK+O ; SHOW CLEARED BKPT JP COMRET+O ; AND RETURN ; ; ; JUMP TO SPECIFIED ADDRESS ; JUMP: CALL GETWRD+O ; GET JUMP ADDRESS CALL COMCHK+O ; BE SURE COMMAND IS OK JP NZ,COMRET+O ; GET OUT IF NOT LD (SYSPC+O),DE ; MAKE JUMP ADDR USER PC JP DBEND+O ; THEN DO A "GO" ; ; ; FILL DESIGNATED AREA OF MEMORY WITH SPECIFIED BYTE ; FILL: CALL GETWRD+O ; GET FILL START ADDRESS LD (START+O),DE ; SAVE STARK+O ; RESTORE USER CODE CLRBR2: LD A,(BRKCNT+O) ; BUMP INC A ; BREAKPOINT LD (BRKCNT+O),A ; COUNTER INC HL ; NEXT TABLE ENTRY DJNZ CLRBR1 ; GO UNTIL 6 CHECKED ; THEN ZERO BREAKPOINT TABLE LD DE,BRKTBL+O ; START ADDR AT BREAK TABLE LD HL,BRKTBL+O+11 ; 12 ADDRESSES TO CLEAR XOR A ; ZERO A CALL LOAD+O ; ZERO THE TABLE LD BC,BRKCUR ; GET BKPT LINE CURSOR POS CALL POSCUR+O ; POSITION CURSOR CALL DISBRK+O ; SHOW CLEARED BKPTS JP COMRET+O ; DONE ; ; ; CLEAR (ZERO) A SPECIFIZ,BADREG ; NO MATCH; BAD COMMAND SLA C ; TIMES 2 FOR DISPLACEMENT REGM03: LD A,(CBUFCT+O) ; PREPARE DEC A ; FOR LD (CBUFCT+O),A ; CALL TO INC HL ; GETWRD CALL GETWRD+O ; GET NEW REG WORD CALL COMCHK+O ; BE SURE ADDRESS WAS OK JP NZ,COMRET+O ; GET OUT IF NOT LD A,C ; CHECK FOR SP & PC CP 0FFH ; SP? JR NZ,REGM04 ; NO, TRY PC LD (SYSSP+O),DE ; CHANGE USER SP JR REGM99 ; END COMMAND REGM04: CP 0FEH ; PC? JR NZ,REGM05 ; NO, REGULAR REGISTER LD (SYSPC+O),DE ; CHANGE USS: LD DE,(SYSPC+O) ; PUT USER PC IN DE SETSS1: INC DE ; MOVE TO NEXT ADDR ACCORDING DJNZ SETSS1 ; TO THE INSTRUCTION LENGTH JR SETSS4 ; THEN SET BKPT AND GO SETSS2: INC HL ; NEXT ADDRESS SETSS3: LD E,(HL) ; LOW JUMP BYTE TO E INC HL ; NEXT ADDRESS LD D,(HL) ; HIGH JUMP BYTE TO D SETSS4: LD HL,BRKTBL+O ; POINT TO THE BKPT TABLE LD (HL),E ; LOW ADDR BYTE TO TABLE INC HL ; BUMP TABLE ADDRESS LD (HL),D ; HIGH ADDR BYTE TO TABLE XOR A ; ZERO LD (BRKCNT+O),A ; BREAKPOINT COUNTET CALL GETWRD+O ; GET FILL END ADDRESS LD (ENDADD+O),DE ; SAVE END ON STACK CALL GETBYT+O ; GET FILL BYTE LD C,E ; SAVE IT IN C DURING CHECK CALL COMCHK+O ; BE SURE COMMAND WAS OK JP NZ,COMRET+O ; GET OUT IF NOT LD HL,(ENDADD+O) ; GET END ADDR BACK LD DE,(START+O) ; GET START ADDR BACK LD A,C ; GET FILL BYTE BACK CALL LOAD+O ; FILL THE MEMORY AREA JP COMRET+O ; DONE ; ; ; MODIFY REGISTER CONTENTS ; REGMOD: LD B,(HL) ; NEXT CHAR TO B CALL NXTCHR+O ; POINT TO NEXT CHAR LD ED BREAKPOINT ; FIXBRK: CALL GETWRD+O ; GET BKPT ADDR TO FIX CALL COMCHK+O ; BE SURE COMMAND WAS OK JP NZ,COMRET+O ; GET OUT IF BAD COMMAND CALL MATCH+O ; SEE IF IT'S A VALID BKPT LD A,(BRKCNT+O) ; GET BREAKPOINT COUNTER CP 6 ; DID WE TRY ALL W/ NO MATCH? JR NZ,FIXBR1 ; NO, THERE'S A MATCH CALL COMCH1+O ; NO MATCH--DO BAD COMMAND JP COMRET+O ; AND GET OUT FIXBR1: LD (HL),0 ; HIGH ADDR BYTE FROM COMPARE DEC HL LD (HL),0 ; LOW ADDR BYTE FROM COMPARE CALL REPBRK+O ; RESTORE USER COER PC JR REGM99 ; END COMMAND REGM05: LD (TMPWRD+O),SP ; SP TO HL--NOTHING LD HL,(TMPWRD+O) ; ON STACK HERE LD B,0 ; ZERO B ADD HL,BC ; ADD DISPLACEMENT LD (HL),E ; LOW BYTE TO STACK INC HL LD (HL),D ; HIGH BYTE TO STACK REGM99: LD E,HOME ; HOME CALL CHROUT+O ; CURSOR JP ENTRY1+O ; THAT'S ALL FOR REGMOD BADREG: CALL COMCH1+O ; BAD COMMAND JP COMRET+O ; GET OUT ; ; ; BLOCK MOVE--START ADDR, END ADDR, DESTINATION ; MOVE: CALL GETWRD+O ; GET MOVE START ADDR LD (START+O),R CALL SETBRK+O ; SET BKPT AT NEXT INST JP DBEND+O ; THEN EXECUTE A "GO" ; ; ; CLEAR (ZERO) ALL BREAKPOINTS ; FIRST RESTORE USER PROGRAM CODE ; CLRBRK: LD B,6 ; CHECK 6 TABLE ENTRIES LD HL,BRKTBL+O ; POINT TO BREAK TABLE XOR A ; ZERO LD (BRKCNT+O),A ; BREAKPOINT COUNTER CLRBR1: LD E,(HL) ; LOW BKPT BYTE TO E INC HL ; NEXT ADDR LD A,(HL) ; HIGH BKPT BYTE TO A OR E ; IS THERE AN ENTRY HERE? JR Z,CLRBR2 ; NO, CHECK NEXT ENTRY LD D,(HL) ; BKPT ADDR NOW IN DE CALL REPBRA,(HL) ; NEXT CHAR TO A ADD A,B ; "HASH" REG IDENTIFIER CP 163 ; SP? JR NZ,REGM01 ; NO, TRY PC LD C,0FFH ; DUMMY DISPLACEMENT FOR SP JR REGM03 ; GO FOR NEW REG CONTENTS REGM01: CP 147 ; PC? JR NZ,REGM02 ; NO, TRY REGULAR REGISTERS LD C,0FEH ; DUMMY DISPLACEMENT FOR PC JR REGM03 ; GO FOR NEW REG CONTENTS REGM02: PUSH HL ; SAVE COM BUFF POINTER LD HL,REGTAB+O ; POINT TO REG ID TABLE LD BC,10 ; 10 VALUES TO CHECK CPIR ; CHECK THEM POP HL ; GET COM BUFF POINTER BACK JR N  DE ; SAVE START CALL GETWRD+O ; GET MOVE END ADDR LD (TMPWRD+O),HL ; SAVE COM BUF POSITION EX DE,HL ; END ADDR NOW IN HL LD DE,(START+O) ; START ADDR IN DE SBC HL,DE ; GOING FOR BYTE COUNT CALL C,COMCH1+O ; OOPS--START>END JP C,COMRET+O ; SO GET OUT INC HL ; HERE'S BYTE COUNT PUSH HL ; "EX BC,HL" POP BC ; BYTE COUNT IN BC LD HL,(TMPWRD+O) ; GET COM BUF POINTER BACK CALL GETWRD+O ; GET DEST ADDR CALL COMCHK+O ; CHECK COMMAND JP NZ,COMRET+O ; GET OUT IF BAD LD HL,(START+O) ; PAST END? JR C,SEARC6 ; YES, STOP INC DE ; NEXT STRING POSITION LD A,(DE) ; GET STRING BYTE CP (HL) ; MATCH? JR Z,SEARC5 ; YES, TRY NEXT DEC HL ; LAST MISS COULD BE 1ST HIT JR SEARC3 ; START STRING OVER SEARC6: LD BC,ERRCUR ; ERROR MESSAGE POSITION CALL POSCUR+O ; POSITION CURSOR LD DE,NOTFND+O ; POINT TO NOT FOUND MSG CALL MSGOUT+O ; PRINT IT CALL BEEP+O ; BEEP KEYBOARD CALL DELAY+O ; HOLD MSG 3 SEC JP COMRET+O ; BYE-BYE SEARC7: LD HL,(TMPWRD+O) ; GET FOUND ADDR LD BC GET OUT INC DE ; ELSE GO TO NEXT STRING SPACE PUSH DE ; SAVE STRING POINTER CALL GETBYT+O ; GET NEXT STRING BYTE LD A,E ; PUT THE BYTE IN A POP DE ; GET STRING POINTER BACK LD (DE),A ; PUT IT IN STRING BUFFER INC BC ; BUMP BYTE COUNT JR SEARC1 ; GO FOR NEXT BYTE SEARC2: LD A,(CBUFCT+O) ; ADJUST BUFFER COUNT DEC A ; FOR LD (CBUFCT+O),A ; THE PERIOD CALL COMCHK+O ; THEN CHECK COMMAND JP NZ,COMRET+O ; GET OUT IF BAD LD (BYTCNT+O),BC ; SAVE STRING LENGTH SEARC8: LD HL,(STSITION CALL POSCUR+O ; FOR ANSWER LD E,'=' ; PRINT CALL CHROUT+O ; EQUAL SIGN LD E,SPACE ; PRINT CALL CHROUT+O ; A SPACE LD A,H ; PRINT CALL HX2ASC+O ; THE LD A,L ; ANSWER CALL HX2ASC+O CALL KBD+O ; WAIT FOR KEYPRESS JP COMRET+O ; THEN GO HOME ; ; ; RETURN TO COMMAND LINE FROM COMMAND ACTION ; COMRET: LD BC,COMCUR ; GET COMMAND CURSOR POSITION CALL POSCUR+O ; POSITION CURSOR LD B,3 ; 3 TIMES TO CLEAR 1 LINE COMRE1: LD DE,BLANK+O ; POINT TO BLANK LINE CALL MSGO; SOURCE TO HL LDIR ; MOVE 'EM JP COMRET+O ; AND DONE ; ; ; SEARCH FOR STRING IN MEMORY--START, END, UP TO 11 BYTES ; SEARCH: LD A,(HL) ; GET 1ST COM BUF CHAR CP '.' ; REPEAT LAST SEARCH? JR NZ,SEARC9 ; NO, DECODE COMMAND LD HL,(TMPWRD+O) ; YES, GET LAST FOUND ADDR LD BC,(BYTCNT+O) ; GET STRING LENGTH ADD HL,BC ; HL NOW PAST LAST FIND LD (START+O),HL ; MAKE THIS THE NEW START JR SEARC8 ; NOW GO TO SEARCH SEARC9: CALL GETWRD+O ; GET SEARCH START ADDR LD (START+O),DE ; SAV,30H ; ADJUST SBC HL,BC ; FOR DISPLAY LD (MEMSEL+O),HL ; SET IT UP FOR DISPLAY CALL DISMEM+O ; DISPLAY FOUND POSITION JP COMRET+O ; AND DONE ; ; TEST FOR END OF STRING SEARCH AREA ; ENDTST: PUSH HL ; SAVE CURRENT MEMORY POINTER PUSH DE ; AND STRING POSITION EX DE,HL ; MEMORY POINTER TO DE LD HL,(ENDADD+O) ; SEARCH END ADDR SBC HL,DE ; PAST END? POP DE ; GET CURRENT POP HL ; CONDITIONS BACK RET ; CALLER CHECKS FLAG ; ; ; PERFORM HEX ADDITION AND SUBTRACTION FOR ART+O) ; POINT TO START ADDR DEC HL ; PREP FOR INC SEARC3: LD DE,SCHSTR+O ; POINT TO STRING LD BC,(BYTCNT+O) ; GET STRING LENGTH LD A,(DE) ; GET 1ST STRING BYTE SEARC4: INC HL ; NEXT MEMORY ADDR CALL ENDTST+O ; PAST END POINT? JR C,SEARC6 ; YES, STOP CP (HL) ; MATCH? JR NZ,SEARC4 ; NO, TRY NEXT MEM ADDR LD (TMPWRD+O),HL ; YES, SAVE "FOUND" ADDR SEARC5: INC HL ; NEXT MEMORY ADDR DEC BC ; ONE FOUND LD A,B ; TEST FOR OR C ; ZERO JR Z,SEARC7 ; ALL FOUND; STOP CALL ENDTST+O UT+O ; BLANK COMMAND LINE DJNZ COMRE1 XOR A ; ZERO A LD (MODFLG+O),A ; CLEAR MODIFY MEMORY FLAG LD (COMFLG+O),A ; CLEAR COMMAND FLAG JP COMAND+O ; GO TO START OF COMMAND MODULE ; ;---------------------------------------------------------------------------- ; RETURN TO USER PROGRAM ; END OF DEBUG MAIN PROGRAM CODE ;---------------------------------------------------------------------------- ; DBEND: LD E,27 ; RESTORE CALL CHROUT+O ; USER LD E,'C' ; CURSOR CALL CHROUT+O ; E START ADDRESS CALL GETWRD+O ; GET SEARCH END ADDR LD (ENDADD+O),DE ; SAVE END ADDR LD BC,0 ; ZERO BC LD DE,SCHSTR+O-1 ; POINT TO STRING BUFFER SEARC1: LD A,(HL) ; GET NEXT COMMAND BYTE CP '.' ; END OF STRING? JR Z,SEARC2 ; YES, START SEARCH PUSH DE ; I NEED A REGISTER! LD E,A ; SAVE CHARACTER LD A,(CBUFCT+O) ; GET BUFFER COUNTER CP 0 ; IF IT'S NEGATIVE, USER LD A,E ; {GET CHAR BACK} POP DE ; {GET DE BACK} CALL M,COMCH1+O ; FORGOT THE END PERIOD JP M,COMRET+O ; SOUSER ; HARITH: CALL GETWRD+O ; GET THE FIRST NUMBER PUSH DE ; SAVE IT ON STACK LD A,(HL) ; ARITHMETIC OPERATOR LD (TMPWRD+O),A ; SAVE IT HERE CALL NXTCHR+O ; ADJ FOR OPERATOR CALL GETWRD+O ; GET SECOND NUMBER POP HL ; 1ST NO. NOW IN HL CALL COMCHK+O ; CHECK COMMAND JP NZ,COMRET+O ; GET OUT IF BAD LD A,(TMPWRD+O) ; GET OPERATOR BACK CP '-' ; SUBTRACT? JR Z,HARIT1 ; YES, GO DO IT ADD HL,DE ; NO, ADD JR HARIT2 HARIT1: SBC HL,DE ; SUBTRACT. HARIT2: LD BC,COMCUR+20 ; CURSOR PO   POSITION LD E,'6' CALL CHROUT+O LD (TMPWRD+O),SP ; PRESERVE CURRENT LOCAL SP LD HL,(SYSPC+O) ; REPLACE CORRECT RETURN ADDR LD SP,(SYSSP+O) ; ON BOTTOM OF USER PUSH HL ; STACK LD (SYSSP+O),SP ; SAVE NEW SYSTEM SP LD SP,(TMPWRD+O) ; RESTORE CURRENT LOCAL SP POP IY ; RESTORE ALL REGISTERS POP IX POP HL ; FIRST THE PRIME REGISTERS POP DE POP BC POP AF EX AF,AF' ; THEN THE REGULAR REGISTERS EXX POP HL POP DE POP BC POP AF LD SP,(SYSSP+O) ; RESTORE THE SYSTEM SPIX+15) ; PUT ENTRY D REGISTER IN H LD L,(IX+14) ; PUT ENTRY C REGISTER IN L JP REGRET+O ; ; DISPLAY HEX CONTENTS OF HL ; DISPHL: LD H,(IX+13) ; ENTRY H TO H LD L,(IX+12) ; ENTRY L TO L JP REGRET+O ; ; DISPLAY AF' VALUES ; DISAFP: LD H,(IX+11) ; ENTRY A' TO H LD L,(IX+10) ; ENTRY F' TO L JP DPRIM+O ; ; DISPLAY BC' VALUES ; DISBCP: LD H,(IX+9) ; ENTRY B' TO H LD L,(IX+8) ; ENTRY C' TO L JP REGRET+O ; ; DISPLAY DE' VALUES ; DISDEP: LD H,(IX+7) ; ENTRY D' TO H LD L,(IX+DISAF5 ; PRINT DASH IF ZERO LD E,'H' ; PRINT H IF SET JR DISAF6 DISAF5: LD E,'-' DISAF6: CALL CHROUT+O LD E,'x' ; BIT 3 NOT USED--DON'T CARE CALL CHROUT+O LD A,L ; GET FLAGS AGAIN BIT 2,A ; CHECK PARITY/OVERFLOW FLAG JR Z,DISAF7 ; PRINT DASH IF ZERO LD E,'P' ; PRINT P IF SET JR DISAF8 DISAF7: LD E,'-' DISAF8: CALL CHROUT+O LD A,L ; GET FLAGS AGAIN BIT 1,A ; CHECK NEGATIVE FLAG JR Z,DISAF9 ; PRINT DASH IF ZERO LD E,'N' ; PRINT N IF SET JR DISAF0 DISAF9 LD E,'-' DISA NUMBERS DISME2: CALL POSCUR+O ; PUT THE CURSOR THERE DISME3: LD A,H ; HIGH ADDR BYTE TO A CALL HX2ASC+O ; PRINT HEX VALUE LD A,L ; LOW ADDR BYTE TO A CALL HX2ASC+O ; PRINT HEX VALUE CALL DISREG+O ; PRINT THE ADDRESS CONTENTS LD A,(LINECT+O) ; GET LINE COUNT DEC A ; ONE LINE DONE RET Z ; RETURN IF DONE LD (LINECT+O),A ; SAVE NEW LINE COUNT LD BC,(CURSOR+O) ; GET THE CURSOR POSITION INC B ; MOVE IT DOWN ONE LINE JR DISME1 ; GO FOR NEXT LINE ; ; ; DISPLAY REGISTER CONTENTS  ; ; RETURN TO CALLING PROGRAM ; RET ; TO CALLING PROGRAM ; ; ;---------------------------------------------------------------------------- ; DEBUG-SPECIFIC SUBROUTINES ;---------------------------------------------------------------------------- ; ; DISPLAY AF HEX VALUES AND FLAG REPRESENTATIONS ; DISPAF: LD H,(IX+19) ; PUT ENTRY A REGISTER IN A LD L,(IX+18) ; ENTRY F REGISTER IN L DPRIM: CALL ONLYRG+O ; PRINT REGISTER HEX VALUES LD E,SPACE ; PRINT TWO SPACES CALL CHROUT+O C6) ; ENTRY E' TO L JP REGRET+O ; ; DISPLAY HL' VALUES ; DISHLP: LD H,(IX+5) ; ENTRY H' TO H LD L,(IX+4) ; ENTRY L' TO L JP REGRET+O ; ; DISPLAY IX VALUES ; DISPIX: LD H,(IX+3) ; ENTRY IXH TO H LD L,(IX+2) ; ENTRY IXL TO L JP REGRET+O ; ; DISPLAY IY VALUES ; DISPIY: LD H,(IX+1) ; ENTRY IYH TO H LD L,(IX) ; ENTRY IYL TO L JP REGRET+O ; ; DISPLAY SP VALUES ; DISPSP: LD HL,(SYSSP+O) ; SYSTEM SP TO HL JP REGRET+O ; ; DISPLAY PC CONTENTS ; DISPPC: LD HL,(SYSPC+O) ; SF0 CALL CHROUT+O LD A,L ; GET FLAGS ONE LAST TIME BIT 0,A ; CHECK CARRY FLAG JR Z,DISAFA ; PRINT DASH IF ZERO LD E,'C' ; PRINT C IF SET JR DISAFB DISAFA: LD E,'-' DISAFB: CALL CHROUT+O RET ; FLAG REPRESENTATIONS DONE ; ; ; DISPLAY HEX CONTENTS OF BC, INDIRECTS, AND ASCII REPRESENTATIONS ; DISPBC: LD H,(IX+17) ; PUT ENTRY B REGISTER IN H LD L,(IX+16) ; PUT ENTRY C REGISTER IN L JP REGRET+O ; ; DISPLAY HEX CONTENTS OF DE,INDIRECTS, AND ASCII REPRESENTATIONS ; DISPDE: LD H,(AND INDIRECTS; ; RETURN TO REGISTER CALLING POINT ; REGRET: CALL ONLYRG+O ; DISPLAY REGISTER CONTENTS CALL DISREG+O ; DISPLAY REGISTER INDIRECTS RET ; ; ; DISPLAY CONTENTS OF ACTUAL REGISTER ONLY. ON ENTRY, ; CONTENTS OF SELECTED REGISTER ARE IN HL. ; ONLYRG: LD A,H ; MOVE REG HIGH BYTE TO A CALL HX2ASC+O ; PRINT ITS HEX REPRESENTATION LD E,SPACE ; PRINT A SPACE CALL CHROUT+O LD A,L ; MOVE REG LOW BYTE TO A CALL HX2ASC+O ; PRINT HEX REPRESENTATION RET ; THAT'S ALL WE ALL CHROUT+O LD A,L ; FLAGS INTO A BIT 7,A ; CHECK SIGN FLAG JR Z,DISAF1 ; PRINT DASH IF ZERO LD E,'S' ; PRINT S IF SET JR DISAF2 DISAF1: LD E,'-' ; PRINT HYPHEN IF NOT SET DISAF2: CALL CHROUT+O LD A,L ; GET FLAGS AGAIN BIT 6,A ; CHECK ZERO FLAG JR Z,DISAF3 ; PRINT DASH IF ZERO LD E,'Z' ; PRINT Z IF SET JR DISAF4 DISAF3: LD E,'-' DISAF4: CALL CHROUT+O LD E,'x' ; BIT 5 NOT USED--DON'T CARE CALL CHROUT+O LD A,L ; GET FLAGS AGAIN BIT 4,A ; CHECK HALF-CARRY FLAG JR Z,YSTEM PC TO HL JP REGRET+O ; ; ; DISPLAY THE SELECTED AREA OF MEMORY ; ON ENTRY, STARTING ADDRESS IS IN HL ; DISMEM: LD A,8 ; SET UP LINE COUNTER LD (LINECT+O),A LD BC,MEMCUR ; GET DISPLAY CURSOR POS DISME1: LD (CURSOR+O),BC ; SAVE CURSOR POSITION CP 5 ; ARE WE ON SELECTED LINE? JR NZ,DISME2 ; NO, GO ON DEC C ; MOVE CURSOR DEC C ; BACK 3 DEC C ; POSITIONS CALL POSCUR+O ; POSITION CURSOR LD DE,PTR+O ; POINT TO POINTER CALL MSGOUT+O ; PRINT IT JR DISME3 ; THEN GO TO  NEED HERE ; ; ; PRINTS THE SIXTEEN INDIRECT VALUES AND ASCII CHARACTERS ; ON ENTRY, THE REGISTER TO BE DISPLAYED MUST BE IN HL ; DISREG: LD E,'-' ; PRINT A HYPHEN CALL CHROUT+O LD E,'>' ; AND A RIGHT CARET CALL CHROUT+O LD BC,16 ; BYTE COUNT LOOP01: LD A,(HL) ; GET A BYTE CALL HX2ASC+O ; PRINT IT LD E,SPACE ; PRINT A SPACE CALL CHROUT+O INC HL ; PREPARE FOR NEXT BYTE DEC C ; DECREMENT BYTE COUNT LD A,C ; NEED TO DO A COMPARE CP 8 ; HAVE WE JUST DONE THE 8TH BYTE?  GETBYT: PUSH BC ; PRESERVE BC LD A,(HL) ; GET 1ST CHARACTER CALL NXTCHR+O ; ADJUST COUNTER & POINTER CALL ASC2HX+O ; CHANGE FROM ASCII TO HEX SLA A ; SHIFT IT TO BITS 7-4 SLA A ; TO BECOME THE HIGH SLA A ; NIBBLE OF THE HEX NUMBER SLA A LD B,A ; SAVE IT IN B LD A,(HL) ; GET 2ND CHARACTER CALL NXTCHR+O ; ADJUST COUNTER & POINTER CALL ASC2HX+O ; CHANGE FROM ASCII TO HEX ADD A,B ; COMBINE WITH HIGH NIBBLE LD E,A ; PUT 2ND (LOW) BYTE IN E POP BC ; RETRIEVE BC RET ; ODONE? JR NZ,LOOP02 ; NO, GO FOR ANOTHER RET ; YES, RETURN ; ; ; DISPLAY BREAKPOINTS ; DISBRK: LD DE,BRKMSG+O ; POINT TO BREAKPOINT MSG CALL MSGOUT+O ; PRINT IT LD HL,BRKTBL+O ; POINT TO BREAKPOINT TABLE LD B,6 ; NUMBER OF ADDRESSES TO DISPLAY DISBR1: INC HL ; HIGH BYTE OF BREAKPOINT ADDR LD A,(HL) ; INTO A CALL HX2ASC+O ; PRINT IT DEC HL ; LOW BYTE OF BREAKPOINT ADDR LD A,(HL) ; INTO A CALL HX2ASC+O ; PRINT IT LD E,SPACE ; PRINT A SPACE CALL CHROUT+O INC HL ; MOVE WS ; ; ; SET BREAKPOINT IN CODE. BREAKPOINT ADDRESS IS IN DE ON ENTRY ; SETBRK: PUSH BC ; WE'LL NEED THESE PUSH HL ; FOR A BIT LD B,0 ; ZERO B LD A,(BRKCNT+O) ; GET BREAKPOINT COUNTER LD C,A ; PUT IT IN C LD HL,CODSAV+O ; POINT TO CODE SAVE TABLE ADD HL,BC ; ADD OFFSET LD A,(DE) ; PROGRAM CODE TO A CP 0FFH ; IS IT ALREADY A BREAK? JR Z,SETBR1 ; YES, DON'T PUT IN CODSAV LD (HL),A ; THEN TO SAVE TABLE EX DE,HL ; BKPT ADDR TO HL LD (HL),0FFH ; BKPT INSTALLED SETBR1: POJR NZ,DISRG1 ; NO, GO ON LD E,SPACE ; YES, PRINT A SPACE CALL CHROUT+O JR LOOP01 ; AND GO FOR ANOTHER BYTE DISRG1: OR A ; ARE WE DONE? JR NZ,LOOP01 ; NO, GO FOR ANOTHER BYTE LD E,SPACE ; YES, PRINT ANOTHER SPACE CALL CHROUT+O LD BC,16 ; GET THE BYTE COUNT AGAIN SBC HL,BC ; SET HL BACK TO WHERE WE STARTED LOOP02: LD A,(HL) ; GET A CHARACTER CP 32 ; IS IT A CONTROL CHARACTER? JR C,DISRG5 ; YES, PRINT A PERIOD CP 127 ; IS IT A GRAPHICS CHARACTER? JR NC,DISRG5 ; YES, PRINT A PNE BYTE DONE ; ; ; ADJUST COMMAND BUFFER COUNTER AND POINT TO NEXT ; CHARACTER IN THE COMMAND BUFFER ; NXTCHR: PUSH BC ; SAVE BC LD C,A ; SAVE CHAR IN C LD A,(CBUFCT+O) ; GET BUFFER COUNTER DEC A ; SUBTRACT 1 FOR THIS CHARACTER LD (CBUFCT+O),A ; SAVE NEW COUNT LD A,C ; GET CHAR BACK INC HL ; POINT TO NEXT CHAR POP BC ; GET BC BACK AGAIN RET ; AND RETURN ; ; ; CHECKS COMMANDS AFTER DECODING BY SPECIFIC COMMAND ; MODULES TO BE SURE THERE ARE NO MISSING OR EXTRA ; CTO NEXT BREAKPOINT INC HL DJNZ DISBR1 ; GO FOR IT IF NOT DONE RET ; ELSE RETURN ; ; ; THESE SUBROUTINES DECODE 4-CHARACTER ASCII ADDRESSES FROM ; THE COMMAND BUFFER. GETWRD RETURNS A 2-BYTE (ONE WORD) ; VALUE IN DE; GETBYT RETURNS A 1-BYTE VALUE (TREATED AS A ; "LOW" BYTE) IN THE E REGISTER. ON ENTRY, THE COMMAND ; BUFFER POINTER (ADDRESS) IS IN HL. ; GETWRD: CALL GETBYT+O ; GET THE FIRST (HIGH) BYTE LD D,A ; AND PUT IT IN D; THEN FALL ; THROUGH FOR 2ND (LOW) BYTE P HL ; RESTORE POP BC ; REGISTERS RET ; AND RETURN ; ; ; LOOK FOR ADDRESS IN BREAKPOINT TABLE ; ADDRESS TO TEST IS IN DE ON ENTRY ; MATCH: LD HL,BRKTBL+O ; POINT TO BREAKPOINT TABLE LD B,6 ; MAX 6 ENTRIES TO CHECK XOR A ; ZERO BREAKPOINT LD (BRKCNT+O),A ; COUNTER MATCH1: LD A,(HL) ; TABLE ENTRY LOW BYTE TO A CP E ; CHECK AGAINST BKPT LOW BYTE JR NZ,MATCH2 ; NO LB MATCH; GO TO NEXT ENTRY INC HL ; LOW BYTE MATCHES LD A,(HL) ; SO TRY HIGH CP D RET Z ; THIS IS THE ONERIOD JR DISRG2 ; NO, IT HAS AN ASCII REPRESENTATION DISRG5: LD E,'.' ; YES, PRINT A PERIOD JR DISRG3 ; DISRG2: LD E,A ; ASCII CHARACTER TO E DISRG3: CALL CHROUT+O ; PRINT PERIOD OR CHARACTER INC HL ; PREPARE FOR NEXT CHARACTER DEC C ; DECREMENT BYTE COUNT LD A,C ; FOR THE COMPARES CP 8 ; DID WE JUST PRINT THE 8TH CHARACTER? JR NZ,DISRG4 ; NO, JUST CONTINUE LD E,SPACE ; YES, PRINT A SPACE CALL CHROUT+O JR LOOP02 ; AND GO FOR ANOTHER BYTE DISRG4: OR A ; WELL, THEN, ARE WE HARACTERS. THE COMMAND BUFFER COUNTER SHOULD BE 0 ; AFTER ANY COMMAND HAS BEEN DECODED. ; COMCHK: LD A,(CBUFCT+O) ; GET COMMAND BUFFER COUNT OR A ; SHOULD BE 0 IF COMMAND OK RET Z ; RETURN OK COMCH1: PUSH AF ; SAVE NZ FLAG FOR RETURN CALL BEEP+O ; BEEP KEYBOARD LD BC,ERRCUR ; POSITION CURSOR CALL POSCUR+O ; FOR ERROR MESSAGE LD DE,BADCOM+O ; POINT TO BAD COMMAND MESSAGE CALL MSGOUT+O ; SEND IT CALL DELAY+O ; WAIT 3 SECONDS POP AF ; GET NZ FLAG BACK RET ; RETURN WITH BAD NE  E; RETURN JR MATCH3 ; NO HIGH BYTE MATCH MATCH2: INC HL ; NO LOW BYTE MATCH MATCH3: LD A,(BRKCNT+O) ; INCREMENT INC A ; BREAKPOINT LD (BRKCNT+O),A ; COUNTER INC HL ; AND TABLE POINTER DJNZ MATCH1 ; CONTINUE RET ; FROM HERE IF NO MATCH AT ALL ; ; ; LOAD SPECIFIED AREA OF MEMORY WITH SPECIFIED BYTE ; ON ENTRY, START ADDRESS IS IN DE, END ADDRESS IS IN HL, ; AND BYTE IS IN A ; LOAD: LD (DE),A ; LOAD BYTE INTO START ADDR INC DE ; NEXT ADDR TO LOAD PUSH HL ; DON'T LOSEAND 0FH ; MASK OFF LOW NIBBLE CALL HX2AS1+O ; SEND LOW HEX NIBBLE TO SCREEN POP BC ; RESTORE BC RET ; SUBROUTINE DONE ; TRANSLATE HEX NIBBLE TO ASCII CHARACTER HX2AS1: CP 10 ; IS THE NIBBLE > 10? JR NC,LETTER ; IT'S A LETTER IF IT IS ADD A,48 ; MAKE IT ASCII FOR A NUMBER JR NIBOUT ; AND PRINT IT LETTER: ADD A,55 ; MAKE IT ASCII FOR A LETTER NIBOUT: LD E,A ; REALLY GO PRINT IT NOW CALL CHROUT+O RET ; HEX. CHAR PRINTED ; ; ; CONVERT ONE ASCII CHARACTER TO ITS HEX VALUE; H BC ; PUSH REGISTERS PUSH DE ; FOR BDOS CALL PUSH HL LD C,CONIN ; GET CHAR FROM KEYBOARD CALL BDOS CP 96 ; LOWER CASE? JR C,KBD2 ; NO, GO AHEAD SUB 32 ; MAKE IT UPPER CASE KBD2: JR GENRET ; RETURN ; ; ; ONE CHARACTER TO SCREEN--ON ENTRY, CHARACTER MUST BE IN E REG ; CHROUT: PUSH BC ; SAVE THE REGISTERS PUSH DE PUSH HL LD C,CONOUT ; CONSOLE OUT FUNCTION CALL BDOS ; SEND CHAR TO SCREEN JP GENRET+O ; POP REGISTERS AND RETURN ; ; ; MESSAGE TO SCREEN--ON ENTRY, ME DELAY2: DEC HL ; THE INNER LOOP LD A,H OR L ; HL = 0? JR NZ,DELAY2 ; NO, GO AGAIN DJNZ DELAY1 ; GO UNTIL B=0. RET ; ;---------------------------------------------------------------------------- ; TABLES ;---------------------------------------------------------------------------- ; ; "HASHED" REGISTER IDENTIFIERS FOR MODIFY REGISTER ; CONTENTS COMMAND ; REGTAB: DEFB 135 ; AF DEFB 133 ; BC DEFB 137 ; DE DEFB 148 ; HL DEFB 104 ; AF' DEFB 105 ; BC' DEFB 107 ; D END ADDR SBC HL,DE ; FILLED ALL YET? POP HL ; GET IT BACK JR NC,LOAD ; NO, DO NEXT RET ; YES, DONE ; ; ; RESTORE USER CODE TO PROGRAM FOR ; CLEAR BREAKPOINT ROUTINES ; ON ENTRY, BREAKPOINT ADDRESS IS IN DE ; REPBRK: PUSH BC ; SAVE CALLER COUNTER PUSH HL ; AND BKPT TABLE ADDR LD B,0 ; ZERO B LD A,(BRKCNT+O) ; GET BREAKPOINT COUNTER LD C,A ; PUT IT IN C LD HL,CODSAV+O ; POINT TO SAVED CODE TABLE ADD HL,BC ; ADD OFFSET LD A,(HL) ; GET SAVED CODE BYTE LD (DE),A ; I.E., 'F' BECOMES 0FH. THE CHARACTERS COMING ; IN HERE HAVE ALREADY BEEN CHECKED FOR PROPER ; RANGE IN THE COMMAND ENTRY MODULE ; ASC2HX: CP 58 ; IS IT A DIGIT? JR NC,ASC2H1 ; NO, MUST BE A LETTER SUB 30H ; ADJUST FOR DIGIT RET ASC2H1: SUB 37H ; ADJUST FOR LETTER RET ; ; ; PRINT CR LF ; NEWLIN: LD E,CR CALL CHROUT+O LD E,LF CALL CHROUT+O RET ; ; ; KAYPRO PROTOCOL TO POSITION CURSOR-- ; CURSOR HEX Y AND X POSITIONS ARE PASSED IN BC ; POSCUR: LD E,27 ; KAYPROSSAGE ADDRESS MUST BE IN DE ; MSGOUT: PUSH BC ; SAVE THE REGISTERS PUSH DE PUSH HL LD C,PSTR ; CONSOLE STRING OUT FUNCTION CALL BDOS ; SENT MESSAGE TO SCREEN JP GENRET+O ; POP REGISTERS AND RETURN ; ; ; CONVERT HEX VALUE TO ASCII REPRESENTATION ; ON ENTRY, CHARACTER MUST BE IN A REGISTER ; HX2ASC: PUSH BC ; PRESERVE BC LD B,A ; SAVE HEX NO. IN B SRL A SRL A SRL A SRL A ; BITS 4-7 NOW IN 0-3 CALL HX2AS1+O ; SEND HIGH HEX NIBBLE TO SCREEN LD A,B ; GET HEX NO. BACK E' DEFB 111 ; HL' DEFB 161 ; IX DEFB 162 ; IY ; ; ; BYTE TABLES FOR SINGLE-STEPPING ; TAB21: DEFB 0CBH ; IF THE INSTRUCTION BYTE DEFB 06H ; IS ANY OF DEFB 0EH ; TAB21 DEFB 10H ; THEN IT'S A DEFB 16H ; 2-BYTE DEFB 18H ; INSTRUCTION DEFB 1EH DEFB 20H DEFB 26H DEFB 28H DEFB 2EH DEFB 30H DEFB 36H DEFB 38H DEFB 3EH DEFB 0C6H DEFB 0D3H DEFB 0D6H DEFB 0DBH DEFB 0DEH DEFB 0E6H DEFB 0EEH DEFB 0F6H DEFB 0FEH T21END: EQU $ ; FOR CHANGES AND CALCULAT; AND PUT IT BACK IN PROGRAM POP HL ; GET BKPT ADDR BACK POP BC ; AND CALLER COUNTER RET ; ; ;---------------------------------------------------------------------------- ; GENERAL SUBROUTINES/PRIMITIVES ;---------------------------------------------------------------------------- ; ; GENERAL RETURN SUBROUTINE FOR BDOS FUNCTIONS ; GENRET: POP HL ; ONE RETURN FOR ALL THE POP DE ; ROUTINES THAT PUSH THESE POP BC ; REGISTERS RET ; ; ; GET CHARACTER FROM KEYBOARD ; KBD: PUS CURSOR POSITION PROTOCOL-- CALL CHROUT+O ; PRINT ESC '=' 'Y' 'X' LD E,'=' CALL CHROUT+O LD E,B ; CURSOR Y POSITION CALL CHROUT+O LD E,C ; CURSOR X POSITION CALL CHROUT+O RET ; CURSOR NOW IN POSITION ; ; ; BEEP KEYBOARD ; BEEP: PUSH DE ; DON'T LOSE DE LD E,BELL ; PRINT BELL CALL CHROUT+O POP DE ; DE BACK RET ; ; ; DELAY APPROX 0.5 SEC FOR EACH COMPLETION OF 0FFFFH ; COUNT IN HL ; DELAY: LD B,6 ; DO INNER LOOP 6 TIMES DELAY1: LD HL,0FFFFH ; APPROX 0.5 SEC FOR  ION ; ; TAB22: DEFB 09H ; IF THE 1ST INSTRUCTION DEFB 19H ; BYTE IS DD, ED, OR FD DEFB 23H ; AND THE NEXT DEFB 29H ; BYTE IS ONE OF DEFB 2BH ; TAB22, DEFB 39H ; THEN IT'S A DEFB 0E1H ; 2-BYTE DEFB 0E3H ; INSTRUCTION. DEFB 0E5H DEFB 0E9H DEFB 0F9H DEFB 40H DEFB 41H DEFB 42H DEFB 44H DEFB 45H DEFB 46H DEFB 47H DEFB 48H DEFB 49H DEFB 4AH DEFB 4DH DEFB 50H DEFB 51H DEFB 52H DEFB 56H DEFB 57H DEFB 58H DEFB 59H DEFB 5AH DEFB 5EH DEFB 60H D----------- ; ; SET LOCAL STACK SPACE ; DEFS 64 ; 32-WORD LOCAL STACK LOCSP: DEFB 0 ; START OF STACK ; ;---------------------------------------------------------------------------- ; ABSOLUTE END OF DEBUG CODE ;---------------------------------------------------------------------------- CODEND EQU $ ; ; ;---------------------------------------------------------------------------- ; CALCULATIONS/EQUATES FOR RELOCATION AND OFFSET ;-------------------------------------------------------H ; INSTRUCTION DEFB 70H DEFB 71H DEFB 72H DEFB 73H DEFB 74H DEFB 75H DEFB 77H DEFB 7EH DEFB 86H DEFB 8EH DEFB 96H DEFB 9EH DEFB 0A6H DEFB 0AEH DEFB 0B6H DEFB 0BEH T32END: EQU $ ; FOR CHANGES AND CALCULATION ; ; TAB42: DEFB 21H ; IF THE 1ST INSTRUCTION DEFB 22H ; BYTE IS DD, ED, OR FD DEFB 2AH ; AND THE NEXT BYTE DEFB 36H ; IS ONE OF DEFB 0CBH ; TAB42, DEFB 43H ; THEN IT'S A DEFB 4BH ; 4-BYTE DEFB 53H ; INSTRUCTION DEFB 5BH DEFB 73H DEFB 7BH ; DU.ASM V8.6 Revised 10/24/83 ; Disk Utility - Written by Ward Christensen 08/06/78 ; ; (See DU.DOC for description and detailed instructions.) ; ; This version of DU is compatible with CP/M 1.4, 2.x and 3.x and does ; not require alteration for various hardware configurations. It ad- ; justs itself automatically to the correct number of sectors, tracks, ; directory size, etc. ; ; Because of the automatic adaption feature, no conditional assembly op- ; tions are included. The only alEFB 61H DEFB 62H DEFB 67H DEFB 68H DEFB 69H DEFB 6AH DEFB 6FH DEFB 72H DEFB 78H DEFB 79H DEFB 7AH DEFB 0A0H DEFB 0A1H DEFB 0A2H DEFB 0A3H DEFB 0A8H DEFB 0A9H DEFB 0AAH DEFB 0ABH DEFB 0B0H DEFB 0BAH DEFB 0B2H DEFB 0B3H DEFB 0B8H DEFB 0B9H DEFB 0BAH DEFB 0BBH T22END: EQU $ ; FOR CHANGES AND CALCULATION ; ; TAB31: DEFB 01H ; IF THE INSTRUCTION BYTE DEFB 11H ; IS ANY OF DEFB 21H ; TAB31, DEFB 22H ; THEN IT'S A DEFB 2AH ; 3-BYTE DEFB 31H ; --------------------- ; CODSIZ EQU CODEND-CODBEG ; AMOUNT OF CODE TO RELOCATE DESTIN EQU RELTOP-CODSIZ-2 ; RELOCATION DESTINATION O EQU DESTIN-CODBEG ; OFFSET FOR CALLS AND JUMPS ; ;---------------------------------------------------------------------------- END ; OF DEBUG  T42END: EQU $ ; FOR CHANGES AND CALCULATION ; ; TABLE LENGTHS FOR CPIR ; LENT21: EQU T21END-TAB21 ; LENGTH OF 1ST 2-BYTE TABLE LENT22: EQU T22END-TAB22 ; LENGTH OF 2ND 2-BYTE TABLE LENT31: EQU T31END-TAB31 ; LENGTH OF 1ST 3-BYTE TABLE LENT32: EQU T32END-TAB32 ; LENGTH OF 2ND 3-BYTE TABLE LENT42: EQU T42END-TAB42 ; LENGTH OF 4-BYTE TABLE ; ;---------------------------------------------------------------------------- ; STACK ;-----------------------------------------------------------------teration that needs to be done is to ; use DDT to set the byte at 103H for your clock speed. Use 0 for 2MHz, ; 1 for 4MHz, 2 for 6MHz. (This only affects the 'Z' SLEEP command.) ; ;---------------------------------------------------------------------- ; ; This program has been heavily modified to allow it to work without ; modification on most versions of CP/M 1.4 and, hopefully, all versions ; of CP/M 2.x and 3.x. If you have difficulty getting this program to ; run, AND if you are using CP/M 2.INSTRUCTION. DEFB 32H DEFB 3AH DEFB 0C2H DEFB 0C3H DEFB 0C4H DEFB 0CAH DEFB 0CCH DEFB 0CDH DEFB 0D2H DEFB 0D4H DEFB 0DAH DEFB 0DCH DEFB 0E2H DEFB 0E4H DEFB 0EAH DEFB 0ECH DEFB 0F2H DEFB 0F4H DEFB 0FAH DEFB 0FCH T31END: EQU $ ; FOR CHANGES AND CALCULATION ; TAB32: DEFB 34H ; IF THE 1ST INSTRUCTION DEFB 35H ; BYTE IS DD, ED, OR FD DEFB 46H ; AND THE NEXT BYTE DEFB 4EH ; IS ONE OF DEFB 56H ; TAB32, DEFB 5EH ; THEN IT'S A DEFB 66H ; 3-BYTE DEFB 6E  x or 3.x, AND if you know your BIOS ; to be bug-free, leave a message on Technical CBBS of Dearborn, Michi- ; gan (313)-846-6127 with a description of the problem and a summary of ; your hardware configuration. One known possible problem involves the ; system tracks on some systems, and results from the system sectors ; being skewed. There is NO way for a program executing under CP/M to ; know about this. This program assumes the standard convention of no ; skew being used on the system tracks. Thisitioned after "M" ; was executed. This occured because "M" was calling POSGP2, ; but the disk wasn't within a group. In this case, "M" now ; uses track/sector positioning. Restored the "(" toggle to ; the way it was before it got changed in v8.3. ; - Peter H. Haas ; ; 10/21/83 Added command 'K'. This allows you to save a file from the ; v8.4 'YANK' sequential memory without leaving DU. Intended for ; use under 3.0, which doesn't have a save command, but worse. Restored help format used in v8.3 and v8.4. Other ; minor changes. - Irv Hoff ; ; 10/23/83 Radix escape ("#") extended to include both decimal and hex ; v8.5 values. Added "Mxx" command operand range check. xx omitted ; or less than the first data group now starts with the first ; data group. xx greater than the last group now gives "not ; within tracks" error. Previously "M" would loop. "M" output ; delimiters and CRLFs now properly handled. Termination of ; back to SPT. Before this change, ; DU would get Record Not Found (RNF) errors on some LSI-type ; controllers (e.g. 179X, uPD765), or would read/write the wrong ; sector on SASI-type controllers (DTC, SHUGART). In the case ; of SASI-type controllers, the sector read/written in error was ; usually (S# MOD 26) on the next track (track 01). ; - Peter H. Haas ; ; 10/12/83 Fixed minor bug which caused garbage to be sent to the ; v8.1 printer if CBIOS console output routins usually isn't a problem be- ; cause the SYSGEN program can be used to get the system from the disk ; so that it can be modified. This program should work under standard ; versions of CP/M 1.4. The only requirement is that the BIOS "SETSEC" ; routine not modify the sector number passed to it in the 'B' register. ; Again, system tracks with skewed sectors will be a problem. ; ; If you add any features or make any useful ; changes to this program, please modem a copy ; to the above CBBS, so theks ; fine for 2.2. I haven't tested it for 1.4. ; - Jeffrey J. Nonken ; ; 10/18/83 Changed the map toggle to conform with original intent to ; v8.3 show erased files. (Includes an '*' if that block has been ; written over by another file.) Can be toggled off with '(' ; prior to asking for 'M'. Also added a change suggested by ; Sigi Kluger. - Irv Hoff ; ; 10/15/83 Combined DU-V81 version of Peter Haas and DU-V81 version of ;  "M" command by ^C now handled properly; stack had HL pushed ; twice. TOS is supposed to be INnBUF pointer, but TOS was ; garbage and INBUF pointer was at TOS+2. Help updated to re ; flect the correct default radix on prototype commands. Out- ; put of "#" command revised, now includes both decimal and ; hex. Fixed long standing bug in "M". If disk was not posi- ; tioned within a group (e.g. disk was positioned within a ; system track), disk would be improperly poe did not return with ; the character still in the 'C' register. ; - Bob Clyne ; ; 10/07/83 Fixed minor bug in WFM's code which prevented DU from work- ; v8.0 ing under CP/M 2.2. Verified operation on Magnolia CP/M+ ; (banked). Changed MHz patch location to allow 6MHz. NOTE: ; No changes made to CP/M+ operation. ; - Sigi F. Kluger ; ; 08/28/83 Modified SFK's CP/M+ fixes so they would work: Used BDOS ; v7.9a function 50 for 3.x BIOS disk calls. Added deblocking  currency of the ; program can be maintained. ; - Ron G. Fowler ; ;----------------------------------------------------------------------- ; ; 10/24/83 The :M change added to v8.3 was made after consultation with ; v8.6 Ward Christensen (the original author), Dave Hardy, and some ; others. It was changed by the person updating v8.5, but is ; now restored to the author's original intent. Personal com- ; ments deleted from v8.5 update since they served no useful ; purpo v8.2 Bob Clyne. Some changes in the help guide. ; - Irv Hoff ; ; 10/14/83 Added support for disks where the boot track (track 00) ; v8.1 contains fewer sectors than other tracks (e.g. 8-inch disks ; formatted in IBM System/34 format where track 00 is single- ; density with 128 byte records, and other tracks are double- ; density with 256 or 1024 byte records. Defaults to SPT, ; but may be changed to another value by the "B[nn]" command. ; "B" or "B0" changes  code ; for CP/M 3.x disk I/O. Flagged all mods with initials (WFM) ; as comment. NOTE: Using BDOS function 50 should work for ; MP/M, if some MP/M user will check code to see if it is ; compatible. - (WFM) ; ; 03/27/83 Modified for use with CP/M+ (version 3.0). Re-enabled dis- ; v7.9 play of erased file toggle (on/off) and suppressed CP/M+ ; date/time fields. - Sigi F. Kluger ; ; ; Many other changes between 1978 and 1983, by ; Ron Fowler, Keith Petersen, Bruceitialize "y" command memory pointer SHLD YNKADR ; ; ; Set up local jumps to BIOS ; LHLD BASE+1 ;warm boot pointer LXI D,3 ;ready for add DAD D SHLD VCONST+1 ;const DAD D SHLD VCONIN+1 ;conin DAD D SHLD VCONOT+1 ;conout DAD D SHLD VLIST+1 ;list CPI 30H ;CP/M 3.x? JNC HELLO DAD D ;punch DAD D ;reader DAD D SHLD VHOME+1 ;home DAD D SHLD VSELDK+1 ;seldsk DAD D SHLD VSETRK+1 ;settrk DAD D SHLD VSTSEC+1 ;setsec DAD D SHLD VSTDMA+1 ;setdma DAD D SHLDCP/M 1.4 offset from base ;of BDOS to SECTRAN routine SKWOFF EQU 1AH ;CP/M 1.4 offset to skew table S2OFF EQU 14 ;offset into FCB for S2 byte DPBOFF EQU 3AH ;CP/M 1.4 offset to DPB within BDOS S2MASK EQU 0FH ;mask for extended RC bits of S2 DPBLEN EQU 17 ;size of CP/M 3.x disk parameter block ; ; ; Define ASCII characters ; CR EQU 0DH ;carriage return LF EQU 0AH ;line feed TAB EQU 09H ;tab BS EQU 08H ;backspace ; ; ORG BASE+100H ; ; JMP PASTCK ;jump over clock byte and i.d ; ; ; Got initial command, set it up ; MOV B,A ;save length DCR B JZ PRMPTR LXI D,INBUF INX H ;skip len INX H ;skip ' ' CALL MOVE MVI A,CR STAX D LXI H,INBUF JMP PRMPTI ; PRMPTR XRA A STA QFLAG CALL RDBUF ; PRMPTI MVI A,255 STA TOGO ;loop count for "/" STA TOGO+1 ; PROMPT EQU $ ; SETSTK LXI SP,$-$ ;modified at init XRA A ;zero 2-up print STA TWOUP ;..switch MVI A,1 STA FTSW ;tell search not to increment PUSH H LXI H,BASE+100H SHLD BUFAD ;for  Ratoff, Ward ; Christensen, (HRF) and (DJH). ; ; ; 08/06/78 Originally written to reconstruct blown disks on CBBS via ; remote access. - Ward L. Christensen ; ; ;----------------------------------------------------------------------- ; ; Sorry for the lack of comments in the code portion of this program, it ; it was just hacked together to satisfy my needs, but lots of other ; people found it useful. Its external documentation is good, but it's ; sadly lacking comments on the instructi VREAD+1 ;read DAD D SHLD VWRITE+1 ;write LDA VERFLG ORA A JZ DOCPM1 DAD D ;listst DAD D SHLD VSCTRN+1 ;sectran JMP HELLO ; DOCPM1 LHLD BDOS+1 MVI L,0 ;BDOS on page boundary XCHG LXI H,TRNOFF ;CP/M 1.4 sectran routine offset DAD D SHLD VSCTRN+1 LXI H,SKWOFF ;CP/M 1.4 skew table offset DAD D SHLD SECTBL ;set up skew table pointer ; HELLO CALL ILPRT DB CR,LF,'DISK UTILITY v8.6',CR,LF DB 'Universal Version under ',0 LDA MPMFLG ORA A JZ HELCPM MVI A,'M' . ; CLOCK DB 1 ;0= 2mhz, 1=4mhz, 2=6mhz DB 'DU.COM v8.6 10/24/83',1AH ;allow type du.com ; PASTCK LHLD BDOS+1 ;get pointer to BDOS entry MVI L,0 ;set HL=base of BDOS SPHL ;put stack there SHLD SETSTK+1 ;save for later lxi sp instr. MVI C,GVERS ;get CP/M version nr CALL BDOS SHLD VERFLG ;save version and MP/M flag MOV A,L ;...version numberr for a flag CPI 30H LXI H,10 ;CP/M 2.x dpb offset JC NOPLUS LXI H,12 ;CP/M 3.x dpb offset ; NOPLUS SHLD DPBOFS LXI H,3000H ;inrdbyte POP H CALL CTLCS ;abort? JZ PRMPTR ;..yes, read buffer ; ; ; Do we have to position in directory after find? ; LDA FINDFL ORA A JNZ POSDIR ;position in directory MOV A,M CPI CR JZ PRMPTR CPI ';' ;logical cr? INX H JZ PROMPT CALL UPCASE STA DUMTYP ;type of dump (a,d,h) ; ; ; Command dispatcher ; CPI '+' JZ PLUS CPI '-' JZ MINUS CPI '=' JZ SEARCH CPI '<' JZ SAVE CPI '>' JZ RESTOR CPI '#' JZ STATS CPI '?' JZ HELP CPI 'A' JZ DUMP ons. ; - Ward L. Christensen ; ;----------------------------------------------------------------------- ; ; System equates ; BASE EQU 0 ;(set to 4200H for TRS-80) ; FCB EQU BASE+5CH BDOS EQU BASE+5 PRINT EQU 9 GVERS EQU 12 RESETDK EQU 13 SELDK EQU 14 SRCHF EQU 17 ;search first SUSER EQU 32 GETDSK EQU 25 GETDPB EQU 31 ; BHOME EQU 8 ;BIOS function numbers BSELDSK EQU 9 BSETTRK EQU 10 BSETSEC EQU 11 BSETDMA EQU 12 BREAD EQU 13 BWRITE EQU 14 BSECTRN EQU 16 ; TRNOFF EQU 15 ; JMP HELMPM ; HELCPM MVI A,'C' ; HELMPM CALL TYPE CALL ILPRT DB 'P/M ',0 LDA VERFLG ORA A JNZ NO14 MVI A,14H ; NO14 PUSH PSW RAR RAR RAR RAR ANI 0FH ORI 30H CPI '9'+1 JC NOALPH ADI 'A'-'9' ; NOALPH CALL TYPE MVI A,'.' CALL TYPE POP PSW ANI 0FH ORI 30H CALL TYPE CALL ILPRT DB CR,LF,LF DB 'Type ? for help',CR,LF DB 'Type X to exit' DB CR,LF,0 CALL GETSTP ;set up parameters LXI H,BASE+80H ;to input buff MOV A,M ORA A JZ PRMPTR ;no command   CPI 'B' JZ BOOT CPI 'C' JZ CHG CPI 'D' JZ DUMP CPI 'F' JZ POSFIL CPI 'G' JZ POS CPI 'H' JZ DUMP CPI 'K' JZ SVFILE ; SAVE FILE FROM YANKED SECTORS CPI 'L' JZ LOGIN CPI 'M' JZ MAP CPI 'N' JZ NEWDSK CPI 'P' JZ PRNTFF CPI 'Q' JZ QUIET CPI 'R' JZ DOREAD CPI 'S' JZ POS CPI 'T' JZ POS CPI 'U' ;CP/M 2.x, 3.x only JZ USER CPI 'V' JZ VIEW CPI 'W' JZ DORITE CPI 'X' JZ XIT CPI 'Y' JZ YANK CPI 'Z' JZ SLEEP CPI '/' JZ REPEAT CPI 'OP H CALL SELECT JMP PROMPT ; ; ; Quiet mode ; QUIET STA QFLAG ;now quiet JMP PROMPT ; ; ; Repeat buffer contents ; REPEAT CALL DECIN ;nn specified? MOV A,D ORA E JZ NNN ;no. LHLD TOGO INX H ;test for first time MOV A,H ORA L ;was it 0FFFFH? JNZ NNN ;no, counting XCHG ;get count SHLD TOGO ;set count ; NNN LHLD TOGO XCHG LXI H,INBUF ;ready to repeat INX D ;test for 0ffffh MOV A,D ORA E JZ PROMPT ;continous DCX D ;count down DCX D ;make up foH H CALL DEC MVI A,9 CALL TYPE POP B CALL HEXZ CALL ILPRT DB CR,LF,'Sec/non-trk 00:',9,0 ; STAT2 LHLD SPT PUSH H CALL DEC MVI A,9 CALL TYPE POP B CALL HEXZ CALL ILPRT DB CR,LF,'Groups:',9,9,0 LHLD DSM PUSH H CALL DEC MVI A,9 CALL TYPE POP B CALL HEXB CALL ILPRT DB CR,LF,'Dir groups:',9,0 LHLD DIRGRP PUSH H CALL DEC MVI A,9 CALL TYPE POP B CALL HEXZ CALL ILPRT DB CR,LF,'Sec/group:',9,0 LDA BLM INR A MOV L,A MVI H,0 PUSH H CALL DERA A JNZ GETC ORI 1 ;no char, retn nz RET ; GETC CALL CONIN ANI 1FH ;allow ASCII CPI 'S'-40H CZ CONIN CPI 'C'-40H RET ;0 set if ctl-c ; ; ; Find our way at initialization ; GETSTP MVI C,GETDSK CALL BDOS ;get curnt dsk MOV C,A ;..we have to select JMP SELECT ;..to get the dph ; LOGIN CALL DOLOG JMP PROMPT ; DOLOG MOV A,M ;disk req? LXI D,0 CPI CR JZ LGNODK CPI ';' JZ LGNODK CALL UPCASE INX H SUI 'A' MOV C,A ; SELECT PUSH H MOV A,C STA DRIVE (' JZ TOGERA ; WHAT XRA A STA QFLAG CALL ILPRT DB '?',0 JMP PRMPTR ; ; ; TOGERA - toggle display of erased files ; TOGERA LDA TOGE CMA STA TOGE JMP PROMPT ; ; ; Exit - since CP/M+ doesn't do a warm boot at jump 0000, we have to ; reset the disk system! ; XIT MVI C,RESETDK CALL BDOS JMP BASE ; ; ; Memory full error ; MEMFUL XRA A STA QFLAG CALL ILPRT DB '+++ OUT OF MEMORY +++' DB CR,LF,0 JMP PRMPTR ; ; ; Print disk statistics ; STATS PUSH H CALL ILPRT r prev inx d XCHG SHLD TOGO MOV A,H ;all done? ORA L XCHG ;get back inbuf ptr JNZ PROMPT ;no, keep going JMP PRMPTR ;all done ; ; ; Set CP/M 2.x, 3.x user number ; USER LDA VERFLG ORA A JZ WHAT CALL DECIN ;get requested user no. MOV A,E CPI 32 ;valid? JNC WHAT MOV A,D ORA A JNZ WHAT MVI C,SUSER PUSH H ;save char pointer CALL BDOS ;set user no. POP H JMP PROMPT ; ; ; Toggle print flag ; PRNTFF LDA PFLAG XRI 1 STA PFLAG JMP PROMPT ; ; ; SleC MVI A,9 CALL TYPE POP B CALL HEXZ CALL ILPRT DB CR,LF,'Dir entries:',9,0 LHLD DRM INX H PUSH H CALL DEC MVI A,9 CALL TYPE POP B CALL HEXZ CALL CRLF POP H JMP PROMPT ; ; ; The following command resets the disk system thru CP/M, and may be ; usable for changing the disk density or format. This can only be done ; if your BIOS resets the auto-density select parameters at every track- ; zero access. ; NEWDSK PUSH H MVI C,RESETDK CALL BDOS LDA DRIVE MOV C,A P;remember later where we are CALL V3CHEK ;check for version 3.x JC VSELDK ;jump if 1.x or 2.x MVI E,0 ;set first call flag MVI A,BSELDSK ;select disk function CALL BDOS50 ;call BIOS via BDOS JMP VSELD3 ; VSELDK CALL $-$ ;addr filled in by 'INIT' LDA VERFLG ORA A ;if not CP/M 2.x ... JZ SELSKP ;..then skip this junk ; VSELD3 MOV A,H ORA L JZ WHAT ;select error MOV E,M ;get the sector table pointer INX H MOV D,M DCX H ;point to start of DPH XCHG SHLD SECTBL LXDB 'Statistics for drive ',0 LDA DRIVE ADI 'A' CALL TYPE MVI A,':' CALL TYPE CALL ILPRT DB CR,LF,'Tracks:',9,9,0 LHLD MAXTRK INX H PUSH H CALL DEC MVI A,9 CALL TYPE POP B CALL HEXZ CALL ILPRT DB CR,LF,'Sys tracks:',9,0 LHLD SYSTRK PUSH H CALL DEC MVI A,9 CALL TYPE POP B CALL HEXZ LHLD SPT00 XCHG LHLD SPT XCHG CALL SUBDE JC STAT1 CALL ILPRT DB CR,LF,'Sec/track:',9,0 JMP STAT2 ; STAT1 CALL ILPRT DB CR,LF,'Sec/trk 00:',9,0 LHLD SPT00 PUSep routine, in tenths of a second ; SLEEP CALL HEXIN ;get count if any MOV A,E ;any? ORA A JNZ SLEPLP MVI E,10 ; SLEPLP LXI B,8000 ;approximately .1 second at 2 MHz LDA CLOCK ORA A JZ SLEEP2 LXI B,16000 ;approximately .1 second at 4 MHz CPI 1 JZ SLEEP2 LXI B,24000 ;approximately .1 second at 6 MHz ; SLEEP2 DCX B MOV A,B ORA C JNZ SLEEP2 PUSH D CALL CTLCS POP D JZ PRMPTR DCR E JNZ SLEPLP JMP PROMPT ; ; ; Check for CTL-C or CTL-S ; CTLCS CALL CONST O  I H,10 ;offset to DPBPTR ; DPBOFS EQU $-2 ;modified during init DAD D MOV A,M ;pick up dpb pointer INX H ;..to use MOV H,M ;..as parameter MOV L,A ;..to logit ; SELSKP CALL LOGIT LHLD SYSTRK ;reset track and sector XCHG ;..to directory CALL SETTRK ;..on every LXI D,1 ;..login CALL SETSEC ;..change LHLD PHYSEC ;this logic will tell MOV A,H ;..if first sec ORA L ;..is physical 0 STA FIRST0 CALL CLCSUB POP H ; LGNODK CALL NORITE RET ; ; ; Read in the digroup ? JNC MAPND3 ; yes, POSGP2 can handle it LHLD SAVSEC ; no, use track/sector positioning SHLD CURTRK XCHG CALL SETSEC LHLD SAVTRK SHLD CURTRK XCHG CALL SETTRK CALL READ XRA A STA NOTPOS POP H JMP INQ MAPND3 LHLD GROUP XCHG JMP POSGP2 ; ; ; Print file name pointed to by HL ; MAPNAM CALL SPACE MOV A,H ORA L ;none? JZ NONAME MOV A,M ;see if alloc CPI 0E5H ;free? MVI A,' ' JNZ MPNSP1 MVI A,'(' ; MPNSP1 CALL TYPE PUSH H ;save pointer MOV A,XCHG JMP MAPRD1 ; MAPRD LHLD DIRGRP ; MAPRD1 CALL REDDIR ;read in directory MOV B,H MOV C,L ; MAPDF LDA DLMREQ ORA A ;delimiter required ? CNZ DELIM ; yes, doit CALL HEXB MVI A,'-' CALL TYPE MVI A,' ' STA DUPFLG CALL GETGRP ;get grp(c) to HL ; MAPCNT INX B ;next grp # PUSH H LHLD DSM ;get highest grp # INX H ;plus 1 for comparison MOV A,L ;when BC reaches DSM+1.. CMP C ;..then we have exceeded.. JNZ MAPC1 ;..the disk capacity.. MOV A,H CMP B ; MAPC1HLD DRM ;max dir entry # INX H ;make 1-relative SHLD FILECT LXI H,0 SHLD MFPTR LXI H,DIRECT ; GETGLP PUSH H ;save pointer to name PUSH H LDA TOGE ORA A JNZ SKERA INX H ; SKERA MOV A,M ;pick up dn byte POP H CPI 0E5H JZ GETGNF MOV A,M ;this code for CP/M+ only... CPI 21H ;suppress time/date field... JZ GETGNF LXI D,14 ;now get record count DAD D ;..S2 portion .. MOV A,M ;..is 0 in CP/M 1.4 ANI 0FH MOV E,A INX H MOV A,M ORA E JZ GETGNF MVI E,16 sk directory ; REDDIR PUSH H CALL NORITE ;positioning lost LHLD SYSTRK SHLD CURTRK LXI H,1 SHLD CURSEC LHLD DRM ;get dir size from dpb INX H ;make 1-relative CALL ROTRHL CALL ROTRHL ;divide by 4 (4 names/sector) MOV B,H MOV C,L LXI D,DIRECT ;DMA address ; RDIRLP PUSH B PUSH D MOV B,D MOV C,E LDA BDOS+2 ;check mem avail DCR A CMP D JC MEMFUL CALL SETDMA LHLD CURTRK XCHG CALL SETTRK LHLD CURSEC XCHG CALL SETSEC CALL READ CALL NXTSEC POP D PM CALL HEX ;show user number CALL SPACE INX H ;skip user byte PUSH B MVI B,8 CALL MAPN2 MVI A,'.' CALL TYPE MVI B,3 CALL MAPN2 LDA DUPFLG CALL TYPE ;space or star POP B MOV A,M ;get extent CALL HEX POP H MOV A,M CPI 0E5H MVI A,' ' JNZ MPNSP2 MVI A,')' ; MPNSP2 CALL TYPE ;")" if erased file JMP FLIP ; NONAME CALL ILPRT DB ' ++FREE++ ',0 ; FLIP LDA TWOUP XRI 1 STA TWOUP MVI A,0FFH STA DLMREQ ;delimiter now required RET ; DELIM LDA POP H JZ MAPEND ;..and we are done PUSH H CALL GETGRP ;get another POP D ;see if same CALL CTLCS JZ MAPND2 MOV A,D CMP H JNZ MAPDIF MOV A,E CMP L JZ MAPCNT ;same, continue ; ; ; Different file encountered ; MAPDIF DCX B CALL HEXB INX B XCHG CALL MAPNAM JMP MAPDF ; ; ; End of map ; MAPEND DCX B ;get last CALL HEXB CALL MAPNAM ; ; ; End of map - reposition to previous group ; MAPND2 CALL CRLF LHLD SYSTRK XCHG LHLD SAVTRK CALL SUBDE ;within a  ;first set for 8-bit grps LDA DSM+1 ORA A JZ SMALGP MVI E,8 ;nope, big groups ; SMALGP MOV D,A ;save group size indicator ; GETGL2 INX H ;pointing into dm field CALL GRPCMP ;compare BC group # against 1 dm field JNZ NOTGOT ;jump if found one ; ; ; Found the file ; PUSH H ;save group pointer LHLD MFPTR MOV A,H ORA L POP H XTHL ;get entry start and save pointer JZ MPFRST MVI A,'*' STA DUPFLG ; MPFRST SHLD MFPTR XTHL ;as they were ; NOTGOT DCR E ;else couOP B LXI H,80H DAD D XCHG DCX B MOV A,B ORA C JNZ RDIRLP LXI B,BASE+80H CALL SETDMA POP H RET ; ; ; Map the directory ; MAP CALL HEXIN PUSH H ;save inbuf ptr LHLD CURTRK SHLD SAVTRK ;save track ... LHLD CURSEC SHLD SAVSEC ; ... and sector MOV A,E ;get start ORA D ;nothing? JZ MAPRD ;..yes, dflt LHLD DIRGRP CALL SUBDE CMC ;start >= default ? JC MAPRD ;..no, default LHLD DSM ;get max CALL SUBDE ;start exceed max ? JC OUTLIM ;..yes, abort  TWOUP ORA A JNZ DELIM1 CALL CRLF JMP DELIM2 ; DELIM1 MVI A,':' CALL TYPE CALL SPACE ; DELIM2 XRA A STA DLMREQ ;delimiter was output RET ; ; ; Print name, length in B ; MAPN2 MOV A,M ANI 7FH ;strip possible 2.x attribute bit INX H CPI ' ' ;printable? JC MAPN2H ;..no, in hex CPI 7EH ;7E is leadin on some CRT's JC MAPN2A ; MAPN2H CALL BHEX JMP MAPN2Z ; MAPN2A CALL TYPE ; MAPN2Z DCR B JNZ MAPN2 RET ; ; ; Find which file group (BC) belongs to ; GETGRP L  nt down JNZ GETGL2 ;go test some more ; GETGNF POP H ;not this one! LXI D,32 ;so go to next DAD D XCHG LHLD FILECT ;there is limit to everything DCX H SHLD FILECT MOV A,H ORA L XCHG ;re-align JNZ GETGLP ; ; ; Get the allocation address, if any ; LHLD MFPTR RET ; ; ; Yank the current sector into memory at location YNKADR ; YANK LDA 7 ;get top byte of CCP pointer MOV B,A LDA YNKADR+1 ;get top byte of memory pointer CMP B JNC YMFULL ;if memory full, say so aH CALL HEX CALL CRLF JMP CLCGRP ; ; ; Get value from input buffer ; GETVAL MOV A,M CPI '<' ;hex escape? RNZ ;no, return ; ; ; "<<" means one "<" ; INX H MOV A,M CPI '<' RZ ; ; ; Got hex ; PUSH D CALL HEXIN ;get value CPI '>' ;proper delimiter? MOV A,E ;get value POP D JNZ WHAT ;error RET ; ; ; Read a byte at a time ; RDBYTE PUSH H LDA FTSW ;first read? ORA A JNZ READ1 LHLD BUFAD MOV A,L ORA A ;in buffer? JM NORD ;yes, skip read ; ;ROMPT ; NOSAVE XRA A STA QFLAG CALL ILPRT DB '++ NO "<" SAVE COMMAND ISSUED ++' DB CR,LF,0 JMP PRMPTR ; ; ; Move (HL) to (DE) length in B ; MOVE MOV A,M STAX D INX H INX D DCR B JNZ MOVE RET ; NORITE XRA A ;get 0 STA WRFLG ;cannot write now RET ; ; ; No match in search, try next character ; SRNOMT POP H CALL CTLCS ;abort? JNZ SEARCH ;..yes LXI H,INBUF MVI M,CR JMP CLCGRP ;show where stopped ; ; ; Search for character string ; SEARCH PUSH H ;save sALL ILPRT DB CR,LF,TAB,'++ EOF ++',CR,LF,0 ; VEWEND POP H CALL CRLF JMP PROMPT ; ; ; Dump in hex or ascii ; DUMP LDA WRFLG ORA A JNZ DUMPOK ; BADDMP XRA A STA QFLAG CALL ILPRT DB '++ Can''t dump, no sector read ++',CR,LF,0 ; EXPL XRA A STA QFLAG CALL ILPRT DB 'Use G command following F,',CR,LF DB 'or R or S following T',CR,LF,0 JMP PRMPTR ; DUMPOK MOV A,M CPI ';' JZ DUMPDF ;default CPI CR JNZ DMPNDF ; ; ; Use default ; DUMPDF LXI B,BASE+80H LXI D,0FFH nd abort yank LDA WRFLG ;check if a read has been done ORA A JZ BADW ;if no read, then cannot yank, so abort PUSH H LHLD YNKADR ;move sector into yank memory XCHG LXI H,BASE+80H MVI B,128 CALL MOVE CALL ILPRT ;tell where last byte is DB 'LAST ADDR=',0 LHLD YNKADR ;calculate last byte LXI B,80H DAD B SHLD YNKADR ;save last byte+1 for next yank DCX H MOV A,H CALL HEX MOV A,L CALL HEX CALL ILPRT DB CR,LF,0 POP H JMP PROMPT ; YMFULL XRA A ;memory full, so ; Have to read ; CALL NXTSEC ; READ1 XRA A STA FTSW ;not first read LHLD CURSEC XCHG CALL SETSEC LHLD CURTRK XCHG CALL SETTRK CALL READ CALL CLCSUB LXI H,BASE+80H ; NORD MOV A,M INX H SHLD BUFAD POP H RET ; ; ; View the file in ASCII starting at current sector, stepping through ; the disk ; VIEW LDA WRFLG ORA A JZ BADDMP CALL HEXIN ;get displ if any PUSH H MOV A,E ORA A JNZ VIEWLP INR E ;dflt=1 ; VIEWLP LXI H,BASE+80H ;to data ; VEWCHR CALL tring pointer ; SRCHL CALL RDBYTE ;get a byte MOV B,A ;save it MOV A,M ;check next match character. CPI '<' ;will it be hex? MOV A,B ;restore disk character JZ SRCHL1 ANI 7FH ;next character is ASCII...strip bit 7 ; SRCHL1 PUSH PSW CALL GETVAL ;get search value MOV B,A POP PSW CMP B ;match? JNZ SRNOMT ;no match INX H MOV A,M ;done? CPI CR JZ SREQU CPI ';' JNZ SRCHL ; ; ; Got match ; SREQU: XRA A STA QFLAG CALL ILPRT DB '= AT ',0 LDA BUFAD ANI 7F JMP DUMP1 ; DMPNDF CALL DISP MOV B,D MOV C,E CPI CR JZ DUMP1 CPI ';' JZ DUMP1 INX H ;skip ',' CALL DISP ; ; ; BC = start, DE = end ; DUMP1 PUSH H ;save command pointer MOV H,B MOV L,C ; DUMPLP MOV A,L ANI 7FH CALL HEX CALL SPACE CALL SPACE LDA DUMTYP CPI 'A' JZ DUMPAS PUSH H ;save start ; DHEX MOV A,M CALL HEX MOV A,L ANI 3 CPI 3 CZ SPACE MOV A,L ANI 7 CPI 7 CZ SPACE MOV A,E CMP L JZ DPOP INX H MOV A,L ANI 0FH JNZ DHEX ; DP say so STA QFLAG ;set to not quiet mode first CALL ILPRT DB '++ YANK MEMORY FULL ++' DB CR,LF,0 JMP PROMPT ; ; ; Save the current sector ; SAVE LDA WRFLG ORA A JZ BADW ;none to save PUSH H LXI H,BASE+80H LXI D,SAVBUF MVI B,128 CALL MOVE MVI A,1 ;..show STA SAVEFL ;..saved exists POP H JMP PROMPT ; ; ; Restore the current sector ; RESTOR LDA SAVEFL ORA A JZ NOSAVE ;none to save PUSH H LXI H,SAVBUF LXI D,BASE+80H MVI B,128 CALL MOVE POP H JMP PCTLCS JZ VEWEND MOV A,M CPI 1AH JZ VEWEOF ANI 7FH CPI 7EH JNC VIEWHX ;show rubout and tilde as hex CPI ' ' JNC VIEWPR CPI CR JZ VIEWPR CPI LF JZ VIEWPR CPI TAB JZ VIEWPR ; VIEWHX MOV A,M ;not ascii...print as CALL BHEX JMP VIEWNP ; VIEWPR CALL TYPE ; VIEWNP INR L JNZ VEWCHR DCR E JZ VEWEND PUSH D ;save count CALL NXTSEC LHLD CURSEC XCHG CALL SETSEC LHLD CURTRK XCHG CALL SETTRK CALL READ POP D ;restore count JMP VIEWLP ; VEWEOF C  OP CALL CTLCS JZ PRMPTR LDA DUMTYP CPI 'H' JZ DNOAS ;hex only POP H ;get start address ; DUMPAS CALL ASTER ; DCHR MOV A,M ANI 7FH CPI ' ' JC DPER CPI 7EH JC DOK ; DPER MVI A,'.' ; DOK CALL TYPE MOV A,E CMP L JZ DEND INX H MOV A,L ANI 0FH JNZ DCHR ; DEND CALL ASTER CALL CRLF PUSH D CALL CTLCS POP D JZ PRMPTR MOV A,E CMP L JNZ DUMPLP POP H JMP PROMPT ; DNOAS POP B CALL CRLF MOV A,E CMP L JNZ DUMPLP POP H JMP PROMPT ; ; ; Positier LXI D,FCB XRA A STAX D INX D MVI B,8 CALL MVNAME MVI B,3 CALL MVNAME LXI D,FCB MVI C,SRCHF PUSH H CALL BDOS INR A JNZ FLOK STA DIRPOS ;group 0 if not found CALL ILPRT DB '++ FILE NOT FOUND ++',CR,LF,0 POP H JMP PROMPT ; FLOK DCR A STA DIRPOS ;save position in directory ANI 3 MOV L,A MVI H,0 DAD H ;x32 bytes/entry DAD H DAD H DAD H DAD H LXI D,BASE+80H DAD D ;HL points to entry LXI D,32 XCHG DAD D XCHG MVI A,'D' STA DUMTYP JMPP/M-2.x) ; POSDIR PUSH H ;save INBUF LHLD BSH XRA A STA FINDFL ;cancel position request LDA DIRPOS ;get position RAR RAR PUSH PSW ANA H STA GRPDIS POP PSW ; POSDLP RAR DCR L JNZ POSDLP ANI 1 ;get group MOV L,A ;setup for POSGP2 MVI H,0 SHLD GROUP XCHG JMP POSGP2 ;position to it ; POSGPH CALL HEXIN ; POSGRP PUSH H LHLD DSM CALL SUBDE POP H JC OUTLIM XCHG SHLD GROUP XCHG XRA A STA GRPDIS PUSH H ; POSGP2 CALL GTKSEC CALL SETTRK XCHG e sequential memory on disk ; ; As CP/M v 3.0 does not have a SAVE function, one has been added here. ; ; Syntax is: ; Kdu:filename.ext ; ^^^ ^ ^ ; ||| | +-- file extension (0-3 characters) ; ||| +-------- file name (1-8 characters) ; ||+--------------- user # (or none) ; |+---------------- drive designation (A-P or none) ; +----------------- DU command ; ; Drive and user may be omitted. If so, omit the colon as well. Drive ; must be specified if the user is. Ion ; POS PUSH PSW MOV A,M CPI ';' JZ POSINQ CPI CR JNZ POSOK ; POSINQ POP PSW JMP INQ ; POSOK POP PSW CPI 'T' JZ POSTKD CPI 'S' JZ POSSCD CPI 'G' JZ POSGPH JMP WHAT ; POSTKD CALL DECIN ; POSTRK PUSH H LHLD MAXTRK CALL SUBDE POP H JC OUTLIM CALL SETTRK CALL NORITE ;track does not read MVI A,1 STA NOTPOS ;show not positioned JMP CLCGRP ; POSSCD CALL DECIN MOV A,D ORA E JZ WHAT ;do not allow sector 0 ; POSSEC PUSH H LHLD SPT CALL CHK00 CA DUMPLP ;which pops H ; MVNAME MOV A,M ;get the character CPI '.' ;see if extension is next JZ MVIPAD ;if so, pad end of file name CPI ':' ;else, see if drive designation end JZ MVIPAD ;yes, advance pointer, &c. CPI CR ;carriage return? JZ PAD ;end of line means end of file name CPI ';' ;else, see if delimiter JZ PAD ;if so, same as CR CALL UPCASE ;otherwise convert to upper case STAX D ;save the character in FCB INX H ;point to next character INX D ;point to next FCBCALL SETSEC CALL READ XRA A STA NOTPOS ;now positioned POP H JMP INQ ; GTKSEC MOV H,D MOV L,E LDA BSH ; GLOOP DAD H DCR A JNZ GLOOP LDA GRPDIS ADD L ;cannot carry MOV L,A ; ; ; Divide by nr of sectors, quotient=track, remainder=sector ; XCHG LHLD SPT CALL NEG XCHG LXI B,0 ; DIVLP INX B DAD D JC DIVLP DCX B XCHG LHLD SPT DAD D PUSH H LHLD SYSTRK DAD B XCHG POP H INX H RET ; POSFIL CALL NORITE MVI A,1 STA FINDFL ;so we position latf the user # is omitted, the cur- ; rent user is used. If the drive is omitted, the current CP/M default ; drive is used. ; ; This function saves the current contents of sequential memory into a ; disk file. The contents of sequential memory are determined by the ; 'yank' function, and the pointer of that function is used here. If ; nothing has been yanked, you get an error. Once the file has been ; saved, the 'yank' pointer is re-initialized to its original value ; (3000H). Control is returnLL SUBDE POP H JC WHAT CALL SETSEC CALL READ XRA A STA NOTPOS ;positioned ok ; CLCGRP CALL CLCSUB JMP INQ ; ; ; Calculate group from track and sector ; CLCSUB PUSH H LHLD SYSTRK XCHG LHLD CURTRK CALL SUBDE XCHG LHLD SPT CALL MULT XCHG LHLD CURSEC DCX H DAD D LDA BLM MOV B,A MOV A,L ANA B STA GRPDIS LDA BSH MOV B,A ; CLCLOP CALL ROTRHL DCR B JNZ CLCLOP SHLD GROUP POP H RET ; ; ; Position in the directory after a find (does not work in C character DCR B ;count down max # of characters JNZ MVNAME ;loop if more to go MOV A,M ;advance pointer to next delimiter CPI CR ;CR is end of line delimiter RZ CPI ';' ;semi-colon is parameter delimiter RZ INX H CPI '.' ;period is file name delimiter RZ CPI ':' ;colon is drive/user delimiter RZ JMP WHAT ; MVIPAD INX H ; PAD MVI A,' ' ; PAD1 STAX D INX D DCR B JNZ PAD1 RET ; ;----------------------------------------------------------------------- ; Sav  ed to DU. ; ; ; Turn free-form file name string into file control block ; SVFILE XRA A STA UZER ;default to current user LXI D,FCB ;point to file control block STAX D ;default to current drive INX D MVI B,8 ;name is 8 chrs. (drive is 0-3 chrs.) CALL MVNAME ;get it, with padding DCX H ;take a look at the delimiter MOV A,M INX H CPI ':' ;was it drive/user? JNZ NODRV ;no, assume it was the name PUSH H ;save file name address LXI H,FCB+1 ;ok then, start with the drive #  CALL BDOS INR A ;see if 0ffh (open error) JNZ SV2A CALL ILPRT DB 'No dir space!',CR,LF,0 JMP ENDSV ; SV2A LXI H,3000H ;starting DMA address SHLD YDN XRA A STA SCOUNT ; SV3 LHLD YDN ;get starting location XCHG MVI C,26 ;set dma address CALL BDOS LXI D,FCB ;get file MVI C,21 ;write sequential CALL BDOS ANA A JZ SV4 CALL ILPRT DB 'Write error',CR,LF,0 JMP ENDSVC ; SV4 LXI H,SCOUNT ;advance sector count INR M LHLD YDN LXI D,80H DAD D ;point to next sething is there ; LHLD YNKADR ;get address of yank pointer MOV A,H ;compare with start of sequential memory CPI 30H ;starts at 3000 JNZ SV1 MOV A,L ANA A JNZ SV1 CALL ILPRT ;send error message DB 'Empty!',CR,LF,0 JMP ESVFIL ; ; ; Set the user number ; SV1 MVI E,0FFH ;see what current user is MVI C,32 CALL BDOS STA CUZER ;save it LDA UZER ;default user? ANA A JZ UZERD ;yes, no change DCR A ;correct offset that was added MOV E,A MVI C,32 CALL BDOS ;set neset default MOV A,M ;get character CPI CR ;CR? JZ MINGO ;..yes, default=1 CPI ';' JZ MINGO CALL HEXIN ;..no, get ## MOV A,D ORA E JZ WHAT ; MINGO PUSH H LHLD CURSEC DCX H MOV A,H ORA L JNZ MINOK LHLD CURTRK MOV A,H ORA L JNZ SEASH LHLD MAXTRK ;wrap to end of disk SHLD CURTRK LHLD MAXSEC JMP MINOK ; SEASH DCX H SHLD CURTRK LHLD SPT CALL CHK00 ;check for track 00 ; MINOK SHLD CURSEC POP H DCX D MOV A,D ORA E JNZ MINGO JMP PLUSMI ; ;  MOV A,M CALL UPCASE ;convert to upper case SUI 40H ;make 'A'-'P' into 1-16 STA FCB ;save as drive # INX H ;point to user number MOV A,M ;see if user # CPI ' ' JZ UZE3 ;no, skip it MVI B,2 ;no more than 2 digits MVI C,0 ; UZE1 MOV A,M ;get a digit CPI ' ' ;see if colon JZ UZE2 ;if so, finished MOV A,C ;get current value ADD A ;*2 ADD A ;*4 ADD C ;*5 ADD A ;*10 MOV C,A ;save it MOV A,M ;get the digit SUI 30H ;make into number ADD C ;add to currentctor address SHLD YDN XCHG ;save in DE LHLD YNKADR ;get next yank address MOV A,D CMP H ;see if we got there yet JC SV3 ;not yet MOV A,E CMP L JC SV3 ;no, do another LHLD SCOUNT MVI H,0 CALL DEC ;print # of sectors CALL ILPRT DB ' sectors written.',CR,LF,0 LXI H,3000H SHLD YNKADR ;reset yank address ; ENDSVC LXI D,FCB MVI C,16 ;close file CALL BDOS ; ENDSV LDA CUZER ;get original user # MOV E,A MVI C,32 ;set it CALL BDOS ; ESVFIL LHLD ENDVAL ;restow user ; ; ; Create the file ; UZERD MVI C,15 ;open file LXI D,FCB CALL BDOS INR A ;see if 0ffh (open error) JZ SV2 ;if error, file does not exist CALL ILPRT DB 'File exists! Delete? ',0 CALL CONIN ;see what the answer is CALL UPCASE CPI 'Y' ;see if yes JZ DELF ;yes, delete the file CALL ILPRT DB 'N',CR,LF,0 JMP ENDSV ;restore user # and return ; DELF CALL ILPRT DB 'Y',CR,LF,0 LXI D,FCB MVI C,19 ;delete file CALL BDOS ; SV2 MVI C,22 ;make file LXI D,FCB ; Go to next sector ; NXTSEC PUSH H PUSH D LHLD CURSEC INX H XCHG LHLD SPT CALL CHK00 ;check for track 00 CALL SUBDE XCHG JNC CHKLST LHLD CURTRK INX H XCHG LHLD MAXTRK CALL SUBDE JNC TRASK LXI D,0 ;wrap to start of disk ; TRASK XCHG SHLD CURTRK LXI H,1 ; NEXTOK SHLD CURSEC POP D POP H RET ; CHKLST PUSH H ;save new current sector LHLD MAXTRK ;are we on last track? XCHG LHLD CURTRK CALL SUBDE POP H ;get new current sector back JC NEXTOK ;if n value MOV C,A INX H DCR B JNZ UZE1 ;do another ; UZE2 MOV A,C ;get user number INR A ;add displacement (0 means no change) STA UZER ; UZE3 MVI B,8 ;That was the drive/user. Now get ; the file name for real. POP H ;restore file name pointer LXI D,FCB+1 ;point to file name again CALL MVNAME ; NODRV MVI B,3 ;get extension, if any CALL MVNAME SHLD ENDVAL ;save location of end of command MVI B,24 ;pad the rest of the dcb with 0 XRA A CALL PAD1 ; ; ; See if anyre end-of-line location JMP PROMPT ;return ; PLUS LXI D,1 ;default to 1 sector MOV A,M ;get next character CPI CR ;CR? JZ PLUSGO ;..yes, default to 1 CPI ';' JZ PLUSGO CALL HEXIN ;get # MOV A,D ORA E JZ WHAT ; PLUSGO CALL NXTSEC DCX D ;more to go? MOV A,D ORA E JNZ PLUSGO ;..yes ; ; ; Ok, incremented to sector, setup and read ; PLUSMI PUSH H LHLD CURSEC XCHG CALL SETSEC LHLD CURTRK XCHG CALL SETTRK POP H CALL READ JMP CLCGRP ; MINUS LXI D,1 ;  ot on last track XCHG ;else, see if we are past LHLD MAXSEC ;..last CP/M usable sector CALL SUBDE XCHG JNC NEXTOK ;if not past last usable sector LXI D,0 ;else, wrap to start of disk JMP TRASK ; ; ; Boot track processing ; BOOT CALL DECIN MOV A,D ORA E ;operand given ? JNZ BOOT1 ; yes, check it out XCHG LHLD SPT ; no, give non-track 00 spt XCHG JMP BOOT2 ; BOOT1 PUSH H LHLD SPT CALL SUBDE ;operand <= non-track 00 spt ? POP H JC WHAT ; no, give bad news JZ PROMPT CPI ',' ;delimiter? JZ CHGHCM PUSH D SHLD HEXAD ;in case 'THRU' CALL HEXIN ;positions to delim MOV A,E ;get value POP D ;..address PUSH PSW ;save value LDAX D ;get old CALL HEX ;echo in HEX POP PSW ;get new STAX D ;save new MOV A,C ;see if 'THRU' ORA A JZ CHHNTH ;..no. CMP E ;..yes, done? JZ PROMPT LHLD HEXAD ;..no: more ; CHHNTH INR E JNZ CHGHEX MOV A,M CPI CR JZ PROMPT CPI ';' JZ PROMPT JMP WHAT ; DOREAD LDA NOTPOS ORA A JN INX H CALL DISP ;get, validate display to DE INX H LXI B,0 ;show no 'THRU' address CPI '-' ;test delimiter from display JNZ CHGNTH ;no through PUSH D ;save from CALL DISP ;get through INX H ;skip end delimitera MOV B,D MOV C,E ;BC = through POP D ;get from JMP CHGAH ; CHGNTH CPI ',' JNZ WHAT ; CHGAH POP PSW CPI 'H' JZ CHGHEX CPI 'A' JNZ WHAT ; ; ; Change ASCII ; CHGALP MOV A,M CPI CR JZ PROMPT CPI ';' JZ PROMPT LDAX D CPI ' ' JC CHGAHX CPMOV A,M CPI 1 ;pause? JNZ ILPOK CALL CONIN CPI 3 ;abort? JZ PRMPTR JMP ILPNX ; ILPOK CALL TYPE ; ILPNX INX H MOV A,M ORA A JNZ ILPLP INX H XTHL RET ; ; ; Display calls HEXIN, and validates a sector displacement, then con- ; verts it to an address ; DISP CALL HEXIN PUSH PSW ;save delimiter MOV A,D ORA A JNZ BADISP MOV A,E ORA A JM BADISP ADI 80H ;to point to buffer at BASE+80h MOV E,A MVI D,BASE/256 POP PSW ;get delimiter RET ; BADISP XRA A  ; BOOT2 XCHG SHLD SPT00 ;save track 00 spt XCHG JMP PROMPT ; ; ; Check for Boot track ; CHK00 PUSH H ;save spt on stack LHLD CURTRK MOV A,H ORA L ;track 00 ? JNZ CHK001 ; no, leave spt as-is LHLD SPT00 ; yes, get spt for track 00 XTHL ;replace on stack ; CHK001 POP H ;get spt (trk 00 or non-trk 00) back RET ; ; ; Tell what group, displacement, track, sector, physical sector ; INQ CALL INQSUB JMP PROMPT ; ; ; Position inquiry subroutine executed via: G S or TZ CANTRD CALL READ JMP PROMPT ; CANTRD XRA A STA QFLAG ;not quiet CALL ILPRT DB '++ Can''t read - not positioned ++',CR,LF DB 'Position by:',CR,LF DB 9,'Track then Sector, or',CR,LF DB 9,'Group',CR,LF,0 JMP PROMPT ; DORITE CALL WRITE JMP PROMPT ; BHEX PUSH PSW MVI A,'<' CALL TYPE POP PSW CALL HEX MVI A,'>' CALL TYPE RET ; HEXB LDA DSM+1 ORA A JZ HEXX MOV A,B CALL HEX ; HEXX MOV A,C JMP HEX ; HEXZ MOV A,B ORA A CNZ HEX MOV A,C ; HEX PUSH PSW I 7EH JNC CHGAHX JMP CHGA2 ; CHGAHX CALL BHEX JMP CHGA3 ; CHGA2 CALL TYPE ; CHGA3 SHLD BACK ;in case "thru" CALL GETVAL ;ASCII or STAX D ;update character INX H ;to next input character ; ; ; See if 'THRU' requested ; MOV A,C ORA A JZ CHANTH CMP E ;done?.. JZ PROMPT ;..yes LHLD BACK ; CHANTH INR E JNZ CHGALP MOV A,M CPI CR JZ PROMPT CPI ';' JZ PROMPT JMP WHAT ; ; ; Change HEX ; CHGHCM INX H ; CHGHEX MOV A,M CPI CR JZ PROMPT CPI ';' STA QFLAG CALL ILPRT DB '++ BAD DISPLACEMENT (NOT 0-7F) ++' DB CR,LF,0 JMP PRMPTR ; DHIN INX H ;skip '#' ; HEXIN LXI D,0 MOV A,M CPI '#' ;decimal? JZ HDIN ;make decimal ; HINLP MOV A,M CALL UPCASE CPI CR RZ CPI ';' RZ CPI ',' RZ CPI '-' ;'THRU'? RZ CPI '>' RZ INX H CPI '0' JC WHAT CPI '9'+1 JC HINNUM CPI 'A' JC WHAT CPI 'F'+1 JNC WHAT SUI 7 ; HINNUM SUI '0' XCHG DAD H DAD H DAD H DAD H ADD L MOV L,A XCHG JMP HINLP ; HDIN (with no operands) ; INQSUB PUSH H LHLD SYSTRK XCHG LHLD CURTRK CALL SUBDE JC NOGRP CALL ILPRT DB 'G=',0 LHLD GROUP MOV B,H MOV C,L CALL HEXB MVI A,':' CALL TYPE LDA GRPDIS CALL HEX CALL ILPRT DB ', ',0 ; NOGRP CALL ILPRT DB 'T=',0 LHLD CURTRK CALL DEC CALL ILPRT DB ', S=',0 LHLD CURSEC CALL DEC CALL ILPRT DB ', PS=',0 LHLD PHYSEC CALL DEC CALL CRLF POP H RET ; CHG MOV A,M ;get type (HEX, ASCII) CALL UPCASE PUSH PSW ;save "H" or "A" RAR RAR RAR RAR CALL NIBBL POP PSW ; NIBBL ANI 0FH CPI 10 JC HEXNU ADI 7 ; HEXNU ADI '0' JMP TYPE ; ; ; Decimal output routine ; DEC PUSH B PUSH D PUSH H LXI B,-10 LXI D,-1 ; DECOU2 DAD B INX D JC DECOU2 LXI B,10 DAD B XCHG MOV A,H ORA L CNZ DEC MOV A,E ADI '0' CALL TYPE POP H POP D POP B RET ; SPACE MVI A,' ' JMP TYPE ; ASTER MVI A,'*' JMP TYPE ; ; ; Inline print routine ; ILPRT XTHL ; ILPLP CALL CTLCS ;abort? JZ PRMPTR    INX H ;skip '#' ; DECIN LXI D,0 MOV A,M CPI '#' JZ DHIN ; DINLP MOV A,M CALL UPCASE CPI CR RZ CPI ';' RZ CPI ',' RZ CPI '-' ;'THRU'? RZ CPI '>' ;escape ? RZ ; yup INX H CPI '0' JC WHAT CPI '9'+1 JNC WHAT SUI '0' PUSH H MOV H,D MOV L,E DAD H ;x2 DAD H ;x4 DAD D ;x5 DAD H ;x10 ADD L MOV L,A MOV A,H ACI 0 MOV H,A XCHG POP H JMP DINLP ; ; ; Read in a console buffer full ; RDBUF CALL ILPRT DB CR,LF,':',0 ; RDBF1 LXI H,ILDA QFLAG ORA A PUSH B ;save chr. (some CBIOS clobber the reg.) VCONOT CZ $-$ ;address filled in by 'INIT' POP B ;retrieve the char. ; ; ; Update column used in tab expansion ; MOV A,C ;get character CPI CR JNZ TYPNCR MVI A,0 STA TABCOL JMP TYPLST ; TYPNCR CPI ' ' ;CTL-character? JC TYPLST ;..no change in column LDA TABCOL INR A STA TABCOL ; TYPLST LDA PFLAG ANI 1 CNZ LIST ;from 'C' reg. ; TYPRET POP H POP D POP B RET ; LIST PUSH B ;saved regs PUS POP B RET ; BSMSG DB BS,' ',BS,'$' ; ; ; Got CTL-R, retype ; RDCTLR MVI M,CR CALL CRLF LXI H,INBUF MVI B,0 ; RDCRL MOV A,M CPI CR JZ RDBLP CALL TYPE INR B INX H JMP RDCRL ; ; ; Got CTL-U or backup to beginning of line. ; RDCTLU MVI A,'^' CALL TYPE MVI A,'U' CALL TYPE JMP RDBUF ; CRLF MVI A,CR CALL TYPE MVI A,LF JMP TYPE ; UPCASE CPI 60H RC ANI 5FH ;make upper case RET ; CONST PUSH B PUSH D PUSH H VCONST CALL $-$ ;address filled in by 'INI PUSH D LDA PSH ;physical sector shift count ORA A ;check for no shift JZ SETSE1 ;jump if none MOV D,A ;loop count in 'D' ; SETSE2 CALL ROTRHL ;shift sector number right DCR D ;decrement loop count JNZ SETSE2 ;loop until done ; SETSE1 POP D INX H ;scale sector # to 1 MOV B,H ;repeat number in BC MOV C,L ; ; ; Moved next label up 1 line to remove 2.2 stack crash ; SETSE0 POP PSW ;restore flags JNC NOTSYS LDA FIRST0 ;see if first sector is 0 ORA A JNZ GSTSEC ;no,NBUF MVI B,0 ; RDBLP CALL CONIN MOV C,A ;save for bs test ; ; ; Evaluate control characters ; CPI 'U'-40H JZ RDCTLU CPI CR JZ RDCR CPI 'H'-40H JZ RDBS CPI 7FH JZ RDBS CPI 'R'-40H JZ RDCTLR CPI 'X'-40H JZ RDCTLX CPI ' ' JC RDBLP MOV M,A INX H INR B JM FULL CALL TYPE JMP RDBLP ; FULL DCR B DCX H MVI A,'*' ;signal we are full CALL TYPE JMP RDBLP ; ; ; Got CR ; RDCR MOV M,A ;save it CALL TYPE ;echo it MVI A,LF ;echo.. CALL TYPE ;..lf H D PUSH H VLIST CALL $-$ ;address filled in by 'INIT' POP H POP D POP B RET ; HOME PUSH H CALL V3CHEK ;check for version 3.x JC VHOME ;for 1.x or 2.x MVI A,BHOME ;BIOS function number CALL BDOS50 ;call BIOS via BDOS POP H RET ; VHOME CALL $-$ ;address filled in by 'INIT' POP H RET ; ; ; Set track # in DE ; SETTRK PUSH H LHLD MAXTRK CALL SUBDE POP H JC OUTLIM XCHG SHLD CURTRK XCHG MOV B,D MOV C,E PUSH H CALL V3CHEK ;check for version 3.x JC T' POP H POP D POP B RET ; CONIN PUSH B PUSH D PUSH H VCONIN CALL $-$ ;address filled in by 'INIT' POP H POP D POP B RET ; ; ; Console out with tab expansion ; TYPE PUSH B PUSH D PUSH H MOV C,A ;for output routine CPI TAB JNZ TYPE1 ; TYPTAB MVI A,' ' CALL TYPE LDA TABCOL ANI 7 JNZ TYPTAB JMP TYPRET ; ; ; Filter out control characters to prevent garbage during view of file ; TYPE1 CPI ' ' JNC TYPEQ CPI CR JZ TYPEQ CPI LF JNZ TYPNCR ; TYPEQ  jump away DCX H ;yes, so decrement JMP GSTSEC ;..requested, then go ; NOTSYS LHLD SECTBL XCHG DCX B CALL V3CHEK ;check for version 3.x JC VSCTRN ;for 1.x or 2.x MVI A,BSECTRN ;BIOS function number CALL BDOS50 ;call BIOS via BDOS JMP VSCTR3 ; VSCTRN CALL $-$ ;addr filled in by 'INIT' ; VSCTR3 LDA SPT+1 ;if spt<256 (hi-ord = 0) ORA A ; then force 8-bit translation JNZ VSCTR1 ; else keep all 16 bits MOV H,A ; VSCTR1 LDA VERFLG ;see if version 2.x ORA A ;set flags  LXI H,INBUF RET ; ; ; Got delete or bs, echo if bs ; RDBS XRA A ;at front.. ORA B ;..of line? JZ RDCTLU ;..yes, echo ^u DCX H DCR B MOV A,C CPI 'H'-40H ;bs? JZ BACKUP ;echo the backspace character MOV A,M ;echo.. CALL TYPE ;..deleted character JMP RDBLP ; BACKUP CALL WIPER JMP RDBLP ; RDCTLX INR B ; RDCX1 DCR B JZ RDBF1 CALL WIPER JMP RDCX1 ; WIPER PUSH B PUSH D PUSH H LXI D,BSMSG ;backspace, space, backspace MVI C,PRINT CALL BDOS POP H POP DVSETRK ;for 1.x or 2.x MVI A,BSETTRK ;BIOS function number CALL BDOS50 ;call BIOS via BDOS POP H RET ; VSETRK CALL $-$ ;addrress filled in by 'INIT' POP H RET ; SETSEC PUSH H PUSH D LHLD SYSTRK XCHG SHLD CURSEC LHLD CURTRK CALL SUBDE POP B MOV H,B MOV L,C PUSH PSW ;save flags CALL V3CHEK ;check for version 3.x JC SETSE0 ;for 1.x or 2.x DCX H ;scale sector # to 0 LDA PHM ;physical sector mask ANA L ;a=log. sector in buffer STA LOGSEC ;save for later    JNZ GSTSEC ;jump if CP/M 2.x MVI H,0 ;CP/M 1.4 good to only 8 bits MOV L,C ;most BIOS return the ; physical sec # in register 'C' GSTSEC SHLD PHYSEC ;this may be reduntant in most 1.4 MOV B,H ; versions, but should cause no MOV C,L ; problems CALL V3CHEK ;check for version 3.x JC VSTSEC ;for 1.x or 2.x MVI A,BSETSEC ;BIOS function number CALL BDOS50 ;call BIOS via BDOS POP H RET ; VSTSEC CALL $-$ ;address filled in by 'INIT' POP H RET ; OUTLIM XRA A STA QFLA;HL=sect addr, BC=len ; READ3L DAD B ;offset to desired sector DCR A ;count down log sector number JNZ READ3L ;loop until done RET ;HL=sect addr, BC=len ; V3CHEK LDA VERFLG ;check for version 3.x CPI 30H ;carry set if not 3.x RET ; BDOS50 SHLD HLREG ;save regs in parameter block XCHG SHLD DEREG MOV H,B MOV L,C SHLD BCREG STA FUNC LXI D,FUNC ;point to parameter block MVI C,50 ;BDOS function number JMP BDOS ; ; ;*************************************************** BADW XRA A STA QFLAG CALL ILPRT DB '++ CANNOT WRITE UNLESS READ ISSUED ++' DB CR,LF,0 JMP EXPL ; PWRITE PUSH H MVI C,1 ;force write type 1 in case 2.x deblock CALL V3CHEK ;check for version 3.x JC VWRITE ;for 1.x or 2.x CALL READ3 ;get physical sector JNZ WRERR ;if error XCHG ;sector address to DE LHLD DMAADR ;DMA address to HL CALL MOVE ;move data to buffer MVI A,BWRITE ;BIOS write function CALL BDOS50 ;call BIOS via BDOS JMP VWRIT3 ;write done ; VWRITE CALL  track' DB CR,LF,CR,LF,CR,LF,CR,LF,CR,LF,CR,LF,CR,LF DB '[More]' DB 1,CR,' ',CR,LF,CR,LF DB 'CHANGE:',CR,LF,CR,LF DB 'CHaddress,byte,byte... (hex)',CR,LF DB 'CAaddress,data... (ASCIIx',CR,LF DB ' Allowed for imbedded hex',CR,LF DB 'CHfrom-through,byte, e.g. CH0-7F,E5',CR,LF DB 'CAfrom-through,byte',CR,LF DB 'D[ff,tt] Dump (hex+ASCII)',CR,LF DB 'Fn.t Find file',CR,LF DB 'Gnn CP/M Allocation Group nn',CR,LF DB 'H[ff,tt] hex dump',CR,LF DB 'K[du:]filenamG CALL ILPRT DB '++ Not within tracks 0-',0 PUSH H LHLD MAXTRK CALL DEC POP H CALL ILPRT DB ' ++' DB CR,LF,0 CALL NORITE JMP PRMPTR ; SETDMA PUSH H ;save HL MOV H,B ;get DMA address in HL MOV L,C SHLD DMAADR ;save for later POP H ;restore HL CALL V3CHEK ;check for version 3.x JC VSTDMA ;for 1.x or 2.x MVI A,BSETDMA ;BIOS function number JMP BDOS50 ;call BIOS via BDOS ; VSTDMA JMP $-$ ;address filled in by 'INIT' ; READ MVI A,1 STA WRFLG PUSH H CALL V******************** ; ; Help Guide ; ;*********************************************************************** ; HELP CALL ILPRT DB CR,LF,CR,LF,'Operands in brackets [...] are optional' DB CR,LF,'Numeric values: ''n'' are decimal, ''x'' hex' DB CR,LF,CR,LF DB '+[n] step in [n] sectors',CR,LF DB '-[n] step out [n] sectors',CR,LF DB '# print disk parameters for current drive',CR,LF DB '=Abc search for ASCII Abc from current sector',CR,LF DB ' caution$-$ ;addr filled in by 'INIT' ; VWRIT3 ORA A JZ WRITOK ; WRERR XRA A STA QFLAG CALL ILPRT DB '++ WRITE failed ++',CR,LF,0 ; WRITOK POP H RET ; READ3 LXI B,DBUFF ;CP/M version 3.x read PUSH B ;save for later MVI A,BSETDMA ;set DMA address CALL BDOS50 MVI A,BREAD ;read a buffer CALL BDOS50 POP H ;buffer address to HL ORA A ;check error code RNZ ;return if error LXI B,128 ;logical sector size LDA LOGSEC ;get logical sect number ORA A ;no offset if zero RZ e[.ext] Dump sequential memory to disk' DB CR,LF,'L Log in drive',CR,LF DB 'LX Log in drive X',CR,LF DB 'M[nn] Map [from group nn]' DB CR,LF,CR,LF,CR,LF,CR,LF,CR,LF DB CR,LF,CR,LF,CR,LF,CR,LF DB '[More]' DB 1,CR,' ',CR,LF,CR,LF DB 'N New disk',CR,LF DB 'P Printer toggle switch',CR,LF DB 'Q Quiet mode (no msgs)',CR,LF DB 'R Read current sector',CR,LF DB 'Snn Sector nn',CR,LF DB 'Tnn Track nn',CR,LF DB 'Unn User nn f3CHEK ;check for version 3.x JC VREAD ;for 1.x or 2.x CALL READ3 ;read a physical sector JNZ RDERR ;if error on read XCHG ;sector address to DE LHLD DMAADR ;DMA address to HL XCHG ;reverse addresses for: CALL MOVE ;move POP H ;read finished RET ; VREAD CALL $-$ ;address filled in by 'INIT' ORA A JZ READOK ; RDERR XRA A STA QFLAG CALL ILPRT DB '++ READ failed, sector may be invalid ++' DB CR,LF,0 ; READOK POP H RET ; WRITE LDA WRFLG ORA A JNZ PWRITE ; : upper/lower case matters',CR,LF DB ' use for hex:',CR,LF DB ' to find "IN 0" use: =<0> or' DB CR,LF DB ' "(tab)H,0(CR)(LF)" use: =<9>H,0' DB CR,LF DB '< save current sector into memory buffer',CR,LF DB '> restore saved sector',CR,LF DB '( toggle map display of erased files',CR,LF DB '? help (displays this guide)',CR,LF DB 'A[ff,tt] ASCII dump',CR,LF db 'B[nn] Boot track number of sectors per  or Find command (CP/M-2 only)',CR,LF DB 'V[nn] View [nn] ASCII sectors',CR,LF DB 'W Write current sector',CR,LF DB 'X Exit program',CR,LF DB 'Y Yank current sector into sequential memory' DB CR,LF,'Z[nn] Sleep [nn tenths]',CR,LF DB '/[nn] Repeat [nn (decimal) times]' DB CR,LF,CR,LF,CR,LF,CR,LF,CR,LF,CR,LF DB CR,LF,CR,LF,CR,LF,CR,LF,CR,LF DB '[More]' DB 1,CR,' ',CR,LF,CR,LF,CR,LF,CR,LF,CR,LF,CR,LF,CR,LF DB 'Cancel a function with C or Ctl-C',CR,LF Dame XCHG MOV E,M INX H XCHG SHLD DRM XCHG MOV A,M INX H STA BSH MOV A,M INX H STA BLM MOV E,M INX H XCHG SHLD DSM XCHG MOV E,M INX H XCHG SHLD AL0 XCHG MOV E,M XCHG SHLD SYSTRK ; LOGCAL LXI H,GRPDIS MOV A,M PUSH PSW LDA BLM MOV M,A PUSH H LHLD DSM XCHG CALL GTKSEC SHLD MAXSEC XCHG SHLD MAXTRK POP H POP PSW MOV M,A MVI L,0 ;initialize count LDA AL0 ;start with al0 CALL COLECT ;count bits LDA AL1 ;do al1 CALL COLECT in 'A' as a count in 'L' ; COLECT MVI H,8 ; COLOP RAL JNC COSKIP INR L ; COSKIP DCR H JNZ COLOP RET ; ; ; HL-DI ==> HL ; SUBDE MOV A,L SUB E MOV L,A MOV A,H SBB D MOV H,A RET ; ; ; Quick kludge multiply, HL*DE ==> HL ; MULT PUSH B PUSH D XCHG MOV B,D MOV C,E MOV A,B ORA C JNZ MULCON LXI H,0 ;filter special case JMP MLDONE ;..of multiply by 0 ; MULCON DCX B MOV D,H MOV E,L ; MULTLP MOV A,B ORA C JZ MLDONE DAD D DCX B JMP MULTLP ; MLD 0 DUPFLG DB 0 YNKADR DW 0 ;pointer to current yank address DMAADR DW 0080H ;DMA address ; FUNC DB 0 ;BDOS parameter block AREG DB 0 BCREG DW 0 DEREG DW 0 HLREG DW 0 ; BACK DS 2 ;to back up in "0CA0-7F,X" DUMTYP DS 1 ; YDN DS 2 ;"K" work area UZER DS 1 CUZER DS 1 SCOUNT DS 1 ENDVAL DS 2 ; ;----------------------------------------------------------------------- ; ; The disk parameter block is moved here from CP/M ; DPB EQU $ ; SPT DS 2 BSH DS 1 BLM DS 1 EXM DS 1 DSM DS 2 DB 'Suspend output with S or Ctl-S',CR,LF DB 'Separate commands with ";"',CR,LF DB ' Example: G0',CR,LF DB ' +;D;Z#20;/',CR,LF DB ' would step in, dump, sleep 2 seconds' DB CR,LF DB ' and repeat until CTL-C is typed',CR,LF DB 'All "nn" usage except "/", "T", and "S" are hex',CR,LF DB ' (use #nn for decimal)' DB CR,LF,CR,LF,CR,LF,CR,LF,CR,LF DB '(See DU.DOC for complete examples)' DB CR,LF,CR,LF,CR,LF,CR,LF,0 JMP PROMPT ; ; ;***** ;count bits MVI H,0 ;make into 16-bit integer SHLD DIRGRP ;save count RET ; ; ; Temporary storage area ; BUFAD DW BASE+100H ;forces initial read HEXAD DW 0 ;to re-fetch a value TOGO DW 0FFFFH ;repeat count (FFFF=cont) TWOUP DB 0 TOGE DB 0 PFLAG DB 0 ;1=print GROUP DW 0 GRPDIS DB 0 SAVEFL DB 0 CURTRK DW 0 CURSEC DW 1 PHYSEC DW 1 LOGSEC DB 0 ;logical sector TABCOL DB 0 FILECT DW 0 DIRPOS DB 0 FINDFL DB 0 ;1=must position after find FTSW DB 1 ;search w/o increment NOTPOS DONE POP D POP B RET ; ; ; Routine to fill in disk parameters with every drive change ; LOGIT LDA VERFLG ORA A ;if not CP/M 2.x then JZ LOG14 ; do it as 1.4 LXI D,DPB ; then move to local MVI B,DPBLEN ; workspace CALL MOVE LHLD SPT SHLD SPT00 ;assume track 00 same JMP LOGCAL ; LOG14 LHLD BDOS+1 ;first find 1.4 BDOS MVI L,0 LXI D,DPBOFF ;then offset to the 1.4 DPB DAD D MVI D,0 MOV E,M ;now move parameters INX H XCHG SHLD SPT SHLD SPT00 ;assume track 00 sRM DS 2 AL0 DS 1 AL1 DS 1 CKS DS 2 SYSTRK DS 2 PSH DS 1 ;physical shift count PHM DS 1 ;physical sector mask ; ; End of disk parameter block ;----------------------------------------------------------------------- ; SAVBUF DS 128 INBUF DS 128 DBUFF DS 4096 ;physical sector buffer ; ; ; Directory read in here; also search work area ; WORK EQU $ DIRECT EQU $ ; END ****************************************************************** ; ; Utility Subroutines ; ;*********************************************************************** ; GRPCMP MOV A,C INR D DCR D JZ CMP8 CMP M INX H RNZ MOV A,B ; CMP8 CMP M RET ; ; ; 2's complement HL ==> HL ; NEG MOV A,L CMA MOV L,A MOV A,H CMA MOV H,A INX H RET ; ; ; HL/2 ==> HL ; ROTRHL ORA A MOV A,H RAR MOV H,A MOV A,L RAR MOV L,A RET ; ; ; Collect the number of '1' bits B 1 ;initially not positioned WRFLG DB 0 ;may not write until '+', '-', or 'G' QFLAG DB 0 ;quiet? (0=no) FIRST0 DB 0 ;sets to 0 if first sec # is 0 DRIVE DB 0 MAXTRK DW 0 MAXSEC DW 0 SPT00 DW 0 ;track 00 sectors per track DLMREQ DB 0 ;delimiter required switch DIRGRP DW 0 ;count of groups allocated to directory SAVTRK DW 0 ;track save area SAVSEC DW 0 ;sector save area VERFLG DB 0 ;CP/M version number MPMFLG DB 0 ;non-zero => MP/M SECTBL DW 0 ;pointer to sector skew table MFPTR DW!  DU.COM v8.6 10/24/83*."g "`}0! 6! "!0"g*"*"4"h"0қ""""R"""P:`ʇ"+Û*.!"+!"b DISK UTILITY v8.6 Universal Version under :a>M>C:P/M :`>0::>.:0: Type ? for help Type X to exit ͕!~WGW## > !^2Pc>282912:>2M!"4~W:L ~ W;#f 2u+ʏ-=% <ʨ>#t?ʰA- BaCD- Fd GO H- K LʞMdNP?QR:uHB z~  ~ >.:{+ #} z~W{ f{ f~;[ _ ÓTr Sʎ G 4(*S͍Vͮ >2Nî (z4*}͍̈́4͡2Nʹ Ó**A͍*}͔*C+:G}2?:Gy "=*2L:K2?- o&"=! *͍V"=2?6 ͮ͡2NÓbk:)=; :?o*}qP *}* # >2L\  \<ª 2K++ FILE NOT FOUND ++ f=2Ko&))))) >D2u ~. : ; # ~ ;#.:4#> *\"Aͮ͡2NÓ*=! u|h~> 0>(:~<u#ͨ>.:ͨ:f:~<~> b>):À ++FREE++ ::2:>2Y::›ã>::u2Y~# ڶ~ڼÿ:¨*#"I!"d!/:;#~$~!$~_#~$:W#e *d|>*2f"d *I+"I|*d:G:h҅:O*g! LAST ADDR=*g "g+|<}< f2P++ YANK MEMORY FULL ++ f:O! >2@f:@! f2P++ NO "<" SAVE COMMAND ISSUED  or Group ff><:<>>::2x<y<x<yE N0: \ |S{0:> :>*:~W~—0WÚ:#~€#z¸{ƀ_ɯ2P++ BAD DISPLACEMENT (NOT 0-7F) ++ W#~#'~  ;,->#04:A4G40))))o#~#~  ;,->#04:40bk)))o|g1 :!0O  ʬʹʹ ow#:o+>*:ow:> :!ɯ +y~:ooj  $6 !~ oSO TO U Vʮ WXJY;ZJ/(@2P?W:;/2;f 2P+++ OUT OF MEMORY +++ WStatistics for drive :RA:>:: Tracks: *S#S> :6 Sys tracks: *S> :6*W*}͍ Sec/track: 7 Sec/trk 00: *WS> :6 Sec/non-trk 00: *}S> :6 Groups: *S> :' Dir groups: *ZS> :6 Sec/group: : :6 Dir entries: *#S> :6f :RO͹f2Pf(z*8#| ɯ2x\ +~#:V !]~ @2\#~ M ~ H yO~0O#2 y<2x]  "{ *g|0 } Empty! É 2y:xʙ =_ \< File exists! Delete? 0 Y N ÀY \\<No dir space! À!0"v2z*v\7Write error x!z4*v"v*gz{*z&S sectors written. !0"g\:y_ *{f~ ʥ;ʥz4z¥*C*Aͮ͡î ~ ;z4*C+|*A|*S"A*U+"A*}++ W~# ɯ2O~% !6 î y G~{4:MŒ *4} 2M*C*Aͮ͡ʹ !~#"4:O4 {¿ !~& ~ ~ ~ :, & *C*Aͮ͡ÿ  ++ EOF ++ f:Oœ 2P++ Can't dump, no sector read ++ 2PUse G command following F, or R or S following T W~;ʧ °  ͣBK ; #ͣ`i}<uu:uA ~<}u}u{ #} ~W:#>^:>U:c> :> :`_O S> ::HCÍ b b y:Py y>2HÅ څ:H<2H:<đ͓ک>͙*S͍V"ABK͓> ͙*"C*A͍`i͓+:2G:Wy#DM:Q?+?*b ͓*>͙-:~5g:`?&i"EDM͓Q> ͙ɯ2P++ Not within tracks 0-*SS ++  W`i"i͓ڞ> Ù>2O͓ڽt*i 2P++ READ failed, sector may be invalid ++ :O1"8*8!zf"8|fW:`4({ 4z4 f:<2<f{T @:i>i] xi~WTf&ˆ00Oùͤf~ ;  #AOy2R͓> ͙:`|4^#V+"b! ~#foʹ*ͮ*E|2Qʹ   *"A!"C*#yyDM/BK:=R͌*Aͮ*C͡! x,͌*A"\*C"^{ʐ*Z͍?ڐ*͍VÓ*Z DM:YĎ'>-:> 2f*#}¼|~z{ʯ 'Ø '**\͍*^"A"Czî*C#*}͍̈́A*A#*S͍4"A!"C*S*A͍;*U͍;4(zq*}|*}͍4"Wf*A|‘*W͙f**A͍G=*=DM'>:::?<, T=*AS, S=*CS, PS=*ES~ #ͣ#-ͣ#BK,4HjA4~ f;f :~:@C:"sd #yWf*s!~ f;f4#~ f;f,i"6{<yʖf*6j~ f;f4:Nµ͡f2P++ Can't read - not positioned ++ Position by: Track then Sector,"  2P++ CANNOT WRITE UNLESS READ ISSUED ++ ^ ͓OtV*i >͙Rr2P++ WRITE failed ++ > ͙> ͙:G =:`0"q"o`i"m2kk2 Operands in brackets [...] are optional Numeric values: 'n' are decimal, 'x' hex +[n] step in [n] sectors -[n] step out [n] sectors # print disk parameters for current drive =Abc search for ASCII Abc from current sector caution: upper/lower case matters use for hex:  ASCII sectors W Write current sector X Exit program Y Yank current sector into sequential memory Z[nn] Sleep [nn tenths] /[nn] Repeat [nn (decimal) times] [More] Cancel a function with C or Ctl-C Suspend output with S or Ctl-S Separate commands with ";" Example: G0 +;D;Z#20;/ would step in, dump, sleep 2 seconds and repeat until CTL-C is typed All "nn" usage except "/", "T", and "Sd string may optionally be placed as an operand of the original 'DU' command, i.e.: A>DU G0;D;G2;=OK<1A>;D for example, if you want to only map the disk, and then exit: A>DUU M;X Once 'DU' is running, it expects single-letter commands much like 'SID' OR 'DDT'. For ease of use, multiple commands may be placed on one line, separated by ";". In addition, a given command or string of commands may be repeated -- either indefinitely (until ^C is pressed) or a given numbe to find "IN 0" use: =<0> or "(tab)H,0(CR)(LF)" use: =<9>H,0 < save current sector into memory buffer > restore saved sector ( toggle map display of erased files ? help (displays this guide) A[ff,tt] ASCII dump B[nn] Boot track number of sectors per track [More] CHANGE: CHaddress,byte,byte... (hex) CAaddress,data... (ASCIIx Allowed for imbedded hex CHfrom-through,byte, e.g. CH0-7F DU-V86.DOC ---------- By Ward Christensen (revised 10/18/83) additional notes by Ron Fowler, Irv Hoff, and Jeffrey Nonken ; This version of DU is compatible with CP/M 1.4, 2.x and 3.x and does ; not require alteration for various hardware configurations. It ad- ; justs itself automatically to the correct number of sectors, tracks, ; directory size, etc. ; INDEX 1.0 INSTALLATION: 2.0 USE: 2.1 COMMANDS, BY FUNCTION 2.2 " are hex (use #nn for decimal) (See DU.DOC for complete examples) fyo#x}/o|/g#ɷ|g}o&҈,%ƒ}o|gBKx¤!ñ T]xʱ ç:`} *}"W*.:^#"}"W^#"~#2~#2^#"^#"^"!?~:w*6 "U"Sw.:́:́&"Zr of times. To avoid an accidental ^C from dropping out of 'DU', only an ex- plicit "X" command will exit 'DU'. 2.1 COMMANDS, BY FUNCTION === HELP: ? displays the help guide POSITIONING: Gnn by allocation group Snn by sector Tnn by track +nn going ahead nn sectors -nn going back nn sectors I/O: < puts current sector "away" into a buffer > recalls previously saved sector ,E5 CAfrom-through,byte D[ff,tt] Dump (hex+ASCII) Fn.t Find file Gnn CP/M Allocation Group nn H[ff,tt] hex dump K[du:]filename[.ext] Dump sequential memory to disk L Log in drive LX Log in drive X M[nn] Map [from group nn] [More] N New disk P Printer toggle switch Q Quiet mode (no msgs) R Read current sector Snn Sector nn Tnn Track nn Unn User nn for Find command (CP/M-2 only) V[nn] View [nn] ALPHABETIC COMMAND SUMMARY 3.0 NOTES 4.0 INTERPRETATING DIRECTORY DATA 4.1 SINGLE DENSITY 4.2 DOUBLE DENSITY 1.0 INSTALLATION: === Because of the automatic adaption feature, no conditional assem- bly options are included. The only alteration that needs to be done is to use DDT to set the byte at 103H for your clock speed. Use 0 for 2MHz, 1 for 4MHz, 2 for 6MHz. (This only affects the 'Z' SLEEP command.) 2.0 USE: === An initial comman#   K writes "yanked" sectors to a file (see "saving sequential memory" in notes below) R reads sector W writes sector Y "yanks" current sector into sequential memory DISPLAYING: # shows disk parameters A dump sector in ASCII D dump the sector (hex + ASCII) G shows current group, track, sector H dump sector in hex M maps the disk =Abc ASCII search, starting at current sector. hex may be imbedded or used alone. To find: "IN 0FEH" use: = (Ignores bit 7 unless using .) Since ";" is a command delimiter, you have to use <3B> to search for a ";". Also, since "<" is a hex escape character, use << to mean a single "<". NOTE: This is a very slow routine. It can take 15 minutes or longer to search an entire double- density double-side ALPHABETIC COMMAND SUMMARY === # PRINTS THE DISK PARAMETERS + Advance 1 sector (if below track 2, this advances to next numerical, if 2 or more, advances based on CP/M's normal sector scrambling algorithm, i.e., allows + to the next logical sector of the file. - backs up 1 logical sector NOTE: + and - may take an amount: For example, +15 advances 15 sectors / repeats position to group nn and read G shows current position H dump sector, hex only Kdu:filename.ext save a file from "yanked" sectors. drive, user are optional. Resets "yank" address. see "saving sequential memory" in notes below. L re-logs in the current disk -- you may pull out a disk, put in a new one, and "L" just to log it in. (see "logging in disk" in notes below) LX  -- where the files are located Mxx map starting at group xx Vnn views (like CP/M type) nn sectors CHANGING: CAnn,VAL change data in ASCII (with escape to hex) CHnn,VAL change data in hex Unn change user to nn SEARCHING: =Abc scan for Abc (IN ASCII) from current sector on (very slow, allow up to 15 minutes to scan an entire disk. Either finds the answer or says: "out of bounds". FNAME find a file in the directory F d disk so be patient. It either finds the string or says: "OUT OF BOUNDS". > gets saved buffer. < and > maybe be used to move a sector to another place. ? displays the help guide A dump sector (ASCII only) Bnn boot nn sectors per track -- not all disks have 26. CHADDR,VAL,VAL,VAL... change hex values in sector CAADDR,CHAR STRING... change ASCII calues in sector NOTE: may be hex imbedentire command -- defaults to "forever" /nn nn may be 2 TO 65535 ( toggles the map display to show/not show erased files. When showing erased files '*' indicates that block duplicates a block in another file. It may not be possible to restore this program without errors. If there are no '*' in this complete file, it can be correctly restored. < saves current sector in a 'save' buffer logs in disk 'X', such as: LB, LC, LA, etc. M dumps a map of the group allocations for files Mn shows which file is allocated to group "N" N resets CP/M via BDOS -- this may make it possible under some implementations of CP/M to change the disk format, i.e., density, sides, etc. P toggles the printer on/off Q quiet -- preceedintg any command, suppresses CRT output R reads into memory the sector  find next occurrence (extent) of same name MISC: ( toggles the map display to show/not show erased files /nn repeat previous command nn times (repeats indefinitely if nn omitted) Bnn boot nn sectors per track LX log in disk X P printer toggle Q before a command does it 'quietly' X exit to CP/M Znn sleep (nn tenths of a second) to allow viewing data before it scrolls off 2.2 ded in the ASCII string: CA00,OK<0D><0A><1A> ----> W writes changes to disk note that the 'C' command echoes the over- laid data for verification. CHADDR-ADDR,BYTE repeats a change CAADDR-ADDR,BYTE repeats a change D dump sector (hex + ASCII) FNAME print directory for file "NAME", then positions to its directory sector. F find next occurrence of name in directory Gnn $  currently positioned at. NOTE: 'R' (read) is implicit in the G, +, and - commands but NOT in the 'S' and 'T' commands Snn position to sector nn, and read TNN seek to track nn (no read) Ux logs user 'x' for next 'F' command V views the current sector -- assumes ASCII data Vnn views nn sectors W writes the current sector to disk NOTE: may NOT be used after an 'F' commande extension (0-3 characters) ||| +-------- file name (1-8 characters) ||+--------------- user # (or none) |+---------------- drive designation (A-P or none) +----------------- DU command Drive and user may be omitted. If so, omit the colon as well. Drive must be specified if the user is. If the user # is omitted, the current user is used. If the drive is omitted, the current CP/M default drive is used. This function saves the current contents of sequential memory into a disk fils (D, A, H) may be optionally followed by a starting and ending address: D0,7F the same as just 'D' D3,5 A20,3F * LOGGING IN DISK: If you have a disk with a 'blown directory', try logging in a good disk of the same density, then put in the 'blown' disk without logging it in. You are opening yourself to possible problems because of the buffering of physical sectors in the 'BIOS'. The best technique, (but not guaranteed), would be to seek to the unused inner  || in ASCII || || extent-^^ || || || || || || file size in sectors-^^ || || || ^^-00 = file active other values (E.G 03) = user # || E5 = file erased ^^-displacement of line in directory sector SECOND LINE - 50 33343536 3738393A 00000000 00000000 *3456789:........* | | | | | ^- allocation groups ^-----allocation group numbers--- as CP/M was used to find file in the directory X exit back to CP/M (must press return). ^C was too easy to hit over modem lines -- requires two bytes: (X,CR) to exit. Y "yank" the current sector into sequential memory (starts at 3000H, increments for each yank) Z sleep -- causes the porgram to pause -- such as to look at a dump. Z is 1 second Znn nn tenths of a second Z50 = 5 seconds e. The contents of sequential memory are determined by the 'yank' function, and the pointer of that function is used here. If nothing has been yanked, you get an error. Once the file has been saved, the 'yank' pointer is re-initialized to its original value (3000H). Control is re- turned to DU. 4.0 INTERPRETATING DIRECTORY DATA === 4.1 SINGLE DENSITY === The following explains the format of a CP/M directory entry as shown by 'DU', using either the 'F' (find file) command, or  tracks of the first disk, do the read, then change disks. That way, if it write anything, you won't have destroyed anything -- assuming the disk is not completely full. Another technique (assuming the second disk does not contain a CP/M system, would be to seek to track 1, do the read there, then change disks to the blown one. * SAVING SEQUENTIAL MEMORY: Since CP/M v 3.0 does not have a SAVE function, one has been added. Syntax is: Kdu:filename.ext ^^^ ^ ^ ||| | +-- fil--^ (just happened to be printable) 4.2 DOUBLE DENSITY === The following is a sample of 'FSID.COM' running on a double- density system: :FSID.COM 00 00534944 20202020 20434F4D 0000003A *.SID COM...:* 10 38003900 3A003B00 00000000 00000000 *8.9.:.;.........* G=0000:00, T=2, S=1, PS=0 The primary difference is that the groups now occupy 2 bytes, i.e., 38 00" "39 00" ... this follows the Intel and CP/M convention of  3.0 NOTES === * Multiple commands: May be separated by ";" EXAMPLE: The following commands will erase the B: disk directory to all E5's: LB log in B: drive G0 position to directory CH0-7F,E5 fill with E5 < save the sector >;W;+;/16 restore, write, next, repeat 16 ---- this could be shortened to: LB;G0;CH0-7F,E5;< >;W;+;/16 * DUMP COMMANDS: All dump commandjust doing 'D' (dump) of the directory sectors, which are located in groups 0 and 1 on a single-density disk. SAMPLE RESULT OF 'FSID.COM' COMMAND: 40 00534944 20202020 20434F4D 0000003A *.SID COM...:* 50 33343536 3738393A 00000000 00000000 *3456789:........* FIRST LINE - 40 00534944 20202020 20434F4D 0000003A *.SID COM...:* || ||| | || || | | || ||^---hex file name/type--^ || || ^file name^ || || || %   putting 16-bit values high-byte-first. This it means group 0038, 0039 etc. Note that in double-density, each group stood for 2k not 1K, so there were half as many groups for the same file. Be very careful when patching a directory under double-density. For exmaple: CH10,38,39,3A,3B... This might try to access group 3938 with resultant angry noise from the disk stepper as it attempts to find where it should go for the data. * * * * *  GRH L0MN PPQYRST U W VXYYZ Y? / ͘ -- VFILER 1.7 File Manipulation Program -- <> ! <> T - Tag File ! C - Copy File U - Untag File ! D - Delete File W - Mass Tag/Untag ! F - File Size -----------------------------! M - Mass Copy <> ! R - Rename File P - Print V - View ! Y - Maí!9"1! ~#͐:M!_)))$.#"O"z(y2":A2E":]/(4:mW 2͘ Strike Any Key to Enter VFILER -- ;!]~ (͟y͹xÂͮ͹Â2!2G"2Y"22H"ɯ2<"^>2<"!\" ͆͏\"!x] ͆2h2|/2\2;"Mass Tag or Untag (T/U)? T(=U¦2"2:"*W" ~*6 *W" "W"*A"ͳ !""*?"2:">2"*W" ~*6**W" "W"*A"ͳ !""*?"2"/2;"*W" ~*6 (@">2"2;"*W" ~*"6* !ͳ> ͭ*W"#j~í2;"/2:"`>2:""::"~#\"*}""I"!"}":o*I"͹/o:;"(:"( *" *}o|g"::"*W"#j>:ͭ>Kͭ*͘ Tagged:>Kí!""*O"*?"ͳ("A""?""W"`*?"*O"ͳ(!6@*U"ͳ(8 !""*A"*U"ͳ(*A" !""ss Delete -----------------------------!--------------------------- <> ! <> - File Forward ! L - Login DIR - File Backward ! N - ReNew Display B - File Backward ! S - Status of Disk G - Go To a File ! X - Exit + - Screen Forward ! - Exit - - Screen Backward ! / - This Summary ---------------------------------------- :<=G2"#~ :!O2"#JB>B:(*!<:G:!Oyͭxͧ:G"ͭ**O""W"=ƀo&:\w*W" ͆6 #"W"\< *W""U""*O" ͳ*O""Q" "S"*S"*Q"  #  0 Nwy#*S" "S"*U"ͳ*Q" "Q""S"*U"ͳ*+*ͳ0 x No Memory for Copy Bufferæi`"M"!*W"#j :6"(ͩInvalid Command: x 0>^ͭx@ͭm!G~ȸ(####~#fo"V<>.",V"V+- "VBVCNDF*O""W""?"@*U"ͳ8"A"`ͳ.ͥ`*W" *A"ͳ V*?""W"*W" "W"|ͳbͥ`*?"*W"ͳ _*A""W"͗*W""W"ͳ*W"*?"ͳ8 b>*W"4*A"ͳ(-8+"W"*"$""ͳ*W"4*A"ͳ(8.ͥ`*"E!""*?""W"*"}(*W" "W"*"""Status of Disk: ͦAڦG:E"A`x2H"͗\:ͧ!"C"*O"*U"ͳ( *C"#"C"! *C"͘ Files in DIR`Mass Delete (Y/N/V=Verify Each)? Y(V¦2F"2*O""W"*W" ~* ~:F"V((}*W" "W"*U"ͳ >2@ chdir ͵ ͠ ͖ 8{ Ăy0 2$@NAMES DIR ZCPR2 ͯ 2<P ͖ (Ăy 2͵ ͋ ͖ ͖ Ăy0 ͖ 2ͯ 1 'd ! ^#V#*0}o|g*  ":D x=D y* >í>ͭ>=ͭ| ͭ} í>í ͖ 8{ Ăy0 2$@NAMES DIR ZCPR2 ͯ 2<P ͖ (Ăy 2͵ ͋ ͖ ͖ Ăy0 ͖ 2ͯ 1 'd ! ^#V#*0}o|g*  ":D x=D y----------------- <> File: ^S - LEFT ^D - RIGHT ^E - UP ^X - DOWN Screen: ^A - LEFT ^F - RIGHT ;`:<"(-͘Chaining to External HELP ...!RQ `External HELP Facility Not Available``ZCPR2 Command Line? ! 6~#6  #^#6~ʦQ `: Chaining Error -- No MCL*!Ns#Fr#F!w# :A2r:!!s0 8 q#:wj  6Y(w#Chaining Error -- MCL Overflow*6&  >22F"~Delete _͘ (Y/N)? Y(6:(*A""A"*W"ͳ ͗*A""W"*A"*?"ͳ:F"Y Deleting File _!\"\"<(ͳ: *W"No File Found`*W" *U"ͳ( ͆*O""U"ͳ*W"ͳ1List Empty0*W"" ͆Rename File to: "!" ~#?(S"!" O>w#:"2\" !"]"͆!h"͏\"<(@File Already ExistsAmbiguous File Name NOT Allowed*W"#!" ͆*"j~ͭ"<¦ß 6# &!N#,i&6 7 <2\##~2#~2##^#V"*#8W+}(z i`:()= "&"*&"͘K Bytes on Disk"#"!N]" ͆͏!\"6:"O:"GJ!h"͏\"2|" ?:MG!*" 1:,"*#"*" ~#~#~(> "#"B>2(""#"2%"2"ͮ2"*#"x:"2%"*K}E !":("E ѯ>#~G#*#": > #   ޯF#N>:ɯ2(" Aw#  w#!"8"ntæ͗:\2":Y"ͭ"<(D:(0Copy Exists -- Erase (Y/N)? Y( :!ͭ"!"""< !Destination Directory Full`Copying File !"[25":!ͭ!"K"*"*\"(=(ASource Read Error`*~/# *"*K"#"K"*M"ͳ >25":Y"ͭ*"*K"|(B+"K"*""(Copy Disk Full"2`:5"ʫ"< Copy Close Error`*!"!!"!!"͏"<}2"""(=ICopy Read Erroræ!~/## ~w# VFILER 1.7 by Richard Conn VFILER is a screen-oriented file manipulation utility. Following in the footsteps of SWEEP, CLEAN, and DISK, VFILER performs the same fundamental functions, but adds screen-oriented displays and manipulation to it, giving a more user-friendly interface in my opinion. VFILER must be installed for a user's terminal. To do this, edit VFILERSC.ASM, follow the directions contained therein, and set up this file to support your CRT's Clear Screen, Goto XY, and Clea.(#*(w#7 . 6?#.#78 (.( *( w##͏6?#Goto Filename: "*O""W""?"2(G:<2@ 2*?"@"?"*W" "W"*U"ͳ *O""W"File NOT Found*?"!""*W"ͳ( ! |*W"#" ?(#͘ Cancels, Turns Up One Line, Other Keys Page Screen >2>"2">FPrint on LST Device (Y/N)? Y¦Printing *W"#j>2"=2>">2~͏\"< Unable to Open Fileæ2|"2\" L!~(@_:O{  *8"~27"#"8"ͳ \":7"!͘VFILER, Version 1.7 [Z80 Code]!""*W""C" *A"ͳ"C"|*"}F0o""|!g""$!*"ͳ(}( o""{9o%""!D*"$|0""}!o""*""Z"*W"""(Z!͘ : Command (? for Help)?!͘-- Screen Directory --!.͘Current File:*Z"""*""W"ͥV*"$""V*?""C"*A"*C"ͳV> ͭ#j~ͭ "C"|*"͘-->*"͘ !ØWorking ...>26"!File Size of !+!!yg)0>g>!or$s%# *!O! ~W$^"!:!ͭ*!*!ͳ "͘ -- CRC Verified:"/2#Error on CRC compare"~#(ͭ> ͭ> (  _*.*. > > _> ͭ> ͭa{_!Fx#~w6 #   [0!U(#7,:;<=>!]"jv>.ͭ~ͭ#*W"\" ~#6#2 :H"__ z{|g}o>2=":="=2=" 8 |:="( G> ͭ2="{0ͭr To End Of Line functions. Once done, assemble VFILERSC, patch it into VFILER by using DDT (I and R commands, with no offset for R), and then save the result. VFILER now is installed to run under CP/M. This version of VFILER is already installed for a Bigboard. This means that it will run without modification on the Kaypro and Xerox 820 also. It is set to allow a 4 drive system (A-D). If the user is running ZCPR2, he should now run GENINS on VFILER, do a normal installation, and write  :"<ķ#: `{ 2:(L:tG:>"<2>"د2>"͘ [View More...]  ͘ (:t=2>":qG:>"<2>"د2>":rG:<2:!K~(#!]">.~#Mass Copy*W"""*O""W"*W" ~*('*W" "W"*U"ͳ 2/2*""W"*?"2!"!~!h"͏2|" !]""͆\"< Unable to Open Sourceæ: pCopy to DIR: ͉: :\"2\: :!2Y":\G:\" 7:Y"G:! -Drives or User Areas must be differe26"!!!!͘Strike Any Key to Continue -- File: HELP VFILERHELP COM;VFILER xxx W??????????? '  out the result. VFILER will take advantage of the special features of ZCPR2 if so installed. When VFILER is invoked, it displays a screen (up to 64) files to the user with an arrow. The user may use the Word Star cursor movement keys (^E for up, ^X for down, ^D for right, ^S for left) or the Kaypro cursor keys (^K for up, ^J for down, ^L for right, ^H for left) to move the cursor to point to a desired file. The < and > keys will also move the cursor. Typing V will view the file on the CRTend the character in A to the screen is provided at ; location 300H in VFILER. No registers are affected by calling the ; routine at this address. ; TYPE EQU 300H ; ; Special ASCII Characters Used ; ESC EQU 1BH CTRLX EQU 'X'-'@' CTRLZ EQU 'Z'-'@' UP EQU 'K'-'@' ; user-defined cursor up DOWN EQU 'J'-'@' ; user-defined cursor down RIGHT EQU 'L'-'@' ; user-defined cursor right LEFT EQU 'H'-'@' ; user-defined cursor left SCR$FOR EQU 'F'-'@' ; user-defined screen forward SCR$BAC EQU 'A4 for H and 1-80 for L. ; ; clear screen CLS: MVI A,CTRLZ ;clear screen JMP TYPE ; position cursor (H=row, L=col) where 1,1=upper left GOTOXY: MVI A,ESC ;ESCape CALL TYPE MVI A,'=' CALL TYPE MOV A,H ;row ADI ' ' CALL TYPE MOV A,L ;column ADI ' ' JMP TYPE ; erase to end of line (output 50 spaces if you don't have this function) EREOL: MVI A,CTRLX JMP TYPE END , P will print it on the printer, C will copy it, D will delete it, etc. See the command summary built into VFILER. VFILER knows all about the DU and DIR (if installed for ZCPR2) forms. Under CP/M or ZCPR2, the DU form may be used. For example, in response to the Login command (L), the user may type any of the following: A <-- log into Disk A, Current User 5 <-- log into Current Disk, User 5 A5 <-- log into Disk A, User 5 wordst <-- log into named dir (under ZCPR2) The entries'-'@' ; user-defined screen backward ; ; The following JUMP Table MUST be located at 200H in VFILER. This accesses ; the required routines. ; ORG 200H ; base page for screen routines JMP CLS ; clear screen on CRT JMP GOTOXY ; position cursor at row/col JMP EREOL ; erase to end of line ; ; The following table is copied by VFILER on startup to define the special ; characters generated by arrow keys on the user's terminal for cursor ; movement. This table is for the BB or Kaypro; MODULE: VFILERSC ; AUTHOR: RICHARD CONN ; VERSION: 1.0 ; DATE: 18 July 83 ; PREVIOUS VERSIONS: None ; NOTE: This is the patch file for VFILER. It allows the user to install ; his terminal-specific function routines. This file is to be assembled ; by the user and integrated into VFILER.COM via DDT. Once complete, ; VFILER must be installed further via GENINS. ; ; A>asm vfilersc.aaz ; A>ddt vfiler.com ; -ivfilersc.hex ; -r ; -g0 ; A>save 32 vfiler.com ; ; An entry point to s may or may not be followed by a colon, as the user desired (i.e., 'A' and 'A:' mean the same thing). . If you do not wish to set ; additional definitions, set all of these values to '@'. ; DB UP,DOWN,RIGHT,LEFT ;cursor up, down, right, left DB SCR$FOR,SCR$BAC ;screen forward, backward ; ; Screen Routines (for BIGBOARD) ; The following are sample routines implemented for the Bigboard & Kaypro ; NO REGISTERS are to be affected by these routines except for the PSW. ; The GOTOXY routine is the only one which receives passed parameters. ; HL is used to pass them, and the value range is 1-2