I'm trying to learn some deep programming through assembler x86 in DOS, as it enables real adress mode. But in my attempt to do this im trying to make a program that prints out wether the user is has pressed one of the control keys; CTRL, CAPS LOCK or SCROLL LOCK but the problem is that the program dont print out. It feels like there is som lack of some kind of fundamental knowledge so I ask if someone here maybe knows whats wrong with my program. It doesn't write out anything but is able to shut down if I press q (as in quit)?.. Thanks
;reads the key and prints out whether a control key has been entered
; (CTRL, CAPS LOCK or SCROLL LOCK)
[BITS 16]
SEGMENT data
ctrlmsg db 'ctrl has been pressed', '$'
capslockmsg db 'caps lock has been pressed', '$'
scrollmsg db 'scroll lock has been pressed', '$'
SEGMENT code
..start:
mov ax, pile
mov ss, ax
mov ax, topofstack
mov sp, ax
mov ax, data
mov ds, ax
mov ax, ctrlmsg
WAITER:
mov ah, 00h
int 16h
cmp al, 71h ; user pressed q, and wants to end program
je END
mov ah, 02h ; wait until user press keyboard and return keyboard flag in AL
int 16h
add al, 71h ; add 71h back to al, so it goes unchanged in other comparisons
cmp al, 02h ; if key board flag is 02h then I expect user to have pressed CTRL
je CTRL ; then jump to CTRL label, in the same way it goes...
add al, 02h
cmp al, 04h
je SCROLLOCK
add al, 04h
cmp al, 06h
je CAPSLOCK
jmp WAITER
END:
mov ax, 04c00h ; ends program
int 21h
WRITESTRING:
mov ah, 13h ; 13h of int 10 is print string
mov al, 00h ; write mode 0 (no attributes)
mov bh, 0h ; video page number
mov bl, 07h ; foreground color
mov cx, 05h ; string length
mov dh, 25 ; row
mov dl, 80 ; col
int 10h
ret
CTRL: ; the other labels CAPS LOCK and SCROLL LOCK are quite similar why I haven't included them in the codesnippet
push ds ; save ds for subroutine
pop es ; pop it in es
push bp
move bp, ctrlmsg ; base pointer point to string
call WRITESTRING
pop bs
jmp waiter ; loop
Greg is right. You shouldn't have to load ss:sp. I know it shows that in the example in the Manual, but dos does it for you... if your stack is declared correctly. You don't show that. Should be something like:
segment pile stack ; the "stack" is important!
resw 100h ; should be enough for anybody
You do have to load ds as you've done. I'd do es too, at this time, rather than leave it until "CTRL:". Your "Writestring" depends on it. (weird interrupt, 10h/13h!)
int 16h, ah=0 is correct for old 83-key keyboards. It'll get "almost" all keystrokes, but it misses a few. I've confused myself quite badly on that one! (long time ago, but I still remember it!) Use ah=10h for modern keyboards. Either way, you check for 'q' and exit. So far so good!
Then you get flags with int 16h/2. Your logic after that eludes me! You're adding 71h to keyboard flags? And then comparing it to 2? I don't think any of your conditional jumps are being taken. That would explain why nothing is being printed! :) (you're also printing to line 25, column 80, which seems strange)
I think you want to test flags with the... (wait for it) "test" instruction. Since more than one of these conditions could be true, maybe something like...
test al, 4 ; bit 2 - control key
jnz notctrl
call print_control
notctrl:
test al, 16 ; bit 4 - scrollock active
jnz not_scrl
call print_scroll
notscrl:
test al, 64 ; bit 6 - capslock active
; etc...
You'll have to preserve al in your subroutines, obviously. Greg is also right that, if you were writing your own int 9 (irq 1) handler for the keyboard, you'd save this information in a buffer. The existing handler does exactly that. In the Bios Data Area, segment 40h... I forget the offset... you'll find these flags. That's where int 16h/2 finds 'em, I'm pretty sure. You could access them there yourself, but the BIOS interrupt might be easier (maybe try it as an exercise?).
Last but not least, wtf is "pop bs"? BS is not a segment register. Nasm won't take no BS! The CPU won't take no BS! :)
Best, Frank