bits 16 cpu 8086 ;;; MS-DOS PSP locations arglen: equ 80h argstart: equ 82h ;;; MS-DOS system calls getch: equ 1h putch: equ 2h puts: equ 9h time: equ 2Ch section .text org 100h ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Read board size from command line cmp byte [arglen],0 ; Command line empty? je printusage ; Then print usage and stop mov al,[argstart] ; Get first byte on command line cmp al,'3' ; <3? jb printusage ; Then print usage and stop cmp al,'8' ; >8? ja printusage ; Then print usage and stop sub al,'0' ; Make number from ASCII mov [boardsz],al ; Store the board size mov ah,puts ; If we made it here, print the mov dx,welcome ; welcome banner. int 21h ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Generate random board call xabcinit ; Initialize the RNG with system time mov si,board xor bx,bx genboard: call xabcrand ; Get random byte mov ah,al call xabcrand ; Get another and ax,0101h ; Keep only the low bit of each byte mov [si+bx],ax ; And store them inc bx ; Two bytes onward inc bx cmp bl,64 ; Are we done? jne genboard ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Copy the board into the goal mov si,board ; Source is board mov di,goal ; Destination is goal mov bx,0 ; Start at the beginning copyboard: mov ax,[si+bx] ; Load word from board mov [di+bx],ax ; Store word in goal inc bx ; We've copied two bytes inc bx cmp bl,64 ; Are we done yet? jne copyboard ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Make an amount of random flips call xabcrand ; Random number and al,15 ; [0..15] add al,5 ; [5..20] mov [sysflips],al ; Store in memory mov cl,al ; Flip doesn't touch CL xor ch,ch ; Set high byte zero randflips: call xabcrand ; Random number call flip ; Do a flip (unused bits are ignored) loop randflips mov byte [usrflips],0 ; Initialize user flips to 0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Print game status (moves, goal moves) status: mov ah,puts mov dx,smoves int 21h mov al,[usrflips] call printal mov ah,puts mov dx,sgoal int 21h mov al,[sysflips] call printal ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Print the game board and goal board mov ah,puts ; Print the header mov dx,sboardhdr int 21h mov cl,2 ; Print two headers mov ah,putch ; Indent columns ;;; Print column headers colheader: mov dl,' ' int 21h int 21h mov bh,0 ; Offset mov bl,'A' ; First letter .curcol: cmp bh,[boardsz] mov dl,bl ; Print letter jb .printcol mov dl,' ' ; Print space if column not used .printcol: call dlspace ; Print DL + separator space inc bl inc bh cmp bh,8 ; Done yet? jb .curcol mov dl,9 ; Separate the boards with a TAB int 21h dec cl ; We need two headers (board and goal) jnz colheader mov ah,puts ; Print a newline afterwards mov dx,newline int 21h ;;; Print the rows of the boards xor bh,bh ; Zero high byte of BX xor cl,cl ; Row index boardrow: mov ch,2 ; Two rows, board and goal mov si,board ; Start by printing the game board .oneboard: xor dh,dh ; Column index mov dl,cl ; Print row number add dl,'1' call dlspace .curpos: cmp dh,[boardsz] ; Column in use? mov dl,' ' ; If not, print a space jae .printpos mov bl,cl ; Row index shl bl,1 ; * 8 shl bl,1 shl bl,1 add bl,dh ; Add column index mov dl,[bx+si] ; Get position from board add dl,'0' ; Print as ASCII 0 or 1 .printpos: call dlspace inc dh ; Increment column index cmp dh,8 ; Are we there yet? jb .curpos ; If not, print next position mov dl,9 ; Separate the boards with a TAB int 21h dec ch ; Have we printed the goal yet? mov si,goal ; If not, print the goal next jnz .oneboard mov ah,puts ; Print a newline mov dx,newline int 21h inc cl ; Next row cmp cl,[boardsz] ; Are we there yet? jb boardrow ; If not, print the next row. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Ask the user for a move mov ah,puts ; Write the prompt mov dx,prompt int 21h readmove: mov ah,getch ; Read a character int 21h or al,32 ; Make letters lowercase cmp al,'q' ; Quit? je quit cmp al,'9' ; Numeric (row) input? jbe .nummove ; If not, alphabetic (column) input ;;; Letter input (column) sub al,'a' ; Subtract 'a' jc invalid ; If it was <'a', invalid input cmp al,[boardsz] ; Is it on the board? jae invalid or al,128 ; Set high bit, for column flip call flip ; Flip the column jmp checkwin ; See if the user has won ;;; Number input (row) .nummove: sub al,'1' ; Rows start at 1. jc invalid ; If <'1', then invalid cmp al,[boardsz] ; Is it on the board? jae invalid call flip ; Flip the row ;;; Check if the user has won the game checkwin: xor bh,bh ; Zero high byte of array index mov dl,[boardsz] xor ch,ch ; Row coordinate .row: xor cl,cl ; Column coordinate .pos: mov bl,ch ; BL = row*8 + col shl bl,1 shl bl,1 shl bl,1 add bl,cl mov al,[board+bx] ; Get position from board cmp al,[goal+bx] ; Compare with corresponding goal pos jne .nowin ; Not equal: the user hasn't won inc cl cmp cl,dl ; Done all positions on row? jb .pos ; If not, do next. inc ch cmp ch,dl ; Done all rows? jb .row ; If not, do next. ;;; If we get here, the user has won mov ah,puts mov dx,win int 21h ret ;;; The user hasn't won .nowin: inc byte [usrflips] ; Record that the user has made a move jmp status ; Print status and board, get new move ;;; Invalid input invalid: mov ah,puts mov dx,nope int 21h jmp readmove ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Flip the line or column in AL. If bit 7 set, flip column. flip: mov si,board ; SI = board test al,al ; This sets sign flag if bit 7 set js .column ; If so, flip column. and al,7 ; We only need the first 3 bits shl al,1 ; Multiply by 8 shl al,1 ; 8086 does not support 'shl al,3' shl al,1 xor ah,ah ; High byte 0 mov bx,ax ; BX = offset mov ah,4 ; 4 words .rowloop xor word [si+bx],0101h ; Flip two bytes at once inc bx inc bx dec ah jnz .rowloop ret .column: and al,7 ; Flip a column. xor ah,ah mov bx,ax ; BX = row offset mov ah,8 ; 8 bytes (need to do it byte by byte) .colloop: xor byte [si+bx],01h ; Flip position add bx,8 dec ah jnz .colloop quit: ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Print usage and stop printusage: mov ah,puts mov dx,usage int 21h ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Print the character in DL followed by a space dlspace: mov ah,putch int 21h mov dl,' ' int 21h ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Initialize the random number generator using the system ;;; time. xabcinit: mov ah,time int 21h mov bx,xabcdat xor [bx],cx xor [bx+2],dx ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; The "X ABC" random number generator ;;; Return random byte in AL xabcrand: push bx ; Save registers push cx push dx mov bx,xabcdat ; RNG state pointer mov cx,[bx] ; CL=x CH=a mov dx,[bx+2] ; DL=b DH=c inc cl ; X++ xor ch,dh ; A^=C xor ch,cl ; A^=X add dl,ch ; B+=A mov al,dl shr al,1 ; B>>1 xor al,ch ; ^A add al,dh ; +C mov dh,al ; ->C mov [bx],cx ; Store new RNG state mov [bx+2],dx pop dx ; Restore registers pop cx pop bx ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Print the byte in AL as a decimal number printal: mov di,decnum + 3 ; Room in memory for decimal number mov dl,10 ; Divide by 10 .loop: xor ah,ah ; Zero remainder div dl ; Divide by 10 add ah,'0' ; Add ASCII 0 to remainder dec di mov [di],ah ; Store digit in memory and al,al ; Done yet? jnz .loop ; If not, next digit mov dx,di ; DX = number string mov ah,puts ; Print the string int 21h ret section .data decnum: db '***$' ; Decimal number placeholder usage: db 'Usage: FLIP [3..8], number is board size.$' welcome: db '*** FLIP THE BITS *** ',13,10 db '--------------------- ', newline: db 13,10,'$' smoves: db 13,10,13,10,'Your flips: $' sgoal: db 9,'Goal: $' sboardhdr: db 13,10,10,'--Board------------' db 9,'--Goal-------------',13,10,'$' prompt: db 13,10,'Press line or column to flip, or Q to quit: $' nope: db 8,32,8,7,'$' ; Beep and erase input win: db 13,10,7,7,7,'You win!$' section .bss boardsz: resb 1 ; Board size sysflips: resb 1 ; Amount of flips that the system does usrflips: resb 1 ; Amount of flips that the user does xabcdat: resb 4 ; Four byte RNG state board: resb 64 ; 8*8 board goal: resb 64 ; 8*8 goal