Diseño de juego simple basado en el simulador LC3: Simple Connect Four

1. Propósito del experimento

  1. Analizar y comprender los problemas asignados a resolver.
  2. Utilice el código ensamblador de LC-3 para diseñar y realizar programas relacionados.
  3. Depure y ejecute programas relacionados a través del emulador LC-3 y obtenga resultados correctos.

2. Contenido experimental

Connect Four es un popular juego de mesa simple. Se dice que el Capitán Garfio se ha estado escondiendo en la residencia durante mucho tiempo debido a su concentración en este juego. Cuando la tripulación descubrió la especialidad del capitán, llamaron a este juego "La amante del capitán".

Connect four es un juego de dos jugadores en el que dos jugadores se turnan para jugar en una cuadrícula de filas y columnas donde cada jugador juega una pieza a la vez hasta que una de las dos piezas se conecta en un cable de línea horizontal, vertical o diagonal.

Este experimento necesita implementar una versión simple de Connect Four en LC-3. Dos jugadores interactúan entre sí a través del teclado y la ventana de salida por turnos. El tablero de ajedrez está compuesto por una cuadrícula de 6 X 6.

Las reglas del juego son las siguientes:

  1. Los dos jugadores se turnan para jugar;
  2. Los jugadores no pueden arrepentirse de un movimiento;
  3. Donde hay un niño, el niño no puede continuar siendo colocado;
  4. Hasta que las cuatro piezas de un lado se puedan conectar en una línea horizontal, una línea vertical o una línea diagonal;
  5. Si el tablero está lleno y nadie gana, hay un empate.

Al comienzo del juego se debe imprimir un tablero vacío, puede utilizar el código ASCII "-" (código ASCII x002D) para indicar que el lugar está vacío, "O" (código ASCII x004F) para indicar la pieza del primer jugador , y "X" (código ASCII x0058) para representar la pieza de ajedrez del segundo jugador. Para que el tablero de ajedrez sea más fácil de observar, agregue un espacio entre cada columna, no agregue después de la sexta columna, el tablero de ajedrez inicial debe ser como sigue:

- - - - - - -
- - -
- - - - -
- - - - - - - -
- - - - - - - - -
- - - - - -

El jugador 1 siempre juega el primer movimiento primero, y luego los dos se turnan para hacer movimientos. Después de cada movimiento, la información del jugador debe imprimirse para recordarle que haga un movimiento. Tomando al jugador 1 como ejemplo, la información impresa debe ser como sigue:

Jugador 1, elige una columna:

Para aclarar la posición del movimiento del jugador, el jugador debe ingresar el número 1-6 y luego presionar Enter, el número 1-6 indica la columna donde se encuentra el movimiento, de izquierda a derecha, no es necesario ingresar el número de línea, el programa debería pasar por defecto de la línea número 6 a Llenar las piezas en orden descendente de la fila número 1. Si los números de columna ingresados ​​antes y después son iguales, el número de fila se reducirá en uno. Por ejemplo, si el jugador coloca una pieza en la segunda columna desde la izquierda por primera vez, debe ingresar 2 y luego presionar Enter, luego la pieza se colocará en la fila 6, columna 2, y cuando el número de columna ingresado más tarde es 2 nuevamente, la pieza se colocará en la columna 2 de la fila 5, y así sucesivamente, vea el siguiente ejemplo de salida para obtener más detalles. El programa debe asegurarse de que el número ingresado por el jugador corresponda al rango de columna correcto, y si la entrada no es razonable, debe aparecer un mensaje de error, indicándole al jugador que continúe escribiendo, por ejemplo, si para el jugador uno:

Jugador 1, elige una columna: D
Movimiento no válido. Intentar otra vez.
Jugador 1, elige una columna: 7
Movimiento no válido. Intentar otra vez.
Jugador 1, elige una columna:

El programa siempre debe solicitar al jugador que sepa el número correcto que debe ingresar, y cuando el usuario completa la entrada, el programa debe dar retroalimentación al jugador a través de la pantalla y luego usar un carácter de nueva línea (código ASCII x000A) para romper la línea.
Cuando un jugador ingresa con éxito, el programa debe imprimir el tablero actualizado y verificar si alguien ha ganado, y si nadie ha ganado, es el turno del siguiente jugador para ingresar.
Cuando uno de ellos gana o empata, el juego termina y el programa muestra la situación final del tablero y termina (Halt). Por ejemplo, si el jugador dos tiene cuatro conectados, debería generar:

El jugador 2 gana.

En caso de empate, el programa debe generar:

Juego de empate.

3. Procedimientos y resultados experimentales

Proceso general (función principal)

El proceso general de mi experimento tentativamente diseñado se muestra en la siguiente figura. El diagrama de flujo es el trabajo de la función principal en el código.
inserte la descripción de la imagen aquí

Representación de matrices de tablero de ajedrez y partes redondas.

Para representar el tablero de ajedrez se utiliza una matriz bidimensional de 6 × 6. La posición en el tablero sin piezas se representa con 0, la posición del jugador 1 se representa con 1 y la posición de la jugada 2 se representa por 1. Los subíndices de matriz comienzan en 0.

Las direcciones de memoria en la computadora son continuas, por lo que es necesario asignar continuamente un espacio de memoria unidimensional de 36 unidades. El funcionamiento específico es el siguiente:

;
;矩阵建立及其初始化
;
	ARRAY	.BLKW	#36

Existe una correspondencia uno a uno entre las coordenadas de la matriz bidimensional y las coordenadas de la matriz unidimensional. Por ejemplo, en la memoria correspondiente a las coordenadas bidimensionales (i, j), la (6 × i + j)ésima posición contada desde la entrada de la matriz. La posición del índice en la memoria de la matriz corresponde a las coordenadas de la matriz bidimensional (índice // 6, índice % 6). Entre ellos, "//" significa divisibilidad y "%" significa tomar el resto.

Debido a que en este experimento, las coordenadas se convierten con frecuencia de unidimensionales a bidimensionales, implementé la función multifunción y la función div. La función de la función multi es multiplicar el parámetro entrante por 6 y devolver, y la función de la función div es devolver el cociente y el resto del parámetro entrante.

Utilice un registro R0 para almacenar el motor actual. Cuando el registro almacena 1, el jugador 1 juega la ronda actual; cuando se almacena -1, juega el jugador 2.

El método de implementación específico de cada módulo en el diagrama de flujo principal se analiza en detalle a continuación, y se adjunta un diagrama de flujo.

Implementar la función de impresión: imprimir la situación actual

Para implementar la función de impresión, solo necesita recorrer los valores en la memoria de la matriz en secuencia. Si el valor del elemento en la matriz es 0, emite "-", si es 1, emite "O", si es -1, emite "X". Cabe señalar que cada resultado de "-" debe ser juzgado. En circunstancias normales, se genera un espacio, pero cuando se requiere un salto de línea, la línea se cambia directamente. El diagrama de flujo es el siguiente:
inserte la descripción de la imagen aquí

Implementar la función isTie: determinar si la situación actual es un empate

Vale la pena señalar que si hay un empate, los 6 elementos en la fila 0 de la matriz del tablero de ajedrez deben ser distintos de cero. También corresponde a las 6 ubicaciones de ARRAY a ARRAY + 5 en la memoria. Por lo tanto, solo es necesario juzgar si las seis posiciones no son 0. Si todos no son 0, la situación es un empate; si no hay ceros, la situación no es un empate. El proceso es el siguiente:
inserte la descripción de la imagen aquí

Implemente la función Reproducir: el jugador ingresa las coordenadas y devuelve la posición de caída real

Las coordenadas ingresadas por el jugador son una ordenada, y esta función necesita encontrar la posición de caída legal de esta columna y devolver sus coordenadas reales (unidimensional). Si la coordenada no existe (la columna está llena o la entrada no es válida), genera un mensaje de error y devuelve -1.

Según la ronda actual, esta función se divide en Play1 y Play2. Su función es la misma.

Idea de algoritmo: suponga que la ordenada de entrada es j (a partir de 0) y la dirección de entrada de la última línea es ARRAY + 30 + j. Luego comience desde esta dirección, reste continuamente 6 (es decir, retroceda una línea), busque la posición con un valor de 0 y devuelva el subíndice si lo encuentra; si no lo ha encontrado, devuelva -1.
El diagrama de flujo es el siguiente:
inserte la descripción de la imagen aquí

Implemente la función isOver: determine si una parte gana

Esta función tiene dos parámetros: índice de coordenadas de movimiento y valor de valor de movimiento. Su significado es, cuando una de las partes cambia el valor de ARRAY[index] a value, ¿se puede distinguir el juego del ganador?

Esta función es el núcleo de este experimento, y se requieren cuatro tipos de juicios: juicio horizontal, juicio vertical, juicio de dirección diagonal principal y juicio de dirección sub-diagonal.

inserte la descripción de la imagen aquí

Para realizar el juicio de la línea de cuatro partes en las cuatro situaciones anteriores, no se considera el algoritmo de fuerza bruta, porque la eficiencia es baja y el diseño de múltiples bucles también es relativamente difícil y propenso a la confusión. Por lo tanto, adopto el siguiente algoritmo optimizado.

Cada vez que cae una pieza de ajedrez, la situación está destinada a cambiar, y puede haber una nueva situación en la que cuatro piezas formen una línea. Puede que no haya tal situación, pero si la hay, este nuevo movimiento definitivamente se convertirá en uno de los cuatro en la línea. Luego tomamos esta nueva pieza de ajedrez como el centro y la expandimos en las siete direcciones que se muestran en la siguiente figura:

inserte la descripción de la imagen aquí
Por ejemplo, para determinar si cuatro piezas forman una línea en dirección horizontal, es necesario contar el número de piezas idénticas consecutivas en el lado izquierdo del punto de caída. Como se muestra en la figura a continuación, la pieza de ajedrez circular roja es una nueva pieza de ajedrez, por lo que al hacer un juicio horizontal, puede usar esta pieza de ajedrez como el centro y extenderla hacia la izquierda y hacia la derecha. Hay dos piezas de ajedrez idénticas consecutivas en su lado izquierdo y una pieza de ajedrez idéntica consecutiva en su lado derecho, más ella misma, solo cuatro piezas forman una línea, por lo que se considera que Yuanfang gana.
inserte la descripción de la imagen aquí
Los juicios de la dirección vertical, la dirección diagonal principal y la dirección subdiagonal son similares a esto. El algoritmo diseñado de esta manera es más eficiente y más conciso en el proceso de codificación e implementación.

Lo anterior es la idea realizada por este experimento. La siguiente sección probará la corrección y solidez de este programa desde varios ángulos.

Prueba 1: Victoria Horizontal

inserte la descripción de la imagen aquí

Prueba 2: Victoria Vertical

inserte la descripción de la imagen aquí

Prueba 3: La dirección diagonal principal gana

inserte la descripción de la imagen aquí

Prueba 4: la dirección subdiagonal gana

inserte la descripción de la imagen aquí

Prueba 5: Dibujar

inserte la descripción de la imagen aquí

Prueba 6: entrada ilegal

La entrada ilegal con desbordamiento superior se prueba en la imagen de la izquierda y la entrada incorrecta con caracteres ilegales se prueba en la imagen de la derecha. Para estos dos tipos de entrada incorrecta, el programa muestra un mensaje de error y le recuerda al jugador que vuelva a ingresar. Muestra que la robustez del programa es buena.
inserte la descripción de la imagen aquí

4. Código completo

A continuación se muestra el código completo de Connect Four LC-3.

;
; author: Cao-Yixuan 2019282129
; date:2021.6.8
; function: a game called Connect Four
;
;
    .ORIG x3000
;
	JSR	main
;
;函数:打印矩阵
;
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
;
;R1 = 36,控制循环
;R2 = 6,控制换行
;R3 = ARRAY
;
		LEA	R3	ARRAY
		AND	R1	R1	#0
		ADD	R2	R1	#6
		ADD	R1	R1	#15
		ADD	R1	R1	#15
		ADD	R1	R1	R2
;
PRINT_LOOP	LDR	R0	R3	#0
			BRp	PRINT_1_CH
			BRn	PRINT_2_CH
			LD	R0	BLANK
			BRnzp	PRINT_CH
PRINT_1_CH	LD	R0	P_1_CH
			BRnzp	PRINT_CH
PRINT_2_CH	LD	R0	P_2_CH
			BRnzp	PRINT_CH	
PRINT_CH	TRAP	x21
			ADD	R3	R3	#1
			ADD	R2	R2	#-1
			BRp	PRINT_BLANK
			ADD	R2	R2	#6
			LD	R0	ENDLINE
			TRAP	x21
			BRnzp	PRINT_L_END
PRINT_BLANK	LD	R0	BLANK1
			TRAP	x21
PRINT_L_END	ADD	R1	R1	#-1
			BRp	PRINT_LOOP
;			
;
;
		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
;

	P_1_CH	.FILL	x004F
	P_2_CH	.FILL	x0058
	ENDLINE .FILL	x0D
	BLANK	.FILL	x002D
	BLANK1	.FILL	x20

;
;函数:PLAY1落子
;输入:TEMP0
;输出:TEMP0
;如果落子失败,R6处返回-1
;如果落子成功,R6处返回落子位置的下标
;

play1	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
;
;初始化返回值R6 = -1
;
		AND	R6	R6	#0
		ADD	R6	R6	#-1
;
;边界检查
;
		LD	R0	TEMP0		;传入参数j
		ADD	R1	R0	#-6
		BRzp	P_1_ERROR
		ADD	R1	R0	#0
		BRn		P_1_ERROR
;
;循环
;R2 = ARRAY+30+j,入口
;R3 = ARRAY+j,出口
;		
			LEA	R2	ARRAY
			ADD	R3	R2	R0
			ADD	R2	R3	#15
			ADD	R2	R2	#15
			NOT	R0	R3
			ADD	R0	R0	#1
P_1_LOOP	LDR	R3	R2	#0
			ADD	R3	R3	#0
			BRz	P_1_RIGHT
			ADD	R2	R2	#-6
			ADD	R4	R2	R0
			BRzp	P_1_LOOP
P_1_ERROR	LEA	R0	ERROR
			TRAP	x22
			BRnzp	PLAY_1_END
P_1_RIGHT	AND	R6	R2	R2
			AND	R4	R4	#0
			ADD	R4	R4	#1
			STR	R4	R6	#0
			LEA	R7	ARRAY
			NOT	R7	R7
			ADD	R7	R7	#1
			ADD	R6	R6	R7
			BRnzp	PLAY_1_END
PLAY_1_END	ST	R6	TEMP0		; 输出
			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


;
;函数:PLAY2落子
;输入:TEMP0
;输出:TEMP0
;如果落子失败,R6处返回-1
;如果落子成功,R6处返回落子位置的下标
;

play2	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

;
;初始化返回值R6 = -1
;
		AND	R6	R6	#0
		ADD	R6	R6	#-1
;
;边界检查
;
		LD	R0	TEMP0		;传入参数j
		ADD	R1	R0	#-6
		BRzp	P_2_ERROR
		ADD	R1	R0	#0
		BRn		P_2_ERROR
;
;循环
;R2 = ARRAY + 30 + j, 作为入口
;R3 = ARRAY + j, 作为出口
;		
			LEA	R2	ARRAY
			ADD	R3	R2	R0
			ADD	R2	R3	#15
			ADD	R2	R2	#15

			NOT	R0	R3
			ADD	R0	R0	#1
P_2_LOOP	LDR	R3	R2	#0
			ADD	R3	R3	#0
			BRz	P_2_RIGHT
			ADD	R2	R2	#-6
			ADD	R4	R2	R0
			BRzp	P_2_LOOP

P_2_ERROR	LEA	R0	ERROR
			TRAP	x22
			BRnzp	PLAY_2_END

P_2_RIGHT	AND	R6	R2	R2
			AND	R4	R4	#0
			ADD	R4	R4	#-1
			STR	R4	R6	#0
			LEA	R7	ARRAY
			NOT	R7	R7
			ADD	R7	R7	#1
			ADD	R6	R6	R7
			BRnzp	PLAY_2_END
PLAY_2_END	ST	R6	TEMP0		; 输出
			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

ERROR	.STRINGZ    "Invalid move. Try again.\n"

;
;函数:判断是否是平局
;output:TEMP1
;

isTie		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

			LEA	R0	ARRAY
			AND	R1	R1	#0		;index = 5 to 0
			ADD	R1	R1	#5
			AND	R2	R2	#0		;返回0
;
TIE_LOOP	ADD	R4	R0	R1
			LDR	R3	R4	#0
			ADD	R3	R3	#0
			BRz	TIE_RET
			ADD	R1	R1	#-1
			BRzp	TIE_LOOP

			ADD	R2	R2	#1		;返回1
TIE_RET		ST	R2	TEMP1

			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

;
;矩阵建立及其初始化
;
	ARRAY	.BLKW	#36

;
;保存寄存器
;
	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
;
;临时存储用寄存器
;

	TEMP0	.FILL	#0
	TEMP1	.FILL	#0
	TEMP2	.FILL	#0
	TEMP3	.FILL	#0
	TEMP4	.FILL	#0
	TEMP5	.FILL	#0
	TEMP6	.FILL	#0
	TEMP7	.FILL	#0
	TEMP8	.FILL	#0
	TEMP9	.FILL	#0


;
;The key function: is it over?
;if return 1, play1 wins
;if return -1, play2 wins
;if return 0, it is not over
;INPUT1: TEMP0, an index of the array
;INPUT2: TEMP1, the value of array[TEMP0] 
;OUTPUT: TEMP0, a bool value
;

isOver		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
;
;行判断
;
JUDGE1		LD	R0	TEMP0		;R0 = index
			LD	R1	TEMP1		;R1 = -value
			NOT	R1	R1
			ADD	R1	R1	#1
			JSR	div
			LD	R2	TEMP1		;R2 = i
			LD	R3	TEMP2		;R3 = j
			AND	R4	R4	#0		;R4 = cnt
			LEA	R5	ARRAY		;R5 元素指针,从行首开始
			ADD	R5	R5	R0
			NOT	R6	R3
			ADD	R6	R6	#1
			ADD	R5	R5	R6
			ST	R5	TEMP3		;行首指针存起来

;
;Loop: jump-tp-middle
;
			BRnzp	J_MIDD_1
J_LOOP_1	LDR	R6	R5	#0
			ADD	R6	R6	R1
			BRnp	J_NOT_1
			ADD	R4	R4	#1
			ADD	R5	R5	#1
			ADD	R6	R4	#-4
			BRz	J_RET_TRUE
			BRnzp	J_MIDD_1
J_NOT_1		AND	R4	R4	#0
			ADD	R5	R5	#1
J_MIDD_1	LD	R6	TEMP3
			ADD	R6	R6	#5
			NOT	R6	R6
			ADD	R6	R6	#1
			ADD	R6	R6	R5
			BRn	J_LOOP_1
			
;
;列判断
;
JUDGE2		ADD	R4	R0	#-16
			ADD	R4	R4	#-2
			BRzp	JUDGE3
			LEA	R5	ARRAY
			ADD	R5	R5	R0
			LDR	R6	R5	#6	
			ADD	R6	R6	R1
			BRnp	JUDGE3
			LDR	R6	R5	#12	
			ADD	R6	R6	R1
			BRnp	JUDGE3
			LDR	R6	R5	#18
			ADD	R6	R6	R1
			BRnp	JUDGE3
			
			BRnzp	J_RET_TRUE

;
;主对角线方向判断
;

JUDGE3		AND	R4	R4	#0
			ADD	R4	R4	#1		;cnt=1
			ST	R2	TEMP3		;存储i
			ST	R3	TEMP4		;存储j
J_3_LOOP_1	ADD	R2	R2	#-1
			BRn	J_3_END_1
			ADD	R3	R3	#-1
			BRn	J_3_END_1
			ST	R2	TEMP0
			JSR	multi
			LD	R5	TEMP1
			ADD	R5	R5	R3
			LEA	R6	ARRAY
			ADD	R5	R5	R6
			LDR	R5	R5	#0
			ADD	R5	R5	R1
			BRnp	J_3_END_1
			ADD	R4	R4	#1
			BRnzp	J_3_LOOP_1
J_3_END_1	LD	R2	TEMP3
			LD	R3	TEMP4
J_3_LOOP_2	ADD	R2	R2	#1
			ADD	R5	R2	#-6
			BRzp	J_3_END_2
			ADD	R3	R3	#1
			ADD	R5	R3	#-6
			BRzp	J_3_END_2
			ST	R2	TEMP0
			JSR	multi
			LD	R5	TEMP1
			ADD	R5	R5	R3
			LEA	R6	ARRAY
			ADD	R5	R5	R6
			LDR	R5	R5	#0
			ADD	R5	R5	R1
			BRnp J_3_END_2
			ADD	R4	R4	#1
			BRnzp	J_3_LOOP_2
J_3_END_2	ADD	R4	R4	#-4
			BRzp	J_RET_TRUE
;
;副对角线方向判断
;

JUDGE4		AND	R4	R4	#0
			ADD	R4	R4	#1			;cnt=1
			LD	R2	TEMP3
			LD	R3	TEMP4
J_4_LOOP_1	ADD	R2	R2	#-1
			BRn	J_4_END_1
			ADD	R3	R3	#1
			ADD	R5	R3	#-6
			BRzp	J_4_END_1
			ST	R2	TEMP0
			JSR	multi
			LD	R5	TEMP1
			ADD	R5	R5	R3
			LEA	R6	ARRAY
			ADD	R5	R5	R6
			LDR	R5	R5	#0
			ADD	R5	R5	R1
			BRnp	J_4_END_1
			ADD	R4	R4	#1
			BRnzp	J_4_LOOP_1
J_4_END_1	LD	R2	TEMP3
			LD	R3	TEMP4
J_4_LOOP_2	ADD	R2	R2	#1
			ADD	R5	R2	#-6
			BRzp	J_4_END_2
			ADD	R3	R3	#-1
			BRn	J_4_END_2
			ST	R2	TEMP0
			JSR	multi
			LD	R5	TEMP1
			ADD	R5	R5	R3
			LEA	R6	ARRAY
			ADD	R5	R5	R6
			LDR	R5	R5	#0
			ADD	R5	R5	R1
			BRnp J_4_END_2
			ADD	R4	R4	#1
			BRnzp	J_4_LOOP_2
J_4_END_2	ADD	R4	R4	#-4
			BRzp	J_RET_TRUE
			AND	R1	R1	#0
			ST	R1	TEMP0
			BRnzp	J_RET_FALSE
J_RET_TRUE	NOT	R1	R1
			ADD	R1	R1	#1		;恢复value, return
			ST	R1	TEMP0
J_RET_FALSE	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


;
;----------------------------------------------
;函数: 实现对6执行乘法的结果
;input: TEMP0
;output: TEMP1
;这里,单独设计一群寄存器保存multi和div函数,
;防止函数在嵌套时出现内存被篡改的现象
;
SAVE_R0_	.FILL	#0
SAVE_R1_	.FILL	#0
SAVE_R2_	.FILL	#0
;
multi		ST	R0	SAVE_R0_	;被调用者保存
			ST	R1	SAVE_R1_
			ST	R2	SAVE_R2_
			LD	R0	TEMP0
			AND	R1	R1	#0		;返回值
			BRnzp	MUL_MIDDLE
MUL_LOOP	ADD	R1	R1	#6
			ADD	R0	R0	#-1					
MUL_MIDDLE	ADD	R0	R0	#0
			BRp	MUL_LOOP
			ST	R1	TEMP1		; 函数返回值
			LD	R0	SAVE_R0_	; 被调用者恢复
			LD	R1	SAVE_R1_
			LD	R2	SAVE_R2_
			RET

;
;函数:除法,获得非负数对6的积和余数
;input: TEMP0
;output: TEMP1, TEMP2
;

div			ST	R0	SAVE_R0_	; 被调用者保存
			ST	R1	SAVE_R1_
			ST	R2	SAVE_R2_
			LD	R0	TEMP0		;余数
			AND	R1	R1	#0		;积
DIV_LOOP	ADD	R2	R0	#-6
			BRn	DIV_END
			ADD	R1	R1	#1
			AND	R0	R2	R2
			BRp	DIV_LOOP
DIV_END		ST	R1	TEMP1
			ST	R0	TEMP2
			LD	R0	SAVE_R0_	; 被调用者恢复
			LD	R1	SAVE_R1_
			LD	R2	SAVE_R2_
			RET

ENDLINE1	.FILL	0x0D
;
;main function
;
main		AND	R1	R1	#0
			ADD	R1	R1	#1		;落子方标记,1=player1,-1=player2
MAIN_LOOP	JSR	print
			JSR isTie
			LD	R2	TEMP1		;R2用于胜负判断
			ADD	R2	R2	#0
			BRnp	tie
			ADD	R1	R1	#0
			BRn	P2_DO
P1_DO		LEA	R0	PLAY_1
			TRAP	x22
			BRnzp	MAIN_JUDGE
P2_DO		LEA	R0	PLAY_2
			TRAP	x22
MAIN_JUDGE	TRAP x20
			TRAP x21
			ADD R0	R0	#-16
			ADD R0	R0	#-16
			ADD R0	R0	#-16	;index = ascii_code - 48
			ADD	R3	R0	#-1		;index in function is from 0 to 5 in every line
			LD	R0	ENDLINE1
			TRAP	x21
			ST	R3	TEMP0
			ST	R1	TEMP1		;we need a input
			ADD	R1	R1	#0
			BRn	P2_DO_1
P1_DO_1		JSR	play1
			BRnzp	P_DO_END1
P2_DO_1		JSR	play2
P_DO_END1	LD	R3	TEMP0
			ADD	R3	R3	#0
			BRzp	MAIN_RIGHT
MAIN_ERROR	;LEA	R0	ERROR
			;TRAP	x22
			BRnzp	MAIN_LOOP
MAIN_RIGHT	ST	R1	TEMP1
			ST	R3	TEMP0
			JSR	isOver
			LD	R4	TEMP0
			ADD	R4	R4	#0
			BRnp	GAME_OVER
			NOT	R1	R1
			ADD	R1	R1	#1
			BRnzp	MAIN_LOOP
GAME_OVER	JSR	print
			ADD	R1	R1	#0
			BRp	p1_win
			BRn	p2_win
;
;三种结局,最终分别调用这三种函数
;
tie			LEA R0	TIE
			TRAP	x22
			HALT	
p1_win		LEA R0	P_1_WIN
			TRAP	x22
			HALT
p2_win		LEA R0	P_2_WIN
			TRAP	x22
			HALT

;
;常用字符串和常量
;
    PLAY_1  .STRINGZ    "Player 1, choose a column: "
    PLAY_2  .STRINGZ    "Player 2, choose a column: "
    P_1_WIN .STRINGZ    "Player 1 Wins.\n"
    P_2_WIN .STRINGZ    "Player 2 Wins.\n"
	TIE		.STRINGZ	"Tie Game.\n"

.END

Supongo que te gusta

Origin blog.csdn.net/weixin_46655675/article/details/130810126
Recomendado
Clasificación