; ---------------------------------------- ; - Monitor/Debugger voor 386-processors - ; - (C) Copyright 1995 by F13 Software - ; ---------------------------------------- ; ------------ Main Program -------------- ; ---------------------------------------- .386p assume cs:CODE,ds:DATA,es:DATA ; ---------------------------------------- ; --------- LINKER DEFINITIONS ----------- ; ---------------------------------------- public bin2hex ; EAX=value, ES:DI=buffer, DL=size, DH=simple extrn disone : proc ; FS:BP=code, ES:DI=buffer, ret. AX=inst. len extrn dfadsiz : byte,dfopsiz : byte ; Addr/Opr size (1 means 32 bit) extrn genregs : byte ; General register array ; ---------------------------------------- ; ------------ DATA SECTION -------------- ; ---------------------------------------- DATA segment public use16 hextbl db '0123456789abcdef' ; Hexadecimal translation table curcode dd 0 ; Current code pointer (U/V) curdata dd 0 ; Current data pointer (D/E/S) savlen dd 0 ; Storage for length (S) handle dw 0 ; File handle for writing (S) tmpflag dw 0 ; Temporary flags storage while editing flags flgtrns db '....odi.sz.a.p.c' ; Flags translation table resflag dw 0 ; Monitor is operating resident tmpaddr dd 0 ; Temporary address, for breakpoints brkpts dd 4 dup (0) ; Breakpoint logical addresses progdr7 dd 0 ; To enable breakpoints only in program regtrns dw 'XA',offset pushloc-4,'XC',offset pushloc-8,'XD',offset pushloc-0ch dw 'XB',offset pushloc-10h,'PS',offset saveesp,'PB',offset pushloc-18h dw 'IS',offset pushloc-1ch,'ID',offset pushloc-20h,'PI',offset saveip dw 'SC',offset savecs,'SD',offset saveseg+6,'SE',offset saveseg+4 dw 'SF',offset saveseg+2,'SG',offset saveseg,'SS',offset savess brktrns dw offset codebrk,offset dtwobrk,0,offset dtrwbrk codebrk db 'Code',0 dtwobrk db 'Data, write only',0 dtrwbrk db 'Data, read/write',0 parblk dw 0,offset args,DATA dd 0,0 newsssp dd 0 newcsip dd 0 oldssesp dp 0 ; Restoring stack after intoprg/int 1 sequence newpsp dw 0 ; For freeing memory occupied by loaded program oldpsp dw 0 ; For freeing memory for program oldint1 dd 0 ; Old debug interrupt vector oldint2 dd 0 ; Old NMI vector (for breaking purposes) noread dw 0 ; Don't read next command from console (already there because of '^') cmdcnt dw 0 ; Number of times to return the same string ('&') putc dw offset putcscr getc dw offset getckbd serport dw 2f8h welcmsg db 0dh,0ah,'---------------------------------------' db 0dh,0ah,'- Monitor/Debugger for 386-processors -' db 0dh,0ah,'- (C)Copyright 1995 by F13 Software -' db 0dh,0ah,'---------------------------------------',0dh,0ah,0 gargmsg db 'Program arguments: ',0 gflgmsg db 'Type flag(s) to change, case indicates state: ',0 illcmsg db '*** Illegal command! ***',0 invamsg db '*** Invalid address! ***',0 invnmsg db '*** Invalid number! ***',0 lerrmsg db '*** Error loading file! ***',0 cerrmsg db '*** Error creating file! ***',0 serrmsg db '*** Error saving file! ***',0 parmmsg db '*** Parameter missing! ***',0 invrmsg db '*** Invalid register name! Use (E)AX-(E)DI, IP, F, CS-GS or SS. ***',0 invfmsg db '*** Invalid flag! Use O,D,I,S,Z,A,P and/or C. ***',0 ctfrmsg db '*** Child must be terminated (and stay resident) before you can use keep! ***',0 alrrmsg db '*** Already in resident mode! ***',0 rsnqmsg db '*** You can''t quit while in resident mode! (DOS would blow your head off) ***',0 nbrkmsg db '*** All breakpoints in use! ***',0 termmsg db '*** Program terminated. ***',0 brkpmsg db 'Enabled breakpoints:',0 nonemsg db 'No breakpoints enabled',0 dmmymsg db '*** The Edit command is not implemented yet. ***',0 cmdbuf db 128 dup (?) rptcmd db 128 dup (?) buffer db 128 dup (?) argbuf db 127 args db 0,0dh,126 dup (?) DATA ends ; ---------------------------------------- ; ------------ CODE SECTION -------------- ; ---------------------------------------- CODE segment public use16 ; ------ Saved information of child ------ poploc label byte saveseg dw 4 dup (0) ; GS,FS,ES,DS savegen dd 8 dup (0) ; EDI,ESI,EPB,onzin,EBX,EDX,ECX,EAX pushloc label byte ; SS:SP=pushloc, pushad, push ds,es,fs,gs savessesp label pword saveesp dd 0 savess dw 0 savecsip label dword saveip dw 0 savecs dw 0 saveflg dw 0000001000000010b ; When loaded, only ints are enabled ; ---------- General Routines ------------ putcscr proc near mov ah,0eh mov bx,0fh int 10h ret endp putcser proc near push dx push ax mov dx,serport add dx,5 ; (2fd) pc0: in al,dx test al,20h jz short pc0 sub dx,5 ; (2f8) pop ax out dx,al pop dx ret endp puts proc near ; DS:SI=ASCIIZ text, DL=hrt flag ps0: lodsb test al,al jz ps1 call putc jmp short ps0 ps1: test dl,dl jz ps2 mov al,0dh call putc mov al,0ah call putc ps2: ret endp getckbd proc near xor ah,ah int 16h ret endp getcser proc near push dx mov dx,serport add dx,5 ; (2fd) gc0: in al,dx test al,1 jz gc0 sub dx,5 ; (2f8) in al,dx pop dx ret endp gets proc near ; DS:DI=buffer push di xor cx,cx gs0: call getc cmp al,0dh jz short gs2 cmp al,8 jz short gs1 stosb call putc inc cx jmp short gs0 gs1: test cx,cx jz short gs0 dec di call putc mov al,' ' call putc mov al,8 call putc dec cx jmp short gs0 gs2: call putc mov al,0ah call putc xor al,al stosb pop di ret endp ; CX=number of chars read getshigh proc near ; DS:DI=buffer test word ptr cmdcnt,0ffffh jz gh0 dec word ptr cmdcnt push di mov si,offset rptcmd mov cx,128 rep movsb pop di mov si,di mov dl,1 call puts ret gh0: call gets cmp byte ptr [di],'&' jz gh1 ret gh1: mov si,di inc si call getnum jcxz gh2 mov cmdcnt,ax mov al,':' call putc push di mov di,offset rptcmd call gets pop di mov byte ptr [di],0 xor cx,cx ret gh2: mov si,offset invnmsg mov dl,1 call puts jmp gh0 endp bin2hex proc near ; AL/AX/EAX=value, ES:DI=dest, DL=size (bytes) push eax ; DH bit 0=1 means leave out '0' and 'h' in '0ffh' DH bit 1=1 means interpret byte sign push bx push cx test dh,2 jz bh3 cmp dl,1 jnz bh3 mov bl,al test bl,bl mov al,'+' jns bh4 mov al,'-' neg bl bh4: stosb mov al,bl bh3: mov bx,offset hextbl mov cl,dl shl cl,3 sub cl,4 ror eax,cl ; Desired nibble->lowest nibble mov cl,dl shl cl,1 xor ch,ch ; CX=Nibble count=byte count*2 test dh,1 jnz short bh1 push ax and al,0fh cmp al,0ah jb short bh0 mov al,'0' stosb pop ax bh1: push ax and al,0fh bh0: xlat stosb pop ax rol eax,4 ; Next nibble loop bh1 test dh,1 jnz bh2 mov al,'h' stosb bh2: pop cx pop bx pop eax ret endp readcmd proc near ; SI=buffer mov di,noread test di,di jz rc0 mov si,di jmp rc1 rc0: mov di,si mov al,'-' call putc push si call getshigh ; Output '-' and read command line pop si rc1: mov al,[di] test al,al jnz rc2 mov word ptr noread,0 jmp rc5 rc2: cmp al,'^' jnz rc3 xor al,al stosb mov noread,di jmp rc5 rc3: cmp al,'a' jb short rc4 cmp al,'z' ja short rc4 sub al,20h ; Convert to uppercase rc4: stosb jmp rc1 rc5: ret ; SI=line to parse & execute endp getnum proc near ; DS:SI=string push edx xor edx,edx xor cx,cx gn0: lodsb sub al,'0' jb short gn1 ; Below '0', stop cmp al,9 jbe short gn2 ; Below or equal '9', process sub al,7 ; Maybe 'A'-'F' cmp al,0ah jb short gn1 ; Below 'A', stop cmp al,0fh ja short gn1 ; Above 'F', stop gn2: or dl,al ; Squeeze nibble in rol edx,4 ; Move next (empty) nibble forward inc cx cmp cx,8 jbe gn0 xor cx,cx ; If more than 8 chars, error, stop gn1: ror edx,4 ; Correct last shift mov eax,edx dec si ; Char that stopped process pop edx ret endp ; EAX=value, CX=chars processed getaddr proc near ; ES:DI=place to store address call getnum jcxz short ga1 cmp eax,0ffffh ja short ga1 mov dx,ax lodsb test al,al jnz ga2 mov ax,dx stosw ; If next byte zero, store offset, done jmp short ga0 ga2: cmp al,':' jnz short ga1 ; Else if not ':', error call getnum jcxz short ga1 cmp eax,0ffffh ja short ga1 stosw ; Store offset mov ax,dx stosw ; Store segment ga0: clc ret ga1: stc ret endp ; CY=error ; ------------ Int 1 handler ------------ int2: cli mov word ptr cs:pushloc-4,ax mov al,20h out 20h,al mov ax,word ptr cs:pushloc-4 ; Issue EOI command to interrupt ctrlr. int1: cli pop cs:saveip pop cs:savecs pop cs:saveflg mov cs:saveesp,esp mov cs:savess,ss push cs pop ss mov sp,offset pushloc pushad push ds push es push fs push gs xor eax,eax mov dr7,eax ; Disable breakpoints while in monitor mov ax,DATA mov ds,ax mov es,ax lss esp,oldssesp ; sti cld mov eax,dr6 ; Get triggered flags and ax,0fh ; AX=000000000000bbbb xor dx,dx ; triggered: 3210 mov cx,4 i10: shrd dx,ax,1 shrd dx,ax,1 shr ax,1 ; DH= bbbbbbbb loop i10 ; triggered: 33221100 not dh ; Not triggered breakpoints may stay mov al,byte ptr progdr7 and al,dh ; Disable rest mov byte ptr progdr7,al xor eax,eax mov dr6,eax ; Clear triggered flags mov al,0dh call putc mov al,0ah call putc ; Show CRLF call showall ; Show processor context ret ; ---------- Command handlers ----------- ; - Used by Setbk(B), Step(P) and Go(G) - breakpt proc near ; EAX=address, DL=no., DH=R/W field movzx bx,dl shl bx,2 mov [bx+offset brkpts],eax ; Save logical address movzx ebx,ax ; EBX=0000oooo shr eax,12 ; EAX=000sssso and al,0f0h ; EAX=000ssss0 add ebx,eax ; EBX=physical address (00aaaaaa) mov al,cs:byte ptr bp0+2 ; Get modrm byte of mov drX,ebx instr. and al,0c7h ; Clear reg field mov cl,dl ; Save breakpoint number shl dl,3 ; Shift brkpoint no. to reg field or al,dl ; Squeeze in reg field mov cs:byte ptr bp0+2,al ; Put altered modrm byte back jmp short bp0 ; Flush instruction prefetch queue bp0: mov dr0,ebx ; Store breakpoint address mov eax,progdr7 shl cl,1 ; CL=breakpoint number*2 mov dl,3 shl dl,cl or al,dl ; Enable breakpoint shl cl,1 ; CL=breakpoint number*4 add cl,8 and edx,300h shl edx,cl or eax,edx ; Set type (Code fetch/Data W/Data R/W) mov progdr7,eax ; Store breakpoint info ret endp ; - Used by Trace(T), Step(P) and Go(G) - intoprg proc near mov al,8fh out 70h,al in al,61h or al,8 jmp short ip0 ip0: out 61h,al xor al,8 jmp short ip1 ip1: out 61h,al mov al,0fh out 70h,al ; Clear NMI (in other words enable it) cli mov eax,progdr7 mov dr7,eax ; Set breakpoints, if any mov dword ptr oldssesp,esp mov word ptr oldssesp+4,ss push cs pop ss mov sp,offset poploc pop gs pop fs pop es pop ds popad lss esp,cs:savessesp push cs:saveflg push cs:savecs push cs:saveip iret endp ; -------- Set breakpoint (B/M/N) ------- setbk proc near mov eax,progdr7 mov cx,3 sb0: rcr al,2 jnc short sb1 loop sb0 sb1: jcxz short sb3 mov dx,3 sub dl,cl ; DL=free breakpoint no. mov bx,cs:savecs ; If code breakpoint, def. seg. = CS mov al,[si-1] ; Get cmd (B/M/N=Code/Data RW/Data W) cmp al,'B' jz short sb2 inc dh mov bx,cs:saveseg+6 ; If data breakpoint, def. seg. = DS cmp al,'N' jz short sb2 inc dh inc dh ; DH=breakpoint type sb2: mov di,offset tmpaddr mov [di+2],bx ; Set default segment for breakpoint push dx call getaddr pop dx jc short sb4 mov eax,tmpaddr ; EAX=ssssoooo (breakpoint address) call breakpt ; Set breakpoint ret sb3: mov si,offset nbrkmsg jmp short sb5 sb4: mov si,offset invamsg sb5: mov dl,1 call puts ret endp ; ------ Clear all breakpoints (C) ------ clrbrkp proc near xor eax,eax mov progdr7,eax ret endp ; ------- Inspect breakpoints (I) ------- insbk proc near mov eax,progdr7 test al,al jz short ib2 push ax mov si,offset brkpmsg mov dl,1 call puts pop ax mov cx,3 ib0: push cx rcr al,2 jnc short ib1 mov dx,3 sub dx,cx ; DX=enabled breakpoint no. shl dx,2 mov bx,dx push eax push dx mov di,offset buffer mov ax,[bx+offset brkpts+2] mov dx,102h call bin2hex mov al,':' stosb mov ax,[bx+offset brkpts] mov dx,102h call bin2hex mov ax,' ' stosw xor al,al stosb mov si,offset buffer xor dl,dl call puts pop cx add cl,16 shr eax,cl and ax,3 shl ax,1 mov bx,ax mov si,[bx+offset brktrns] mov dl,1 call puts pop eax ib1: pop cx loop ib0 ret ib2: mov si,offset nonemsg mov dl,1 call puts ret endp ; ------------- Trace (T) --------------- trace proc near or word ptr cs:saveflg,100h ; Set trap flag jmp intoprg ; Into program endp ; -------------- Step (P) --------------- step proc near mov di,offset buffer lfs bp,cs:savecsip call disone ; FS:BP=next instruction mov ax,fs shl eax,16 mov ax,bp mov dx,3 ; DH=0, code breakpoint call breakpt ; Set breakpoint 3 there and word ptr cs:saveflg,0feffh ; No traps jmp intoprg ; And into it endp ; --------------- Go (G) ---------------- go proc near test byte ptr [si],0ffh ; Set breakpoint? jz short go0 mov di,offset tmpaddr mov ax,cs:savecs mov [di+2],ax call getaddr jc short go1 mov eax,tmpaddr ; EAX=ssssoooo (breakpoint address) mov dx,3 ; Code breakpoint, breakpoint 3 call breakpt ; Set it go0: and word ptr cs:saveflg,0feffh ; Clear trap flag jmp intoprg ; Go into program go1: mov si,offset invamsg mov dl,1 call puts ret endp ; ------- Show/Edit Registers (R) ------- ; Showall displays: ; ; eax=01234567 ecx=76543210 edx=89abcdef ebx=fedcba98 ; esp=00000000 epb=ffffffff esi=5555aaaa edi=aaaa5555 ; ds=0123 es=3210 fs=4567 gs=7654 ss=89ab flags=odIsZaPc ; cs:ip=0505:a0a0 66 b8 00 4c 00 00 mov eax,00004c00h showall proc near mov si,offset genregs+8 mov bx,offset pushloc-4 mov cx,8 sa0: push cx push si push bx mov di,offset buffer movsw movsb mov al,'=' stosb mov eax,cs:[bx] cmp cx,4 jnz short sa1 mov eax,saveesp sa1: push cx mov dx,104h call bin2hex pop cx mov ax,2020h stosw xor al,al stosb mov si,offset buffer xor dl,dl cmp cx,5 jz short sa2 cmp cx,1 jnz short sa7 sa2: inc dl sa7: call puts pop bx pop si sub bx,4 add si,12 pop cx loop sa0 ; Display general register file add bx,2 mov cx,5 sa3: push cx push bx mov di,offset buffer mov al,'g'+2 sub al,cl cmp cx,1 jnz short sa4 mov al,'s' sa4: stosb mov ax,3d73h ; 's=' stosw mov ax,cs:[bx] cmp cx,1 jnz short sa5 mov ax,savess sa5: mov dx,102h call bin2hex mov ax,20h stosw mov si,offset buffer xor dl,dl call puts pop bx sub bx,2 pop cx loop sa3 ; Show DS,ES,FS,GS,SS mov di,offset buffer mov eax,67616c66h ; flag stosd mov ax,3d73h ; s= stosw mov bx,offset flgtrns mov cx,16 sa6: mov al,16 sub al,cl xlat ; Get state letter cmp al,'.' ; If interesting flag, continue jz sa8 mov dx,cx dec dx bt cs:saveflg,dx mov dl,0 ; Test flag bit position -> C rcl dl,6 ; If set, DL=20h else DL=0 sub al,dl ; State letter uppercase if flag set stosb sa8: loop sa6 xor al,al stosb mov si,offset buffer mov dl,1 call puts ; Show flags mov di,offset buffer mov eax,693a7363h stosd ; cs:i mov ax,3d70h stosw ; p= mov byte ptr dfadsiz,0 mov byte ptr dfopsiz,0 lfs bp,dword ptr cs:savecsip mov word ptr curcode,bp mov word ptr curcode+2,fs call disone mov si,offset buffer mov dl,1 call puts ; Show cs:ip, hex/disassembled instr. ret endp doflg: mov si,offset gflgmsg xor dl,dl call puts mov di,offset buffer call gets mov ax,cs:saveflg mov tmpflag,ax mov si,offset buffer df0: push cx lodsb cmp al,' ' jz short df2 cmp al,',' jz short df2 cmp al,'.' jz df3 xor bx,bx cmp al,'A' jb short df1 cmp al,'Z' ja short df1 add al,20h inc bx ; Uppercase: BX=1, lowercase: BX=0 df1: mov di,offset flgtrns mov cx,16 repnz scasb jnz short df3 mov dx,0fffeh shl dx,cl shl bx,cl and tmpflag,dx or tmpflag,bx df2: pop cx loop df0 mov ax,tmpflag mov cs:saveflg,ax mov sp,bp ret df3: mov si,offset invfmsg mov dl,1 call puts mov sp,bp ret regs proc near mov ax,[si] test al,al jz showall ; No parameters? Show all regs mov bp,sp ; Save stack in case of error aborts test ah,ah jnz short rg7 ; Just one letter as parameter? cmp al,'F' jz doflg ; Must be an 'F' for flags jmp short rg2 ; Else invalid rg7: push si xor cx,cx rg0: lodsb test al,al jz short rg1 inc cx cmp al,',' jnz short rg0 dec cx push cx call getnum mov edx,eax mov bl,cl pop cx pop si ; EDX=val, CX=regname len, BX=num len test bl,bl jz short rg6 ; If num len =0, invalid number cmp cl,3 ja short rg2 ; If regname len >3, invalid register jz short rg4 ; If =3, see if starts with 'E' cmp bl,4 mov bl,0 ; Use 16 bits by default jbe short rg5 ; If =2, num len must be <=4, else inv. jmp short rg6 rg4: lodsb cmp al,'E' jnz short rg2 ; If len=3, first must be 'E', else inv mov bl,1 ; If ok, use 32 bits rg5: lodsw ; AX=register name test bl,bl jz rg11 ; If 32 bits, see if name!=xS or IP cmp ax,7069h jz rg2 cmp ah,'S' jz rg2 rg11: mov di,offset regtrns-2 mov cx,15 rg8: inc di inc di scasw loopnz rg8 ; Search for it jnz short rg2 ; Not found? Invalid register mov di,[di] ; Get its address test bl,bl ; 16 or 32 bit? jnz short rg9 mov cs:[di],dx ; Store 16 bits jmp short rg10 rg9: mov cs:[di],edx ; Store 32 bits rg10: mov sp,bp ret rg1: mov si,offset parmmsg jmp short rg3 rg2: mov si,offset invrmsg jmp short rg3 rg6: mov si,offset invnmsg rg3: mov dl,1 call puts mov sp,bp ret endp ; ---------- Set Arguments (A) ---------- setarg proc near mov si,offset gargmsg xor dl,dl call puts mov di,offset argbuf+1 call gets mov argbuf,cl add di,cx mov byte ptr [di],0dh ret endp ; -------------- Load (L) --------------- load proc near mov bx,newpsp test bx,bx jz short ld1 mov ax,4c00h int 21h ; If necessary, terminate child first ld1: cli mov dword ptr oldssesp,esp mov word ptr oldssesp+4,ss sti mov ax,4b01h mov bx,offset parblk mov dx,si int 21h ; Load new one cli mov ax,DATA mov ds,ax mov es,ax lss esp,oldssesp sti jc ld2 mov ah,62h int 21h cmp bx,oldpsp ; Did we get here because the child jnz short ld0 ; just terminated? No, continue load mov word ptr newpsp,0 ; Yes, clear prog loaded flag cmp byte ptr cmdbuf,'Q' jz quit ; Did we want to quit? Yes, quit cmp byte ptr cmdbuf,'L' jz short ld1 ; Did we want to load again? Yes, do so mov si,offset termmsg ; Child must be terminated via normal mov dl,1 ; debugging functions call puts ; Show 'prog. terminated' message jmp short ld3 ; and stay in monitor but abort load ld0: mov newpsp,bx ; Save child's PSP mov word ptr curdata,0 mov word ptr curdata+2,bx ; Also to curdata... mov word ptr cs:saveseg+4,bx ; and to ES mov word ptr cs:saveseg+6,bx ; and DS push es push cs pop es mov di,offset savegen mov cx,8 xor eax,eax rep stosd ; Clear general registers mov di,offset saveseg stosd ; And FS and GS pop es mov word ptr cs:saveflg,0000001000000010b ; I=1, rest 0 mov eax,newcsip mov curcode,eax mov cs:savecsip,eax mov eax,newsssp ror eax,16 ; MSW=SP, LSW=SS mov cs:savess,ax shr eax,16 ; MSW=0, LSW=SP mov cs:saveesp,eax ; Process child's SS:SP and CS:IP ld3: ret ld2: mov si,offset lerrmsg mov dl,1 call puts ret endp ; -------------- Dump (D) --------------- dumpone proc near ; FS:SI=source; ES:DI=buffer mov ax,fs mov dx,102h call bin2hex mov al,':' stosb mov ax,si mov dx,102h call bin2hex mov al,' ' stosb stosb mov bx,di add bx,49 mov cx,16 mov dx,101h dp0: push cx lods byte ptr fs:[si] push bx call bin2hex pop bx cmp al,' ' jnb dp1 mov al,'.' dp1: mov es:[bx],al inc bx mov al,' ' stosb pop cx loop dp0 stosb add di,16 xor al,al stosb ret endp dump proc near test byte ptr [si],0ffh jz short dm2 mov di,offset curdata call getaddr jc short dm1 dm2: lfs si,curdata mov cx,16 dm0: push cx mov di,offset buffer call dumpone push si mov si,offset buffer mov dl,1 call puts pop si pop cx loop dm0 mov word ptr curdata,si ret dm1: mov si,offset invamsg mov dl,1 call puts ret endp ; --------- Disasm 16/32 (U/V) ---------- disasm proc near test byte ptr [si],0ffh jz short da2 mov di,offset curcode call getaddr jc short da1 da2: lfs bp,curcode mov cx,16 da0: push cx mov di,offset buffer call disone mov si,offset buffer mov dl,1 call puts pop cx loop da0 mov word ptr curcode,bp ret da1: mov si,offset invamsg mov dl,1 call puts ret endp disa16 proc near mov byte ptr dfadsiz,0 mov byte ptr dfopsiz,0 call disasm ret endp disa32 proc near mov byte ptr dfadsiz,1 mov byte ptr dfopsiz,1 call disasm ret endp ; ------------ Redirect (W) ------------- redir proc near mov ax,offset putcscr mov dx,offset getckbd test byte ptr [si],0ffh jz short rd0 call getnum jcxz rd1 mov serport,ax mov dx,ax add dx,3 ; (2fb) mov al,80h out dx,al ; Select baud-rate divisor regs sub dx,3 ; (2f8) mov ax,3 ; 38400 baud out dx,ax ; Store baud-rate add dx,3 ; (2fb) mov al,3 out dx,al ; No parity, 8 data bits, 1 stop bit inc dx ; (2fc) mov al,0fh out dx,al ; Loop mode off mov ax,offset putcser mov dx,offset getcser cli rd0: mov putc,ax mov getc,dx ret rd1: mov si,offset invnmsg mov dl,1 call puts ret endp ; -------------- Quit (Q) --------------- quit proc near test word ptr resflag,1 jnz short qu0 xor eax,eax mov progdr7,eax mov dr7,eax mov ax,2501h lds dx,oldint1 int 21h ; Reset interrupt 1 vector mov ax,DATA mov ds,ax mov ax,2502h lds dx,oldint2 ; Reset NMI vector int 21h mov ax,4c00h int 21h qu0: mov si,offset rsnqmsg mov dl,1 call puts ret endp ; --------- Keep resident (K) ----------- keep proc near test word ptr newpsp,0ffffh jnz short kp2 test word ptr resflag,1 jnz short kp3 mov word ptr resflag,1 mov al,8fh out 70h,al in al,61h or al,8 jmp short kp0 kp0: out 61h,al xor al,8 jmp short kp1 kp1: out 61h,al mov al,0fh out 70h,al ; Clear NMI (in other words enable it) mov bx,oldpsp mov es,bx mov ds,bx mov ax,3100h mov dx,prgend sub dx,bx int 21h ; Keep resident kp2: mov si,offset ctfrmsg jmp short kp4 kp3: mov si,offset alrrmsg kp4: mov dl,1 call puts ret endp ; -------------- SaveBlk ---------------- saveblk proc near mov di,offset curdata call getaddr mov dx,offset invamsg jc sv1 cmp byte ptr [si],',' mov dx,offset parmmsg jnz sv1 inc si call getnum mov dx,offset invnmsg jcxz sv1 cmp eax,0ffff0h+0ffffh ja sv1 mov savlen,eax cmp byte ptr [si],',' mov dx,offset parmmsg jnz sv1 inc si mov ah,3ch mov cx,20h mov dx,si int 21h mov dx,offset cerrmsg jc sv1 mov handle,ax assume ds:nothing,es:DATA sv4: mov ah,40h mov bx,es:handle mov ecx,es:savlen cmp ecx,8000h jb sv3 mov cx,8000h sv3: lds dx,es:curdata int 21h jc sv2 ; err cmp ax,cx jnz sv2 ; err movzx eax,ax sub es:savlen,eax jz sv5 ; done add word ptr es:curdata+2,800h jmp short sv4 assume ds:DATA,es:DATA sv5: push DATA pop ds mov ah,3eh mov bx,handle int 21h mov dx,offset serrmsg jc sv1 ret sv1: mov si,dx mov dl,1 call puts ret sv2: push DATA pop ds mov ah,3eh mov bx,handle int 21h mov dx,offset serrmsg jmp short sv1 ; Strange order to enable short jumps endp ; --------------- Dummy ----------------- edit proc near mov si,offset dmmymsg mov dl,1 call puts ret endp ; ---------- Illegal command ------------ illcmd proc near mov si,offset illcmsg mov dl,1 call puts ret endp ; ------ Command translation table ------ calltbl dw offset setarg,offset setbk,offset clrbrkp,offset dump dw offset edit,offset illcmd,offset go,offset illcmd,offset insbk dw offset illcmd,offset keep,offset load,offset setbk,offset setbk dw offset illcmd,offset step,offset quit,offset regs,offset saveblk dw offset trace,offset disa16,offset disa32,offset redir dw 3 dup (offset illcmd) ; ---------- Argument handler ----------- doargs proc near ; DS=ES=PSP mov si,80h lodsb ; Get command line length movzx cx,al jcxz short ar2 ; None? Stop mov di,si mov al,' ' repz scasb ; Get rid of leading spaces jcxz short ar2 ; Nothing else? Stop lea si,[di-1] mov al,',' repnz scasb ; Search for ',' mov ax,DATA mov es,ax ; ES=DATA jcxz short ar0 mov byte ptr [di-1],0dh ; If found, replace it by CR push si mov si,di mov di,offset args mov al,cl stosb ; Store length byte to args rep movsb ; Store everything behind ',' mov al,0dh stosb ; Store CR pop si ar0: mov di,offset buffer ar1: lodsb cmp al,0dh jz short ar2 stosb jmp short ar1 ; Move arguments to buffer, until CR ar2: ret endp ; -------- Program Entry Point ---------- start: mov ax,ds mov bx,PRGEND sub bx,ax ; PRGEND-PSP=paragraphs needed mov ah,4ah int 21h ; Shrink allocated memory call doargs ; Handle arguments mov ax,DATA mov es,ax mov es:oldpsp,ds mov ds,ax ; DS=ES=DATA mov si,offset welcmsg mov dl,1 call puts ; Show message push ds push es mov ax,3501h int 21h mov word ptr oldint1,bx mov word ptr oldint1+2,es mov ax,3502h int 21h mov word ptr oldint2,bx mov word ptr oldint2+2,es push cs pop ds xor eax,eax mov dr7,eax mov ax,2501h mov dx,offset int1 int 21h mov al,8fh out 70h,al ; Disable NMI mov ax,2502h mov dx,offset int2 int 21h pop es pop ds ; Install int 1 & NMI handler mov si,offset buffer test byte ptr [si],0ffh jz short st0 ; Any arguments? call load ; Yes, try to load st0: mov si,offset cmdbuf call readcmd ; Read command lodsb ; Get first letter test al,al jz short st0 ; Empty? Then repeat sub al,'A' jb short st1 cmp al,'Z'-'A' ja short st1 ; Test against 'A'-'Z' boundaries cbw shl ax,1 mov bx,ax call word ptr cs:[bx+offset calltbl] ; Handle command (DS:SI=args) jmp st0 st1: call illcmd jmp st0 CODE ends ; ---------------------------------------- ; --------------- STACK ------------------ ; ---------------------------------------- STK segment stack dw 128 dup (?) resstk label word STK ends PRGEND segment public ends end start