# Key-scanning macro in NASM Win16 against DOSBOX (Intel 8086)

Supports backspacing, but not wrapped backspacing.


%macro newline 0.nolist
mov     ax, 0E0Dh
int     10h
mov     al, 0Ah
int     10h
%endmacro

%macro getc 0.nolist
%%get:
mov     ah, 11h
int     16h        ; get keystroke status
jnz     %%pressed
hlt
jmp     %%get
%%pressed:
mov     ah, 10h
int     16h        ; get the pressed key on "modern" keyboards
%endmacro

; %1 = string to write into
%macro scan 1
pushaw
mov     di, %1     ; prep for stosb
xor     cx, cx
%%get:
getc
cmp     al, 13
je      %%done     ; enter pressed
cmp     al, 8
je      %%bspace
cmp     al, ' '
jb      %%get      ; ignore most non-printing characters
cmp     al, '~'
ja      %%get

push    bp
mov     ah, 0Eh    ; print the valid character
int     10h
push    bp

stosb              ; write the character to the string
inc     cx
cmp     cx, 80     ; ensure the buffer isn't exceeded
jae     near %%done
jmp     near %%get ; room for more
%%bspace:
cmp     cx, 0
je      %%get      ; ignore starting bspace

push    bp
mov     ax, 0E08h
int     10h        ; bspace twice, to clear space
mov     al, 32
int     10h
mov     al, 8
int     10h
pop     bp

dec     di         ; overwrite character position
dec     cx         ; step back a character
jmp     %%get
%%done:
xor     ax, ax
stosb              ; append null-terminating character (zero)
newline
popaw
%endmacro

• There is no such thing as "NASM Win16 syntax". This is only using 86-DOS / ROM-BIOS interfaces. – ecm Dec 13 '19 at 17:12

It's fine to write this scan macro for educational purposes, but I think that considering its length a subroutine would have been more appropriate. The macro's bytes are inserted everywhere the macro is invoked. That adds up! Having that many duplicated bytes in a program is a waste of space.

## Some observations

push    bp
mov     ah, 0Eh    ; print the valid character
int     10h
push    bp
stosb              ; write the character to the string


That second push bp was probably meant to be pop bp, wasn't it?
But why would you want to preserve the BP register when the BIOS.Teletype function doesn't even touch it.

User @ecm commented about the BIOS Teletype function: BUG: If the write causes the screen to scroll, BP is destroyed by BIOSes for which AH=06h destroys BP
If this is the bug that made you decide to preserve the BP register, then I would say that preserving BP is still not the right thing to do. Because your code only supports non wrapped backspacing, it is reasonable to assume that no scrolling will occur. If scrolling were to happen then the backspacing user could get confused! Furthermore it's easy to make sure that scrolling can't occur simply by setting the cursor at the left edge of the screen at the start.

For maximum safety, I would write the char from AL in the string before teletyping it. On some implementations of BIOS/DOS etc. , I've seen the AX register modified even when the official documentation told it was preserved.

stosb              ; write the character to the string
mov     bh, 0      ; DisplayPage
mov     ah, 0Eh    ; print the valid character
int     10h


I've added the required BIOS parameter for the DisplayPage. In all of your posts you keep ignoring this parameter. I'm wondering if you have any documentation about these BIOS video functions that possitively states that there are no selectable display pages.

%%bspace:
cmp     cx, 0
je      %%get      ; ignore starting bspace


The cmp cx, 0 instruction has a 3-byte encoding, but the test cx, cx instruction (that does the same) has but a 2-byte encoding. In any macro you will want to write the shortest code.

%%bspace:
test    cx, cx
jz      %%get      ; ignore starting bspace


There's also the jcxz instruction that you could use here. Again shorter but some people will say it is slow. It's up to you to decide if that is of real importance here. Don't forget that the user at the keyboard is much, much slower than any of the lesser instructions that you could choose!

%%bspace:
jcxz    %%get      ; ignore starting bspace


cmp     cx, 80     ; ensure the buffer isn't exceeded
jae     near %%done
jmp     near %%get ; room for more


The code becomes a little more efficient if you write the jump that is more likely first. The buffer will nearly never fill up completely, and so the jump to %%done will almost never happen.

cmp     cx, 80     ; ensure the buffer isn't exceeded
jb      near %%get ; room for more
jmp     near %%done


## A quick rewrite

; %1 = string to write into
%macro scan 1
pushaw
mov     di, %1     ; prep for stosb
xor     cx, cx
mov     bh, 0      ; DisplayPage
%%get:
getc
cmp     al, 13
je      %%done     ; enter pressed
cmp     al, 8
je      %%bspace
cmp     al, ' '
jb      %%get      ; ignore most non-printing characters
cmp     al, '~'
ja      %%get

stosb              ; write the character to the string
mov     ah, 0Eh    ; print the valid character
int     10h
inc     cx
cmp     cx, 80     ; ensure the buffer isn't exceeded
jb      near %%get ; room for more
jmp     near %%done
%%bspace:
jcxz    %%get      ; ignore starting bspace
mov     ax, 0E08h
int     10h        ; bspace twice, to clear space
mov     al, 32
int     10h
mov     al, 8
int     10h
dec     di         ; overwrite character position
dec     cx         ; step back a character
jmp     %%get

%%done:
xor     ax, ax
stosb              ; append null-terminating character (zero)
newline
popaw
%endmacro

• RBIL has this to say for interrupt 10h function 0Eh: "BUG: If the write causes the screen to scroll, BP is destroyed by BIOSes for which AH=06h destroys BP" – ecm Dec 13 '19 at 17:10
• I usually do mov bx, 7 in my other posts, so bh is still 0 there. – T145 Dec 13 '19 at 18:44
• @ecm I've added some reasoning about the Teletype bug in my answer. Additionally I think we can't let paranoia take over. There can always be bugs everywhere and it's highly impractical to preserve everything all the time. After decades of programming I have never encountered any of the bugs that Ralf Brown talks about. I'm sure they exist, but I've never come accross one. – Sep Roland Dec 14 '19 at 11:49