; vim: filetype=nasm ; ; hangman.asm: Boot sector hangman by xlq ; ; To run: ; nasm -o hangman hangman.asm ; qemu -fda hangman ; ; Or copy it onto the first sector of a real floppy. org 0x7C00 stack equ 0x7B80 ; stack (grows downwards, of course) lives equ 0x7B80 ; place to store number of lives target_word equ 0x7B82 ; place to store target word max_target_len equ 60 ; maximum length of target word guessed equ 0x7BC0 ; bitmap - one bit per ASCII char 0..127 ; set if guessed cli ; set the stack pointer xor ax,ax mov ss,ax mov ds,ax mov es,ax mov sp,stack cld say_hello: ; print welcome message mov bh,2 .hello_msg: mov bl,5 .equals_loop: mov al,'=' call putc dec bl jnz .equals_loop dec bh jz .end mov si,.message call puts jmp .hello_msg .end: mov si,newline call puts jmp read_guess .message: db " Hangman! ",0 newline: db 13,10,0 backspace: db 8,32,8,0 read_guess: mov si,.message call puts mov di,target_word ; read keyboard presses until enter is pressed .loop: xor ah,ah int 0x16 ; now al=character cmp al,8 ; backspace? je .backspace cmp al,13 ; enter? je .enter cmp di,(target_word+max_target_len) je .loop and al,0x7F mov [di],al inc di call putc jmp .loop .backspace: cmp di,target_word je .loop dec di mov si,backspace call puts jmp .loop .enter: mov [di],byte 0 ; null-terminate string call clear_line ; clear line to hide target mov [lives],word 10 ; reset lives counter ; clear guessed bitmap mov di,guessed mov cx,16 xor al,al rep stosb mov [guessed+4],byte 1 ; space always revealed jmp guess .message: db "Target: ",0 .message_end: guess: mov si,.word_str call puts xor bp,bp mov ax,bp mov cx,bp .print_word_loop: ; has this character been revealed? mov al,[target_word+bp] test al,al jz .end call split8 mov bl,[guessed+bx] test bl,dl jz .not_revealed .is_revealed: call putc inc bp jmp .print_word_loop .not_revealed: inc cl mov al,'-' call putc inc bp jmp .print_word_loop .end: mov si,.lives_str call puts mov ax,[lives] call putint test cl,cl ; neither putint nor puts clobbers cx jz you_win mov si,.guess_str call puts ; read a guess character from the keyboard xor ah,ah int 0x16 and al,0x7F ; print it :) call putc ; already guessed? call split8 mov cl,[guessed+bx] test cl,dl jnz .already_guessed or cl,dl mov [guessed+bx],cl ; deduct a life if this char is not in the word mov si,target_word mov dl,al .loop: lodsb test al,al jz .fail cmp al,dl je .end2 jmp .loop .fail: ; dec life counter dec word [lives] jz you_die .already_guessed: .end2: call up_line call up_line jmp guess .word_str: db "Word: ",0 .lives_str: db 13,10,"Lives: ",0 .guess_str: db 13,10,"Guess: ",0 you_die: mov si,die_str call puts jmp start_again die_str: db 13,10,"You die.",13,10,0 you_win: mov si,win_str call puts start_again: ; wait for any key xor ah,ah int 0x16 ; clear 5 lines mov cl,5 .loop: call up_line loop .loop ; start again jmp say_hello win_str: db 13,10,13,10,"You survive.",13,10,0 split8: ; IN: al=value ; OUT: bx=value/8 dx=1<<(value%8) ; all other registers unchanged pusha mov bp,sp xor bx,bx mov bl,al shr bx,3 mov [bp+8],bx ; set bx in pusha structure mov cl,al and cl,7 xor dx,dx inc dx shl dx,cl mov [bp+10],dx ; set dx in pusha structure popa ret up_line: ; move up to the previous line ; and clear it! pusha mov ah,3 xor bh,bh int 0x10 ; dh=row, dl=col test dh,dh jz .nodec dec dh .nodec: xor dl,dl mov ah,2 xor bh,bh pusha int 0x10 ; print 80 spaces mov cx,80 mov al,32 .loop: call putc loop .loop popa ; stacktastic! int 0x10 popa ret clear_line: ; print 80 backspaces, since printing backspaces ; doesn't un-line-wrap mov bl,80 .loop: mov si,backspace call puts dec bl jnz .loop ret puts: ; si=string pointer lodsb test al,al jz .end call putc jmp puts .end: ret putc: ; al=character pusha xor bx,bx mov ah,14 int 0x10 popa ret putint: ; ax=int pusha mov bp,sp push word 0 mov cx,10 mov si,sp .loop: xor dx,dx ; simplify! div cx add dl,'0' dec si mov [si],dl test ax,ax jnz .loop mov sp,si call puts mov sp,bp popa ret times 510-($-$$) nop dw 0xAA55