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