LC-3简易四子棋(深大计系1实验4) 思路+代码+详细注释

明天做LC-3的四子棋实验,但是我想睡懒觉就今天把他做了,这个实验挺难的,要写好久还容易出bug,我把自己的思路和代码分享一下,让大家少走弯路

目录

  • 前置知识
    • 标号
    • 伪操作
    • TRAP伪指令
    • 代码块(函数)调用
  • 题目描述
  • 实现思路
    • 流程
    • 判断胜利思路
    • 矩阵存放与填写
  • 测试结果
  • 代码

前置知识

在做实验前要掌握LC-3的一些伪指令和操作

标号

标号 = 内存地址 (可以这么粗略的理解,编译器建立了名字到内存的映射)
LC-3简易四子棋(深大计系1实验4) 思路+代码+详细注释_第1张图片

伪操作

特性:相当于库函数了,调用这些伪操作,编译器自动帮你做一些工作,标号以.开头,下面给出标号的解释表格

标号名 标号后跟的操作数 解释
.ORIG 起始地址 这个汇编文件的起始地址
.END 程序在此结束
.FILL 数字n 将这个地方用n填充
.STRINGZ 字符串 将这个地方作为字符串首地址,往后填充字符串,自动填充\0

用法:

.ORIG 0x3000	; 程序在0x3000地址开始
.END			; 编译在此停止 但不是停止程序
.FILL #123		; 这个地址将被赋值 123 (十进制)
.STRINGZ "abc"	; 这个地址起始,往后四个地址是 a b c \0

TRAP伪指令

除了伪操作,还可以使用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 ----- 

提示:

  • 参考程序在内存中分配36个空间来表示棋盘的每个位置
  • 最简单的遍历棋盘的方法为:如果该位置为空,则存放0,如果该位置为选手一的棋子,则存放1,如果为选手二的棋子,则存放-1,这样可以最大程度的使用状态码。

在下面文章的代码中,我没有听取上述这个建议,我是用1和2来表示的,所以判断时遇到不少麻烦。。。

  • 记得所有的输入和输出使用ASCII字符,必要时自己转换。
  • 从键盘接收输入时使用TRAP x20 (GETC),显示字符时使用TRAP x20 和TRAP x21
    (OUT),合适的地方使用子例程,每次子例程都应暂存和恢复要使用的寄存器,这样有利于调试程序。
  • 程序首行指定第一条指令的地址,即.ORIG x3000。
  • 最后提交的文件为connect4.asm,根据提交的说明上传程序以便打分。

实现思路

流程

代码首先读取玩家1的输入,判断并输出非法信息直到玩家输入合法为止,然后改变棋盘矩阵的值,接着输出更新后的棋盘,然后判断输赢,如果未分出胜负,则继续读取玩家2的输入,然后做同样的操作,仍然未分出胜负则查看是否棋盘已满,如果未满则继续,已满则平局
LC-3简易四子棋(深大计系1实验4) 思路+代码+详细注释_第2张图片

判断胜利思路

这里我用1来表示玩家1的棋子,用2来表示玩家2的棋子,我们需要判断4种情况:分别是横向四连“”,纵向四连“|”,斜向四连“\”和”/

如图:

  1. 枚举起点(两重循环),然后向下(或者是右方,斜下方)找4个格子
  2. 统计4个格子中,1出现的个数
  3. 如果1出现个数为4,则玩家1获胜
  4. 如果1出现次数为0则玩家2获胜
  5. 否则难分胜负,不做操作

枚举起点的时候注意下标不要越界即可。斜着找的话,LDR指令不是有offset嘛,直接设置就行了,偏移量分别为0,1,2,3 ,或者是 0,-1,-2,-3
LC-3简易四子棋(深大计系1实验4) 思路+代码+详细注释_第3张图片
判断的循环:外循环(行)6次 内循环(列)3次
\ 判断的循环:外循环(行)3次 内循环(列)3次
/ 判断的循环:外循环(行)3次 内循环(列)3次
| 判断的循环:外循环(行)3次 内循环(列)6次

如图:枚举起点的定义
LC-3简易四子棋(深大计系1实验4) 思路+代码+详细注释_第4张图片

矩阵存放与填写

在内存中连续的方式存放矩阵,即开36个空间,然后0-6是第一行,依次类推

题目的输入要求非常怪,是像俄罗斯方块一样,下落式的,指定列数,然后从下往上填对应的行数,所以我们填写的思路也很怪

矩阵填写思路:使用指针的思想

  1. 用一个数组存储指向矩阵最后一行各个列的指针

  2. 然后通过输入的数字作为数组偏移,找到列指针

  3. 将列指针指向的内存改写为玩家标志(1或者2)

  4. 将该指针指向上一行(指针-6即可,指向下一次输入的位置)

重点就是输入的列数作为指针数组的偏移,找到对应列的指针,以及找到之后,指针要指向上一行(即指向下一次输入的位置)

LC-3简易四子棋(深大计系1实验4) 思路+代码+详细注释_第5张图片

测试结果

LC-3简易四子棋(深大计系1实验4) 思路+代码+详细注释_第6张图片
LC-3简易四子棋(深大计系1实验4) 思路+代码+详细注释_第7张图片
LC-3简易四子棋(深大计系1实验4) 思路+代码+详细注释_第8张图片

代码

; @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

你可能感兴趣的:(计算机系统,LC-3,汇编,计算机系统)