;[]-------------------------------------------------------------------------[]
;|	Program name: 3_3_3.ASM                                              |
;|	Image   file: 3_3_3.COM                                              |
;|	Note        : A protection broken TSR program.                       |
;|                    It is combined 3_2_6.ASM with 3_3_2.ASM.               |
;|                    It can specify sector data file and register data file |
;|                      to store or load on any disk.                        |
;|                    ***** All rights reserved! *****                       |
;[]-------------------------------------------------------------------------[]


cseg	segment
	assume	cs:cseg,ds:cseg
	org	100H
start:
	jmp	initialize

;------- Data area ---------------------

old_int05	dd	?
old_int13	dd	?
old_int21	dd	?
old_int60	dd	?
old_int61	dd	?
in_dos_seg	dw	?
in_dos_off	dw	?
busy_05		db	0

N_save		db	2
dummy_read	db	0
load_count	dw	0
count		dw	0
reg_buf		dw	800H dup (0FFFFH)
reg_file	db	' :REGISTER.DAT',0
sec_file	db	' :A_SECTOR.DAT',0
fake_file	db	' :A_SECTOR.DAT',0
handle		dw	0
name_ptr	dw      name_buf
name_buf	db	1000H dup (0),0FFH
reg_title	db	' AX    BX    CX    DX   '
		db	' AX    BX    CX    DX   '
		db	'FLAG  BYTE  NAME'
		db	0DH,0AH,0

;------- Define area -------------------

BLOCK		EQU	11
AX_1		EQU	[di+00]
BX_1		EQU	[di+02]
CX_1		EQU	[di+04]
DX_1		EQU	[di+06]
AX_2		EQU	[di+08]
BX_2		EQU	[di+10]
CX_2		EQU	[di+12]
DX_2		EQU	[di+14]
FLAG_2		EQU	[di+16]
BYTES		EQU	[di+18]
NAMES		EQU	[di+20]

;------- Include area ------------------

DEBUG	EQU	0
include	register.inc
include	interrup.inc
include	display.inc
include	file.inc
include	debug.inc

;------- INT 05H -----------------------

new_int05	proc	far

	cmp	cs:busy_05,1		; Test for INT 05H busy
	je	i05_done		; If busy then branch

	sti				; Enable interrupt
	mov	cs:busy_05,1		; Set INT 05H busy
	call	dis_buffer		; Display register buffer
	call	dis_name		; Display name buffer
	mov	cs:busy_05,0		; Reset INT 05H idle

i05_done:
	iret				; Return calling program

new_int05	endp

;---------------------------------------
; Display buffer that saved register values
;---------------------------------------

dis_buffer	proc	near

	pushall				; Save all the register values

	mov	ax,cs			; Adjust DS, ES
	mov	ds,ax
	mov	es,ax

	call    dreturn			; Display return
	dstring	reg_title		; Display title

	mov	bx,offset reg_buf	; Get buffer start address
	mov	cx,0			; Let CX to be a counter

;---------------------------------------
; In the following routine, AX, BX, CX, DX are used for:
;	AX: displaying data
;	BX: point to buffer which to be displayed
;	CX: total displayed data, used for comparing with count
;	DX: control display format
;	    ( one block per line & insert one blank line per two blocks)
;---------------------------------------

n3:	mov	dx,0			; Reset DX counting
n2:	mov	ax,[bx]			; Get data
	call	dsword			; Display it
	dstr	<' ',' '>		; Display '  '
	add	bx,2			; BX point to next data
	add	dx,1			; Increase DX
	add	cx,2			; Increase CX
	cmp	dx,BLOCK		; Does one block have been displayed ?
	jne	n1			; If not, check another
	call	dreturn			; If yes, display return
	jmp	n2			; and go on displaying
n1:	cmp	dx,BLOCK * 2		; Does two blocks have been displayed ?
	jne	n2			; If not, go on displaying
	call	dreturn			; If yes, display return
	call	dreturn			; and insert one blank line
	cmp	cx,count		; Does all data have been displayed?
	jbe	n3			; If not, go on displaying

	popall				; Restore all the register values

	ret				; Return

dis_buffer	endp

;---------------------------------------
; Display name_buf
;---------------------------------------

dis_name	proc	near

	pushall				; Save all the register values

	mov	ax,cs			; Adjust DS, ES
	mov	ds,ax
	mov	es,ax

	dstring	name_buf		; Use dstring macro to display name_buf

	popall				; Restore all the register values

	ret				; Return

dis_name	endp

;------- INT 13H -----------------------

new_int13	proc	far

	sti				; Enable interrupt

	pushf				; Save FLAG value
	call	in_dos			; Check in_dos
	jne	i13_1			; It is in dos, so branch

	cmp     ah,00H			; Check for reseting disk
	je	i13_1			; If true, then branch

	cmp	dl,00H			; Check for accessing disk A
	je	i13_2			; If true, go to save routine.

i13_1:	popf				; Restore FLAG value
	cli				; Disable interrupt
	jmp	cs:old_int13		; Jump to Old INT 13H routine

i13_2:	popf				; Restore FLAG value
	call	record_reg1		; Record register value
	call	transfer_data		; Transfer data
	call	record_reg2		; Record register value
	ret	2			; Discard FLAG value in stack
new_int13	endp

;---------------------------------------
; Check IN DOS subroutine.
; If INT 21H is idle, then set ZR flag, else reset to NR flag.
;---------------------------------------

in_dos		proc	near
	pushr	<bx,es>			; Save register
	mov	es,cs:in_dos_seg	; Let ES:BX point to in_dos byte
	mov	bx,cs:in_dos_off	;
	cmp	byte ptr es:[bx],0	; Check this byte (if idle then set ZR)
	popr	<es,bx>			; Restore register
	ret
in_dos		endp

;---------------------------------------
; Record register value before accessing disk.
;---------------------------------------

record_reg1	proc	near

	pushall				; Save all the register values

	mov	ax,cs			; Adjust DS, ES
	mov	ds,ax
	mov	es,ax

	cmp	count,0F00H		; Check for buffer remainder size
	ja	record1_done		; Not enough, don't record

	mov	di,offset reg_buf	; Get buffer start address
	add	di,count		; Adjust di with count

	get_ax				; Get AX from stack
	mov	AX_1,ax			; Put AX to buffer
	get_bx				; Get BX from stack
	mov	BX_1,ax			; Put BX to buffer
	get_cx				; Get CX from stack
	mov	CX_1,ax			; Put CX to buffer
	get_dx				; Get DX from stack
	mov	DX_1,ax			; Put DX to buffer

record1_done:

	popall				; Restore all the register value

	ret				; Return

record_reg1	endp

;---------------------------------------
; Transfer data by using Old INT 13H
;---------------------------------------

transfer_data	proc	near

	cmp	cs:dummy_read,1		; If dummy_read is set to 1
	je	t1			; then read sector from file

	cli				; else read sector from disk
	pushf
	call	cs:old_int13		; Call old_int13 to access disk
	sti
	jmp	transfer_done

;
; In following routine, you can not use <pushall> and <popall> macro.
; Register AX, BX, CX, DX, and FLAG must be set by the values of reg_buf.
;
t1:
	pushr	<di,ds>			; Save DI, DS value

	mov	ax,cs			; Adjust DS
	mov	ds,ax

	mov	di,offset reg_buf	; DI get the reg_buf start address
	add	di,load_count		; Adjust DI with load_count
	mov	ax,AX_1			; Get the requesting AX value
	cmp	ah,02H			; Is it a read request ?
	jne	t2			; If not, then branch
	mov	BX_1,bx			; Set loaded buffer offset
	mov	BX_2,bx			; Set loaded buffer offset
	call	load_sector		; If yes, then load data from file
t2:
	add	load_count,BLOCK * 2	; Adjust load_count with Block size
	mov	ax,load_count		; Get the adjusted load_count value
	cmp	ax,count		; Does it larger then count ?
	jb	t3			; If not, then branch
	mov	load_count,0		; If yes, reset load_count to zero
t3:
	mov	ax,FLAG_2		; Get new FLAG value from reg_buf
	push	ax			; Push it to stack
	popf				; Pop it from stack to FLAG
	mov	ax,AX_2			; Get new AX value from reg_buf
	mov	bx,BX_2			; Get new BX value from reg_buf
	mov	cx,CX_2			; Get new CX value from reg_buf
	mov	dx,DX_2			; Get new DX value from reg_buf

	popr	<ds,di>			; Restore DS, DI value

transfer_done:

	ret				; Return

transfer_data	endp

;---------------------------------------
; Record register value after accessing disk.
;---------------------------------------

record_reg2	proc	near

	pushall				; Save all the register values

	cmp	cs:dummy_read,1		; If dummy_read is set to 1
	je	record2_done		; then does't need to record register

	mov	ax,cs			; Adjust DS, ES
	mov	ds,ax
	mov	es,ax

	cmp	count,0F00H		; Check for buffer remainder size
	ja	record2_done		; Not enough, don't record

	mov	di,offset reg_buf	; Get buffer start address
	add	di,count		; Adjust di with count

	get_ax				; Get AX from stack
	mov	AX_2,ax			; Put AX to buffer
	get_bx				; Get BX from stack
	mov	BX_2,ax			; Put BX to buffer
	get_cx				; Get CX from stack
	mov	CX_2,ax			; Put CX to buffer
	get_dx				; Get DX from stack
	mov	DX_2,ax			; Put DX to buffer
	get_f				; Get FLAG from stack
	mov	FLAG_2,ax		; Put FLAG to buffer

	add	count,BLOCK * 2		; Increase count with block size

	mov	ax,AX_1			; Get function call number
	cmp	ah,02H			; Is it a read request ?
	jne	r2_1			; If not, then branch
	call	save_sector		; If yes, then save sector data

r2_1:
	call	save_reg		; Write reg_buf to file 'REGISTER.DAT'

record2_done:

	popall				; Restore all the register values

	ret				; Return

record_reg2	endp

;------- INT 13H Subroutine ------------

;---------------------------------------
; Adjust disk parameter before accessing files from floppy disk.
;---------------------------------------

adjust_N	proc	near

	pushr	<ax,si,di,ds,es>	; Save register values

	mov	ax,0			; Let DS:SI point to disk parameter N
	mov	ds,ax
	mov	si,0525H

	mov	ax,cs			; Let ES:DI point to N_save
	mov	es,ax
	mov	di,offset N_save

	mov	al,es:[di]		; Exchange disk parameter N with N_save
	mov	ah,ds:[si]
	mov	ds:[si],al
	mov	es:[di],ah

	popr	<es,ds,di,si,ax>	; Restore register values

	ret				; Return

adjust_N	endp

;---------------------------------------
; Write sector data to file.
;---------------------------------------
; In this routine, ds:di must point to current block in reg_buf.

save_sector	proc	near

	mov	ax,351EH		; Get INT 1EH interrupt vector
	int	21H			; ES:BX will point to disk parameter
					; after this function call

	mov	cl,es:[bx+3]		; Get disk parameter N value
	mov     ax,AX_1			; Get requesting AX value
	mov	ah,al			; Let AH hold sector number
	xor	al,al			; Let AL to be reset
	shr	ax,1			; Divide AX by 2
	shl	ax,cl			; AX get total bytes to be saved
					; Each sector has 128 * 2 ^ N bytes

	mov	BYTES,ax		; Record saved bytes
	mov	ax,word ptr sec_file[2]	; Get saving file name
	mov	NAMES,ax		; Record saving file name

	call	adjust_N		; Adjust disk parameter N

	h_creat	sec_file,0		; Creat saving file
	jc      s1			; If error, then branch

	push	ds			; Save DS value
	mov	cx,BYTES		; Let CX get the number of saving bytes
	mov	bx,handle		; Let BX get the handle
	get_bx				; In requesting program, ES:BX is
	mov	dx,ax			;   pointing to the buffer that data to
	get_es				;   be loaded, so let DS:DX point to
	mov	ds,ax			;   the buffer now.
	mov	ah,40H			; Let AX get the function call number
	int	21H			; Write to file
	pop	ds			; Restore DS value

	h_close	handle			; Close file

s1:
	call	adjust_N		; Adjust disk parameter N

	inc	sec_file[2]		; Let sec_file to be the next file

	ret				; Return

save_sector	endp

;---------------------------------------
; Load sector data from file.
;---------------------------------------
; In this routine, ds:di must point to current block in reg_buf.

load_sector	proc	near

	mov	ax,NAMES		; Get loading file name
	mov	word ptr fake_file[2],ax; Put it to fake_file

	call	adjust_N		; Adjust disk parameter N

	h_open	fake_file,RDWR		; Open fake_file

	push	ds			; Save DS value
	mov	dx,BX_1			; Let DX get buffer offset
	mov	cx,BYTES		; Let CX get loading bytes number
	mov	bx,handle		; Let BX get handle
	mov	ax,es			; Get buffer segment
	mov	ds,ax			; Put it to DS
	mov	ah,3FH			; Read data
	int	21H			; Execute
	pop	ds			; Restore DS value

	h_close	handle			; Close fake_file

	call	adjust_N		; Adjust disk parameter N

	ret				; Return

load_sector	endp

;---------------------------------------
; Save buffer data to 'REGISTER.DAT'.
;---------------------------------------

save_reg	proc	near

	pushall				; Save all the register values

	mov	ax,cs			; Adjust DS, ES
	mov	ds,ax
	mov	es,ax

	call	adjust_N		; Adjust disk parameter N

	h_creat	reg_file,0		; Creat 'REGISTER.DAT' file
	mov	cx,count		; Get count value
	add	cx,2			; Write count+2 bytes
	h_write	handle,count,cx		; from count to file
	h_close	handle			; Close 'REGISTER.DAT'

	call	adjust_N		; Adjust disk parameter N

	popall				; Restore all the register values

	ret				; Return

save_reg	endp

;---------------------------------------
; Load buffer data from 'REGISTER.DAT'.
;---------------------------------------

load_reg	proc	near

	pushall				; Save all the register values

	mov	ax,cs			; Adjust DS, ES
	mov	ds,ax
	mov	es,ax

	call	adjust_N		; Adjust disk parameter N

	h_open	reg_file,RDWR		; Open 'REGISTER.DAT'
	jc	l1			; If error then branch
	h_read	handle,count,1002H	; Read 1002H bytes to count
	h_close	handle			; Close 'REGISTER.DAT'
	mov	dummy_read,1		; Set dummy_read flag

l1:
	call	adjust_N		; Adjust disk parameter N

	popall				; Restore all the register values

	ret				; Return

load_reg	endp

;------- INT 21H -----------------------

new_int21	proc	far

	pushall				; Save all the register values
	sti

	mov	ax,cs			; Adjust DS, ES
	mov	ds,ax
	mov	es,ax
	get_ax				; Restore AX

	cmp	ah,3DH			; Is it a open file request ?
	je	i21_1			; If yes, record its name

	cmp	ah,4EH			; Is it a find file request ?
	je	i21_1			; If yes, record its name

	popall				; Restore all the register values
	jmp	cs:old_int21		; Jump to execute DOS function call

i21_1:
	call	record_file		; Record ASCII file name
	popall				; Restore all the register values
	call	change_file		; Change file name and execute INT 21H

	retf	2			; Far return and discard old flag value

new_int21	endp

;---------------------------------------
; Record ASCII file name to name_buf.
;---------------------------------------

record_file	proc	near

	mov	di,name_ptr		; Get the pointer of name_buf

	get_ds				; Get old DS value
	mov	ds,ax			; Set DS to old DS value
	get_dx				; Get old DX value
	mov	si,ax			; Set SI to old DX value
rf1:
	cmp	word ptr es:[di],0FFH	; Does the name_buf use up ?
	je	rf3			; If yes, don't record any more
	lodsb				; Let AL get one ASCII character
	cmp	al,0			; Is it the end of the file name ?
	je	rf2			; If yes, put <Return> to name_buf
	stosb				; If not, put it to name_buf
	jmp	rf1			; Repeat this routine again
rf2:
	mov	al,0AH			; Put <Return> to name_buf
	stosb
	mov	al,0DH
	stosb
rf3:
	mov	cs:name_ptr,di		; Let name_ptr hold the pointer

	ret				; Return

record_file	endp

;---------------------------------------
; Change file name beginning with  'A:' .
;---------------------------------------

change_file	proc	near

	push	dx			; Save DX register value
	push	bx			; Save BX register value

	mov	bx,dx			; Let BX hold the index
	cmp	word ptr [bx],':A'	; Check the file name
	je	cf1			; If it begins with 'A:' or 'a:'
	cmp	word ptr [bx],':a'	; then add DX with 2
	je	cf1			;

	jmp	cf2			; Otherwise, don't modify DX

cf1:
	add	dx,2			; Bypass the driver number
cf2:
	pop	bx			; Restore BX register value
					; It must be restored befor calling

	pushf				; Execute Old INT 21H function call
	call	cs:old_int21		;
	sti				; Enable interrupt

	pop	dx			; Restore DX register value

	ret				; Return

change_file	endp

;------- INT 60H -----------------------

new_int60	proc	far

	assume	cs:cseg,ds:nothing	; Override segment

	sti
	pushr	<ax,dx,ds>

	set_int	05H,<word ptr old_int05>,<word ptr old_int05[2]>
	set_int	13H,<word ptr old_int13>,<word ptr old_int13[2]>
	set_int	21H,<word ptr old_int21>,<word ptr old_int21[2]>
	set_int	60H,<word ptr old_int60>,<word ptr old_int60[2]>
	set_int	61H,<word ptr old_int61>,<word ptr old_int61[2]>

	popr	<ds,dx,ax>
	iret

new_int60	endp

;------- Main program ------------------

initialize:

	assume	cs:cseg,ds:cseg

	mov	bx,5CH			; DS:BX point to FCB1
	mov	al,[bx]			; Get parameter of drive
	cmp	al,0			; Test for specifing drive number
	jne	init_1			; If it specified, bypass

	mov	ah,19H			; Otherwise, use DOS function call
	int	21H			; to get current drive number
	add	al,1			; Let 01 represent drive 'A'
					; and 02 represent drive 'B', etc.

init_1:
	add	al,'A'-1		; Adjust drive number to 'ABCD...'
	mov	bx,offset reg_file	; Assign each file with drive number
	mov	[bx],al			;
	mov	bx,offset sec_file	;
	mov	[bx],al			;
	mov	bx,offset fake_file	;
	mov	[bx],al			;

	mov	ah,34H			; Get in_dos byte address
	int	21H
	mov	in_dos_seg,es
	mov	in_dos_off,bx

	call	load_reg		; Load data to reg_buf

	get_int	05H,old_int05
	set_int	05H,<offset new_int05>
	get_int	13H,old_int13
	set_int	13H,<offset new_int13>
	get_int	21H,old_int21
	set_int	21H,<offset new_int21>
	get_int	60H,old_int60
	set_int	60H,<offset new_int60>
	get_int	61H,old_int61
	set_int	61H,<offset new_int61>

	mov	dx,offset initialize	; Terminate and stay resident
	int	27H

cseg	ends
	end	start
