实验目的与要求
(1)熟悉和理解 LC-3 的子程序指令格式。
(2)掌握利用子程序解决问题的思路,加深对底层硬件的理解。
实验内容
具体一点: https://users.ece.utexas.edu/~patt/04f.306/LabAssignments/Lab5/lab5.html
根据点和盒子游戏提供的一个通用框架以及一些提供好的子程序。通过编写以下子程序来完成点和盒子游戏: DISPLAY_BOARD, GET_ADDRESS, FILL_BOX BOXES_COMPLETED, APPLY_MOVE, IS_OCCUPIED, TRANSLATE_MOVE, IS_INPUT_VALID, UPDATE_STATE, IS_GAME_OVER, DISPLAY_PROMPT。
实验步骤与过程
- 主程序
主程序首先显示游戏板和提示信息,然后从键盘获取输入字符并回显到屏幕。如果输入字符为 ‘Q’,程序退出;否则继续获取另一个字符并验证输入的合法性和有效性。若移动有效且位置未被占用,则应用该移动,更新游戏状态并重新显示游戏板。主程序循环进行上述操作,直到检测到游戏结束或玩家输入 ‘Q’ 退出游戏。
- DISPLAY_BOARD实现
这个子程序用于将游戏板和两名玩家的分数打印到屏幕上。它首先保存当前寄存器状态,然后逐行输出游戏板的内容,包括行号和行数据。接着调用 DISPLAY_SCORE 子程序显示玩家分数,最后恢复寄存器状态并返回。
DISPLAY_SCORE 子程序:
- GET_ADDRESS实现
这个子程序根据给定的行号(R0)和列号(R1)计算并返回相应的地址。首先保存寄存器的值,然后从基地址 ROW0_ADDR 开始,通过计算行偏移和列偏移来获得目标地址,最后恢复寄存器的值并返回。
- FILL_BOX实现
这个子程序根据当前玩家的编号,填充一个方块。它通过保存和恢复寄存器的值,获取当前玩家的编号,并将编号存储到方块中。
- BOXES_COMPLETED实现
这个子程序通过调用 GET_ADDRESS 获取绘制线条的地址,然后根据线条的方向(水平或垂直)分别调用 IS_BOX_COMPLETE 检查相邻的正方形是否完整,如果完整则调用 FILL_BOX 填充该正方形,最后返回完成的正方形数量。
IS_BOX_COMPLETE子程序:
BOUNDS_CHECK:
- APPLY_MOVE实现
这个子程序根据传入的列号和行号,在适当的位置写入 - 或 |,表示玩家的棋步。
- IS_OCCUPIED实现
这个子程序用于检查给定位置是否被占用,并将结果存储在寄存器 R3 中。如果位置未被占用,寄存器 R3 的值为零;如果位置已被占用,寄存器 R3 的值为 -1。
- TRANSLATE_MOVE实现
这个子程序将输入的列和行的 ASCII 码翻译为相应的列号和行号,并将结果存储在寄存器 R1 和 R0 中。
- IS_INPUT_VALID实现
这个子程序检查输入的行(R0)和列(R1)的ASCII字符是否有效。思路为: 当行号为0或2或4或6时,列号为B或D或F时,有效;当行号为1或3或5时,列号为A或C或E或G时,有效;其他情况无效。
- UPDATE_STATE实现
这个子程序根据上一步完成的方块数(R0)更新当前玩家的分数。如果当前玩家是玩家1,则更新玩家1的分数;否则更新玩家2的分数。如果没有完成方块,则切换当前玩家。最后,恢复寄存器的值并返回。
- IS_GAME_OVER实现
这个子程序检查游戏是否已经结束,并输出相应的获胜者或平局信息。如果有获胜者,寄存器 R3 的值为零;如果是平局或游戏尚未结束,寄存器 R3 的值置为 -1。
- DISPLAY_PROMPT实现
这个子程序用于打印提示信息,提醒玩家输入棋步或退出游戏。根据当前玩家的值,选择性地显示玩家1或玩家2的提示信息。
- 运行结果
运行结果图片过多,结果跟以下这两个样例相同即可:
(1)样例1(users.ece.utexas.edu/~patt/04f.306/LabAssignments/Lab5/example1.txt)
(2)样例2(users.ece.utexas.edu/~patt/04f.306/LabAssignments/Lab5/example2.txt)
实验代码
.ORIG x3000
JSR DISPLAY_BOARD
PROMPT JSR DISPLAY_PROMPT
TRAP x20 ; 从键盘获取一个字符到 R0
TRAP x21 ; 输出到屏幕上
LD R3, ASCII_Q_COMPLEMENT ; 加载 ASCII 'Q' 的补码
ADD R3, R0, R3 ; 比较第一个字符和 'Q'
BRz EXIT ; 如果输入是 'Q',则退出
ADD R1, R0, #0 ; 将 R0 移动到 R1,释放 R0 以便进行另一个 TRAP
TRAP x20 ; 获取另一个字符到 R0
TRAP x21 ; 输出到屏幕上
JSR IS_INPUT_VALID
JSR TRANSLATE_MOVE ; 将移动转换为 {0..6} 坐标
ADD R3, R3, #0
BRz VALID_MOVE
LEA R0, INVALID_MOVE_STRING
TRAP x22
BR PROMPT
VALID_MOVE JSR IS_OCCUPIED
ADD R3, R3, #0 ; 如果位置未被占用,R3 将为零
BRz UNOCCUPIED
LEA R0, OCCUPIED_STRING ; 如果位置已被占用,输出相应的消息
TRAP x22 ; 并返回到提示符
BR PROMPT
UNOCCUPIED JSR APPLY_MOVE ; 应用移动
JSR BOXES_COMPLETED ; 返回此移动完成的盒子数到 R3
ADD R0, R3, #0 ; 将完成的盒子数移动到 R0,UPDATE_STATE 需要它
JSR UPDATE_STATE ; 根据需要更改分数和玩家
JSR DISPLAY_BOARD
JSR IS_GAME_OVER
ADD R3, R3, #0 ; 如果有赢家,R3 将为零
BRnp PROMPT ; 否则,返回循环
EXIT LEA R0, GOODBYE_STRING
TRAP x22 ; 输出再见消息
TRAP x25 ; 停止
ASCII_Q_COMPLEMENT .FILL xFFAF ; Q的补码
INVALID_MOVE_STRING .STRINGZ "\nInvalid move. Please try again.\n"
OCCUPIED_STRING .STRINGZ "\nThis position is already occupied. Please try again.\n"
GOODBYE_STRING .STRINGZ "\nThanks for playing! Goodbye!\n"
; DISPLAY_BOARD
; 此子程序将游戏板和两名玩家的当前分数打印到屏幕上。
DISPLAY_BOARD ST R0, DB_R0
ST R1, DB_R1
ST R2, DB_R2
ST R3, DB_R3
ST R7, DB_R7
AND R1, R1, #0 ; 将 R1 清零,作为循环计数器
ADD R1, R1, #6 ; 将 R1 设置为6,表示有6行需要输出
LEA R2, ROW0 ; 将 R2 指向 ROW0,表示第一行的地址
LEA R3, ZERO ; 将 R3 指向 ZERO,表示行号的地址
LD R0, ASCII_NEWLINE ; 加载换行符到 R0
OUT ; 输出换行符
OUT ; 再次输出换行符
LEA R0, COL ; 将 R0 指向列标题字符串
PUTS ; 输出列标题字符串
LD R0, ASCII_NEWLINE ; 加载换行符到 R0
OUT ; 输出换行符
DB_ROWOUT ADD R0, R3, #0 ; 将行号地址移到 R0
PUTS ; 输出行号
ADD R0, R2, #0 ; 将行地址移到 R0
PUTS ; 输出行内容
LD R0, ASCII_NEWLINE ; 加载换行符到 R0
OUT ; 输出换行符
ADD R2, R2, #8 ; 增加 R2,使其指向下一行
ADD R3, R3, #3 ; 增加 R3,使其指向下一个行号
ADD R1, R1, #-1 ; R1 减 1,表示一行已输出
BRzp DB_ROWOUT ; 如果 R1 >= 0,继续输出下一行
JSR DISPLAY_SCORE ; 调用 DISPLAY_SCORE 子程序,显示分数
LD R0, DB_R0
LD R1, DB_R1
LD R2, DB_R2
LD R3, DB_R3
LD R7, DB_R7
RET
DB_R0 .BLKW #1
DB_R1 .BLKW #1
DB_R2 .BLKW #1
DB_R3 .BLKW #1
DB_R7 .BLKW #1
; DISPLAY_SCORE
; 打印分数到屏幕上
DISPLAY_SCORE ST R0, DS_R0 ; 保存寄存器
ST R7, DS_R7
LEA R0, DS_BEGIN_STRING
TRAP x22 ; 打印分数字符串的第一部分
LD R0, SCORE_PLAYER_ONE
LD R7, ASCII_OFFSET
ADD R0, R0, R7 ; 创建第一个玩家分数的ASCII码
TRAP x21 ; 输出分数
LEA R0, DS_OTHER_STRING
TRAP x22 ; 打印分数字符串的第二部分
LD R0, SCORE_PLAYER_TWO
LD R7, ASCII_OFFSET
ADD R0, R0, R7 ; 创建第二个玩家分数的ASCII码
TRAP x21 ; 输出分数
LD R0, ASCII_NEWLINE
TRAP x21
LD R0, DS_R0 ; 恢复寄存器
LD R7, DS_R7
RET
DS_R0 .BLKW #1
DS_R7 .BLKW #1
DS_BEGIN_STRING .STRINGZ "SCORE Player 1: "
DS_OTHER_STRING .STRINGZ " Player 2: "
; IS_BOX_COMPLETE
; 输入 R1 正方形中心的列号 (0-6)
; R0 正方形中心的行号 (0-6)
; 返回 R3 如果正方形完整则为零;如果不完整则为-1
IS_BOX_COMPLETE ST R0, IBC_R0 ; 保存寄存器
ST R1, IBC_R1
ST R2, IBC_R2
ST R4, IBC_R4
ST R7, IBC_R7
ADD R0, R0, #-1 ; 检查上方管道
JSR BOUNDS_CHECK
ADD R3, R3, #0
BRnp IBC_NON_COMPLETE
JSR IS_OCCUPIED
ADD R3, R3, #0
BRz IBC_NON_COMPLETE
ADD R0, R0, #2 ; 检查下方管道
JSR BOUNDS_CHECK
ADD R3, R3, #0
BRnp IBC_NON_COMPLETE
JSR IS_OCCUPIED
ADD R3, R3, #0
BRz IBC_NON_COMPLETE
ADD R0, R0, #-1 ; 检查左方管道
ADD R1, R1, #-1
JSR BOUNDS_CHECK
ADD R3, R3, #0
BRnp IBC_NON_COMPLETE
JSR IS_OCCUPIED
ADD R3, R3, #0
BRz IBC_NON_COMPLETE
ADD R1, R1, #2 ; 检查右方管道
JSR BOUNDS_CHECK
ADD R3, R3, #0
BRnp IBC_NON_COMPLETE
JSR IS_OCCUPIED
ADD R3, R3, #0
BRz IBC_NON_COMPLETE
ADD R1, R1, #-1 ; 返回原始正方形位置
AND R3, R3, #0
BR IBC_EXIT
IBC_NON_COMPLETE AND R3, R3, #0
ADD R3, R3, #-1
IBC_EXIT LD R0, IBC_R0 ; 恢复寄存器
LD R1, IBC_R1
LD R2, IBC_R2
LD R4, IBC_R4
LD R7, IBC_R7
RET
IBC_R0 .BLKW #1
IBC_R1 .BLKW #1
IBC_R2 .BLKW #1
IBC_R4 .BLKW #1
IBC_R7 .BLKW #1
; BOXES_COMPLETED
; 输入 R1 列号 (0-6)
; R0 行号 (0-6)
; 返回 R3 此次移动完成的正方形数量
BOXES_COMPLETED ST R7, BC1_R7 ; 保存寄存器
ST R4, BC1_R4
JSR GET_ADDRESS ; 获取将在游戏板结构中绘制线条的地址
AND R4, R1, #1
BRz BC1_VERTICAL ; 如果绘制的线条是垂直的,则跳转
AND R4, R4, #0 ; R4 将保存完成的正方形数量
ADD R0, R0, #-1 ; 检查上方正方形是否完整
JSR IS_BOX_COMPLETE
ADD R3, R3, #0 ; 如果正方形完整,R3 将为零
BRnp BC1_SKIP1
ADD R4, R4, #1 ; 完成一个正方形
JSR FILL_BOX
BC1_SKIP1 ADD R0, R0, #2 ; 检查下方正方形是否完整
JSR IS_BOX_COMPLETE
ADD R3, R3, #0 ; 如果正方形完整,R3 将为零
BRnp BC1_SKIP2
ADD R4, R4, #1
JSR FILL_BOX
BC1_SKIP2 ADD R0, R0, #-1 ; 恢复 R0
BRnzp BC1_EXIT
BC1_VERTICAL AND R4, R4, #0
ADD R1, R1, #-1 ; 检查左方正方形是否完整
JSR IS_BOX_COMPLETE
ADD R3, R3, #0 ; 如果正方形完整,R3 将为零
BRnp BC1_SKIP3
ADD R4, R4, #1
JSR FILL_BOX
BC1_SKIP3 ADD R1, R1, #2 ; 检查右方正方形是否完整
JSR IS_BOX_COMPLETE
ADD R3, R3, #0 ; 如果正方形完整,R3 将为零
BRnp BC1_SKIP4
ADD R4, R4, #1
JSR FILL_BOX
BC1_SKIP4 ADD R1, R1, #-1 ; 恢复 R1
BC1_EXIT ADD R3, R4, #0 ; 将完成的正方形数量移到 R3
LD R7, BC1_R7 ; 恢复寄存器
LD R4, BC1_R4
RET
BC1_R7 .BLKW #1
BC1_R4 .BLKW #1
; BOUNDS_CHECK
; 检查边界
; 输入 R1 数字列号
; R0 数字行号
; 返回 R3 如果有效返回零;如果无效返回 -1
BOUNDS_CHECK ADD R1, R1, #0 ; 列检查
BRn BC_HUGE_ERROR ; 如果列号为负数,跳转到错误处理
ADD R3, R1, #-6 ; 检查列号是否大于 6
BRp BC_HUGE_ERROR ; 如果列号大于 6,跳转到错误处理
ADD R0, R0, #0 ; 行检查
BRn BC_HUGE_ERROR ; 如果行号为负数,跳转到错误处理
ADD R3, R0, #-6 ; 检查行号是否大于 6
BRp BC_HUGE_ERROR ; 如果行号大于 6,跳转到错误处理
AND R3, R3, #0 ; 有效移动,返回 0
BR BC_DONE
BC_HUGE_ERROR AND R3, R3, #0
ADD R3, R3, #-1 ; 无效移动,返回 -1
BC_DONE RET
BC_NEGA .FILL #-65 ; 填充值 -65
BC_NEGZERO .FILL #-48 ; 填充值 -48
; 常量
COL .STRINGZ " ABCDEFG"
ZERO .STRINGZ "0 "
ONE .STRINGZ "1 "
TWO .STRINGZ "2 "
THREE .STRINGZ "3 "
FOUR .STRINGZ "4 "
FIVE .STRINGZ "5 "
SIX .STRINGZ "6 "
ASCII_OFFSET .FILL x0030
ASCII_NEWLINE .FILL x000A
; 棋盘
ROW0 .STRINGZ "* * * *"
ROW1 .STRINGZ " "
ROW2 .STRINGZ "* * * *"
ROW3 .STRINGZ " "
ROW4 .STRINGZ "* * * *"
ROW5 .STRINGZ " "
ROW6 .STRINGZ "* * * *"
CURRENT_PLAYER .FILL #1; 玩家一先手
SCORE_PLAYER_ONE .FILL #0
SCORE_PLAYER_TWO .FILL #0
; IS_GAME_OVER
; 检查是否有赢家。如果有,输出赢家
; 返回 R3 如果有赢家则为零;如果还没有赢家则为 -1
; IS_GAME_OVER
; 检查是否有赢家。如果有,输出赢家
; 返回 R3 如果有赢家则为零;如果还没有赢家则为 -1
IS_GAME_OVER
ST R0, IGO_R0 ; 保存寄存器
ST R1, IGO_R1
ST R2, IGO_R2
ST R3, IGO_R3
ST R7, IGO_R7
; 检查两个人的总得分是否小于 9
LD R0, SCORE_PLAYER_ONE
LD R1, SCORE_PLAYER_TWO
ADD R2, R0, R1 ; R2 = 玩家1的分数 + 玩家2的分数
ADD R3, R2, #-9 ; R3 = 总分 - 9
BRn IGO_NOT_FINISHED ; 如果总分小于 9,跳转到 IGO_NOT_FINISHED
; 如果总分等于 9,比较分数
NOT R1, R1
ADD R1, R1, #1
ADD R3, R0, R1 ; R3 = 玩家1的分数 - 玩家2的分数
BRz IGO_TIE
BRn IGO_PLAYER_TWO_WIN
IGO_PLAYER_ONE_WIN
LEA R0, PLAYER_ONE_WIN_STRING
PUTS
AND R3, R3, #0
BR IGO_EXIT
IGO_PLAYER_TWO_WIN
LEA R0, PLAYER_TWO_WIN_STRING
PUTS
AND R3, R3, #0
BR IGO_EXIT
IGO_TIE
LEA R0, TIE_STRING
PUTS
AND R3, R3, #0
BR IGO_EXIT
IGO_NOT_FINISHED
AND R3, R3, #0
ADD R3, R3, #-1 ; R3 = -1
IGO_EXIT
LD R0, IGO_R0 ; 恢复寄存器
LD R1, IGO_R1
LD R2, IGO_R2
LD R7, IGO_R7
RET
IGO_R0 .BLKW #1
IGO_R1 .BLKW #1
IGO_R2 .BLKW #1
IGO_R3 .BLKW #1
IGO_R7 .BLKW #1
PLAYER_ONE_WIN_STRING .STRINGZ "Game over. Player 1 is the winner!\n"
PLAYER_TWO_WIN_STRING .STRINGZ "Game over. Player 2 is the winner!\n"
TIE_STRING .STRINGZ "It's a tie!\n"
; FILL_BOX
; 输入 R1 方块中心的列号 (0-6)
; R0 方块中心的行号 (0-6)
; 用当前玩家的编号填充方块
FILL_BOX
ST R0, WPN_R0 ; 保存寄存器
ST R1, WPN_R1
ST R7, WPN_R7
ST R6, WPN_R6 ; 保存 R6 寄存器以防被修改
ST R5, WPN_R5
; 获取当前玩家的编号
LD R6, CURRENT_PLAYER ; 假设 CURRENT_PLAYER 存储当前玩家的编号 ASCII 码
LD R5, ASCII_OFFSET
ADD R6, R6, R5
; 调用 GET_ADDRESS 获取中心位置的地址
JSR GET_ADDRESS
STR R6, R3, #0 ; 将玩家编号写入中心位置
LD R0, WPN_R0 ; 恢复寄存器
LD R1, WPN_R1
LD R7, WPN_R7
LD R5, WPN_R5
LD R6, WPN_R6 ; 恢复 R6 寄存器
RET
WPN_R0 .BLKW #1
WPN_R1 .BLKW #1
WPN_R7 .BLKW #1
WPN_R6 .BLKW #1
WPN_R5 .BLKW #1
CURRENT_PLAYER_PTR .FILL CURRENT_PLAYER ; CURRENT_PLAYER 的地址
; UPDATE_STATE
; 输入 R0 上一步完成的方块数
UPDATE_STATE
ST R0, US_R0 ; 保存寄存器
ST R1, US_R1
ST R6, US_R6
ST R7, US_R7
LD R6, CURRENT_PLAYER ; 加载 CURRENT_PLAYER
ADD R1, R6, #0 ; 玩家值存到R1
ADD R1, R1, #-1 ; 检测是否是玩家1,是的话更新玩家1的分数
BRz US_UPDATE_PLAYER_ONE
; 更新玩家2的分数
LD R7, SCORE_PLAYER_TWO
ADD R7, R7, R0
ST R7, SCORE_PLAYER_TWO
BR US_CHECK_SWITCH
US_UPDATE_PLAYER_ONE
; 更新玩家1的分数
LD R7, SCORE_PLAYER_ONE
ADD R7, R7, R0
ST R7, SCORE_PLAYER_ONE
US_CHECK_SWITCH
; 检查是否没有完成方块
ADD R0, R0, #0
BRp US_EXIT
ADD R0, R0, #0
BRz US_SWITCH_PLAYER
US_SWITCH_PLAYER
ADD R6, R6, #-1 ; 读取当前玩家
BRz SWITCH_TO_PLAYER_2 ; 如果当前玩家是 1,跳转到 SWITCH_TO_PLAYER_2
AND R6, R6, #0
ADD R6, R6, #1
BR US_EXIT
SWITCH_TO_PLAYER_2
AND R6, R6, #0
ADD R6, R6, #2 ; 将当前玩家设置为 2
US_EXIT
ST R6, CURRENT_PLAYER
LD R0, US_R0 ; 恢复寄存器
LD R1, US_R1
LD R6, US_R6
LD R7, US_R7
RET
US_R0 .BLKW 1
US_R1 .BLKW 1
US_R6 .BLKW 1
US_R7 .BLKW 1
; DISPLAY_PROMPT
; 提示玩家输入
DISPLAY_PROMPT
ST R0, DP_R0 ;保存寄存器
ST R7, DP_R7
LD R0, CURRENT_PLAYER ; 加载 CURRENT_PLAYER 的地址指针
ADD R0, R0, #-1 ; 从地址加载当前玩家的值
BRz DP_PLAYER_ONE
LEA R0, PLAYER_TWO_PROMPT
PUTS
BRnzp DP_EXIT
DP_PLAYER_ONE
LEA R0, PLAYER_ONE_PROMPT
PUTS
DP_EXIT
LD R0, DP_R0 ;恢复寄存器
LD R7, DP_R7
RET
DP_R0 .BLKW #1
DP_R7 .BLKW #1
PLAYER_ONE_PROMPT .STRINGZ "Player 1, input a move (or 'Q' to quit): "
PLAYER_TWO_PROMPT .STRINGZ "Player 2, input a move (or 'Q' to quit): "
; GET_ADDRESS
; 输入 R1 列号 (0-6)
; R0 行号 (0-6)
; 返回 R3 数据结构中相应的地址
GET_ADDRESS
ST R0, GA_R0 ; 保存寄存器
ST R1, GA_R1
ST R7, GA_R7
LD R3, ROW0_ADDR ; 读取 ROW0 的基地址
; 计算行的偏移量
ADD R7, R0, R0 ; R7 = R0 * 2
ADD R7, R7, R7 ; R7 = R0 * 4
ADD R7, R7, R7 ; R7 = R0 * 8
; 加上行偏移量
ADD R3, R3, R7
; 加上列偏移量
ADD R3, R3, R1
LD R0, GA_R0 ; 恢复寄存器
LD R1, GA_R1
LD R7, GA_R7
RET
GA_R0 .BLKW #1
GA_R1 .BLKW #1
GA_R7 .BLKW #1
ROW0_ADDR .FILL ROW0 ; ROW0 的地址
; APPLY_MOVE (在适当的位置写入 - 或 |)
; 输入 R1 列号 (0-6)
; R0 行号 (0-6)
APPLY_MOVE
ST R0, AM_R0 ; 保存寄存器
ST R1, AM_R1
ST R7, AM_R7
ST R6, AM_R6 ; 保存 R6 寄存器以防被修改
JSR GET_ADDRESS ; 获取指定位置的地址
; 检查列号以确定写入 '-' 还是 '|'
AND R7, R1, #1 ; 检查列号是否为奇数
BRz WRITE_HYPHEN ; 如果列号为偶数,写 '|'
LD R6, ASCII_HYPHEN ; 如果列号为奇数,写 '-'
STR R6, R3, #0
BR AM_EXIT
WRITE_HYPHEN
LD R6, ASCII_PIPE ; 写 '-'
STR R6, R3, #0
AM_EXIT
LD R0, AM_R0 ; 恢复寄存器
LD R1, AM_R1
LD R7, AM_R7
LD R6, AM_R6 ; 恢复 R6 寄存器
RET
AM_R0 .BLKW #1
AM_R1 .BLKW #1
AM_R7 .BLKW #1
AM_R6 .BLKW #1
ASCII_HYPHEN .FILL x002D ; '-' 的 ASCII 码
ASCII_PIPE .FILL x007C ; '|' 的 ASCII 码
; IS_OCCUPIED (检查位置是否被占用)
; 输入 R1 列号 (0-6)
; R0 行号 (0-6)
; 返回 R3 如果位置未被占用则为零;如果被占用则为 -1
IS_OCCUPIED
ST R0, IO_R0 ; 保存寄存器
ST R1, IO_R1
ST R7, IO_R7
ST R6, IO_R6 ; 保存 R6 寄存器以防被修改
JSR GET_ADDRESS ; 获取指定位置的地址
LDR R6, R3, #0 ; 加载该地址的值到 R6
LD R7, ASCII_SPACE ; 加载空格的 ASCII 码到 R7
NOT R7, R7 ; 取反 R7
ADD R7, R7, #1 ; R7 = -ASCII_SPACE
ADD R6, R6, R7 ; 如果 R6 是空格,则 R6 为零
BRz IO_UNOCCUPIED ; 如果位置未被占用(空格),跳转到 IO_UNOCCUPIED
; 位置被占用
AND R3, R3, #0
ADD R3, R3, #-1
BR IO_EXIT
IO_UNOCCUPIED
; 位置未被占用
AND R3, R3, #0
IO_EXIT
LD R0, IO_R0 ; 恢复寄存器
LD R1, IO_R1
LD R7, IO_R7
LD R6, IO_R6 ; 恢复 R6 寄存器
RET
IO_R0 .BLKW #1
IO_R1 .BLKW #1
IO_R7 .BLKW #1
IO_R6 .BLKW #1
ASCII_SPACE .FILL x0020 ; 空格的 ASCII 码
; TRANSLATE_MOVE 翻译棋步
; 输入 R1 列的ASCII码 ('A'-'G')
; R0 行的ASCII码 ('0'-'6')
; 返回 R1 列号 (0-6)
; R0 行号 (0-6)
TRANSLATE_MOVE
ST R7, TM_R7 ; 保存寄存器
LD R7, ASCII_A
ADD R1, R1, R7 ; 翻译列
LD R7, ASCII_0
ADD R0, R0, R7 ; 翻译行
LD R7, TM_R7 ; 恢复寄存器
RET
TM_R7 .BLKW #1
ASCII_A .FILL #-65 ; -'A'
ASCII_0 .FILL #-48
; IS_INPUT_VALID (检查输入是否有效)
; 输入 R1 列的ASCII字符
; R0 行的ASCII字符
; 返回 R3 如果有效则为零;如果无效则为 -1
IS_INPUT_VALID
ST R0, IIV_R0 ; 保存寄存器
ST R1, IIV_R1
ST R7, IIV_R7
; 检查行的有效性
LD R7, ASCII_1
ADD R3, R0, R7 ; R3 = R0 - '1'
BRz IIV_CHECK_ODD ; 如果 R0 == '1',则检查奇数行
LD R7, ASCII_3
ADD R3, R0, R7 ; R3 = R0 - '3'
BRz IIV_CHECK_ODD ; 如果 R0 == '3',则检查奇数行
LD R7, ASCII_5
ADD R3, R0, R7 ; R3 = R0 - '5'
BRz IIV_CHECK_ODD ; 如果 R0 == '5',则检查奇数行
LD R7, ASCII_0
ADD R3, R0, R7 ; R3 = R0 - '0'
BRz IIV_CHECK_EVEN ; 如果 R0 == '0',则检查偶数行
LD R7, ASCII_2
ADD R3, R0, R7 ; R3 = R0 - '2'
BRz IIV_CHECK_EVEN ; 如果 R0 == '2',则检查偶数行
LD R7, ASCII_4
ADD R3, R0, R7 ; R3 = R0 - '4'
BRz IIV_CHECK_EVEN ; 如果 R0 == '4',则检查偶数行
LD R7, ASCII_6
ADD R3, R0, R7 ; R3 = R0 - '6'
BRz IIV_CHECK_EVEN ; 如果 R0 == '6',则检查偶数行
BR IIV_INVALID ; 否则无效
IIV_CHECK_ODD
; 检查奇数行的列有效性 (A, C, E, G)
LD R7, ASCII_A
ADD R3, R1, R7 ; R3 = R1 - 'A'
BRz IIV_VALID ; 如果 R1 == 'A',则有效
LD R7, ASCII_C
ADD R3, R1, R7 ; R3 = R1 - 'C'
BRz IIV_VALID ; 如果 R1 == 'C',则有效
LD R7, ASCII_E
ADD R3, R1, R7 ; R3 = R1 - 'E'
BRz IIV_VALID ; 如果 R1 == 'E',则有效
LD R7, ASCII_G
ADD R3, R1, R7 ; R3 = R1 - 'G'
BRz IIV_VALID ; 如果 R1 == 'G',则有效
BR IIV_INVALID ; 否则无效
IIV_CHECK_EVEN
; 检查偶数行的列有效性 (B, D, F)
LD R7, ASCII_B
ADD R3, R1, R7 ; R3 = R1 - 'B'
BRz IIV_VALID ; 如果 R1 == 'B',则有效
LD R7, ASCII_D
ADD R3, R1, R7 ; R3 = R1 - 'D'
BRz IIV_VALID ; 如果 R1 == 'D',则有效
LD R7, ASCII_F
ADD R3, R1, R7 ; R3 = R1 - 'F'
BRz IIV_VALID ; 如果 R1 == 'F',则有效
BR IIV_INVALID ; 否则无效
IIV_VALID
; 输入有效
AND R3, R3, #0
BR IIV_EXIT
IIV_INVALID
; 输入无效
AND R3, R3, #0
ADD R3, R3, #-1
IIV_EXIT
LD R0, IIV_R0 ; 恢复寄存器
LD R1, IIV_R1
LD R7, IIV_R7
RET
IIV_R0 .BLKW #1
IIV_R1 .BLKW #1
IIV_R7 .BLKW #1
ASCII_B .FILL #-66 ; -'B'
ASCII_C .FILL #-67 ; -'C'
ASCII_D .FILL #-68 ; -'D'
ASCII_E .FILL #-69 ; -'E'
ASCII_F .FILL #-70 ; -'F'
ASCII_G .FILL #-71 ; -'G'
ASCII_1 .FILL #-49 ; -'1'
ASCII_2 .FILL #-50 ; -'2'
ASCII_3 .FILL #-51 ; -'3'
ASCII_4 .FILL #-52 ; -'4'
ASCII_5 .FILL #-53 ; -'5'
ASCII_6 .FILL #-54 ; -'6'
.END
实验结论或体会
本次实验练习了 LC3 子程序的实现,以及从高级语言迁移到汇编语言的思路。使我更深地理解了 LC-3 指令集,增强了我对计算机底层操作的认知。本次实验debug也是相当耗费时间,检查了三天,这也不断地提醒我保持清晰的思路和给代码配上注释的重要性。
标签:计算机系统,LD,R7,R0,游戏,R3,ADD,深圳大学,R1 From: https://blog.csdn.net/qq_73179413/article/details/140779568