303 lines
8.7 KiB
Text
303 lines
8.7 KiB
Text
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
|