title DDTZ main section, Ver. 2.7 subttl History, entries, externals ; ; CPM/DOS+ 8080/Z80/64180 debugger, by C.B. Falconer. ; CAUTION - Code is extremely tight - study before changing. ; ; 2.7 88/5/25. Handling 64180 opcodes. Now issued only with the ; DDTZM command set (MSDOS debug compatible). Added support for ; undocumented z80 opcodes. Dis/assm changes. cbf. ; 2.6m 87/7/11. Dissassembler section, so MOV disassembles correctly ; under 8080 (or V20-80 under MSDOS). cbf. ; 2.5m 86/3/16. Command structure altered to match MSDOS DEBUG, to ; avoid silly errors when switching between systems. Ver. no. ; shows "M" for this variant. MATCHDEBUG equate controls. Adds ; Quit command, removes Untrace. Others shifted, see tbl/doc. ; ; 2.5 86/2/17. Correct exit from load of HEX files. 4 bytes moved, ; but affects the relocation vector. cbf ; ; 2.4 86/2/12. Correct assembler section for inx/dcx/push/pop xy. ; No changest to this segment, except version no. No changes ; to 8080 only assembler/disassembler (DDTY). cbf ; ; 2.3 86/1/10. Reworked from DDTY 2.2.5 to handle Z80 ops. Z80 ; registers are displayed, cannot be set. Tracing works on Z80 ; opcodes. Added based dump/substitute commands, @ to set base, ; char value parameters, made h command default for anything ; not recognizable. Standard relocater system. k command added ; Assy time switch cuts assemble/disassemble to 8080 code set. ; ; v5 - 2 Apr. 84. Upshift & strip hi order bit, hex file reads. ; "h" shows decimal for difference. ; "#" prefix causes decimal parameter input. ; ; Used for assembly/disassembly only extrn assemble, disassem, keep extrn .bdos; initialize use only extrn begin; for overwrite checks only ; ; Routines useable by assembler/disassembler entry dblblk, blank, couta, csta entry crlf, qbrk, nextch, skipblks entry t4hx, t2hx, tstr; a,f entry getline; a,f,b,c,d,e entry pcnt, pcnta; a,f,d,e,h,l. a=count, de^list entry nextparm; d,e,h,l; hl := de^; de:=de+2 entry rdhex, rdhexc; a:=exitch, de := value entry qdelim; flags entry chkop; all. hl^ past op. de to op at entry entry based; a,f,h,l. hl := hl+base entry ldbased,tbased; a,f. getvalue/type_adr using hl+base entry index,indexwd; a,f,h,l entry casexfr; a,f,h,l + routine entry sdem, ldem; hl^ =:= de; hl:=hl+2 entry delesshl; a,f. comparator, unsigned entry t4hxbc,t4hxblk; a,f (with trailing blank) entry err; abort return to command level entry dos, foperate; save regs. operate on tfcb ; ; Data area for assembler/disassembler entry dendptr; disassembly end for lcmd only entry disasmp; where to dis/assemble from entry aflag; -1, 0, or deflines. ; ; scratch space available for assembler/disassembler entry storeptr; wd entry exitstk; wd entry buff; 16 bytes entry tfcb, defdma; cpm standard ; ; Read/only data areas, set by chkop entry opkind, zopkind entry z80flg; set if running on Z80 entry unloaded; memory not used ; ; Values for relocater system entry codesize, pages ; subttl equates, configuration cseg ; true equ -1 false equ not true matchdebug equ true; false matches DDT command structure ; true matches DEBUG command structure ; ver equ 27 if matchdebug debugver equ 'M'; Signify MSDOS match else; matchddt debugver equ ' '; lower case for debuggery/patches endif; matchddt ; ; Customization parameters traplvl equ 7; rst level for steps etc deflines equ 12; default for l command deflgh equ 192; default bytes for dcommand ; cbufsiz equ 40; characters in cons in buffer stksize equ 44; DDTs own stack. Even number only ; ; CPM parameters reboot equ 0 bdos equ reboot + 5 tfcb equ reboot + 05ch defdma equ reboot + 080h tpa equ reboot + 0100h ; ; Ascii chars tab equ 9 lf equ 0ah cr equ 0dh rub equ 07fh ; ; z80 opcodes used if Z80 cpu only. Always guarded. exaf macro db 08h endm exx macro db 0d9h endm pushix macro db 0ddh,0e5h endm popix macro db 0ddh,0e1h endm pushiy macro db 0fdh,0e5h endm popiy macro db 0fdh,0e1h endm ldai macro db 0edh,057h endm ldia macro db 0edh,047h endm ldar macro db 0edh,05fh endm ; subttl Main loop ; ; If the act of loading a program overwrites any of the above area ; then DDT is cut back to begin here and dis/assem commands removed ddtbgn: .bdosexu: jmp init; altered, alt ^bdos when cut-back ; ; setup for return from executed program ; h,l setrtn: lxi h,return shld spsave lxi h,progrtn shld return ret ; ; program exit via return progrtn: lxi sp,stack call setrtn ; " " cmdloop: lxi sp,stack call zerotctr call chkused jc cmdloop1 lxi h,.bdosexu; In case lower portion overwritten shld bdos+1 cmdloop1: call qbrk; flush any input call crlf mvi a,'-' call couta call newln jz cmdloop lxi h,cmdloop; set return on stack push h cpi ' ' jc err sui '@' jc cmdloop2 cpi maxcmd lxi h,cmdtable jc casexfr cmdloop2: adi '@'; restore input char call pcnta; try for a number conversion jmp hcmda ; subttl command table ; cmdtable: dw setbase; @)set up offset dw acmd; a)ssemble first dw progrtn; b)egin (* i.e. initialize stack & return *) dw ccmnd; c)ompare first,last,against dw dcmd; d)ump first,last,base if matchdebug; not DDT commands dw scmd; * e)nter_mem 1st_address err dw fcmd; f)ill first,last,value dw gcmd; g)oto address, trap1, trap2 dw hcmd; h)exarith valu1, valu2 dw err; * dw err dw err; * dw rcmd; * l)oad_from_file [offset] dw mcmd; m)ove first,last,destination dw icmd; * n)ame init tfcbs_cmdline dw err dw err dw reboot; * q)uit to DOS dw xcmd; * r)egister examine/change dw wcmnd; * s)earch first,last,value dw tcmd; t)race_execution [count] dw lcmd; * u)nassemble first,last dw err dw kcmd; * w)rite [1st,last] dw rdexit; * x)amine memory usage else; match DDT commands, not DEBUG dw err; * dw fcmd; f)ill first,last,value dw gcmd; g)oto address, trap1, trap2 dw hcmd; h)exarith valu1, valu2 dw icmd; * i)nit tfcb_cmdln dw err dw kcmd; * k)eep_in_file [1st,last} dw lcmd; * l)istcode first,last dw mcmd; m)ove first,last,destination dw err; * dw err dw err dw rdexit; * q)uery (* memory used for application *) dw rcmd; * r)ead_from_file [offset] dw scmd; * s)ubstitute_in_mem 1st adr. dw tcmd; t)race_execution [count] dw ucmd; * u)ntrace_execution [count] (* quiet *) dw err dw wcmnd; * w)here first,last,value dw xcmd; * x)amine_change [register] endif; DDT, not DEBUG structure dw ycmd; bc := p1; de := p2; execute (p3) dw zcmd; Show Z80 registers maxcmd equ ($-cmdtable)/2 ; subttl Command executors etc. ; ; set base on @ command setbase: call pcnt lhld baseval cnz nextparm; does not disturb flags shld baseval rnz ; " " ; crlf and t4hx t4hxc: call crlf jmp t4hx ; ; execute arbitrary user routine as command ; y bcvalue, devalue, routineaddress ycmd: call get3p pchl; call user routine ; ; open file, using tfcb ; a,f fopen: xra a sta charcntr sta tfcb+32; set back to first record sta tfcb+12 mvi a,0fh ; " " ; operate on file tfcb, function (a) ; a,f foperate: push d lxi d,tfcb call dos pop d ret ; acmd: call ckpcnt; aborts if not available jmp assemble ; ; c(ompare) start_addr, end_addr, against_addr ccmnd: call get3p; bc:=start, de := end; hl := against ccmnd1: ldax b cmp m jz ccmnd5; no difference call crlfbk call t4hxbc; compare address ldax b call t2hx; compare contents call blank call t4hxblk; against address mov a,m call t2hx; against contents ccmnd5: inx b inx h call delessbc jnc ccmnd1 ret ; kcmd: call ckpcnt; aborts if not available jmp keep ; lcmd: call ckpcnt; aborts if not available call nextparm; does not disturb flags jz dadef dcr a jc lcmd0; 1st parm skipped, continue shld disasmp jz dadef; 1 parm only lcmd0: call nextparm shld dendptr ani 07fh dcr a jz disassm ; " " ; command error exit err: call crlf mvi a,'?' call couta jmp cmdloop ; ; dissassemble for default line count dadef: mvi a,deflines ; " " ; connector to dissassem. (a) is kind coding disassm: jmp disassem ; ; Dump parm1 thru parm2, relative to base ; (parm1 defaults to prev dump end, parm2 to parm1+0c0h) ; (optional) parm3 sets base dcmd: lhld baseval shld base call pcnt jz dcmd2 call nextparm jc dcmd1 shld dumptr dcmd1: ani 7fh dcr a jz dcmd2 call nextparm shld dumplast dcr a jz dcmd3 call nextparm shld base jmp dcmd4 dcmd2: lhld dumptr mov a,l ani 0f0h mov l,a lxi d,deflgh-1 dad d jnc dcmd3 lxi h,65535; prevent wrap-around dcmd3: shld dumplast dcmd4: call crlfbk lhld dumptr push h call tbased; show with possible offset dcmd5: mov a,l ani 0fh jz dcmd6 call blks43 dcr l jmp dcmd5 dcmd6: pop h push h dcmd7: call blank call ldbased call t2hx inx h mov a,l ani 7 cz blank call dumpdone jc dcmd8 mov a,l ani 0fh jnz dcmd7 dcmd8: shld dumptr mov a,l ani 0fh jz dcmda dcmd9: call blks43 inr l mov a,l ani 0fh jnz dcmd9 call blank dcmda: pop h dcmdb: call ldbased call ascii inx h xchg lhld dumptr call delesshl xchg jnz dcmdb call dumpdone jnc dcmd4 ret ; ; crlf and check for break ; a,f crlfbk: call crlf ; " " ; get console status. If ready, abort command, else z flag ; a,f (if not broken, else never returns) qbrk: call csta rz mvi c,1 call .bdosexu; flush the char jmp cmdloop ; ; load a := (hl + base)^. Z flag if base zero ; a,f ldbased: push h call based mov a,m pop h ret ; ; Show hl as possible offset from base. ; a,f tbased: call t4hx push h call based jz tbx mvi a,':' call couta call t4hx tbx: pop h ret ; ; hl := hl + base. Z flag if base is zero ; a,f,h,l based: push d xchg lhld base mov a,l ora h dad d pop d ret ; ; get 3 paramaters from command line to bc,de,hl resp. ; a,f,b,c,d,e,h,l get3p: call pcnt cpi 3 jnz err ; " " ; load regs with 3 parms, no count check ld3p: call nextparm push h call nextparm push h call nextparm pop d pop b ret ; ; set carry for (de) less than (bc), z flag if equal ; a,f delessbc: mov a,d ! sub b rnz mov a,e ! sub c ret ; ; fill parm1 thru parm2 with parm3 fcmd: call get3p mov a,h ! ora a jnz err fcmd1: call delessbc jc cmdloop mov a,l stax b ! inx b jmp fcmd1 ; ; goto [parm1],[trap1[,trap2]] gcmd: call crlf call pcnt call ld3p push h ! push b ! pop h ! pop b; xchg bc,hl ; " " ; execute, z flag no setups, carry no pc change ; hl = pc, de = trap1, bc = trap2 on entry ; (a) = trapcount + 1 run: di jz run2 jc run1 shld pcsave run1: ani 7fh dcr a ! jz run2 push b call setatrp pop d dcr a cnz setatrp run2: lxi sp,stack pop d ! pop b ! pop psw ! pop h sphl lhld pcsave push h lhld hlsave ei ret ; ; set a trap at (de) ; b,h,l setatrp: push psw lxi h,trp1set mov a,m ! inr m ! ora a jz setatrp2 inx h ! mov a,m inx h ! mov b,m inx h cmp e ! jnz setatrp2 mov a,b cmp d ! jnz setatrp2 mov a,m ! stax d setatrp2: inx h ! mov m,e inx h ! mov m,d inx h ! ldax d ! mov m,a mvi a,0c7h + 8*traplvl; (rst traplvl) stax d pop psw ret ; ; H parm1 parm2 hex sub/difference hcmd: call pcnt ; " " ; Entry after other command line items used hcmda: call nextparm dcr a ! jz thxdecr dcr a ! jnz err push h call nextparm pop d push h call crlf dad d call t4hxblk pop h mov a,e ! sub l ! mov l,a mov a,d ! sbb h ! mov h,a jmp thexdec; show in hex and decimal ; ; output (hl) in hex and decimal after cr/lf ; a,f,b,c,d,e,h,l thxdecr: call crlf ; " " ; output (hl) in hex and decimal to console ; a,f,b,c,d,e,h,l thexdec: call t4hxblk push h call tdzs pop h call blank mov a,l jmp ascii ; ; istring - setup tfcb ; move char string into defdma, set length, etc. ; parse initial file name into tfcb icmd: lda consbuf+1; line length lxi d,consbuf+3; ignoring "i" character lxi h,defdma mov b,a inr b icmd1: mov m,a inx h mvi m,0; set a terminator dcr b ldax d inx d jnz icmd1 xra a sta tfcb+32 lxi h,tfcb call pfnm mvi m,0; set first extent lxi h,tfcb+16 call pfnm mvi m,0 ret ; ; parse filename into fcb hl^. Only drive/name/ext fields ; Return a = terminator character, hl points to fcb+12 ; a,f,c,h,l pfnm: call skipblks mvi m,0 inx h mvi c,8; hl+c=fcb+9 jz pfnm1; eol, blank fill mov m,a inx h ! dcr c call nextch; jz pfnm1; eol, blank files. hl+c=fcb+9 cpi ':' jz pfnm2 pfnm1: call ldflda; not a drive spec jmp pfnm3 pfnm2: dcx h ! inr c mov a,m sui '@' dcx h ! mov m,a ! inx h; hl=fcb+1, c=8 call ldfld; exit hl=fcb+9 pfnm3: cpi '.' mvi c,3 jnz ldflda; blank fill extent ; " " ; load field hl^ for c chars. ; Return a = terminator char, hl points past field ; a,f,c,h,l ldfld: call nextch; jams at eol ; " " ; Entry with initial char. in a ; a,f,c,h,l ldflda: mvi m,' '; default call qfndelim jc ldfld2; control char jz ldfld2; other delimiter cpi '*' mov m,a jz ldfld4 inx h ! dcr c jnz ldfld ldfld1: call nextch; skip to delimiter call qfndelim jnz ldfld1 ret ldfld2: inx h ! dcr c; early delimiter found jnz ldflda; with the ".", blank or control ret ldfld4: mvi m,'?' inx h ! dcr c jnz ldfld4 jmp ldfld1 ; ; move memory parm1 thru parm2 to parm3 (up)] mcmd: call get3p mcmd1: call delessbc rc ldax b ! inx b ! mov m,a ! inx h jmp mcmd1 ; ; Check for hex file, tfcb ; a,f,h,l qhexfile: lxi h,tfcb+9 mov a,m ani 7fh ! cpi 'H' rnz inx h ! mov a,m ani 7fh ! cpi 'E' rnz inx h ! mov a,m ani 7fh ! cpi 'X' ret ; ; check for program loaded above (hl)^. carry if so ; a,f,d,e cmpload: xchg lhld unloaded mov a,l ! sub e ! mov a,h ! sbb d xchg ret ; chkldmax: call cmpload rnc shld unloaded ret ; ; check for program loaded above disassembler section ; a,f,d,e chkused: push h lxi h,begin call cmpload pop h ret ; ; read from file tfcb into defdma ; a,f fread: mvi a,20 call foperate; fread ora a ret ; ; r(ead) from file tfcb with bias parm1 rcmd: call pcnt jz rcmd1 dcr a ! jnz err call nextparm rcmd1: push h ; " " loadit: lxi d,defdma mvi a,26 call dos; set to std dma call fopen inr a ! jz err call qhexfile jz loadit5; read from hex file pop h lxi d,tpa dad d loadit1: push h call fread; read from file tfcb pop h jnz rdexit lxi d,.bdosexu-128 call delesshl jc err; too big lxi d,defdma mvi c,128 loadit2: ldax d ! inx d mov m,a ! inx h dcr c jnz loadit2 call chkldmax jmp loadit1 loadit3: call rdhexbyt; record type loadit4: call rdhexbyt; data mov m,a ! inx h ! dcr e jnz loadit4 call rdhexbyt push psw call chkldmax pop psw jnz err loadit5: call charead; load from hex file jc err sui ':' jnz loadit5 mov d,a pop h ! push h; get bias value call rdhexbyt; record length mov e,a call rdhexbyt push psw call rdhexbyt ; no load check to allow load above cp/m pop b mov c,a; form address dad b; plus bias mov a,e ora a jnz loadit3 mov a,b ! ora c pop b jz rdexit; dont update pc on zero exu point mov a,b ! ora c jnz rdexit; or on non-zero bias shld pcsave ; " " rdexit: lxi h,rdxmsg mov a,m rdexit1: call couta inx h ! mov a,m ora a jnz rdexit1 rdexit2: call crlf lhld unloaded push h call t4hxblk lhld pcsave call t4hxblk pop h dcx h mov l,h mvi h,0 ; " " ; output (hl) in decimal to console. ; suppress leading zeros. ; a,f,b,c,d,e,h,l tdzs: lxi b,0f00ah; c=divisor=10, b=iter cnt=-16 xra a; clear tdzs1: dad h; (hl) := (hl)/10; rdr to (a) ral; shift off into (a) cmp c; test jc tdzs2; no bit sub c; bit = 1 inx h tdzs2: inr b jm tdzs1; not done push psw; save output digit mov a,h ! ora l cnz tdzs; not left digit, recursive pop psw; last unlisted digit adi '0' jmp couta ; rdxmsg: db cr,lf,'Next PC Save',0 ; ; read hex byte to (a), update cksum (d) ; a,f,d rdhexbyt: push b push d call getch rlc ! rlc ! rlc ! rlc ani 0f0h push psw call getch pop b ora b mov b,a pop d add d mov d,a mov a,b pop b ret ; ; substitute memory at parm up scmd: lhld baseval shld base call pcnt jz err call nextparm dcr a ! jz scmd1 dcr a ! jnz err push h call nextparm shld base pop h scmd1: call crlf push h call tbased call blank pop h call ldbased call t2hx call blank call newln jz scmd2 cpi '.' rz push h call pcnta dcr a ! jnz err call nextparm mov a,h ! ora a jnz err mov a,l pop h ! push h push psw call based pop psw mov m,a pop h scmd2: inx h jmp scmd1 ; ucmd: xra a jmp track ; tcmd: mvi a,-1 ; " " ; track machine execution. show on (a) track: sta showtrc call pcnt jz track1 dcr a ! jnz err call nextparm mov a,l ! ora h jz err dcx h track1: shld tracectr ; " " showrun: call showcpu jmp run ; ; w(here) first_address, last_address, search_value wcmnd: call get3p; (bc):=first, (de):=last, (hl):=value wcmnd1: call delessbc rc; done, exit ldax b ! cmp l ! inx b jnz wcmnd1; not here ldax b ! cmp h jnz wcmnd1; 2nd byte does not match call crlf dcx b call t4hxbc; list address inx b call qbrk jmp wcmnd1; and continue ; subttl Register display ; ; display register, all or 1 only and modify xcmd: call skipblks jnz xcmd1 call showcpu call qz80 jnz zshow ret xcmd1: lxi b,11; selected registers - modify lxi h,regnames xcmd2: cmp m jz xcmd3; valid reg, index in (c) inx h ! inr b ! dcr c jnz xcmd2 jmp err xcmd3: call skipblks jnz err push b call crlf call dumpreg call blank call getline call pcnt pop b rz dcr a ! jnz err call nextparm mov a,b cpi 05 ! jnc xcmd4 mov a,h; flag revisions ora a ! jnz err mov a,l cpi 02 jnc err call getflgs mov h,a mov b,c mvi a,0feh call lrotate ana h mov b,c mov h,a mov a,l call lrotate ora h stax d ret xcmd4: jnz xcmd5 mov a,h; (a) revisions ora a ! jnz err mov a,l lxi h,asave mov m,a ret xcmd5: push h; bc, de, hl, sp, pc revisions call regkept pop d mov m,e ! inx h ! mov m,d ret ; ; Show z80 registers command zcmd: call qz80 jz err ; " " ; show all z80 only registers. No alteration allowed zshow: call crlf mvi c,9 lxi h,rreg lxi d,zregnm zshow1: ldax d call couta mvi a,'=' call couta mov a,m call t2hx mov a,c cpi 6 jnc zshow2 dcx h mov a,m call t2hx zshow2: call blank dcx h ! inx d ! dcr c jnz zshow1 ret ; zregnm: db 'riafbdhxy' ; 987654321 ; ; a,f,c,d,e getflgs: push h lxi h,flgbits mov e,b ! mvi d,0 ! dad d mov c,m lxi h,flgsave mov a,m xchg pop h ret ; ; a,f,c,d,e showflg: call getflgs showflg1: rar dcr c jnz showflg1 ral ani 1 jmp hexdig ; ; hl := pointer to register storage for id (a) ; a,f,d,e,h,l regkept: lxi h,reglocns-6 call index mov e,m mvi d,-1 lxi h,pcsave+2 dad d ret ; ; get stored value to hl for reg id (a) ; a,f,d,e,h,l regvalue: call regkept mov e,m ! inx h ! mov d,m xchg ret ; ; dump reg content, (a) is id name char, b is id ; a,f,d,e,h,l dumpreg: mov a,m call couta mov a,b cpi 05 jc showflg ; " " ; show reg value 8 bit or 16bit, on z flag set/reset. a=b is regid ; a,f,d,e,h,l showreg: push psw mvi a,'=' call couta pop psw jnz showreg1 lxi h,asave mov a,m jmp t2hx showreg1: push psw call regvalue call t4hx pop psw sui 8 rc; bc or de > 0..1 sui 2 rnc; pc mvi a,'>'; show mem for hl^ and sp^ call couta mov e,m ! inx h ! mov d,m xchg ; " " ; output (hl) as 4 hex chars to console ; a,f t4hx: mov a,h call t2hx mov a,l ; " " ; output (a) as 2 hex chars to console ; a,f t2hx: push psw rar ! rar ! rar ! rar call hexdig pop psw ; " " ; output (a) lsbits as hex char to console ; a,f hexdig: ani 0fh adi 090h ! daa aci 040h ! daa ; " " ; display (a) as ascii char, use '.' for all controls ; a,f ascii: cpi rub jnc ascii1 cpi ' ' jnc couta ascii1: mvi a,'.' jmp couta ; showcpu: lxi h,regnames mvi b,00 call crlf showcpu1: push b ! push h call dumpreg; id b, name hl^ pop h ! pop b inr b inx h mov a,b cpi 05 cnc blank mov a,b cpi 11 jc showcpu1 ; " " showcd: call setraps; to get length, etc push psw push d ! push b; save for possible run call chkused jnc showcd1 lhld pcsave shld disasmp mvi a,-1 call disassm jmp showcd3 showcd1: dcx h shld dumplast lhld pcsave showcd2: mov a,m call t2hx call blank inx h call dumpdone jnc showcd2 showcd3: pop b ! pop d pop psw ret ; ; left rotate (a) (b)-1 places lrotate: dcr b rz rlc jmp lrotate ; ; read next char from file. carry for eof or error ; a,f charead: push h ! push d ! push b lda charcntr ani 7fh jnz charead1 call fread stc jnz charead2 sta charcntr charead1: mvi d,0 mov e,a lxi h,defdma dad d mov a,m ani 07fh; strip any high order bit call upshift cpi 1ah stc jz charead2 lxi h,charcntr inr m ora a charead2: pop b ! pop d ! pop h ret ; subttl console i/o stuff ; ; line in to buffer. lnptr := first char ; a,f getline: push h mvi a,10 lxi d,consbuf call dos; get console line lxi h,consbuf+2 shld lnptr pop h ret ; ; output 3 blanks, 4th if a = 8 n entry ; a,f blks43: cpi 8 cz blank ; " " ; output 3 blanks ; a,f trplbk: call blank ; " " ; output 2 blanks ; a,f dblblk: call blank jmp blank ; ; t4hx with trailing blank t4hxblk: call t4hx ; " " ; output blank to console ; a,f blank: mvi a,' '; blank ; " " ; output (a) to console ; a,f couta: push d mov e,a mvi a,02 call dos; put console char pop d ret ; ; upshift (a) if lower case ; a,f upshift: cpi 'z'+1 rnc cpi 'a' rc ani 5fh ret ; ; getline and first char newln: call getline ; " " ; (a) := next char from console buffer, upshifted ; At line end a stream of s will be returned. ; compared to cr on exit. ; a,f nextch: call nxtch call upshift cpi cr ret ; ; (a) := next char from console buffer, NOT upshifted ; At line end a stream of s will be returned. ; Compared to cr on exit ; a,f nxtch: push h lxi h,consbuf+1 mov a,m ora a mvi a,cr jz nxtch1 dcr m lhld lnptr mov a,m inx h shld lnptr nxtch1: pop h cpi cr ret ; ; Skip input blanks. z flag for eol. Return non-blk char. ; a,f skipblks: call nextch rz cpi ' ' jz skipblks ret ; ; cr & lf to console ; a,f crlf: mvi a,cr call couta mvi a,lf jmp couta ; ; get console status. nz if char ready ; a,f csta: mvi a,11 call dos ani 1 ret ; ; output $ terminated string de^ ; a,f tstr: mvi a,9 ; " " ; call dos function a, preserve registers dos: push b ! push d ! push h mov c,a call .bdosexu pop h ! pop d ! pop b ret ; ; output (bc) as 4 hex characters with trailing blank ; a,f t4hxbc: push h mov h,b ! mov l,c call t4hxblk pop h ret ; dumpdone: mov a,h ora l stc rz; prevent wrap arounds xchg; dumpdone lhld dumplast xchg ; " " ; zero flag if (hl) = (de), carry if (hl) > (de) ; a,f delesshl: mov a,d ! sub h rnz mov a,e ! sub l ret ; ; next char and check for delimiter nxtchq: call nextch ; " " qdelim: cpi ',' ! rz cpi ']' ! rz ; " " ; filename delimiters, z if so, carry for control qfndelim: cpi cr ! rz cpi tab ! rz cpi '.' ! rz cpi ' ' ! ret ; ; get character, and check for hex getch: call charead ; " " ; check (a) is hex char, convert to binary. ; divert to "err" if not. carry for decimal digit range ; a,f chkhex: sui '0' cpi 10 rc adi 0f9h cpi 16 jnc err cpi 10 rnc jmp err ; ; get next parameter from (de)^ to (hl) ; de := de+2, hl nextparm: xchg call ldem xchg ret ; ; read next hex value, get next char ; a,f,d,e rdhex: call nextch ; " " ; read next hex value from command line to (de); ; initial character in (a) ; a,f,d,e rdhexc: cpi ' ' jz rdhex; deblank xchg lxi h,0000 cpi '#' jz rdec; get a decimal parameter cpi '''' jz rdchar cpi '"' jz rdchar rdhex1: call chkhex dad h ! dad h ! dad h ! dad h ora l ! mov l,a call nxtchq jnz rdhex1 rdhex2: xchg ret ; ; read a char parameter to hl, terminated by (a) on input. ; 2 chars max. Exchange with de ; a,f,d,e (net via rdhex entry) rdchar: push d mov d,a call nxtch jz err mov l,a call nxtch jz err cmp d jz rdc1 mov h,a call nxtch cmp d jnz err rdc1: call nxtch; get delimiter call qdelim jnz err pop d xchg ret ; ; read a decimal parameter to (hl), xchange with (de) ; a,f,d,e (net via rdhex entry) rdec: call nxtchq jz rdhex2 call chkhex jnc err; non numeric character push d mov d,h ! mov e,l dad h ! dad h; 4* dad d; 5* pop d dad h; 10* call index; + (a) jmp rdec ; ; store (de) in (hl)^, increment params ; f, hl := hl+2 storede: push h lxi h,params inr m pop h ; " " ; store de in hl^, advance hl ; h,l sdem: mov m,e ! inx h mov m,d ! inx h ret ; subttl parameter input ; ckpcnt: call chkused jnc err; command not available now ; " " else go do pcnt and return ; count parameters available and parse them ; (a) returns count of parameters. z flag set on it ; de points to first parameter storage point ; If the first parameter is skipped return cy and 8 bit in (a) ; Set hl to 0 ; a,f,d,e,h,l pcnt: call skipblks ; " " ; pcnt with first character in (a) ; a,f,d,e,h,l pcnta: lxi h,params mvi m,0 inx h cpi cr jz pcnt3 cpi ',' jnz pcnt1 mvi a,80h sta params lxi d,0 pcnt1: cnz rdhexc call storede cpi cr jz pcnt3 call rdhex call storede cpi cr jz pcnt3 call rdhex call storede cpi cr jnz err pcnt3: lxi d,params ldax d cpi 81h jz err lxi h,0 inx d ora a ! rlc ! rrc; sign to carry ret ; subttl Trapping ; ; clear any traps ; If (bc) a trap point then pcsave := (bc) ; a,f,d,e,h,l clrtrps: lxi h,trp1set mov a,m; count of traps set mvi m,00 inr a clrtrps1: dcr a rz; all cleared push psw inx h ! mov e,m; clear traps inx h ! mov d,m; trap location call delessbc jnz clrtrps2; not trapped on this one push h mov h,b ! mov l,c shld pcsave pop h clrtrps2: inx h mov a,m; original code (saved) stax d pop psw jmp clrtrps1 ; regnames: db 'CZMEIABDHSP' ; reglocns: db boffset, doffset, hoffset, soffset, poffset ; flgbits: db 1,7,8,3,5 ; zerotctr: lxi h,0 shld tracectr ret ; ; trap entry via RST trapit: di shld hlsave; Save all 8080 regs. pop h shld pcsave push psw lxi h,0002 dad sp pop psw lxi sp,spsave+2 push h ! push psw ! push b !push d; 8080 regs saved. call savz80; save auxiliary registers lhld pcsave mov b,h ! mov c,l; save trap point dcx b; point before trapping instruction call clrtrps ei lhld tracectr mov a,h ! ora l jz trapit5 dcx h shld tracectr call csta jnz trapit5; interrupted lda showtrc ora a jnz showrun call setraps jmp run trapit5: call zerotctr mvi a,'*' call couta lhld pcsave call t4hx call showcpu jmp cmdloop ; ; save auxiliary z80 registers. Main set is free ; interrupts are disabled savz80: call qz80 rz; cant execute instructions exaf push psw exaf pop h shld afprime lxi h,0 dad sp; save stack, use sp as index lxi sp,afprime exx push b ! push d ! push h pushix pushiy exx sphl; restore stack ldai mov l,a ldar mov h,a shld ireg ret ; ; index word (hl) := (hl) + 2 * (a) [max a =127] ; a,f,h,l indexwd: add a ; " " ; index (hl) := (hl) + (a) ; a,f,h,l index: add l mov l,a rnc inr h ret ; ; transfer to case (a) on table (hl)^ ; a,f,h,l (and routine) casexfr: call indexwd mov a,m ! inx h ! mov h,m ! mov l,a pchl ; subttl Trap setting, code analysis ; ; at entry hl points to op to decode ; at exit, de = primary trap, bc = secondary trap ; and hl points past the instruction, for tracing etc. ; z flag set to release control (xfr to system), ; else (a) = 2 for 1 trap, 3 for 2 traps, carry set ; a,f,b,c,d,e,h,l setraps: lhld pcsave lxi d, progrtn call delesshl rz; release control on outer block ret mov b,m xchg lxi h,reboot call delesshl rz; release control on reboot mvi a,0c7h+8*traplvl; (rst traplvl) cmp b rz; release control on trap instr. ; " " ; Entry here, with op pointer in de, avoids release control exits ; a,f,b,c,d,e,h,l chkop: ldax d mov b,a inx d push d; stack a possible word operand ptr call optype; (or next adr=trap pt for 1 byte ops) sta opkind; preserve for disassembler use push d ret; to xfr address from table. ; ; Return a := opcode classification for opcode (b) ; de = action address, h=xtended length, l=prefixed length (added) ; a,f,d,e,h,l optype: mvi d,-1 lxi h,opmsktbl-4 optype1: inr d inx h ! inx h ! inx h ! inx h mov a,m ! ana b ! inx h cmp m ! inx h mov a,d jnz optype1 mov e,m ! inx h ! mov d,m; de := action point inx h push psw mov a,m ! inx h ! mov l,m ! mov h,a pop psw ret ; op macro mask, val, action, lghxtnd, lghidx db mask, val dw action db lghxtnd,lghidx endm ; ; Table allows recognition of Z80 opcode lengths. ; * marks items whose order in table is important. ; Early entries found first. opmsktbl: ; mask val exec. xtnd index (SEE op macro ABOVE) op 0ffh,076h,opmovetc,1,0; 0 *mov m,m hlt 76 (eliminate in all) op 0cfh,009h,opmovetc,2,1; 1 dad 9 19 29 39 op 0f7h,022h,oplxietc,1,3; 2 lhld shld 22 2a op 0f7h,023h,opmovetc,1,1; 3 inc/dec hl 23 2b op 0feh,034h,opmovetc,1,2; 4 inr/dcr m 34 35 op 0ffh,036h,opmvietc,1,3; 5 *mvi m 36 op 0c7h,046h,opmovetc,1,2; 6 *mov rr,m 46 4e 56 5e 66 6e (76) 7e op 0ffh,073h,opmovetc,3,2; 7 *mov m,e 73 op 0efh,064h,opmovetc,2,2; 8 *mov h,h, mov m,h 64 74 (for 64180) op 0f8h,070h,opmovetc,1,2; 9 *mov m,r 70 71 72 (73 74) 75 (76) 77 op 0c7h,086h,opmovetc,1,2; 10 arith m 86 8e 96 9e a6 ae b6 be op 0ffh,021h,oplxietc,2,3; 11*lxi h 21 op 0ffh,0cbh,opbitpic,1,3; 12 set/res/bit/shifts 0cb op 0ffh,0e3h,opmovetc,1,1; 13 xthl e3 op 0fbh,0e1h,opmovetc,1,1; 14 pop/push hl e1 e5 xyjmp equ ($-opmsktbl)/6 op 0ffh,0e9h,oppchl, 1,1; 15 pchl 0e9 op 0ffh,0f9h,opmovetc,1,1; 16 sphl 0f9 ; The above portion order is wired into the disassembler section op 0ffh,0edh,opextend,1,0; 17 extension ed op 0c7h,043h,opmovetc,3,1; 18 mov rr,e 43 4b 53 5b 63 6b 73 7b reti equ ($-opmsktbl)/6 op 0f7h,045h,opmovetc,1,1; 19 mov (retn, reti) 45,4d op 0dfh,0ddh,opxyregs,1,0; 20 x/y prefixes dd fd op 0f7h,000h,opmovetc,2,0; 21*exaf,nop 0 8 op 0c7h,000h,opjr, 2,0; 22*jr/djnz (0 8) 10 18 20 28 30 38 op 0ffh,0c3h,opcaljmp,1,0; 23 jmp 0c3 op 0ffh,0cdh,opcaljmp,1,0; 24 call 0cd op 0f7h,032h,oplxietc,1,0; 25 lda sta 32 3a op 0ffh,0c9h,opreturn,1,0; 26 ret 0c9 op 0c7h,0c7h,oprstnn, 1,0; 27 rst c7 cf d7 df e7 ef f7 ff op 0c7h,006h,opmvietc,1,2; 28*mvi 6 e 16 1e 26 2e (36) 3e op 0c7h,0c6h,opmvietc,1,0; 29 aritopi c6 ce d6 de e6 ee f6 fe op 0c7h,0c2h,opcjccd, 1,0; 30 j(ccd) c2 ca d2 da e2 ea f2 fa op 0c7h,0c4h,opcjccd, 1,0; 31 c(ccd) c4 cc d4 dc e4 ec f4 fc op 0c7h,0c0h,oprccode,1,0; 32 r(ccd) c0 c8 d0 d8 e0 e8 f0 f8 op 0cfh,001h,oplxietc,2,0; 33*lxi 1 11 (21) 31 op 0f7h,0d3h,opmvietc,1,0; 34 in/out d3 db op 000h,000h,opmovetc,1,1; 35 the rest, all 1 byte ; ; ---- Cases for SETRAPS execution ----- ; ; Check for z80 operating, and set opflg for this op if so ; z flag if not z80 in operation ; a,f qz80: lda z80flg ora a ret ; ; 0cbh - bit pickers opbitpic: call qz80 jz opmovetc pop h inx h jmp opxtnd2 ; ; 0ddh & 0fdh - x/y index reg ops opxyregs: call qz80 jz opmovetc pop h ! push h ! mov b,m call optype sta zopkind; save for disassembler cpi xyjmp mov a,l; opcode length jnz opxtnd1 pop h dcx h mov a,m; which index inx h inx h push h; advance, length 2 cpi 0ddh lhld ixreg jz oppchl1 lhld iyreg jmp oppchl1 ; ; 0edh - extension opextend: call qz80 jz opmovetc; 1 byte nop on 8080 pop h ! push h ! mov b,m call optype sta zopkind; save for disassembler cpi reti jz opreti mov a,h; size opxtnd1: pop h call index; point past instruction opxtnd2: push h jmp opmovetc ; opreti: pop h ! inx h ! push h jmp opreturn ; opjr: call qz80 jz opmovetc pop h mov a,m inx h push h mov e,a ral; sign to carry sbb a; extend sign mov d,a dad d xchg jmp setrapx2 ; ; jmp/call opcaljmp: call operand jnz setrapx1 ; " " ; ret opreturn: call stacktop jmp setrapx1 ; ; rst n oprstnn: mov a,b ani 38h mov e,a mvi d,0 jmp setrapx1 ; ; pchl oppchl: lhld hlsave oppchl1: xchg call qsyscall jnz setrapx1 jmp opreturn ; ; j(ccd)/c(ccd) opcjccd: call operand jnz setrapx2 ; " " ; mov etc (one byte, no xfrs) opmovetc: pop d ! push d jmp setrapx1 ; ; r(ccd) oprccode: call stacktop ; " " ; set two traps. bc is following op, de is destination setrapx2: pop b ! push b mvi a,02 jmp setrapx ; ; lxi, lhld, shld, lda, sta oplxietc: pop d ! inx d ! push d ; " " ; mvi, in, out, aritopi (ex cpi) opmvietc: pop d ! inx d ! push d ; " " ; To set one trap only setrapx1: mvi a,01 ; " " ; To set (a) traps setrapx: inr a stc pop h; set hl to next op code (for lgh) ret ; ; ---- Subroutines for SETRAPS ---- ; ; load de := wd operand pointed at by TOS of caller. ; z flag if system entry point ; a,f,b,c,d,e,h,l operand: pop b ! pop h call ldem push h ! push b ; " " ; z flag if (de)=system transfer point ; a,f,h,l qsyscall: lhld bdos+1 call delesshl rz lda reboot+2 cmp d rz; a direct bios call lhld .bdosexu+1; catches lxi h,bdos; push h; ret jmp delesshl ; ; Set de to value on top of run-time stack ; d,e,h,l stacktop: lhld spsave ; " " ; load de from hl^, advance hl ; d,e,h,l ldem: mov e,m ! inx h ! mov d,m ! inx h ret ; subttl ----- Initialization - used once only ----- ; ; Initialization init: lhld bdos+1 shld .bdosexu+1 mvi c,6 lxi d,.bdos init1: dcx h ! dcx d mov a,m ! stax d dcr c jnz init1; copy serial etc - look like bdos lxi h,.bdosexu shld .bdos+1 lxi h,.bdos shld bdos+1 lxi d,signon call tstr mvi a,0c3h; (jmp) sta 8*traplvl lxi h,trapit shld 8*traplvl+1 lxi h,firstoclear mvi c,lastoclear-firstoclear init2: mvi m,0; We are using the CCP stack inx h dcr c jnz init2; clear it all out mvi a,cbufsiz sta consbuf lxi h,tpa shld disasmp shld dumptr shld unloaded shld pcsave; must be at or before operand locn xmk equ $-ddtbgn; for overlay error checking lxi sp,pcsave lxi h,0 mvi c,(pcsave - stack)/2 init3: push h dcr c jnz init3; clear 8080 reg. images lxi d,progrtn push d; default return for loading push h; default bias for loading lxi d,c8080 stc sbb a jpe init6; z80 clears overflow sta z80flg lxi d,cz80 init6: call tstr lda tfcb+1 cpi ' ' jnz loadit jmp progrtn; MUST be last instruction ; ; This definition re-uses the init area as execute time stack return equ $-2; rejammed to "progrtn" ; ; subttl ---- Initialized storage ----- ; consbuf: db cbufsiz ; signon is stored in console buffer area, overwritten signon: db 'DDTZ v' db ver/10 + '0', '.', ver MOD 10 +'0',debugver db ' by CB Falconer. CPU=$' c8080: db '8080$' cz80: db 'Z80$' cbufused equ $-consbuf firstoclear: ; ; Macro for storage allocation errors only, SLRMAC specific px macro v, xmsg .printx v&xmsg endm ; ; Code storage space for this segment codesize equ $-ddtbgn; for loader/relocater ; subttl ---- Uninitialized storage ----- ; org signon; Re-use signon space ds 1; returned length ds cbufsiz; actual storage ; if ($-consbuf) LT cbufused xtra set cbufused - ($-consbuf) +++ ERROR signon too long for overlay +++ px %xtra, < excess bytes> endif ; ; Following are uninitialized variables (also last of above buffer) z80flg: ds 1; Is a Z80 running? ; ; Storage for auxiliary Z80 registers iyreg: ds 2 ixreg: ds 2 hlprime: ds 2 deprime: ds 2 bcprime: ds 2 afprime: ds 2 ireg: ds 1 rreg: ds 1; saved, but never restored. ; base ds 2; base for offset displays baseval ds 2; recorded base, set by @ ; unloaded: ds 2; Marks memory not loaded tracectr: ds 2; Counter for steps to execute showtrc: ds 1; Flag to trace each step trp1set: ds 1; count of traps set ds 2; location of first trap ds 1; saved opcode at 1st trap ds 3; second trap, as 1st trap lastoclear: ; ; Storage and code for this segment pages equ (($-ddtbgn)+255) / 256; for loader ; ; Following storage overlays the initialization code. org init; reuse this space ; ; dis/assembler communication variables disasmp: ds 2 dendptr: ds 2 aflag: ds 1; reserve for decoupling dis/assem opkind: ds 1; set by chkop zopkind: ds 1; set on z80 only code ; ; Dump control dumptr: ds 2; Next to dump for D command dumplast: ds 2; last to dump ; ; input processing lnptr: ds 2; pointer to unused char in consbuf params: ds 7; 1st is count, then input params charcntr: ds 1; for hex input ; ds stksize; DDT run time stack stack: desave: ds 2; on TOS when control xfr bcsave: ds 2 flgsave: ds 1 asave: ds 1 spsave: ds 2 hlsave: ds 2 pcsave: ds 2 ; if ($-ddtbgn) GT xmk xtra set ($-ddtbgn) - xmk +++ ERROR too much stack space for overlay +++ px %xtra, < excess bytes> endif ; poffset equ pcsave-$; offsets for display/modify soffset equ spsave-$ hoffset equ hlsave-$ doffset equ desave-$ boffset equ bcsave-$ ; ; Working variables for dis/assembler section. storeptr: ds 2 exitstk: ds 2 buff: ds 16 ; usrstack equ return-$ px %usrstack, < bytes in user stack> ; org lastoclear; for slrmac bug ; end