He hecho una rutina en código máquina para disponer de una copia del último contenido del Búfer de Entrada.
USO
Para recuperar la copia basta pulsar simultáneamente las teclas Z y X (son las que he escogido, podría haber sido cualquier otro par de teclas con tal que pulsadas simultáneamente no generen ningún carácter)
Cada vez que se pulsa la tecla ENTER estando en el Búfer de Entrada Inicial se guarda automáticamente una copia de su contenido. Más adelante, si pulsamos las teclas Z y X simultáneamente "estando en el Búfer de Entrada Inicial, se repone el contenido anterior (el actual se pierde).
Cuando digo "Búfer de Entrada Inicial" (aka BEI) me refiero al por defecto del JA. No sirven ni el Búfer de Entrada de Edición ni los generados por palabras como QUERY, RETYPE, etc.
Concretamente, si cometemos un error que el JA te permite corregir (cuando se pone el "?" en video inverso al principio) este contenido no se guarda al pulsar ENTER con lo que se mantiene el anterior contenido salvado.
Si usamos EDIT tampoco salva nada, ni es necesario, pues si abortamos la edición de una palabra, siempre podemos volverla a Editar.
Es útil cuando estamos trabajando en modo interactivo. Pues nos permite recuperar una línea (o parrafada) de comandos y volverlos a ejecutar (iguales o modificados).
También es útil si estamos definiendo una palabra nueva y tras escribirla de una tangada, al entrarla se genera un ERROR 5 (estructuras mal puestas como por ejemplo olvidarnos el THEN de un IF anterior o el LOOP de un DO, etc) pues en este caso tendríamos que volver a escribirlo todo desde el principio. Ahora basta con pulsar Z+X y recuperamos todo.
Si pulsamos ENTER con el BEI vacío, se pierdo lo guardado pues siempre se guarda el último contenido del BEI al pulsar el ENTER y en este caso es un espacio en blanco.
REQUISITOS
Tal como lo he programado (se puede modificar) espera un JA con 48K de memoria extra (64 en total: ROM+RAM).
En mi caso he usado el emulador EightyOne versión 1.28 pues con otras más antiguas (como la 1.23) no me funcionó.
Se reserva cerca de 1KB de la memoria superior (Desde $FC20 hasta $FFFF) de los cuales 734 son espacio para almacenar el BEI (734 es el tamaño máximo posible)
Incluyo un diccionario llamado oldbuff.tzx que contiene tres palabras:
- LIMIT que reserva la zona de memoria superior desde $FC20 hasta $FFFF ambos incluidos
- PUTBUFF que carga y arranca la rutina guardada en el PF de SAVEBUFF
- SAVEBUFF es una palabra tipo CREATE que almacena la rutina en código máquina en su PF.
INSTALACION y Puesta en Marcha
- Ejecutar LIMIT
- Ejecutar PUTBUFF
- (opcional) FORGET SAVEBUFF para borrar estas tres palabras (una vez arrancada la rutina ya no hacen falta)
Se han de ejecutar por separado LIMIT y PUTBUFF, pues LIMIT realiza internamente un QUIT necesario para que entre en vigor el RAMTOP (límite de memoria usable) y esto provoca que cualquier palabra que venga después no se ejecute.
FUNDAMENTO
Al arrancar la rutina se pone el JA en modo de Interrupción 2 (ver hilo Jupiter Ace Interrupción Modo 2) apuntando a la rutina que llamaré "Linicio" (es la etiqueta dónde empieza en el código fuente correspondiente). Así esta rutina se ejecuta 50 veces por segundo y después de cada ejecución prosigue con la rutina de interrupción original del JA.
Lo que hace es:
1- Comprobar si se ha pulsado ENTER (lee el último carácter tecleado de la variable del sistema del JA KEYCOD) y en caso afirmativo copia el BEI a un bloque de memoria con la etiqueta Buffer y también se guardan los valores de las cuatro variables del sistema del JA relacionadas con el BEI que son CURSOR, L_HALF, INSCRN y ENDBUF (ver manual del JA página 141)
2- Comprobar si se han pulsado simultáneamente las teclas Z y X leyendo el puerto $FEFE y en caso afirmativo se restaura lo almacenado en el bloque de memoria Buffer y también se restauran los valores de las cuatro variables del sistema del JA relacionadas con el BEI a los valores que tenían cuando se copió el BEI.
En ambos casos comprueba que estemos en el BEI y no en "otro" Búfer de entrada. Para ello comprueba el valor de registro SP que debe ser un valor concreto (función del valor de ORIGEN del programa escogido)
(Opcional)
Se puede volver al modo 1 de interrupción, con lo que deja de funcionar la copia/restauración del búfer, haciendo:
65489 CALL
Para volver al modo 2 de interrupción nuevamente reactivando la copia/restauración:
65292 CALL o ejecutando de nuevo PUTBUFF
OJO: No usar el que activa el modo 1 si ya estamos en modo 1 ni el que activa el modo 2 si ya estamos en modo 2. Puede colgarse el JA.
LIMIT Coloca en la variable del sistema RAM (15384/3C18) del JA la dirección del primer byte del bloque de memoria (64544/FC20) que NO puede utilizar FORTH. Este bloque empieza en la dirección almacenada en RAM y se extiende hasta el final de la memoria que en nuestro caso es 65535/FFFF.
Código: Seleccionar todo
: LIMIT
-992 15384 ! QUIT
;
NOTA: El JA cuando lista/edita palabras con LIST/EDIT interpreta los números CON SIGNO. Es por ello que al listar LIMIT se muestra -992 que es 64544 considerado CON SIGNO:
-992 DUP . U. <ENTER>
Imprimirá en pantalla (INVIS activado):
-992 64544
PUTBUFF Copia la rutina en código máquina almacenada en SAVEBUFF en la zona de memoria reservada. Empieza en 64544/FC20 y llega hasta 65492/FFD4 (949 bytes en total). Antes comprueba que se haya modificado RAM al valor correspondiente y una vez copiada la rutina la arranca. A partir de este momento se pueden borrar las tres palabras, pues ya no son necesarias.
NOTA: Por lo mismo dicho con LIMIT, los números se muestran CON SIGNO:
-992 es 64544/FC20
-43 es 65493/FFD5
-244 es 65292/FF0C
Código: Seleccionar todo
: PUTBUFF
15384 @ -992 = 0=
IF
CR CR ." RAMTOP Incorrect!"
CR CR ." Use LIMIT before this one."
CR QUIT
THEN
SAVEBUFF -43 -992
DO
DUP C@ I C! 1+
LOOP
DROP -244 CALL CR CR
." Z+X keys to retrieve Old buffer "
CR
;
SAVEBUFF Solo almacena el código máquina que ha de cargar en memoria PUTBUFF. EL listado que pongo a continuación es un "cargador" Forth generado a partir del SAVEBUFF original.
El SAVEBUFF original se hizo de otra manera:
Primero generamos con TASM el fichero restbuff.BIN con el código máquina de la rutina (cuyo Código Fuente listo más abajo). Ocupa 949 bytes.
Ahora hacemos: CREATE SAVEBUFF 949 ALLOT con lo que tenemos SAVEBUFF con un PF (Parameter Field) de 949 bytes.
Al ejecutar SAVEBUFF nos deja en la pila la dirección de inicio de su PF. Con este dato y con la opción "Load Memory Block" del menú "File" del EightyOne (es el que he usado, pero también servirían otros emuladores) cargo en memoria restbuff.BIN poniendo como "Address" la dirección del inicio del PF de SAVEBUFF. Con esto ya tenemos el SAVEBUFF original (el que incluyo en el fichero oldbuff.tzx).
Código: Seleccionar todo
BASE C@ DECIMAL 16 BASE C!
CREATE SAVEBUFF
00 C, 00 C, 00 C, 00 C, 00 C,
00 C, 00 C, 00 C, 00 C, 00 C,
00 C, 00 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 20 C, 20 C,
20 C, 20 C, 20 C, 26 C, FC C,
2E C, 20 C, 36 C, 1D C, 23 C,
36 C, FF C, 3E C, FC C, ED C,
47 C, ED C, 5E C, FD C, E9 C,
F3 C, F5 C, 08 C, F5 C, C5 C,
D5 C, E5 C, 21 C, 12 C, FC C,
B7 C, ED C, 72 C, C2 C, CE C,
FF C, 21 C, 26 C, 3C C, 7E C,
21 C, 22 C, FC C, FE C, 0D C,
7E C, 20 C, 32 C, FE C, 01 C,
28 C, 30 C, 36 C, 01 C, 2A C,
1E C, 3C C, 22 C, 28 C, FC C,
2A C, 20 C, 3C C, 22 C, 24 C,
FC C, 2A C, 24 C, 3C C, 22 C,
26 C, FC C, 44 C, 4D C, 2A C,
22 C, 3C C, 22 C, 2A C, FC C,
B7 C, ED C, 42 C, 28 C, 0F C,
44 C, 4D C, 2A C, 24 C, 3C C,
11 C, 2C C, FC C, ED C, B0 C,
C3 C, 6D C, FF C, 36 C, 00 C,
01 C, FE C, FE C, 16 C, 00 C,
ED C, 58 C, 3E C, 0C C, A3 C,
21 C, 23 C, FC C, 20 C, 50 C,
7E C, FE C, 01 C, 28 C, 4D C,
36 C, 01 C, ED C, 4B C, 26 C,
FC C, 2A C, 2A C, FC C, B7 C,
ED C, 42 C, 28 C, 3F C, E5 C,
21 C, FF C, 26 C, ED C, 5B C,
24 C, 3C C, B7 C, ED C, 52 C,
44 C, 4D C, 21 C, FF C, 26 C,
36 C, 20 C, 54 C, 5D C, 1B C,
0B C, ED C, B8 C, C1 C, ED C,
5B C, 26 C, FC C, 21 C, 2C C,
FC C, ED C, B0 C, 2A C, 26 C,
FC C, 22 C, 24 C, 3C C, 2A C,
24 C, FC C, 22 C, 20 C, 3C C,
2A C, 28 C, FC C, 22 C, 1E C,
3C C, 2A C, 2A C, FC C, 22 C,
22 C, 3C C, C3 C, CE C, FF C,
36 C, 00 C, C3 C, 40 C, 01 C,
ED C, 56 C, FD C, E9 C,
BASE C!
Código fuente Rutina en código máquina:
Código: Seleccionar todo
; Jupiter Ace (aka JA) TOPRAM (RAM variable: $3C18) must be EXACTLY $FC20
; Interrupt mode 2: At each interrupt executes the routine whose
; address is the one stored at memory address $nn20 where $nn is the value
; of the I register.
.list
CURSOR .equ $3C20 ; Address of the cursor in the input buffer.
L_HALF .equ $3C24 ; Address of the start of the input buffer.
INSCRN .equ $3C1E ; Address of the start of the current logical line
ENDBUF .equ $3C22 ; Address of the end of the current logical line
KEYCOD .equ $3C26 ; ASCII last key pressed
; These five address above are part of JA's System Variables.
ENDSCR .equ $26FF ; Address of video ram's last byte
ORIGIN .equ $FC20 ; Address where the program is to be loaded
.org ORIGIN
Vector: .word 0 ; Space to store the Interrupt vector.
Keyflag .byte 0 ; Flag: End of pressing <ENTER>
Keyflag2 .byte 0 ; Flag: End of pressing <Z+X>
cursorB .word 0 ; Old value of CURSOR
l_halfB .word 0 ; Old value of L_HALF
inscrnB .word 0 ; Old value of INSCRN
endbufB .word 0 ; Old value of ENDBUF
Buffer .fill 736,32 ; Space to save the input buffer copy (+2 safety bytes)
First: ld h,Vector / $100 ; (1) Store in Vector ($nn20=$FC20) the value of "Linicio" (the vector to the routine
ld l,$20 ; to be executed at each interrupt). This routine begins at "Linicio"
ld (hl),Linicio % $100
inc hl
ld (hl),Linicio / $100 ; ----End 1----
ld a,Vector / $100 ; (2) Set I register to $nn and interrupt mode 2 is activated.
ld i,a
im 2 ; ----End 2----
jp (iy) ; Back to Forth
;------------------------------ ; (4) INTERRUPT ROUTINE
Linicio: di ; (3) Dissable interrupts and save registers.
push af ; after our interrupt routine is finished the program jumps to the original
ex af,af' ; JA interrupt routine that, when terminated, will restore interrupts and
push af ; and saved registers (see --End 4-- below).
push bc
push de
push hl ; ----End 3----
ld hl,ORIGIN - 14 ; (5) Test we are at standar input buffer
or a ; NOT in any other (QUERY, RETYPE, etc.)
sbc hl,sp ; Test: SP = ORIGIN - 14
jp nz,Fin2 ; ----End 5----
ld hl,KEYCOD ; (6) Test <ENTER> pressed (Input buffer -> Copia)
ld a,(hl)
ld hl,Keyflag
cp 13
ld a,(hl)
jr nz,NotRet
cp 1
jr z,Finito ; If Keyflag=1 -> Already evaluated. Wait until key depressed
ld (hl),1
;--------------------Input buffer and associated variables -> Copy
ld hl,(INSCRN)
ld (inscrnB),hl
ld hl,(CURSOR)
ld (cursorB),hl
ld hl,(L_HALF)
ld (l_halfB),hl
ld b,h
ld c,l
ld hl,(ENDBUF)
ld (endbufB),hl
or a
sbc hl,bc ; number of bytes to copy = (ENDBUF) - (L_HALF)
jr z,Finito
ld b,h
ld c,l
ld hl,(L_HALF)
ld de,Buffer
ldir
jp Finito
;--------------------
NotRet: ld (hl),0
Finito: ; ----End 6----
ld bc,$fefe ; (7) Test Z and X keys pressed at same time. (Copy -> Input buffer)
ld d,0
in e,(c) ; Read Keyboard port: Z=bit 2 X=bit 3 (1->off, 0->pressed)
ld a,1100b
and e
ld hl,Keyflag2
jr nz,NotRet2 ; If (z) -> Z and X pressed at a time.
ld a,(hl)
cp 1
jr z,Fin2 ; If Keyflag2=1 -> Already evaluated. Wait until keys depressed.
ld (hl),1
;--------------------Copy restored as Input buffer (also restore system variables)
ld bc,(l_halfB) ; number of bytes to restore = (endbufB) - (l_halfB)
ld hl,(endbufB)
or a
sbc hl,bc
jr z,Fin2
push hl ; (9) Clean actual input buffer before restoring old one
ld hl,ENDSCR
ld de,(L_HALF)
or a
sbc hl,de ; number of bytes to clean (fill with spaces). Never = 0 (at least will be 31)
ld b,h
ld c,l
ld hl,ENDSCR
ld (hl),$20
ld d,h
ld e,l
dec de
dec bc
lddr
pop bc ; ----End 9----
ld de,(l_halfB)
ld hl,Buffer
ldir
ld hl,(l_halfB)
ld (L_HALF),hl
ld hl,(cursorB)
ld (CURSOR),hl
ld hl,(inscrnB)
ld (INSCRN),hl
ld hl,(endbufB)
ld (ENDBUF),hl
jp Fin2
NotRet2: ld (hl),0
;------------------------------ END of INTERRUPT ROUTINE.
Fin2: ; ----End 4----
JP $0140 ; Jump to original interrupt routine of the JA. (avoiding repeat what alreay
; have been done here (save registers...)
Inter1: im 1 ; Interrupt mode 1 activation (must be called from Forth)
jp (iy) ; Back to Forth
.end
EDICION (4-5-2022)
Se han corregido un par de efectos que se daban en ciertas circunstancias. Ahora, antes de restaurarse un Búfer de entrada, se limpia el actual primero a fin de evitar que queden caracteres sobrantes en el búfer de entrada o en la pantalla.