409 lines
5.4 KiB
NASM
409 lines
5.4 KiB
NASM
bits 16
|
|
cpu 8086
|
|
|
|
org 0x100
|
|
|
|
section .code
|
|
|
|
select_target:
|
|
; TODO: Implement target word selection
|
|
mov si, 1
|
|
|
|
extract_target:
|
|
shl si, 1
|
|
shl si, 1
|
|
add si, targets
|
|
|
|
mov di, target
|
|
|
|
; 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
|
|
|
|
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:
|
|
; TODO: Check whether word is in the list of target words
|
|
|
|
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
|
|
|
|
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 4 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"
|