一、实验目的
分析和理解指定的需解决问题。
利用LC-3的汇编代码设计实现相关程序。
通过LC-3仿真器调试和运行相关程序并得到正确的结果。
二、实验内容
用LC3汇编语言实现简易四子棋。
规则:
两位选手依次轮流落子;
选手不能悔棋;
有子的地方不能继续落子;
直到有一方的四个棋子能够连成一条水平线、垂直线或者是对角线;
如果棋盘已满,无人获胜,则平局。
游戏最初时打印空棋盘,用"-" (即ASCII 码 x002D)来表示该处为空,"O"(ASCII 码 x004F)表示第一位选手的棋子,"X" (ASCII 码 x0058)来表示第二位选手的棋子,为了让棋盘更易于观察,在各列间加一个空格,第6列之后不要添加,初始棋盘应该如下:
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- - - - - -
选手一始终先下第一步棋,然后两者轮流落子,在每次落子之后,应该打印该选手的信息,提示他落子,以选手一为例,应该打印信息如下:
Player 1, choose a column:
为了明确选手的落子的位置,该选手应该输入数字1-6,然后回车,数字1-6指示在落子所在的列,从左到右,无需输入行号,程序应默认从行号6到行号1递减的顺序填入该棋子,若前后输入的列号相同,则行号减一。例如,如果选手第一次在左起第二列落子,应该输入2,然后回车,则该棋子落在行6列2处,当后面输入的列号再次为2时,则将棋子落子行5列2处,以此类推,详情见后续示例输出。程序应该确保选手输入的数字对应正确的列的范围,如果输入不合理,应该输出一条错误信息,提示该选手继续输入,例如,如果对于选手一:
Player 1, choose a column: D
Invalid move. Try again.
Player 1, choose a column: 7
Invalid move. Try again.
Player 1, choose a column:
程序应该一直提示该选手,知道输入正确的数字,当用户输入完成,程序应通过显示回馈给选手,然后通过换行符(ASCII 码 x000A)换行。
当选手输入成功后,程序应打印更新后的棋盘,并检查是否有人获胜,如果没人获胜,则轮到下一位输入。
当其中一位获胜或平局时,游戏结束,程序显示最后的棋盘情况并终止(Halt)。例如,如果选手二有四子相连,应该输出:
Player 2 Wins.
如果平局,程序应该输出:
Tie Game.
三、实验步骤与结果
按如下架构编写代码
代码如下
.ORIG x3000
BRnzp pre_main ; 从main函数开始
; ----------------------------------------------------
; print 函数 打印矩阵 矩阵首地址存在mat0x标号
; 标号解释:
; pr_l1 print loop 1 外循环
; pr_l2 print loop 2 内循环
; pr_c1 print case 1 情况1 这是玩家1的棋子
; pr_c2 print case 2 情况2这是玩家2的棋子
; pr_l2ed loop 2 end 循环2结束
print ST R0, save_R0 ; 保存寄存器
ST R1, save_R1
ST R2, save_R2
ST R3, save_R3
ST R4, save_R4
ST R5, save_R5
ST R6, save_R6
ST R7, save_R7
AND R1, R1, #0 ; R1=6
ADD R1, R1, #6
LEA R3, mat0x ; R3保存矩阵首地址
pr_l1 AND R2, R2, #0 ; R2=6
ADD R2, R2, #6
pr_l2 LDR R4, R3, #0 ; R4=M[R3]取矩阵数据
ADD R3, R3, #1 ; R3+=1
ADD R5, R4, #0 ; 跳转判断
BRz pr_c3
ADD R5, R4, #-1 ; mat=1表示玩家1棋子
BRz pr_c1
ADD R5, R4, #-2 ; mat=2表示玩家2棋子
BRz pr_c2
pr_c1 LD R0, p1 ; 情况1玩家1棋子
TRAP x21
BRnzp pr_l2ed
pr_c2 LD R0, p2 ; 情况2玩家2棋子
TRAP x21
BRnzp pr_l2ed
pr_c3 LD R0, no_p ; 情况3无棋子
TRAP x21
pr_l2ed LD R0, space ; 打印空格
TRAP x21
ADD R2, R2, #-1
BRp pr_l2 ; 循环2到此结束
LD R0, nextLi ; 打印回车
TRAP x21
ADD R1, R1, #-1
BRp pr_l1 ; 循环1到此结束
LD R0, save_R0 ; 恢复寄存器
LD R1, save_R1
LD R2, save_R2
LD R3, save_R3
LD R4, save_R4
LD R5, save_R5
LD R6, save_R6
LD R7, save_R7
RET
; ----------------------------------------------------
; judge1 函数 判断纵向四连 | 的情况
; 标号解释:
; j1_l1 judge 1 loop 1 外循环
; j1_l2 judge 1 loop 2 内循环
; j1_ad1 judge 1 add 1 下一个行指针+1处
; j1_ad2 , j1_ad3, j1_ad4 同上(if跳转用)
judge1 ST R0, save_R0 ; 保存寄存器
ST R1, save_R1
ST R2, save_R2
ST R3, save_R3
ST R4, save_R4
ST R5, save_R5
ST R6, save_R6
ST R7, save_R7
; 判断|
AND R1, R1, #0
ADD R1, R1, #3 ; R1=3外循环迭代器
LEA R3, mat0x ; R3首行地址
j1_l1 AND R2, R2, #0
ADD R2, R2, #6 ; R2=6内循环迭代器
j1_l2 ADD R4, R3, #-1 ; 获取当前行地址(偏移-1从0开始)
ADD R4, R4, R2 ; R4计算列偏移地址
AND R5, R5, #0 ; R5记录玩家1棋子数目
LDR R0, R4, #0
BRz j1_l2ed ; 如果遇到0直接break
ADD R0, R0, #-1
BRnp j1_ad1
ADD R5, R5, #1
j1_ad1 ADD R4, R4, #6 ; R4指向下一行
LDR R0, R4, #0
BRz j1_l2ed ; 如果遇到0直接break
ADD R0, R0, #-1
BRnp j1_ad2
ADD R5, R5, #1
j1_ad2 ADD R4, R4, #6 ; R4指向下一行
LDR R0, R4, #0
BRz j1_l2ed ; 如果遇到0直接break
ADD R0, R0, #-1
BRnp j1_ad3
ADD R5, R5, #1
j1_ad3 ADD R4, R4, #6 ; R4指向下一行
LDR R0, R4, #0
BRz j1_l2ed ; 如果遇到0直接break
ADD R0, R0, #-1
BRnp j1_ad4
ADD R5, R5, #1
j1_ad4 ADD R6, R5, #-4 ; 只要能走到这里必定为4个1或2
BRz winc1_pre ; 如果p1计数器为4则p1赢
ADD R5, R5, #0
BRz winc2_pre ; 如果p1计数器为0则p2赢
j1_l2ed ADD R2, R2, #-1
BRp j1_l2 ; 内循环到此结束
ADD R3, R3, #6 ; R3指向下一行
ADD R1, R1, #-1
BRp j1_l1 ; 外循环到此结束
; 判断|结束
LD R0, save_R0 ; 恢复寄存器
LD R1, save_R1
LD R2, save_R2
LD R3, save_R3
LD R4, save_R4
LD R5, save_R5
LD R6, save_R6
LD R7, save_R7
RET
; ----------------------------------------------------
; judge2 函数 判断斜向四连 \ 的情况
; 标号解释:同judge1
judge2 ST R0, save_R0 ; 保存寄存器
ST R1, save_R1
ST R2, save_R2
ST R3, save_R3
ST R4, save_R4
ST R5, save_R5
ST R6, save_R6
ST R7, save_R7
; 判断\
AND R1, R1, #0
ADD R1, R1, #3 ; R1=3外循环迭代器
LEA R3, mat0x ; R3首行地址
j2_l1 AND R2, R2, #0
ADD R2, R2, #3 ; R2=3内循环迭代器
j2_l2 ADD R4, R3, #-1 ; 获取当前行地址(偏移-1从0开始)
ADD R4, R4, R2 ; R4计算列偏移地址
AND R5, R5, #0 ; R5记录玩家1棋子数目
LDR R0, R4, #0
BRz j2_l2ed ; 如果遇到0直接break
ADD R0, R0, #-1
BRnp j2_ad1
ADD R5, R5, #1
j2_ad1 ADD R4, R4, #6 ; R4指向下一行
LDR R0, R4, #1
BRz j2_l2ed ; 如果遇到0直接break
ADD R0, R0, #-1
BRnp j2_ad2
ADD R5, R5, #1
j2_ad2 ADD R4, R4, #6 ; R4指向下一行
LDR R0, R4, #2
BRz j2_l2ed ; 如果遇到0直接break
ADD R0, R0, #-1
BRnp j2_ad3
ADD R5, R5, #1
j2_ad3 ADD R4, R4, #6 ; R4指向下一行
LDR R0, R4, #3
BRz j2_l2ed ; 如果遇到0直接break
ADD R0, R0, #-1
BRnp j2_ad4
ADD R5, R5, #1
j2_ad4 ADD R6, R5, #-4 ; 只要能走到这里必定为4个1或2
BRz winc1_pre ; 如果p1计数器为4则p1赢
ADD R5, R5, #0
BRz winc2_pre ; 如果p1计数器为0则p2赢
j2_l2ed ADD R2, R2, #-1
BRp j2_l2 ; 内循环到此结束
ADD R3, R3, #6 ; R3指向下一行
ADD R1, R1, #-1
BRp j2_l1 ; 外循环到此结束
; 判断\结束
LD R0, save_R0 ; 恢复寄存器
LD R1, save_R1
LD R2, save_R2
LD R3, save_R3
LD R4, save_R4
LD R5, save_R5
LD R6, save_R6
LD R7, save_R7
RET
; ----------------------------------------------------
pre_main BRnzp p_main ; main函数过于远 中转
; ----------------------------------------------------
; 字符存放
space .FILL 0x20
no_p .FILL 0x2D
p1 .FILL 0x4F
p2 .FILL 0x58
nextLi .FILL 0x0D
; 矩阵存放
mat0x .FILL #0
.FILL #0
.FILL #0
.FILL #0
.FILL #0
.FILL #0
mat1x .FILL #0
.FILL #0
.FILL #0
.FILL #0
.FILL #0
.FILL #0
mat2x .FILL #0
.FILL #0
.FILL #0
.FILL #0
.FILL #0
.FILL #0
mat3x .FILL #0
.FILL #0
.FILL #0
.FILL #0
.FILL #0
.FILL #0
mat4x .FILL #0
.FILL #0
.FILL #0
.FILL #0
.FILL #0
.FILL #0
mat50 .FILL #0
mat51 .FILL #0
mat52 .FILL #0
mat53 .FILL #0
mat54 .FILL #0
mat55 .FILL #0
; 寄存器存放
save_R0 .FILL #0
save_R1 .FILL #0
save_R2 .FILL #0
save_R3 .FILL #0
save_R4 .FILL #0
save_R5 .FILL #0
save_R6 .FILL #0
save_R7 .FILL #0
; ----------------------------------------------------
p_main BRnzp main ; main函数过于远 中转
winc1_pre BRnzp winc1 ; 中转
winc2_pre BRnzp winc2
; ----------------------------------------------------
; judge3 判断斜向四连 / 的情况
; 标号解释:同judge1
judge3 ST R0, save_R0 ; 保存寄存器
ST R1, save_R1
ST R2, save_R2
ST R3, save_R3
ST R4, save_R4
ST R5, save_R5
ST R6, save_R6
ST R7, save_R7
; 判断/
AND R1, R1, #0
ADD R1, R1, #3 ; R1=3外循环迭代器
LEA R3, mat0x ; R3首行地址
j3_l1 AND R2, R2, #0
ADD R2, R2, #3 ; R2=3内循环迭代器
j3_l2 ADD R4, R3, #-1 ; 获取当前行地址(偏移-1从0开始)
ADD R4, R4, R2 ; R4计算列偏移地址
ADD R4, R4, #3
AND R5, R5, #0 ; R5记录玩家1棋子数目
LDR R0, R4, #0
BRz j3_l2ed ; 如果遇到0直接break
ADD R0, R0, #-1
BRnp j3_ad1
ADD R5, R5, #1
j3_ad1 ADD R4, R4, #6 ; R4指向下一行
LDR R0, R4, #-1
BRz j3_l2ed ; 如果遇到0直接break
ADD R0, R0, #-1
BRnp j3_ad2
ADD R5, R5, #1
j3_ad2 ADD R4, R4, #6 ; R4指向下一行
LDR R0, R4, #-2
BRz j3_l2ed ; 如果遇到0直接break
ADD R0, R0, #-1
BRnp j3_ad3
ADD R5, R5, #1
j3_ad3 ADD R4, R4, #6 ; R4指向下一行
LDR R0, R4, #-3
BRz j3_l2ed ; 如果遇到0直接break
ADD R0, R0, #-1
BRnp j3_ad4
ADD R5, R5, #1
j3_ad4 ADD R6, R5, #-4 ; 只要能走到这里必定为4个1或2
BRz winc1_pre ; 如果p1计数器为4则p1赢
ADD R5, R5, #0
BRz winc2_pre ; 如果p1计数器为0则p2赢
j3_l2ed ADD R2, R2, #-1
BRp j3_l2 ; 内循环到此结束
ADD R3, R3, #6 ; R3指向下一行
ADD R1, R1, #-1
BRp j3_l1 ; 外循环到此结束
; 判断/结束
LD R0, save_R0 ; 恢复寄存器
LD R1, save_R1
LD R2, save_R2
LD R3, save_R3
LD R4, save_R4
LD R5, save_R5
LD R6, save_R6
LD R7, save_R7
RET
; ----------------------------------------------------
; judge4 判断横向四连 即 - 的情况
; 标号解释:同judge1
judge4 ST R0, save_R0 ; 保存寄存器
ST R1, save_R1
ST R2, save_R2
ST R3, save_R3
ST R4, save_R4
ST R5, save_R5
ST R6, save_R6
ST R7, save_R7
; 判断-
AND R1, R1, #0
ADD R1, R1, #6 ; R1=6外循环迭代器
LEA R3, mat0x ; R3首行地址
j4_l1 AND R2, R2, #0
ADD R2, R2, #3 ; R2=3内循环迭代器
j4_l2 ADD R4, R3, #-1 ; 获取当前行地址(偏移-1从0开始)
ADD R4, R4, R2 ; R4计算列偏移地址
AND R5, R5, #0 ; R5记录玩家1棋子数目
LDR R0, R4, #0
BRz j4_l2ed ; 如果遇到0直接break
ADD R0, R0, #-1
BRnp j4_ad1
ADD R5, R5, #1
j4_ad1 LDR R0, R4, #1
BRz j4_l2ed ; 如果遇到0直接break
ADD R0, R0, #-1
BRnp j4_ad2
ADD R5, R5, #1
j4_ad2 LDR R0, R4, #2
BRz j4_l2ed ; 如果遇到0直接break
ADD R0, R0, #-1
BRnp j4_ad3
ADD R5, R5, #1
j4_ad3 LDR R0, R4, #3
BRz j4_l2ed ; 如果遇到0直接break
ADD R0, R0, #-1
BRnp j4_ad4
ADD R5, R5, #1
j4_ad4 ADD R6, R5, #-4 ; 只要能走到这里必定为4个1或2
BRz winc1_pre ; 如果p1计数器为4则p1赢
ADD R5, R5, #0
BRz winc2_pre ; 如果p1计数器为0则p2赢
j4_l2ed ADD R2, R2, #-1
BRp j4_l2 ; 内循环到此结束
ADD R3, R3, #6 ; R3指向下一行
ADD R1, R1, #-1
BRp j4_l1 ; 外循环到此结束
; 判断-结束
LD R0, save_R0 ; 恢复寄存器
LD R1, save_R1
LD R2, save_R2
LD R3, save_R3
LD R4, save_R4
LD R5, save_R5
LD R6, save_R6
LD R7, save_R7
RET
; ----------------------------------------------------
; 列指针存放
ptr0 .FILL #0
ptr1 .FILL #0
ptr2 .FILL #0
ptr3 .FILL #0
ptr4 .FILL #0
ptr5 .FILL #0
; ----------------------------------------------------
; main 函数 ,程序从这里开始执行
; 标号解释:
; ma_lp1 main loop 1 主函数循环1
; in1 main input 1 玩家1输入
; in2 main input 2 玩家2输入
; i1t2 input 1 test 2 玩家1输入合法测试2
; i1ed input 1 end 输入1测试结束
; 其他同理
; winc1 win case 1 玩家1获胜
; winc2 win case 2 玩家2获胜
; EOM End Of Main main函数结束
main AND R1, R1, #0 ; R1=18 迭代变量
ADD R1, R1, #15
ADD R1, R1, #3
JSR print
LEA R0, mat50 ; 列指针赋值
ST R0, ptr0
LEA R0, mat51
ST R0, ptr1
LEA R0, mat52
ST R0, ptr2
LEA R0, mat53
ST R0, ptr3
LEA R0, mat54
ST R0, ptr4
LEA R0, mat55
ST R0, ptr5
ma_lp1 ADD R0, R0, #0
in1 LEA R0, p1inp ; 打印输入
TRAP x22
TRAP x20 ; 玩家1输入
TRAP x21 ; 回显
ADD R0, R0, #-16
ADD R0, R0, #-16
ADD R0, R0, #-16 ; 输入-48 ascii转数字
ADD R2, R0, #0 ; 输出回车
LD R0, nextLi_
TRAP x21
ADD R0, R2, #0
BRp i1t2 ; 判断输入是否>'0'
LEA R0, err ; 输出错误提示
TRAP x22
BRnzp in1
i1t2 ADD R2, R0, #-7 ; R2=输入-'7'
BRn i1ed ; 判断输入是否<'7'
LEA R0, err
TRAP x22
BRnzp in1
i1ed AND R2, R2, #0
ADD R2, R2, #1 ; 玩家1填数字1
LEA R3, ptr0 ; R3取列指针地址
ADD R3, R3, R0 ; R3列指针按照输入偏移
LDR R4, R3, #-1 ; 读取指针数据
STR R2, R4, #0 ; 将数据写入R3指向的地址
ADD R4, R4, #-6 ; 指针--
STR R4, R3, #-1 ; 将偏移后的值存回去
JSR print
JSR judge1 ; 判断 | 输赢
JSR judge2 ; 判断 \ 输赢
JSR judge3 ; 判断 / 输赢
JSR judge4 ; 判断 - 输赢
in2 LEA R0, p2inp ; 打印:请玩家2输入
TRAP x22
TRAP x20 ; 玩家2输入
TRAP x21
ADD R0, R0, #-16
ADD R0, R0, #-16
ADD R0, R0, #-16 ; 输入-48 ascii转数字
ADD R2, R0, #0 ; 输出回车
LD R0, nextLi_
TRAP x21
ADD R0, R2, #0
BRp i2t2 ; 判断输入是否>'0'
LEA R0, err ; 输出错误提示
TRAP x22
BRnzp in2
i2t2 ADD R2, R0, #-7 ; R2=输入-'7'
BRn i2ed ; 判断输入是否<'7'
LEA R0, err
TRAP x22
BRnzp in2
i2ed AND R2, R2, #0
ADD R2, R2, #2 ; 玩家2填数字2
LEA R3, ptr0 ; R3取列指针地址
ADD R3, R3, R0 ; R3列指针按照输入偏移
LDR R4, R3, #-1 ; 读取指针数据
STR R2, R4, #0 ; 将数据写入R3指向的地址
ADD R4, R4, #-6 ; 指针--
STR R4, R3, #-1 ; 将偏移后的值存回去
JSR print
JSR judge1 ; 判断 | 输赢
JSR judge2 ; 判断 \ 输赢
JSR judge3 ; 判断 / 输赢
JSR judge4 ; 判断 - 输赢
ADD R1, R1, #-1
BRp ma_lp1 ; 循环到此结束
LEA R0, draw ; 平局
TRAP x22
BRnzp EOM
winc1 LEA R0, p1win ; p1赢了
TRAP x22
BRnzp EOM
winc2 LEA R0, p2win ; p2赢了
TRAP x22
BRnzp EOM
EOM AND R0, R0, #0 ; main函数结束
; ----------------------------------------------------
; 字符存放
nextLi_ .FILL 0x0D
; 字符串存放
p1inp .STRINGZ "player1 choose a cloumn:"
p2inp .STRINGZ "player2 choose a cloumn:"
draw .STRINGZ "Tie Game\n"
err .STRINGZ "Invalid move. Try again.\n"
p1win .STRINGZ "player1 win\n"
p2win .STRINGZ "player2 win\n"
.END
四、实验结论
利用LC3汇编语言可以编写简易的小程序;
通过此次实验,加深了对LC3汇编语言的掌握程度;
编写代码时,要注意跳转指令的范围只有256,超出范围需要进行中转;
(by 子忆)