Programming Exercise I: Showing a One-Player Dot-and-Box Game
(Unlike other languages, using others’ assembly code is obvious.)
Development Requirements
When start developing the exercise, follow the two requirements below:
- Have to use the MIPS (Microprocessor without Interlocked Pipeline Stages) assembly language to develop the exercise.
- Use the MARS (MIPS Assembler and Runtime Simulator) to test your exercise.
The MARS will also be used by the instructor to grade exercises.
Soft Due Date† and Submission Method
On or before Thursday, February 20, 2025 and upload the source code (no documentation needed) to the section of “COVID-19 Exams, Homeworks, & Programming Exercises” of
Blackboard.
†Since related topics may not be covered completely by the due date, no penalty will be applied if submitted after the due date.
However, you may lag behind if you are not able to submit it by then.
In addition, the Exam I will cover the materials from the Programming Exercise I.
Objective
Design and implement a MIPS assembly program, which displays a 2×2 dot-and-box game of one player.
The final program is about 100 lines without using subroutines.
The purpose of this exercise is to get students ready for assembly programming and the second exercise, playing a dot-and-box game against computer.
An example of a 2×2 dot-and-box game
|
|
|
Rules of a Dot-and-Box Game
Starting with an empty grid of dots, players take turns, adding a single horizontal or vertical line between two unjoined adjacent dots.
A player who completes the fourth side of a 1×1 box earns one point and takes another turn. The game ends when no more lines can be placed.
The winner of the game is the player with the most points.
The board may be of any size.
Requirements
The limited 2×2 dot-and-box game includes the following requirements:
- (Printing user prompts: 10%)
Print appropriate user prompts to guide users to interact with your system.
- (Displaying the game board: 10%)
The board includes two parts: the game board (left) and the index (right).
. . . . 0 . 1 .
2 3 4
. . . . 5 . 6 .
7 8 9
. . . . a . b .
(board) (index)
- (Input: 10%)
Read the next move [0-9ab].
- (Marking the moves and showing the board: 30%)
Mark the move by using the index.
For example, if the first move is 8, the board is displayed as follows:
. . . . 0 . 1 . . . . . 0 . 1 .
2 3 4 2 3 4
. . . . 5 . 6 . ⇒ . . . . 5 . 6 .
7 8 9 8 | 7 8 9
. . . . a . b . . . . . a . b .
(board) (index) (board) (index)
- (Next move: 20%)
Continue with the next move after asking.
- (New game: 20%)
Start a new game after asking.
- Ignore the result (win/loss).
The game ends when the user requests for no new game.
Programming Hints
The four essential components of software are (i) algorithms, (ii) data structures, (iii) programming languages, and (iv) code, where algorithms are the most critical one.
In addition, using appropriate data structures could save a great deal of coding work, especially for assembly coding.
The following hints are from the instructor and you do not necessarily have to use them:
- To represent the game board, a 2×2 grid:
. . . . 0 . 1 .
2 3 4
. . . . 5 . 6 .
7 8 9
. . . . a . b .
(board) (index)
the instructor uses the following strings to represent the game board and index:
board: .ascii "\n\n . . . . 0 . 1 ."
.ascii "\n 2 3 4"
.ascii "\n . . . . 5 . 6 ."
.ascii "\n 7 8 9"
.asciiz "\n . . . . a . b .\n"
The assembly code annotated with the corresponding offsets is given as follows:
board: .ascii "\n\n . . . . 0 . 1 ."
offset = 0 1234567890123456789012345678
.ascii "\n 2 3 4"
offset = 28 + 1234567890123456789012345678
.ascii "\n . . . . 5 . 6 ."
offset = 56 + 1234567890123456789012345678
.ascii "\n 7 8 9"
offset = 84 + 1234567890123456789012345678
.asciiz "\n . . . . a . b .\n"
offset = 112 + 1234567890123456789012345678 9
where the numbers are offsets (not assembly code) and the new line, ‘\n’, is one byte long.
The instructor uses the following two commands to represent the offsets and markers, respectively:
offset: .byte 6, 8, 33, 35, 37, 62, 64, 89, 91, 93, 118, 120
marker: .byte '-', '-', '|', '|', '|', '-', '-', '|', '|', '|', '-', '-'
Convert the indexes a and b to 10 and 11, respectively.
Assuming the integer move, 0-11 (or 0-9 a-b), is stored in $t0
, the following code then puts the marker at that location:
lb $t1, offset($t0) # Load $t1 with the offset of the index $t0.
lb $t2, marker($t0) # Find the corresponding marker.
sb $t2, board($t1) # Put the marker at the location, board+offset.
For example, if the first move is 8 stored in $t0
and the above code is executed, the board is displayed as follows:
. . . . 0 . 1 . . . . . 0 . 1 .
2 3 4 2 3 4
. . . . 5 . 6 . ⇒ . . . . 5 . 6 .
7 8 9 8 | 7 8 9
. . . . a . b . . . . . a . b .
(board) (index) (board) (index)
- Using subroutines can reduce repeated code.
An example of how to call the subroutine
routine1
is given as follows:
jal routine1 # Jump and link to routine1
next:
...
where the label next
is not required.
The following command in the subroutine routine1
will return the control back to the next
, the next command after the “jal routine1
”:
routine1:
...
jr $ra # Jump to the contents of $ra
where the return address is saved in the register $31
or $ra
- When a subroutine calls another subroutine, the return address,
$31
or $ra
, has to be saved before making the call.
The return address is restored after the called subroutine returns.
The instructor uses the runtime stack to save the return addresses, so deep subroutine calls are supported.
Use the following two commands to push the $ra
to the runtime stack:
subu $sp, $sp, 4 # Decrement the $sp to make space for $ra.
sw $ra, ($sp) # Push the return address, $ra.
and use the following two commands to pop up the $ra
from the runtime stack:
lw $ra, ($sp) # Pop the return address, $ra.
addu $sp, $sp, 4 # Increment the $sp.
You may also use the runtime stack to save the register and variable values.
- Sample code of marking a game board (with input [0-9]) is given below for your reference:
MarkBoard.asm (for input [0-9] but no a or b)
|
|
#
######################### Marking the Game Board #########################
#
.data
pr1: .asciiz "\n********** Marking the game board **********
board: .ascii "\n\n . . . . 0 . 1 ."
.ascii "\n 2 3 4"
.ascii "\n . . . . 5 . 6 ."
.ascii "\n 7 8 9"
.asciiz "\n . . . . a . b .\n"
pr2: .asciiz "\nEnter an move: [0-9] "
offset: .byte 6, 8, 33, 35, 37, 62, 64, 89, 91, 93, 118, 120
marker: .byte '-', '-', '|', '|', '|', '-', '-', '|', '|', '|', '-', '-'
.text
.globl main
#
########################## Main Program #######################
#
main: # Printing the prompt 1
la $a0, pr1
li $v0, 4 # 4: printing string
syscall
# Printing the game board
la $a0, board
li $v0, 4 # 4: printing string
syscall
# Printing the prompt 2
la $a0, pr2
li $v0, 4 # 4: printing string
syscall
# Reading the move
li $v0, 5 # 5: reading integer (no a or b)
syscall
move $a0, $v0 # a0: the integer move
jal MarkMove # Calling the subroutine MarkMove
# Printing the game board
la $a0, board
li $v0, 4 # 4: printing string
syscall
# Closing up
li $v0, 10 # 10: exiting
syscall
#
######################### Mark a Move #########################
#
# Input: $a0 (the integer move 0-9)
# Output:
MarkMove:
subu $sp, $sp, 4 # Decrement the $sp to make space for $ra.
sw $ra, ($sp) # Push the return address, $ra.
subu $sp, $sp, 4 # Decrement the $sp to make space for $t0.
sw $t0, ($sp) # Push the $t0.
lb $t0, offset($a0) # Load $t0 with the offset of the index $a0.
lb $t1, marker($a0) # Find the corresponding marker.
sb $t1, board($t0) # Put the marker at the location, board+offset.
lw $t0, ($sp) # Pop $t0.
addu $sp, $sp, 4 # Increment the $sp.
lw $ra, ($sp) # Pop the return address, $ra.
addu $sp, $sp, 4 # Increment the $sp.
jr $ra # Return.
|
An Example of MarkBoard.asm Execution
|
|
********** Marking the game board *********
. . . . 0 . 1 .
2 3 4
. . . . 5 . 6 .
7 8 9
. . . . a . b .
Enter an move: [0-9] 8
. . . . 0 . 1 .
2 3 4
. . . . 5 . 6 .
| 7 8 9
. . . . a . b .
-- program is finished running --
|
†The italic, white text with a navy background color is entered by users.
‡Notice the subroutine is not necessary.
It is to show how to use subroutines and runtime stack.
Execution Examples
The following list shows some execution examples:
Examples of Exercise I Execution
|
|
Start Showing a One-Player 2x2 Dot-and-Box Game.
. . . . 0 . 1 .
2 3 4
. . . . 5 . 6 .
7 8 9
. . . . a . b .
Enter the next move (0..b): 8
. . . . 0 . 1 .
2 3 4
. . . . 5 . 6 .
| 7 8 9
. . . . a . b .
Continue? (y/n) y
. . . . 0 . 1 .
2 3 4
. . . . 5 . 6 .
| 7 8 9
. . . . a . b .
Enter the next move (0..b): b
. . . . 0 . 1 .
2 3 4
. . . . 5 . 6 .
| 7 8 9
. .-. . a . b .
Continue? (y/n) y
. . . . 0 . 1 .
2 3 4
. . . . 5 . 6 .
| 7 8 9
. .-. . a . b .
Enter the next move (0..b): 0
.-. . . 0 . 1 .
2 3 4
. . . . 5 . 6 .
| 7 8 9
. .-. . a . b .
Continue? (y/n) n
New game? (y/n) y
Start Showing a One-Player 2x2 Dot-and-Box Game.
. . . . 0 . 1 .
2 3 4
. . . . 5 . 6 .
7 8 9
. . . . a . b .
Enter the next move (0..b): 5
. . . . 0 . 1 .
2 3 4
.-. . . 5 . 6 .
7 8 9
. . . . a . b .
Continue? (y/n) y
. . . . 0 . 1 .
2 3 4
.-. . . 5 . 6 .
7 8 9
. . . . a . b .
Enter the next move (0..b): 2
. . . . 0 . 1 .
| 2 3 4
.-. . . 5 . 6 .
7 8 9
. . . . a . b .
Continue? (y/n) n
New game? (y/n) n
-- program is finished running --
|
Possible Instructions to Be Used
The following directives and instructions may be used in this exercise, but you are not limited to them.
For instruction syntax, check
MIPS Instruction Reference.
No. |
Directive |
Description |
1 |
.ascii "string" |
Allocating space for string |
2 |
.asciiz "string" |
Allocating space for string, NULL terminated |
3 |
.byte |
Allocating space for a byte |
4 |
.data |
Beginning of data section |
5 |
.globl name |
Making the following name be a global symbol |
6 |
.half |
Allocating space for a half word (two bytes) |
7 |
.space n |
Allocating n bytes of space |
8 |
.text |
Beginning of text section |
No. |
Instruction |
Operation |
Description |
1 |
add rd, rs, rt |
rd = rs + rt |
Add;
rd : destination register, rs : first source register, and rt : second source register or immediate value.
Check MIPS Registers and Usage Convention.
|
2 |
addu rd, rs, rt |
rd = rs + rt (no overflow) |
Add unsigned |
3 |
beq rs, rt, label |
if rs==rt then goto label |
Branch if equal to |
4 |
bgt rs, rt, label |
if rs>rt then goto label |
Branch if greater than |
5 |
blt rs, rt, label |
if rs<rt then goto label |
Branch if less than |
6 |
j label |
jump to label |
Jump |
7 |
jal label |
jump to label and save the return address in $31 or $ra |
Jump and link |
8 |
jr rs |
jump to [rs] ; [ ]: contents of |
Jump and link |
9 |
la rd, mem |
rd = address( mem ) |
Load address |
10 |
lb rd, mem |
rd = mem |
Load byte |
11 |
lh rd, mem |
rd = mem |
Load half word |
12 |
li rd, imm |
rd = imm |
Load immediate |
13 |
lw rd, mem |
rd = mem |
Load word |
14 |
mul rd, rs, rt |
rd = rs × rt |
Multiply |
15 |
sb rs, mem |
mem = rs |
Store byte |
16 |
sub rd, rs, rt |
rd = rs - rt |
Subtract |
17 |
subu rd, rs, rt |
rd = rs - rt (no overflow) |
Subtract unsigned |
18 |
sw rs, mem |
mem = rs |
Store word |
19 |
syscall |
|
System call;
check System Services. |
The following table lists some
System Services provided by the MARS:
Service |
Code in $v0 |
Arguments |
Result |
print_int |
1 |
$a0 = integer to be printed |
|
print_float |
2 |
$f12 = float to be printed |
|
print_double |
3 |
$f12 = double to be printed |
|
print_string |
4 |
$a0 = address of string in memory |
|
read_int |
5 |
|
integer returned in $v0 |
read_float |
6 |
|
float returned in $v0 |
read_double |
7 |
|
double returned in $v0 |
read_string |
8 |
$a0 = address of string input buffer
$a1 = length of string buffer (n ) |
|
malloc |
9 |
$a0 = amount |
address in $v0 |
exit |
10 |
|
|
print character |
11 |
$a0 = character to be printed |
|
read character |
12 |
|
character returned in $v0 |
Evaluations
The following features will be considered when grading:
- Specifications:
- The instructor has given the exercise specifications as detailedly as possible.
If you are confused about the specifications, you should ask in advance.
Study the specifications very carefully.
No excuses for misunderstanding or missing parts of the specifications after grading.
- The specifications may not be possible to cover every detail.
You are free to implement the issues not mentioned in the specifications, but the implementations should make sense.
Implemented functions lacking common sense may cause the instructor to grade your exercise mistakenly, and thus lower your grade.
- The exercise must meet the specifications.
However, exercises with functions exceeding the specifications will not receive extra credits.
- Grading:
- This exercise will be graded after the source code is submitted.
Students take full responsibility if the code does not work.
- The MARS simulator will be used to grade exercises.
Make sure your exercise works on MARS.
- Before submitting the exercise, test it comprehensively.
Absolutely no extra points will be granted after grading.
- A set of test data will be used to test the exercises of all students.
The grades are primarily based on the results of testing.
Other factors such as performance, programming styles, algorithms, and data structures will be only considered minimally.
- Unless specified, no error checking is required.
- The total weight of exercises is 20% (10% each) of the final grade.
- Though Exercise II covers parts of Exercise I, you still have to submit both exercises separately.
One exercise will not be counted twice.
- The instructor will inform you the exercise evaluations by emails or Blackboard after grading.
- Comments:
- According to a study, students in computer-science courses learn much more by building large-scale exercises instead of many small-scale test programs, which give fragmented knowledge contrary to solid understanding of the language.
- Time management is critical for software development.
If you are not able to complete the exercises, show whatever you have accomplished, so the instructor can give partial credit to your exercises.