You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1123 lines
17 KiB
1123 lines
17 KiB
bits 16 |
|
cpu 8086 |
|
|
|
org 0x100 |
|
|
|
args_length equ 0x80 |
|
args_area equ 0x81 |
|
|
|
section .code |
|
|
|
parse_arguments: |
|
xor ch, ch |
|
mov cl, [args_length] |
|
mov si, args_area |
|
|
|
; Skip leading spaces |
|
.skip_leading_space: |
|
test cx, cx |
|
jz .leading_spaces_skipped |
|
cmp byte [si], ' ' |
|
jne .leading_spaces_skipped |
|
dec cx |
|
inc si |
|
jmp .skip_leading_space |
|
|
|
.leading_spaces_skipped: |
|
|
|
; See whether we have /h, /u, /l, or /? |
|
cmp cx, 2 |
|
jb .not_mode_option |
|
cmp byte [si], '/' |
|
jne .not_mode_option |
|
|
|
cmp byte [si + 1], 'h' |
|
je .hard_mode |
|
cmp byte [si + 1], 'H' |
|
jne .not_hard_mode |
|
|
|
.hard_mode: |
|
mov byte [hard_mode], 1 |
|
add si, 2 |
|
sub cx, 2 |
|
jmp .after_option_space |
|
|
|
.not_hard_mode: |
|
cmp byte [si + 1], 'u' |
|
je .ultra_hard_mode |
|
cmp byte [si + 1], 'U' |
|
jne .not_ultra_hard_mode |
|
|
|
.ultra_hard_mode: |
|
mov byte [hard_mode], 1 |
|
mov byte [ultra_hard_mode], 1 |
|
add si, 2 |
|
sub cx, 2 |
|
|
|
.after_option_space: |
|
test cx, cx |
|
jz .option_done |
|
cmp byte [si], ' ' |
|
jne .option_done |
|
dec cx |
|
inc si |
|
jmp .after_option_space |
|
|
|
.option_done: |
|
|
|
.not_ultra_hard_mode: |
|
cmp byte [si + 1], 'l' |
|
je print_license |
|
cmp byte [si + 1], 'L' |
|
je print_license |
|
|
|
cmp byte [si + 2], '?' |
|
je print_help |
|
|
|
.not_mode_option: |
|
|
|
test cx, cx |
|
jz seed_rng_date |
|
|
|
seed_rng_argument: |
|
xor ax, ax |
|
xor dx, dx |
|
mov bp, mull32 |
|
call store32 |
|
|
|
.loop: |
|
xor dx, dx |
|
mov ax, 10 |
|
mov bp, mulr32 |
|
call store32 |
|
call mul32 |
|
mov bp, addl32 |
|
call store32 |
|
|
|
lodsb |
|
cmp al, '0' |
|
jb print_help |
|
cmp al, '9' |
|
ja print_help |
|
|
|
sub al, '0' |
|
|
|
xor ah, ah |
|
xor dx, dx |
|
mov bp, addr32 |
|
call store32 |
|
|
|
call add32 |
|
mov bp, mull32 |
|
call store32 |
|
|
|
loop .loop |
|
|
|
mov bp, rng_seed |
|
call store32 |
|
jmp select_target |
|
|
|
print_help: |
|
mov ah, 9 |
|
mov dx, help_str |
|
int 0x21 |
|
|
|
ret |
|
|
|
print_license: |
|
mov ah, 9 |
|
mov dx, license_str |
|
int 0x21 |
|
|
|
ret |
|
|
|
seed_rng_date: |
|
; Get date |
|
mov ah, 0x2a |
|
int 0x21 |
|
mov [year], cx |
|
mov [month], dh |
|
%ifdef DEBUG |
|
mov cx, 2022 |
|
mov dh, 2 |
|
mov dl, 24 |
|
%endif |
|
mov [day], dl |
|
|
|
; addl32 = year * 10000 |
|
mov ax, 10000 |
|
mul word [year] |
|
mov bp, addl32 |
|
call store32 |
|
|
|
; addr32 = month * 100 + day |
|
mov ax, 100 |
|
mul word [month] |
|
add ax, [day] |
|
mov bp, addr32 |
|
call store32 |
|
|
|
; rng_seed = addl32 + addr32 = year * 10000 + month * 100 + day |
|
call add32 |
|
mov bp, rng_seed |
|
call store32 |
|
|
|
select_target: |
|
; Call rng to get a number |
|
call rng |
|
mov bp, rng_output |
|
call store32 |
|
|
|
; Multiply rng output by number of target words |
|
xor si, si |
|
xor dx, dx |
|
xor ax, ax |
|
mov cx, targets_len |
|
.multiply_loop: |
|
add ax, [rng_output + 0] |
|
adc dx, [rng_output + 2] |
|
adc si, 0 |
|
loop .multiply_loop |
|
|
|
mov ax, si |
|
%ifdef DEBUG |
|
call hexprint16 |
|
call newline |
|
%endif |
|
|
|
load_target: |
|
shl si, 1 |
|
shl si, 1 |
|
add si, targets |
|
|
|
mov di, target |
|
|
|
call unpack_target |
|
|
|
; Figure out whether this is a removed word |
|
; If so, reroll target word |
|
cmp byte [target], '{' |
|
je select_target |
|
|
|
print_greeting: |
|
mov ah, 9 |
|
mov dx, greeting_str |
|
int 0x21 |
|
|
|
read_guess: |
|
; Number of thus far guessed letters stored in bx |
|
xor bx, bx |
|
|
|
.loop: |
|
mov ah, 8 |
|
int 0x21 |
|
|
|
cmp al, 3 |
|
je .ctrl_c |
|
|
|
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 |
|
|
|
.ctrl_c: |
|
ret |
|
|
|
.done: |
|
|
|
check_hard_mode: |
|
cmp byte [hard_mode], 1 |
|
jne .done |
|
|
|
; See whether exact hits are reused |
|
xor bp, bp |
|
mov cx, 5 |
|
.exact_hits: |
|
mov al, [exact_hits + bp] |
|
test al, al |
|
jz .not_exact_hit |
|
|
|
cmp al, [guess + bp] |
|
je .exact_hit_match |
|
|
|
mov dx, bp |
|
add dl, '1' |
|
mov [exact_hit_error_str.position], dl |
|
mov [exact_hit_error_str.letter], al |
|
mov si, exact_hit_error_str |
|
jmp print_error |
|
|
|
.exact_hit_match: |
|
.not_exact_hit: |
|
inc bp |
|
loop .exact_hits |
|
|
|
; See whether wrongly placed letters are reused |
|
xor bp, bp |
|
.wrong_places: |
|
cmp bp, 5 |
|
je .done |
|
|
|
mov dl, [wrong_places + bp] |
|
test dl, dl |
|
jz .not_wrong_place |
|
|
|
; Count how many times letter is used in exact_hits + wrong_places |
|
xor bx, bx |
|
mov cx, 5 |
|
mov si, exact_hits |
|
.count_exact_hits: |
|
lodsb |
|
cmp al, dl |
|
jne .not_count_exact_hit |
|
inc bh |
|
.not_count_exact_hit: |
|
loop .count_exact_hits |
|
mov cx, 5 |
|
mov si, wrong_places |
|
.count_wrong_places: |
|
lodsb |
|
cmp al, dl |
|
jne .not_count_wrong_places |
|
inc bh |
|
.not_count_wrong_places: |
|
loop .count_wrong_places |
|
|
|
; Count how many times letter is used in guess |
|
mov cx, 5 |
|
mov si, guess |
|
.count_guess: |
|
lodsb |
|
cmp al, dl |
|
jne .not_count_guess |
|
inc bl |
|
.not_count_guess: |
|
loop .count_guess |
|
|
|
cmp bl, bh |
|
jae .used_enough |
|
|
|
mov [wrong_place_error_str.letter], dl |
|
mov ax, bp |
|
add ax, '1' |
|
mov [wrong_place_error_str.position], al |
|
mov si, wrong_place_error_str |
|
jmp print_error |
|
|
|
.used_enough: |
|
.not_wrong_place: |
|
inc bp |
|
jmp .wrong_places |
|
|
|
.done: |
|
|
|
check_ultra_hard_mode: |
|
cmp byte [ultra_hard_mode], 1 |
|
jne .done |
|
|
|
; See whether any ruled-out letters were used |
|
xor bp, bp |
|
mov cx, 5 |
|
.ruled_out: |
|
xor bh, bh |
|
mov bl, [guess + bp] |
|
mov ax, bx |
|
mov al, [alphabet + bx - 'a'] |
|
cmp al, ' ' |
|
jne .not_ruled_out |
|
|
|
mov [ruled_out_error_str.letter], bl |
|
mov ax, bp |
|
add al, '1' |
|
mov [ruled_out_error_str.position], al |
|
mov si, ruled_out_error_str |
|
jmp print_error |
|
|
|
.not_ruled_out: |
|
inc bp |
|
loop .ruled_out |
|
|
|
; See whether wrongly places letters were moved |
|
xor bp, bp |
|
mov cx, 5 |
|
.wrong_places: |
|
mov al, [wrong_places + bp] |
|
test al, al |
|
jz .not_wrong_place |
|
|
|
cmp al, [guess + bp] |
|
jne .moved |
|
|
|
mov [not_moved_error_str.letter], al |
|
mov ax, bp |
|
add ax, '1' |
|
mov [not_moved_error_str.position], al |
|
mov si, not_moved_error_str |
|
jmp print_error |
|
|
|
.moved: |
|
.not_wrong_place: |
|
inc bp |
|
loop .wrong_places |
|
|
|
.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 si, not_found_str |
|
|
|
print_error: |
|
mov ah, 9 |
|
mov dx, si |
|
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 |
|
|
|
.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 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 not used in target at all, remove from list of possible letters |
|
test bh, bh |
|
jnz .used_in_target |
|
|
|
xor dh, dh |
|
mov di, dx |
|
mov byte [alphabet + di - 'a'], ' ' |
|
|
|
.used_in_target: |
|
; 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 |
|
|
|
mov ah, 9 |
|
mov dx, space_dash_space_str |
|
int 0x21 |
|
|
|
mov cx, 26 |
|
mov si, alphabet |
|
.alphabet_loop: |
|
lodsb |
|
mov dl, al |
|
mov ah, 2 |
|
int 0x21 |
|
loop .alphabet_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 |
|
|
|
cmp byte [guesses], 1 |
|
je .singular |
|
|
|
.plural: |
|
mov dx, guesses_str |
|
jmp .print |
|
|
|
.singular: |
|
mov dx, guess_str |
|
|
|
.print: |
|
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 |
|
|
|
; out: |
|
; dx:ax = result |
|
rng: |
|
; https://github.com/lynn/hello-wordl/blob/7da40c1f067eb1ec157d4c5b7a9bd8257ed39342/src/util.ts#L14 |
|
|
|
; rng_t = rng_seed += rng_seed + 0x6d2b79f5 |
|
mov bp, rng_seed |
|
call load32 |
|
add ax, 0x79f5 |
|
adc dx, 0x6d2b |
|
call store32 |
|
mov bp, rng_t |
|
call store32 |
|
|
|
; xorl32 = rng_t |
|
mov bp, xorl32 |
|
call store32 |
|
|
|
; xorr32 = rng_t >> 15 |
|
mov cx, 15 |
|
call rshift32 |
|
mov bp, xorr32 |
|
call store32 |
|
|
|
; mull32 = xorl32 ^ xorr32 = rng_t ^ (rng_t >> 15) |
|
call xor32 |
|
mov bp, mull32 |
|
call store32 |
|
|
|
; mulr32 = rng_t | 1 |
|
mov bp, rng_t |
|
call load32 |
|
or ax, 1 |
|
mov bp, mulr32 |
|
call store32 |
|
|
|
; rng_t = mull32 * mulr32 = (rng_t ^ (rng_t >> 15)) * (rng_t | 1) |
|
call mul32 |
|
mov bp, rng_t |
|
call store32 |
|
|
|
; xorl32 = rng_t |
|
mov bp, xorl32 |
|
call store32 |
|
|
|
; xorr32 = rng_t >> 7 |
|
mov cx, 7 |
|
call rshift32 |
|
mov bp, xorr32 |
|
call store32 |
|
|
|
; mull32 = xorl32 ^ xorr32 = rng_t ^ (rng_t >> 7) |
|
call xor32 |
|
mov bp, mull32 |
|
call store32 |
|
|
|
; mulr32 = rng_t | 61 |
|
mov bp, rng_t |
|
call load32 |
|
or ax, 61 |
|
mov bp, mulr32 |
|
call store32 |
|
|
|
; addl32 = rng_t |
|
mov bp, rng_t |
|
call load32 |
|
mov bp, addl32 |
|
call store32 |
|
|
|
; addr32 = mull32 * mulr32 = (rng_t ^ (rng_t >> 7)) * (rng_t | 61) |
|
call mul32 |
|
mov bp, addr32 |
|
call store32 |
|
|
|
; xorl32 = rng_t |
|
mov bp, rng_t |
|
call load32 |
|
mov bp, xorl32 |
|
call store32 |
|
|
|
; xorr32 = addl32 + addr32 = rng_t + (rng_t ^ (rng_t >> 7)) * (rng_t | 61) |
|
call add32 |
|
mov bp, xorr32 |
|
call store32 |
|
|
|
; rng_t = xorl32 ^ xorr32 = rng_t ^ (rng_t + (rng_t ^ (rng_t >> 7)) * (rng_t | 61)) |
|
call xor32 |
|
mov bp, rng_t |
|
call store32 |
|
|
|
; xorl32 = rng_t |
|
mov bp, xorl32 |
|
call store32 |
|
|
|
; xorr32 = rng_t >> 14 |
|
mov cx, 14 |
|
call rshift32 |
|
mov bp, xorr32 |
|
call store32 |
|
|
|
; result = xorl32 ^ xorr32 = rng_t ^ (rng_t >> 14) |
|
call xor32 |
|
|
|
ret |
|
|
|
; in: |
|
; bp = address of uint32 |
|
; out: |
|
; dx = high 16 bits |
|
; ax = low 16 bits |
|
load32: |
|
mov ax, [bp + 0] |
|
mov dx, [bp + 2] |
|
ret |
|
|
|
; in: |
|
; bp = address of uint32 |
|
; dx = high 16 bits |
|
; ax = low 16 bits |
|
store32: |
|
mov [bp + 0], ax |
|
mov [bp + 2], dx |
|
ret |
|
|
|
; in: |
|
; dx:ax = uint32 to be shifted |
|
; cx = shift amount (must not be 0) |
|
; out: |
|
; dx:ax = result |
|
rshift32: |
|
push cx |
|
.loop: |
|
shr dx, 1 |
|
rcr ax, 1 |
|
loop .loop |
|
pop cx |
|
ret |
|
|
|
; in: |
|
; [xorl32], [xorr32] = operands |
|
; out: |
|
; dx:ax = result |
|
xor32: |
|
mov ax, [xorl32 + 0] |
|
xor ax, [xorr32 + 0] |
|
mov dx, [xorl32 + 2] |
|
xor dx, [xorr32 + 2] |
|
ret |
|
|
|
; in: |
|
; [mull32], [mulr32] = operands |
|
; out: |
|
; dx:ax = result |
|
mul32: |
|
push bx |
|
push cx |
|
; c = 1<<16 |
|
; l = l₁<<16 + l₀ |
|
; r = r₁<<16 + r₀ |
|
; l·r = (l₁<<16 + l₀)·(r₁<<16 + r₀) |
|
; = l₁<<16·r₁<<16 + l₁<<16·r₀ + l₀·r₁<<16 + l₀·r₀ |
|
; = l₁·r₁<<32 + l₁·r₀<<16 + l₀·r₁<<16 + l₀·r₀ ||1<<32 = 0 (mod 1<<32) |
|
; = l₁·r₀<<16 + l₀·r₁<<16 + l₀·r₀ |
|
|
|
; l₀·r₀ |
|
mov ax, [mull32 + 0] |
|
mul word [mulr32 + 0] |
|
mov bx, ax |
|
mov cx, dx |
|
|
|
; l₀·r₁<<16 |
|
mov ax, [mull32 + 0] |
|
mul word [mulr32 + 2] |
|
add cx, ax |
|
|
|
; l₁·r₀<<16 |
|
mov ax, [mull32 + 2] |
|
mul word [mulr32 + 0] |
|
add cx, ax |
|
|
|
mov dx, cx |
|
mov ax, bx |
|
|
|
pop cx |
|
pop bx |
|
|
|
ret |
|
|
|
; in: |
|
; [addl32], [addr32] = operands |
|
; out: |
|
; dx:ax = result |
|
add32: |
|
mov ax, [addl32 + 0] |
|
mov dx, [addl32 + 2] |
|
add ax, [addr32 + 0] |
|
adc dx, [addr32 + 2] |
|
ret |
|
|
|
%ifdef DEBUG |
|
hexprint32: |
|
xchg ax, dx |
|
call hexprint16 |
|
xchg ax, dx |
|
hexprint16: |
|
xchg ah, al |
|
call hexprint8 |
|
xchg ah, al |
|
hexprint8: |
|
%rep 4 |
|
rol al, 1 |
|
%endrep |
|
call hexprint4 |
|
%rep 4 |
|
rol al, 1 |
|
%endrep |
|
hexprint4: |
|
push ax |
|
and al, 0xf |
|
cmp al, 10 |
|
jb .digit09 |
|
|
|
add al, 'a' - 10 - '0' |
|
|
|
.digit09: |
|
add al, '0' |
|
mov ah, 0xe |
|
int 0x10 |
|
|
|
pop ax |
|
ret |
|
%endif |
|
|
|
section .data |
|
alphabet db 'abcdefghijklmnopqrstuvwxyz' |
|
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 |
|
|
|
hard_mode db 0 |
|
ultra_hard_mode db 0 |
|
|
|
rng_seed dd 0 |
|
rng_t dd 0 |
|
|
|
rng_output dd 0 |
|
|
|
xorl32 dd 0 |
|
xorr32 dd 0 |
|
mull32 dd 0 |
|
mulr32 dd 0 |
|
addl32 dd 0 |
|
addr32 dd 0 |
|
|
|
year dw 0 |
|
month dw 0 |
|
day dw 0 |
|
|
|
exact_hit_error_str: |
|
db ' - letter at position ' |
|
.position db '#' |
|
db " must be '" |
|
.letter db '#' |
|
db "'$" |
|
|
|
wrong_place_error_str: |
|
db " - letter '" |
|
.letter db '#' |
|
db "' from position " |
|
.position db '#' |
|
db ' must be reused$' |
|
|
|
not_moved_error_str: |
|
db " - letter '" |
|
.letter db '#' |
|
db "' in position " |
|
.position db '#' |
|
db ' must be moved$' |
|
|
|
ruled_out_error_str: |
|
db " - letter '" |
|
.letter db '#' |
|
db "' in position " |
|
.position db '#' |
|
db ' has been ruled out$' |
|
|
|
section .rodata |
|
|
|
greeting_str db 'Welcome to hello DOSdl. Press Ctrl+C to exit. Run `dosdl /?` for help.', 13, 10, '$' |
|
|
|
space_dash_space_str db ' - $' |
|
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.$' |
|
guess_str db ' guess.$' |
|
|
|
help_str: |
|
db 'Usage: dosdl [/h | /u | /l | /?] [seed]', 13, 10 |
|
db '/h Enable hard mode.', 13, 10 |
|
db '/u Enable ultra hard mode.', 13, 10 |
|
db '/l Display license info.', 13, 10 |
|
db '/? Display this help.', 13, 10 |
|
db 13, 10 |
|
db 'Hello DOSdl is a word guessing game. You have six tries to guess the correct', 13, 10 |
|
db 'English word. After a guess the game displays feedback under each letter:', 13, 10 |
|
db 13, 10 |
|
db "' ' Letter is in the correct place.", 13, 10 |
|
db "'^' Letter is in the wrong place.", 13, 10 |
|
db "'x' Letter is not in used in the word, or its uses are already covered by the", 13, 10 |
|
db ' above cases.', 13, 10 |
|
db 13, 10 |
|
db 'After the feedback, the game shows a list of letters that have not yet been', 13, 10 |
|
db 'ruled out.', 13, 10 |
|
db 13, 10 |
|
db 'In hard mode all letters marked as being in the correct place must stay fixed', 13, 10 |
|
db 'and those marked as being in the wrong place must be reused. In ultra hard mode', 13, 10 |
|
db 'letters marked as being in the wrong place must also be moved and letters that', 13, 10 |
|
db 'have been ruled out must not be played again.$' |
|
|
|
%include "dictionary.inc" |
|
%include "targets.inc" |
|
%include "license.inc"
|
|
|