page 200,150 ; for the listfile formatting ;----------------------------------------------- ; ; Project: DDT ; ------------ ; ; DDT: a Driver Debugger Tool for the Atari PortFolio ; ; version: 0.87 ; date : 12-09-1998 ; by : P.R. Faasse ; ; Description: ; ------------ ; ; This is my own small and adaptable debugger. It has most things ; I find desirable in a debugger, with an enormous extra: all I ; ever needed to debug drivers. This extension to the previous ; debugger was made when I was fighting with my PoFoIDE (C) ; device driver and found that neither DOS's DEBUG, nor my own ; debugger was any help at all. ; ; This debugger will accept either a .com or a .sys file to be ; debugged. There is no support for .exe files. I develop most of ; my code in assembler and NEVER make .exe files. M$ may prefer ; them, I loathe them. They are an invitation to wasting loads of ; memory. On a PortFolio memory is one thing I've always got a ; shortage of. ; ; When a .com file is loaded this debugger will act as most ; debuggers do: You can Trace, Continue (till address) and dump ; data as you desire. ; ; When a .sys file (device driver) is loaded this debugger has ; the (admitted minimal) services available to allow you to debug ; this driver. You can set command packets, call the driver's ; strategy and/or interrupt routine, even trace through them (use ; 'B' to set breakpoints on entry of the driver...). At this ; moment I have support for block device drivers only. I have no ; experience with character/clock device drivers... ; ; NB: This debugger is written for an Atari PortFolio. It will ; however work on any PC-compatible platform. The entire ; development ant testing of this debugger was done on a ; normal PC. The only real PortFolio-specific part is the ; fact that I have restricted all screen output to the left ; 40 characters of the screen and 8 lines at a time. ; The Portfolio's screen is 40 x 8 characters..... ; ; One PoFo-special is the divide-by-zero trapping. The PoFo ; is a real fanatic about dividing by zero. It has a trap ; handler with an error in it for divide-by-zero, and it ; seems to divide by all the return parameters of every ; system call. It just derails TOO OFTEN on a /0 error. This ; debugger traps the /0 errors, and displays where they ; happened. This trapping is NOT watertight, it could cause ; other problems. The /0 trap handler uses DOS/DIP calls. If ; that happens while the computer is running DOS/DIP ; functions you get the famous DOS re-entrancy problems. The ; risk of that is however less serious that the certain crash ; of the PoFo when not trapping is done. ; ; ; Copyright notice: ; ----------------- ; ; (C) 17-08-1998 by P.R. Faasse, Hakfort 337, 1102 LA Amsterdam ; ; The Program in this file is subject to the GNU Copyright ; version 2.0 or later. See the file COPYING for details. ; ; Warranty: ; --------- ; ; As nearly all computer programs this code comes without any ; warranty. When using this code I am not responsible if anything ; goes wrong with your hardware/software, data, married life or ; otherwise. I have made the program, tested it on a normal PC ; and an Atari PortFolio and have found no bugs. May you find the ; same... If not, I accept no responsibility. ; ; Feedback: ; --------- ; ; I have put considerable effort into making this program run ; witout errors. I have no illusions of infallability, so I would ; not be greatly surprised if anyone else does find errors. If ; you find any, be so kind as to drop me a note. I can be reached ; at the following addresses: ; ; e-mail: faasse@nlr.nl ; ; mail: P.R. Faasse ; Hakfort 337 ; 102LA Amsterdam ; Netherlands ; ; Update history: ; --------------- ; ; aug 1998: ; ; Copied the code from a previous version (called debug) to ; this program. Debug did not have any support for driver ; debugging. Debug was a very down-to-earth hex debugger. ; ; 14-09-1998: ; ; Started adding a disassembler. Not all codes available ; yet, but it's a beginning. A rudimentary user-interface ; is there too. The commands 'u' (disassembler at IP), and ; 'v' (next instruction) are there. ; ; 16-09-1998: ; ; First version of the disassembler ready. May still have ; some errors inside, so be carful when using it. Upgrading ; the user interface for the disassembler now. ; ; 17-09-1998: ; ; Made a CI for the 8088 and put it in place. That make the ; thing a little smaller and easier to understand. ; Added nametable support. ; ; 19-09-1998: ; ; Found one nasty bug. This one did not show up on the PC ; (80586) where I was testing the ddt, just on the PoFo ; itself. The 8088 handles a /0 with trace flag set ; differently than the 80(2345)86.. The 80x86 (x>1) does a ; /0 trap and forgets about the trace flag. The 8088 does ; the /0 trap, and starts tracing the /0 handler. I changed ; that to make the 8088 act exactly the same (user level) ; as a 80286/386 etc.. I know the div/idiv instructions ; that can cause a /0 trap are 2-bytes long. I used that ; knowledge to (blindly...) substract 2 from the IP (on ; stack). A 286 etc.. (with perhaps 3-bytes div..) will not ; encounter this because they do not GET into the trace ; handler in this way.. Should be waterproof. Undocumented ; 8088 div instructions could blast this scheme however.... ; ; I also: ; - changed the UnAsm default segment. I attempt to make it ; do-what-I-want, hope you like it.. ; - removed the text 'CS IP code' in trace modus, took too ; much screen. ; ; Intended other changes: ; ; - Compression of the disassembler tables. I have done none ; now, the tables (DisTab1..9) and the strings table are ; open for size reduction. ; ; started: The string table becomes smaller if you use a ; single capital as en-of-string. This works. SegMt and ; LHook where always together, made into one routine. ; I've looked at (even built) some compression for the ; parser tables, the results of that where so minimal ; that I prefer the uncompressed tables. I gained some 20 ; bytes, the table mechanism became difficult to ; understand.. ; ; - A better command parameter parser. That too should make ; the code smaller. I could use the disassembler's parser ; for that. ; ; Perhaps I can regain the space I lost when adding the ; names table support. I'll keep the address names table ; support, it's too nice.. ; ; There is one bug in the 'Next' handler. It does not ; handle a 'FF'-indirect call. For the moment I've ; commented that out. Next on such an instruction will give ; you a trace.. Bug is fixed. 'Next' on a call [bx+12H] ; should work too now, tested:works ; ; I also changed the output texts a little. The start ; message no longer includes all the help messages. That ; keeps the screen a little cleaner. ; ; 20-09-1998: ; ; Started making the 'registers' output somewhat more ; usable. The code bytes are not needed, the PSW bits are ; better displayed bitwise. That works best if I move the ; register output around a little. ; ; Found one bug in the process: The debugger could go wild ; when the debuggee sets somthing wild in the status ; register. I'll have to correct that when the debugger is ; entered. Done, no more strange texts when a program comes ; back to the debugger with the direction flag set.... ; Also set the default state of the interrupt flag for the ; program being debugged to '1'. All programs had been ; running with interrupts disabled till now.... ; ; 04-10-1998: ; ; Found and killed one bug in the disassembler. The ; DWModRmPar part would use the last byte of the offset to ; produce the register when the D-bit is 0. This was ; because the bl register held the last byte of the offset ; instead of the extension byte. A push/pop of bx should ; fix that. ; ; 15-11-1998: ; ; Removed the sub cx,0200H from the names table scan. This ; should make Klaus's name-table-append mechanism work ; good. ; ;------------------------------------------ code segment para 'code' ; define code segment org 0100h ; start at offset 0100H assume cs:code, ds:code, es:code, ss:code start: jmp init ; do init actions ;== data =================================================== ; constants ; --------- ; general constants LF equ 00AH ; code CR equ 00DH ; code EOS equ '$' ; End Of String code ; all parameters, buffers etc.. ; ----------------------------- ; init message qmess db "DDT (Driver Debug Tool) v0.87.",cr,lf,eos ; help message hmess db "debug: Cont Dump Edit In Mod Next",cr,lf db " Out Quit Regs Seg Trace UnAsm ?",cr,lf db " Where",cr,lf,EOS dhmess db "driver: Brk Cmd Dump Edit Int Strat",cr,lf db " Opt Unit View eXec",cr,lf,eos ; the dump header strings imess db "segment = ",'$' ; This one is used fo bothe normal and driver buffer dump dmess db "adr: 08 19 2A 3B 4C 5D 6E 7F 01234567",CR,LF,EOS ; command prompt pmess db ">",EOS ; registers command strings rmess db "AX BX CX DX SI DI BP SP",CR,LF,EOS smess db cr,lf,"CS IP DS ES SS PSW ODITSZAPC",CR,LF,EOS ; when no memory there mmess db "No memory available",cr,lf,eos ; when no file name is geiven nmess db "No file loaded",CR,LF,EOS ; when file load goes wrong lmess db "Error loading file",CR,LF,EOS bmess db "File too large",CR,LF,EOS ; command error string cerr db "Pardon ?",CR,LF,EOS ; newline nlmess db CR,LF,EOS ; data buffer and parameters databuf equ 080H ; use DTA as data buffer ; I/O defaults ioseg dw 0 ; I/O segment ioofs dw 0100H ; I/O offset ionum dw 28H ; number of bytes (default = 28H, to ; fit nicely on the Portofolio screen) ; file interface fhandle dw 0 ; file handle ; driver debug parameters DRVR db 0 ; driver debug: 1 = driver ; 0 = .COM file drvCS dw 0 ; code segment of the driver DrvBrk db 0 ; driver breakpoints 1=yes ; NameTable pointer ; nb: a name table is preceeded by the bytes ; db 'ddt nt' NameTab dd 0 ; default: no nametable ntmess db 'Name table found',cr,lf,eos ; single-stepping interface, ; storage for all user registers savAX dw 0 ; AX savBX dw 0 ; BX savCX dw 0 ; CX savDX dw 0 ; DX savSI dw 0 ; SI savDI dw 0 ; DI savBP dw 0 ; BP savSP dw 0 ; SP savCS dw 0 ; CS savIP dw 0100H ; IP default for .com savDS dw 0 ; DS savES dw 0 ; ES savSS dw 0 ; SS savF dw 0200H ; PSW default ints ON ; old vector storage for trace oldTs dw 0 ; old trace interrupt segment oldTo dw 0 ; old trace interrupt offset ; my breakpoint storage brkpta db 0 ; breakpoint set (1=yes) brkpts dw 0 ; breakpoint segment brkpto dw 0 ; breakpoint offset brkptc db 0 ; breakpoint code byte brkptn equ 0CCH ; interrupt code byte ; old vector storage for breakpoints oldBs dw 0 ; old breakpoint interrupt segment oldBo dw 0 ; old breakpoint interrupt offset ; old vector storage for /0 oldZs dw 0 ; old divide-by-zero interrupt segment oldZo dw 0 ; old divide-by-zero interrupt offset ;== Name Table =========================================== ; ; This is an example name table. ; If you build such a table into your code, ddt ; will show the names of the subroutines etc.. that are ; in the table. NB: No reference or change in the ; executable statements are needed to make this work. ddt ; will simply scan for the existence of this kind of table ; in the code, and use it if it finds one. ; ; It looks like a good idea to use the following mechanism to ; allow easy ON/OFF switching of the name table: ; ; un-comment the next line to get a name table ;With_NT equ 1 ; names tabel is now OFF.. ; check if the names table is requested. IFDEF With_NT ; This macro NM can make life a load easier ; when creating a name table. NB: ANY location in ; the code can be in the name table. The disassembler ; will show the name with the location. Note that the ; parameter NtName is used twice in the macro, first ; to get the offset of the address in the cs, seccond ; as the string to be printed. The names table mechanism ; does not require these to be the same, it is the most ; obvious way to use the name table however ; name table entry definition macro NM macro NtName dw NtName db '&NtName',0 endm ; signal string for ddt. It will recognize the ; existence of the table when it finds this string. ; It could be located anywhere in the code. The program ; itself will probably never reference this table. It is ; there for the sole purpose of pseudo-symbolic ; debugging. db 'ddt nt' ; for ddt: name table follows ; the name table entries, in MASM 'pur sang' dw Init ; offset of the routine db 'Init',0 ; string to print as name ; The macro can make life a load easier here ; simply: NM ; Note: comment is allowed here... NM Main ; the start of the code NM SkipSp ; skip spaces on input line NM Input ; read user input NM MainC ; command entry of Main NM CI ; Command Interpreter (small parser) NM Mess ; give message (dx) NM Prompt ; give promp message NM DisHexByte ; display hex byte NM DisHexWord ; display hex word ; well, I could put all names here, no point in doing ; so; The idea should be clear now.. ; you CAN assemble ddt with this names table active. ; There is little point in doing so: ddt cannot ; debug itself. The trace/div0/breakpoint vectors can be ; used only once, ddt grabs them. NB: ddt CANNOT be used ; upon itself. Doing so may SEEM to work. Pretty soon ; however, the ddt being 'ddt-ed' will take over. ; end of name table: DO not FORGET, else the disassembler ; will probably simply crash.... dw 0 ; end of table is db 0 ; an all-zero entry ENDIF ; ;== program ============================================== prog proc near ; the actual program ;------------- ; init things ;------------- main: mov dx,offset qmess ; call Mess ; ;------------------------------------- ; prepare for tracing and breakpoints ;------------------------------------- mov ax,03501H ; get Trace interrupt vector int 21H ; mov ax,es ; store for later mov oldTs,ax ; mov oldTo,bx ; mov ax,cs ; mov es,ax ; mov ax,02501H ; set new vector mov dx,offset TrRet ; int 21H ; mov ax,03503H ; get breakpoint interrupt vector int 21H ; mov ax,es ; mov oldBs,ax ; mov oldBo,bx ; mov ax,cs ; mov es,ax ; mov ax,02503H ; set new vector mov dx,offset BrRet ; int 21H ; mov ax,03500H ; get /0 interrupt vector int 21H ; mov ax,es ; mov oldZs,ax ; mov oldZo,bx ; mov ax,cs ; mov es,ax ; mov ax,02500H ; set new vector mov dx,offset D0Ret ; int 21H ; ;------------------------------------- ; get a segment for debugging ;------------------------------------- mov ax,4800H ; mov bx,1000H ; get 64 Kb mov ioofs,bx ; int 21H ; jnc maini1 ; ; no 64 K available use what is there cmp bx,0 ; test for no memory jnz maini0 ; ; set parameters mov ioofs,0 ; ; give message mov dx,offset mmess ; jmp mainln1 ; ; allocate what is there maini0: mov ioofs,bx ; save size mov ax,4800H ; int 21H ; ; ax = segment for debugging, ioofs = size maini1: mov savES,ax ; set all segments to mov savDS,ax ; current cs mov savCS,ax ; mov savSS,ax ; mov ioseg,ax ; default I/O segment too mov word ptr InstLoc+2,ax ; default unasm segment mov ax,ioofs ; compute stack pointer shl ax,1 ; shl ax,1 ; shl ax,1 ; shl ax,1 ; dec ax ; mov savSP,ax ; ; clear the block mov cx,ax ; cx = size mov di,0 ; mov es,ioseg ; mov al,0 ; rep stosb ; mov ax,cs ; mov es,ax ; ; make real ioofs mov ax,0100H ; mov ioofs,ax ; ;--------------------------------------------- ; load command tail as program (.COM or .SYS) ;--------------------------------------------- mov si,080H ; si -> command tail cmp byte ptr [si],0 ; see if command tail there jnz maintail ; jmp mainln ; no tail -> just debug ; make command tail ASCIZ maintail: mov bl,[si] ; inc si ; mov bh,0 ; mov byte ptr [si+bx],0 ; ; test if a driver is being debugged cmp byte ptr [si+bx-1],'s' ; jnz debcom ; cmp byte ptr [si+bx-2],'y' ; jnz debcom ; cmp byte ptr [si+bx-3],'s' ; jnz debcom ; ; debugging a driver mov byte ptr DRVR,1 ; set driver flag mov word ptr ioofs,0 ; default offset = 0 mov byte ptr InitCmdStr,CR ; fill in the command ; tail ; look for file name debcom: call SkipSp ; jc mainln ; empty -> no file ; file name there, open it as a file mov dx,si ; mov ax,03D00H ; open for read int 21H ; jc mainln ; ; open ok, put file handle mov fhandle,ax ; ; prepare for read mov bx,fhandle ; get handle number mov cx,savSP ; get block size sub cx,0200H ; leave 256 bytes stack ; driver -> from 0000, else: from 0100 cmp byte ptr DRVR,1 ; jnz ldcom ; ; specials for driver debugging mov dx,0000H ; loads at address 0 mov ax,savCS ; save code segment mov drvCS,ax ; mov ax,0000H ; default IP at 0000H mov savIP,ax ; jmp doload ; ; config for .com file debug ldcom: mov dx,0100H ; store from 0100H onward ; generic part again doload: mov ax,savCS ; in new CS mov ds,ax ; mov ax,3F00H ; int 21H ; mov ax,cs ; get ds back mov ds,ax ; ; test errors jnc mainl1 ; ; handle errors mov dx,offset lmess ; jmp MainLn1 ; ; test if the file fits mainl1: mov cx,savSP ; get block size again sub cx,0200H ; cmp ax,cx ; jc mainl2 ; ; file is bigger than block mov dx,offset bmess ; warn and continue call Mess ; ; no errors, close file mainl2: mov ax,03E00H ; mov bx,fhandle ; int 21H ; ; goto help handler jmp MainFd ; ; no file loaded mainln: mov dx,offset nmess ; MainLn1:call Mess ; jmp ScanNtDone ; ; file-load done MainFd: ;-------------------- ; look for a name table ;-------------------- mov si,savIP ; from beginning of program mov ds,savCS ; mov cx,savSP ; cx = block size ; sub cx,0200H ; v0.86 -> v0.87 change ; This is the ONLY change.. ; What a version upgrade... ScanNT: cmp word ptr [si],'dd' jnz ScanNt1 ; cmp word ptr [si+2],' t' jnz ScanNt1 ; cmp word ptr [si+4],'tn' jnz ScanNt1 ; ; names table found! add si,6 ; mov ax,si ; mov cs:word ptr NameTab,ax ; save it's pointer mov cs:word ptr NameTab+2,ds ; mov ax,cs ; mov ds,ax ; ; say what & where mov dx,offset ntmess; call Mess ; jmp ScanNtDone ; ScanNt1:inc si ; loop ScanNT ; ; no names table found mov ax,cs ; mov ds,ax ; ScanNtDone: ;-------------------- ; prepare disassembler parameters ;-------------------- mov ax,savIP ; start at start of code mov word ptr InstLoc,ax ; ;------------------- ; get command ;------------------- ; give prompt MainC: call Prompt ; send prompt ; get user input call Input ; jc MainC ; ignore empty input ; get command pointer mov al,[si] ; get opcode ; make si -> pars (if any..) MainP1: inc si ; cmp byte ptr [si],0 ; end of command jz MainPe ; ; skip rest of command cmp byte ptr [si],' ' ; skip non-spaces jnz MainP1 ; ; di -> command; si -> parameters MainPe: nop ; end of Main parameter handling ; test if driver/non-driver command set mov di,offset DrvCommandTab ; driver table cmp Drvr,0 ; jnz MainC1 ; mov di,offset DbgCommandTab ; just debugger MainC1: jmp CI ; ; a small macro to make enterin the command table ; easier Cmd macro CmdChar,CmdHand ; I nicked some db '&CmdChar' ; handy macro-tricks dw CmdHand ; from Klaus in the endm ; pofoide/ddt time.. ; table of commands when driver loaded DrvCommandTab: Cmd B,DrvBreakpoints ; Driver commands are the Cmd C,DrvCmd ; upper-case commands Cmd D,DrvDataBuf ; Cmd E,DrvEdit ; Cmd I,DrvInterrupt ; Cmd O,DrvOpt ; Cmd S,DrvStrategy ; Cmd U,DrvUnit ; Cmd V,DrvView ; Cmd X,DrvExec ; ; table of commands when no driver loaded DbgCommandTab: Cmd ?,MainH ; Normal commands are Cmd c,Continue ; lower-case Cmd d,Dump ; Cmd e,Edit ; Cmd i,Inp ; Cmd m,RegEd ; Cmd n,Next ; Cmd o,Outp ; Cmd q,Quit ; Cmd r,Regs ; Cmd s,SetSeg ; Cmd t,Trace ; Cmd u,UnAsm ; Cmd w,Where ; db 0FFH ; No valid command dw Error ; ;---------------------------------------------------- ; Routine/handler CI ; purp : a command Interpreter for the 8088 ; in : al = command char ; ds:di -> command table ; out : control transfered to the 'routine' indicated ; uses : flags, si CI: cmp byte ptr [di],0FFH ; test end of table jz CiFound ; cmp al,[di] ; test for match jz CiFound ; ; skip this entry add di,3 ; jmp CI ; CiFound:inc di ; push [di] ; ret ; ;-------------------- ; print help message ;-------------------- MainH: mov dx,offset hmess ; give help message call Mess ; cmp byte ptr DRVR,1 ; jnz MainHcom ; mov dx,offset dhmess; call Mess ; MainHCom: jmp MainC ; ;----------------------- ; Quit the debugger ;----------------------- ; restore breakpoint and trace vectors Quit: mov ax,02501H ; Trace vector mov dx,oldTo ; mov bx,oldTs ; mov ds,bx ; int 21H ; mov ax,cs ; mov ds,ax ; mov ax,02503H ; Breakpoint vector mov dx,oldBo ; mov bx,oldBs ; mov ds,bx ; int 21H ; mov ax,cs ; mov ds,ax ; mov ax,02504H ; /0 vector mov dx,oldZo ; mov bx,oldZs ; mov ds,bx ; int 21H ; mov ax,cs ; mov ds,ax ; mov ax,4C00H ; int 21H ; ;-------------------------------------------------- ; Error handler ;-------------------------------------------------- Error: mov dx,offset cerr ; call Mess ; jmp MainC ; ;-------------------------------------------------- ; SetSeg handler ;-------------------------------------------------- SetSeg: call SkipSp ; skip spaces jnc setseg1 ; ; error handler jmp Error ; setseg1:call GetHexWord ; get segment adres mov ioseg,ax ; store mov word ptr InstLoc+2,ax ; jmp MainC ; ;-------------------------------------------------- ; Dump handler ;-------------------------------------------------- Dump: ; handle default parameters call SkipSp ; look for parameters jc dodump ; ; get start address call GetHexWord ; read address and ax,0FFF8H ; make 8 byte boundary mov ioofs,ax ; call SkipSp ; skip spaces jnc dump01 ; ; only one par, go dump with this jmp dodump ; ; second par too dump01: call GetHexWord ; add ax,7 ; and ax,0FFF8H ; mov ionum,ax ; ; go execute a dump dodump: mov dx,offset imess ; call Mess ; mov ax,ioseg ; call DisHexWord ; mov dx,offset nlmess; call Mess ; mov dx,offset dmess ; print header call Mess ; ; get data from dump segment mov si,ioofs ; mov bx,ionum ; mov ax,ioseg ; mov es,ax ; ; go dump memory dump1: mov ax,si ; test if ready cmp bx,0 ; jz dumpe ; ; save si for ASCII dump push si ; ; Display address mov ax,si ; call DisHexWord ; ; 8 bytes hex mov cx,8 ; dump 8 bytes dump2: mov al,' ' ; call DisChar ; mov al,es:[si] ; call DisHexByte ; inc si ; dec bx ; loop dump2 ; mov al,' ' ; space between call DisChar ; ; ASCII (if this goes) mov cx,8 ; dump ASCII part pop si ; dump3: mov al,es:[si] ; cmp al,' ' ; I do not dump below jc dump5 ; dump4: call DisChar ; inc si ; loop dump3 ; mov dx,offset nlmess; call Mess ; jmp dump1 ; dump5: mov al,'.' ; jmp dump4 ; ; dump done dumpe: mov ax,cs ; mov es,ax ; mov ax,ionum ; add ax,ioofs ; mov ioofs,ax ; ; get next command jmp MainC ; ;-------------------------------------------------- ; Registers handler ;-------------------------------------------------- Regs: ; display header mov dx,offset rmess ; print header call Mess ; ; display registers mov si,offset savAX ; mov cx,7 ; 7 registers regs1: lodsw ; call DisHexWord ; display mov al,' ' ; one space separation call DisChar ; loop regs1 ; ; last register without trailing space lodsw ; call DisHexWord ; mov dx,offset smess ; segments message call Mess ; mov si,offset savCS ; mov cx,6 ; 6 registers regs2: lodsw ; call DisHexWord ; display mov al,' ' ; one space separation call DisChar ; loop regs2 ; ; now the PSW bits mov bx,savF ; get the bits mov cx,6 ; reg3: mov al,'0' ; test bh,00001000B ; jz reg4 ; mov al,'1' ; reg4: call DisChar ; shl bx,1 ; loop reg3 ; ; rest of the bits mov cx,3 ; reg5: mov al,'0' ; shl bx,1 ; test bh,00001000B ; jz reg6 ; mov al,'1' ; reg6: call DisChar ; shl bx,1 ; loop reg5 ; mov dx,offset nlmess; goto next line call Mess ; jmp MainC ; ;-------------------------------------------------- ; Edit handler ;-------------------------------------------------- Edit: ; handle default parameters call SkipSp ; look for parameters jc doedit ; ; get start address call GetHexWord ; read offset address mov ioofs,ax ; ; go edit doedit: mov ax,ioseg ; display current values call DisHexWord ; mov al,':' ; call DisChar ; : : mov ax,ioofs ; call DisHexWord ; mov al,' ' ; call DisChar ; mov ax,ioseg ; mov si,ioofs ; mov es,ax ; mov al,es:[si] ; push cs ; pop es ; call DisHexByte ; mov al,':' ; call DisChar ; ; get new input call Input ; jnc edit0 ; ; leave unchanged mov ax,ioofs ; inc ax ; mov ioofs,ax ; jmp doedit ; ; test for commands edit0: cmp byte ptr [si],'q' ; quit jnz edit1 ; edite: jmp MainC ; edit1: cmp byte ptr [si],'-' ; decrement pointer jnz edit2 ; mov ax,ioofs ; dec ax ; mov ioofs,ax ; jmp doedit ; edit2: call GetHexByte ; other = hex byte, store jc edite ; mov si,ioofs ; mov bx,ioseg ; mov es,bx ; mov byte ptr es:[si],al ; store byte mov ax,cs ; mov es,ax ; inc ioofs jmp doedit ; ;-------------------------------------------------- ; Trace handler ;-------------------------------------------------- Trace: ; see if I'm attempting to trace the debugger itself. ; I've tried it, leads to disaster mov ax,cs ; cmp ax,savCS ; jnz trace0 ; ; give message and do not fall for it mov dx,offset owncs ; call Mess ; jmp MainC ; owncs db "Cannot trace ddt itself!",lf,cr,eos ; I make an exception for tracing INT's: an int ; instruction will be treated as a breakpoint 2 bytes ; further on. I've tried to trace the DIP code, leads to ; desaster. There is no way to correct DIP code anyway, ; better skip through it quickly. trace0: mov ax,savCS ; look for CD code mov si,savIP ; mov ds,ax ; cmp byte ptr [si],0CDH ; INT instruction jz trace1 ; jmp trace2 ; ; fake a continue instruction two bytes ; down the code trace1: inc si ; two bytes more inc si ; push cs ; restore ds pop ds ; mov brkpto,si ; set breakpoint address mov brkpts,ax ; jmp SetPts ; execute continue ; no special instruction to handle, do normal trace trace2: push cs ; cs -> ds pop ds ; ; set all registers for trace or savF,0100H ; set trace flag mov brkpta,0 ; jmp SetPts ; ;-------------------------------------------------- ; Next handler ;-------------------------------------------------- Next: mov ax,savCS ; get next code byte mov si,savIP ; mov ds,ax ; mov ax,[si] ; in ax push cs ; pop ds ; ; preset cx to 2 bytes mov cx,2 ; ; see if it is any of the call instructions cmp al,0E8H ; 2-bytes call jz next3 ; cmp al,09AH ; 5-bytes call jz next5 ; ; indirect call, needs inspection cmp al,0FFH ; indirect call jnz next0 ; no indirect call ; test next low byte too mov al,ah ; get seccond byte and al,00110000B ; xx01xxxxB = cmp al,00010000B ; indirect call jnz next0 ; no indirect call ; indirect call found, see how long it is ; more work to do. parameter byte is in ah mov al,ah ; get seccond byte and al,11000000B ; get mod bits cmp al,11000000B ; mod = 11 jz next2 ; cmp al,10000000B ; mod = 10 jz next4 ; cmp al,01000000B ; mod = 01 jz next3 ; mov al,ah ; tests for mod = 00 and al,00000111B ; r/m field decides cmp al,00000100B ; r/m = 110 jz next4 ; jmp next2 ; ; no call, handle as if trace given next0: jmp Trace ; next5: inc cx ; next4: inc cx ; next3: inc cx ; next2: ; put breakpoint there add cx,savIP ; mov brkpto,cx ; mov ax,savCS ; mov brkpts,ax ; and savF,0FEFFH ; clear trace flag jmp SetPts ; ;-------------------------------------------------- ; Continue handler, continue code execution. Optionally ; a breakpoint may be specified together with this. ;-------------------------------------------------- Continue: ; handle default parameters call SkipSp ; look for parameters jnc cont1 ; ; no breakpoint, no trace mov brkpto,0 ; mov brkpts,0 ; and savF,0FEFFH ; jmp SetPts ; ; get start address cont1: call GetHexWord ; read segment address jc ContErr ; mov brkpts,ax ; ; see if another parameter there call SkipSp ; offset too there? jnc cont2 ; ; no segment, use code segment mov ax,brkpts ; seg -> offset mov brkpto,ax ; mov ax,savCS ; cs -> seg mov brkpts,ax ; jmp SetPts ; ; both segment and offset there cont2: call GetHexWord ; jc ContErr ; mov brkpto,ax ; ; go set things and start and savF,0FEFFH ; clear trace flag jmp SetPts ; goto user program ; errors in continue setting ContErr:jmp Error ; ;-------------------------------------------------- ; Fragment SetPts ; ; This piece of code sets all the breakpoints/watchpoints/ ; followpoints etc. It then continues at the ToUser part ;-------------------------------------------------- SetPts: ; see if tracing is to be done, In that case all ; breakpoints etc.. are disabled, I get all views there ; are anyway. test savF,0100H ; test trace flag jz SetPts1 ; ; trace modus, simply step an instruction jmp ToUser SetPts1: ; see if any breakpoint there mov ax,brkpts ; if all is zero, no breakpoints or ax,brkpto ; cmp ax,0 ; jz SetPts3 ; no breakpoints, set other pts ; set breakpoint mov ax,brkpts ; get old code byte mov si,brkpto ; mov ds,ax ; mov al,ds:[si] ; get old byte mov cs:brkptc,al ; mov al,brkptn ; put new byte mov ds:[si],al ; cmp byte ptr ds:[si],brkptn ; see if byte really there jz SetPts2 ; ; byte not there give message and quit mov al,cs:brkptc ; try to put code byte back mov ds:[si],al ; mov ax,cs ; mov ds,ax ; jmp Error ; ; byte there, continue SetPts2:mov ax,cs ; get ds back mov ds,ax ; mov brkpta,1 ; ; set other user features SetPts3: ; the rest of the code is already there jmp ToUser ; ;-------------------------------------------------- ; Fragment ToUser: ; This part of the code loads all the user registers and starts ; the execution of code there. Breakpoint/watchpoint etc.. must ; be set before this part of code is executed, else the debugger ; will never appear again. ;-------------------------------------------------- ToUser: ; switch to user's stack cli ; stack change mov ax,savSS ; SS mov ss,ax ; mov sp,savSP ; SP sti ; stack change done ; load user's registers mov ax,savF ; PSW push ax ; mov ax,savCS ; CS push ax ; mov ax,savIP ; IP push ax ; mov bx,savBX ; BX mov cx,savCX ; CX mov dx,savDX ; DX mov si,savSI ; SI mov di,savDI ; DI mov bp,savBP ; BP mov ax,savES ; ES mov es,ax ; mov ax,savDS ; DS mov ds,ax ; mov ax,cs:savAX ; AX ; go execute user instructions iret ; ;-------------------------------------------------- ; Divide-by-zero handler ; This is a kinda special case in the user-program return ; processing. I never set breakpoints etc.. to force this. It's ; just that the PoFo's own trap handler for /0 errors is such a ; mess that I have decided to trap these errors to the debugger ; ALWAYS. I give a warning when this happens and attempt to ; return to the debugger. That MAY fail. The alternative WILL ; fail. Make your choice.... ;-------------------------------------------------- ; I attempt to produce a nice error message ; and goto the trace return code. When an INDOS ; condition exists that may not work.... D0Ret: pushf ; cld ; passify PSW sti ; push ax ; save some registers push dx ; push ds ; mov ax,cs ; ds=cs mov ds,ax ; mov ah,9 ; mov dx,offset Div0Mess int 21h ; ; see if a breakpoint was set cmp brkpta,1 ; jnz D0brkp ; ; a breakpoint set, restore it push di ; mov di,brkpto ; mov ax,brkpts ; push ax ; mov al,brkptc ; pop ds ; mov [di],al ; pop di ; mov ax,cs ; mov ds,ax ; mov brkpta,0 ; D0brkp: pop ds ; pop dx ; pop ax ; popf ; jmp FrUser ; ; the error message Div0Mess: db ">>>> Divide by zero <<<<",cr,lf,eos ;-------------------------------------------------- ; Fragment BrRet ;-------------------------------------------------- BrRet: cld ; passify PSW sti ; mov cs:savAX,ax ; do partial user save mov ax,ds ; mov cs:SavDS,ax ; mov ax,cs ; mov ds,ax ; ; see if a breakpoint was set cmp brkpta,1 ; jnz brret1 ; ; correct IP on stack pop ax ; dec ax ; push ax ; ; a breakpoint set, restore it push di ; mov di,brkpto ; mov ax,brkpts ; push ax ; mov al,brkptc ; pop ds ; mov [di],al ; pop di ; mov ax,cs ; mov ds,ax ; mov brkpta,0 ; brret1: jmp FrUser1 ; ;-------------------------------------------------- ; Fragment TrRet return from trace ;-------------------------------------------------- TrRet: cld ; passify PSW sti ; ; see if the trace came from div/0 or other ; ddt traps... push bp ; +2 go look at the stack mov bp,sp ; +4 <- offset on stack push ax ; pushf ; mov ax,cs ; get ddt cs in ax cmp ax,[bp+4] ; see if trace came from ddt jnz TrRet1 ; not so -> real trace ; trace came from ddt itself, not a real trace ; clear trace flag, and go to the original handler ; correct on-stack IP to make the 8088 act just like ; a >=80286 mov ax,[bp+6] ; get flags word and ax,0FEFFH ; clear trace flag mov [bp+6],ax ; put back on stack mov ax,[bp+8] ; correct IP on stack dec ax ; dec ax ; mov [bp+8],ax ; ; get stack ok again popf ; pop ax ; pop bp ; iret ; TrRet1: popf ; pop ax ; pop bp ; ; jmp FrUser ; ;-------------------------------------------------- ; Fragment FrUser/FrUser1 ; This part handles a return from a user program. It saves all ; the registers in ddt's buffers, restores the stack to the ; debugger stack, restores breakpoint code etc... ;-------------------------------------------------- FrUser: mov cs:savAX,ax ; AX mov ax,ds ; mov cs:savDS,ax ; DS mov ax,cs ; mov ds,ax ; ds -> code segment FrUser1:mov savBX,bx ; BX mov savCX,cx ; CX mov savDX,dx ; DX mov savSI,si ; SI mov savDI,di ; DI mov ax,es ; ES mov savES,ax ; mov ax,cs ; es -> cs again mov es,ax ; mov savBP,bp ; BP pop ax ; IP mov savIP,ax ; pop ax ; CS mov savCS,ax ; pop ax ; PSW from stack and ax,0FEFFH ; clear trace flag mov savF,ax ; mov ax,ss ; SS mov savSS,ax ; mov ax,sp ; SP mov savSP,ax ; cli ; no interrupts during stack change mov ax,cs ; mov ss,ax ; ss:sp -> normal stack mov sp,offset einde ; sti ; interrupts on again ; display location Where: mov si,offset savCS ; lodsw ; mov word ptr InstLoc+2,ax call DisHexWord ; display address mov al,':' ; call DisChar ; lodsw ; mov word ptr InstLoc,ax call DisHexWord ; display address mov al,' ' ; call DisChar ; ; disassemble one instruction call DisInstr ; mov dx,offset nlmess; goto next line call Mess ; jmp MainC ; ;-------------------------------------------------- ; Input handler ;-------------------------------------------------- Inp: call SkipSp ; skip spaces jnc inp1 ; ; error handler IoErr: jmp Error ; inp1: call GetHexWord ; get input adres jc IoErr ; mov dx,ax ; in al,dx ; call DisHexByte ; mov dx,offset nlmess; call Mess ; jmp MainC ; ;-------------------------------------------------- ; Output handler ;-------------------------------------------------- Outp: call SkipSp ; skip spaces jc IoErr ; outp1: call GetHexWord ; output address jc IoErr ; mov dx,ax ; ; see if byte there call SkipSp ; jc IoErr ; ; get output byte call GetHexByte ; jc IoErr ; ; do output out dx,al ; ; done jmp MainC ; ;-------------------------------------------------- ; Register edit handler ;-------------------------------------------------- RegEd: call SkipSp ; jc RegErr ; ; get register string in ax call GetChar ; jc RegErr ; mov ah,al ; call GetChar ; jc RegErr ; and ax,0DFDFH ; low-> high conversion mov bx,ax ; save in BX ; get the new value call SkipSp ; jc RegErr ; call GetHexWord ; jc RegErr ; mov cx,ax ; ; see where to edit mov si,offset RegTab ; RegLp: lodsw ; cmp ax,0 ; jz RegErr ; cmp ax,bx ; jz RegFnd ; lodsw ; jmp RegLp ; ; found the reg I want RegFnd: lodsw ; ax = pointer mov di,ax ; mov [di],cx ; jmp MainC ; RegErr: jmp Error ; ; table for register modify, gives ; string -> addres conversion RegTab dw "AX",offset SavAX ; dw "BX",offset SavBX ; dw "CX",offset SavCX ; dw "DX",offset SavDX ; dw "SI",offset SavSI ; dw "DI",offset SavDI ; dw "BP",offset SavBP ; dw "FL",offset SavF ; dw "DS",offset SavDS ; dw "ES",offset SavES ; dw "SS",offset SavSS ; dw "SP",offset SavSP ; dw "CS",offset SavCS ; dw "IP",offset SavIP ; dw 00,00 ; ;-------------------------------------------------- ; Driver debug Command input handler ;-------------------------------------------------- DrvCmdErr: jmp Error ; DrvCmd: call SkipSp ; skip spaces jc DrvCmdErr ; ; test if any of my known commands mov bl,[si] ; mov bh,[si+1] ; and bx,0DFDFH ; low -> up ; get from table push si ; mov si,offset DCmdTab ; DCmdlp: lodsw ; cmp ax,0 ; jz NoCCmd ; cmp ax,bx ; jz DCmdFnd ; lodsb ; jmp DCmdLp ; ; found the command DCmdFnd:lodsb ; get command opcode pop si ; inc si ; inc si ; jmp drvcmd1 ; ; set next hex byte as command in the request packet NoCCmd: pop si ; call GetHexByte ; jc DrvCmdErr ; drvcmd1:mov byte ptr rqcmd,al ; ; make rest of the packet ok too mov word ptr rqsts,0 ; status = 0 mov di,offset DCTab ; jmp CI ; ; a small macro to make enterin the command table ; easier DCmd macro CmdOpc,CmdHand db CmdOpc dw CmdHand endm DCTab: DCmd 0,DCInit DCmd 1,DCMchk DCmd 2,DCBPB DCmd 3,DCIO DCmd 4,DCIO DCmd 8,DCIO DCmd 9,DCIO DCmd 12,DCIO DCmd 0FFH,DCOther ; Init command DCInit: mov byte ptr rqlen,rqstdlen+10 ; request length mov word ptr rqInitEndPtr,0 mov word ptr rqInitEndPtr+2,0 mov word ptr rqInitCmdStr,offset InitCmdStr mov word ptr rqInitCmdStr+2,cs jmp MainC ; done ; for Media check DCMChk: mov byte ptr rqlen,rqstdlen+2 ; request length mov byte ptr rqMediaDesc,0F8H ; descriptor mov byte ptr rqMediaRet,0 ; status jmp MainC ; done ; for build BPB DCBPB: mov byte ptr rqlen,rqstdlen+9 mov byte ptr rqBuildBPBDesc,0 mov word ptr rqBuildBPBScratch,offset SecBuf mov word ptr rqBuildBPBScratch+2,cs mov word ptr rqBuildBPBPtr,0 mov word ptr rqBuildBPBPtr+2,0 jmp MainC ; done ; for I/O commands DCIO: mov byte ptr rqlen,rqstdlen+9 mov byte ptr rqRdWrDesc,0F8H mov word ptr rqRdWrAddr,offset SecBuf mov word ptr rqRdWrAddr+2,cs mov word ptr rqRdWrNSec,1 ; sector number may be a parameter call GetHexWord jnc dcio1 ; mov ax,0 ; default = 0000H dcio1: mov word ptr rqRdWrStartSec,ax jmp MainC ; done ; unknown command, set biggest defaults, with all ; unknown parameters to 00 DCOther:mov byte ptr rqlen,rqstdlen+10 ; request length ; fill in flex part mov word ptr rqFlex+00,0 mov word ptr rqFlex+02,0 mov word ptr rqFlex+04,0 mov word ptr rqFlex+06,0 mov word ptr rqFlex+08,0 ; done jmp MainC ; ; command mnemonic -> command opcode conversion ; table DCmdTab: db 'IN',0 ; Init db 'MC',1 ; Media Check db 'BP',2 ; Build BPB db 'CR',3 ; IoCtl Read db 'RD',4 ; Read db 'NR',5 ; Nondestructive Read db 'IS',6 ; Input Status db 'IF',7 ; Input Flush db 'WR',8 ; Write db 'WV',9 ; Write-Verify db 'OS',10 ; Output Status db 'OF',11 ; Output Flush db 'CW',12 ; IoCtl Write db 'DO',13 ; Device Open db 'DC',14 ; Device Close db 'RM',15 ; Removable Media db 'OB',16 ; Output till busy db 'GC',19 ; Generic Control db 'SD',23 ; Set Device db 'GD',24 ; Get Device db 0,0,0 ; end of table ;-------------------------------------------------- ; Data Buffer display ;-------------------------------------------------- DrvDataBuf: ; handle default parameters call SkipSp ; look for parameters jc drvdmp ; ; get start address call GetHexWord ; read address and ax,001F8H ; make 8 byte boundary, mov ioofs,ax ; max = 512 bytes call SkipSp ; skip spaces jnc drvdmp0 ; ; only one par, go dump with this jmp drvdmp ; ; second par too drvdmp0:call GetHexWord ; add ax,7 ; and ax,0FFF8H ; mov ionum,ax ; ; go execute a dump drvdmp: mov dx,offset dmess ; print header call Mess ; ; get data from driver I/O buffer mov si,ioofs ; mov bx,ionum ; ; go dump memory drvp1: mov ax,si ; test if ready cmp bx,0 ; jz drvpe ; ; save si for ASCII dump push si ; ; Display address mov ax,si ; call DisHexWord ; ; 8 bytes hex mov cx,8 ; dump 8 bytes drvp2: mov al,' ' ; call DisChar ; mov al,cs:[si+SecBuf]; call DisHexByte ; inc si ; dec bx ; loop drvp2 ; mov al,' ' ; space between call DisChar ; ; ASCII (if this goes) mov cx,8 ; dump ASCII part pop si ; drvp3: mov al,cs:[si+SecBuf] ; cmp al,' ' ; I do not dump below jc drvp5 ; drvp4: call DisChar ; inc si ; loop drvp3 ; mov dx,offset nlmess; call Mess ; jmp drvp1 ; drvp5: mov al,'.' ; jmp drvp4 ; ; dump done drvpe: mov ax,ionum ; add ax,ioofs ; mov ioofs,ax ; ; get next command jmp MainC ; ;-------------------------------------------------- ; Driver buffer Edit handler ;-------------------------------------------------- DrvEdit: ; handle default parameters call SkipSp ; look for parameters jc drvdoedit ; ; get start address call GetHexWord ; read offset address mov ioofs,ax ; ; go edit drvdoedit: mov ax,ioofs ; call DisHexWord ; mov al,' ' ; call DisChar ; mov si,ioofs ; mov al,[si+SecBuf] ; call DisHexByte ; mov al,':' ; call DisChar ; ; get new input call Input ; jnc drvedit0 ; ; leave unchanged mov ax,ioofs ; inc ax ; and ax,01FFH ; mov ioofs,ax ; jmp drvdoedit ; ; test for commands drvedit0: cmp byte ptr [si],'q' ; quit jnz drvedit1 ; drvedte:jmp MainC ; drvedit1: cmp byte ptr [si],'-' ; decrement pointer jnz drvedit2 ; mov ax,ioofs ; dec ax ; and ax,01FFH ; mov ioofs,ax ; jmp drvdoedit ; drvedit2: call GetHexByte ; other = hex byte, store jc drvedte ; mov si,ioofs ; mov byte ptr [si+SecBuf],al ; store byte inc ioofs jmp drvdoedit ; ;-------------------------------------------------- ; Driver unit setting handler ;-------------------------------------------------- DrvUnit:call SkipSp ; skip spaces jc ToIoEr1 ; ; set next hex byte as unit in the request packet call GetHexByte ; jc ToIoEr1 ; mov byte ptr rqunit,al ; jmp MainC ; ToIoEr1:jmp Error ; jump extension ;-------------------------------------------------- ; Driver debug Strategy caller ;-------------------------------------------------- DrvStrategy: ; make saved registers ready mov ax,cs ; make request packet mov savES,ax ; pointer mov ax,offset rqpack ; mov savbx,ax ; mov ax,drvCS ; make start address mov savCS,ax ; segment mov ds,ax ; mov ax,ds:6 ; get offset from driver code mov cs:SavIp,ax ; mov ax,cs ; get ds back mov ds,ax ; ; go call the driver's routine CallDrv: ; see if driver breakpoint wanted cmp DrvBrk,0 ; jz calldr2 ; ; breakpoint to be set... mov ax,savCS ; load breakpoint mov brkpts,ax ; registers mov ax,savIP ; mov brkpto,ax ; mov brkpta,1 ; ; set registers for breakpoint-like run ; may end at head of the driver, or when call finished ; depending upon the value of DrvBrk calldr2:cli ; stack change mov ax,savSS ; SS mov ss,ax ; mov sp,savSP ; SP sti ; stack change done ; push a far return address on the stack mov ax,cs ; pointing to CallDrvRet push ax ; mov ax,offset CallDrvRet ; push ax ; ; save new user stack mov ax,ss ; mov savSS,ax ; mov ax,sp ; mov savSP,ax ; ; switch back to debug stack cli ; mov ax,offset einde ; mov sp,ax ; mov ax,cs ; mov ss,ax ; sti ; ; the rest of the code is already there jmp SetPts ; use trace code ; The return part of the driver call part CallDrvRet: ; give message push ds ; save regs push dx ; push ax ; push cs ; make message pop ds ; mov dx,offset DrvCallRetMess; mov ah,9 ; int 21h ; pop ax ; restore regs pop dx ; pop ds ; ; go fake an int stack state pushf ; flags on stack push cs ; code segment call BrRet ; do as if breakpoint hit DrvCallRetMess db ">>> Driver call returned <<<",LF,CR,EOS ;-------------------------------------------------- ; Driver debug Interrupt caller ;-------------------------------------------------- DrvInterrupt: ; breakpoint run witout a breakpoint active, just ; the breakpoint code at the end of the routine call mov brkpta,0 ; breakpoint run not ; active ; make saved registers ready mov ax,drvCS ; make start address mov savCS,ax ; segment mov ds,ax ; mov ax,ds:8 ; get offset from driver code push cs ; get ds back pop ds ; mov savIP,ax ; offset ; load es:bx with cs:0000 too, to see if the driver ; really uses only the strategy input mov ax,0 ; mov savBX,ax ; mov ax,drvCS ; mov savES,ax ; ; the rest is the same as DrvInterrupt tail jmp CallDrv ; ;-------------------------------------------------- ; Driver call(s) execute ;-------------------------------------------------- DrvExec: ; prepare code fragment for the two calls mov ax,DrvCS ; get driver's segment mov dsseg,ax ; mov diseg,ax ; mov es,ax ; mov si,6 ; offset strategy routine mov ax,es:[si] ; mov dsofs,ax ; mov ax,es:[si+2] ; mov diofs,ax ; ; prepare registers for driver call mov ax,cs ; mov es,ax ; mov bx,offset rqpack; ; dirty-trick call to the driver, ; self-chaning code. I feel shame. db 09AH ; call-far opcode dsofs dw 0 ; offset dsseg dw 0 ; segment db 09AH ; call-far opcode diofs dw 0 ; offset diseg dw 0 ; segment ; display what became of it call DisPack ; ; done jmp MainC ; ;-------------------------------------------------- ; Driver debug Command Options input ;-------------------------------------------------- DrvOpt: mov di,offset InitCmdStr ; di -> result drvtail0: call GetChar ; get string char jnc drvtail1 ; ; end of input mov byte ptr [di],CR; jmp MainC ; drvtail1: mov [di],al ; inc di ; jmp drvtail0 ; ;-------------------------------------------------- ; Driver debug breakpoint setting ;-------------------------------------------------- DrvBreakpoints: ; toggle driver breakpoints flag cmp DrvBrk,0 ; jz drvbron ; ; make breakpoints OFF mov DrvBrk,0 ; mov dx,offset BrkOffMess ; jmp drvbrkend ; ; make breakpoints ON drvbron:mov DrvBrk,1 ; mov dx,offset BrkOnMess ; ; jmp drvbrkend ; ; message the actual state of the breakpoint flag drvbrkend: mov ah,9 ; int 21h ; jmp MainC ; BrkOnMess: db "Driver breakpoints ON",LF,CR,EOS BrkOffMess: db "Driver breakpoints OFF",LF,CR,EOS ;-------------------------------------------------- ; Driver debug View status I/O data ;-------------------------------------------------- DrvView:call DisPack ; jmp MainC ; ; -------------------------------------- ; Routine DisPack ; purp : display the results of a driver call ; in : nothing ; out : ; uses : DisPack:mov dx,offset StatMess ; call Mess ; ; test busy bit mov ax,rqsts ; get status word test ax,0200H ; jz DPNBusyMess ; mov dx,offset BusyMess ; call Mess ; DPNBusyMess: ; test done bit mov ax,rqsts ; get status word test ax,0100H ; jz DPNDoneMess ; mov dx,offset DoneMess ; call Mess ; DPNDoneMess: ; new line for the thing call NewLine ; ; test error bit mov ax,rqsts ; get status word test ax,08000H ; jz DPStsOk ; mov dx,offset ErrMess ; call Mess ; ; give nice error message mov ax,rqsts ; and al,01111111B ; clear done flag mov si,offset DrvErrTab ; si -> table begin call StrTab ; jnc DPStsDone ; ; not in the table -> print hex mov ax,rqsts ; and al,01111111B ; call DisHexByte ; DPStsDone: call NewLine ; DPStsOk: ; print fixed and flexible part of I/O packet DPPFix: mov dx,offset DPFixed ; call Mess ; mov cx,2 ; mov si,offset rqpack ; DPFixLp:lodsb ; call DisHexByte ; mov al,' ' ; call DisChar ; loop DPFixLp ; ; the command too lodsb ; mov si,offset DrvCmdTab ; call StrTab ; jnc DPCmdDone ; ; no in the table, print hex call DishexByte ; DPCmdDone: mov dx,offset nlmess ; call Mess ; ; see how many bytes to dump mov al,rqlen ; cmp al,14 ; min is 14 bytes jc DPDone ; sub al,13 ; mov cl,al ; mov ch,0 ; ; print header and prepare the rest mov dx,offset DPFlex ; call Mess ; mov si,offset rqflex ; DPFlLp: lodsb ; call DisHexByte ; mov al,' ' ; call DisChar ; loop DPFlLp ; NewLine:mov dx,offset nlmess ; call Mess ; DPDone: ret ; ; some messages about the driver's status return ; busy/done and header message StatMess db "Driver status: ",eos BusyMess db "busy ",eos DoneMess db "done ",eos ; driver error report errmess db ">>> error <<<: ",eos ; the (known) error causes DrvErrTab: db 0,"write protect",eos db 1,"unknown unit",eos db 2,"drive not ready",eos db 3,"unknown command",eos db 4,"CRC error",eos db 5,"bad request lenth",eos db 6,"seek error",eos db 7,"unknown media",eos db 8,"sector not found",eos db 9,"printer out of paper",eos db 10,"write fault",eos db 11,"read fault",eos db 12,"general failure",eos db 15,"invalid disk change",eos db 0FFH,eos ; command overview DrvCmdTab: db 0,"Init",eos db 1,"MediaCheck",eos db 2,"BuildBPB",eos db 3,"IoctlRead",eos db 4,"Read",eos db 8,"Write",eos db 9,"Writevfy",eos db 12,"IoCtlWrite",eos db 0FFH,eos DPFixed db "len unit cmd : ",eos DPFlex db "buf: ",eos ; -------------------------------------- ; Routine StrTab ; purp : print one of a number of strings in a table ; in : al = selector, 00 .. FEH, FFH = end of table ; ds:si -> begin of the table ; out : carry set if end-of table hit. ; uses : dx,si StrTab: cld ; direction is up push ax ; save input par mov ah,al ; ah for comparison ; at the head of a string: STStart:lodsb ; get begin byte cmp al,ah ; jz STIsMess ; if message found: cmp al,0FFH ; jz STNoMess ; if end of table found ; this is not the correct message, skip it STSkip: lodsb ; cmp al,eos ; jz STStart ; jmp StSkip ; ; message found, print it STIsMess: mov dx,si ; call Mess ; pop ax ; clc ; ret ; ; message not in the table STNoMess: mov dx,si ; call Mess ; pop ax ; stc ; ret ; prog endp ; -------------------------------------- ; Routine Input ; purp : user input (buffered) from keyboard ; in : nothing ; out : carry set if empty input or only spaces input ; else: input in databuf, ASCIZ, si -> first non-space ; uses : databuf Input proc near ; all procs are near push ax ; push bx ; push dx ; push di ; ; get command string mov ax,0A00H ; buffered input mov di,databuf ; 120 chars for command mov byte ptr [di],120 ; mov dx,databuf ; int 21H ; get data buffer ; goto next line mov dx,offset nlmess ; call Mess ; ; make to ASCIZ buffer mov si,(databuf+1) ; mov bl,[si] ; mov bh,0 ; inc si ; si -> first char mov byte ptr [si+bx],0 ; make ASCIZ string ; skip leading spaces call SkipSp ; ; restore registers pop di ; pop dx ; pop bx ; pop ax ; ret ; Input endp ; -------------------------------------- ; Routine DisChar ; purp : Writes AL to display ; in : character in al ; out : nothing ; uses : nothing DisChar proc near ; all procs are near pushf ; push ax ; push dx ; mov dl,al ; mov ah,02H ; int 21H ; disnsio:pop dx ; pop ax ; popf ; ret ; DisChar endp ; -------------------------------------- ; Routine ToHex ; purp : converts nibble to hex ASCII character ; in : nibble in al ; out : ASCII char in al ; uses : nothing ToHex proc near pushf ; and al,0FH ; make sure of 0 .. F cmp al,0AH ; jge tohex1 ; ; 0 .. 9 add al,'0' ; 0 .. 9 convert jmp tohexe ; ; A .. F tohex1: add al,('A' - 0AH) ; tohexe: popf ; ret ; ToHex endp ; -------------------------------------- ; Routine FrHex ; purp : converts ASCII hex character to nibble ; in : ASCII char in al ; out : nibble in al, carry clear if 0 .. 9 or A .. F or a .. f ; carry set and al unchanged if no hex nibble ; uses : nothing FrHex proc near pushf ; ; test for 0 .. 9 cmp al,'0' ; jl frhexn ; cmp al,'9' ; jg frhex1 ; ; is 0 .. 9 -> convert and al,0FH ; frhexe: popf ; clc ; ret ; ; test A .. F frhex1: cmp al,'A' ; jl frhexn ; cmp al,'F' ; jg frhex2 ; ; A .. F -> convert sub al,'A' ; add al,0AH ; jmp frhexe ; ; test a .. f frhex2: cmp al,'a' ; jl frhexn ; cmp al,'f' ; jg frhexn ; ; a .. f -> convert sub al,'a' ; add al,0AH ; jmp frhexe ; ; error frhexn: popf ; stc ; ret ; FrHex endp ; -------------------------------------- ; Routine DisHexByte and DisHexWord ; purp : Writes AL or AX as hex ascii ; in : byte in AL (DisHexByte) ; word in AX (DisHexWord) ; out : nothing ; uses : nothing DisHexWord proc near ; all procs are near ; save registers push ax ; ; higher byte mov al,ah ; call DisHexByte ; ; lower byte pop ax ; call DisHexByte ; ; done ret ; DisHexWord endp DisHexByte proc near ; all procs are near ; save registers pushf ; push ax ; ; higher nibble shr al,1 ; shr al,1 ; shr al,1 ; shr al,1 ; call ToHex ; call DisChar ; ; lower nibble pop ax ; push ax ; and al,0FH ; call ToHex ; call DisChar ; ; restore registers pop ax ; popf ; ; done ret ; DisHexByte endp ; -------------------------------------- ; Routine GetHexByte and GetHexWord ; purp : gets a hex byte/word to al/ax ; in : si -> command string ; out : byte in AL (GetHexByte) ; word in AX (GetHexWord) ; uses : nothing GetHexWord proc near ; all procs are near push bx ; push cx ; call SkipSp ; jc ghwe ; ; get nibbles mov cx,4 ; mov bx,0 ; call Getchar ; jc ghwe ; call FrHex ; jc ghwe ; jmp ghw2 ; ; get a character ghw1: call GetChar ; jc ghwf ; call FrHex ; jc ghwf ; ghw2: shl bx,1 ; shl bx,1 ; shl bx,1 ; shl bx,1 ; mov ah,0 ; add bx,ax ; loop ghw1 ghwf: mov ax,bx ; clc ; ghwe: pop cx ; pop bx ; ret ; GetHexWord endp GetHexByte proc near ; all procs are near push bx ; push cx ; call SkipSp ; jc ghbe ; ; get nibbles mov cx,2 ; mov bx,0 ; call GetChar ; jc ghbe ; call FrHex ; jc ghbe ; jmp ghb2 ; ; get a character ghb1: call GetChar ; jc ghbf ; call FrHex ; jc ghbf ; ghb2: shl bx,1 ; shl bx,1 ; shl bx,1 ; shl bx,1 ; mov ah,0 ; add bx,ax ; loop ghb1 ghbf: mov ax,bx ; clc ; ghbe: pop cx ; pop bx ; ret ; GetHexByte endp ; -------------------------------------- ; Routine GetChar ; purp : get one ASCII character ; in : si -> character buffer ; out : nothing ; uses : nothing GetChar proc near mov al,[si] ; get a character cmp al,0 ; test for end of string jz getchar1 ; -> error return inc si ; point to next character clc ; ok return ret ; getchar1: stc ; error return ret ; GetChar endp ; -------------------------------------- ; Routine Mess ; purp : write message to user ; in : message offset in dx ; out : mothing ; uses : nothing Mess proc near mov ax,0900H ; int 21H ; ret ; Mess endp ; -------------------------------------- ; Routine SkipSp ; purp : skip spaces in string ; in : si -> string ; out : carry set if end of string found ; else si -> first non-space char in the string ; uses : nothing SkipSp proc near ; skip leading spaces skip1: cmp byte ptr [si],0 ; 00 = end of string jz skip3 ; cmp byte ptr [si],' ' ; test for space jnz skip2 ; other is character inc si ; skip the space jmp skip1 ; next char skip2: clc ; character found ret ; skip3: stc ; end of string found ret ; SkipSp endp ; -------------------------------------- ; Routine Prompt ; purp : write prompt to user ; in : nothing ; out : mothing ; uses : nothing Prompt proc near mov dx,offset pmess ; give prompt message call Mess ; ret ; Prompt endp ;--------------------------------------- ; handler UnAsm ; purp : Un-assembles instructions ; in : IoSeg: segment ; out : disassembly to the console ; uses : ax,bx,ds:si,flags UnAsmNum dw 7 ; default 7 lines UnAsm: ; handle default parameters call SkipSp ; look for parameters jc DoUnAsm ; ; get start address call GetHexWord ; read address jc DoUnAsm ; mov word ptr InstLoc,ax ; set as offset call SkipSp ; skip spaces jnc UnAsm1 ; ; only one par, go disassemble with this jmp DoUnAsm ; ; second par too UnAsm1: call GetHexWord ; mov UnAsmNum,ax ; DoUnAsm: mov cx,UnAsmNum ; get # instructions jcxz DisAsmDone ; NextDis:push cx ; save on stack ; see if InstLoc is in the names table lds si,InstLoc ; call LookNt ; jc nndis ; push ds ; mov ax,cs ; mov ds,ax ; mov dx,offset SpDis ; call Mess ; pop ds ; nldis: lodsb ; cmp al,eom ; jz nldndis ; call DisChar ; jmp nldis ; nldndis:mov ax,cs ; mov ds,ax ; mov dx,offset nlmess ; call Mess ; pop cx ; loop ndisfin ; jmp DisAsmDone ; ndisfin:push cx ; nndis: mov ax,cs ; mov ds,ax ; mov ax,word ptr InstLoc+2 ; call DisHexWord ; mov al,':' ; call DisChar ; mov ax,word ptr InstLoc ; call DisHexWord ; mov al,' ' ; call DisChar ; call DisInstr ; disassemble it ; cleanup for disassembler mov ax,InstSize ; add word ptr InstLoc,ax ; mov ax,cs ; put es back mov es,ax ; mov dx,offset nlmess ; call Mess ; pop cx ; loop NextDis ; DisAsmDone: jmp MainC ; spdis db ' ',eos ; -------------------------------------- ; Routine DisInstr ; purp : This is the heart of the disassembler, it disassembles ; one instruction of code. The opcode has to be in al ; in : es:di -> code to disassemble ; out : one instruction disassembled, NO CR/LF, es:di and ds:si ; are NOT cleaned up. ; uses : ax,bx,ds:si,es:di,flags ; ; nb : table form: ; ; dw [and][match],[strptr],[routine] ; ; [and] = byte anded to the pattern first ; [match] = wat it has to be then ; [strptr] = pointer to string to be printed ; strptr = 0000H -> no string ; [routine] = offset of routine to call next ; routine = 0000H -> no routine ; ; all entries will be in word form. This means that the ; [and][match] word will be in reverse-order in memory ; ; This piece of code may, at first glance, seem impossible to ; produce. It is in fact quite simple. The code may look complex, ; but there is a very simple basic idea behind this disassembler: ; ; I use a simple pattern matching routine, and a patterns table, ; to find out if a certain opcode byte corresponds with any of ; the 8088's known instructions. Some of the bits in an opcode ; are don't cares when deciding this. These bits are and-ed out ; by the and byte in the patterns table. The remains has to match ; with a certain pattern in order to match an opcode of the 8088. ; This match pattern (with all the anded-away bits set to zero) ; is the match byte of the patterns table. Once a match is found ; a menemonic string has to be printed out. This is pointed to by ; the next word in the patterns table. Many opcode patterns may ; share the same mnemonic, so I've used a pointer into a list of ; strings instead of the strings themselves. Next the operands of ; the instruction have to be parsed and displayed. ; ; Sometimes a single opcode byte is not enough to determine the ; instruction. In that case the relevant information of the first ; byte is stored in some bits/flags, and the seccond byte is run ; through the same process to try to find out which instruction ; we have at hand. The bits/flags extraction routine is then the ; 'parameter' routine of the pattern. These routines extrect the ; bits/flags, and next re-run the above pattern matching routine ; with a seccond byte as it's input. In this case, no string is ; printed, so the string pointer is a null. ; ; Sometimes there are no parameters, in that case the parameter ; routine pointer is a null. The parameters routines may look ; like a real mess at first. I've simply worked my way through a ; 8088 instruction table, making routines for what I needed on ; the way. Later I've re-organized things a little to make the ; code more compact. This is what I ended up with. Perhaps it ; could be done more efficient. I'm open for suggestions. ; ; NB: I've made disassemblers for the 8051 and 6803 by the same ; simple means. A pattern matcher, a patterns table and parameter ; routines. This seems to work quite good. ; ; the info about the instruction InstSize dw 0 ; size of the instruction InstLoc dd 0 ; start of the instruction ; jmp/call target address (for name table usage..) JCDest dd 0 ; Jmp/Call Destination ; some state info of the instruction InstOver db 0 ; no segment overrule InstDBit db 0 ; 1=dest is register InstWBit db 0 ; 1=word operands InstVBit db 0 ; 1=count in cl, 0=count=1 InstSbit db 0 ; 1=sign-extend operand DisInstr: ; init data for start of instruction mov InstOver,0 ; no overrides mov InstDBit,0 ; from mov InstWBit,0 ; byte mov InstVBit,0 ; count = 1 mov InstSBit,0 ; no sign-extend les di,InstLoc ; get start of the instruction mov si,offset DisMn ; Pointer to main table mov InstSize,0 ; no bytes done yet jmp DisCont ; ;------------------------------ ; The pattern matching part ; ------------------ ; ; entry point is also used when additional ; parsing is needed ; ; input: es:di -> next byte to parse ; ds:si -> some parse table ;------------------------------ DisCont:mov bl,es:[di] ; get opcode inc di ; point to next byte inc InstSize ; DiLoop: lodsw ; get [match][and] word and ah,bl ; do the and cmp al,ah ; do the match jnz DiFail ; -> no fire ; a match is there, test the string lodsw ; get offset cmp ax,0 ; test for 0 jz DiNoStr ; skip if no string ; string there, print it, padd to 8 chars push si ; prepare for print push cx ; push dx ; mov si,ax ; ds:si -> string mov cx,7 ; DiStrLp:lodsb ; get string char test al,020H ; test for capital jz DiPadd ; if end -> go padd ; not end, print call DisChar ; jcxz DiStrLp ; next char dec cx ; jmp DiStrLp ; ; handle last char DiPadd: or al,020H ; make lower case call DisChar ; jcxz DiEndStr ; skip padding if cx == 0 dec cx ; jcxz DiEndStr ; skip padding if cx == 0 ; padd to 7 characters DiPaddL:mov al,' ' ; call DisChar ; loop DiPaddL ; DiEndStr: pop dx ; restore registers pop cx ; pop si ; ; string done, test if routine there DiNoStr:lodsw ; get routine offset cmp ax,0 ; test for 0 jz DiNoRout ; skip if no routine ; routine there, go execute it ; NB: programming trick used: the combination ; push ax / ret comes down to jmp [ax].. ; The real ret of the DisInstr is to be found at the end ; of the parameter parsing routine. push ax ; makes jmp out of the ret DiNoRout: ret ; done ; when the bytes do not match, next table entry DiFail: lodsw ; skip string lodsw ; skip routine jmp DiLoop ; next table entry ;--------------------------------------------------------------- ; The parsing tables: ;--------------------------------------------------------------- ; The format of these tables is the following: ; ; For each entry there are three words: ; ; 1) the pattern word: ; ; High-byte is the AND byte; the byte to be parsed will first ; be AND-ed with this high byte of the pattern word. the ; result of this AND will be compared with the low byte of the ; pattern word. If the compare does not match the rest of the ; table entry is skipped, and the next table entry is tested ; with the original byte to be parsed. If the compare does ; match the string and routine words are used. ; ; 2) the string word ; ; The string word is only used when the pattern word gives a ; match with the byte to be parsed. Even when the pattern word ; results in a match with the byte to be parsed, if the string ; word is 0000H it is ignored. If the string word is not ; 0000H, it is used as an offset in the code segment, the ; string (terminated with a CAPITAL) at that offset is printed, ; and padded to 8 characters. ; NB: if the string is longer than 8 characters, no padding ; is done. ; ; 3) the routine word ; ; The Routine word too is only used when the pattern word ; gives a match, and the word itself is not 0000H. If the ; pattern word matches and the routine word is not 0000H, it ; is used as an offset in the current code segment, and the ; routine found there is jumped to. ; ;--------------------------------------------------------------- ;------------------------------ ; 1-th and main 8088 parsing table ;------------------------------ DisMn: ; pat: str: rout: dw 0FFD7H,XlatStr ,0 ; xlat dw 0FF9FH,LahfStr ,0 ; lahf dw 0FF9EH,SahfStr ,0 ; sahf dw 0FF9CH,PushfStr ,0 ; pushf dw 0FF9DH,PopfStr ,0 ; popf dw 0FF37H,AaaStr ,0 ; aaa dw 0FF27H,DaaStr ,0 ; daa dw 0FF3FH,AasStr ,0 ; aas dw 0FF2FH,DasStr ,0 ; das dw 0FF98H,CbwStr ,0 ; cbw dw 0FF99H,CwdStr ,0 ; cwd dw 0FFC3H,RetStr ,0 ; ret dw 0FFCBH,RetfStr ,0 ; retf dw 0FFCDH,IntStr ,HexBPar; int xx dw 0FFCCH,IntStr ,Par3 ; int3 dw 0FFCEH,IntoStr ,0 ; into dw 0FFCFH,IretStr ,0 ; iret dw 0FFF8H,ClcStr ,0 ; clc dw 0FFF5H,CmcStr ,0 ; cmc dw 0FFF9H,StcStr ,0 ; stc dw 0FFFCH,CldStr ,0 ; cld dw 0FFFDH,StdStr ,0 ; std dw 0FFFAH,CliStr ,0 ; cli dw 0FFFBH,StiStr ,0 ; Sti dw 0FFF4H,HltStr ,0 ; hlt dw 0FF9BH,WaitStr ,0 ; wait dw 0FFF0H,LockStr ,0 ; lock dw 0FF90H,NopStr ,0 ; nop ; return near-far with stack adjust dw 0FFC2H,RetStr ,HexWPar; ret dw 0FFCAH,RetfStr ,HexWPar; retf ; all short relative jumps dw 0FFEBH,JmpStr ,Dis8Par; dw 0FF74H,JzStr ,Dis8Par; dw 0FF7CH,JlStr ,Dis8Par; dw 0FF7EH,JleStr ,Dis8Par; dw 0FF72H,JcStr ,Dis8Par; dw 0FF76H,JbeStr ,Dis8Par; dw 0FF7AH,JpeStr ,Dis8Par; dw 0FF70H,JoStr ,Dis8Par; dw 0FF78H,JsStr ,Dis8Par; dw 0FF75H,JnzStr ,Dis8Par; dw 0FF7DH,JgeStr ,Dis8Par; dw 0FF7FH,JgStr ,Dis8Par; dw 0FF73H,JncStr ,Dis8Par; dw 0FF77H,JaStr ,Dis8Par; dw 0FF71H,JnoStr ,Dis8Par; dw 0FF79H,JnsStr ,Dis8Par; dw 0FFE2H,LoopStr ,Dis8Par; dw 0FFE1H,LoopzStr ,Dis8Par; dw 0FFE0H,LoopnzStr,Dis8Par; dw 0FFE3H,JcxzStr ,Dis8Par; ; the long relative/intersegment jump dw 0FFE9H,JmpStr ,Dis16Par ; dw 0FFEAH,JmpStr ,PtrPar ; ; calls relative/intersegment dw 0FFE8H,Callstr ,Dis16Par ; dw 0FF9AH,Callstr ,PtrPar ; ; the segment overrides, no string, just routine dw 0E726H,0 ,HSegOver ; ; 16-bits register-only instructions dw 0F850H,PushStr ,Reg16Par ; dw 0F858H,PopStr ,Reg16Par ; dw 0F890H,XchgAxStr,Reg16Par ; dw 0F840H,IncStr ,Reg16Par ; dw 0F848H,DecStr ,Reg16Par ; ; push/pop segment registers dw 0E706H,PushStr ,SegPar ; dw 0E707H,PopStr ,SegPar ; ; the rep and string things, very simple, once ; you decided to separate byte/word instructions dw 0FFF2H,RepStr ,0 ; dw 0FFF3H,RepzStr ,0 ; dw 0FFA4H,MovsbStr ,0 ; dw 0FFA5H,MovswStr ,0 ; dw 0FFA6H,CmpsbStr ,0 ; dw 0FFA7H,CmpswStr ,0 ; dw 0FFAEH,ScasbStr ,0 ; dw 0FFAFH,ScaswStr ,0 ; dw 0FFACH,LodsbStr ,0 ; dw 0FFADH,LodswStr ,0 ; dw 0FFAAH,StosbStr ,0 ; dw 0FFABH,StoswStr ,0 ; ; immediate with ax/al dw 0FE04H,AddStr ,AImPar ; dw 0FE14H,AdcStr ,AImPar ; dw 0FE2CH,ISubStr ,AImPar ; dw 0FE1CH,SbbStr ,AImPar ; dw 0FE24H,AndStr ,AImPar ; dw 0FEA8H,TestStr ,AImPar ; dw 0FE0CH,OrStr ,AImPar ; dw 0FE34H,XorStr ,AImPar ; dw 0FE3CH,CmpStr ,AImPar ; ; mov immediate with other register dw 0F0B0H,MovStr ,RegImPar ; ; I/O instructions dw 0F6E4H,IInStr ,InPar ; dw 0F6E6H,OutStr ,OutPar ; ; mov ax/al,memory dw 0FEA0H,MovStr ,AMemPar ; dw 0FEA2H,MovStr ,MemAPar ; ; some quite complicated mov/add etc.. ones dw 0FC88H,MovStr ,DWModRegRmPar ; dw 0FC00H,AddStr ,DWModRegRmPar ; dw 0FC10H,AdcStr ,DWModRegRmPar ; dw 0FC28H,ISubStr ,DWModRegRmPar ; dw 0FC18H,SbbStr ,DWModRegRmPar ; dw 0FC20H,AndStr ,DWModRegRmPar ; dw 0FC08H,Orstr ,DWModRegRmPar ; dw 0FC30H,XorStr ,DWModRegRmPar ; dw 0FC38H,CmpStr ,DWModRegRmPar ; dw 0FE84H,TestStr ,WModRegRmPar ; dw 0FE86H,XchgStr ,WModRegRmPar ; ; the immediate-register/memory ones, some are signed, ; some are either signed or unsigned. Seccond byte ; gives the real operation, first one only indicates ; the type of operation dw 0FE80H,0 ,HRegMemImm ; extra parsing dw 0FE82H,0 ,HSRegMemImm ; ; all shifts/rotates etc.. need parsing of the ; seccond byte dw 0FCD0H,0 ,HShRot ; extra parsing ; to/from segment register dw 0FF8EH,MovStr ,SegMRm ; dw 0FF8CH,MovStr ,MRmSeg ; ; pointer loads dw 0FF8DH,LeaStr ,LModRegRmPar ; dw 0FFC5H,LdsStr ,LModRegRmPar ; dw 0FFC4H,LesStr ,LModRegRmPar ; ; math instructions, need more parsing dw 0FEF6H,0 ,HMath ; ; aam, aad, pop reg/mem, FF-instr require extra parsing dw 0FFD4H,0 ,HAam ; dw 0FFD5H,0 ,HAad ; dw 0FF8FH,0 ,HPopRegMem ; dw 0FFFFH,0 ,HFfInstr ; dw 0FFFEH,0 ,HFeInstr ; dw 0FEC6H,0 ,HC6Instr ; dw 0F8D8H,EscStr ,EscPars ; ; catch-all remainder, for when the parser fails ; This will ALWAYS match (Mask = 00, Pattern = 00) ; Ends up in db , single-byte instruction. dw 00000H,DbStr ,DisFail ; ;------------------------------ ; end of 1-st parser table ;------------------------------ ;------------------------------ ; 2-nd parsing table ;------------------------------ ; for (S)RegMemImm instructions ; ; The first part of the table contains the instructions ; that have an unsigned version only, the seccond part ; has the instruction entries that can be either signed ; or unsigned. This table is doubly-used in that sense. ; DisRegMemImm: dw 03820H,AndStr ,RegMemImm ; these ones dw 03808H,OrStr ,RegMemImm ; do not have dw 03830H,XorStr ,RegMemImm ; signed ver. DisSRegMemImm: dw 03800H,AddStr ,RegMemImm ; these ones dw 03810H,AdcStr ,RegMemImm ; have signed dw 03828H,ISubStr ,RegMemImm ; variety. dw 03818H,SbbStr ,RegMemImm ; dw 03838H,CmpStr ,RegMemImm ; ; end of table dw 00000H,DbStr ,DisFail ; ;------------------------------ ; end of 2-nd parser table ;------------------------------ ;------------------------------ ; 3-rd parser table ;------------------------------ ; for shifts/rotates ; ; The real operation to be performed is indicated in the ; seccond byte of the instruction. This table is used to ; parse that. ; DisShRot: dw 03800H,RolStr ,ShRotPar ; dw 03808H,RorStr ,ShRotPar ; dw 03810H,RclStr ,ShRotPar ; dw 03818H,RcrStr ,ShRotPar ; dw 03820H,ShlStr ,ShRotPar ; dw 03828H,ShrStr ,ShRotPar ; dw 03838H,SarStr ,ShRotPar ; ; end of table dw 00000H,DbStr ,DisFail ; ;------------------------------ ; end of 3-rd parser table ;------------------------------ ;------------------------------ ; 4-th parsing table ;------------------------------ ; for arithmatic (F6) instructions ; DisMath: dw 03820H,MulStr ,ModRmPar ; dw 03818H,NegStr ,ModRmPar ; dw 03828H,IMulStr ,ModRmPar ; dw 03830H,DivStr ,ModRmPar ; dw 03838H,IDivStr ,ModRmPar ; dw 03810H,NotStr ,ModRmPar ; dw 03800H,TestStr ,RegMemImm ; ; end of table dw 00000H,DbStr ,DisFail ; ;------------------------------ ; end of 4-th parser table ;------------------------------ ;------------------------------ ; 5-th parsing table ;------------------------------ ; for aam instruction ; DisAam: dw 0FF0AH,AamStr ,0 ; dw 00000H,DbStr ,DisFail ; ;------------------------------ ; end of 5-th parser table ;------------------------------ ;------------------------------ ; 6-th parsing table ;------------------------------ ; for aad instruction ; DisAad: dw 0FF0AH,AadStr ,0 ; dw 00000H,DbStr ,DisFail ; ;------------------------------ ; end of 6-th parser table ;------------------------------ ;------------------------------ ; 7-th parsing table ;------------------------------ ; for pop reg/mem instruction ; DisPopRegMem: dw 03800H,PopStr ,LModRmPar ; dw 00000H,DbStr ,DisFail ; ;------------------------------ ; end of 7-th parser table ;------------------------------ ;------------------------------ ; 8-th parsing table ;------------------------------ ; C6/C7 instructions ; DisC6Instr: dw 03800H,MovStr ,RegMemImm ; dw 00000H,DbStr ,DisFail ; ;------------------------------ ; end of 8-th parser table ;------------------------------ ;------------------------------ ; 9-th parsing table ;------------------------------ ; FF/FE instructions used for both ; DisFfInstr: dw 03820H,JmpStr ,LModRmPar ; dw 03828H,JmpFStr ,LModRmPar ; dw 03810H,CallStr ,LModRmPar ; dw 03818H,CallfStr ,LModRmPar ; dw 03830H,PushStr ,LModRmPar ; DisFeInstr: dw 03800H,IncStr ,ModRmPar ; dw 03808H,DecStr ,ModRmPar ; dw 00000H,DbStr ,DisFail ; ;------------------------------ ; end of 9-th parser table ;------------------------------ ;--------------------------------------------------------------- ; The 8088 disassembler string tables ;--------------------------------------------------------------- ;------------------------------ ; I like ASCIZ strings here ;------------------------------ eom equ 0 ; end of message ;------------------------------ ; The mnemonic strings table: ;------------------------------ ; ; Mainly Str db 'opc',eom exceptions when the ; assembler does not accept the label, SubStr is a ; reserved word: -> ISubStr. ; ; This is a very boring part of the disassembler... ; Each string ends in a capital instead of an eom now... ; this saves some memory bytes... ; XlatStr db 'xlaT' LahfStr db 'lahF' SahfStr db 'sahF' PushfStr db 'pushF' PopfStr db 'popF' AaaStr db 'aaA' DaaStr db 'daA' AasStr db 'aaS' DasStr db 'daS' CbwStr db 'cbW' CwdStr db 'cwD' RetStr db 'reT' RetfStr db 'retF' IntStr db 'inT' IntoStr db 'intO' IretStr db 'ireT' ClcStr db 'clC' CmcStr db 'cmC' StcStr db 'stC' CldStr db 'clD' StdStr db 'stD' CliStr db 'clI' StiStr db 'stI' HltStr db 'hlT' WaitStr db 'waiT' LockStr db 'locK' NopStr db 'noP' JmpStr db 'jmP' JzStr db 'jZ' JlStr db 'jL' JleStr db 'jlE' JcStr db 'jC' JbeStr db 'jbE' JpeStr db 'jpE' JoStr db 'jO' JsStr db 'jS' JnzStr db 'jnZ' JgeStr db 'jgE' JgStr db 'jG' JncStr db 'jnC' JaStr db 'jA' JpoStr db 'jpO' JnoStr db 'jnO' JnsStr db 'jnS' LoopStr db 'looP' LoopzStr db 'loopZ' LoopnzStr db 'loopnZ' JcxzStr db 'jcxZ' CallStr db 'calL' PushStr db 'pusH' PopStr db 'poP' XchgAxStr db 'xchg aX' XchgStr db 'xchG' IncStr db 'inC' DecStr db 'deC' AndStr db 'anD' OrStr db 'oR' XorStr db 'xoR' TestStr db 'tesT' CmpStr db 'cmP' AddStr db 'adD' AdcStr db 'adC' ISubStr db 'suB' SbbStr db 'sbB' RepStr db 'reP' RepzStr db 'repZ' MovswStr db 'movsW' MovsbStr db 'movsB' CmpswStr db 'cmpsW' CmpsbStr db 'cmpsB' ScaswStr db 'scasW' ScasbStr db 'scasB' LodswStr db 'lodsW' LodsbStr db 'lodsB' StoswStr db 'stosW' StosbStr db 'stosB' IInStr db 'iN' OutStr db 'ouT' MovStr db 'moV' ShlStr db 'shL' ShrStr db 'shR' SarStr db 'saR' RolStr db 'roL' RorStr db 'roR' RclStr db 'rcL' RcrStr db 'rcR' LeaStr db 'leA' LdsStr db 'ldS' LesStr db 'leS' ImulStr db 'i' MulStr db 'muL' IdivStr db 'i' DivStr db 'diV' NegStr db 'neG' NotStr db 'noT' AadStr db 'aaD' AamStr db 'aaM' EscStr db 'esC' JmpfStr db 'jmpF' callfStr db 'callF' ; All unparsable bytes are rewarded with 'db ' DbStr db 'db',eom ;------------------------------ ; 16-bit register table ;------------------------------ ; The names of the registers when they are used as ; 16-bits registers ; Reg16Tab: db 0,'ax',eos db 1,'cx',eos db 2,'dx',eos db 3,'bx',eos db 4,'sp',eos db 5,'bp',eos db 6,'si',eos db 7,'di',eos db 0FFH,eos ;------------------------------ ; 8-bit register table ;------------------------------ ; The names of the registers when they are used as ; 8-bits registers ; Reg8Tab: db 0,'al',eos db 1,'cl',eos db 2,'dl',eos db 3,'bl',eos db 4,'ah',eos db 5,'ch',eos db 6,'dh',eos db 7,'bh',eos db 0FFH,eos ;------------------------------ ; segment register table ;------------------------------ ; The names of the segment registers ; RegSegTab: db 26H,'es',eos db 2EH,'cs',eos db 36H,'ss',eos db 3EH,'ds',eos db 0FFH,eos ;------------------------------ ; r/m modi table ;------------------------------ RmTab: db 0,'si+bx',eos db 1,'di+bx',eos db 2,'si+bp',eos db 3,'di+bp',eos db 4,'si',eos db 5,'di',eos db 6,'bp',eos db 7,'bx',eos db 0FFH,eos ;------------------------------ ; some special strings ;------------------------------ AxStr db 'ax',eos AlStr db 'al',eos DxStr db 'dx',eos ClStr db 'cl',eos ;--------------------------------------------------------------- ; Handler 'routines' for when additional parsing is needed ;--------------------------------------------------------------- ; Some instructions can not be fully parsed from the first byte ; of the instruction. In these case a seccond loop through the ; parser engine is needed. Most of the time another parser table ; is used to parse the next byte of the instruction. This shows ; the real power of this parser-engine approach. The handlers ; 'memorize' the important parts of the current state of the ; parser, get new input and continue parsing. Note that these ; 'routines' are not real routines at all, not return instruction ; to be found here. ; ; handler for segment overrides HSegOver: mov InstOver,bl ; store for later use mov si,offset DisMn ; main table jmp DisCont ; goto entry point ; handler for register/memory, immediate HRegMemImm: call WBit ; mov si,offset DisRegMemImm ; more parsing jmp DisCont ; ; handler for signed register/memory, immediate HSRegMemImm: call WBit ; call SBit ; mov si,offset DisSRegMemImm ; jmp DisCont ; ; handler for shifts and rotates HShRot: call WBit ; call VBit ; mov si,offset DisShRot ; jmp DisCont ; ; handler for math instructions HMath: call WBit ; mov si,offset DisMath ; jmp DisCont ; ; handler for aam instruction HAam: mov si,offset DisAam ; jmp DisCont ; ; handler for aad instruction HAad: mov si,offset DisAad ; jmp DisCont ; ; handler for pop reg/mem HPopRegMem: mov si,offset DisPopRegMem ; jmp DisCont ; ; handler for C6/C7 HC6Instr: call WBit ; get Wbit mov si,offset DisC6Instr ; jmp DisCont ; ; handler for FF instr HFfInstr: mov si,offset DisFFInstr ; jmp DisCont ; ; handler for FE/FF instr HFeInstr: call WBit ; get Wbit mov si,offset DisFeInstr ; jmp DisCont ; ;--------------------------------------------------------------- ; The parameter routines ;--------------------------------------------------------------- ; This is a real zoo of routines. These routines process the ; parameters of the instructons. Looks like a terrible mess, is ; in fact one... Re-used code all over the place. This is why a ; disassembler is said to be difficult to make. Once you look ; closer you'll find quite some system in this madmess. ; ; The current byte from the parser engine (untouched..) is in ; register bl, es:[di] points to the next byte in the code. The ; registers ds:si, ax, cx and dx are free for use. The ; parameter (ptr) InstLoc points to the first byte of the code ; (before parsing started). The parameter (byte) InstSize ; contains the number of bytes in the instruction (so-far..), the ; parameters InstSBit etc.. come from the parser or are produced ; here. ; ; NB: I use somewhat of a trick here. Many of these 'routines' ; would normally end in a code sequence: ; ; call Something ; ret ; ; That is functionally equivalent with ; ; jmp Something ; ; Keep that in mind when you get surprised at the 'subroutines' ; ending in jmp Something.... The same trick has been used in the ; parser engine. ; ;------------------------------ ; opcode dump ;------------------------------ ; happens when the parser fails to find ; a valid opcode/etc... Forces back to ; single-byte instruction, db string is ; already printed, this just dumps the ; opcode byte and resets the InstSize. ; DisFail:mov InstSize,1 ; one-byte instruction les di,InstLoc ; reset pointer es:di mov al,es:[di] ; dump the opcode jmp DisHexByte ; ;------------------------------ ; load next byte from code ;------------------------------ ; byte into bl, ; di and InstSize increased DisNext:mov bl,es:[di] ; inc di ; inc InstSize ; ret ; ;------------------------------ ; Bit extraction routines ; The opcode bytes are full of ; bits. Some of these bits must ; stored for later use ;------------------------------ ;------------------------------ ; test and store W-bit WBit: test bl,00000001B ; jz wbit1 ; inc InstWBit ; wbit1: ret ; ;------------------------------ ; test and store D-bit DBit: test bl,00000010B ; jz dbit1 ; inc InstDBit ; dbit1: ret ; ;------------------------------ ; test and store S-bit SBit: test bl,00000010B ; jz sbit1 ; inc InstSBit ; sbit1: ret ; ;------------------------------ ; test and store V-bit VBit: test bl,00000010B ; jz vbit1 ; inc InstVBit ; vbit1: ret ; ;------------------------------ ; Some simple string dump routines ; I do not always have registers free ; to start printing things. ; Besides: this makes the routines ; easier to understand. ;------------------------------ ;------------------------------ ; print ',' Comma: push ax ; mov al,',' ; call DisChar ; pop ax ; ret ; ;------------------------------ ; print '3' Par3: mov al,'3' ; jmp DisChar ; ;------------------------------ ; print ']' RHook: push ax ; mov al,']' ; call DisChar ; pop ax ; ret ; ;------------------------------ ; print 'ax' or 'al', depends ; upon the WBit AxAlPar:mov dx,offset AlStr ; cmp InstWBit,0 ; test word/byte jz AlPar ; mov dx,offset AxStr ; AlPar: jmp Mess ; ;------------------------------ ; print 'cl' ClPar: mov dx,offset ClStr ; jmp Mess ; ;------------------------------ ; print 'dx' DxPar: mov dx,offset DxStr ; jmp Mess ; ;------------------------------ ; hex bytes/words/pointers/offsets ;------------------------------ ;------------------------------ ; HexBPar:call DisNext ; hex byte parameter mov al,bl ; jmp DisHexByte ; ;------------------------------ ; SgnBPar: call DisNext ; next byte as +/- displ test bl,10000000B ; look at the sign from the byte jnz sgnbp1 ; mov al,'+' ; '+' call DisChar ; jmp sgnbp2 ; sgnbp1: mov al,'-' ; '-' call DisChar ; neg bl ; make number positive sgnbp2: mov al,bl ; jmp DisHexByte ; hex byte ;------------------------------ ; HexWPar:mov ax,es:[di] ; get word call DisNext ; call DisNext ; jmp DisHexWord ; ;------------------------------ ; <8-bits offset> (jmp/call target) Dis8Par:call DisNext ; mov al,bl ; 8-bit offset cbw ; convert to 16-bit jmp DisPar ; treat as 16-bit ;------------------------------ ; <16-bits offset> (jmp/call target) Dis16Par: mov ax,es:[di] ; call DisNext ; call DisNext ; DisPar: add ax,word ptr InstLoc ; add ax,InstSize ; mov word ptr JCDest,ax ; mov word ptr JCDest+2,es ; DisFin: call DisHexWord ; jmp DestName ; try to find a name ;------------------------------ ; full pointer: XXXX:XXXX (jmp/call target) PtrPar: add InstSize,4 ; mov ax,es:[di+2] ; mov word ptr JCDest+2,ax ; call DisHexWord ; mov al,':' ; call DisChar ; mov ax,es:[di] ; mov word ptr JCDest,ax ; add di,4 ; jmp DisFin ; ;------------------------------ ; DestName: find name for JCDest DestName: lds si,JCDest ; call LookNt ; jc DnFin ; destnm0:mov al,' ' ; call DisChar ; mov al,'(' ; call DisChar ; destnm1:lodsb ; cmp al,eom ; jz destnm2 ; call DisChar ; jmp destnm1 ; destnm2:mov al,')' ; call DisChar ; ; get ds back DnFin: mov ax,cs ; mov ds,ax ; ret ; ;------------------------------ ; LookNt: find name for ds:si ; see if a nametable there LookNt: mov ax,word ptr cs:NameTab or ax,word ptr cs:NameTab+2 jz LookNtE ; ; names table there, see if segments ok LookNt1:mov ax,ds ; cmp ax,word ptr cs:NameTab+2; jnz LookNtE ; ; get offset in ax mov ax,si ; get offset ; ds:si -> names table lds si,cs:NameTab ; ds:si -> names table ; scan the table LookNt2: ; see if end of table cmp word ptr [si],0 ; record 0000,00 jnz LookNtS ; is end of table cmp byte ptr [si+2],eom ; jz LookNtE ; ; test if address matches LookNtS:cmp ax,[si] ; match with ax jz LookNtF ; ; name table not finished, skip name add si,2 ; skip the name LookNt3:cmp byte ptr [si],eom ; see if string ended jz LookNt4 ; inc si ; jmp LookNt3 ; ; skip the eom-byte LookNt4:inc si ; jmp LookNt2 ; ; no name table there LookNtE:stc ; ret ; LookNtF:add si,2 ; clc ; ret ; ;------------------------------ ; ImPar: cmp InstWBit,0 ; test word/byte jnz ImParW ; ; immediate byte jmp HexBPar ; ; immediate word ImParW: jmp HexWPar ; ;------------------------------ ; memory accessing parts ;------------------------------ ;------------------------------ ; [ if one has been found ; [ if no segment override Segmt: push ax ; push si ; mov al,InstOver ; mov si,offset RegSegTab ; call StrTab ; jc SegMt1 ; mov al,':' ; call DisChar ; SegMt1: mov al,'[' ; always with RHook call DisChar ; pop si ; pop ax ; ret ; ;------------------------------ ; <[adr]> MemPar: call SegMt ; may have segment override mov ax,es:[di] ; get pointer mov word ptr JCDest,ax ; mov ax,es ; mov word ptr JCDest+2,ax ; call HexWPar ; call DestName ; jmp RHook ; ;------------------------------ ; ModRmPar: mov al,bl ; find modus and al,11000000B ; get rid of unusable bits ; simple scan through the modi cmp al,00000000B ; jz ModIs00 ; cmp al,01000000B ; jz ModIs01 ; cmp al,10000000B ; jz ModIs10 ; cmp al,11000000B ; jz ModIs11 ; jmp DisFail ; else parsing failed ; Modus 11: r/m = a register field ModIs11:mov al,bl ; get register field jmp RRegPar ; ; Modus 00: test if r/m = 110 -> only displacement ModIs00:mov al,bl ; and al,00000111B ; cmp al,00000110B ; jnz ModIs001 ; Modus 00 r/w = 110 -> just displacement jmp MemPar ; ; normal case Modus 00, r/m <> 110 ; register as indicated in r/m, no displacement ModIs001: call SegMt ; segover (if any) call RmCode ; jmp RHook ; ] ; modus 01: single-byte displacement, sign-extended ModIs01:call SegMt ; segover (if any) call RmCode ; call SgnBPar ; jmp RHook ; ] ; modus 10: r/m with 16-bits offset ModIs10:call SegMt ; call RmCode ; mov al,'+' ; call DisChar ; call HexWPar ; jmp RHook ; RmCode: push ax ; push si ; mov al,bl ; mov si,offset RmTab ; jmp RegFrTab ; ;------------------------------ ; Register routines ;------------------------------ ; The register is mostly coded ; somewhere in the opcode or ; the extension byte. ;------------------------------ ; as register SegPar: mov al,bl ; and al,00011000B ; or al,00100110B ; mov si,offset RegSegTab jmp StrTab ; ;------------------------------ ; Reg16Par: push ax ; push si ; mov al,bl ; jmp RReg16Par ; ;------------------------------ ; reg par in bits 5-3 of al MRegPar:push ax ; push si ; mov al,bl ; shr al,1 ; shr al,1 ; shr al,1 ; jmp RegPar ; ;------------------------------ ; reg par in bits 2-0 of al RRegPar:push ax ; push si ; ; jmp RegPar ; RegPar: ; read register name from table mov si,offset Reg8Tab ; default=8bits cmp InstWBit,0 ; test 16-bits jz RegFrTab ; no -> get from table RReg16Par: ; 16-bits reg mov si,offset Reg16Tab ; ; jmp RegFrTab ; get from table RegFrTab: and al,00000111B ; scan table for register call StrTab ; pop si ; pop ax ; ret ; ;------------------------------ ; the real parameter sets ;------------------------------ ;------------------------------ ; A,Imm AImPar: call WBit ; call AxAlPar ; call Comma ; jmp ImPar ; ;------------------------------ ; in instruction parameters InPar: call WBit ; set word flag call AxAlPar ; call Comma ; test bl,08H ; jnz InPar1 ; jmp HexBPar ; InPar1: jmp DxPar ; ;------------------------------ ; out instruction parameters OutPar: call WBit ; test bl,08H ; jnz OutPar1 ; call HexBPar ; jmp OutPar2 ; OutPar1:call DxPar ; OutPar2:call Comma ; jmp AxAlPar ; ;------------------------------ ; reg,immediate RegImPar: test bl,00001000B ; different WBit location jz regimpar1 ; inc InstWBit ; regimpar1: mov al,bl ; call RRegPar ; call Comma ; jmp ImPar ; ;------------------------------ ; A,mem AMemPar:call WBit ; call AxAlPar ; call Comma ; jmp MemPar ; ;------------------------------ ; mem,A MemAPar:call WBit ; call MemPar ; call Comma ; jmp AxAlPar ; ;------------------------------ ; r/m,seg MRmSeg: inc InstWBit ; always words call DisNext ; mov bh,bl ; call ModRmPar ; call Comma ; mov bl,bh ; get bl back jmp SegPar ; ;------------------------------ ; seg,r/m SegMRm: inc InstWBit ; always words call DisNext ; call SegPar ; call Comma ; jmp ModRmPar ; ;------------------------------ ; , always words LModRegRmPar: inc InstWBit ; call DisNext ; jmp ModRegRmPar ; ;------------------------------ ; the same as below, without reverse-order option ; WModRegRmPar: call WBit ; call DisNext ; jmp ModRegRmPar ; ;------------------------------ ; DWModRegRmPar: call WBit ; set the bits call DBit ; call DisNext ; ; see in what order to handle things cmp InstDBit,0 ; jz dwmodregrm1 ; ; normal order of things ModRegRmPar: call MRegPar ; first the middle register call Comma ; call ModRmPar ; then the mod and r/m field ret ; ; reverse order dwmodregrm1: push bx ; save bx for register call ModRmPar ; first the mod and r/m field call Comma ; pop bx ; call MRegPar ; then the middle register field ret ;------------------------------ ; , RegMemImm: call ModRmPar ; call Comma ; cmp InstSbit,0 ; jnz rmimm2 ; ; no sign, is just a hex word/byte cmp InstWbit,0 ; jnz rmimm1 ; jmp HexBPar ; rmimm1: jmp HexWPar ; ; sign set too, MUST be byte rmimm2: jmp SgnBPar ; ;------------------------------ ; shift/rotate parameters ShRotPar: call ModRmPar ; call Comma ; cmp InstVBit,0 ; jnz shrp1 ; ; VBit = 0 -> xx,1 mov al,'1' ; jmp DisChar ; ; VBit <> 0 -> xx,cl shrp1: jmp ClPar ; ;------------------------------ ; pop/push , always word LModRmPar: inc InstWBit ; jmp ModRmPar ; ;------------------------------ ; esc instruction pars EscPars:mov al,bl ; dump xxx bits as a number and al,00000111B ; add al,'0' ; call DisChar ; call Comma ; call DisNext ; next xxx bit field too mov al,bl ; and al,00111000B ; shr al,1 ; shr al,1 ; shr al,1 ; add al,'0' ; call DisChar ; call Comma ; jmp ModRmPar ; ;== init =================================================== init: mov ah,4AH ; free all unused memory mov bx,offset einde ; mov cl,4 ; shr bx,cl ; inc bx ; int 21H ; ; set the stackpointer, grows down mov sp,offset StackTop ; jmp prog ; ;----------------------------------------------------------- ; The request packet etc.. for the driver functions ;----------------------------------------------------------- ; ; The request packet (13 bytes fixed, rest flexible) ; rqpack equ this byte ; rqlen db 0 ; length of the packet rqunit db 0 ; unit code rqcmd db 0 ; command code rqsts dw 0 ; status return rqrsvd db 8 dup(0) ; reserved rqstdlen equ 13 ; standard packet is 13 bytes ; ; flexible part ; rqFlex equ this byte ; ; rqInitNumUnit: ; Init : No of units rqMediaDesc: ; Media check: descriptor byte rqBuildBPBDesc: ; Build BPB: descriptor byte rqRdWrDesc: ; Rd/Wr: descriptor byte db 0 ; 0 ; rqInitEndPtr: rqMediaRet: ; Media check: returned value rqBuildBPBScratch: ; Build BPB: 512 bytes scratch space rqRdWrAddr: ; Rd/Wr: transfer address db 0 ; 1 ; db 0 ; 2 ; db 0 ; 3 ; db 0 ; 4 ; rqInitBPBPtr: ; Init: BPB Pointer rqInitCMDStr: ; Init: command string rqBuildBPBPtr: ; BuildBPB: BPB pointer rqRdWrNSec: ; Rd/Wr: sector count db 0 ; 5 ; db 0 ; 6 ; rqRdWrStartSec: ; Rd/Wr: starting sector db 0 ; 7 ; db 0 ; 8 ; rqInitBlockDevNr: db 0 ; 9 ; ; ;== Uninitialised data ===================================== ; ; NB: from here on I do NOT store the parameters/buffers on ; disk. They are allocated run-time. ; ; Sector buffer. I have room for one sector only, sorry, but ; otherwise this thing becomes too big.... ; SecBuf equ this byte ; SecBufSize equ 512 ; ; ; Command tail string ; InitCMDStr equ SecBuf+SecBufSize InitCMDStrSize equ 80 ; ; ;== stack ================================================== ; ; Stack space ; StackBot equ InitCMDStr+InitCmdStrSize StackTop equ StackBot+256 ; ;== einde ================================================== ; ; End of the code once loaded ; einde equ StackTop ; ; MASM seems to need this, I am confused about what it means.. ; code ends ; end of code segment end start ; end of the program, entry point = start