Puerto 58047 (E2BFh)

Bit 0 ENTRADA indica que la línea está ocupada.
Puerto 57535 (EOBFh)

Bits 0- 7 SALIDAS de datos por Centronics.
Puerto 58303 (E3BFh)

Bits 0-3 SALIDAS señal Centronics de dato enviado.
Corrientes estándar
        Corriente K- 3/253 (FDh) es un duplicado de las corrientes 0 y 1.
Corriente S - 2/254 (FEh) es un duplicado de la corriente 2.
        Corriente R- 1/255 (FFh) se usa para escribir en un área de trabajo, y pone
el código contenido en el registro A dentro de la dirección contenída en la
variable del sistema K CUR 23643/4 (5C5B/Ch), e incrementa la dirección en K
CUR. Esto no es tan útil como parece en principio, dado que la rutina para
reservar espacio es llamada primero, y ésta desplaza toda la memoria sobre la
dirección en K CUR hasta RAMTOP, inutilizando la corriente para poner
alguna cosa en algún lugar sobre RAMTOP, o en algún sitio que no debe ser
movido.. La rutina se inicia en 3969(F81h), y la corriente puede ser utilizada
sólo para salida; cualquier intento para usarla como entrada causará un error.
Las corrientes 0 y 1 (K) son vinculadas, normalmente, a la parte inferior de
la pantalla y al teclado. También permiten realizar entrada por sus canales.
Corriente 2 (S) es sólo para salida; normalmente, a pantalla.
        Corriente P 3 es sólo para salida; normalmente, vinculada a la impresora.
Hay 19 corrientes disponibles, y cada una tiene que ser vinculada a un
canal; cada corriente tiene dos bytes en el área de las corrientes de las variables
del sistema, comenzando en 23569 (5C10h) para la corriente - 3, conteniendo el
desplazamiento del canal al que está vinculada desde la base del área de los
canales. Recuerde que la primera corriente es - 3, por lo que para encontrar la
dirección del canal para la corriente 0 tendrá que mirar en 23574/5 (5C16/7h).
        Un canal está compuesto por un mínimo de cinco bytes; los dos primeros
poseen la dirección de la rutina de salida; los dos siguientes contienen la direc-
ción de la rutina de entrada, y el último byte contiene las letras mayúsculas del
código del canal (S, K, P, etc.). Los canales asociados con el interfaz del Micro-
drive son sustancialmente más grandes que el mínimo, y su formato se muestra
en el manual proporcionado con el interfaz.
Rutina de grabación y reproducción
10  ;   este corto programa permite
20  ;   oir una voz por la entrada
30  ;   EAR y meterlo en la memoria.
   _______
             65

_______
 66
 	
 			40	;  El texto puede ser reproducido
 			50	;  luego por el altavoz del Spectrum.
 			60	;  es un programa muy sencillo
 			70 	;  pero funciona.
			80 	;
	C350 		90 	LIMITE 	EQU 	50000  ;  limite inferior de la memoria
	0005 		100 	TIEMPO 	EQU 	5  ;  	      retardo entre cada bit.
	FDE6 		110 	PRIMER 	EQU 	64998  ;  El primer byte que se va a usar.
	FDE8 		120 		ORG  	65000
 	FDE9	21E6FD 	130 	INICIO 	LD 	HL, PRIMERO  ; calcula la memoria
	FDEB	1150C3 	140 		LD 	DE, LIMITE  ;     disponible para su uso.
	FDEE 	E5 	150 		PUSH 	HL
	FDEF 	A7 	160 		AND 	A
	FDF0 	ED52 	170 		SBC 	HL, DE
	FDF2 	E5 	180 		PUSH 	HL
	FDF3 	C1 	190 		POP 	BC
	FDF4 	E1 	200 		POP 	HL
	FDF5 	F3 	210 		DI	; desconecta las interrupciones para
 			220	; 		   tener una temporizacion correcta
 	FDF6	3E7F 	230 	ESCUCH 	LD 	A, #7F ; espera un sonido
	FDF8 	DBFE 	240 		IN 	A, (#FE)
	FDFA 	CB77 	250 		BIT 	6, A
	FDFC 	28F8 	260 		JR 	Z, ESCUCHA
	FDFE 	C5 	270 		PUSH 	BC
	FDFF 	0608 	280 	TOMABY 	LD 	B, 8  ; lee 8 bits
 	FE01	2B 	290 		DEC 	HL
	FE02 	DBFE 	300 	OYE 	IN 	A,  (#FE)   ;  un bit cada vez
	FE04	17	310		RLA
	FE05	17	320		RLA
	FE06 	CB16 	330 		RL 	(HL)  ;  y la transfiere a la memoria
 	FE08	100A 	340 		DJNZ 	PAUSA ; pausa y coge el siguiente bit
	FE0A 	D1	350		POP 	DE ;	 pero si se han leido 8 mira
	FE0B 	1B	360		DEC DE ;	si hay sitio y decrementa el contador
	FE0C 	7A 	370 		LD 	A, D
	FE0D 	B3 	380 		OR	E
	FE0E 	D5 	390 		PUSH 	DE
	FE0F 	20EE 	400 		JR 	NZ,TOMABY; si hay sitio vuelve a coger
	FE11	C1	410		POP	BC;	otro byte, si  no, acaba.
	FE12	FB	420		El
	FE13	C9	430		RET
	FE14	0E05	440	PAUSA	LD	C, TIEMPO
	FE16	0D	450	ESPERA	DEC	C
	FE17	20FD	460		JR	NZ, ESPERA
	FE19	18E7	470		JR	OYE
	FE1B	21E6FD	480	HABLA	LD	HL, PRIMERO ;	 es lo mismo que para escuchar
	FE1E	1150C3	490		LD	DE, LIMITE
	FE21	E5	500		PUSH	HL
	FE22	A7	510		AND	A
	FE23	ED52	520		SBC	HL, DE
	FE25	E5	530		PUSH	HL
	FE26	C1	540		POP	BC
	FE27	E1	550		POP	HL
	FE28	C5	560		PUSH	BC
	FE29	F3	570		DI
	FE2A	0608	580	SACABY	LD	B, 8  ;  saca 8 bits
	FE2C	2B	590		DEC	HL
	FE2D	7E	600		LD	A, (HL)	; notar que se sacan todos los bits y
	FE2E	0F	610		RRCA  ;		  esto cambiara el color de BORDER
	FE2F	0F	620		RRCA
	FE30	0F	630		RRCA
	FE31	D3FE	640	SACABI	OUT	(#FE), A  ; un bit cada vez,como al leer
	FE33	07	650		RLCA
	FE34	0E05	660		LD	C, TIEMPO
	FE36	0D	670	RETARD	DEC	C
	FE37	20FD	680		JR	NZ, RETARDO
	FE39	10F6	690		DJNZ	SACABI
	FE3B	C1	700		POP	BC

   _______
             67
 
	FE3C	08	710		DEC	BC
	FE3D	79	720		LD	A, C
	FE3E	80	730		OR	B
	FE3F	C5	740		PUSH	BC
FE40	20E8	750		JR	NZ, SACABY
FE42	C1	760		POP	BC
	FE43	FB	770		El
	FE44	C9	780		RET

 

        La secuencia de encendido en el Spectrum, que borra la memoria y coloca
las variables del sistema, también inicializa el registro de interrupciones para
contener 63 (3Fh) y poner el modo de interrupción a 1 (IM1). La modificación
del registro I parece innecesaria, dado que el modo IM1 no lo usa -porque en
este modo cualquier interrupción hace un RST 56 (38h)-, pero la innovadora
forma en la que la ULA maneja la pantalla en el Spectrum hace importantes los
bits 6 y 7 del registro I.
        En cualquier ciclo de instrucción de máquina, el microprocesador Z80
ejecuta una operación para refrescar la memoria durante la cual los contenidos
del registro I se sacan en los ocho bits altos del bus de direcciones y la línea de
petición de memoria se activa. La ULA genera una interrupción cada vez que
desea actualizar la pantalla. Esto hace que la CPU ejecute la rutina de servicio
de interrupción, asumiendo que las interrupciones estén habilitadas. Lo normal
es que sea la exploración del teclado y la actualización del contador FRAMES,
pero si la ROM del Interfaz está conectada y activa, sucede que se habilitan las
interrupciones y se produce un retorno inmediato sin explorar el teclado o
hacer ninguna otra cosa.
        Cuando la rutina de interrupciones se completa, la CPU retorna a lo que
estaba haciendo previamente. Si esto incluye una instrucción de lectura o
escritura en una memoria entre 16384 (4000h) y 32767 (7FFFh), que la ULA
comprueba mirando las dos líneas superiores del bus de direcciones, y la línea
_______
             69
6
Usando
las interrupciones

 
MREQ, la ULA para el reloj en la CPU mientras completa la actualización de
la pantalla.
        Si el registro I tiene el bit superior puesto a 0 y el bit 6 a 1, la ULA se
confunde, debido al refresco de la memoria dinámica durante T3 y T4 de un
ciclo M1 (de coger instrucción de la memoria). Esto activa la línea MREQ, y
hace que el registro I salga en los ocho bits superiores del bus de direcciones. La
ULA entonces cree que la CPU está haciendo una operación de lectura o
escritura en este área de la RAM, aun cuando haya intentado pararla, y la ULA
omite su propia lectura para actualizar la pantalla, produciendo una interferen-
cia en la pantalla. El registro I no puede, por tanto, poseer un valor que tenga
los dos bits superiores dispuestos de este modo; en otras palabras, cualquier
número desde el 64 al 127 ( 40h a 7Fh) inclusive, si se quieren evitar interferen-
cias en la pantalla.
        Modificando IM2 puede usar las interrupciones para sus propios fines,
mientras que usted haga una RST 56 (38h) al final de su propia rutina de
servicio de interrupción, la cual habilitará las interrupciones antes de retornar al
programa de llamada -si usted quiere explorar el teclado y actualizar el
contador- y terminar con una instrucción RETI.
        Si usted no ha usado la RST 56 (38h) dentro de la rutina de interrupción,
debe ejecutar una instrucción El antes de la RETI, si quiere habilitar las
instrucciones para llamar de nuevo a la rutina de servicio de interrupción.
Recuerde que tendrá que volver a colocar el modo IM1 y permitir las
interrupciones antes de regresar al BASIC, a menos que usted esté usando una
RST 56 (38h) dentro de la rutina de interrupción.
        El modo IM2 es algo enrevesado. Al recibir una interrupción, que se
produce 50 veces por segundo (60 en Estados Unidos), la CPU salva la dirección
de la próxima instrucción del programa que está ejecutando en la pila de la
máquina, e imposibilita cualquier otra interrupción. Entonces mira a la posición
señalada por el bus de datos + (256 * el registro I) y salta a la dirección que
está contenida en esa posición + (256 * el contenido de la posición siguiente).
Se considera normalmente como una mala práctica el tener el bit 0 en el bus de
datos en estado alto usándolo como puntero en IM2, dado que el vector
debería comenzar siempre en una dirección par, pero desafortunadamente con
el Spectrum no hay elección.
        Ejemplo: El registro I contiene 10 (0Ah), y el bus de datos contendrá 255
(FFh). 10 * 256 = 2560 y 2560 + 255 = 2815; por tanto, la dirección de salto
será tomada de los contenidos de la dirección 2815 + (256 * los contenidos de la
dirección 2816). La dirección 2815 contiene 34 y la dirección 2816 contiene
1281. Esto lo puede comprobar por sí mismo haciendo un PEEK en su
Spectrum, ya que éstas están en la ROM; por tanto, la dirección de salto será
34 + (256 * 128), que es 32802.
        De igual forma, si el registro I contiene 6 :
        6 * 256 = 1536 y 1536 + 255 = 1791.
        1791 contiene 221 y 1792 contiene 113.
        ...221 + (113 * 256) = 29149; por tanto, el salto será a 29149.
        Alternativamente, si usted tiene un Spectrum 48K, y el registro I poseyera

_______
 70

 
200: 200 * 256 = 51200 y 51200 + 255 = 51455; de modo que el salto sería a la
dirección que usted pusiera dentro de esta posición y de la siguiente, en el modo
normal del Z80 con el byte inferior primero.
        Esto se puede representar imaginando la interrupción como una instruc-
ción invísible en el programa que está siendo ejecutado. En el momento de la
interrupción, la instrucción invisible es ejecutada como si fuera una DI seguida
por una ínstrucción de llamada (CALL), a la dirección inmediatamente anterior
a la señalada por el registro I y el bus de datos; al ser llamada, la dirección está
en los dos bytes siguienres en el orden del primer byte inferior del Z80 estándar.
La instrucción, al ser invisible, no puede colocar su propia dirección de retorno
en la pila de la máquina; por consiguiente, la dirección posterior a la de la
última instrucción ejecutada en su programa, se guarda en la pila y es a esta
dirección a la que volverá el control después a la instrucción RETI, al final de
la rutina de interrupción.
        La instrucción RETI debe ser precedida por una instruccíón El. La razón
de que la DI sea incorporada en la llamada ejecutada por la interrupción es
para asegurarse de que si el tiempo de ejecución de la rutina de servicio de
interrupción es más largo que la dilación entre dos interrupciones, el programa
no se quede encerrado en un bucle.
        Es muy fácil escribir un programa que cambie la dirección de salto en una
interrupción cargando los bytes del vector (las dos direcciones miradas para
determinar dónde se hace el salto) con la dirección deseada dentro del programa.
        Nota: Siempre que las rutinas de interrupción sean usadas es de vital
importancia que todos los registros utilizados por ellas se guarden al entrar y
se recuperen antes de volver al programa principal. No intente pasar datos des-
de o hacia la rutina de interrupción en los registros.
        Debido a la limitacíón de los valores que puede contener el registro I, sólo
hay un número limitado de dírecciones a las que se puede saltar en el Spectrum
de 16K, y ésas las fijan los contenidos de la ROM. El interfaz del Mícrodrive
crea un nuevo problema cuando se usan las interrupciones basadas en ROM,
ya que cambia el vector síempre que está conectado en el apéndice F se da una
lista de los vectores para la "ISSUE 2" del Spectrum y la "ISSUE 1 " del
interfaz del Microdrive; pero si no está seguro de su versión, o sí tiene una
versión diferente, esto debe ser comprobado. En los programas comerciales es
peligroso usar vectores de la ROM, ya que cualquier cambio o adiciones futu-
ras podrían inutilízar su programa.
        El uso típico de las rutinas de interrupción es controlar sprites y sonido
continuo dentro de un programa. Sabiendo cada cuánto tiempo se genera una
interrupción, es fácil calcular la velocidad del movimiento de un sprite e,
independientemente de cualquier otra operación dentro del programa, la veloci-
dad permanecerá constante.
        El uso de las rutinas de la ROM dentro de una rutina de interrupción se
complica debido a la posibilidad de que la ROM del interfaz esté conectada
cuando se produce la ínterrupción y esto debe ser permítido cuando se escribe
la rutina. Por ejemplo, si una rutína SPRITE usa una llamada de la ROM de
16K para dibujar el sprite en la pantalla por medio de la llamada de PLOT en

_______
             71

 
8933 (22E5h) cuando la ROM del interfaz está activa, la llamada será a la
dirección 8933 en la ROM del interfaz. Esta es una dirección que no existe, y
causará, inevitablemente, el fallo del programa.
        Una forma de solventar el problema es incorporar dentro de la rutina de
interrupción un chequeo para ver que la ROM está presente. El método más
fácil de chequear es mirar en una dirección de la ROM que contenga un valor
diferente en cada una. Yo suelo usar la dirección 20 (14h), la cual contiene 213
(D5h) en la ROM del interfaz y 255 (FFh) en la ROM de 16K. Entonces se
puede adoptar la acción apropiada para cada ROM.
        Si la llamada de la ROM es para la ROM de 16K, se puede llamar
directamente cuando ésta esté presente, y mediante la instrucción RST 16
cuando la ROM de 8K esté conectada. La ROM de 8K se puede utilizar con el
código de enlace 32h (los detalles se dan en el capítulo 3).
En el apéndice G se da un sencillo programa de SPRITES que mueve un
grupo de cuatro puntos, haciéndolos botar en los cuatro bordes de la pantalla,
emitiendo un sonido y cambiando el color del borde, mostrando métodos de
resolver el problema.
        Para una comprensión total de las interrupciones, si va a hacer algún uso de
ellas, le sugiero que introduzca el programa usando su ensamblador. El listado
está dado directamente desde nuestro ensamblador, para asegurarse de que no
hay errores; es algo extraño en el hecho de que los números hexadecimales están
precedidos por un # , y los números binarios por un % en el código fuente. La
rutina se puede relocalizar si se calcula un nuevo vector y el registro I se cambia
en consecuencia. Una vez que usted haya entrado y ensamblado el programa,
antes de intentar ejecutarlo, siga leyendo.
        El primer problema con el que usted se puede encontrar es, si tiene un
Spectrum de 16K, que el vector tendrá que estar basado en ROM. Desgraciada-
mente, cuando estoy escribiendo esto, no he encontrado el modo de usar un
Spectrum de 16K con el interfaz del Microdrive.
        Antes de hacer cualquier cosa, salve el código fuente y el código objeto
en cinta o Microdrive y, si tiene un Microdrive, saque el cartucho. Para iniciar
el SPRITE, debe llamar a la rutina etiquetada SETUP del listado. Si el vector
no ha sido cambiado, RANDOMIZE USR 51457. Ahora debería ver un punto
negro moviéndose en la pantalla; si no lo ve, debe comprobar su código de
nuevo. La presencia del SPRITE no debería afectar a ninguna otra cosa que
esté haciendo el ordenador, de modo que puede ser introducido un programa y
ejecutado normalmente mientras que no pase los límites de la memoria usada
por la rutina de la interrupción.
        Algunas de las instrucciones del BASIC que actúan sobre las interrupciones
pueden demostrarse ahora. Introduzca la línea de BASIC :
10 BEEP 5,60: FOR N = 1 TO 100: NEXT N: GOTO 10
        Cuando está se ejecute, verá que el SPRITE se para mientras que el BEEP
se está ejecutando. Otros comandos que inhiben las interrupciones son los que
salvan y cargan.
_______
 72

 
        El sprite se desplazará a la velocidad de 50 pixels por segundo en Europa
(60 pixels por segundo en Estados Unidos); por tanto, en España tardará 3,5
segun.dos para desplazarse desde la parte inferior de la pantalla a la parte
superIor.
        Para más información sobre el diseño de sprites se recomienda consultar el
libro Sprites y gráficos en lenguaje máquina para ZX Spectrum (Durst, Anaya
Multimedia, 1985).
_______
             73

 

        Con la adición del Interfaz 1, cualquier instrucción de BASIC que falle la
comprobación de sintaxis del Spectrum es, normalmente, dirigida a las rutinas
de manejo de error mediante la dirección contenida en la nueva variable del
sistema VECTOR. Esta puede modificar se para señalar a una dirección de la
RAM, lo cual permite escribir un programa que compruebe la sintaxis nueva-
mente y ejecute las instrucciones de que esté programado para tratar. Antes de
que se puedan usar estas facilidades es necesario comprender el método por el
que se comprueban las líneas, de modo que la rutina extra pueda hacer las
mismas comprobaciones.
        Tan pronto como se pulse la tecla ENTER después de introducir una línea
del BASIC dentro de la memoria intermedia de la entrada, el intérprete de la
ROM entra en acción. Este ejecuta la línea, pero no llega a ejecutar los
comandos, porque la bandera de sintaxis está bajada (bit 7 en FLAGS). Si
ocurre cualquier error, es señalado con una '?' y debe corregirse el error antes
de que la línea pueda ser insertada en un programa.
        El mismo proceso se lleva a cabo en el momento de la ejecución pero,
dado que la bandera de sintaxis está levantada, el comando es ejecutado.
        Recuerde que siempre que una rutina en la RAM se usa como parte del
intérprete BASIC, la ROM de 8K estará paginada.
        Hay rutinas en ambas memorias ROM que comprueben la bandera de
sintaxis y vuelven a la rutina que hace la llamada sólo cuando se está
ejecutando. Estas son más útiles cuando se añaden comandos, como se demues-
_______
             75
7
Extendiendo
el BASIC
con el Interfaz 1 

 
tra en las rutinas al final de este capítulo. El criterio básico para comprobar la
sintaxis se da a continuación, y las rutinas de la ROM han sido usadas hasta
donde ha sido posible.
        La dirección del carácter en la que falla la sintaxis, en lo referente a la
ROM, estará presente en la variable del sistema CH_ADD al llegar al compro-
bador ampliado de la sintaxis en la RAM. Por esta razón es importante
asegurarse de que el primer carácter del comando agregado al BASIC rompe la
sintaxis normal. Si éste no fuera el caso, el intérprete iniciaría la ejecución al
mismo tiempo, y sería casi imposible obtener de nuevo el control. Por esta
razón, es más simple usar un carácter no alfabético al inicio del comando
adicional, que hace que el sistema se salga del modo normal de órdenes (K),
asegurando el error de la sintaxis. Los símbolos ,*, y '!' son ideales para esto.
        Lo primero que se debe comprobar son los caracteres del comando. Esto
se hace tomando cada uno de los caracteres en sucesión y asegurándose de que
es el esperado. La rutina de la ROM de 16K en 24 (18h) guardará el carácter
que se está tratando en el registro A, y la rutina en 32 (20h) cogerá el carácter
siguiente e incrementará CH_ADD. Puede probarse entonces cada carácter y, si
no es correcto, se llama a la rutina de manejo de errores saltando al VECTOR
original, que es 496 (lF0h).
        Entonces:

        1. Si hubiera una expresión numérica a continuación, se usaría la rutina
             de 16K en 7298 (lC82h). Esta evalúa la siguiente expresión como
             numérica; si no es de este tipo, causará un error. En el momento de
             ejecutar pondrá también el valor dentro de la pila del calculador.
        2. Si hubiera dos expresions numéricas juntas separadas por una coma, se
             usaría la rutina de 16K en 7290 (lC7Ah). Esta actúa como la anterior,
             pero acumula dos números cuando se está ejecutando.
        3. Si a continuación debe haber una expresión alfanumérica (entre comi-
             llas o una variable terminada en $), entonces se puede usar la rutina de
             16K en 7308 (lC8Ch). Esta funciona como las anteriores, pero generan-
             do un error si la expresión no es alfanumérica y durante la ejecución
             almacena los parámetros como se describe al final del capítulo 8.

        Nota: En las tres rutinas anteriores, el primer carácter de la expresión a
evaluar debe estar en el registro A, y CHADD debe contener su dirección antes
de llamar a la rutina. Después de la evaluación, el registro A contendrá el
primer carácter después de la expresión, y CHADD su dirección. Se permiten
los operadores matemáticos, así como el uso de variables en las expresiones
evaluadas, y esto se puede usar como una ventaja para encontrar datos de
variables. Por ejemplo, la expresión X$(1,4 TO 9) o A * (B + (C / (D + E)))
podrían ser válidas, y el resultado sería apilado en el momento de la ejecución.
        Cuando la evaluación se completa, un CALL 1463 (5B7h) comprobará que
el carácter actual es el índicador de final (dos puntos o el código de un retorno
de carro: 13 (ODh)) y vuelve al intérprete durante la comprobación de síntaxís
o a su rutina para ejecutar, si el programa está funcionando; si no es ninguno
de estos caracteres, genera un error .
_______
76

 
        En el momento de ejecutar, su rutina tendrá que realizar la orden, cogiendo
información de la pila del calculador, si es necesario, y volviendo al intérprete
mediante una instrucción IP 1473 (5C 1H), con CHADD señalando al carácter
siguiente que se tiene que interpretar.
        Para demostrar la adición de los comandos, el siguiente programa añade
!CALLnn y !FRE. !CALLnn llamará a una rutina de código máquina en
la dirección indicada a continuación de !CALL. !FRE devolverá un número in-
dicando la memoria libre.
A91C	D7	10	CALL	RST	16
A91D	1800	20		DEFW	24 ; 		coge un caracter
A91F	FE21	30		CP	"!"  ;		es una admiracion?
A921	2032	40		JR	NZ,ERROR ; 	si no, produce error
A923	CD87A9	50		CALL	SIGUIENTE ; 	coge el siguiente Caracter
A926	FE43	60		CP	"C"  ; 		es una C
A928	2017	70		JR	NZ, FREE ; 	si no,salta y mira
		80	;				si se trata de un !FRE
A92A	CD87A9	90		CALL	SIGUIENTE
A92D	FE41	100		CP	"A"	;	se comprueba cada caracter
A92F	2024	110		JR	NZ, ERROR ; 	se produce un error
A931	CD87A9	120		CALL	SIGUIENTE ; 	si no coincide alguno
A934	FE4C	130		CP	"L"
A936	201D	140		JR	NZ, ERROR
A938	CD87A9	150		CALL	SIGUIENTE
A93B	FE4C	160		CP	"L"
A93D	2016	170		JR	NZ, ERROR
A93F	1817	180		JR	ESCALL 	; para llegar aqui la
		190	;			  palabra ha de ser correcta
A941	FE46	200	FREE	CP	"F"  ; 	  lo mismo con !FRE
A943	2010	210		JR	NZ, ERROR
A945	CD87A9	220		CALL	SIGUIENTE
A948	FE52	230		CP	"R"
A94A	2009	240		JR	NZ, ERROR
A94C	CD87A9	250		CALL	SIGUIENTE
A94F	FE45	260		CP	"E"
A951	2002	270		JR	NZ, ERROR
A953	181A	280		JR	ESFRE ;  es !FRE
A955	C3F001	290	ERROR	JP	496 ; 	el 'VECTOR' original
A958	CD87A9	300	ESCALL	CALL	SIGUIENTE
A95B	D7	310		RST	16
A95C	821C	320		DEFW	7298 ; 	evalua la siguiente
		330	;			expresion BASIC como
		340	;			numerica, causando un error si no.
		350	;			En tiempo de ejecucion,
		360	;			deja el valor en la
		370	;			pila del calculador.
A95E	CDB705	380		CALL	1463 ;	mira si es el final
		390	;			de la sentencia y vuelve
A961	D7	400		RST	16 ; 	en el tiempo de ejecucion
A962	A22D	410		DEFW	11682 ;	pila a BC, ver cap. 8
A964	38EF	420		JR	C, ERROR; el acarreo es 1 si
		430	;			  supera a 65535
A966	ED436BA9	440		LD	(DEST), BC ; la direccion del CALL
A96A	D7	450		RST	16
A96B		460	DEST	DEFS	2 ; 	y la pone aqui
A96D	1815	470		JR	FINAL ; 	la ROM de 16K estara
		480	;			presente al llamar a la rutina
A96F	CDB705	490	ESFRE	CALL	1463 ; 	sale durante
		500	;			la comprobacion de sintaxis
A972	210000	510		LD	HL, 00 ; 	borra HL
A975	39	520		ADD	HL, SP;	suma la direccion de SP
A976	ED5B655C	530		LD	DE,	(23653) ; esta es STKEND
A97A	ED52	540		SBC	HL,DE ; 	resta de la direccion en SP
_______
             77

 
A97C	E5	550		PUSH	HL ; 	para obtener el espacio
		560	; 			libre para el BASIC
A97D	C1	570		POP	BC ; 	el resultado ahora en BC
A97E	D7	580		RST	16
A97F	2B2D	590		DEFW	11563 ; 	apila BC, ver cap. 8
A981	D7	600		RST	16
A982	E32D	610		DEFW	11747 ; 	imprime el valor en la pila
A984	C3C105	620	FINAL	JP	1473 ; 	vuelve a BASIC
A987	D7	630	SIGUIE	RST	16
A988	2000	640		DEFW	32; 	rutina NEXT_CH de la ROM
A98A	E6DF	650		AND	223 ; 	que seA en mayusculAs
A98C	C9	660		RET
        Normalmente, los comandos o funciones de ampliación del BASIC termi-
narán con un salto al intérprete principal, como en el listado anterior, y es muy
importante que sea la ROM de 8K la que esté conectada cuando se haga este
salto, pues si no el programa fallará.
        Para mayor información sobre este tema se recomienda la lectura de
Programación del Interface I y Microdrive (Núñez Castain, Anaya Multimedia,
1985).
_______
78

 

8
El calculador
        El Spectrum contiene un poderoso calculador en la ROM que puede ser
usado en beneficio del programador en código máquina. Como tiene 66 rutinas
diferentes, examinaré detalladamente sólo las más útiles. Para usar el calcula-
dor es importante entender la forma en la que el Spectrum maneja los números,
cómo situarlos para que pueda cogerlos y cómo recuperar la respuesta de los
cálculos terminados.
        Todos los números usados por el calculador se almacenan como 5 bytes en
representación de punto flotante binario o como pequeños enteros.
Representación de enteros pequeños
        El primer byte es siempre 0.
        El segundo byte es el signo 255 (FFh) para negativo o 0 para positivo.
        El tercero y cuarto bytes son el número actual en el formato Z80 estándar ,
el byte bajo primero.
        El byte final es siempre 0.
        El número 0 se considera positivo.
_______
             81

 
Representación en punto flotante
        El primer byte es el exponente; éste es el número de veces que el punto
binario ha sido movido a la izquierda, para hacer que el bit 7 esté a 1 ya la
derecha del punto binario; el bit 7 del exponente indica en qué dirección ha
sido movido el punto binario. Si está a 1, el punto ha sido movido a la
izquierda, como en el ejemplo siguiente.
        Tomar el número 126 decimal, 7Eh, que en binario es 01111110. El punto
binario, que debe ponerse, está en la derecha. Cuesta siete movimientos a la
izquierda hacer que el bit más significativo esté a la derecha del punto binario.
        El proceso es el siguiente:
    0 movimientos   0 1 1 1 1 1 1 0.
    1 movimientos   0 1 1 1 1 1 11 .0
    2 movimientos   0 1	1 1 1	1.1 0
    3 movimientos   0 1	1 1 1 . 1 1 0
       y así hasta el último
    7 movimientos   0. 1 1 1 1 1 1 0
        Cualquiera de los bits a la izquierda del punto binario estará simpre a 0;
por tanto, éste y el punto binario pueden ser descartados, dado que se conoce
su posición para cualquier número. Normalmente, nosotros hacemos esto con
números decimales, muchas veces inconscientemente, al no mostrar un punto
decimal a la derecha de un número entero, aunque todos sabemos dónde está.
        Este proceso nos da la parte del número conocida como la mantisa, que,
para el ejemplo dado, es 11111100 en binario, y el exponente es 7 (el número de
veces que el punto binario ha sido movido a la izquierda). El bit más significati-
vo de la mantisa será siempre 1, por lo que este bit puede ser usado para
mostrar el signo del número; 1 para un número negativo y 0 para uno
positivo.
        El exponente se expresa en binario con signo; el bit 7 está a 1, si el punto
binario ha sido movido a la izquierda, ya 0 si ha sido movido a la derecha. De
este modo, en el ejemplo anterior la mantisa era 7, en binario 00000111, pero el
punto se había movido a la izquierda; por tanto, el bit 7 debe estar a 1,
quedando 10000111 ó 135 decimal. Por consiguiente, el número expresado en
cinco bytes queda:
	Exponente	 Mantisa
Binario 	10000111 	01111100 	00000000 	00000000	00000000
Decimal	135	124	0	0	0
Hexadecimal	87	7C	0	0	0
        Normalmente, el número dado en el ejemplo anterior hubiera sido almace-
nado como entero pequeño, pero lo hemos usado aquí para simplificar.
        El calculador usa su propia pila, en la que guarda todos los números sobre

_______
82

 
los que esté trabajando, y lo primero que se debe hacer antes de realizar
cualquier cálculo es poner los números que van a utilizarse dentro de la pila del
calculador. Esto se puede realizar de tres formas diferentes :
1. Un número puede ser guardado en la pila cogiéndolo de un registro o
de una pareja de registros y usando una rutina de la ROM para
convertirlo al formato del calculador .
2. El número se puede cambiar a la forma usada por el calculador, y
entonces se guarda en la pila.
3. El número se puede escribir en la memoria en representación ASCII, y
se puede usar el comprobador de sintaxis del BASIC para leerlo y
situarlo dentro de la pila del calculador en la forma correcta.
        Cada método tiene sus ventajas e inconvenientes, y cada uno se presta a
diferentes tipos de números. Ahora consideraré el uso de cada uno de ellos.
1. Para números enteros pequeños hay dos rutinas que se pueden usar:

	CALL 11560 (2D28h)
	
        Esta se emplea poniendo el número que va a ser transferido a la
 pila del calculador dentro del registro A. Obviamente, el rango está
 limitado de 0 a 255 (0- FFh), y el número debe ser positivo.

	CALL 11563 (2D2Bh)

        Esta aceptará números en el rango de 0 a 65535 (0-FFFFh)
 almacenados en la pareja de registros BC. De nuevo el número sólo
 puede ser positivo, a menos que el inicio de la rutina sea evitado. La
 llamada se hace entonces a 11569 (2D31h), con el registro A puesto a 0
 y el registro E puesto a 255 (FFh) ; entonces el número será transferido
 al calculador como negativo.
2. Para números en forma de cinco bytes similar a la del calculador se
     usa la rutina siguiente :

	CALL 10934 (2AB6h)

        La representación de cinco bytes del número, como se describió
 anteriormente en Representación en punto flotante, debe estar en los
 registros A, E, E, C, B; el exponente, en el registro A, y la mantisa, en
 los otros cuatro registros en orden.
3. Representación ASCII:

	CALL 11419 (2C9Bh)

        Siempre que el intérprete de BASIC se encuentra con un número en
 una línea BASIC que se está introduciendo, sitúa la forma binaria de
 cinco bytes del número después de la versión ASCII, lista para usar
_______
             83

 
más tarde cuando el programa se ejecute. La rutina usada para hacer
esto puede realizar tambíén la conversión para un programa de código
máquina escrito por el usuario. Esta rutina evita todos los problemas
de conversión de números, bien manualmente o bien escribiendo una
rutina en su propio programa que los convierta, y merece una detallada
atención.
        Para usar esta rutina, la variable del sistema CH_ADD 23645
(5C5Dh) debe contener la dirección del primer carácter del número que
se quiere guardar, y el registro A debe contener el código del carácter
al que señala CH_ADD. El primer carácter será, normalmente, el dígito
más significativo de un número decimal; pero, ya que la rutina es la
que se usa para explorar las líneas del BASIC, podría ser el código
binario 196 (C4h), si los dígitos siguientes son un número binario. Este
caso sólo puede ser convertido a 16 dígitos, en decimal 65535 (FFFFh).
Cualquier intento de superarlo se convertirá en un salto a la rutina de
manejo de errores del BASIC. El formato con E (exponencial) puede ser
usado si así se desea, y el número debe estar ordenado exactamente
igual que si estuviera en una línea BASIC.
        Después de los caracteres que forman el número y, si se ha usado,
el exponente, se debe añadir un byte con valor 13 (0Dh). Esto permite a
la rutina saber que ha llegado al final del número y que no hay nada
más que buscar.
        Hay dos rutinas de la ROM que hacen lo opuesto a las dos rutinas de
apilar A y apilar BC. Estas toman la última entrada en la pila del calculador y
la convierten en un número entero, si es posible. Si el número es demasiado
largo, entonces la bandera de acarreo estará a 1 a la salida, y si el número es
negativo la bandera 0 se coloca a 0. Para un número positivo, la bandera
0 se colocará a 1. El número será borrado de la pila modificando el puntero,
pero la pareja de registros DE seguirá señalándolo en la memoria, permitiendo
recuperarlo si es necesario para alguna otra cosa, aunque es fácil duplicarlo
antes de intentar la recuperación, y entonces borrar la copia si el cálculo sale
bien. Las direcciones de llamada son :
PILA hacia A: CALL 11733 (2DD5H)
PILA hacia BC: CALL 11685 (2DA5H)
        Existe también la rutina contraria a la expuesta anteriormente en 11249
(2BF 1h), la cual devuelve el último valor de la pila a los mismos registros. Esta
rutina también borra el número de la pila, pero dado que no se puede
recuperar el número, las banderas no se modifican. Debe hacerse un duplicado
en la pila si se necesita tener el número en la misma después de la recuperación.
PILA hacia A, E, D, C, B: CALL 11249 (2BF 1 H)
        Cuando los números son generados desde dentro del programa, la rutina

_______
84

 
para imprimir un número desde la pila puede ser usada para "imprimir" el
número en un espacio de la memoria -lo opuesto a imprimirlo en pantalla-
en la forma ASCII. El número máximo de posiciones de memoria que se
necesitarán para un solo número es 14.
        Esto se consigue escribiendo una subrutina que sitúa los contenidos del
registro A dentro de la posición de la memoria siguiente cada vez que se le
llama, abriendo un canal que señala a su propia subrutina, y haciendo este
canal activo poniendo su dirección de base en CUR_CHL 23633 (5C51h). Se
puede llamar entonces a la rutina de la ROM en 11747 (2DE3h) para imprimir
el número. Su rutina será llamada entonces con cada carácter del número en
secuencia, que debe guardar en las posiciones de la memoria, preparado para
usar después. Recuerde que el índice que se usa para señalar a las direcciones
donde se guarda el número no puede ser guardado en un registro entre llamadas
a su rutina ni tampoco en la pila de la máquina; por tanto, se deben reservar
dos bytes de memoria para salvar esta dirección. Un programa que haga esto
puede ser algo así :
A2BB	21DFA2	10	MODIFI	LD	HL, ESPACIO
A2BE	22EFA2	20		LD	(ES_PA), HL
A2C1	2A515C	30		LD	HL, (23633)  ; CUR-CHL
A2C4	E5	40		PUSH	HL
A2C5	11D6A2	50		LD	DE, INICIO ; hace que su subrutina sea el
A2C8	4E	60		LD	C,  (HL) 	; canal actual y guarda la
A2C9	73	70		LD	(HL), E 	; direccion original para
A2CA	23	80		INC	HL 	; volverla a poner al final.
A2CB	46	90		LD	B,  (HL)
A2CC	72	100		LD	(HL), D
A2CD	C5	110		PUSH	BC
A2CE	CDE32D	120		CALL	11747  ; la rutina que imprime los numeros
A2D1	C1	130		POP	BC       ; Devuelve la direccion original
A2D2	E1	140		POP	HL       ; el numero del canal de destino.
A2D3	71	150		LD	(HL), C
A2D4	23	160		INC	HL
A2D5	70	170		LD	(HL), B
		180	; EL RESTO DEL PROGRAMA VIENE AQUI
A2D6	2AEFA2	190	INICIO	LD	HL,  (ES_PA)
A2D9	77	200		LD	(HL), A
A2DA	23	210		INC	HL
A2DB	22EFA2	220		LD	(ES_PA), HL
A2DE	C9	230		RET
A2DF		240	ESPACI	DEFS	16
A2EF	DFA2	250	ES_PA	DEFW	ESPACIO
        Esta es la rutina usada y comentada previamente. Cuando se la llama,
toma la parte superior de la pila del calculador y la saca en ASCII, como un
número decimal, por la corriente actual. El número se retira de la pila.
        Siempre que se use el calculador, es importante asegurarse de que la pila
está equilibrada. El calculador siempre obra de una forma prefijada (cuando se
usa correctamente), y dará resultados erróneos cuando está desequilibrado.
También se debe dejar espacio entre RAMTOP y STKBOT, para que la pila
del calculador crezca hacia arriba y la pila de la máquina hacia abajo sin
chocarse entre sí.
        Dado que algunas rutinas del calculador lo usan recursivamente, será
necesarío dejar más espacio del que aparentemente usa. De hecho, es mejor

_______
             85

 
pecar de exceso debido a este hecho. Si existe alguna duda de qué es lo que hay
en la pila después de completar algunas operaciones, se puede vaciar con un
CALL 5823 (16BFh), pero recuerde que esto borrará todo lo que hay en la pila.
Uso del calculador
        Para entender cómo manejar el calculador, puede ayudarle a comprenderlo
el considerarlo como un procesador separado, con su propio conjunto de
instrucciones, que se conectan con un RST 40 (28h) y desconectan con un
ENDCALC, código 56 (38h).
        Durante el tiempo que el calculador está encendido, sus códigos se cogerán
de las memorias siguientes a la RST 40. Estos son una serie de bytes definidos
en un programa normal de Z80. Cuando el calculador encuentra una instruc-
ción ENDCALC, el control es devuelto al Z80, y éste continúa la ejecución del
programa desde la dirección siguiente a ENDCALC. Algunos códigos necesitan
operandos, y otros sólo pueden utilizarse al comienzo de una serie, dado que
requieren que los registros Z80 estén colocados de una forma especial para
operar.
        Cada vez que se usa la pila, su longitud varía en cinco bytes. Siempre que
un número es situado en la pila se comprueba si hay espacio. Cuando el espacio
no es suficiente, se producirá un error en el BASIC.
        El calculador no está limitado sólo a operaciones numéricas; se usa
también para producir una cadena de BASIC y funciones VAL. Estas las
estudiaremos más tarde.
        Los códigos que comprende el calculador se explican a continuación, con
la descripción de su operación. En cada caso, el cambio de la pila del calculador
se da en bytes -se usan cinco por cada valor- para la ejecución, por el
calculador, de un solo código.
        X es el valor por debajo de Y en la pila del calculador, siendo Y el último
valor almacenado. El resultado de un cálculo se deja siempre en la parte superior
de la pila, y este resultado es representado por Z. Por ejemplo, para el código de
resta (03), si X = 5 y Y = 9, X se colocará dentro de la pila del calculador
seguida por Y, la RST 40 (28h) estará seguida por un byte 03. Después de la
operación, la pila del calculador contiene - 5, que es el resultado que hemos
denominado Z. Además, X e Y serán borradas.
        Los saltos se hacen desde la posición en la que se almacena el operando
que indica la distancia según la forma estándar Z80.
Código 	Función 			Operación

	00 	SALTA SI CIERTO 	Salta la distancia (en notación de complemen-
				to a 2) indicada en el operando (el byte defini-
				do después del código 00) y si y no es 0.
	(CAMBIO PILA - 5)
	01 	INTERCAMBIA 		Invierte el orden de X e Y en la pila
	(CAMBIO PILA 0)
_______
86

 
 	02  	BORRAR   		Quita Y de la pila. Z = X.
           (CAMBIO PILA - 5)
 	03 	RESTAR  		X - Y = Z 	           (CAMBIO PILA - 5)
 	04 	MUL TIPLICAR  	X * Y = Z 	           (CAMBIO PILA - 5)
 	05 	DIVIDIR  		X / Y = Z 	           (CAMBIO PILA - 5)
 	06  	ELEVAR A POTENCIA  	X'Y = Z   	           (CAMBIO PILA - 5)
	 07  	OR BINARIO   		X o Y. Si = 0 entonces Z = X; si no, Z = 1
 						           (CAMBIO PILA - 5)
 	08  	AND BINARIO  	 	X y Y. ambos deben ser números. Si y = 0,
     				entonces Z = 0; si no, Z = X. Hay un código
     				distinto para manejar cadenas: 16 (10h).
 						           (CAMBIO PILA - 5)
        La secuencia de códigos desde el 09 al 14 (0Eh) maneja valores numéricos.
Cada código sólo puede ser usado como la primera operación después del RST
40 (28h), ya que el registro B debe contener el código en el momento de
realizarse la operación.
        Hay un segundo grupo de códigos para comparaciones de cadenas; todos
vuelven con Z = 1, si la comparación da resultado correcto, o Z = 0, si es
falsa. (CAMBIO PILA - 5)
	09 	Y < = X			10 (0Ah) y > = X 	    11 (0Bh) y < > X
	12 	(0Ch) Y > X		 13 (0Dh) Y < X 		       14 (0Eh) Y = X
	15 	(0FH) ADICION	 	      X + Y = Z 	             (CAMBIO PILA- 5)
        En la siguiente secuencia de códigos, X y/o y deben contener los paráme-
tros de una cadena. Los detalles de cómo conseguir estos parámetros y cómo
ponerlos en la pila del calculador se dan posteriormente. La parte o partes que
son parámetros de cadenas están marcadas con el símbolo $.
Código 	         Función 		Operación
	16 	(10h) $ Y n.º 		X$ y Y. Si y = 0, entonces Z$ será una cade-
						        na vacía. (CAMBIO PILA = 5)

        Los códigos del 17 al 22 (11h-16h) son los equivalentes para cadenas de los
códigos 09-14 (0Eh) y son llamados con el registro B conteniendo el código. De
nuevo Z = 1, la comparación es correcta, o Z = 0, si es falsa.
            (CAMBIO PILA - 5)
	17	(11h) Y$ < = X$		18 (12h) Y$ > = X$	19 (13h) Y$ < > X$
	20	(14h) Y$ > X$		21 (15h) Y$ < X$		   22 (16h) Y$ = X$
	23	(17h) ADICCION	X$ + Y$ = Z$. Las dos cadenas son unidas
				en el área de trabajo, y los nuevos parámetros
				se devuelven en Z$. Recuerde que si no hay
				espacio para copiar las dos cadenas dentro del
				área de trabajo se producirá un error del
				BASIC.		            (CAMBIO PILA - 5)
_______
             87

	24 	( 18h) VAL$ 		VAL$ Y$ = Z$. La nueva cadena se crea en el
					área de trabajo de la misma forma que antes.
					El código debe estar contenido también en el
					registro B cuando se realiza la llamada. Cual-
					quier tipo de error causará un error del BA-
					SIC. Esta es la rutina usada por el intérprete
					de BASIC, y está sujeta a todo el proceso de
					comprobación de sintaxis (CAMBIO PILA 0)
	25 	 (19h) USR$		Z = USR$. Este también es usado por el in-
					térprete de BASIC,y está sujeto a la comproba-
					ción de sintaxis. Y$ debe contener los paráme-
					tros de una variable alfanumérica (terminada
					en $), que contenga una sola letra desde la A a
					la U. Al terminar, Z será la dirección del
					gráfico definible por el usuario.
								 (CAMBIO PILA 0)
	26	 ( 1 Ah) LEER EN	Esta rutina permite poner un solo byte en el
					AREA DE TRABAJO mediante cualquier co-
					rriente de la 0 a la 15 (0Fh), según se le
					indique en Y. El byte es trata como si fuese
					una cadena, y Z$ son los parámetros de esta
					cadena. Si la bandera de acarreo no está pues-
					ta a 1 por la rutina de entrada, no se realiza
					ninguna acción y Z$ es devuelta como una
					cadena nula.
	27	 (1 Bh) NEGAR		Z = Y, pero con el signo cambiado.
	 	 	 	 	 	 	 	 (CAMBIO PILA 0)
	28	 (lCh) CODE		Z = CODE Y$. Tal como lo usa el intérprete
					BASIC. (CAMBIO PILA 0)
	29	 (lDh) VAL		Z = ZAL Y$. Tal como lo usa el intérprete
					BASIC y en realidad accede al comprobador
					de líneas para dar el resultado en Z. Sujeto a
					los errores del BASIC. (CAMBIO PILA 0)
	30	 ( 1 Eh) LEN		Z = LEN Y$. Esta es más fácil de hacer sin
					usar el calculador; todo lo que se hace es
					apilar los bytes de tamaño de los parámetros
					de la cadena. (CAMBIO PILA 0)
 
        Todas las funciones algebraicas siguientes devuelven la respuesta (Z) en la
parte superior de la pila del calculador y el tamaño de ésta no cambia.
   Código     Operación
   31 	(lFh)   Z = SIN Y
   32 	(20h)  Z = COS Y
   33 	(21h)  Z = TAN Y
   34 	(22h)  Z = ASN Y
_______
88

 
	35 	(23h)  Z = ACS Y
	36 	(24h)  Z = ATN Y
	37 	(25h)  Z = LN Y
	38 	(26h)  Z = EXP Y
	39 	(27h)  Z = INT Y
	40 	(28h)  Z = SQR y
	41 	(29h)  Z = SGN y
	42 	(2Ah) Z = ABS Y

	43 	 (2Bh) PEEK 		Z = PEEK Y. Tal como lo usa el intérprete
					BASIC. 			(CAMBIO PILA 0)
	44	 (2Ch) IN		Z = IN Y. Esta ejecuta la instrucción del Z80
					IN A, (C) después de pasar el Y de la pila a la
					pareja de registros BC como un número ente-
					ro. 			(CAMBIO PILA 0)
	45 	 (2Dh) USR No. 		Precaución. Esta producirá la ejecución de un
					salto a la dirección Y. Es usada por el BASIC
					para saltar al código máquina. La dirección de
					retorno será 11563 (2D2Bh); la rutina pasará
					el valor en el registro BC a la pila al serle
					devuelto el control. Este es un código intere-
					sante, dado que abre la posibilidad de usar
					rutinas de la ROM y la RAM recursivamente
					desde dentro del calculador.
					 			(CAMBIO PILA 0)
	46	(2Eh) STR$ 		Z$ = Y. El valor superior de la pila del calcu-
					lador se imprime en el AREA DE TRABAJO
					y se evalúa como una cadena; sus parámetros
					se ponen en la pila del calculador .
					 			(CAMBIO PILA 0)
	47 	 (2Fh) CHR$		Z$ = CHR$ Y. Si 0 < Y < 255, entonces se
					crea un espacio en el AREA DE TRABAJO, y
					el valor es transferido allí como un byte. Este
					se interpreta entonces como una cadena, y sus
					parámetros se devuelven en Z$.
					 			(CAMBIO PILA 0)
	48 	 (30h) NOT		Z = 1, si Y = 0; si no, Z = 0.
					 			(CAMBIO PILA 0)
	49 	 (31h) DUPLICAR	Z = Y. Y es duplicada.
					 		        (CAMBIO PILA + 5)
	50 	 (32h) X MOD Y		Esta devuelve Z = INT (X/Y) y debajo de Z
					 (donde X estaba en un principio) X - INT
					(X/Y). 			(CAMBIO PILA 0)
	51 	 (33h) SALTO		Esta lo único que hace es el equivalente de
					una instrucción del Z80 JR (salto relativo),
					pero para el calculador. La longitud del salto
_______
             89

 
_______
90
					se toma de la posición siguiente al código. La
					pila del calculador no es modificada.
		 		 				(CAMBIO PILA 0)
	52 	(34h) STK DATA	Esta permite leer un valor de las posiciones
					siguientes al código. Los bits 6 y 7 del primer
					byte (el exponente) que está a continuación del
					código determinan el número de bytes que
					forman la mantisa, 00 BIN, para 1, al 11 BIN,
					para 4. Entonces se almacena el número en
					punto flotante en la pila del calculador .
					 		          (CAMBIO PILA + 5)
	53	 (35h) DEC JR NZ	Esta es una equivalente directa del código del
					Z80 DJNZ, pero para el calculador. Se toma
					la variable del sistema BREG como si fuera el
					registro B. No es segura para usar,dado que
					esta rutina es usada ampliamente por el calcu-
					lador para sus propios fines, y el valor de
					BREG puede variar. 	(CAMBIO PILA 0)
	54	 (36h) Y < 0		Z = 1, si Y < 0; si no, Z = 0.
					 			(CAMBIO PILA 0)
	55	 (37h) Y > 0		Z = 1, si Y > 0; si no, Z = 0.
		 	 	 	 	 		(CAMBIO PILA 0)
	56	(38h) ENDCALC		Devuelve el control al Z80 en la	siguiente
					dirección. 		(CAMBIO PILA 0) 
	57	(39h) COGE ARGT	Usada internamente por el calculador para
					establecer el valor de Y (que para el fin de Z
					como resultado, será Z) en SIN Y o COS Y.
					 			(CAMBIO PILA 0)
	58	 (3Ah) TRUNCAR	Z = INT Y, donde Z es Y truncada hacia 0.
	 	 	 	 	 	 		(CAMBIO PILA 0)
	59	 (3Bh) FP CALC 2	Usado por el intérprete para ejecutar una sola
					operación del calculador. No tiene uso prácti-
					co para los fines de este libro. 
	60	(3CH) E A FP		Z = Y * (10 elevado al registro A). 
	 	 	 	 	 	 		(CAMBIO PILA 0) 
	61	 (3Dh) RE APILA	Z = la versión en punto flotante de Y, donde
					Y puede ser un número entero pequeño.
					 			(CAMBIO PILA 0)
	62	 (3Eh) SERIES		Usado internamente por el calculador para ge-
					nerar un polinomio de Chebyshev. Práctica-
					mente no se usa, ya que es llamado por las
					rutinas que lo necesitan.
	63	(3Fh) APILAR No. 	Como antes, pero usado para apilar constan-
					tes.
	64	 (40h) ST MEM		Usado para almacenar en el área de la memo-
					ria. A la entrada, el registro A debe contener

 
					un valor comprendido entre C0 y C5, según
					cuál de las cinco posiciones de la memoria se
					va a usar	.	    (CAMBIO PILA - 5)
	65	 (41h) REC MEM 	Recuperación de memoria. Es la inversa de la
					rutina anterior. 	    (CAMBIO PILA + 5)

        Para las operaciones de cadenas, los parámetros pueden ser pasados a la
pila del calculador mediante la rutina en 10934 (2AB6h) ya mencionada. La
pareja de registros BC contiene la longitud de la cadena; la dirección de inicio
está en la pareja de registros DE, y el nombre del registro en A (en la forma
comentada en el capítulo 2 en Salvando, cargando y verificando.).
        Para comprender mejor el calculador, encontrará en el apéndice G, Subruti-
nas más útiles, una rutina que le permitirá hacer experimentos con él.
_______
             91

 

Apéndice A
Conversión
de hexadecimal
a decimal
MSB
	0	1	2	3	4	5	6	7	8	9	A	B	C	D	E	F

0	0	256	512	768	1024	1280	1536	1792	2048	2304	2560	2816	3072	3328	3584	3840

1	4096	4352	4608	4864	5120	5376	5632	5888	6144	6400	6656	6912	7168	7424	7680	7936

2	8192	8448	8704	8960	9216	9472	9728	9984	10240	10496	I0752	11008	11264	11520	1l776	12032

3	12288	12544	12800	13056	13312	13568	13824	14080	14336	14592	14848	15104	15360	15616	15872	16128

4	16384	1664O	16896	17152	17408	17664	17920	18176	18432	18688	18944	19200	19456	19712	19968	20224

5	20480	20736	20992	21248	21504	21760	22016	22272	22528	22784	23040	23296	23552	23808	24064	24320

6	24576	24832	25088	25344	25600	25856	26112	26368	26624	26880	27136	27392	27648	27904	28l6O	28416

7	28672	28928	29184	29440	29696	29952	30208	30464	30720	30976	31232	31488	31744	32000	32256	32512

8	32768	33024	33280	33536	33792	34048	34304	34560	348l6	35072	35328	35584	35840	36096	36352	36608

9	36864	37120	37376	37632	37898	38144	38400	38656	38912	39168	39424	39680	39936	40192	40448	40704

A	40960	41216	41472	41728	41984	42240	42496	42752	43008	43264	43520	43776	44032	44288	44544	44800

B	45056	45312	45568	45824	46080	46336	46592	46848	47104	47360	476l6	47872	48128	48384	48640	48896

C	49152	49408	49664	49920	50176	50432	50688	50944	51200	51456	51712	51968	52224	52490	52736	52992

D	53248	53504	53760	540l6	54272	54528	54784	55040	55296	55552	55808	56064	56320	56576	56932	57088

E	57344	57600	57856	58112	58368	58624	58880	59136	59392	59648	59904	60l60	604l6	60672	60928	61184

F	61440	61696	61952	62208	62464	62720	62976	63232	63488	63744	64000	64256	64512	64768	65024	65280
_______
             93

 
LSB
	0	1	2	3	4	5	6	7	8	9	A	B	C	D	E	F

0	0	1	2	3	4	5	6	7	8	9	11	10	12	13	14	15

1	16	17	18	19	20	21	22	23	24	25	26	27	28	29	30	31

2	32	33	34	35	36	37	38	39	40	41	42	43	44	45	46	41

3	48	49	50	51	52	53	54	55	56	57	58	59	60	61	62	63

4	64	65	66	61	6B	69	10	71	72	13	74	75	16	71	18	19

5	80	81	82	83	84	85	86	87	88	89	90	91	92	93	94	95

6	96	97	98	99	100	101	102	103	104	105	106	107	108	109	110	111

7	112	113	114	115	116	117	118	119	120	121	22	123	124	125	126	127

8	128	129	130	131	132	133	134	135	136	137	138	139	140	141	142	143

9	144	145	146	147	148	149	150	151	152	153	154	155	156	157	158	l59

A	160	161	162	163	164	165	166	167	168	169	170	171	172	173	174	175

B	176	177	178	79	180	181	182	183	184	185	186	187	189	189	190	191

C	192	193	194	195	196	191	198	199	200	201	202	203	204	205	206	201

D	208	209	210	211	212	213	214	215	210	211	218	219	220	221	222	223

E	224	225	226	221	228	229	230	231	232	233	234	235	236	231	238	239

F	240	241	242	243	244	245	246	241	248	249	250	251	252	253	254	255

Nibbles

	HEX	DEC	BIN	HEX	DEC	BIN
 	0	0	0000	8	8	1000
	1	1	0001	9	9	1001
	2	2	0010	A	10	1010
	3	3	0011	B	11	1011
	4	4	0100	C	12	1100
	5	5	0101	D	13	1101
	6	6	0110	E	14	1110
	7	7	0111	F	15	1111
_______
94

 
Apéndice B
Mapa de memoria
del Spectrum
(23675) UDG

(23730) RAMTOP

(23653) STK  END

PILA DEL CALCULADOR

(23651) STK BOT

AREA DE TRABAJO 

(23649) WORKSP

AREA DE EDICION

(23641) E LINE 

VARIABLES

(23627) VARS

BASIC

(23635) PROG

INFOR. CANALES 

(23631) CHANS

MAPAS MICRODR
_______
             95
VARIABLES
FIJAS
PRAMT    FFFFh o 7FFFh
65535  o 32767
5CEFh 23731 ULTIMO BYTE 
DE LAS VARIABLES DE 8K
5CB6h 23733 ULTIMO BYTE 
DE LAS VARIABLES DE 16K

5BFFh 23551 ULTIMO BYTE 
DE LA MEMORIA 
INTERMEDIA DE LA 
IMPRESORA
5AFFh 23295 ULTIMO BYTE 
DEL MAPA DE ATRIBUTOS 
DE LA PANTALLA
57FFh 22527 ULTIMO BYTE 
DEL MAPA DE PANTALLA

3FFFh 16383 ULTIMO BYTE 
DE LA ROM DE 16K

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

	

	

	

	

 
Apéndice C
Mapa de pantalla
del Spectrum
        La pantalla del Spectrum está dividida en 192 filas de 256 puntos. Cada
línea contiene 32 bytes de memoria en orden ascendente. A continuación se lista
las direcciones del primer byte de cada línea de pantalla con la dirección del
atributo que controla a éste. La línea 1 representa la línea superior en la
pantalla.
	LINEA	PIXEL	ATRIBUTO
	1	4000H	5800H	25	4060H	5860H
	4	100H		26	4160H
	3	4200H		27	4260H
	4	4300H		28	4360H
	5	4400H		29	4460H
	6	4500H		30	4560H
	7	4600H		31	4660H
	8	4700H		32	4760H
	9	4020H	5820H	33	4080H	5880H
	10	4120H		34	4180H
	11	4220H		35	4280H
	12	4320H		36	4380H
	13	4420H		37	4480H
	14	4520H		38	4580H
	15	4620H		39	4680H
	16	4720H		40	4780H
	17	4040H	5840H	41	40A0H	58A0H
	18	4140H		42	41A0H
	19	4240H		43	42A0H
	20	4340H		44	43A0H
	21	4440H		45	44A0H
	22	4540H		46	45A0H
	23	4640H		47	46A0H
	24 	4740H 		48 	48A0H
_______
96

_______
             97
 
	49	40C0H	58C0H	121	48E0H	59E0H
	50	41C0H		122	49E0H
	51	42C0H		123	4AE0H
	52	43C0H		124	4BE0H
	53	44C0H		125	4CE0H
	54	45C0H		126	4DE0H
	55	46C0H		127	4EE0H
	56	47C0H		128	4FE0H
	57	40E0H	58E0H	129	5000H	5A00H
	58	41E0H		130	5100H
	59	42E0H		131	5200H
	60	43E0H		132	5300H
	61	44E0H		133	5400H
	62	45E0H		134	5500H
	63	46E0H		135	5600H
	64	47E0H		136	5700H
	65	4800H	5900H	137	5020H	5A20H
	66	4900H		138	5120H
	67	4A00H		139	5220H
	68	4B00H		140	5320H
	69	4C00H		141	5420H
	70	4D00H		142	5520H
	71	4E00H		143	5620H
	72	4F00H		144	5720H
	73	4820H	5920H	145	5040H	5A40H
	74	4920H		146	5t40H
	75	4A20H		147	5240H
	76	4B20H		148	5340H
	77	4C20H		149	5440H
	78	4D20H		150	5540H
	79	4E20H		151	5640H
	80	4F20H		152	5740H
	81	4840H	5940H	153	5060H	5A60H
	82	4940H		154	51b0H
	83	4A40H		155	5260H
	84	4840H		156	53b0H
	85	4C40H		157	5460H
	86	4D40H		158	5560H
	87	4E40H		159	5660H
	88	4F40H		160	5760H
	89	4860H	5960H	161	5080H	5A80H
	90	4960H		162	5180H
	91	4A60H		163	5280H
	92	4B60H		164	5380H
	93	4C60H		165	5480H
	94	4Db0H		t66	5580H
	95	4E60H		167	5680H
	96	4F60H		168	5780H
	97	4880H	5980H	169	50A0H	5AA0H
	98	4980H		170	51A0H
	99	4A80H		171	52A0H
	100	4880H		172	53A0H
	101	4C80H		173	54A0H
	102	4D80H		174	55A0H
	103	4E80H		175	56A0H
	104	4F80H		176	57A0H
	105	48A0H	59A0H	177	50C0H	5AC0H
	106	49A0H		178	51C0H
	107	4AA0H		179	52C0H
	108	48A0H		180	53C0H
	109	4CA0H		181	54C0H
	110	4DA0H		182	55C0H
	111	4EA0H		183	56C0H
	112	4FA0H		184	57C0H
	113	48C0H	59C0H	185	50E0H	5AE0H
	114	49C0H		186	51E0H
	115	4AC0H		187	52E0H
	116	4BC0H		188	53E0H
	117	4CC0H		189	54E0H
	118	4DC0H		190	55E0H
	119	4EC0H		191	56E0H
	120	4FC0H		192	57E0H

 
Apéndice D
El mapa
del teclado
	V	C	X	Z	C/S	
						
254 (FEH)						
253 (FOH)	G	F	0	5	A	
251 (FBH)	T	R	E	W	Q	
247 (F7H)	5	4	3	2	1	
239 (EFH)	6	7	8	9	0	
223 (OFH)	Y	U	I	O	P	
191 (BFH)	H	J	K	L	ENT	
127 (7FH)	B	N	M	S/S	B/S	

_______
98
    D4                    D3                   D2                    D1                   D0

 
Apéndice E
El juego
de caracteres
del Spectrum
Código Carácter 		Notas especiales

0 y 1 	Nada 		Usados sólo después de INK, PAPER, OVER, INVER-
			SE, FLASH, BRIGHT, AT o TAB.
2-5 	Nada 		Usados sólo después de INK, PAPER, AT o TAB.
6 			Hace una tabulación (TAB) a la siguiente posición de
			media pantalla.
7 	EDIT 		Código devuelto por la rutina de entrada del teclado si
			se pulsan CAPS SHIFT y 1. No imprimible. Frecuente-
			mente el código BELL en las impresor as y terminales.
8 	RETROCESO 	Código devuelto por la rutina de entrada del teclado
			si se pulsan CAPS SHIFT y '5'. Se puede imprimir en
			la pantalla para retroceder un espacio. También es
			reconocido por muchas impresoras, ya que es el código
			ASCII de retroceso.
9 	AVANCE 	Código devuelto por la rutina de entrada del teclado si
			se pulsan CAPS SHIFT y '8'. No se puede usar para
			mover un espacio a la derecha en la pantalla, ya que el
			Spectrum no actualiza las posiciones de pantalla des-
			pués de su uso. Código ASCII correspondiente al TAB.
10 	ABAJO 		Como el anterior, pero con CAPS SHIFT y '6'. Es el
			código ASCII de salto de línea.
_______
             99

 
11 	ARRIBA 	Como el anterior, pero con CAPS SHIFT y '7'. Código
			ASCII de tabulación vertical.
12 	BORRAR 	Como el anterior, pero con CAPS SHIFT y 'O'. Salto
			de hoja en ASCII.
13 	ENTER 		Código devuelto cuando se pulsa la tecla ENTER ;
			ejecuta un retorno de carro (vuelve a la posición iz-
			quierda) y salto de línea cuando se imprime en la
			pantalla. Es también el código ASCII para el retorno de
			carro.
14 			Precede a un número en una línea de un programa
			BASIC. No se usa prácticamente. El código SO en
			ASCII.
15 			No se usa en el Spectrum. Código SI en ASCII.
16 			Código de control de la tinta usado para preceder al
			número del color de la tinta (INK). Por ejemplo, para
			cambiar a rojo todos los caracteres siguientes que se
			impriman en pantalla, deberá usar la rutina RST 16
			con el registro A conteniendo un 16 seguido por 2.
			Nótese que no es el caracter ASCII '2', sino el valor 2.
			Esto se demuestra en el programa DeBASE.
17 			Como el anterior, pero para el papel (PAPER).
18 			Igual al anterior, para parpadeo (FLASH); el código
			siguiente puede ser sólo 0 ó 1.
19 			Como el anterior, para brillo (BRIGHT).
20 			Como el anterior, para inverso (INVERSE).
21 			Como el anterior, para OVER.
22			Código decontrol AT; debe estar seguido por los
			valores de la línea y de la columna.
23 	Como el anterior, pero con TAB ; sólo necesita el valor
de la columna.
        Los códigos del 24 al 31 no son usados por el Spectrum; sin embargo, el
código 27 es el código escape usado por muchos periféricos, seguido por una
letra, para indicar la acción que se pide. Mientras que el CHR$ 27 ha sido
estandarizado como el código escape, no ha sucedido así con los códigos que le
siguen.
        Los códigos que quedan son todos representaciones de caracteres, con los
códigos del 32 al 126 formando el conjunto ASCII estándar y que se muestran en
el manual del Spectrum.
        El código 127 --el símbolo copyright (©)- es el utilizado para borrar
caracteres del ASCII. ¡T enga cuidado!
_______
100

 
Apéndice F
Vectores
de interrupción
de la ROM
	16K ROM 	8K ROM

	Mk 2 Spectrum 	Mk 1 Interface

I = 0 	204300 	23755
I = 1	52818	8501
I = 2	22269	25888
I = 3	39020	4196
I = 4	10419	52486
I = 5	2294	6701
I = 6	29149	51711
I = 7	16039	32459
I = 8	2088	14353
I = 9	65129	58170
I = 10	32802	1200
I = 11	58888	2039
I = 12	53183	59861
I = 13	52503	8205
I = 14	14367	49921
I = 15	27928	58884
I = 16	51984	32742
I = 17	8729	32477
I = 18	52481	4831
I = 19	49749	21965
I = 20	25705	56790
I = 21	51673	45182
I = 22	51568	65535
I = 23	12493	57851
I = 24	15582	61947
I = 25	23842	60952
I = 26	13824	52189
I = 27	7306	16129
I = 28	49947	6400
I = 29	2344	4870
I = 30	26573	65535
_______             101

 
	16K ROM 	8K ROM

	Mk 2 Spectrum 	Mk 1 Interface

I = 31 	3360 	57855
I = 32	52513	23755
I = 33	33485	8501
I = 34	544	2588B
I = 35	49537	4196
I = 36	8527	52486
I = 37	23670	6701
I = 38	20'444	51711
I = 39	288	32459
I = 40	32348	14753
I = 41	58154	58170
I = 42	19754	1200
I = 43	23653	2039
I = 44	7117	59B61
I = 45	55781	8205
I = 46	23713	49921
I = 47	4569	58B84
I = 48	60208	32742
I = 49	576401	32477
I = 50	13627	4831
I = 51	13256	21965
I = 52	1560	567910
I= 53	57124	45182
I = 54	34307	65535
I = 55	41231	57851
I = 56	65535	61947
I = 57	65535	60952
I = 58	65575	52189
I = 59	65535	16129
I = 60	255	6400
I = 61	0	4870
I = 62 	255 	65535
I = 63 	60 	255
_______
102