I have a 16-bit assembly program (NASM) that changes to VGA graphics mode and tries to fill the entire screen with a solid color, but i can only fill up to 32767 (0x7fff) pixels (instead of the full 320x200 display which is 64000 (0xfa00) pixels), any more and the all the white pixels disappear for some reason. I'm assuming it's some sort of signed increment, but I'm not too sure since I'm still learning a bit. Otherwise, the pixels can show up and get drawn to the screen (i use QEMU)
init.asm:
org 0h
bits 16
%define ENDL 0dh, 0ah
jmp _entry
_entry:
push msg_hello
call puts
jmp entry
entry:
call toggle_vga
push 0fh
push 07fffh
call fill_screen
jmp endp
toggle_vga:
push ax
mov ah, 0fh
mov al, 0h
int 10h
cmp al, 13h
je .toggle_vga_off
jne .toggle_vga_on
.toggle_vga_on:
mov ah, 00h
mov al, 13h
int 10h
jmp .toggle_vga_end
.toggle_vga_off:
mov ah, 00h
mov al, 03h
int 10h
jmp .toggle_vga_end
.toggle_vga_end:
pop ax
ret
fill_screen:
push bp
mov bp, sp
push es
push ax
push dx
push cx
push di
mov ax, 0a000h
mov es, ax
mov dl, [bp + 6]
mov cx, [bp + 4]
mov di, 0
.fill_screen_loop:
cmp di, cx
jge .fill_screen_done
mov [es:di], dx
inc di
jmp .fill_screen_loop
.fill_screen_done:
pop di
pop cx
pop dx
pop ax
pop es
pop bp
ret
puts:
push bp
mov bp, sp
push bx
push ax
mov bx, [bp + 4]
.puts_loop:
cmp byte [bx], 0
je .puts_done
mov ah, 0eh
mov al, [bx]
int 10h
inc bx
jmp .puts_loop
.puts_done:
pop ax
pop bx
pop bp
ret
endp:
cli
hlt
msg_hello: db 'init: loaded successfully.', ENDL, 0
jge .fill_screen_done
jumps based on the inequality between signed values. For purposes of signed two's-complement arithmetic, 0xfa00
is the number -1536. The values 0 through 0x7fff are positive, and so while di
is in this range, we do have di >= cx
and so the loop continues. But then you increment to 0x8000
, which in signed arithmetic is -32768, and the condition is no longer true.
You want to use jae .fill_screen_done
which jumps based on the inequality between unsigned values. (There's more info in this answer; I seem to recall an even better one somewhere but I can't find it now.) Or even just je .fill_screen_done
; you increment di
by one on each step, so there is no possibility of it becoming "above" before being equal.
Another small bug is mov [es:di], dx
; this stores a word (two bytes) since dx
is a 16-bit register. But you only want to store one byte from dl
(you have left dh
uninitialized), so make it mov [es:di], dl
.
You could make this more efficient by duplicating the byte into both halves of dx
(mov dh, dl
), then storing a word at a time (mov [es:di], dx
) and incrementing di
by two (add di, 2
, or inc di
twice). Make sure that cx
is always an even number.
Better still would be to use rep stosw
, which I'll let you research on your own.