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"