深圳大学-计算机系统(1)实验-简单游戏设计

实验目的与要求

(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。

实验步骤与过程

  1. 主程序
    主程序首先显示游戏板和提示信息,然后从键盘获取输入字符并回显到屏幕。如果输入字符为 ‘Q’,程序退出;否则继续获取另一个字符并验证输入的合法性和有效性。若移动有效且位置未被占用,则应用该移动,更新游戏状态并重新显示游戏板。主程序循环进行上述操作,直到检测到游戏结束或玩家输入 ‘Q’ 退出游戏。
    在这里插入图片描述
  2. DISPLAY_BOARD实现
    这个子程序用于将游戏板和两名玩家的分数打印到屏幕上。它首先保存当前寄存器状态,然后逐行输出游戏板的内容,包括行号和行数据。接着调用 DISPLAY_SCORE 子程序显示玩家分数,最后恢复寄存器状态并返回。
    在这里插入图片描述
    DISPLAY_SCORE 子程序:
    在这里插入图片描述
  3. GET_ADDRESS实现
    这个子程序根据给定的行号(R0)和列号(R1)计算并返回相应的地址。首先保存寄存器的值,然后从基地址 ROW0_ADDR 开始,通过计算行偏移和列偏移来获得目标地址,最后恢复寄存器的值并返回。
    在这里插入图片描述
  4. FILL_BOX实现
    这个子程序根据当前玩家的编号,填充一个方块。它通过保存和恢复寄存器的值,获取当前玩家的编号,并将编号存储到方块中。
    在这里插入图片描述
  5. BOXES_COMPLETED实现
    这个子程序通过调用 GET_ADDRESS 获取绘制线条的地址,然后根据线条的方向(水平或垂直)分别调用 IS_BOX_COMPLETE 检查相邻的正方形是否完整,如果完整则调用 FILL_BOX 填充该正方形,最后返回完成的正方形数量。
    在这里插入图片描述
    IS_BOX_COMPLETE子程序:
    在这里插入图片描述
    在这里插入图片描述BOUNDS_CHECK:
    在这里插入图片描述
  6. APPLY_MOVE实现
    这个子程序根据传入的列号和行号,在适当的位置写入 - 或 |,表示玩家的棋步。
    在这里插入图片描述
  7. IS_OCCUPIED实现
    这个子程序用于检查给定位置是否被占用,并将结果存储在寄存器 R3 中。如果位置未被占用,寄存器 R3 的值为零;如果位置已被占用,寄存器 R3 的值为 -1。
    在这里插入图片描述
  8. TRANSLATE_MOVE实现
    这个子程序将输入的列和行的 ASCII 码翻译为相应的列号和行号,并将结果存储在寄存器 R1 和 R0 中。
    在这里插入图片描述
  9. IS_INPUT_VALID实现
    这个子程序检查输入的行(R0)和列(R1)的ASCII字符是否有效。思路为: 当行号为0或2或4或6时,列号为B或D或F时,有效;当行号为1或3或5时,列号为A或C或E或G时,有效;其他情况无效。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  10. UPDATE_STATE实现
    这个子程序根据上一步完成的方块数(R0)更新当前玩家的分数。如果当前玩家是玩家1,则更新玩家1的分数;否则更新玩家2的分数。如果没有完成方块,则切换当前玩家。最后,恢复寄存器的值并返回。
    在这里插入图片描述
    在这里插入图片描述
  11. IS_GAME_OVER实现
    这个子程序检查游戏是否已经结束,并输出相应的获胜者或平局信息。如果有获胜者,寄存器 R3 的值为零;如果是平局或游戏尚未结束,寄存器 R3 的值置为 -1。
    在这里插入图片描述
    在这里插入图片描述
  12. DISPLAY_PROMPT实现
    这个子程序用于打印提示信息,提醒玩家输入棋步或退出游戏。根据当前玩家的值,选择性地显示玩家1或玩家2的提示信息。
    在这里插入图片描述
  13. 运行结果
    运行结果图片过多,结果跟以下这两个样例相同即可:
    (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也是相当耗费时间,检查了三天,这也不断地提醒我保持清晰的思路和给代码配上注释的重要性。

  • 13
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值