hello-dosdl/dosdl.asm

452 lines
6.0 KiB
NASM

bits 16
cpu 8086
org 0x100
section .code
select_target:
; TODO: Implement target word selection
; https://github.com/lynn/hello-wordl/blob/7da40c1f067eb1ec157d4c5b7a9bd8257ed39342/src/util.ts#L14
mov si, 1397
shl si, 1
shl si, 1
add si, targets
mov di, target
call unpack_target
read_guess:
; Number of thus far guessed letters stored in bx
xor bx, bx
.loop:
mov ah, 8
int 0x21
cmp al, 8
je .backspace
cmp bx, 5
je .full
cmp al, 'A'
jb .loop
cmp al, 'Z'
jbe .letter
cmp al, 'a'
jb .loop
cmp al, 'z'
jbe .letter
jmp .loop
.full:
cmp al, 13
je .done
jmp .loop
.letter:
; Lowercase
or al, 32
mov byte [guess + bx], al
inc bx
; Echo to screen
mov dl, al
mov ah, 2
int 0x21
jmp .loop
.backspace:
test bx, bx
jz .loop
; Rub out the letter
; PC-DOS 1.1 documentation says only 8 is needed, but DOSBox
; seems buggy here
mov ah, 2
mov dl, 8
int 0x21
mov dl, ' '
int 0x21
mov dl, 8
int 0x21
dec bx
jmp .loop
.done:
check_dictionary:
; Dictionary is split by initial letter
xor bx, bx
mov bl, [guess]
sub bl, 'a'
; Entry in the table is 4 bytes
shl bx, 1
shl bx, 1
mov si, [dictionaries + bx] ; Start address
mov cx, [dictionaries + bx + 2] ; Number of letters
.dictionary_loop:
push cx
mov di, dictionary_entry
; Entry is stored 5 bits per letter (first letter is implicit)
; 3332_2222 5444_4433 0000_5555
; Load 5444_4433_3332_2222 into dx and extract 3 first letters
mov dx, [si]
add si, 2
mov cl, 5
%rep 3
mov al, dl
and al, 0x1f
add al, 'a'
stosb
shr dx, cl
%endrep
; Load 0000_5555 into al, shift one over, or with dl which has
; 0000_0005
mov al, [si]
inc si
shl al, 1
or al, dl
add al, 'a'
stosb
sub di, 4
mov bp, 1
mov cx, 4
.compare_loop:
mov al, [di]
inc di
cmp al, [guess + bp]
jne .not_match
inc bp
loop .compare_loop
pop cx
jmp word_found
.not_match:
pop cx
loop .dictionary_loop
check_targets:
mov cx, targets_len
mov si, targets
.targets_loop:
mov di, dictionary_entry
call unpack_target
xor bp, bp
.compare_loop:
cmp bp, 5
je .match
mov al, [di + bp]
cmp al, [guess + bp]
jne .not_match
inc bp
jmp .compare_loop
.match:
jmp word_found
.not_match:
loop .targets_loop
word_not_found:
mov ah, 9
mov dx, not_found_str
int 0x21
; Wait for a keypress
mov ah, 8
int 0x21
; Clear line
mov ah, 2
mov dl, 13
int 0x21
mov ah, 9
mov dx, erase_word_str
int 0x21
mov si, not_found_str
.clear_loop:
lodsb
cmp al, '$'
je .done
mov ah, 2
mov dl, ' '
int 0x21
jmp .clear_loop
.done:
mov ah, 2
mov dl, 13
int 0x21
jmp read_guess
word_found:
find_exact_hits:
mov si, guess
mov di, exact_hits
mov cx, 5
.loop:
lodsb
cmp al, [si - guess + target - 1]
je .hit
; Zero out if not the right letter
xor al, al
.hit:
stosb
loop .loop
find_wrong_places:
; Zero out first
mov di, wrong_places
mov cx, 5
xor al, al
rep stosb
xor bp, bp
.loop:
cmp bp, 5
je .done
mov al, [exact_hits + bp]
inc bp
test al, al
jnz .loop
; What was the guessed letter?
mov dl, [guess + bp - 1]
; How many times does it appear in the target?
xor bh, bh
mov cx, 5
mov si, target
.count_target:
lodsb
cmp al, dl
jne .not_found_in_target
inc bh
.not_found_in_target:
loop .count_target
; How many times does it appear in the feedback already?
xor bl, bl
mov cx, 5
mov si, exact_hits
.count_exact_hits:
lodsb
cmp al, dl
jne .not_found_in_exact_hits
inc bl
.not_found_in_exact_hits:
loop .count_exact_hits
mov cx, 5
mov si, wrong_places
.count_wrong_places:
lodsb
cmp al, dl
jne .not_found_in_wrong_places
inc bl
.not_found_in_wrong_places:
loop .count_wrong_places
; If in target more than in feedback → wrong place
cmp bh, bl ; target <= feedback
jbe .loop
mov byte [wrong_places + bp - 1], dl
jmp .loop
.done:
print_feedback:
call newline
mov cx, 5
xor bp, bp
xor bx, bx ; How many exact hits?
.loop:
mov al, [exact_hits + bp]
test al, al
jnz .exact_hit
mov al, [wrong_places + bp]
test al, al
jnz .wrong_place
mov dl, 'x'
jmp .print
.exact_hit:
mov dl, ' '
inc bx
jmp .print
.wrong_place:
mov dl, '^'
.print:
mov ah, 2
int 0x21
inc bp
loop .loop
call newline
is_finished:
inc byte [guesses]
cmp bx, 5
je victory
cmp byte [guesses], 6
je loss
jmp read_guess
loss:
mov dx, word_was_str
mov ah, 9
int 0x21
mov cx, 5
mov si, target
.loop:
lodsb
mov dl, al
mov ah, 2
int 0x21
loop .loop
jmp exit
victory:
mov dx, correct_in_str
mov ah, 9
int 0x21
mov dl, [guesses]
add dl, '0'
mov ah, 2
int 0x21
mov dx, guesses_str
mov ah, 9
int 0x21
exit:
ret
newline:
push ax
push dx
mov ah, 2
mov dl, 13
int 0x21
mov dl, 10
int 0x21
pop dx
pop ax
ret
; in:
; si = packed target word
; di = 5 byte buffer for unpacking the target
; out:
; si = first byte after the packed target word
unpack_target:
push ax
push cx
push dx
push di
; Target word is stored packed 5 bits per letter into 32 bits
; 2221_1111 4333_3322 5555_4444 0000_0005
; Load 4333_3322_2221_1111 into dx and extracts first three letters
mov dx, [si]
add si, 2
mov cl, 5
%rep 3
mov al, dl
and al, 0x1f
add al, 'a'
stosb
shr dx, cl
%endrep
; Load 0000_0005_5555_4444 into ax, shift over by one, combine into dx
; which has 0000_0000_0000_0004, and extract last two letters
mov ax, [si]
add si, 2
shl ax, 1
or dx, ax
%rep 2
mov al, dl
and al, 0x1f
add al, 'a'
stosb
shr dx, cl
%endrep
pop di
pop dx
pop cx
pop ax
ret
section .data
target times 5 db 0
guess times 5 db 0
exact_hits times 5 db 0
wrong_places times 5 db 0
guesses db 0
dictionary_entry times 5 db 0
section .rodata
not_found_str db ' - word not found$'
erase_word_str db ' $'
word_was_str db 'The word was: $'
correct_in_str db 'Correct in $'
guesses_str db ' guesses.$'
%include "dictionary.inc"
%include "targets.inc"