1. Propósito del experimento
- Analizar y comprender los problemas asignados a resolver.
- Utilice el código ensamblador de LC-3 para diseñar y realizar programas relacionados.
- 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:
- Los dos jugadores se turnan para jugar;
- Los jugadores no pueden arrepentirse de un movimiento;
- Donde hay un niño, el niño no puede continuar siendo colocado;
- 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;
- 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.
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:
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:
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:
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.
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:
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.
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
Prueba 2: Victoria Vertical
Prueba 3: La dirección diagonal principal gana
Prueba 4: la dirección subdiagonal gana
Prueba 5: Dibujar
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.
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