Mi primer programa en ensamblador Z80

Estaba limpiando un área de trabajo antigua cuando vi mi viejo cuaderno azul que usé de 1986 a 1989. Había sufrido el paso del tiempo y polvo.
Mi viejo cuaderno de 1988
Mi viejo cuaderno de 1988
Pensé que era el mismo cuaderno que había visto antes, porque resulta que tengo dos cuadernos azules del mismo modelo, pero perdí uno. Para mi sorpresa, descubrí que era el segundo cuaderno perdido ¡Donde puse mi primer programa en ensamblador Z80!
Muy contento, de inmediato tomé fotos de las páginas importantes, en caso de que perdiera el cuaderno de nuevo.
Y los recuerdos comenzaron a volver lentamente...

La historia

Mi juego Karateka: Pantalla de título
Tenía 9 años y quería escribir juegos sorprendentes. Por supuesto, era demasiado niño para crear algo al nivel de los juegos que veía en las revistas como Compute! o Input MSX.
Después de cuatro años de escribir programas en lenguaje BASIC, sentí que me detenía la lentitud inherente de un lenguaje interpretado.
Este juego fue escrito en junio de 1988, mi padre daba una clase de programación del procesador VDP TMS9128. Este procesador de video tenía amplia disponibilidad en ese momento (via Digikey) debido al crash de las computadoras personales.
Mis notas sobre la estructura de los 16K de VRAM del VDP
Mis notas sobre la estructura de los 16k de VRAM del VDP. Note los números de registros.
Los estudiantes habían estado soldando sus computadoras basadas en Z80 sobre un tablero de cobre perforado con agujeros espaciados por 2.54mm.
Desafortunadamente mis manos no eran precisas para sostener un cautín de soldar, así que había estado observando aburrido como los alumnos disfrutaban construir sus computadoras, como lograban hacerlas funcionar, y como solucionaban los bugs.
Mi juego Karateka: Dentro del juego
Esto me concedió toneladas de tiempo para pensar acerca del concepto de programación del ensamblador Z80. Había sido incapaz de atrapar el concepto de como poner juntas las instrucciones. Las semanas previas antes de ese domingo (puedo recordar que era domingo), mientras observaba a mi padre poner el código para la EPROM de monitor/VDP/teclado para la computadora estudiantil, finalmente se me prendió el foco. ¡Por supuesto! Varias instrucciones componen una sentencia de lenguaje BASIC, así que era una cuestión de replicar lo mismo en ensamblador.
Esa mañana de domingo antes de la clase, comencé a escribir furiosamente sobre mi cuaderno un juego de un karateka que luchaba contra una mujer que lanzaba cuchillos. Tenía la idea completa en mi cabeza, ahora me doy cuenta por primera vez de que tenía un algoritmo en BASIC muy claro en mi mente. Era solo cuestión de trasladarlo a ensamblador. Me había dado cuenta de que la separación de líneas del lenguaje BASIC era invisible en ensamblador, pero cada dirección podía relacionarse con un número imaginario de línea del lenguaje BASIC.
Puedes imaginar la sorpresa de toda la clase cuando les mostré orgullosamente mi juego. Recuerdo que tres alumnos copiaron manualmente el código de un vaciado de la RAM y después hicieron copias fotostáticas para otros estudiantes.

Haciendolo funcionar en 2021

Aquí están las fotos de mi cuaderno. Tendrás que perdonar las horribles letras de niño. Apenas algunos comentarios aquí y allá.
Mientras comentaba cada página (¡y analizaba a mi yo de 9 años!), trasladé este código a tniASM v0.44 para MSX y Colecovision (solo porque sí). Se requiere la siguiente capa de apoyo y traducción porque no disponemos de la ROM original de la computadora estudiantil:

	;
	; Karateka
	;
	; por Oscar Toledo G.
	; (c) Copyright Oscar Toledo G. 1988-2021
	; https://nanochess.org/
	;
	; Creación: Jun/1988. Tenía 9 años.
	; Revisión: May/12/2021. Portado a MSX/Colecovision.
	;

COLECO: EQU 0   ; Defina esto a 0 para MSX, 1 para Colecovision

RAM_BASE:	EQU $E000-$7000*COLECO
VDP:		EQU $98+$26*COLECO

KEYSEL:	EQU $80
JOYSEL:	EQU $C0
JOY1:	EQU $FC
JOY2:	EQU $FF

    if COLECO
	fname "KARATECV.ROM"

	org $8000,$9fff

	dw $aa55	; Sin pantalla de título de BIOS
	dw 0
	dw 0
	dw 0
	dw 0
	dw START

	jp 0		; RST $08
	jp 0		; RST $10
	jp 0		; RST $18
	jp 0		; RST $20
	jp 0		; RST $28
	jp 0		; RST $30
	jp 0		; RST $38

	jp 0		; Sin manejo de NMI

    else
	fname "KARATMSX.ROM"

	org $4000,$5fff

	dw $4241
	dw START
	dw 0
	dw 0
	dw 0
	dw 0
	dw 0
	dw 0

SNSMAT:	equ $0141

    endif

WRTVDP:
	ld a,b
	out (VDP+1),a
	ld a,c
	or $80
	out (VDP+1),a
	ret

SETWRT:
	ld a,l
	out (VDP+1),a
	ld a,h
	or $40
	out (VDP+1),a
	ret

WRTVRM:
	push af
	call SETWRT
	pop af
	out (VDP),a
	ret

FILVRM:
	push af
	call SETWRT
.1:	pop af
	out (VDP),a
	push af
	dec bc
	ld a,b
	or c
	jp nz,.1
	pop af
	ret

	; Configura el VDP antes del juego
setup_vdp:
	LD BC,$0200
	CALL WRTVDP
	LD BC,$C201	; Sin interrupciones
	CALL WRTVDP
	LD BC,$0F02	; $3C00 para tabla de caracteres
	CALL WRTVDP
	LD BC,$FF03	; $2000 para tabla de color
	CALL WRTVDP
	LD BC,$0304	; $0000 para tabla de bitmaps
	CALL WRTVDP
	LD BC,$3605	; $1b00 para tabla de atributos de sprites
	CALL WRTVDP
	LD BC,$0706	; $3800 para bitmaps de sprites
	CALL WRTVDP
	LD BC,$0407	; Borde azul
	CALL WRTVDP
    IF COLECO
	LD HL,($006C)   ; Caracteres BIOS
	LD DE,-128
	ADD HL,DE
    ELSE
	LD HL,($0004)   ; Caracteres BIOS
	INC H
    ENDIF
        PUSH HL
        LD DE,$0100
        LD BC,$0300
        CALL LDIRVM
        POP HL
        PUSH HL
        LD DE,$0900
        LD BC,$0300
        CALL LDIRVM
        POP HL
        LD DE,$1100
        LD BC,$0300
        CALL LDIRVM

        LD HL,$2000
        LD BC,$1800
        LD A,$F4
        CALL FILVRM
	RET

LDIRVM:
        EX DE,HL
.1:     LD A,(DE)
        CALL WRTVRM
        INC DE
        INC HL
        DEC BC
        LD A,B
        OR C
        JR NZ,.1
        RET

GTTRIG:
    if COLECO
        out (KEYSEL),a
        ex (sp),hl
        ex (sp),hl
        in a,(JOY1)
        ld c,a
        in a,(JOY2)
        and c
        ld c,a
	out (JOYSEL),a
	ex (sp),hl
	ex (sp),hl
	in a,(JOY1)
        and c
        ld c,a
	in a,(JOY2)
	and c
	rlca
	rlca
	ccf
	ld a,0
	sbc a,a
	ret
    else
	xor a
	call $00d8
	or a
	ret nz
	ld a,1
	call $00d8
	or a
	ret nz
        ld a,2
        call $00d8
        or a
        ret nz
        ld a,3
        call $00d8
        or a
        ret nz
        ld a,4
        call $00d8
        ret
    endif

	;
	; Obtiene la dirección del joystick
	; 0 - Sin movimiento
	; 1 - Arriba
	; 2 - Arriba + derecha
	; 3 - Derecha
	; 4 - Derecha + abajo
	; 5 - Abajo
	; 6 - Abajo + izquierda
	; 7 - Izquierda
	; 8 - Izquierda + arriba
	;
GTSTCK:
    if COLECO
        out (JOYSEL),a
	ex (sp),hl
	ex (sp),hl
        in a,(JOY1)
	ld b,a
	in a,(JOY2)
	and b
        and $0f
        ld c,a
        ld b,0
        ld hl,joy_map
        add hl,bc
        ld a,(hl)
        ret

joy_map:
        db 0,0,0,6,0,0,8,7,0,4,0,5,2,3,1,0

    else
	xor a
	call $00d5
	or a
	ret nz
	ld a,1
	call $00d5
	or a
	ret nz
	ld a,2
	jp $00d5
    endif

	; Rutinas ROM olvidadas

	; Limpia la pantalla
LIMPIA:	; $04cc
	LD HL,$3C00
	LD BC,$0300
	XOR A
	JP FILVRM

	; Copia cadena al VDP
INMEDIATO:	; $0169
	EX (SP),HL

.0:	LD A,(HL)
	INC HL
	OR A
	JR Z,.1
	PUSH AF
	POP AF
	OUT (VDP),A
	JR .0

.1:	EX (SP),HL
	RET

	;
	; Inicio del juego
	;
START:		; 8000
	DI
	LD SP,RAM_BASE+256
Cada trozo extra de código será agregado al final del código previo.
Página 1 de mi juego Z80
Página 1 de mi juego Z80, click derecho para mayor resolución.
Esta es la página 1 de mi juego Z80. El código fuente a la derecha fue escrito pensando en ponerlo dentro de un programa ensamblador que iba a programar (imagina eso, un niño de 9 años pensando ambiciosamente en ¡escribir un ensamblador!)
Para entrar este código en la computadora de los estudiantes, uno solo necesita entrar el microcódigo a la izquierda. Si encuentras un error del lado derecho es porque estaba pensado solo para referencia.
El código comienza por inicializar el juego y crear un bucle principal. Puse NAVAJA como NEVAJA. Nota como reemplacé PONEHL por una rutina MSX similar WRTVDP. La rutina INMEDIATO copia los siguientes bytes directamente al VDP hasta que encuentra un byte cero.

JUEGO:
	CALL setup_vdp	; Requerido para iniciar VDP
	CALL LIMPIA	; Limpia la pantalla
	CALL PRESENTACION       ; Pantalla de título
	CALL GRAFICOS	; Pone gráficos
PRIN:		; 8009
	CALL PATCH	; Parche para el salto
	CALL KARATEKA	; Mueve karateka
	CALL MALORA	; Mueve enemigo
	CALL NEVAJA	; Mueve cuchillos
	JP PRIN		; Repite el bucle principal

        ; Pantalla de título
PRESENTACION:	; 8018
	LD HL,$3C4D
	CALL SETWRT
	CALL INMEDIATO
Página 2 de mi juego Z80
Página 2 de mi juego Z80.
Por supuesto, no tenía idea de directivas excepto que poner un byte en cada línea. Esta es la pantalla de título. "PULSE ESPACIO" indica que se debe presionar la barra de espacio. Lo cambié para ser un botón de joystick del MSX o Colecovision.

	DB "KARATEKA",0

	LD HL,$3ECA
	CALL SETWRT
	CALL INMEDIATO
	DB "PULSE ESPACIO",0
Página 3 de mi juego Z80
Página 3 de mi juego Z80.
Utiliza la rutina de BIOS interna $04f5 para leer la matriz del teclado, esto fue reemplazado por una llamada GTTRIG de MSX (o en Colecovision lee los botones). Entonces pone los gráficos para el karateka, el cuchillo y la judoka.

	; Cambiado del original
TEC:	XOR A
	CALL GTTRIG
	OR A
	RET NZ
	JP TEC

	; Pone los gráficos para el Karateka, cuchillo y  Judoka
GRAFICOS:
	LD HL,$3800
	LD DE,GRAFICOS_8600
	LD B,$00
.1:	LD A,(DE)
	CALL WRTVRM
	INC HL
	INC DE
	DJNZ .1

	LD HL,$0B08
	LD DE,GRAFICOS_85D8
	LD B,$28
.2:	LD A,(DE)
Página 4 de mi juego Z80
Página 4 de mi juego Z80.
Ahora pone algunas variables para la posición del karateka y los cuchillos disparados por el enemigo. También reinicia la variable kicks_given. Comienza a llenar el color para el cielo y la hierba.

	CALL WRTVRM
	INC HL
	INC DE
	DJNZ .2

        LD A,$68
	LD (y_karateka),A
	LD A,$D0
	LD (x_karateka),A
	LD A,$00
	LD (spr_karateka),A
	LD A,$00
	LD (knife1),A
	LD A,$00
	LD (knife2),A
	LD A,$00
        LD (kicks_given),A

	LD HL,$2B00
	LD B,$08
.3:	LD A,$77	; Color turquesa
	CALL WRTVRM
	INC HL
	DJNZ .3
	LD HL,$2808
	LD B,$08
.4:	LD A,$22	; Color cesped
Página 5 de mi juego Z80
Página 5 de mi juego Z80.
Entonces limpia la pantalla y comienza a poner los caracteres en la pantalla para representar visualmente el cielo y el pasto. Y sí, embarazosamente el niño de 9 años esta repitiendo código como loco.

	CALL WRTVRM
	INC HL
	DJNZ .4

	LD HL,$2300
	LD B,$08
.5:	LD A,$77
	CALL WRTVRM
	INC HL
	DJNZ .5

	CALL LIMPIA

	LD HL,$3C00
	LD B,$00
.6:	LD A,$60
	CALL WRTVRM
	INC HL
	DJNZ .6

	LD B,$E0
.7:	LD A,$60
	CALL WRTVRM
	INC HL
	DJNZ .7

	LD B,$20
.8:	LD A,$01
	CALL WRTVRM
	INC HL
Página 6 de mi juego Z80
Página 6 de mi juego Z80.
Pone el color para el enemigo y lo pone en la pantalla usando los caracteres definidos. Entonces pone 3 variables, y una de ellas queda sin usar.

	DJNZ .8

	LD HL,$2B08
	LD B,$28
.9:	LD A,$17
	CALL WRTVRM
	INC HL
	DJNZ .9

	LD HL,$3DA2
	LD A,$61
	CALL WRTVRM
	LD HL,$3DA3
	LD A,$62
	CALL WRTVRM
	LD HL,$3DC2
	LD A,$63
	CALL WRTVRM
	LD HL,$3DC3
	LD A,$64
	CALL WRTVRM
	LD A,$00
	LD (knife_time+1),A     ; SIN USAR
	LD A,$00
	LD (kicks_given),A
	LD A,$00
	LD (knife_time),A
Página 7 de mi juego Z80
Página 7 de mi juego Z80.
Por alguna razón desconocida pongo de nuevo el registro 1 del VDP, entonces pone la posición de inicio para el jugador. Note que las microclaves dicen 3E 68 corrigiendo el código fuente a la derecha que dice LD A,70 poniendo el jugador debajo del piso.
El movimiento es a la izquierda y derecha, arriba para golpes con la mano, abajo para patadas. De nuevo la lectura del teclado es reemplazada por GTSTCK de MSX (y trasladado para Colecovision).

	LD BC,$C201
	CALL WRTVDP
	LD HL,$1B00
	LD A,$68
	CALL WRTVRM
	INC HL
	LD A,$E0
	CALL WRTVRM
	INC HL
	LD A,$00
	CALL WRTVRM
	INC HL
	LD A,$0E
	CALL WRTVRM
	RET

	; Mover karateka
KARATEKA:	; 8132
	XOR A
	CALL GTSTCK	; Cambiado del original
	CP $07
	JP Z,MIK
	CP $03
	JP Z,MDK
	CP $01
	JP Z,GODMA
Página 8 de mi juego Z80
Página 8 de mi juego Z80.
Creo que iba en una compilación mental de BASIC a ensamblador, es lo único que puede explicar porque el código esta tan mal optimizado. Optimizarlo un poco me habría ahorrado mucho tiempo al volver a entrar el código.
Mover el karateka a la izquierda se lograba simplemente sustrayendo 8 de la coordenada X, y actualizando también en la tabla de atributos de sprites. También hace animación del jugador en dos cuadros.

	CP $05
	JP Z,GODPI
	RET

	; Mover karateka a la izquierda
MIK:	; 8149
	LD A,(x_karateka)
	SUB $08
	LD (x_karateka),A
	LD HL,$1b01
	CALL WRTVRM
	LD A,(spr_karateka)
	CP $00
	JP Z,SP2
	LD A,$00
	LD (spr_karateka),A
	JP SPR

SP2:	; 8167
	LD A,$08
	LD (spr_karateka),A
SPR:	LD HL,$1b02
	CALL WRTVRM
	RET
Página 9 de mi juego Z80
Página 9 de mi juego Z80.
El movimiento a la derecha replica esencialmente el mismo código que el movimiento a la izquierda. Pude haber usado una subrutina para pasar los parametros en los registro DE o BC..
Como no tiene idea de la dirección del jugador, tiene que leer el cuadro del sprite para saber en que dirección va. Otra variable lo haría más fácil.

	; Mover karateka a la derecha
MDK:	; 8173
	LD A,(x_karateka)
	ADD A,$08
	LD (x_karateka),A
	LD HL,$1b01
	CALL WRTVRM
	LD A,(spr_karateka)
	CP $04
	JP Z,SPC
	LD A,$04
	LD (spr_karateka),A
	JP SPR0

SPC:	LD A,$0C
	LD (spr_karateka),A
SPR0:	LD HL,$1b02
	CALL WRTVRM
	RET

	; GOlpe De MAno
GODMA:	; 819D
	LD A,(spr_karateka)
	CP $00
	JP Z,GAI
	CP $08
	JP Z,GAI
	CP $04
Página 10 de mi juego Z80
Página 10 de mi juego Z80.
El código para golpear a la izquierda (GAI) y derecha (GAD), simplemente actualiza el sprite al cuadro de golpe, y llama a NADEO (donde mira si el enemigo es golpeado), entonces realiza un retardo (¡deteniendo el juego completo!) y restaura el sprite original. Todavía necesitaba entender el concepto de objetos independientes.

	JP Z,GAD
	CP $0C
	JP Z,GAD
	RET

GAI:	; 81B5
	LD HL,$1B02
	LD A,$10
ZON:	CALL NADEO
	LD BC,$4000
.1:	DEC BC
	LD A,B
	OR C
	JR NZ,.1
	LD A,(spr_karateka)
	JP WRTVRM

GAD:	; 81CB
	LD HL,$1B02
	LD A,$14
	JP ZON
Página 11 de mi juego Z80
Página 11 de mi juego Z80.
El código para manejar patadas es esencialmente una copia del código previo. BOLAS es una exclamación, muy similar a ¡Ay Caramba!

GODPI:	; 81D3 Olvidé la etiqueta en el código
	LD A,(spr_karateka)
	CP $00
	JP Z,IZ
	CP $08
	JP Z,IZ
	CP $04
	JP Z,DER
	CP $0C
	JP Z,DER
	RET

IZ:	; 81EB
	LD HL,$1B02
	LD A,$18
HOLA:	CALL NADEO
	LD BC,$4000
BOLAS:	DEC BC
	LD A,B
	OR C
	JR NZ,BOLAS
	LD A,(spr_karateka)
	JP WRTVRM

DER:    ; 8201
	LD HL,$1B02
	LD A,$1C
	JP HOLA
Página 12 de mi juego Z80
Página 12 de mi juego Z80.
Después de ver que no había forma de evitar los cuchillos, implementé el salto para el karateka.
Esto se ve más pensado, como llamar NEVAJA para mantener los cuchillos en movimiento. Pero todavía son dos subrutinas separadas, una para mover el jugador arriba, y otra para moverlo abajo. Cada una con su propio retardo.

	; Salto del karateka
SALTO:	; 8209
	LD A,(y_karateka)
	LD B,$04
OSC:	LD HL,$1B00
	SUB $04
	CALL WRTVRM
	PUSH HL
	PUSH AF
	CALL NEVAJA
	PUSH BC
	LD BC,$4000
.1:	DEC BC
	LD A,B
	OR C
	JR NZ,.1
	POP BC
	POP AF
	POP HL
	DJNZ OSC
	LD B,$04
KAR:	LD HL,$1B00
	ADD A,$04
	CALL WRTVRM
	PUSH HL
	PUSH AF
	CALL NEVAJA
	PUSH BC
Página 13 de mi juego Z80
Página 13 de mi juego Z80.
Mover los cuchillos es un contador simple de izquierda a derecha, actualizando la pantalla.
El niño no pudo solucionar como mover los cuchillos más cerca del enemigo que los lanzaba, porque los cuchillos borraban con espacios detrás suyo así que borrarían la mitad derecha del enemigo.

	LD BC,$4000
BOCHA:	DEC BC
	LD A,B
	OR C
	JR NZ,BOCHA
	POP BC
	POP AF
	POP HL
	DJNZ KAR
	RET

NEVAJA:	; 8247
	LD A,(knife1)
	ADD A,$A5
	LD H,$3D
	LD L,A
	LD A,$65
	CALL WRTVRM
	LD A,(knife1)
	ADD A,$A4
	LD H,$3D
	LD L,A
	LD A,$60
	CALL WRTVRM
	LD A,(knife1)
	INC A
Página 14 de mi juego Z80
Página 14 de mi juego Z80.
Un soprendente gasto de bytes repitiendo el mismo código para mover un cuchillo paralelo en la misma columna usando una variable diferente.

	LD (knife1),A
	LD A,(knife2)
	ADD A,$C5
	LD H,$3D
	LD L,A
	LD A,$65
	CALL WRTVRM
	LD A,(knife2)
	ADD A,$C4
	LD H,$3D
	LD L,A
	LD A,$60
	CALL WRTVRM
	LD A,(knife2)
	INC A
	LD (knife2),A
	CP $1A
	CALL Z,B2
	LD A,(knife1)
	CP $1A
	CALL Z,B1
	RET

B1:	DEC A
	ADD A,$A5
	LD H,$3D
Página 15 de mi juego Z80
Página 15 de mi juego Z80.
De nuevo un gasto de bytes borrando cada cuchillo por separado, cuando ambos vienen juntos. Tal vez estaba pensando en hacerlos aparecer en momentos diferentes, pero no pude lograrlo. La función MALORA (una variante de malo) mira si los cuchillos tocan al jugador, de nuevo comparando cada cuchillo por separado.

	LD L,A
	LD A,$60
	CALL WRTVRM
	LD A,$00
	LD (knife1),A
	RET

B2:	DEC A
	ADD A,$C5
	LD H,$3D
	LD L,A
	LD A,$60
	CALL WRTVRM
	LD A,$00
	LD (knife2),A
	RET

MALORA:	; 82B9
	LD A,(x_karateka)
	RRCA
	RRCA
	RRCA
	LD B,A
	LD A,(knife1)
	ADD A,$05
	CP B
	JP Z,MUERTO
	LD A,(knife2)
Página 16 de mi juego Z80
Página 16 de mi juego Z80.
La función MUERTO simplemente llena la tabla de color completa con colores aleatorios (¡Hey! ¡Un efecto visual especial!) y se detiene antes de regresar al monitor interno. No podía insertar un salto al juego principal porque la instrucción RST 00 utiliza un solo byte, y un salto usa 3 bytes (recuerda que no estaba usando un ensamblador) así que para esta vez ahora si puse el salto al juego principal.

	ADD A,$05
	CP B
	JP Z,MUERTO
	RET

MUERTO:	; 82D3
	LD HL,$2000
	LD BC,$1800
M2:	LD A,R	
	CALL WRTVRM
	INC HL
	DEC BC
	LD A,B
	OR C
	JR NZ,M2
	LD BC,$0000
M3:	DEC BC
	LD A,B
	OR C
	JR NZ,M3
	JP JUEGO	; No en el original porque hubiera tenido que reubicar
Página 17 de mi juego Z80
Página 17 de mi juego Z80.
Finalmente mi primera subrutina de ayuda NADEO, llamada 4 veces para ver si el enemigo es tocado. No me imagino que intentaba abreviar aquí. La comprobación es simple, si el jugador esta parado exactamente donde salen los cuchillos, le pegará al enemigo, por supuesto tratando de hacer que al jugador lo toquen los cuchillos. Desafortunadamente esta posición pega en el aire, y no hay indicación visual del golpe...

NADEO:	; 82ED
	CALL WRTVRM
	PUSH AF
	PUSH HL
	PUSH BC
	PUSH DE
	LD A,(x_karateka)
	CP $28
	CALL Z,GOLPES
	POP DE
	POP BC
	POP HL
	POP AF
	RET

GOLPES:	; 8301
	LD A,(kicks_given)
	INC A
	LD (kicks_given),A
	CP $14
	JP Z,GANAR
	RET

GANAR:	; 830E
	CALL LIMPIA
	LD HL,$3C03
	CALL SETWRT
	CALL INMEDIATO
Página 18 de mi juego Z80
Página 18 de mi juego Z80.
Esta parte es sencilla. Solo un mensaje al ganar (BIEN!)

	DB "BIEN!",0
	LD BC,$0000
BION:	DEC BC
	LD A,B
	OR C
	JR NZ,BION
	JP JUEGO
Página 19 de mi juego Z80
Página 19 de mi juego Z80.
Obviamente escribí el juego completo de mi mente al papel, y no pensé en un retardo para hacer el juego correr a una velocidad manejable. Así que este código hace un retardo y también maneja el salto.
Nota que no usé interrupciones. No sabía de las interrupciones entonces, pero la simple idea de tener una constante de tiempo continua referida en términos de cuadros de video ¡hubiera hecho estallar mi mente!

PATCH:
	LD BC,$4000
.1:	DEC BC
	LD A,B
	OR C
	JR NZ,.1
	XOR A
	CALL GTTRIG
	OR A
	JP NZ,SALTO
	RET
Página 20 de mi juego Z80
Página 20 de mi juego Z80.
Esta página no tuvo que portarse ya que VPOKE se reemplazó por la rutina WRTVRM. Y no se usa VPEEK. Por cierto, esto muestra la influencia que tenía en mí la revista Input MSX.
Página 21 de mi juego Z80
Página 21 de mi juego Z80.
Página 22 de mi juego Z80
Página 22 de mi juego Z80.
Estas páginas contienen los gráficos para el juego. Debe existir en alguna parte papel cuadriculado con los dibujos originales pero no los he encontrado.

GRAFICOS_85D8:
	DB $00,$03,$06,$0D,$1B,$17,$03,$01	; JUDOKA
	DB $00,$C0,$00,$C0,$40,$C0,$80,$00
	DB $0F,$F3,$00,$07,$07,$02,$1C,$30
	DB $F0,$CF,$00,$E0,$E0,$40,$38,$0C

	DB $10,$10,$20,$FF,$FF,$20,$10,$10	; CUCHILLO

GRAFICOS_8600:
	DB $03,$07,$07,$03,$01,$03,$03,$03	; KARATEKA IZQ.
	DB $03,$03,$03,$01,$01,$01,$03,$0F
	DB $C0,$E0,$E0,$C0,$80,$C0,$C0,$C0
	DB $C0,$C0,$C0,$80,$80,$80,$80,$80

	DB $03,$07,$07,$03,$01,$03,$03,$03	; KARATEKA DER.
	DB $03,$03,$03,$01,$01,$01,$01,$01
	DB $C0,$E0,$E0,$C0,$80,$C0,$C0,$C0
	DB $C0,$C0,$C0,$80,$80,$80,$C0,$F0

	DB $03,$07,$07,$03,$01,$03,$03,$03	; KARATEKA CAMINA IZQ.
	DB $03,$03,$03,$01,$02,$04,$0C,$3D
	DB $C0,$E0,$E0,$C0,$80,$C0,$C0,$C0
	DB $C0,$C0,$C0,$80,$40,$20,$60,$E0

	DB $03,$07,$07,$03,$01,$03,$03,$03	; KARATEKA CAMINA DER.
	DB $03,$03,$03,$01,$02,$04,$06,$07
	DB $C0,$E0,$E0,$C0,$80,$C0,$C0,$C0
	DB $C0,$C0,$C0,$80,$40,$20,$30,$BC

	DB $03,$07,$07,$03,$01,$FF,$FF,$03	; KARATEKA GOLPE IZQ.
	DB $03,$03,$03,$01,$01,$01,$03,$0F
	DB $C0,$E0,$E0,$C0,$80,$C0,$C0,$C0
	DB $C0,$C0,$C0,$80,$80,$80,$80,$80

	DB $03,$07,$07,$03,$01,$03,$03,$03	; KARATEKA GOLPE DER.
	DB $03,$03,$03,$01,$01,$01,$01,$01
	DB $C0,$E0,$E0,$C0,$80,$FF,$FF,$C0
	DB $C0,$C0,$C0,$80,$80,$80,$C0,$F0

	DB $03,$07,$07,$03,$01,$03,$C3,$C3	; KARATEKA PATEA IZQ.
	DB $33,$33,$0F,$07,$00,$00,$01,$07
	DB $C0,$E0,$E0,$C0,$80,$C0,$C0,$C0
	DB $C0,$C0,$C0,$80,$80,$80,$80,$80

	DB $03,$07,$07,$03,$01,$03,$03,$03	; KARATEKA PATEA DER.
	DB $03,$03,$03,$01,$01,$01,$01,$01
	DB $C0,$E0,$E0,$C0,$80,$C0,$C3,$C3
	DB $CC,$CC,$F0,$E0,$00,$00,$80,$E0
Página 23 de mi juego Z80
Página 23 de mi juego Z80.
La última página contiene documentación de las variables y la mitad está desincronizada, y el cuchillo 3 no se implementó. Es un ejemplo práctico de documentación real :P

	ORG RAM_BASE

y_karateka:	RB 1
x_karateka:	RB 1
spr_karateka:	RB 1
knife1:		RB 1
knife2:		RB 1
kicks_given:	RB 1
knife_time:    RB 1    ; NOT USED

Descargas

Puede descargar el código fuente de este juego.

Extras

Solo me tomó casi tres años (11-feb-2024) descubrir que los dibujos para el juego estaban de hecho en el mismo cuaderno, pero las dos páginas estaban pegadas entre si.
Dibujo de cuchillo para mi juego sencillo de Karateka Dibujo del jugador y el enemigo para mi juego sencillo de Karateka
El siguiente dibujo no fue usado en el juego debido a que lucía demasiado diferente de mis dibujos base, pero recuerdo que la pierna en diagonal se inspiró en ese dibujo. Por supuesto, me tomó años descubrir que un movimiento de patada no es simplemente una pierna levantada (no lo repitas).
Dibujo del jugador haciendo un movimiento de karate

Ligas relacionadas

Última modificación: 22-feb-2024