明天做LC-3的四子棋实验,但是我想睡懒觉就今天把他做了,这个实验挺难的,要写好久还容易出bug,我把自己的思路和代码分享一下,让大家少走弯路
在做实验前要掌握LC-3的一些伪指令和操作
标号 = 内存地址 (可以这么粗略的理解,编译器建立了名字到内存的映射)
特性:相当于库函数了,调用这些伪操作,编译器自动帮你做一些工作,标号以.
开头,下面给出标号的解释表格
标号名 | 标号后跟的操作数 | 解释 |
---|---|---|
.ORIG | 起始地址 | 这个汇编文件的起始地址 |
.END | 无 | 程序在此结束 |
.FILL | 数字n | 将这个地方用n填充 |
.STRINGZ | 字符串 | 将这个地方作为字符串首地址,往后填充字符串,自动填充\0 |
用法:
.ORIG 0x3000 ; 程序在0x3000地址开始
.END ; 编译在此停止 但不是停止程序
.FILL #123 ; 这个地址将被赋值 123 (十进制)
.STRINGZ "abc" ; 这个地址起始,往后四个地址是 a b c \0
除了伪操作,还可以使用TRAP x__
的语句,实现系统的调用
值得注意的是,输入输出都要用到R0寄存器,所以保存寄存器R0的值,再用R0输出信息,是非常关键的,也是导致输出错误的一大原因
指令号 | 解释 |
---|---|
TRAP x25 | 程序exit |
TRAP x23 | 打印输入提示信息并且接受用户的一个输入字符(ASCII码形式 )到R0低8位 |
TRAP x21 | 将R0中低8位的值以ASCII码形式打印到屏幕上 |
TRAP x20 | 不带输入提示信息的输入读取 和 TRAP x23 类似 |
TRAP x22 | 将R0中的值作为字符串首地址 连续输出字符串直到遇到0 |
LC-3提供非常简陋的方式调用代码块(尚不能称之为函数。。。
因为所有代码共享8个寄存器,所以调用代码块之前记得保存寄存器的值
不要试图在一个 带RET的 函数中调用另外的函数,可能会引发意想不到的bug,比如死机(是的我死机过
func ADD R0, R0, #1 ; 这个函数叫func 功能是R0++
RET
...... 其他代码 ......
JSR func ; JSR + 标号即可调用(跳转到标号然后再跳转回来)
四子棋是一款普遍流行的简易型桌面游戏,据说,虎克船长曾因专注于此游戏而长期隐身在住所,当船员们发现船长的这一专长之后,他们称这个游戏为“船长的情妇”。
四子棋是个双人游戏,两人轮流下棋,棋盘由行和列组成的网格,每个选手每次下一个子直到两人中有一人的棋子连成一条水平线、垂直线或者是对角线。
本实验需要在LC-3中实现简易版四子棋的游戏,两位选手通过键盘和输出窗口轮流交互操作,棋盘由6 X 6的网格组成。
游戏规则如下:
两位选手依次轮流落子;
选手不能悔棋;
有子的地方不能继续落子;
直到有一方的四个棋子能够连成一条水平线、垂直线或者是对角线;
如果棋盘已满,无人获胜,则平局。
游戏最初时应该打印空的棋盘,可以用ASCII码"-" (即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.
示例输出
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- - - - - -
Player 1, choose a column: 1
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- - - - - -
O - - - - -
Player 2, choose a column: 2
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- - - - - -
O X - - - -
Player 1, choose a column: 2
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- O - - - -
O X - - - -
Player 2, choose a column: 3
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- O - - - -
O X X - - -
Player 1, choose a column: 3
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- O O - - -
O X X - - -
Player 2, choose a column: 1
- - - - - -
- - - - - -
- - - - - -
- - - - - -
X O O - - -
O X X - - -
Player 1, choose a column: 4
- - - - - -
- - - - - -
- - - - - -
- - - - - -
X O O - - -
O X X O - -
Player 2, choose a column: 4
- - - - - -
- - - - - -
- - - - - -
- - - - - -
X O O X - -
O X X O - -
Player 1, choose a column: 3
- - - - - -
- - - - - -
- - - - - -
- - O - - -
X O O X - -
O X X O - -
Player 2, choose a column: 4
- - - - - -
- - - - - -
- - - - - -
- - O X - -
X O O X - -
O X X O - -
Player 1, choose a column: 4
- - - - - -
- - - - - -
- - - O - -
- - O X - -
X O O X - -
O X X O - -
Player 1 Wins.
----- Halting the processor -----
提示:
在下面文章的代码中,我没有听取上述这个建议,我是用1和2来表示的,所以判断时遇到不少麻烦。。。
代码首先读取玩家1的输入,判断并输出非法信息直到玩家输入合法为止,然后改变棋盘矩阵的值,接着输出更新后的棋盘,然后判断输赢,如果未分出胜负,则继续读取玩家2的输入,然后做同样的操作,仍然未分出胜负则查看是否棋盘已满,如果未满则继续,已满则平局
这里我用1来表示玩家1的棋子,用2来表示玩家2的棋子,我们需要判断4种情况:分别是横向四连“—
”,纵向四连“|
”,斜向四连“\
”和”/
”
如图:
枚举起点的时候注意下标不要越界即可。斜着找的话,LDR指令不是有offset嘛,直接设置就行了,偏移量分别为0,1,2,3 ,或者是 0,-1,-2,-3
—
判断的循环:外循环(行)6次 内循环(列)3次
\
判断的循环:外循环(行)3次 内循环(列)3次
/
判断的循环:外循环(行)3次 内循环(列)3次
|
判断的循环:外循环(行)3次 内循环(列)6次
在内存中连续的方式存放矩阵,即开36个空间,然后0-6是第一行,依次类推
题目的输入要求非常怪,是像俄罗斯方块一样,下落式的,指定列数,然后从下往上填对应的行数,所以我们填写的思路也很怪
矩阵填写思路:使用指针的思想
用一个数组存储指向矩阵最后一行各个列的指针
然后通过输入的数字作为数组偏移,找到列指针
将列指针指向的内存改写为玩家标志(1或者2)
将该指针指向上一行(指针-6即可,指向下一次输入的位置)
重点就是输入的列数作为指针数组的偏移,找到对应列的指针,以及找到之后,指针要指向上一行(即指向下一次输入的位置)
; @program : 简易四子棋游戏LC-3
; @author : 李若龙 2018171028
; @last modify : 2020/5/26 18:44
; @explain : 用分割线----划分不同代码块
.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