Category : System Diagnostics for your computer
Archive   : STEP13.ZIP
Filename : STEP13.ASM

 
Output of file : STEP13.ASM contained in archive : STEP13.ZIP


PAGE 60,132
TITLE 'Step INT 13H Calls'
NAME STEP13
;
; ***********************************************************
; * *
; * S T E P 1 3 *
; * *
; * Rev 1.0 *
; * *
; * Nov 20, 1987 *
; * *
; * Mike Parker *
; * 2374 Meadowpark Court *
; * Maryland Heights, MO 63043 *
; * *
; * CIS 70270,161 *
; * *
; * STEP13 intercepts BIOS INT 13h calls and displays a *
; * window that shows the function being called and the *
; * parameters being passed to it. Program execution will *
; * resume when a key is pressed. This allows you to single *
; * step through accesses to the disk at the BIOS level. *
; * *
; * STEP13 can be activated and de-activated through key- *
; * board commands. *
; * *
; ***********************************************************
;
;
;
TESTING EQU 0

BG EQU 10H ; BG sets background color (10h = BLUE)

BLACK EQU 00H + BG
BLUE EQU 01H + BG
GREEN EQU 02H + BG
CYAN EQU 03H + BG
RED EQU 04H + BG
MAGENTA EQU 05H + BG
BROWN EQU 06H + BG
WHITE EQU 07H + BG
GRAY EQU 08H + BG
LTBLUE EQU 09H + BG
LTGREEN EQU 0AH + BG
LTCYAN EQU 0BH + BG
LTRED EQU 0CH + BG
LTMAGENTA EQU 0DH + BG
YELLOW EQU 0EH + BG
HIWHITE EQU 0FH + BG
;
CR EQU 13
LF EQU 10
BIGR EQU 1352H ; 'R' key
SMALLR EQU 1372H ; 'r'
BIGS EQU 1F53H ; 'S' key
SMALLS EQU 1F73H ; 's'
ESCAPE EQU 011BH
HOME EQU 4700H
UPARROW EQU 4800H
PGUP EQU 4900H
LEFTARROW EQU 4B00H
RIGHTARROW EQU 4D00H
XEND EQU 4F00H
DOWNARROW EQU 5000H
PGDN EQU 5100H
;
BLANK EQU 32
BRDROW EQU 205
BRDCOL EQU 186
ULC EQU 201
URC EQU 187
LLC EQU 200
LRC EQU 188
;
REGBX EQU 00 ; offsets on saved register stack
REGCX EQU 02
REGDX EQU 04
REGDI EQU 06
REGSI EQU 08
REGBP EQU 10
REGES EQU 12
;
; Format of Step_Flg
;
STOP EQU 01 ; stop and display if bit 0 = 1
TERM EQU 02 ; terminate STEP13 if bit 1 = 1
;
IF TESTING
INT13OFF EQU 0e0h*4 ; during testing an unused INT is used
ELSE
INT13OFF EQU 13h*4
ENDIF

INT1cOFF EQU 1ch*4
INT28OFF EQU 28h*4

Code SEGMENT PUBLIC PARA 'CODE'

ASSUME CS:Code, DS:Code

ORG 100h

Main: ; starting point
jmp SHORT Main_1

Res_Str db 'STEP13' ; Resident ID string to determine if
db 0 ; already resident
;
Main_1:
cli
cld
mov AX,CS
mov SS,AX
mov SP,OFFSET Pstack
mov BX,DS ; get difference between segments
sub AX,BX ; so .EXE file can run during testing
sti
;
mov BX,OFFSET PRSIZE ; program size in bytes
mov CL,4
shr BX,CL ; convert it to paragraphs
add BX,AX ; add difference between segments
inc BX ; make it next multiple of 16 bytes
mov CX,BX ; CX = number paragraphs in this program
; offset from PSP base
;
mov BX,002ch
mov BX,[BX] ; get segment addr of environment from PSP
;
mov AX,CS
mov DS,AX ; change DS and ES to local area
mov SegPSP,ES ; save starting segment to release later
mov ES,AX
mov SegEnv,BX ; save environment segment to release also
mov SegEND,CX ; now save CX
;
; Now that segment registers are all in order see if this program is already
; resident by finding out who currently has INT 13h.
; Get segment of the program and look for ID string found at start of this
; program.
;
push ES
mov AL,13h
mov AH,35h
int 21h ; return address in ES:BX
call Cmp_Str ; search for ID string
pop ES
jnz chkeq ; not found, continue
;
; This program already installed
;
mov DX,OFFSET ERRmsg
mov AH,9
int 21h
;
mov AH,4ch ; terminate (without staying resident)
mov AL,00
int 21h

;
; Check equipment list to see if we have graphics or monochrome card
;
chkeq:
int 11h
and AX,0030h ; save video mode
mov BX,0b000h
cmp AX,0030h ; monochrome?
jz setcrd
mov BX,0b800h ; no, graphics card
setcrd:
mov Scrseg,BX ; save base segment
;
; Calculate starting address on screen for window
;
mov BX,OFFSET W1 ; window parameters
call Window_Parms ; calc start addr of window, wid and ht
;
; Make room for window save area - AX is number of bytes needed for window
;
mov CL,4
shr AX,CL ; divide by 16 to get # paragraphs
inc AX ; handle any remainder
mov BX,SegPSP ; starting PSP segment
add BX,SegEND ; size of program in paragraphs
mov Winseg,BX ; starting segment of window save area
add SegEND,AX ; expand total space needed by this
; programs code and window save area
;
; Redirect INT 13h, INT 28h and INT 1ch
;
push ES
cli
mov AX,0
mov ES,AX
mov AX,OFFSET New13
xchg AX,ES:[INT13OFF]
mov Save13,AX
mov AX,CS
xchg AX,ES:[INT13OFF+2]
mov Save13+2,AX

IFE TESTING
mov AX,OFFSET New1c
xchg AX,ES:[INT1cOFF]
mov Save1c,AX
mov AX,CS
xchg AX,ES:[INT1cOFF+2]
mov Save1c+2,AX
;
mov AX,OFFSET New28
xchg AX,ES:[INT28OFF]
mov Save28,AX
mov AX,CS
xchg AX,ES:[INT28OFF+2]
mov Save28+2,AX
ENDIF

sti
pop ES
;
mov DX,OFFSET INITmsg
mov AH,9
int 21h

IF TESTING
mov SP,OFFSET Tstack
or BYTE PTR CS:Step_Flg,STOP ; set flag to stop and display

include test13.asm
;
mov AH,4ch ; terminate
mov AL,00
int 21h
ELSE
;
; Terminate and stay resident
;
mov AL,00
mov AH,31h
mov DX,SegEND ; segment offset to release free memory
int 21h ; won't return
ENDIF

;
; Compare string in program to ID string
; Entry:
; ES = segment of owning process
; DS = current programs segment
;
; Return:
; ZF = 1 if strings equal (TSR already installed)
; ZF = 0 if not found
;
Cmp_Str:
mov SI,OFFSET Res_Str
mov DI,SI ; source and destination offsets the same
mov BX,-1 ; assume not found here
cmp1:
lodsb
scasb
jnz neql
or AL,AL ; terminating null
jnz cmp1
mov BX,0 ; found string
neql:
mov AX,BX
or AX,AX
ret

;
; Restore vectors and free resident program area.
; Interrupts are off upon entry.
;
Restore:
push AX
push DS
push ES
;
mov AX,CS
mov DS,AX ; load my data segment
;
mov AX,0
mov ES,AX
mov AX,Save13 ; restore INT vectors
mov ES:[INT13OFF],AX
mov AX,Save13+2
mov ES:[INT13OFF+2],AX

IFE TESTING
mov AX,Save28
mov ES:[INT28OFF],AX
mov AX,Save28+2
mov ES:[INT28OFF+2],AX
;
mov AX,Save1c
mov ES:[INT1cOFF],AX
mov AX,Save1c+2
mov ES:[INT1cOFF+2],AX
ENDIF

;
; Free allocated memory from environment and this program
;
mov ES,SegEnv
mov AH,49h
int 21h
;
mov ES,SegPSP ; start of this programs segment
mov AH,49h
int 21h
;
pop ES
pop DS
pop AX
ret

;
; INT 1ch will be redirected to here each time a timer tick occurs.
; Do keyboard BIOS call to get state of control and shift keys.
; If both LEFT and RIGHT shift keys down, enable the Int 13 intercept code.
; If CONTROL key and LEFT and RIGHT shift keys down, set the terminate flag.
; On the next Int 28h call by DOS, we will remove this program from memory.
;
New1c:
push AX
mov AH,02 ; get shift status
int 16h
and AL,07 ; save control, left and right shift status
cmp AL,03 ; are they both down?
jnz tstctl ; jump if no
or BYTE PTR CS:Step_Flg,STOP ; set flag to stop and display Int 13h
tstctl:
cmp AL,07 ; three keys down?
jnz n1cout
or BYTE PTR CS:Step_Flg,TERM ; set terminate flag
n1cout:
pop AX
jmp CS:[DWORD PTR Save1c] ; continue in the chain

;
; The DOS idle loop will be redirected to here.
; Check for TERMINATE bit set. If so, free all memory used by this TSR program
; and restore all vectors to their original values.
;
New28:
test BYTE PTR CS:Step_Flg,TERM
jz n28out
call Restore
cli
n28out:
jmp CS:[DWORD PTR Save28] ; continue in the chain

;
; INT 13h will be redirected to here
;
New13:
push AX ; use USERS'S stack for first 3 words
push DS
pushf ; push them but we don't need them here
;
cli
mov AX,CS ; now switch to internal stack
mov DS,AX
mov SSsave,SS
mov SPsave,SP
mov SS,AX
mov SP,OFFSET Pstack
sti
;
push ES
push BP
push SI
push DI
push DX
push CX
push BX
mov BP,SP ; save base pointer to display reg's
mov ES,AX
test BYTE PTR Step_Flg,STOP ; should we stop and display?
jz norun ; no, restore registers and leave
;
; Recover registers on USER'S stack
;
push ES
les BX,Ssave
mov AX,ES:[BX+2] ; get DS
mov DSsave,AX
mov AX,ES:[BX+4] ; get AX
mov AXsave,AX
mov Last_FC,AH ; save as last function code
mov AX,ES:[BX+6] ; get IP
mov IPsave,AX
mov AX,ES:[BX+8] ; get CS
mov CSsave,AX
mov AX,ES:[BX+10] ; get FLAGS on entry
mov FLsave,AX
pop ES
;
cld ; clear direction flag for string operations
test BYTE PTR Skip_Flg,0ffh ; skip certain functions?
jz noskip ; no, continue
mov AX,AXsave ; get USER's AX register with function code
cmp AH,Skip_Typ ; compare upper half to type to skip
jz norun ; don't want to break on this one again
mov BYTE PTR Skip_Flg,00 ; skip flag was set but this is new
; function, so break from now on
noskip:
call Open_Window
call Show_Func ; show function number
call Disp_Text ; display description of BIOS function
call Show_Help
;
call Get_Key ; wait for a key pressed
call Disp_Key ; dispatch to key handler
jc noskip ; if carry set, need to redisplay window
call Close_Window
norun:
pop BX
pop CX
pop DX
pop DI
pop SI
pop BP
pop ES
cli
mov SS,SSsave ; switch back to user's stack
mov SP,SPsave
;
mov AX,FLsave ; get original user's flags before INT 13h
push AX ; sneak them into flag register
popf
;
pop AX ; dummy pop to get flags off stack
pop DS ; restore registers left on user's stack
pop AX ; now get real AX

IFE TESTING
pushf
cli ; give interrupt code IF = 0 like normal
call CS:[DWORD PTR Save13]
ENDIF

push AX ; use USERS'S stack for first 3 words
push DS
pushf ; they will get used this time
;
cli
mov AX,CS ; now switch to internal stack
mov DS,AX
mov SSsave,SS
mov SPsave,SP
mov SS,AX
mov SP,OFFSET Pstack
sti
;
push ES
push BP
push SI
push DI
push DX
push CX
push BX
mov BP,SP ; save base pointer to display reg's
mov ES,AX
cld ; clear direction flag for string operations
;
; Recover registers on USER'S stack
;
push ES
les BX,Ssave
mov AX,ES:[BX+0] ; get FLAGS on exit
mov FLsave,AX
mov AX,ES:[BX+2] ; get DS
mov DSsave,AX
mov AX,ES:[BX+4] ; get AX
mov AXsave,AX
mov AX,ES:[BX+6] ; get IP
mov IPsave,AX
mov AX,ES:[BX+8] ; get CS
mov CSsave,AX
pop ES
;
xor AL,AL
xchg AL,Ret_Flg ; read value and clear flag for next time
test AL,0ffh ; is flag set to display return value?
jz nostop
;
call Open_Window
call Show_Func ; show function number
call Disp_Ret ; display return code in AX and flags
call Get_Key ; wait for a key pressed
call Close_Window
nostop:
pop BX
pop CX
pop DX
pop DI
pop SI
pop BP
pop ES
cli
mov SS,SSsave
mov SP,SPsave
popf
pop DS
pop AX
retf 2

;
; Display return code in AX, carry and zero flags
;
Disp_Ret:
mov CX,0301h
mov DX,OFFSET RCStr
mov DI,14
mov AX,AXsave
call OutWord
;
mov DX,OFFSET CFStr
mov DI,14
add DI,DX
mov AL,'0'
test FLsave,0001 ; carry flag is least significant bit
jz dcy
inc AL ; carry was set so change to '1'
dcy:
mov [DI],AL ; store it in string
add CX,0100h ; move cursor position
call Disp_Str
;
mov DX,OFFSET ZFStr
mov DI,14
add DI,DX
mov AL,'0'
test FLsave,0040h ; test zero flag
jz dzf
inc AL ; zero was set so change to '1'
dzf:
mov [DI],AL ; store it in string
add CX,0100h ; move cursor position
call Disp_Str
;
mov CX,0a0ah ; display some help
mov DX,OFFSET HlpStr5
mov AH,YELLOW
call WPrint
;
mov CX,0b0bh ; move cursor position
mov DX,OFFSET HlpStr6
mov AH,YELLOW
call WPrint
ret

;
; Display function code description in window
;
Show_Func:
mov CX,0007h ; cursor position - row, col
mov DX,OFFSET FCStr ; message address
mov DI,DX
add DI,19 ; offset this many into string
mov AL,Last_FC ; get last function code
call Shexbyt ; convert it to ASCII
mov BX,OFFSET W1 ; window
mov AH,YELLOW ; attribute
call WPrint
ret

;
; Display input parameters
;
Disp_Parms:
call Disp_Drv ; show drive number
call Disp_Head ; head number
call Disp_Trk ; track number
call Disp_Sec ; sector number
ret

;
; Display input parameters for Hard Disk calls
; Displays "CYLINDER" instead of "TRACK"
;
Disp_HD_Parms:
call Disp_Drv
call Disp_Head
call Disp_Cyl ; show cylinder number
ret

;
; Display Drive ID
;
Disp_Drv:
mov CX,0401h
mov DX,OFFSET DrvStr
mov DI,DX
add DI,8
mov AX,[BP+REGDX] ; DL register has drive #
call Shexbyt
mov AH,HIWHITE
call WPrint
ret

;
; Display Head #
;
Disp_Head:
add CX,0100h
mov DX,OFFSET HedStr
mov DI,DX
add DI,8
mov AX,[BP+REGDX] ; DH register has head #
mov AL,AH
call Sdecbyt
mov AH,HIWHITE
call WPrint
ret

;
; Display Track #
;
Disp_Trk:
add CX,0100h
mov DX,OFFSET TrkStr
mov DI,DX
add DI,7
mov AX,[BP+REGCX] ; CH register has track #
mov AL,AH
xor AH,AH ; clear upper half
call HextoDec
dec SI ; back up to get three chars instead
movsb ; of normal two
movsb
movsb
mov AH,HIWHITE
call WPrint
ret

;
; Display Cylinder #
;
Disp_Cyl:
add CX,0100h
mov DX,OFFSET CylStr
mov DI,DX
add DI,7
mov AX,[BP+REGCX] ; CH register has cylinder #
mov AL,AH
xor AH,AH ; clear upper half
call HextoDec
dec SI
movsb
movsb
movsb
mov AH,HIWHITE
call WPrint
ret

;
; Display Sector #
;
Disp_Sec:
add CX,0100h
mov DX,OFFSET SecStr
mov DI,DX
add DI,8
mov AX,[BP+REGCX] ; CL register has sector #
call Sdecbyt
mov AH,HIWHITE
call WPrint
ret

;
; Display HELP in Window
;
Show_Help:
mov CX,0a03h ; cursor position - row, col
mov AH,LTRED ; attribute for char
mov AL,'S'
call WChar ; write one character with attribute
;
add CX,0001h ; add 1 to column number
mov DX,OFFSET HlpStr1 ; message address
mov AH,WHITE
call WPrint
;
add CX,0012h ; move column number
mov AH,LTRED ; attribute for char
mov AL,'R'
call WChar ; write one character with attribute
;
add CX,0001h ; move column number
mov DX,OFFSET HlpStr2
mov AH,WHITE
call WPrint
;
mov CX,0b01h ; cursor position - row, col
mov AH,LTRED
mov AL,'E'
call WChar
;
add CX,0001h
mov AH,LTRED
mov AL,'S'
call WChar
;
add CX,0001h
mov AH,LTRED
mov AL,'C'
call WChar
;
add CX,0001h
mov DX,OFFSET HlpStr3
mov AH,WHITE
call WPrint
;
add CX,000dh
mov AH,LTRED
mov AL,18h ; up arrow
call WChar
;
add CX,0001h
mov AH,LTRED
mov AL,19h ; down arrow
call WChar
;
add CX,0001h
mov AH,LTRED
mov AL,1bh ; left arrow
call WChar
;
add CX,0001h
mov AH,LTRED
mov AL,1ah ; right arrow
call WChar
;
add CX,0001h
mov DX,OFFSET HlpStr4
mov AH,WHITE
call WPrint
ret

;
; HextoDec converts a word in AX to an ASCII string
; Entry:
; AX = word to convert
; Exit:
; SI = Pointer to last two character of ASCII string in 'Astr'
;
HextoDec:
push AX
push CX
push DX
push DI
;
mov DI,OFFSET Astr
mov CX,10000
xor DX,DX
div CX ; num / 10000
add AL,'0'
stosb
mov AX,DX
mov CX,1000
xor DX,DX
div CX ; num / 1000
add AL,'0'
stosb
mov AX,DX
mov CX,100
xor DX,DX
div CX ; num / 100
add AL,'0'
stosb
mov AX,DX
mov CX,10
xor DX,DX
div CX ; num / 10
add AL,'0'
stosb
mov AX,DX
add AL,'0'

stosb
xor AL,AL ; terminator in string
stosb
sub DI,3 ; back up pointer
mov SI,DI
;
pop DI
pop DX
pop CX
pop AX
ret

;
; Display the text description of the current BIOS function code
;
Disp_Text:
mov CX,LENFC / 6 ; number of entries
mov AX,AXsave ; get user's function code
cmp AH,CL ; is it in range?
jnb not_fnd ; jump if no
mov DI,OFFSET FCtbl
cmpdt:
cmp AH,[DI+1] ; is this the right function?
jz dtexec
add DI,6 ; no, point to next
loop cmpdt
not_fnd:
mov DX,OFFSET STRUN
mov CX,0201h
call Disp_Str
ret

dtexec:
mov CX,0201h ; row, col to position string
mov DX,[DI+2] ; get pointer to string
call [WORD PTR DI+4] ; go to display routine
ret

;
; Display string of DOS function code description
;
; Entry:
; DX = pointer to string
; CX = Row, Col
;
Disp_Str:
mov BX,OFFSET W1
mov AH,HIWHITE
call WPrint
ret

;
; Routines to build string for display - some values need to be filled in
; Entry:
; DX = pointer to string
; CX = Row, Col in window
;
; Read sectors
;
Disp_02:
mov DI,5
add DI,DX
mov AX,AXsave ; AL has number of sectors to read
call Sdecbyt ; convert it to ASCII decimal
call Disp_Str
call Disp_Parms ; show input parameters
call Disp_Buf ; show buffer addr in ES:BX
ret
;
; Write sectors
;
Disp_03:
mov DI,6
add DI,DX
mov AX,AXsave ; AL has number of sectors to write
call Sdecbyt ; convert it to ASCII decimal
call Disp_Str
call Disp_Parms ; show input parameters
call Disp_Buf
ret

;
; Verify sectors
;
Disp_04:
mov DI,7
add DI,DX
mov AX,AXsave ; AL has number of sectors to write
call Sdecbyt ; convert it to ASCII decimal
call Disp_Str ; show function description
call Disp_Parms ; show input parameters
ret

;
; Format track
;
Disp_05:
call Disp_Str
call Disp_Parms
call Disp_Buf
ret

;
; Read and Write Long
;
Disp_0a:
Disp_0b:
call Disp_Str
call Disp_HD_Parms ; show Drive, Head and Cylinder
call Disp_Sec ; show Sector #
call Disp_Buf ; and Buffer address
ret

;
; Seek to Cylinder
;
Disp_0c:
call Disp_Str
call Disp_HD_Parms ; show Drive, Head and Cylinder
ret

;
; Alternate Disk Reset
;
Disp_0d:
call Disp_Str
call Disp_Drv ; show drive ID
ret

;
; Test for Drive Ready
;
Disp_10:
call Disp_Str
call Disp_Drv
ret

;
; Recalibrate Drive
;
Disp_11:
call Disp_Str
call Disp_Drv
ret

;
; Get Disk Type
;
Disp_15:
call Disp_Str
call Disp_Drv
ret

;
; Set Disk Type
;
Disp_17:
mov DI,17
add DI,DX
mov AX,AXsave ; AL has disk type
call Shexbyt
call Disp_Str
;
mov AX,AXsave
mov DX,OFFSET Str170
cmp AL,00 ; no disk?
jz d17shw
mov DX,OFFSET Str171
cmp AL,01 ; regular diskette?
jz d17shw
mov DX,OFFSET Str173
cmp AL,03 ; high capacity diskette?
jnz d17drv
d17shw:
add CX,0100h
call Disp_Str
d17drv:
call Disp_Drv
ret

;
; Display ES:BX registers
;
Disp_Buf:
mov DX,OFFSET BufStr ; show buffer address
mov DI,17
add CX,0100h
call OutESBX
ret

;
; Convert users's ES and BX to ASCII, imbed them in target string
; Entry:
; DX = pointer to string
; DI = offset where to put converted ES:BX
; CX = Row, Col
;
OutESBX:
add DI,DX
mov AX,[BP+REGES] ; get user's ES
call Shexwrd
inc DI ; skip past ':'
mov AX,[BP+REGBX] ; get user's BX
call Shexwrd
call Disp_Str
ret

;
; Convert word in AX to ASCII, imbed it in target string
; Entry:
; AX = word to convert
; CX = row, col
; DX = pointer to string
; DI = offset where to put converted AX
;
OutWord:
add DI,DX
call Shexwrd
call Disp_Str
ret


Open_Window:
push BP
push ES
;
; Calculate current page offset each time since it can change at any time
;
mov AH,0fh ; get current video state
int 10h
mov CL,BH ; returns BH = active page #
xor CH,CH
mov AX,0100h ; page length in paragraphs
mul cx
add AX,Scrseg ; add base segment of screen
mov Pageseg,AX
;
; Save current contents of window area
;
mov BX,OFFSET W1 ; window parameters
mov SI,[BX].startmem ; screen addr of start of window
mov DX,[BX].height
mov ES,Winseg ; point to allocated memory block
mov DI,0000
saverow:
push DS
mov CX,[BX].xwidth
mov DS,Pageseg ; get screen segment
push SI ; save screen offset
rep movsw ; do word to get char and attribute
pop SI
pop DS
add SI,BytesPL ; next row down
dec DX
jnz saverow
;
; Draw window
;
mov ES,Pageseg ; get screen segment
mov DI,[BX].startmem ; physical addr of start of window
mov AH,HIWHITE
;
; Draw top border
;
push DI
mov AL,ULC ; write upper left corner
stosw
mov CX,[BX].xwidth
sub CX,2
mov AL,BRDROW
rep stosw
mov AL,URC ; write upper right corner
stosw
pop DI
add DI,BytesPL ; next row down
;
; Draw middle of window
;
mov DX,[BX].height
sub DX,2
winrows:
push DI
mov AL,BRDCOL ; left border column
stosw
mov CX,[BX].xwidth
sub CX,2
mov AL,BLANK
rep stosw
mov AL,BRDCOL ; right border column
stosw
pop DI
add DI,BytesPL ; next row down
dec DX
jnz winrows
;
; Draw bottom border
;
push DI
mov AL,LLC ; write lower left corner
stosw
mov CX,[BX].xwidth
sub CX,2
mov AL,BRDROW
rep stosw
mov AL,LRC ; write lower right corner
stosw
pop DI
;
pop ES
pop BP
ret


Close_Window:
push ES
mov BX,OFFSET W1
mov DI,[BX].startmem ; physical addr of screen area
mov ES,Pageseg ; point to allocated memory block
mov SI,0000 ; start of save memory block
mov DX,[BX].height
resrow:
push DS
mov CX,[BX].xwidth
mov DS,Winseg ; get saved memory segment
push DI ; save screen offset
rep movsw ; do word to get char and attribute
pop DI
pop DS
add DI,BytesPL ; next row down
dec DX
jnz resrow
pop ES
ret

;
; Print a string in window.
; Entry:
; CX = Row, Col position relative to start of window
; BX = Window pointer
; AH = Attribute
; DX = addr of string
;
WPrint:
push ES
call GetRC ; convert to row, col in DI
mov ES,Pageseg
mov SI,DX ; move string address
wpwrt:
cmp BYTE PTR [SI],00 ; test for string terminator
jz wpexit
lodsb
stosw
jmp wpwrt
wpexit:
pop ES
ret

;
; Write one character and attribute to window
;
; Entry:
; CX = Row, Col position relative to start of window
; BX = Window pointer
; AH = attr
; AL = char
;
WChar:
push ES
call GetRC ; convert to row, col in DI
mov ES,Pageseg
stosw
pop ES
ret

;
; Convert row, col in CX to physical addr in DI
;
GetRC:
push AX
push DX
mov DI,[BX].startmem ; physical start of window memory
add DI,BytesPL ; go down a row
add DI,2 ; go in one char, this is 0,0
mov AL,CH
xor AH,AH ; AX = relative row number
mul BytesPL ; this clobbers DX
add DI,AX
mov AX,CX ; restore coordinates
xor AH,AH ; AX = relative col number
shl AX,1 ; times 2 for attribute
add DI,AX
pop DX
pop AX
ret

;
; STORE HEX WORD
; Convert a hex word to ASCII and store it in destination string
; Entry:
; AX = Word to convert
; DI = destination pointer
;
Shexwrd:
xchg AL,AH ; convert upper half first
call Shexbyt
xchg AL,AH ; convert lower half
call Shexbyt
ret

;
; STORE HEX BYTE
; Convert a hex byte to ASCII and store it in destination string
; Entry:
; AL = Byte to convert
; DI = destination pointer
;
Shexbyt:
push AX
mov AH,AL ; save temporarily
shr AL,1
shr AL,1
shr AL,1
shr AL,1
cmp AL,10
jb wh1
add AL,07
wh1:
add AL,'0'
stosb
mov AL,AH
and AL,0fh
cmp AL,10
jb wh2
add AL,07
wh2:
add AL,'0'
stosb
pop AX
ret

;
; STORE DECIMAL BYTE
; Convert a hex byte to ASCII decimal and store it in destination string
; Entry:
; AL = Byte to convert
; DI = destination pointer
;
Sdecbyt:
and AX,00ffh ; mask off upper half
call HextoDec
movsb ; copy 2 chars to destination string
movsb
ret

;
; Check if key pressed requires special handling
;
; Entry:
; AX = key pressed
;
Disp_Key:
mov DI,OFFSET Keytbl ; keys and subroutine addresses
mov CX,LENKTAB / 4 ; number of entries
cmpdk:
cmp AX,[DI] ; is key in table?
jz dkexec
add DI,4 ; no, point to next key value
loop cmpdk
clc ; carry clear means exit upon return
ret
dkexec:
add DI,2
call [WORD PTR DI]
ret

;
; Table of keys to watch for followed by the address of the routine to execute
;
Keytbl dw ESCAPE, DO_Esc
dw BIGS, Do_Skey
dw SMALLS, Do_Skey
dw BIGR, Do_Rkey
dw SMALLR, Do_Rkey
dw UPARROW, Do_Up
dw DOWNARROW, Do_Down
dw LEFTARROW, Do_Left
dw RIGHTARROW, Do_Right
dw HOME, Do_Home
dw XEND, Do_End
dw PGUP, Do_Pgup
dw PGDN, Do_Pgdn
LENKTAB EQU $ - Keytbl

;
; If ESC key, clear flag so Int 13h will not be stopped each time
;
Do_Esc:
and BYTE PTR Step_Flg,255-STOP
clc ; carry clear means exit upon return
ret

;
; 'S' key - skip successive functions of current type.
; Return to intercepting calls when something new comes along.
;
Do_Skey:
mov AL,0ffh
mov Skip_Flg,AL ; set flag to show we should skip something
mov AL,Last_FC ; get current function code
mov Skip_Typ,AL ; save it
clc ; carry clear means exit upon return
ret

;
; 'R' key - stop after INT 13h call and let user see results
;
Do_Rkey:
mov AL,0ffh
mov Ret_Flg,AL ; set flag
clc ; carry clear means exit upon return
ret

;
; DO_Up - move window up one line
;
Do_Up:
mov BX,OFFSET W1
call Close_Window
mov AX,[BX].leftrow
or AX,AX ; already at top of screen?
jz upexit
dec [BX].leftrow
dec [BX].rightrow
call Window_Parms ; recalculate parameters
upexit:
stc ; don't exit upon return
ret

;
; DO_Down - move window down one line
;
Do_Down:
mov BX,OFFSET W1
call Close_Window
mov AX,[BX].rightrow
cmp AX,24 ; already at bottom of screen?
jz dnexit
inc [BX].leftrow
inc [BX].rightrow
call Window_Parms ; recalculate parameters
dnexit:
stc ; don't exit upon return
ret

;
; DO_Left - move window left one column
;
Do_Left:
mov BX,OFFSET W1
call Close_Window
mov AX,[BX].leftcol
or AX,AX ; already at left of screen?
jz lfexit
dec [BX].leftcol
dec [BX].rightcol
call Window_Parms ; recalculate parameters
lfexit:
stc ; don't exit upon return
ret

;
; Do_Right - move window right one column
;
Do_Right:
mov BX,OFFSET W1
call Close_Window
mov AX,[BX].rightcol
cmp AX,79 ; already at right of screen?
jz rtexit
inc [BX].leftcol
inc [BX].rightcol
call Window_Parms ; recalculate parameters
rtexit:
stc ; don't exit upon return
ret

;
; Do_Home - move window to upper left corner
;
Do_Home:
mov BX,OFFSET W1
call Close_Window
xor AX,AX
mov [BX].leftrow,AX
mov [BX].leftcol,AX
;
mov AX,[BX].leftrow
add AX,[BX].height
dec AX
mov [BX].rightrow,AX
;
mov AX,[BX].leftcol
add AX,[BX].xwidth
dec AX
mov [BX].rightcol,AX
;
call Window_Parms ; recalculate parameters
stc ; don't exit upon return
ret

;
; Do_End - move window to lower left corner
;
Do_End:
mov BX,OFFSET W1
call Close_Window
mov AX,24
mov [BX].rightrow,AX
xor AX,AX
mov [BX].leftcol,AX
;
mov AX,[BX].rightrow
sub AX,[BX].height
inc AX
mov [BX].leftrow,AX
;
mov AX,[BX].leftcol
add AX,[BX].xwidth
dec AX
mov [BX].rightcol,AX
;
call Window_Parms ; recalculate parameters
stc ; don't exit upon return
ret

;
; Do_Pgup - move window to upper right corner
;
Do_Pgup:
mov BX,OFFSET W1
call Close_Window
xor AX,AX
mov [BX].leftrow,AX
mov AX,79
mov [BX].rightcol,AX
;
mov AX,[BX].leftrow
add AX,[BX].height
dec AX
mov [BX].rightrow,AX
;
mov AX,[BX].rightcol
sub AX,[BX].xwidth
inc AX
mov [BX].leftcol,AX
;
call Window_Parms ; recalculate parameters
stc ; don't exit upon return
ret

;
; Do_Pgdn - move window to lower right corner
;
Do_Pgdn:
mov BX,OFFSET W1
call Close_Window
mov AX,24
mov [BX].rightrow,AX
mov AX,79
mov [BX].rightcol,AX
;
mov AX,[BX].rightrow
sub AX,[BX].height
inc AX
mov [BX].leftrow,AX
;
mov AX,[BX].rightcol
sub AX,[BX].xwidth
inc AX
mov [BX].leftcol,AX
;
call Window_Parms ; recalculate parameters
stc ; don't exit upon return
ret

;
; Wait for any key pressed
;
Get_Key:
push BP ; just in case
mov AH,00
int 16h
pop BP
ret

;
; Calculate window parameters:
; 1) Physical starting address of window
; 2) Width of window in characters
; 3) Height of window
;
; Entry:
; BX = window pointer
; Exit:
; AX = Number of bytes contained in window
;
Window_Parms:
mov AX,[BX].leftrow
mul BytesPL ; multiply by bytes per line
mov CX,[BX].leftcol
shl CX,1 ; times 2 for attribute byte
add AX,CX
mov SI,AX ; starting physical address of window
mov [BX].startmem,AX ; save it for later
;
; Calculate size of window in bytes to allocate memory
;
mov AX,[BX].rightcol
inc AX
sub AX,[BX].leftcol
mov [BX].xwidth,AX
mov CX,[BX].rightrow
inc CX
sub CX,[BX].leftrow
mov [BX].height,CX
mul CL ; AX = nbr of bytes of screen area
shl AX,1 ; times 2 to get attributes also
ret

;
; Table of function codes.
; (1st) word is DOS function code (AH),
; (2nd) word is pointer to string to display,
; (3rd) word is subroutine address to handle display of this type function.
;
FCtbl:
dw 0000h, STR00, Disp_Str
dw 0100h, STR01, Disp_Str
dw 0200h, STR02, Disp_02
dw 0300h, STR03, Disp_03
dw 0400h, STR04, Disp_04
dw 0500h, STR05, Disp_05
dw 0600h, STRUN, Disp_Str
dw 0700h, STRUN, Disp_Str
dw 0800h, STR08, Disp_Str
dw 0900h, STR09, Disp_Str
dw 0a00h, STR0a, Disp_0a
dw 0b00h, STR0b, Disp_0b
dw 0c00h, STR0c, Disp_0c
dw 0d00h, STR0d, Disp_0d
dw 0e00h, STRUN, Disp_Str
dw 0f00h, STRUN, Disp_Str
dw 1000h, STR10, Disp_10
dw 1100h, STR11, Disp_11
dw 1200h, STRUN, Disp_Str
dw 1300h, STRUN, Disp_Str
dw 1400h, STR14, Disp_Str
dw 1500h, STR15, Disp_15
dw 1600h, STR16, Disp_Str
dw 1700h, STR17, Disp_17
LENFC EQU $ - FCtbl
;
;
STR00 db 'Reset Disk System'
db 0

STR01 db 'Get Disk Status'
db 0

STR02 db 'Read XX Disk Sector(s)'
db 0

STR03 db 'Write XX Disk Sector(s)'
db 0

STR04 db 'Verify XX Disk Sector(s)'
db 0

STR05 db 'Format Disk Track'
db 0

STR08 db 'Get Current Drive Parameters'
db 0

STR09 db 'Initialize Fixed-Disk Base Tables'
db 0

STR0a db 'Read Long'
db 0

STR0b db 'Write Long'
db 0

STR0c db 'Seek to Cylinder'
db 0

STR0d db 'Alternate Disk Reset'
db 0

STR10 db 'Test For Drive Ready'
db 0

STR11 db 'Recalibrate Drive'
db 0

STR14 db 'Controller Diagnostics'
db 0

STR15 db 'Get Disk Type'
db 0

STR16 db 'Change of Disk Status'
db 0

STR17 db 'Set Disk Type to XXH'
db 0

STR170 db 'No Disk'
db 0

STR171 db 'Regular Diskette'
db 0

STR173 db 'High Capacity Diskette (1.2 MB)'
db 0

STRUN db '** Undefined Function **'
db 0

;
; Define format of WINDOW structure
;
Window STRUC
leftrow dw ?
leftcol dw ?
rightrow dw ?
rightcol dw ?
xwidth dw ?
height dw ?
startmem dw ?
Window ENDS

;
; Allocate and initialize WINDOW parameters
;
W1 Window <0, 42, 13, 79, 0, 0, 0>

Winseg dw 0000 ; store segment of allocated memory
; for saving screen data
Scrseg dw 0000 ; base segment for screen memory
Pageseg dw 0000 ; segment after page offset factored in
BytesPL dw 80*2 ; number of bytes per line (char + attr)
Ret_Flg db 00 ; 'stop on return' flag
SegEnv dw 0000 ; segment of environment
SegPSP dw 0000 ; segment of PSP at load time
SegEND dw 0000 ; program segment size
Step_Flg db 00 ; step flag
; stop and display if bit 0 = 1
; terminate STEP13 if bit 1 = 1
;
Skip_Typ db 00 ; type of function call to skip temporarily
Skip_Flg db 00 ; set if function should be skipped

Save13 dw ? ; save original Int13 vector here, IP
dw ? ; and SEG

Save28 dw ? ; save original Int28 vector here, IP
dw ? ; and SEG

Save1c dw ? ; save original Int1c vector here, IP
dw ? ; and SEG

Ssave LABEL DWORD
SPsave dw ? ; save some of the user's registers here
SSsave dw ? ; others will be on local stack

AXsave dw ? ; these will have been left on user's
DSsave dw ? ; stack instead of local stack
CSsave dw ?
IPsave dw ?
FLsave dw ?
Last_FC db ? ; save 'AH' as last function code
;
INITmsg db CR
db LF
db '*** STEP13 Version 1.0 Installed ***'
db CR
db LF
db LF
db ' Press BOTH shift keys down to enable STEP13.'
db CR
db LF
db ' Press CONTROL and BOTH shift keys down to remove STEP13 '
db 'from memory.'
db CR
db LF
db '$'

ERRmsg db CR
db LF
db 'STEP13 already installed'
db '$'

FCStr db 'Int 13H - Function XXH'
db 0

DrvStr db 'Drive: XXH'
db 0

HedStr db 'Head: XX'
db 0

TrkStr db 'Track: XXX'
db 0

SecStr db 'Sector: XX'
db 0

CylStr db 'Cyl: XXX'
db 0

BufStr db 'Buffer Address = XXXX:XXXXH'
db 0

RCStr db 'Return Code = XXXXH'
db 0

CFStr db 'Carry Flag = X'
db 0

ZFStr db 'Zero Flag = X'
db 0

HlpStr1 db 'kip Current Func'
db 0

HlpStr2 db 'eturn Code'
db 0

HlpStr3 db ' - Non Stop'
db 0

HlpStr4 db ' - Move Window'
db 0

HlpStr5 db 'Press Any Key'
db 0

HlpStr6 db 'To Continue'
db 0

Astr db 32 dup (0) ; string for converting ASCII characters

;
; Local Program Stack Area
;
db 128 dup (?)
Pstack EQU $


IF TESTING
db 128 dup (?) ; Test Stack Area
Tstack EQU $

Buffer db 512 dup (?)
ENDIF


PRSIZE EQU $

Code ENDS

END Main