Add timers to MIPS assembly language concentration game
1.Add a visible turn counter display to the last line of the screen. Update it after each turn.
2.Add a timer to turn cards back face-down after they have been exposed for 2 seconds,
if the user hasn’t clicked on a card. This involves expanding the polling loop to look for timeouts. (or for our graduate students, using callback rouines.)
3.Track the number of pairs matched to determine when the game is over.
Display a Win message.
4.Delete all the cards, when the game is over.
Clear the mouse Map and the shuffle array.
5.Add a timer to show the number of seconds passed since the game started on the bottom of the display.
It should stop when the game is over.
6.Add two “buttons” to the bottom line: <Exit><New Game>. These may also respond to keystrokes (x,n).
This will involve modification to the existing mouse event, and addition of keyboard to the polling loop.
7.For a “knock-your-socks-off” extra-credit add on:
Use a read/write file to track high scores (low times and low turn-counts) for each level of the game.
Display the best 5 scores for each level. Ask the user for their name if they get on the list.
Solution
# { box class
box: .struct
ul: .byte 0 # upper left
top: .byte 0
ur: .byte 0 # upper right
left: .byte 0
middle: .byte 0
right: .byte 0
ll: .byte 0 # lower left
bot: .byte 0 # bottom
lr: .byte 0 # lower right
.data
# { card class
card: .struct
word: .word 0
x: .word 0
y: .word 0
match: .byte 0
faceup: .byte 0
.byte 0
.byte 0
.data
.align 4
infile: .asciiz “wordfile.txt”
words: .space 2444 #611*4
.extern state,4
.extern card1,4
.extern card2,4
.extern turns,4
.extern left,4
.extern time,4
.extern n,4
.extern gameState,4
.extern tstart,4
# { instances of box}
boxBlank:
.ascii ” ”
boxd: .byte 201,205,187
.byte 186,32,186
.byte 200,205,188
boxs: .byte 218,196,191
.byte 179,32,179
.byte 192,196,217
boxw: .ascii “¬Þ¬Þ¬Þ”
.ascii “¬ß ¬à”
.ascii “¬á¬á¬á”
boxg1: .ascii “¬¡¬¡¬¡¬¡ ¬¡¬¡¬¡¬¡”
boxb: .ascii “¬Ý¬Ý¬Ý¬Ý ¬Ý¬Ý¬Ý¬Ý”
.extern lfsr,4
#——————————————————————————-
# void box::draw(int x:a0, int y:a1, int w:a2, int h:a3,box *this:s0)
.code
box.draw:
mov $t9,$a0 # save x for re-use
syscall $xy #
lb $a0,box.ul($s0)
syscall $print_char
lb $a0,box.top($s0)
mov $t0,$a2
b 2f
1: syscall $print_char
addi $t0,$t0,-1
2: bgtz $t0,1b
lb $a0,box.ur($s0)
syscall $print_char
mov $a0,$t9
addi $a1,$a1,1
syscall $xy
b 4f
3: lb $a0,box.left($s0)
syscall $print_char
lb $a0,box.middle($s0) #
mov $t0,$a2
b 2f
1: syscall $print_char
addi $t0,$t0,-1
2: bgtz $t0,1b
lb $a0,box.right($s0)
syscall $print_char
mov $a0,$t9
addi $a1,$a1,1
syscall $xy
addi $a3,$a3,-1
4: bgtz $a3,3b
lb $a0,box.ll($s0)
syscall $print_char
lb $a0,box.bot($s0)
mov $t0,$a2
b 2f
1: syscall $print_char
addi $t0,$t0,-1
2: bgtz $t0,1b
lb $a0,box.lr($s0)
syscall $print_char
jr $ra
#——————————————————————————-
# int random()
# random generator that uses the Galois LFSR PRNG function
random:
li $v0,0x0d000001
lw $t0,lfsr($gp)
srl $t1,$t0,1
andi $t0,$t0,1
beqz $t0,1f
li $t0,-1
1: and $v0,$v0,$t0
xor $v0,$v0,$t1
sw $v0,lfsr($gp)
jr $ra
#——————————————————————————-
#void Fileread(a0: char * filename,a1: char * buffer)
Fileread:
or $t0,$a1,$0 # save pointer to place to load file
add $a1,$0,$0
add $a2,$0,$0
syscall $open
add $a0,$v0,$0 # save file descriptor
or $a1,$t0,$0 # recover pointer to buffer
ori $a2,$0,1 # read a single char at a time
ori $t1,$0,611 # counter of words
1: syscall $read # read a char
lb $t0,($a1) # load read char in t3
beqz $t0,2f
beq $t0,13,1b # if it’s CR, continue
beq $t0,10,2f # if we reached end of line, continue
addi $a1,$a1,1
b 1b
2: sb $0,($a1) # insert end of string
addi $a1,$a1,1
addi $t1,$t1,-1
bnez $t1,1b
syscall $close
jr $ra
#——————————————————————————-
# void card.card(card * c)
card.draw:
addi $sp,$sp,-12
sw $ra,0($sp) # save return address
sw $s0,4($sp)
sw $s1,8($sp)
or $s1,$0,$a0
lw $a0,card.x($s1)
lw $a1,card.y($s1)
ori $a2,$0,3
ori $a3,$0,1
lb $t0,card.match($s1)
beqz $t0,0f
la $s0,boxBlank
b 2f
0: lb $t1,card.faceup($s1)
bnez $t1,1f # draw up
la $s0,boxd
b 2f
1: la $s0,boxs
2: jalbox.draw
lb $t0,card.faceup($s1)
beqz $t0,3f # for face down, don’t draw word
lw $a0,card.x($s1)
addi $a0,$a0,1
lw $a1,card.y($s1)
addi $a1,$a1,1
syscall $xy # else, put the word
lw $t0,card.word($s1)
sll $t0,$t0,2
la $t1,words
add $a0,$t1,$t0
syscall $print_string
3: lw $ra,0($sp)
lw $s0,4($sp)
lw $s1,8($sp)
add $sp,$sp,12
jr $ra
#——————————————————————————-
# void card.card(card * c)
card.card:
addi $sp,$sp,-4
sw $ra,0($sp) # save return address
sw $0,card.word($a0)
sw $0,card.x($a0)
sw $0,card.y($a0)
sb $0,card.match($a0)
ori $t0,$0,0
sb $t0,card.faceup($a0)
lw $ra,0($sp)
add $sp,$sp,4
jr $ra
#——————————————————————————-
# card * card.new()
card.new:
addi $sp,$sp,-12
sw $ra,0($sp) # save return address
sw $s0,4($sp)
sw $a0,8($sp)
ori $a0,$0,16 # reserve space for a card
syscall $malloc
or $s0,$0,$v0
or $a0,$0,$v0
jalcard.card
or $v0,$0,$s0
lw $ra,0($sp)
lw $s0,4($sp)
lw $a0,8($sp)
add $sp,$sp,12
jr $ra
#——————————————————————————-
#void Shuffle(card * array[ ]:a0, int N:a1)
Shuffle:
addi $sp,$sp,-8
sw $ra,0($sp) # save return address
sw $a1,4($sp)
1: jal random
div $v0,$a1
mfhi $t0
sll $t0,$t0,2
add $t0,$t0,$a0
lw $t2,($t0)
addi $a1,$a1,-1
sll $t1,$a1,2
add $t1,$t1,$a0
lw $t3,($t1)
lw $t4,card.word($t2) # shuffle only the words…
lw $t5,card.word($t3)
sw $t5,card.word($t2)
sw $t4,card.word($t3)
bgtz $a1,1b
lw $ra,0($sp)
lw $a1,4($sp)
add $sp,$sp,8
jr $ra
#——————————————————————————-
# void clrscr()
clrscr:
addi $sp,$sp,-16
sw $ra,0($sp) # save return address
sw $a0,4($sp)
sw $a1,8($sp)
sw $v0,12($sp)
ori $t0,$0,25*80
ori $a0,$0,32
1: syscall $print_char
addi $t0,$t0,-1
bnez $t0,1b
or $a0,$0,$0
or $a1,$0,$0
syscall $xy
lw $ra,0($sp)
lw $a0,4($sp)
lw $a1,8($sp)
lw $v0,12($sp)
add $sp,$sp,16
jr $ra
#——————————————————————————-
.data
prompt: .asciiz “Please enter the level of difficulty (1-6): ”
mturns: .asciiz “Turns: ”
mtime: .asciiz “Time: ”
mwin0: .asciiz “ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»”
mwin1: .asciiz “º Congratulations. º”
mwin2: .asciiz “º You Win! º”
mwin3: .asciiz “ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ”
bttns: .asciiz “<Exit><New game>”
wid: .word 0
hei: .word 0
array: .word 0 # pointer to start address of array of cards
.data
keyboard: .struct 0xa0000000 #start from hardware base address
flags: .byte 0
mask: .byte 0
.half 0
keypress: .byte 0,0,0
presscon: .byte 0
keydown: .half 0
shiftdown: .byte 0
downcon: .byte 0
keyup: .half 0
upshift: .byte 0
upcon: .byte 0
.data
mouse: .struct 0xa0000018
flags: .byte 0
mask: .byte 0
.half 0
.word 0
move: .word 0,0
down: .word 0,0
up: .word 0,0
wheel: .word 0,0
wheeldown: .word 0,0
wheelup: .word 0,0
.data
timer: .struct 0xa0000050 #start from timer base address
flags: .byte 0
mask: .byte 0
.half 0
t1: .word 0
t2: .word 0
t3: .word 0
t4: .word 0
t5: .word 0
t6: .word 0
t7: .word 0
.code
.globl main
#——————————————————————————-
# intpollMouse()
pollMouse:
addi $sp,$sp,-12
sw $ra,0($sp) # save return address
sw $s0,4($sp)
sw $s1,8($sp)
or $t1,$0,$0
or $t2,$0,$0
loop:
la $a0,keyboard.flags # hardware address of keyboard flags
addi $a1,$0,1 # 1 byte of data
syscall $IO_read # read flags
blez $v0,updtim # branch if no keyboard flags
andi $t0,$v0,1 # flag bit 0
beqz $t0,updtim
la $a0,keyboard.keypress # hardware address of ascii data
addi $a1,$0,1 # 1 byte of data
syscall $IO_read
beq $v0,’x,ext
beq $v0,’X,ext
beq $v0,’n,nugame
beq $v0,’N,nugame
updtim:
lw $t0,time($gp)
beq $t0,-1,1f
la $a0,timer.flags
addi $a1,$0,1
syscall $IO_read
blez $v0,1f
move $t0,$v0
addi $t7,$0,1
addi $t8,$0,2
la $t9,timer.t1
c1: and $t1,$t0,$t8
beqz $t1,c2
move $a0,$t9
syscall $IO_read
bne $t7,1,c2
lw $t6,time($gp)
addi $t6,$t6,1
sw $t6,time($gp)
addi $a0,$0,60
addi $a1,$0,24
syscall $xy
la $a0,mtime
syscall $print_string
lw $a0,time($gp)
syscall $print_int
lw $t6,tstart($gp)
beqz $t6,c2
sub $t6,$a0,$t6
blt $t6,2,c2
lw $a0,card1($gp)
lw $s0,card.x($a0)
lw $s1,card.y($a0)
b 8f
c2: addi $t7,$t7,1
sll $t8,$t8,1
addi $t9,$t9,4
andi $t3,$t7,8
beqz $t3,c1
1: la $a0,mouse.flags
li $a1,1
syscall $IO_read
beqz $v0,loop
move $t2,$v0
andi $t0,$t2,1
beqz $t0,2f
la $a0,mouse.move
li $a1,4
syscall $IO_read
2: andi $t0,$t2,2
beqz $t0,3f
la $a0,mouse.down
li $a1,4
syscall $IO_read
bnez $t1,3f
ori $t1,$t1,1
3: andi $t0,$t2,4
beqz $t0,4f
la $a0,mouse.up
li $a1,4
syscall $IO_read
beqz $t1,4f
ori $t1,$t1,2
andi $s0,$v0,0xFFFF
srl $s1,$v0,16
4: andi $t0,$t2,8
beqz $t0,5f
la $a0,mouse.wheel
li $a1,4
syscall $IO_read
5: andi $t0,$t2,16
beqz $t0,6f
la $a0,mouse.wheeldown
li $a1,4
syscall $IO_read
6: andi $t0,$t2,32
bnez $t0,7f
la $a0,mouse.wheelup
li $a1,4
syscall $IO_read
7: bne $t1,3,loop
bne $s1,24,8f
blt $s0,25,8f
ble $s0,30,ext
blt $s0,35,8f
ble $s0,44,nugame
b 8f
ext:
li $t0,1
sw $t0,gameState($gp)
b 8f
nugame:
li $t0,2
sw $t0,gameState($gp)
b 8f
8: move $v0,$s0
move $v1,$s1
lw $ra,0($sp)
lw $s0,4($sp)
lw $s1,8($sp)
add $sp,$sp,12
jr $ra
# card.clic(card * c: a0)
card.clic:
addi $sp,$sp,-20
sw $ra,0($sp) # save return address
sw $s0,4($sp)
sw $s1,8($sp)
sw $s2,12($sp)
sw $s3,16($sp)
lw $s0,state($gp)
lw $s1,card1($gp)
lw $s2,card2($gp)
lb $t0,card.match($a0)
bnez $t0,6f
ori $t0,$0,1
bnez $s0,1f
addi $s0,$s0,1
move $s1,$a0
sw $0,tstart($gp)
b 5f
1: bne $s0,1,2f
addi $s0,$s0,1
lw $t1,time($gp)
sw $t1,tstart($gp)
move $s2,$a0
bne $s1,$s2,5f
or $t0,$0,$0
or $s0,$0,$0
b 5f
2: move $s3,$a0
sw $0,tstart($gp)
sb $0,card.faceup($s1)
sb $0,card.faceup($s2)
lw $t1,card.word($s1)
lw $t2,card.word($s2)
bne $t1,$t2,3f
sb $t0,card.match($s1)
sb $t0,card.match($s2)
lw $t1,left($gp) # decrement cards left
addi $t1,$t1,-2
sw $t1,left($gp)
3: move $a0,$s1
jalcard.draw
move $a0,$s2
jalcard.draw
beq $s3,$s1,4f
beq $s3,$s2,4f
move $a0,$s3
move $s1,$s3
ori $s0,$0,1
ori $t0,$0,1
b 5f
4: or $s0,$0,$0
b 6f
5: sb $t0,card.faceup($a0)
jalcard.draw
6: sw $s0,state($gp)
sw $s1,card1($gp)
sw $s2,card2($gp)
lw $s0,turns($gp)
addi $s0,$s0,1
sw $s0,turns($gp)
lw $ra,0($sp)
lw $s0,4($sp)
lw $s1,8($sp)
lw $s2,12($sp)
lw $s3,16($sp)
add $sp,$sp,20
jr $ra
#——————————————————————————-
# void main()
main:
ori $t0,$0,1
sw $t0,lfsr($gp)
la $a0,infile # load all words in memory
la $a1,words
jalFileread
la $a0,timer.t1 # hardware address of timer1
addi $a1,$0,4 # 4 byte data
addi $a2,$0,1000 # timer interval every 1 second
syscall $IO_write
1: jalclrscr
la $a0,prompt
syscall $print_string
kloop:
jal random
la $a0,keyboard.flags # hardware address of keyboard flags
addi $a1,$0,1 # 1 byte of data
syscall $IO_read # read flags
blez $v0,kloop # branch if no keyboard flags
andi $t0,$v0,1 # flag bit 0
beqz $t0,kloop
la $a0,keyboard.keypress # hardware address of ascii data
addi $a1,$0,1 # 1 byte of data
syscall $IO_read
addi $v0,$v0,-48
beqz $v0,1b #
sltiu $t0,$v0,7 # check for out-of-range
beqz $t0,1b # try again
jalclrscr # clear the screen
sw $0,state($gp)
sw $0,card1($gp)
sw $0,card2($gp)
sw $0,turns($gp)
sw $0,time($gp)
sw $0,gameState($gp)
sw $0,tstart($gp)
ori $t0,$0,2
sllv $s0,$t0,$v0 # calculate 2<<n
sw $v0,n($gp) # save difficulty n
sw $s0,left($gp) # save difficulty 2<<n, number of cards
or $a0,$0,$s0 # reserve space for card array
sll $a0,$a0,2 # multiply by 4 to get number in bytes
syscall $malloc
la $t0,array # point to start of array with s5
sw $v0,($t0) # save pointer to allocated array
or $s5,$0,$v0
lw $v0,n($gp) # restore difficulty level in v0
li $s1,2 # initial boxes wide
li $s2,1 # initial boxes tall
andi $t0,$v0,1 # odd?
srl $v0,$v0,1 # divided by 2
sllv $s1,$s1,$v0
add $v0,$v0,$t0
sllv $s2,$s2,$v0
la $t0,wid
sw $s1,($t0)
la $t0,hei
sw $s2,($t0)
or $s7,$0,$0
mov $s4,$0 # working counters y
mov $s3,$0 # x
li $a2,3 # width of middle of box
2: li $a3,1 # height of middle of box
sll $a0,$s3,2
add $a0,$a0,$s3 # x = 5 * s3
sll $a1,$s4,1
add $a1,$a1,$s4 # y = 3 * s4
jalcard.new
or $s0,$v0,$0
sw $a0,card.x($s0) # save x and y
sw $a1,card.y($s0)
bnez $s7,3f
jal random # generate a word number
ori $t0,$0,611
divu $v0,$t0
mfhi $s6
3: sw $s6,card.word($s0)
sw $s0,($s5)
addi $s5,$s5,4
xori $s7,$s7,1
addi $s3,$s3,1
bne $s3,$s1,2b
mov $s3,$0
addi $s4,$s4,1
bne $s4,$s2,2b
la $t0,array # shuffle the cards
lw $a0,($t0)
lw $a1,left($gp)
jal Shuffle
or $s0,$a1,$0 # print cards
la $t0,array
lw $s1,($t0) # get array pointer in s1
4: lw $t0,($s1) # get a card pointer
or $a0,$0,$t0
jalcard.draw
addi $s1,$s1,4
addi $s0,$s0,-1
bnez $s0,4b
addi $a0,$0,60
addi $a1,$0,24
syscall $xy
la $a0,mtime
syscall $print_string
lw $a0,time($gp)
syscall $print_int
li $s0,0
li $s1,0
li $s2,0
5: or $a0,$0,$0
ori $a1,$0,24
syscall $xy
la $a0,mturns
syscall $print_string
lw $a0,turns($gp)
syscall $print_int
ori $a0,$0,25
ori $a1,$0,24
syscall $xy
la $a0,bttns
syscall $print_string
lw $t0,left($gp)
beqz $t0,winner
jalpollMouse
lw $t0,gameState($gp)
bnez $t0,restart
7: li $t0,5
div $t1,$v0,$t0
li $t0,3
div $t2,$v1,$t0
la $t0,wid
lw $t3,($t0)
la $t0,hei
lw $t4,($t0)
bge $t1,$t3,5b
bge $t2,$t4,5b
mul $t0,$t2,$t3
add $t0,$t0,$t1
sll $t0,$t0,2
la $t1,array
lw $a0,($t1) # get array pointer in a0
add $a0,$a0,$t0
lw $a0,($a0)
jalcard.clic
b 5b
winner:
ori $a0,$0,30
ori $a1,$0,10
syscall $xy
la $a0,mwin0
syscall $print_string
ori $a0,$0,30
addi $a1,$a1,1
syscall $xy
la $a0,mwin1
syscall $print_string
ori $a0,$0,30
addi $a1,$a1,1
syscall $xy
la $a0,mwin2
syscall $print_string
ori $a0,$0,30
addi $a1,$a1,1
syscall $xy
la $a0,mwin3
syscall $print_string
restart:
ori $t0,$0,2
lw $s0,n($gp) # get difficulty n
sllv $s0,$t0,$s0 # calculate 2<<n
la $t0,array # free all allocated space
lw $s1,($t0) # get array pointer in s1
10: lw $a0,($s1) # get a card pointer
syscall $free
addi $s1,$s1,4
addi $s0,$s0,-1
bnez $s0,10b
la $t0,array
lw $a0,($t0) # get pointer to allocated array
syscall $free # free it
li $t0,-1
sw $t0,time($gp)
wa: lw $t0,gameState($gp)
beq $t0,1,quit
beq $t0,2,1b
jalpollMouse
b wa
quit:
syscall $exit