作要求:
- Write a TSR program which uses the keyboard interrupt.
一以interrupt作件的TSR(Terminate and Stay Resident)程式。 - When a hot key (using ^ as hot key) is pressed, the TSR program writes your English name and student ID in the middle of the console.
用“^”作的,按下去後在幕上示你的“英文名字和”。 - Note: the TSR program must write your name and ID directly onto the video RAM of the console instead of using INT calls.
提醒:TSR程式必直接字串料“控制面的位址”,而不是使用INT指令。(用了程式掉)



心得
次作的程序流程跟以往在撰的程式不一,保留一部分的程式片段於部,且在行的候行似於安的理程序, DOS 系的interrupt vector table 修改成跳到自己指定的程序片段,因此,所的程序被作中指令被,而在入,示於幕的指令,中入的程序行控制。
於早期的 DOS 有很多 interrupt vector table 的值是可以被修改的,了鼓早期的者在 MS-DOS 上面,因此允使用者行修改,但由於interrupt vector 有碰撞的可能,就生程序相容性的。在的 vector table 是被部分保以防止作系流程不正,也是防止病毒的一方式。
而在理入的 scan key ,可以照以下的址:
http://www.delorie.com/djgpp/doc/rbinter/it/06/0.html
次的程序流程稍微比麻,有判的 AND OR 布林算,了不大幅度修改程式,用展的方式理每一步。
清 push 指令 call proc 取得的念,由於入的是 reg16 因此的是 [bp+4]、[bp+6],又因是 near call,bp return_address(只有 ip)都是 2 bytes,所以第一位置在 [bp+4]。
在音理部分,於以下的址。
http://www.autoitscript.com/forum/topic/40848-beep-music-mario-bros-theme/
Values for keyboard make/break (scan) code: 01h Esc 31h N 02h 1 ! 32h M 03h 2 @ 33h , < 63h F16 04h 3 # 34h . > 64h F17 05h 4 $ 35h / ? 65h F18 06h 5 % 36h Right Shift 66h F19 07h 6 ^ 37h Grey* 67h F20 08h 7 & 38h Alt 68h F21 (Fn) [*] 09h 8 * 39h SpaceBar 69h F22 0Ah 9 ( 3Ah CapsLock 6Ah F23 0Bh 0 ) 3Bh F1 6Bh F24 0Ch - _ 3Ch F2 6Ch -- 0Dh = + 3Dh F3 6Dh EraseEOF 0Eh Backspace 3Eh F4 0Fh Tab 3Fh F5 6Fh Copy/Play 10h Q 40h F6 11h W 41h F7 12h E 42h F8 72h CrSel 13h R 43h F9 73h <delta> [*] 14h T 44h F10 74h ExSel 15h Y 45h NumLock 75h -- 16h U 46h ScrollLock 76h Clear 17h I 47h Home 77h [Note2] Joyst But1 18h O 48h UpArrow 78h [Note2] Joyst But2 19h P 49h PgUp 79h [Note2] Joyst Right 1Ah [ { 4Ah Grey- 7Ah [Note2] Joyst Left 1Bh ] } 4Bh LeftArrow 7Bh [Note2] Joyst Up 1Ch Enter 4Ch Keypad 5 7Ch [Note2] Joyst Down 1Dh Ctrl 4Dh RightArrow 7Dh [Note2] right mouse 1Eh A 4Eh Grey+ 7Eh [Note2] left mouse 1Fh S 4Fh End 20h D 50h DownArrow 21h F 51h PgDn 22h G 52h Ins 23h H 53h Del 24h J 54h SysReq ---non-key codes--- 25h K 55h [Note1] F11 00h kbd buffer full 26h L 56h left \| (102-key) 27h ; : 57h F11 AAh self-test complete 28h ' " 58h F12 E0h prefix code 29h ` ~ 59h [Note1] F15 E1h prefix code 2Ah Left Shift 5Ah PA1 EEh ECHO 2Bh \ | 5Bh F13 (LWin) F0h prefix code (key break) 2Ch Z 5Ch F14 (RWin) FAh ACK 2Dh X 5Dh F15 (Menu) FCh diag failure (MF-kbd) 2Eh C FDh diag failure (AT-kbd) 2Fh V FEh RESEND 30h B FFh kbd error/buffer full
程式明
基本上都是由 TSR.asm 主下去修改的,必了解 segment at 的指令是抓的位置。於 combination 的入合,取 keyboard flag 利用 bit mask 字抓出判。
而在理出的候,特要注意的位址,如果有加指定的 segment address,用正在行的程序的 cs, ds, ss,由於程序用到另一 segment address 入,因此的候要特注意。
由於 mov 指令有 mem, mem 的格式,因此需要多一部分去理。在示息的部分,定座的 base 是左上角,了不 counter 生的情去做理。而在的 RAM 中,是用 byte 去表示一格的示(印出字元+示性),因此每次印出的移是 2 bytes 位。
使用 wasd 上下控制一方向示 (^<)=)
字型撰 beep function, delay 理上不方便,有 int 指令去直接算差,如果要求是以秒位,可以用系去判,但如果在其以下 ms 位法表示,藉此使用一些 nop 去拖延。
TITLE TSR homework6 (TSR.asm)
; Write a TSR program which uses the keyboard interrupt.
; When a hot key (using ^ as hot key) is pressed, the TSR
; program writes your English name and student ID in the
; middle of the console.
; Note: the TSR program must write your name and ID directly
; onto the video RAM of the console instead of using INT calls.
intno EQU 09h ; keyboard interrupt number
center EQU (80*13+40)*2 ; screen 80x25, two bytes for each cell
leftup EQU (80*0+0)*2 ; left up side
six_key EQU 07h ; scan code for 6 key
w_key EQU 11h ; scan code for w key
a_key EQU 1eh ; scan code for a key
s_key EQU 1fh ; scan code for s key
d_key EQU 20h ; scan code for d key
m_key EQU 32h ; scan code for m key
rt_shift EQU 01h ; right shift key: bit 0
lt_shift EQU 02h ; left shift key: bit 1
VRamText SEGMENT at 0b800h ; screen ram memory
VRamText ENDS
codeseg SEGMENT PARA
assume cs:codeseg, ds:codeseg, es:VRamText
ORG 100h ; this is a .com program. must be before main
start: ; start ENDS
jmp setup ; jump to TSR installation
; data block
lcounter dw 0 ; pressed counter
scounter dw 0 ; shift counter
ncounter dw 0 ; shift next counter
shiftimg db '>' ; shift image character
old_interrupt dd ? ; DWORD(32bit) es:ip
msg db 'Morris_100502205', '$'
msglen equ $-msg-1
musichz WORD 2600, 796, 796, 796, 1686, 1592, 1592, 1592, 3373, 3183
WORD 3373, 3183, 3183, 6366, 1686, 1592, 1592, 1686, 1592, 1592
WORD 1686, 14857, 1418, 1502, 1418, 1263, 1418, 1592, 1787, 1686
WORD 1592, 1592, 1686, 1592, 1592, 1686, 1592, 1418, 1502, 1418
WORD 1263, 1, 1126, 1062, 843, 796, 1, 1686, 1592, 1592
WORD 1686, 1592, 1592, 1686, 1592, 1418, 1502, 1418, 1263, 1418
WORD 1592, 1787, 1893, 1787, 1592, 1418, 1592, 1787, 1893, 2125
WORD 1893, 1787, 1592, 1787, 1893, 2125, 2385, 2125, 1893, 1787
WORD 1893, 2125, 2527, 2385, 1, 3573, 3183, 3785, 2385, 2527
WORD 2677, 2836, 2527, 2385, 1418, 2527, 1418, 709, 2836, 3183
WORD 2836, 2527, 1592, 2836, 1592, 796, 3183, 3573, 3183, 2836
WORD 1787, 3005, 1787, 893, 3573, 3785, 4011, 3785, 1893, 1787
WORD 1592, 2836, 2527, 2385, 1418, 2527, 1418, 709, 2836, 3183
WORD 2836, 2527, 1592, 2836, 1592, 796, 3183, 3573, 3183, 2836
WORD 1787, 1893, 1787, 1686, 1592, 3183, 3183, 3183, 3183, 6366
WORD 6366, 6366, 6745, 6366, 6745, 6366, 6009, 5672, 5354, 5053
musicde WORD 200, 200, 200, 200, 200, 200, 200, 200, 200, 200
WORD 200, 200, 400, 400, 200, 200, 200, 200, 200, 200
WORD 200, 200, 200, 200, 200, 400, 200, 200, 200, 200
WORD 200, 200, 200, 200, 200, 200, 200, 200, 200, 200
WORD 400, 200, 10, 200, 10, 200, 200, 200, 200, 200
WORD 200, 200, 200, 200, 200, 200, 200, 200, 400, 200
WORD 200, 200, 200, 200, 200, 400, 200, 200, 200, 200
WORD 200, 200, 400, 200, 200, 200, 200, 200, 200, 400
WORD 200, 200, 200, 200, 400, 400, 200, 200, 200, 200
WORD 200, 200, 200, 200, 200, 200, 200, 200, 200, 200
WORD 200, 200, 200, 200, 200, 200, 200, 200, 200, 200
WORD 200, 200, 200, 200, 200, 200, 200, 200, 200, 400
WORD 400, 200, 200, 200, 200, 200, 200, 200, 200, 200
WORD 200, 200, 200, 200, 200, 200, 200, 200, 200, 200
WORD 200, 200, 200, 200, 200, 200, 200, 200, 200, 200
WORD 200, 200, 200, 200, 200, 200, 200, 200, 200, 200
; memory-resident code begins here
int_handler:
pushf ; save flags
push ax ; save regs
push dx
push es
push cx
push di
push si
; point ES:DI to the DOS keyboard flag byte:
mov ax, 40h ; DOS data segment is at 40h
mov es, ax
mov di, 17h ; location of keyboard flag
mov ah, es:[di] ; copy keyboard flag into AH
; test for the LEFT_SHIFT and RIGHT_SHIFT keys:
test ah, rt_shift ; right shift key held down??
jnz L1 ; yes: process
test ah, lt_shift ; left shift key held down?
jnz L1 ; yes: process
jmp byPass ; no LEFT_SHIFT or RIGHT_SHIFT: exit
; test if hot-key(^) pressed by (left/right)shift+6
L1: in al, 60h ; 60h keyboard input port
cmp al, six_key ; 6 key pressed?
jnz byPass &bsp; ; no: exit
; point ES:DI to the DOS screen RAM, DS:SI to the string address
; clear last time print
sub lcounter, 2*msglen ; make clear using
mov ax, cs
mov ds, ax
mov ax, VRamText
mov es, ax
mov ax, center
add ax, lcounter
mov di, ax ; screen buffer offset
lea si, msg ; string address offset
; place the message into the video text buffer
mov cx, msglen
CL1: ; run write into screen buffer
mov al, ' ' ; get character from string[si]
mov BYTE PTR es:[di], al ; write into buffer[di]
inc di
inc di ; skip over the color attribute byte
inc si
add lcounter, 2
loop CL1
sub lcounter, 2*msglen
add lcounter, 2
mov ax, cs
mov ds, ax
mov ax, VRamText
mov es, ax
mov ax, center
add ax, lcounter
mov di, ax ; screen buffer offset
lea si, msg ; string address offset
; place the message into the video text buffer
mov cx, msglen
DP1: ; run write into screen buffer
mov al, BYTE PTR [si] ; get character from string[si]
mov BYTE PTR es:[di], al ; write into buffer[di]
inc di
inc di ; skip over the color attribute byte
inc si
add lcounter, 2
loop DP1
jmp byPassEnd
byPass: ; ignore unneeded input
; check w, a, s, d input
in al, 60h ; 60h keyboard input port
cmp al, w_key ; w key pressed?
jz wshift ; yes: wshift
cmp al, a_key ; a key pressed?
jz ashift ; yes: ashift
cmp al, s_key ; s key pressed?
jz sshift ; yes: sshift
cmp al, d_key ; d key pressed?
jz dshift ; yes: dshift
jmp byPassEnd
wshift:
mov ax, scounter
mov ncounter, ax
sub ncounter, 80
sub ncounter, 80
mov shiftimg, '^'
jmp shiftpro
ashift:
mov ax, scounter
mov ncounter, ax
dec ncounter
dec ncounter
mov shiftimg, '<'
jmp shiftpro
sshift:
mov ax, scounter
mov ncounter, ax
add ncounter, 80
add ncounter, 80
mov shiftimg, '='
jmp shiftpro
dshift:
mov ax, scounter
mov ncounter, ax
add ncounter, 1
add ncounter, 1
mov shiftimg, '>'
jmp shiftpro
; point ES:DI to the DOS screen RAM, DS:SI to the string address
shiftpro:
; clear last time print
mov ax, cs
mov ds, ax
mov ax, VRamText
mov es, ax
mov ax, leftup
add ax, scounter
mov di, ax ; screen buffer offset
lea si, shiftimg ; string address offset
; place the message into the video text buffer
mov cx, 1
CSL1: ; run write into screen buffer
mov al, ' ' ; get character from string[si]
mov BYTE PTR es:[di], al ; write into buffer[di]
inc di
inc di ; skip over the color attribute byte
inc si
add scounter, 2
loop CSL1
sub scounter, 2
;
mov ax, ncounter
mov scounter, ax
mov ax, cs
mov ds, ax
mov ax, VRamText
mov es, ax
mov ax, leftup
add ax, scounter
mov di, ax ; screen buffer offset
lea si, shiftimg ; string address offset
; place the message into the video text buffer
mov cx, 1
SL1: ; run write into screen buffer
mov al, BYTE PTR [si] ; get character from string[si]
mov BYTE PTR es:[di], al ; write into buffer[di]
inc di
inc di ; skip over the color attribute byte
inc si
add scounter, 2
loop SL1
sub scounter, 2
byPassEnd:
in al, 60h ; 60h keyboard input port
cmp al, m_key ; m key pressed?
jnz byPassEnd2 ; no: exit
; music play
mov ax, cs
mov ds, ax
mov cx, 160 ; music size
lea di, musichz
lea si, musicde
loops:
mov ax, [di]
push ax
mov ax, [si]
push ax
call beep
inc di
inc di
inc si
inc si
mov ax, 1 ; make delay between music note
push ax
mov ax, 200
push ax
call beep
loop loops
byPassEnd2:
pop si ; restore regs
pop di
pop cx
pop es
pop dx
pop ax
popf ; restore flags
sti ; set interrupt flag
jmp cs:old_interrupt ; jump to INT routine
; jmp cs:[old_interrupt] is the same, and by this method far call
align dword ; because memory cell bound, DWORD will be faster.
beep PROC ; push hz[bp+6], push delay[bp+4]
push bp
mov bp, sp
push cx
push ax
mov al, 10110110b ; out control byte
out 43h, al ; to port 43h
mov ax, [bp+6] ; Frequency = 1.19 MHz/ ???hz
out 42h,al ; out low-byte of frequency divider
mov al, ah
out 42h, al ; out high-byte of frequence divider
; on-off-on
in al, 61h
or al, 00000011b ;on bit0 and bit 1
out 61h, al
and al, 11111100b ; off bit0 and bit 1
out 61h, al ; of port 61h
or al, 00000011b ;on bit0 and bit 1
out 61h, al
; delay ??? ms
mov cx, [bp+4] ; get delay parameter
delay_loop:
push cx
mov cx, 300h ; this value will response different computer clock rate.
loop2:
and ax, ax ; nop
loop loop2
pop cx
loop delay_loop
; close
in al, 61h
and al, 11111100b ; off bit0 and bit 1
out 61h, al ; of port 61h
pop ax
pop cx
pop bp
ret 4 ; clear stack 2+2 bytes
beep ENDP
end_ISR label byte
setup:
; mov ax, cs ; not necessary for .com program
; mov ds, ax ; code segment already correct.
mov ah, 35h
mov al, intno ; get INT 9 vector
int 21h
mov WORD PTR old_interrupt, bx ; save INT 9 vector
mov WORD PTR old_interrupt+2, es
cli ; clear interrupt flag, disables external interrupts
mov ah, 25h ; set interrupt vector, INT 9 with ds:dx
mov al, intno ;
mov dx, OFFSET int_handler
int 21h
sti ; set interrupt flag
mov ah, 09h ; write a $-terminated string to standard output
mov dx, OFFSET msg
int 21h ; only to show something.
mov ax, 3100h ; terminate and stay resodent
mov dx, OFFSET end_ISR ; point to end of resident code
int 21h ; dx must be the program size in bytes. execute MS-DOS function
codeseg ENDS
END start
文章定位: