cursos:ensamblador:anexo1

Anexo (I)


Los usuarios de Linux, aunque podemos utilizar emuladores como rvm (Retro Virtual Machine) o ejecutar el utilísimo spin con ayuda del “emulador” de Windows wine, tradicionalmente hemos usado el emulador fuse (“Free Unix Spectrum Emulator”) por su facilidad de instalación y uso y las muchas opciones que tiene, incluyendo Memory Browser y Debugger o Depurador.

En el caso del depurador de Fuse, tanto en su versión GTK como en su versión en SDL puro se utiliza mediante comandos de texto (al contrario que emuladores como Spin que tienen un completo debugger muy amigable con el usuario).


FUSE en su versión GTK



FUSE en su versión SDL


Como se puede ver en las capturas (vamos a centrarnos en la versión GTK), en la parte superior tenemos:


  • El estado de los registros y los bancos de memoria.
  • El contenido y un desensamblado de la memoria apuntada por PC (izquierda)
  • El contenido y un desensamblado de la memoria apuntada por SP (derecha), es decir, el contenido de la pila.


En la parte inferior podemos ver una serie de botones y una caja de texto para introducción de comandos con su botón de “Evaluate” para ejecutarlos:

  • Single Step: Ejecuta un único opcode del Z80 y detiene de nuevo la emulación.
  • Continue: Continua la emulación de forma continuada, pero dejando la ventana del depurador abierta (la cual no se actualiza mientras el emulador se ejecuta).
  • Break: Detiene la emulación y vuelve al depurador.
  • Haciendo doble click en cualquier ubicación del Stack hará que la emulación continúe hasta que PC alcance el valor contenido en esa dirección.
  • Close: Cierra el depurador y vuelve a la emulación.
  • Haciendo doble click en cualquier ubicación del Stack hará que la emulación continúe hasta que PC alcance el valor contenido en esa dirección.
  • Haciendo doble click en cualquier entrada del panel de “Eventos” (derecha del todo) hará que la emulación continúe hasta que se alcance dicho evento (como, por ejemplo, el final del frame de refresco de pantalla actual).
  • Evaluate: evaluará y ejecutará el comando introducido en la caja de texto.

La potencia del debugger de FUSE radica en sus comandos, que vamos a ver resumidos a continuación.

Como normas generales:


  • El debugger de Fuse no distingue entre minúsculas y mayúsculas.
  • Todos los números se interpretarán como en base decimal a menos que lleven el prefijo $ o 0x.
  • Los comandos pueden ser escritos completos o abreviados. Por ejemplo, cuando indicamos ba{se} quiere decir que podemos usar tanto base como ba.
  • Las direcciones de memoria en los comandos se pueden especificar de 2 formas: o como una dirección absoluta (entera o hexadecimal en el rango $0000 a $ffff) o como un string página_de_RAM:offset para poder referirse a bancos. Las páginas de RAM se referencian con su entero, y las ROMs se referencia con el prefijo R. Así, el offset $1234 en la ROM 1 sería R1:$1234.
  • El debugger admite para los valores numéricos (excepto para los identificadores de breakpoint) expresiones simples con operandos básicos (+, -, *, /, ==, !=, >, <, >=, , &, |, ^, &&, ||) incluyendo operaciones entre valores inmediatos y registros. Estas expresiones se pueden usar también en las “condiciones” para los breakpoints.


Estos son los comandos disponibles:


  • ba{se} dirección: Cambia la posición de la ventana del debugger para empezar en la dirección indicada.
  • co{ntinue}: Equivale a pulsar el botón CONTINUE para continuar la emulación.
  • s{tep}: Equivale a pulsar el botón “SINGLE STEP” para avanzar en un opcode la emulación.
  • fi{nish}: Sale del actual CALL o equivalente. Lo hace intentando poner un breakpoint temporal basado en la pila, por lo que no es “infalible”.
  • n{ext}: Avanzar hasta el siguiente opcode, usando la misma técnica que “finish”.
  • se{t} dirección valor: POKEar el valor indicado en la dirección de memoria especificada.
  • se{t} registro valor: Asignar un valor a un registro del Z80.
  • o{ut} puerto valor: Escribir un valor en el puerto indicado.
  • br{eakpoint} [dirección] [condición]: Establece un breakpoint que detiene la emulación cuando un opcode se ejecuta en la dirección indicada y la condición se cumple. Si la dirección se omite, se asume que nos referimos a PC.
  • br{eakpoint} p{ort} (r{ead}|w{rite}) puerto [condición]: Establece un breakpoint que se dispara cuando lee o escribe el puerto indicado. Se puede indicar una condición que haga que el breakpoint sólo se ejecute si se evalúa a true.
  • br{eakpoint} (r{ead}|w{rite}) [dirección] [condición]: Establece un breakpoint que se dispara cuando la dirección de memoria indicada es leída (tanto por una instrucción como por un fetch-opcode del Z80) o escrita.
  • br{eakpoint} ti{me} time [condición]: Establece un breakpoint que se dispara tras “time” t-estados desde el inicio del frame actual. Se puede indicar una condición que haga que el breakpoint sólo se ejecute si se evalúa a true.
  • cl{ear} [dirección]: Eliminar todos los breakpoints en la dirección indicada o en PC si se omite. No elimina los breakpoints de lectura/escritur de puertos.
  • del{ete} [id]: Elimina el breakpoint con el ID indicado, o todos si no se especifica ID.
  • cond{ition} id [condición]: Modifica el breakpoint con el ID indicado para que sólo se dispare si la condición indicada es cierta, o siempre si se omite la condición.
  • t{breakpoint} [opciones]: Equivalente al comando 'breakpoint' con todas sus variedades, excepto que es temporal y se autoelimina en cuanto se dispare una vez.
  • i{gnore} id N: No disparar el breakpoint indicado por dicho ID las siguientes “N” veces que ocurra (y que debería haberse disparado si no lo ignorásemos).
  • di{sassemble} dirección: Centrar el panel de desensamblado para empezar en la dirección indicada.



La rutina de la ROM BEEPER (en $03b5), nos permite reproducir sonido pasándole los siguientes parámetros:


  • BEEPER ($03b5) -Reproduce una nota de sonido de longitud DE (f*t) y pitch HL.

    • DE = duración_en_segundos * frecuencia
    • HL = (437500 / frecuencia) - 30.125


Para poder reproducir melodías en el Spectrum (alojadas por ejemplo en una tabla DEFB), necesitaremos conocer las frecuencias y el pitch de cada nota de la escala musical, de modo que podamos hacer:


    ld de, frecuencia_deseada*duracion_en_segundos
    ld hl, pitch_para_esa_nota
    call $03b5


Las notas, extraídas del libro Cracking the code on the ZX Spectrum, son las siguientes:


Nota Frecuencia
(DE = f*duración_en_seg)
Pitch (HL)
C 0 16.35 $6868
C#0 17.32 $628d
D 0 18.35 $5d03
D#0 19.44 $57cb
E 0 20.60 $52d7
F 0 21.83 $4e2b
F#0 23.12 $49cc
G 0 24.50 $45a3
G#0 25.96 $41b6
A 0 27.50 $3e06
A#0 29.14 $3a87
B 0 30.87 $373e
C 1 32.70 $3425
C#1 34.64 $3137
D 1 36.70 $2e72
D#1 38.88 $2bd6
E 1 41.20 $295c
F 1 43.66 $2706
F#1 46.24 $24d7
G 1 49.00 $22c2
G#1 51.92 $20cc
Nota Frecuencia
(DE = f*duración_en_seg)
Pitch (HL)
A 1 55.00 $1ef4
A#1 58.28 $1d34
B 1 61.74 $1b90
C 2 65.40 $1a03
C#2 69.20 $188c
D 2 73.40 $172a
D#2 77.76 $15dc
E 2 82.40 $149f
F 2 87.32 $1374
F#2 92.48 $125c
G 2 98.00 $1152
G#2 103.84 $1057
A 2 110.00 $f6b
A#2 116.56 $e8b
B 2 123.48 $db8
C 3 130.80 $cf2
C#3 138.56 $0c37
D 3 146.80 $b86
D#3 155.52 $adf
E 3 164.80 $a40
F 3 174.64 $9ab
Nota Frecuencia
(DE = f*duración_en_seg)
Pitch (HL)
F#3 184.96 $91f
G 3 196.00 $89a
G#3 207.68 $81c
A 3 220.00 $7a6
A#3 233.12 $736
B 3 246.96 $6cd
C 4 261.60 $66a
C#4 277.12 $60c
D 4 293.60 $5b3
D#4 311.04 $560
E 4 329.60 $511
F 4 349.28 $4c6
F#4 369.92 $480
C 4 392.00 $43d
G#4 415.36 $3ff
A 4 440.00 $3c4
A#4 466.24 $38c
B 4 493.92 $357
C 5 523.20 $326
C#5 554.24 $2f7
D 5 587.20 $2ca
D#5 622.08 $2a1
E 5 659.20 $279
F 5 698.56 $254
F#5 739.84 $231
G 5 784.00 $20f
G#5 830.72 $1f0
Nota Frecuencia
(DE = f*duración_en_seg)
Pitch (HL)
A 5 880.00 $1d3
A#5 932.48 $1b7
B 5 987.84 $19c
C 6 1046.40 $183
C#6 1108.48 $16c
D 6 1174.40 $156
D#6 1244.16 $141
E 6 1318.40 $12d
F 6 1397.12 $11b
F#6 1479.68 $109
G 6 1568.00 $f8
G#6 1661.44 $e9
A 6 1760.00 $da
A#6 1864.96 $cc
B 6 1975.68 $bf
C 7 2092.80 $b2
C#7 2216.96 $a7
D 7 2348.80 $9c
D#7 2488.32 $91
E 7 2636.80 $87
F 7 2794.24 $7e
F#7 2959.36 $75
G 7 3136.00 $6d
G#7 3322.88 $65
A 7 3520.00 $5e
A#7 3729.92 $57
B 7 3951.36 $50

Por ejemplo, para reproducir una nota G#4 durante medio segundo:

  • DE = 415.36 (de la tabla) * 0.5 segundos = 207 = $cf
  • HL = $19f (de la tabla)
  • call $03b5


[ | ]

  • cursos/ensamblador/anexo1.txt
  • Última modificación: 24-01-2024 12:36
  • por sromero