Jupiter Ace Forth: Forth Debugger

Elurdio
Mensajes: 832
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 181 veces
Agradecimiento recibido: 141 veces

Jupiter Ace Forth: Forth Debugger

Mensajepor Elurdio » 03 Abr 2023 00:04

Forth Debugger

Se llama DEBUG y solo funciona en emuladores que permitan sobreescribir la ROM y con el Pack de 48K de memoria extra añadido (51K en total).

La palabra DEBUG toma el nombre de una palabra del búfer de entrada y la depura paso a paso.

Ejemplo:

DEBUG TEST depurará la palabra TEST.

A cada paso se muestra la pantalla del Debugger con la siguiente información:
Ejemplo:
debug2.png
debug2.png (7.64 KiB) Visto 1080 veces

A la izquierda muestra verticalmente (abajo la más reciente) las palabras que se van ejecutando. La que se muestra primero empezando por abajo es la que "se va a ejecutar" (aún no se ha ejecutado).

Arriba a la derecha se muestra el estado de la pila actual (Stack) y más a la derecha la pila anterior (S-Old). Máximo siete números cada una.
Al medio hacia el centro se muestran de cero hasta siete variables con sus contenidos (solo las 6 primeras letras del nombre como máx.). En la pantalla de ejemplo se muestran las variables MOKA y MIVARLARGA.
Más a la derecha se muestran las siete últimas posiciones de la pila de Retornos.

Todos los números mostrados por DEBUG son enteros con signo.

Debajo está el menú con las diferentes acciones posibles:

NOTA: Todas las acciones (excepto "Abort") se refieren a la "depuración", no afectan a la ejecución de la palabra, que sigue su curso. Ejemplo: Si hago "Over" la palabra afectada NO se depura, pero se ejecuta normalmente.

  • Step: Si la palabra en curso es depurable, la depura
  • Over: No depura la palabra en curso
  • Back: Deja de depurar la palabra en depuración y volvemos a la palabra madre. Se podría haber llamado "Out". Deshace el "Step" en curso y queda como si en vez de "Step" en su momento hubiéramos pulsado "Over".
  • Abort: Aborta DEBUG y la ejecución de la palabra que se depura. Borra la pila.
  • Loop: Ejecuta un bucle de cualquier tipo por completo en vez de paso a paso. Se reanuda la depuración con la siguiente palabra.
  • Veiw: Si se mantiene pulsada, muestra la pantalla original con su contenido actualizado hasta donde se ha ejecutado la palabra en depuración.
  • BK: Activa/Desactiva los BreakPoints. Por defecto están desactivados. Cuando están activados se muestra un asterisco al lado.
  • Exit: Continúa la ejecución sin depurar nada hasta que llega al final de todo. Si se encuentra un BreakPoint y estos están activados, se detiene en el mismo.

"Loop" solo se activa con palabras que tiene un OF=2 (Operand Field) (*).
Lo que hace es "marcar" como punto de reinicio de la depuración la palabra que la sigue y ejecuta "Exit" automáticamente. Cuando se alcance la palabra "marcada" se reanudará la depuración. Esto implica que si, por el motivo que sea, la palabra "marcada" no se ejecuta, pues la ejecución llegará hasta el final sin depurarse nada más. La única excepción a esto es si se encuentra con un BreakPoint y están activados los mismos. Entonces la depuración continúa desde el BreakPoint.

Por tanto, OJO con "Loop". Si se sale del bucle si pasar por el final, no se reanudará la depuración (por ejemplo si salimos del bucle con EXIT (la palabra del Ace Forth, no el "Exit" del depurador)).

Los BreakPoints se han de poner a mano en las palabras. Es la palabra BREAKP. Para ello se EDITA la palabra y se le añaden los BreakPoints. Luego la redefinimos. La palabra BREAKP solo funciona si la ROM es sobreescribible.

Debido a cómo se han implementado los BreakPoints en DEBUG es imprescindible haber ejecutado DEBUG (vale incluso ejecutarlo sin ningún nombre) o haber implementado un BREAKP (ambos casos con la ROM sobrescribible) al menos UNA VEZ antes de redefinir cualquier palabra con BREAKP o de cargar un diccionario con tales palabras encima de otro ya cargado. De lo contario puede producirse un cuelgue del Jupiter Ace. (**)

En cuanto a lo de mostrar contenidos de Variables seleccionadas: La selección consiste en incluir sus nombres en la definición de la palabra VARSLIST.

La palabra VARSLIST ya vienen con el paquete, por lo que hay que EDITARLA y luego redefinirla.

NO tocar la primera palabra de VARSLIST que es MUX. Siempre ha de estar y ser la primera. DEBUG mostrará las variables que se pongan a continuación de MUX, hasta que o bien se llegue a siete (no se muestran más de siete) o bien se encuentra una que no es una variable (ya no busca más).

En la pantalla del ejemplo VARSLIST es -> : VARSLIST MUX MOKA MIVARLARGA ;
Si no se quiere mostrar ninguna variable -> : VARSLIST MUX ;

debug.TZX
(5.27 KiB) Descargado 4 veces
(Úlitma Versión 8-5-2023). Actualizada la palabra 2DROP.

NOTA Importante 1: DEBUG actualmente carga su rutina maestra en c.m. en la zona de memoria desde $E000-$EF6F y NO se reserva la memoria. Se puede usar el "Cargador de Búferes" pues empieza en $F120. También es compatible con el desensamblador del Z80 "DISS" que ocupa $CC00-$D775

Al NO reservarse la memoria, un crecimiento grande de la pila de Retornos o de la pila de datos o del diccionario pueden corromperlo.

NOTA Importante 2: Si durante la ejecución de DEBUG se produce un ERROR o se fuerza con un Break (Shift+Space) entonces NO se ha restaurado la ROM. Es urgente ejecutar DEBUG (sin ningún nombre es suficiente) so riesgo de que se cuelgue el sistema.

(*) En general todas la palabras que hacen bucles tienen OF=2 (por el tamaño del salto -> 2 bytes), pero hay otras palabra también con OF=2 que no son bucles. Un ejemplo es IF que tienen OF=2 y salta hacia adelante. Así, si pulsamos LOOP cuando estamos en un IF la palabra "marcada" solo se ejecutará si el flag es verdadero. Puede ser útil usarlo en casos así.

Por otro lado, si creamos una palabra de usuario que es un bucle y su OF es mayor de 2, pues el comando "Loop" no funcionará con ella...

(**) Esto es así porque hay dos palabras BREAKP. La "auxiliar" que es la que se ejecuta cuando introducimos BreakPoints y otra, la "definitiva" que es la que realmente se compila en la palabra cuando escribimos BREAKP. Esta última reside en ROM y ha de ser creada. Solo DEBUG o BREAKP "auxiliar" la crean (y solo si la ROM es sobreescribible obviamente).

Si cargamos con LOAD un diccionario con palabras que tienen BREAKP en su definición y aún NO se ha creado la palabra BREAKP "definitiva" en ROM (vamos, NO existe aún) un REDEFINE de dicha palabra (que es automático si ya había un diccionario en memoria antes del LOAD) es un cuelgue casi seguro. Y si no hay un cuelgue, tarde o temprano lo habrá.

Elurdio
Mensajes: 832
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 181 veces
Agradecimiento recibido: 141 veces

Re: Jupiter Ace Forth: Forth Debugger

Mensajepor Elurdio » 03 Abr 2023 12:26

Fuentes Comentados: DEBUG y las palabras necesarias para su funcionamiento: DBG->END, WROM, BREKLOAD, SRSTLOAD y SROMLOA2

DEBUG Palabra Forth que arranca, ejecuta y detiene el Forth Debugger.

Código: Seleccionar todo


HEX                 \ Ponemos base hexadecimal.

: DEBUG ( - )
\ Toma el nombre de una palabra del Input Búfer y la depura.

\ Restauramos la ROM alterada -> Podemos usar DEBUG sin nombre para que se cargue la ROM original y no haga nada más.
\ ----------------
 DBG->END           \ Restaura el Secuenciador Original
 
 109C 109A !        \ Restaura el CF de Headerless CFA=109A
 
 18FB 1A0E !        \ Restaura el CF de Headerless CFA=1A0E
 
  4F2   A1 !        \ Restaura el JP $04F2 al MEL.
\ ----------------
 
 WROM               \ Comprueba que la ROM sea sobrescribible. Si no es así -> Mensaje de Aviso y Abort.
 BREKLOAD           \ Carga en ROM la palabra Headerless BREAKP (CFA=$44) para hacer BreakPoints.

 FIND               \ Deja en la pila el CFA de la palabra a depurar.
 
 ?DUP 0=            \ Si no damos ningún nombre de palabra a depurar -> Terminamos.
 IF
  EXIT
 THEN
 
 SRSTLOAD           \ Carga los desvíos al nuevo secuencidor desde los RSTs 28H y 30H
 
 MCDEBUG            \ Deja en la pila el PFA de MCDEBUG (tipo CREATE con el c.m. que usa DEBUG)
 E000 FA0 MOVE      \ Mueve el c.m. anterior a $E000 y siguientes.
 
 VARSTORE E044 !    \ Pone en $E044 el PFA de VARSTORE (tipo CREATE) que contiene las variables a mostrar.
 VARSOFF            \ Desactiva todas las variables en VARSTORE
 VARS>STORE         \ Carga y activa en VARSTORE (tipo CREATE) la info de las variables a mostrar
 
\ Alteramos la ROM desviando cosas a la rutina c.m. en la RAM.
\ -----------------
 E01E 109A !        \ Cambia el CF de Headerless CFA=$109A a $E01E
 
 E4AC 1A0E !        \ Cambia el CF de Headerless CFA=$1A0E a $E4AC
 
 E4AF 00A1 !        \ Cambia JP $04F2 en $00A0 a JP $E4AF -> Interceptamos salto a MEL.
\ ------------------

 SROMLOA2           \ Desvía el Secuenciador en ROM para que apunte al cargado en RAM y hace un EXECUTE
                    \   que ejecuta la palabra que se quiere depurar (su CFA está en la pila, lo puso FIND)

\ Restauramos la ROM a su estado original.
\ ----------------
 DBG->END       
 109C 109A !
 18FB 1A0E !
  4F2   A1 !
\ ----------------
;

IMMEDIATE

DECIMAL             \ Ponemos base Decimal.


EDIT(6-5-2023): Última versión de DEBUG

DBG->END Palabra Forth en c.m. que repone en la ROM el Secuenciador Original. Es también la última palabra que se depura, de ahí su nombre.

Código: Seleccionar todo

            .LIST
           
            ; Inicio del Cargador
INI:       
            EX      DE,HL       ; Cuando arranca la palabra DE=PFA si es una en c.m.
           
            LD      DE,SINI-INI ; Ahora HL apunta a L04B8, inicio del c.m. del Secuenciador Original
            ADD     HL,DE       ;    pues este cargador (hasta JP(IY) incluido) ocupa 15 bytes
           
            LD      DE,$04B8    ; Reponemos el Secuenciador Original en la ROM
            LD      BC,SFIN-SINI ; Tamaño de Secuencidar (14 bytes)
            LDIR
           
            JP      (IY)        ; Volvemos a Forth.
   
            ; Código máquina del Secuenciador Original
SINI:
            POP     HL
            POP     HL
            LD      E,(HL)
            INC     HL
            LD      D,(HL)
            INC     HL
            PUSH    HL
            EX      DE,HL
            LD      E,(HL)
            INC     HL
            LD      D,(HL)
            INC     HL   
            EX      DE,HL
            JP      (HL)
SFIN:
            .END


WROM Palabra Forth que comprueba que se puede escribir en la ROM.

Código: Seleccionar todo

: WROM
\ Si la ROM no se puede sobreescribir -> Avisa y QUIT

 123 DUP 7 C!           \ Escribimos 123 en la dirección de memoria $0007
 7 C@                   \ Leemos el contenido de la dirección de memoria $0007
 = 0=                   \ Si el contenido es 123 -> flag 0, si NO es 123 -> flag 1 -> ROM no escribible.
 
 IF                     \ Si ROM NO-Escribible, lo notifica y salimos con QUIT (conserva la pila)
  CR CR ." ROM Portected! "
  CR QUIT
 THEN
 127 7 C!               \ Ponemos 127 en $0007 para que futuros test de sobrescritura no den falso positivo.
;


BREKLOAD Palabra Forth en c.m. que Carga en la ROM la palabra Forth BREAKP con CFA=$44 y es una tipo primitiva en c.m. que no hace nada.

Código: Seleccionar todo

            .LIST
           
            ; DE=PFA de la palabra que contendrá este código.
           
            ; Cargador de la Palabra-ROM BREAKP a partir de la dirección $003B (Cabecera+PFA)
            ; Esta palabra es tipo primitiva en c.m. Su CFA= $44 y su PFA=$46
INI:           
            EX      DE,HL           ; HL=INI
            LD      DE,SINI-INI     ; 15 bytes
            ADD     HL,DE           ; HL=SINI   
            LD      DE,$003B        ;
            LD      BC,FINI-SINI    ; 13 bytes.
            LDIR
            JP      (IY)            ; Vuelta a Forth
SINI:           
            .BYTE   "BREAK",$D0,0,0,6,$46,0,$FD,$E9
FINI:           
            .END


SRSTLOAD Palabra Forth en c.m. que modifica los RSTs 28H y 30H para que apunten al secuenciador modificado en la rutina maestra.

Código: Seleccionar todo

            .LIST
           
            ; DE=PFA de la palabra que contendrá este código.
           
            ; Cargador de los nuevos contenidos de los RSTs 28H y 30H con los saltos a la rutina maestra en c.m.
            ; cargada por DEBUG

INI:           
            EX      DE,HL           ; HL=INI
            LD      DE,SINI-INI     
            ADD     HL,DE           ; HL=SINI   
            LD      DE,$0028         
            LD      BC,FINI-SINI
            LDIR
            JP      (IY)            ; Vuelta a Forth
SINI:           
            POP     HL
            JP      $E011
            NOP
            NOP
            NOP
            NOP
            POP     HL
            JP      $E00E
FINI:           
            .END


SROMLOA2 Palabra Forth en c.m. que coloca en el secuenciador original el código que lo intercepta hacia el secuenciador modificado y luego arranca el depurador.

Código: Seleccionar todo

            .LIST
           
            ; DE=PFA de la palabra que contendrá este código.

JP4BA       .EQU    $E014           ; Dirección del punto de entrada 3 del Secuenciador modificado
JP4BF       .EQU    $E017           ; Dirección del punto de entrada 4 del Secuenciador modificado

            ; Cargador
INI:
            EX      DE,HL
            LD      DE,L04B8-INI
            ADD     HL,DE
            LD      DE,$04B8
            LD      BC,FINI-L04B8
            LDIR
           
; Arrancamos la Depuración ejecutando la palabra que se va a depurar.
; Como el Secuenciador está interceptado, podemos controlarlo TODO.
           
            JP      $069C           ; Salta al PF de EXECUTE que ejecutará la palabra cuyo CFA toma de la pila
           
            ; Código de Interceptación del Secuenciador Original.
L04B8: 
            RST     30 H
                           
;--------------------------------------
L04B9: 
            RST     28 H       

L04BA: 
            JP      JP4BA     

            NOP         
            NOP
;--------------------------------------
L04BF: 
            JP      JP4BF   
FINI:           
            .END

NOTA: La interceptación se hace colocando en los 4 puntos de entrada del secuenciador original saltos al secuenciador modificado que está en la rutina maestra en c.m. que coloca DEBUG en la RAM. Como los dos primeros puntos de entrada están pegados, hemos tenido que usar RSTs en vez de saltos, pues estos no caben.

<<<Continúo en otro post>>>

Elurdio
Mensajes: 832
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 181 veces
Agradecimiento recibido: 141 veces

Re: Jupiter Ace Forth: Forth Debugger

Mensajepor Elurdio » 03 Abr 2023 23:55

Estas palabras son las relacionadas con la visualización de Variables durante el depurado:

VARS>STORE A partir de las variables contenidas en VARSLIST genera una lista con la info necesaria para el depurador.

Código: Seleccionar todo

: VARS>STORE ( - )
\ Rellena una lista, la palabra VARSTORE tipo CREATE, con hasta 7 elementos con 3 campos cada uno:
\ Campo 1: 1 byte con cero o 1 si está desactivada/activada la variable
\ Campo 2: 6 bytes con hasta las 6 primeras letras del nombre (no se pueden mostrar más en la pantalla del DEBUG)
\ Campo 3: 2 bytes con la dirección de memoria con el valor (el PFA de la variable)
\ Esta lista es la que se usará durante la depuración para imprimir la info de las variables a mostrar.

 1                      \ Contador de Variables rellenadas (máximo de 7)
 VARSLIST               \ Deja en la pila el Word Pointer (WP) al CA de la primera variable compilada en VARSLIST.
 
 BEGIN                  \ Para cada CA (compilation Address) en VARSLIST, si se cumple lo siguiente:
 
  OVER 8 <              \ Contador menor que 8 (máximo caben 7 vars en la lista)
  OVER @ @ 4080 = AND   \ Y el CA corresponde a una VARIABLE (si VARSLIST vacía -> CA es de ";"
 
 WHILE                  \ hacemos:
  OVER OVER             \ Copiamos el contador y el CA (equivale a 2DUP)
  @ 2+                  \ Pone en la pila el PFA de la variable actual en VARSLIST (Contador: 1->1ª, 2->2ª, etc.)
  VARSPUT               \ Toma el PFA y el contador de la pila y rellena el elemento correspondiente al contador
                        \     con el nombre (máx. 6 primeras letras) y el PFA. Además marca el elemento como activado.
  SWAP 1+ SWAP 2+       \ Actualiza el contadador (+1) y la CA (+2)
 REPEAT
 2DROP
;

VARSLIST y MUX Son dos palabras Forth que juntas hacen que la palabra tipo COLON VARSLIST se comporte como una lista de CFAs de VARIABLES (VAR1, VAR2, ... VAR7) que al ejecutarse simplemente coloca en al pila la dirección de memoria justo después del CFA de MUX y nada más.
El uso de MUX es una técnica avanzada que además DEBUG no depura bien con según qué se haga con ella.
La gran ventaja de esta lista es que al ser tipo COLON, si se mueve alguna de las variables en memoria, el valor de su CFA compilado en el PF de VARSLITS es corregido automáticamente por el Ace Forth, cosa que NO sucedería si fuera una lista tipo CREATE o generada con DEFINER

Código: Seleccionar todo

: VARSLIST
 MUX VAR1 VAR2 ··· VAR7
;

: MUX
 R>
;

VARSPUT Palabra Forth que espera en la pila un número de elemento y el PFA de la variable y rellena el elemento correspondiente de la lista

Código: Seleccionar todo

: VARSPUT (n,PFA - addr,CFA)
\ Espera en la pila el número de elemento (1 a 7) y el PFA de la Variable.
\ Activa el elemento (pone a 1 su primer campo), rellena el 2º campo con el nombre (máx. 6 letras) y en campo 3 el PFA

 PUTTEST                \ Comprueba que el número de elemento sea 1 a 7 y que el PFA es de una Variable.
                        \ Era necesario cuando VARSPUT se usaba a mano. Ahora está de más (es un residuo antiguo)
 SWAP 1- 9 *            \ Toma el número del elemento y calcula el incremento a sumar al PFA de VARSTORE
 VARSTORE +             \ Suma el incremento anterior al PFA de VARSTORE -> Apunta al inicio del elemento (Campo 1)
 DUP 1 SWAP C!          \ Guarda 1 en el Campo 1 -> Activa dicha variable.
 1+ DUP 6 + SWAP        \ Deja en la pila las direcciones de inicio y final del Campo 2.
 OVER OVER              \ Equivale a 2DUP
 UDO                    \ Bucle Unsigned apoyado en BEGIN/UNTIL
 BEGIN
  32 I C!               \ Rellena con espacios el Campo 2.
 ULOOP
 UNTIL
                        \ Aquí pila: {PFA[variable],Campo3[incio],Campo2[inicio]}
 ROT DUP >R ROT !       \ Guarda en Campo 3 el PFA de la variable
 R> 2-                  \ Deja la pila así: {Campo2[inicio],CFA[variable]} listo para VARSNAME
 VARSNAME               \ Rellena el Campo2 con el nombre de la variable
;

VARSNAME Palabra Forth que Rellena el Campo2 (el nombre)

Código: Seleccionar todo

: VARSNAME (addr,CFA - )
\ Rellena el Campo2 (empieza en "addr") de VARSTORE con el nombre de la variable con el CFA dado.

 DUP 1- C@              \ Obtiene el tamaño del nombre de la variable
 63 AND                 \ Apaga el bit#6 que está encendido si la palabra es inmmediata
 
 DUP DUP 6 >            \ Si tamaño mayor que 6 -> lo fija a 6. Si menor, lo deja tal cual (Campo2 son 6 bytes)
 IF                     \ Tanto si lo acorta como si no, le llamo tamaño reducido.
  DROP 6 SWAP
 THEN
                        \ Aquí la pila es: {addr,CFA,Name[size_reduced],Name[size_original]}
 ROT 5 -                \ CFA-5=dirección del último carácter del nombre de la variable
 SWAP -                 \ CFA-5-Name[size_original]=dirección del primer carácter del nombre de la variable
 SWAP 0                 \ Pila: {addr,addr[char1],Name[size_reduced],0}
 DO                     \ Inicia un bucle de 0 al tamaño del nombre reducido
  DUP I + C@            \ Toma el iésimo char del nombre (de izq. a derecha)
  127 AND               \ Apaga el bit#7 (está encendido si es el último char)
  3 PICK I + C!         \ Guarda el char en la posición adecuada del Campo2 de VARSTORE
 LOOP
 2DROP
;


BREAKP Palabra Forth Inmediata que compila el CA de BREAKP en ROM. Usa WROM_ABORT

Código: Seleccionar todo

: BREAKP
 WROM_ABORT BREKLOAD 68 ,
;

IMMEDIATE


WROM_ABORT Palabra Forth que detecta si ROM se puede escribir. Si no, aborta.

Código: Seleccionar todo

: WROM_ABORT
 123 DUP 7 C! 7
 C@ = 0=
 IF
  CR CR ." ROM Portected! "
  CR ABORT
 THEN
 127 7 C!
;


EDIT (8-4-2023): Faltaba BREAKP y la que usa: WROM_ABORT

<<<Continúo en otro Post>>>

Elurdio
Mensajes: 832
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 181 veces
Agradecimiento recibido: 141 veces

Re: Jupiter Ace Forth: Forth Debugger

Mensajepor Elurdio » 04 Abr 2023 12:46

Ahora van los Fuentes de las palabra Forth que se han "embedido" en el c.m. de la rutina maestra y se ejecutan desde allí.

Podía haberlo hecho en c.m. pero es más fácil programar en Forth (y a menudo más compacto), por lo que cuando algo se intuía largo/complejo he optado por hacerlo así.

Muchas, aunque no todas, de estas palabras son modificaciones para este fin de palabras que ya tenía en otros programas por lo que a veces hacen cosas que parecen inútiles, como por ejemplo ISMACHINE? que tiene que borrar un dato que sobra tras llamar a MC?. Es así porque la palabra del programa original que llama a MC? necesita ese dato que ahora me sobra. Y como ésta unas cuantas cosas "inútiles" más en todas estas palabras embebidas reaprovechadas.

1ª Palabra Embedida (y las que utiliza)

ISMACHINE? Palabra Forth que toma un CFA de la pila y devuelve 1 si corresponde a una palabra de Usuario en c.m. o cero si no lo es.
Necesita la palabra MC?

Código: Seleccionar todo

: ISMACHINE? ( CFA - flag )
\ flag=1 si CFA es de una palabra de Usuario en c.m.

 DUP 15434 SWAP U<          \ Comprueba si la palabra es de Usuario (15434<CFA->de Usuario)
 SWAP MC?                   \ MC? devuelve 1 si CF apunta dentro del PF de la palabra.
 SWAP DROP AND              \ Devuelve 1 si las dos comprobaciones anteriores son ambas ciertas.
;

MC? Palabra Forth que comprueba si el CF (Code Field) de la palabra dada corresponde a una Primitiva de Usuario. Utiliza PF_SIZE, U> y MCROM?.

Código: Seleccionar todo

: MC? ( CFA - CFA,f)
\ True si MCROM? es true o si CF apunta dentro del PF
\ NOTA: Esta palabra solo tiene sentido si CFA es de un palabra de Usuario.
\ Si se usa con una palabra ROM podría dar cierto por casualidad.
 
 DUP MCROM? >R          \ Hacemos el test MCROM? y guardamos el flag resultante en Rstack
 DUP @ >R               \ Guardamos CF en Rstack
 DUP 1+                 \ PFA-1=límite inferior PF (un byte antes del primer byte del PF)
 OVER 2+ DUP            \ PFA (dos copias)
 PF_SIZE +              \ PFA+PFA[size]=límite superior PF (1 byte más allá del último byte del PF)
 I                      \ I nos pone en la pila una copia del CF en Rstack
 U>                     \ flag true si límite superior > CF
 SWAP R> U<             \ flag true si límite inferior < CF
 AND                    \ AND los dos flags anteriores
 R> OR                  \ Recurperamos flag de MCROM? y hacemos OR con el anterior.
;

U> Palabra Forth que hace una comparación > sin signo.

Código: Seleccionar todo

: U> ( n1,n2 - flag)
\ flag true si n1>n2 ambos "sin signo"
\ Como solo disponemos de U< hacemos NOT(U< OR =)

 OVER OVER           \ Equivale a un 2DUP
 U< ROT ROT = AND 0= \ NOT(U< OR =)
;

MCROM? Palabra Forth que comprueba si el CFA dado corresponde a una Primitiva que apunta a la ROM

Código: Seleccionar todo

: MCROM? ( CFA - f)
\ True si CF apunta a ROM y no es ninguno de los tipos de palabras originales del Ace Forth

 @                  \ Pone el CF en la pila
 DUP 15360 U< SWAP  \ Mira si CF < 15360 -> si true -> CF es una dirección de la ROM. Deja CF en TOS (1º en la pila)
 4360 OVER = SWAP   \ Mira si CF = 4360  -> si true -> CF de una tipo COMPILER
 4229 OVER = SWAP   \ Mira si CF = 4229  -> si true -> CF de una tipo DEFINER
 4076 OVER = SWAP   \ Mira si CF = 4076  -> si true -> CF de una tipo CREATE
 3779 OVER = SWAP   \ Mira si CF = 3779  -> si true -> CF de una tipo COLON
 4085 OVER = SWAP   \ Mira si CF = 4085  -> si true -> CF de una tipo CONSTANT
 4080 =             \ Mira si CF = 4080  -> si true -> CF de una tipo VARIABLEE
 OR OR OR OR OR     \ Resumimos en un solo flag si nuestro CF es de uno de los 6 tipos de palabras anteriores.
 0=                 \ y lo negamos -> Solo es true si NO es de ninguno de los 6 tipos anteriores.
 AND                \ Además, para ser true el CF tiene que apuntar a la ROM.
;

PF_SIZE Palabra Forth que dado un PFA (Parameter Field Address) nos devuelve el tamaño del PF (Parameter Field)

Código: Seleccionar todo

: PF_SIZE ( PFA - size)
\ Devuelve el tamaño del PF de la palabra correspondiente
\ Normalmente el tamaño de la palabra sin contar el tamaño del nombre está almacenado en la cabecera, pero si la
\ palabra es la última que se creo y no se ha redefinido, este campo vale 0.

 DUP 7 - @              \ Obtiene el tamaño de la palabra sin contar el nombre
 ?DUP                   \ Si tamaño=0 -> aún no calculado (es la última palabra del diccionario)
 0=
 IF
  HERE OVER -           \ Calculamos el tamaño del PF sin usar el tamaño anterior (pues es 0)
 ELSE
  7 -                   \ Calculamos el tamaño del PF[tamaño]=Tamaño[sin contar nombre]-7
 THEN
 SWAP DROP
;


EDIT(26-4-2023): Nueva versión de ISMACHINE? que incluye el tipo "raro" de primitivas de Usuario que apunta a ROM.

<<Continúo en otro Post >>

Elurdio
Mensajes: 832
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 181 veces
Agradecimiento recibido: 141 veces

Re: Jupiter Ace Forth: Forth Debugger

Mensajepor Elurdio » 04 Abr 2023 22:25

2ª Palabra Embedida (y las que utiliza)

NEXTWP Palabra Forth que espera un Word Pointer y devuelve el siguiente Word Pointer teniendo en cuenta los posibles OF (Operand Field) que pudieran haber. Utiliza OFSIZE

Código: Seleccionar todo

: NEXTWP ( WP - WP)
\ Dado un Word Pointer WP nos devuelve el WP de la siguiente palabra.

 DUP OFSIZE + 2+        \ WP[siguiente]=WP[actual]+OF[tamaño]+2
;

OFSIZE Palabra Forth que espera un Word Pointer y devuelve el tamaño del OF (Operand Field) asociado (puede ser cero si no existe). Utiliza FORTHSTEP
NOTA: OFSIZE es utilizada por DEBUG como palabra independiente. De hecho, ahora mismo NEXTWP NO se utiliza para nada. Lo necesitaba una característica de DEBUG que finalmente no implementé. OFSIZE se utiliza con la opción "Loop" de DEBUG.

Código: Seleccionar todo

: OFSIZE ( WP - OF[size])
\ Espera un WP y devuelve el tamaño del OF asociado. Si no existe OF devuelve 0.

 DUP @                      \ Obtenemos el CFA de la palabra CFA=(WP)
 15424 OVER >               \ Comprueba si CFA es ROM  o RAM (palabra Ace Forth o palabra de Usuario)
 IF
  FORTHSTEP                 \ Si es ROM lo hacemos por tipo palabra, pues son todas conocidas.
 ELSE                       \ Si es RAM aplicamos un método general (aplicable a ROM pero hay excepciones)
  DUP @ 4418 =              \ Comprobamos que CFA=(WP) sea una tipo Compilante (Runtime) -> (CFA)=4418.
  IF
   3 - C@                   \ OF[tamaño]=(CFA[Runtime]-3)
   DUP 255                  \ Si OF[tamaño]=255 ($FF) -> Tamaño variable. No está aquí el tamaño.
   =
   IF                       \ Aquí pila: {WP,n,flag} n=255 si flag=1 o n=OF[tamaño] si flag=0
    DROP DUP 2+ @ 2+        \ Cuando el tamaño es variable: OF[tamaño]=(WP+2)+2
   THEN
  ELSE
   0                        \ Si no es una tipo compilante -> OF=0. Solo las tipo compilante tienen OF.
  THEN
 THEN
 SWAP DROP
;

FORTHSTEP Palabra Forth que a partir de un Word Pointer y el CFA compilado de una palabra ROM devuelve el tamaño de su OF. Utiliza OF1?, OF2?, OF4? y OFN?

Código: Seleccionar todo

: FORTHSTEP ( WP,CFA - n)
\ Espera en la pila el WP y el CFA compilado en ese WP de una palabra ROM y devuelve el tamaño del OF asociado.
\ En vez de un método general, busca en todas las posibles palabras de la ROM del Ace Forth con OF.

 DUP OF1?               \ Mira entre las palabras de la ROM del Ace Forth con OF de tamaño 1
 IF
  SWAP DROP
 ELSE
  DUP OF2?              \ Mira entre las palabras de la ROM del Ace Forth con OF de tamaño 2
  IF
   SWAP DROP
  ELSE
   DUP OF4?             \ Mira entre las palabras de la ROM del Ace Forth con OF de tamaño 4
   IF
    SWAP DROP
   ELSE
    OFN?                \ Mira entre las palabras de la ROM del Ace Forth con OF de tamaño varibable.
    IF
    ELSE
     0                  \ Si no es ninguno de los casos anteriores -> No tiene OF -> Tamaño OF=0.
    THEN
   THEN
  THEN
 THEN
;

OF1? Palabra Forth que toma el CFA compilado de una palabra de la ROM del Ace Forth y comprueba si es ASCII que tiene OF=1 (la única).
Si es así, devuelve el tamaño (1) y un 1 (flag). Si no, solo un flag (0)

Código: Seleccionar todo

: OF1? ( CFA - {1},flag)
\ Comprueba si CFA corresponde al CFA[Runtime] de ASCII, la única palabra de la ROM con OF=1.
\ NOTACION (··· - {n},f)
\   El número n dentro de {} solo se sube a la pila si flag es verdadero.
\   Si es falso, solo se sube flag

 4171 =
 IF
  1 1
 ELSE
  0
 THEN
;

OF2? Palabra Forth que comprueba si el CFA dado corresponde a alguna de las palabras ROM con OF=2. Utiliza POLYDUP
Si es así, devuelve el tamaño (2) y un 1 (flag). Si no, solo un flag (0)

Código: Seleccionar todo

: OF2? ( CFA - {2},f)
\ See NOTATION in OF1?
\ OF2 comes from "Operand Field 2". That is, OF size = 2 bytes.
\ There are 8 primitives compiling words with an OF size of two bytes
\ 4113 ($1011) is the CFA of LITERAL
\ 4739 ($1283) is the CFA of IF
\ 4721 ($1271) is the CFA of ELSE
\ 4749 ($128D) is the CFA of UNTIL
\ 4744 ($1288) is the CFA of WHILE
\ 4726 ($1276) is the CFA of REPEAT
\ 4914 ($1332) is the CFA of LOOP
\ 4924 ($133C) is the CFA of +LOOP

 8 POLYDUP
 4113 =
 8 ROLL 4739 =
 8 ROLL 4721 =
 8 ROLL 4749 =
 8 ROLL 4744 =
 8 ROLL 4726 =
 8 ROLL 4914 =
 8 ROLL 4924 =
 OR OR OR OR
 OR OR OR
 IF
  2 1
 ELSE
  0
 THEN
;

POLYDUP Palabra Forth que realiza múltiples copias de un número dado. Si número de copias es 0 o negativo no hace nada.

Código: Seleccionar todo

: POLYDUP ( n1,n2 - n1...n1 n2 veces)
 1- DUP 1 <
 IF
  DROP EXIT
 THEN
 0
 DO
  DUP
 LOOP
;

OF4? Palabra Forth que comprueba si el CFA dado corresponde a un número en coma flotante, el único caso de la ROM con OF=4.
Si es así, devuelve el tamaño (4) y un 1 (flag). Si no, solo un flag (0)

Código: Seleccionar todo

: OF4? ( CFA - {4},f)
\ See NOTATION in OF1?
\ OF4 comes from "Operand Field 4". That is, OF size = 4 bytes
\ 4196 ($1064) is the CFA for a Floating point number enclosed in a definition

 4196 =
 IF
  4 1
 ELSE
  0
 THEN
;

OFN? Palabra Forth que comprueba si el CFA suministrado corresponde a una palabra ROM con OF de tamaño variable. Solo existen dos: ." o (
Si es así, devuelve el tamaño (n) y un 1 (flag). Si no, solo un flag (0).

Código: Seleccionar todo

: OFN? ( WP,CFA - {n},f)
\ See NOTATION in OF1?
\ OFN comes from "Operand Field n" where n is the size of a variable-size OF.
\ 4985 ($1379) is the CFA of [(]
\ 5014 ($1396) is the CFA of [."]
\ These Words are the only Primitives thaT have a variable OF size in Ace's ROM.

 DUP 5014 = SWAP 4985
 = OR
 IF
  DUP 2+ @ 2+           \ OF[size]=(WP+2)+2
  1                     \ flag=1
 ELSE
  0
 THEN
;


<<<Continúo en otro Post>>>

Elurdio
Mensajes: 832
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 181 veces
Agradecimiento recibido: 141 veces

Re: Jupiter Ace Forth: Forth Debugger

Mensajepor Elurdio » 04 Abr 2023 22:52

3ª Palabra Embedida (y las que utiliza)

La palabra H4. actualmente solo se usa en algunas pruebas. Se implementó pensando en visualizar la Pila de Retornos como números hexadecimales sin singo, pero finalmente lo descarté (porque allí también se muestran los índices/límites de DO/LOOPS y similares). Posiblemente se eliminen de la versión final de la rutina maestra en c.m.

H4. Palabra Forth que imprime un entero de la pila en formato Hexadecimal (sin signo) con 4 dígitos (rellena con ceros por la izquierda) independientemente de la base acutual (que no se ve afectada). Usa H2.

Código: Seleccionar todo

: H4. ( n - )
\ Prints n in hexadecimal unsigned format of at least four digits

 DUP 4096 U<
 IF
  ASCII 0 EMIT DUP 256 U<
  IF
   ASCII 0 EMIT
  THEN
 THEN
 H2.
;

H2. Palabra Forth que imprime un entero de la pila en formato Hexadecimal (sin signo) con al menos 2 dígitos (rellena con ceros por la izquierda) independientemente de la base actual (que no se ve afectada). Usa H.

Código: Seleccionar todo

: H2. ( n - )
 \ Prints number n in hexadecimal unsigned format of at least two digits.
 
 DUP 16 U<
 IF
  ASCII 0 EMIT
 THEN
 H.
;

H. Palabra Forth que imprime un entero en la pila en formato hexadecimal sin signo sin afectar la base actual.

Código: Seleccionar todo

: H. ( n - )
\ Prints the number n in Stack in Hexadecimal unsigned format

 BASE DUP C@ SWAP 16
 OVER C! ROT U. C!
;

Elurdio
Mensajes: 832
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 181 veces
Agradecimiento recibido: 141 veces

Re: Jupiter Ace Forth: Forth Debugger

Mensajepor Elurdio » 04 Abr 2023 23:17

4ª Palabra Embedida (Ésta palabra se ha creado expresamente para la rutina maestra en c.m.)

Utiliza cuatro valores de 16 bits de la rutina maestra en c.m. a los que se les han puesto justo antes una Word con el valor $0FF0 para que se puedan usar como Variables Forth. Las direcciones de estas Words hacen de CFAs de las mismas. Son: CONTADOR, CONTOLD, SP_NEW y SP_OLD.

CORRECTOR Palabra Forth que corrige CONTADOR en función del siguiente criterio:

Si CONTADOR > CONTOLD y Si SP_NEW >= SP_OLD -> Restamos uno a CONTADOR

El significado de estas variables y de este criterio se verá en el Fuente de la Rutina Maestra en c.m. que carga DEBUG al arrancar (Fuente que aún no he subido)

Código: Seleccionar todo

: CORRECTOR ( - )
\ Corrige la variable CONTADOR

 VAR_CONTADOR @ VAR_CONTOLD @ >
 IF
  VAR_SP_NEW @ VAR_SP_OLD @ OVER
  OVER > ROT ROT =
  OR
  IF
   VAR_CONTADOR @ 1- VAR_CONTADOR !
  THEN
 THEN
;

<<<Continúo en otro Post>>>

Elurdio
Mensajes: 832
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 181 veces
Agradecimiento recibido: 141 veces

Re: Jupiter Ace Forth: Forth Debugger

Mensajepor Elurdio » 05 Abr 2023 00:01

5ª Palabra Embedida (Ésta palabra se ha creado expresamente para la rutina maestra en c.m.)

6. Palabra Forth que imprime un entero con signo completado a 6 espacios (singo y hasta cinco digitos). O sea, si la representación del número con signo ocupa menos de 6 caracteres, rellenamos por la izquierda con espacios. Utiliza D->PAD

Código: Seleccionar todo

: 6. ( n - )
\ Imprime n como entero con signo justificado a 6 espacios

 DUP 0<             \ Convierte n en un entero doble añadiendo un -1 o un cero según n sea negativo o positivo.
 IF
  -1
 ELSE
  0
 THEN
 
 D->PAD             \ Convierte el entero doble con signo en un string y lo coloca en el PAD. Deja en la pila
                    \ la dirección del primer carácter y el tamaño del string.
 DUP 6 SWAP -       \ Calcula el número de espacios que faltan para que el string sea de 6 caracteres.
 SPACES TYPE        \ Imprime los espacios que faltan antes de imprimir el string.
;

D->PAD Palabra Forth que toma un entero doble con signo de la pila y lo convierte en un string que coloca en el PAD. Deja en la pila la dirección del primer carácter y el número de caracteres del string.
NOTA: Esta palabra está en el manual del Jupiter Ace (pág 113).

Código: Seleccionar todo

: D->PAD ( d - addr,n)
\ Toma un entero doble con singo d y coloca en el PAD el string que lo representa. Deja en la pila la dirección
\ "addr" del primer carácter del string y el tamaño n del mismo
\ Ver manual del Jupiter Ace página 113

 DUP >R DUP 0<
 IF
  DNEGATE
 THEN
 <# #S R> SIGN #>
;

<<<Continúo en otro Post>>>

Elurdio
Mensajes: 832
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 181 veces
Agradecimiento recibido: 141 veces

Re: Jupiter Ace Forth: Forth Debugger

Mensajepor Elurdio » 05 Abr 2023 12:45

6ª Palabra Embedida (Ésta palabra se ha creado expresamente para la rutina maestra en c.m.)

MEDIOCLS Palabra Forth que toma de la pila un número de columna (0 a 31) y borra la pantalla a la derecha de dicha columna (incluida) excepto la última línea. Así NO se borra ni se desplaza el histórico de las palabras que se han ido ejecutando durante la depuración, que está a la izquierda de la pantalla.

Código: Seleccionar todo

: MEDIOCLS ( col - )
\ Borra toda la pantalla a la izquierda de la columna dada (incluida) excepto la última línea.
\ Pantalla: 24 líneas (0 a 23) por 32 columnas (0 a 31)

 23 0                   \ De la línea 0 a la 22 (todas-1) de la pantalla. No tocamos la línea #23
 DO
  I OVER AT             \ Nos colocamos en la columna dada y en la línea actual del bucle
  31 OVER - SPACES      \ Imprimimos los espacios que faltan hasta completar la línea desde "col" hasta el final
 LOOP
 DROP
;

NOTA: Finalmente en la rutina se cargó una variante más simple de esta palabra, pues siempre usa la columna #12.
Se cambió la línea 31 OVER - SPACES a 20 SPACES. Pero se puede cambiar este número (el 20) desde el c.m. y seguimos teniendo la posibilidad de usar otros anchos.

7ª Palabra Embedida (Ésta palabra se ha creado expresamente para la rutina maestra en c.m.)

DSHOW6 Palabra Forth que lista el contenido de posiciones de memoria (words) entre dos direcciones dadas.
Espera en la pila fila, columna, address_fin, address_ini.
Precisa de UDO y ULOOP+ (que se usan conjuntamente con BEGIN/UNTIL para generar un bucle tipo DO/+LOOP pero "sin signo").
Utiliza "6." (ya visto) -> Los números impresos están justificados a la derecha hasta 6 chars (signo incluido).
El incremento por defecto es -2 pero se puede cambiar desde el c.m. a +2.
DEBUG la usa para listar los contenidos de las pilas (unas crecientes (+2) y otras decrecientes (-2))

Código: Seleccionar todo

: DSHOW6 ( fila, columna, address_fin, address_ini - )
\ Imprime los números almacenados en las direcciones dadas en formato 6 chars justificados a la izquierda

 UDO                    \ [UDO]+[BEGIN]/[ULOOP+]+[UNTIL] equivale a DO/+LOOP pero con índice/límite Sin Signo
 BEGIN 
  OVER OVER             \ Equivale a un 2DUP
  AT                    \ Coloca la posición de impresión en el punto (fila,columna)
  I @ 6.                \ Imprime el contenido de la dirección de memoria I en formato 6 chars justificados a izq.
  SWAP 1+ SWAP          \ fila=fila+1
  -2                    \ Incremento del bucle
 ULOOP+
 UNTIL
 DROP DROP
;

NOTA: En el constructo UDO/BEGIN//ULOOP+/UNTIL el bucle es BEGIN/UNTIL. UDO solo carga el índice/límite en Rstack y ULOOP+ los evalúa a cada ciclo dejando en la pila un flag 0/1 para UNTIL. Si se "depurara" DSHOW6 con DEBUG y se quiesiera usar el comando "Loop" se habría de aplicar al UNTIL, no al ULOOP+.

UDO y ULOOP+ Palabras Forth para generar un bucle sin signo conjuntamente con BEGIN/UNTIL. Son de uso general y ya he hablado de ellas en otras ocasiones.

UDO Palabra Forth que hace las veces de DO para el bucle BEGIN/UNTIL. Coloca en la pila de Retornos el límite y el índice del bucle para ser evaluados por ULOOP+.

Código: Seleccionar todo

: UDO ( n1,n2 - )
\ Coloca n1 y n2 en la pila de retornos al igual que haría DO

 R> ROT >R SWAP >R
 >R
;

ULOOP+ Palabra Forth que hace las veces de +LOOP para el bucle BEGIN/UNTIL. A cada ejecución suma el incremento que toma de la pila al "índice" que UDO puso en la pila de retornos. Luego lo compara con el "limite" que UDO puso en la pila de retornos. Si no se cumple la condición de terminar -> pone un 0 en la pila para UNTIL. Si se alcanzó el final -> pone un 1 en la pila para UNTIL y elimina los dos números de la pila de retornos que puso UDO.

Código: Seleccionar todo

: ULOOP+ ( inc - flag)
\ Incrementa el límite en Rstack con "inc" (inc puede ser negativo) y evalúa si se alcanzó el término del bucle
\ dejando un flag en la pila para UNTIL

 DUP R> R> ROT +        \ Baja el índice desde Rstack a Dstack y le suma el incremento.
 >R >R                  \ Sube el nuevo índice de nuevo a Rstack
 0>                     \ Deja un 1 en la pila si el "inc" es positivo o un cero si es negativo.
 I' J                   \ Pongo en la pila copias del límite (I') y del índice (J)
 U<                     \ Los comparo como números Sin Signo.
 SWAP                   \ Traigo a TOS (Top Of the Stack) el flag que nos dice si el "inc" es mayor que cero o no.
 IF
  0=                    \ Si era mayor que cero invertimos el flag que resultó del U< anterior. Esto se hace porque
 THEN                   \ el criterio de fin si el inc es negativo es indice<límite y si es positivo índice>=limite
 DUP                    \ Duplicamos el flag: uno para el IF siguiete y el otro para dejarlo para el UNTIL
 IF                     \ Si el flag es 1 -> Se acabó -> Elimino índice y límite de Rstack.
  R> R> R> DROP DROP
  >R
 THEN
;


8ª y Última Palabra Embedida (Ésta palabra se ha creado expresamente para la rutina maestra en c.m.)

VARSREAD Palabra Forth que Imprime las variables a mostrar a partir de la info contenida en la lista tipo CREATE VARSTORE.
Utiliza "6." (ya visto)

Código: Seleccionar todo

: VARSREAD ( - )
\ Imprime las variables a mostrar a partir de la info contenida en la lista tipo CREATE VARSTORE.

 12                 \ Línea inicial
 -8124 @            \ -8124=$E044 -> ($E044)=PFA VARSTORE. Lo llamaremos DP (de Data Pointer)
 BEGIN
  DUP C@            \ Pone en la pila el valor almacenado en el Campo1 (1->Var activada, 0->Var desactivada)
 WHILE              \ El bucle se ejecuta hasta que se encuentra una Var desactivada -> Se detiene.
                    \ VARSTORE tiene un Campo1 a cero de una "hipotética" Var #8 -> Máx. 7 vars mostradas.
  OVER 12 AT        \ Posición de impresión (fila,col=12). La columna es fija, va variando la fila 12,13,...
  1+                \ DP=DP+1 -> DP apunta al inicio del Campo2 (hasta 6 letras del nombre de la variable)
  DUP 6 TYPE        \ Imprimimos el Campo2
  6 +               \ DP=DP+6 -> DP apunta al inicio del Campo3 que contiene el PFA (u OFA) de la variable
  DUP @ @           \ Ponemos en la pila el valor de la variable
  6.                \ Imprimimos el valor de la variable justificado
  2+                \ DP=DP+2 -> DP apunta al inicio del Campo1 de la siguiente variable en VARSTORE
  SWAP 1+ SWAP      \ fila=fila+1
 REPEAT
 2DROP
;

EDIT (12-4-2023): He tenido que añadir una nueva palabra embebida para listar Rstack pues DSHOW6 no me sirve (lo comento en este post)

9ª Palabra Embebida

DSHOWR Palabra Forth que espera 4 datos en la pila: {fila,columna,address,n} va imprimiendo en la "columna" dada, empezando en la "fila" dada el número entero contenido en address+shif con shift=0,2,4,...,n-2 (el n dado).

Código: Seleccionar todo

: DSHOWR ( fila, columna, address_ini, número de bytes - )
\ Imprime los números almacenados en las direcciones dadas en formato 6 chars justificados a la izquierda

 0
 DO
  3 PICK 3 PICK AT      \ Coloca la posición de impresión en el punto (fila,columna)
  I OVER + @ 6.         \ Imprime el contenido de la dirección de memoria en formato 6 chars justificados a izq.
  ROT 1+ ROT ROT        \ fila=fila+1
  2                     \ Incremento del bucle
 +LOOP
 DROP DROP DROP
;

<<<Continúo en otro Post>>>

Elurdio
Mensajes: 832
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 181 veces
Agradecimiento recibido: 141 veces

Re: Jupiter Ace Forth: Forth Debugger

Mensajepor Elurdio » 05 Abr 2023 16:09

En cuanto a las palabras Forth embebidas en c.m.

Programar en Forth es más fácil y, a menudo, más compacto que hacerlo en c.m.

Muchas de las cosas que ha de hacer la Rutina Maestra en c.m. que intercepta el Ace Forth durante la depuración de palabras es mucho más fácil hacerlo aprovechando palabras Forth existentes.

Ejemplos:

  • Imprimir un número entero en pantalla (palabra ".")
  • Imprimir un número en coma flotante en pantalla (palabra "F.")
  • etc.

Puesto que puedo llamar a a palabras Forth en ROM desde c.m. sin problemas, pues así lo hago.

Pero si ya puestos, pretendemos llamar palabras Forth de Usuario desde código máquina, hay que asegurarse que dichas palabras no van a cambiar de posición en memoria. La más manera más fácil de hacerlo es embeberlas en el c.m. que las ejecutará y que ha de ser también fijo (en memoria RAM fija, NO en el PF de una palabra Forth).

Embeber una palabra Forth en c.m significa copiar su PF (Parameter Field) precedido de su CF (code Field) en la rutina en c.m.
La dirección en memoria de su CF será su CFA y por lo tanto, otras palabras Forth embebidas y también la rutina en c.m. que las contiene, las llamarán con ese CFA.

Para obtener el fuente de la palabra a embeber utilizo una palabra que me imprime en pantalla el CF y todos los CA compilados como una serie de Words en Hexadecimal.

Un programa que haga esto con cualquier tipo de palabra sería todo un proyecto. Pero si aprovechamos el hecho que la mayoría de palabras compiladas en una palabra de usuario solo tienen Operand Fields OF de tamaño par, podemos hacer una palabra muy sencilla para este fin (*).

: FORTH>CM ( CFA - )
\ Hace un listado vertical de todas las CA de Word en Word (16 bits) en Hexadecimal

DUP 2+ PF_SIZE OVER +
1+ SWAP
DO
CR 12 SPACES I @
." .WORD $"
H4. 2
+ULOOP
CR
;


Si hacemos:

FIND FORTH>CM FORTH>CM generamos el listado de los CAs de FORTH>CM

OJO: Esta palabra usa .", por lo que me he asegurado que sea de tamaño par (contiene 8 letras en el texto que imprIme)

Se genera esto:
.
.WORD $0EC3
.WORD $1379
.WORD $0006
.WORD $6663
.WORD $2061
.WORD $202D
.WORD $086B
.WORD $0E13
.WORD $6060
.WORD $0912
.WORD $0DD2
.WORD $0E09
.WORD $0885
.WORD $1323
.WORD $0A95
.WORD $1011
.WORD $000C
.WORD $0A83
.WORD $12E9
.WORD $08B3
.WORD $1396
.WORD $0008
.WORD $572E
.WORD $524F
.WORD $2044
.WORD $2420
.WORD $6028
.WORD $1011
.WORD $0002
.WORD $5FEA
.WORD $FFE0
.WORD $0A95
.WORD $04B6

Como FORTH>CM utiliza a su vez otras palabras Forth de Usuario, a saber: PF_SIZE H4. y +ULOOP estas también tendrán que embeberse.

Ahora cualquier llamada a otra palabra Forth de Usuario se ha de substituir por la etiqueta que se le ha puesto al embeberla (doy por supuesto que será el mismo nombre que tiene la palabra)

Al primer Word listado lo etiquetaremos con el nombre de la palabra (o como nos parezca) y será su CFA.

Las localizamos haciendo:

NOTA: H4. ya salió en los posts anteriores, nos imprime el número en hexadecimal con 4 dígitos.

FIND PF_SIZE H4.
FIND H4. H4.
FIND +ULOOP 2+ @ H4. (**)

Obtendremos:

6060 
6028
5FEA

Ahora los buscamos en el listado y substituimos esa Word por la etiqueta que tendrá la palabra a la que se refieren.

Nos queda:

FORTH>CM .WORD  $0EC3
.WORD $1379
.WORD $0006
.WORD $6663
.WORD $2061
.WORD $202D
.WORD $086B
.WORD $0E13
.WORD PF_SIZE
.WORD $0912
.WORD $0DD2
.WORD $0E09
.WORD $0885
.WORD $1323
.WORD $0A95
.WORD $1011
.WORD $000C
.WORD $0A83
.WORD $12E9
.WORD $08B3
.WORD $1396
.WORD $0008
.WORD $572E
.WORD $524F
.WORD $2044
.WORD $2420
.WORD H4.
.WORD $1011
.WORD $0002
.WORD +ULOOP
.WORD $FFE0
.WORD $0A95
.WORD $04B6

Este es el fuente que hay que embeber en el c.m. junto con las otras palabras que usa y cualquier otra que usaran aquellas, etc.

NOTA: Al embeber palabras compilantes de usuario como +ULOOP en nuestro caso solo hemos de embeber la parte Runtime de la misma precedidada del CF Runtime.

En este caso, además, la cosa se complica porque +ULOOP tiene su Runtime en c.m.

En mi caso he procurado NO utilizar ninguna compilante de Usuario para no enredarme. Es por ello que he utilizado el constructo UDO/BEGIN//ULOOP+/UNTIL en los que tanto UDO como ULOOP+ son definciones COLON normales y corrientes, NO palabras tipo COMPILER.


(*)Hay que asegurarse que las palabras que vamos a embeber, si utilizan ." o ( éstos sean de tamaño Par.
Ojo también con ASCII que tiene OF de tamaño uno. Si se tiene que usar, se puede poner un comentario impar con ( justo antes o depués y así queda compensado.
En general, si tenemos en cuenta lo anterior y no usamos COMPILER ni DEFINER, casi seguro que todos los OF son pares como ha sido el caso con todas las palabras que he embebido en c.m. de los posts anteriores.

(**) +ULOOP es una palabra Compilante (como todas las estructuras de control) y hemos de hallar su CFA Runtime. FIND nos devuelve el CFA de la cabecera. Para hallar el CFA Runtime a partir del CFA de la cabecera tenemos que sumarle 2 y sacar lo allí contenido (ver "Jupiter Ace Forth: Estructura de las Palabras")

<<<Continúo en otro Post>>>

Elurdio
Mensajes: 832
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 181 veces
Agradecimiento recibido: 141 veces

Re: Jupiter Ace Forth: Forth Debugger

Mensajepor Elurdio » 05 Abr 2023 21:40

Fichero Fuente de la Rutina en c.m. que carga DEBUG en $E000
Esta es la rutina que toma el control cada vez que se intercepta el Secuenciador.
debug3.png
debug3.png (43.16 KiB) Visto 483 veces

Diagrama Actualizado (8-4-2023)

En el esquema esta rutina sería todo el bloque de memoria de la izquierda de la imagen.
Secuenciador modificado mostrado en color rojo.
La columna derecha está antes en memoria que la izquierda.
El bloque de la derecha muestra en verde todo lo que DEBUG altera/intercepta (y que repone al terminar)
El tamaño de los Recuadros NO no está a escala. Por ejemplo, solo la subrutina PNAME es mucho mayor que todo lo que le precede desde $E000 y el trocito de recuadro que queda justo después de dicha subrutina es casi tan grande como ella.

El fuente que pongo es el que se compiló para el DEBUG que he subido. Hay mucha cosa pendiente de comentar y reorganizar/modificar. Es el que uso aún mientras voy arreglando cosas. Ahora mismo tengo un Bug detectado que he de arreglar pero aún no he tenido tiempo (ni muchas ganas, pues cada vez que pienso en tener que depurar esta rutina con el debugger del Z80, lo dejo para otro rato...)

El fichero Forth Debug.txt al que se hace referencia al principio también está pendiente de terminarse.

Como ocupa más de 60.000 caracteres -> No me deja cargarlo como código.

Lo pongo como .ZIP para descargar.

debug.zip
(15.9 KiB) Descargado 4 veces
EDIT(6-5-2023): Debug.zip Actualizado (código fuente de la rutina maestra en c.m.)

Elurdio
Mensajes: 832
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 181 veces
Agradecimiento recibido: 141 veces

Re: Jupiter Ace Forth: Forth Debugger

Mensajepor Elurdio » 05 Abr 2023 21:42

EDIT (12-4-2023)

Y no necesito este post. Ahora el fichero fuente del c.m. está todo en uno como fichero .ZIP en el post anterior.

Este igual lo uso más adelante.

Elurdio
Mensajes: 832
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 181 veces
Agradecimiento recibido: 141 veces

Re: Jupiter Ace Forth: Forth Debugger

Mensajepor Elurdio » 06 Abr 2023 11:21

Funcionamiento Escueto de la Rutina Maestra en c.m.

Llamo a esta Rutina Maestra simplemente "el depurador"

El funcionamiento del depurador se basa en dos pilares:

  1. Interceptar el Secuenciador
  2. Detectar cuando termina de ejecutarse una palabra concreta.

Punto 1) - Interceptar el Secuenciador

Consiste en poner Jumps desde el Secuenciador Original a un Secuenciador Modificado en el depurador.

Así, cada vez que una palabra llama al Secuenciador, es interceptada por el depurador que toma el control.

Punto 2) - Detectar el final de la ejecución de una palabra.

Es el punto más complicado.

Cada vez que el secuenciador añade o quita un Word Pointer WP de la pila de retornos Rstack, se suma uno o se resta uno de la variable CONTADOR.

Así CONTADOR cuenta los WPs de Rstack.

Para saber cuando una palabra termina, se apunta en la variable STEPOVER el valor de CONTADOR antes de ejecutarla y se da por terminada cuando STEPOVER es mayor o igual que CONTADOR.

Cuando se produce un salto dentro de una palabra (IF/ELSE/UNTIL/LOOP/etc.) CONTADOR se descuenta (vale uno más de lo que debería).

Para detectar los saltos se lleva a cabo una "contabilidad" más compleja que la expuesta. Además de CONTADOR se lleva la variable CONTOLD (con el valor anterior de CONTADOR) y dos variables SP_NEW y SP_OLD con el valor del registro SP (actual y anterior).

Podemos afirmar que

Si CONTADOR > CONTOLD y SP_NEW >= SP_OLD -> Se ha producido un salto.

Entonces procedemos a restar uno a CONTADOR para corregirlo.

Lo que pasa es que no solo los saltos descuentan a CONTADOR, sino que hay muchas palabras del Ace Forth que también lo hacen y nuestra rutina tiene que estar al tanto de si esto sucede o no.

Esto complica bastante el asunto. Pero como podemos estudiar todas y cada una de las palabras ROM del Ace Forth, lo podemos solucionar.

Pero las palabras de Usuario también pueden alterar por su cuenta el núemro de WPs en Rstack (sin que CONTADOR se entere) y ahí poco podemos hacer, pues no siempre podemos detectarlo.... Estas palabras simplemente o no se pueden depurar o según que comandos del depurador no funcionarán como se espera que lo hagan.
----------------------
NOTA: Lo expuesto está basado en un documento más detallado (y soporífero) del asunto. Se puede consultar en este enlace.

Elurdio
Mensajes: 832
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 181 veces
Agradecimiento recibido: 141 veces

Re: Jupiter Ace Forth: Forth Debugger

Mensajepor Elurdio » 06 Abr 2023 12:26

Bug (pendiente de Solucionar) EDIT (7-4-2023): SOLUCIONADO

Afecta a las palabras de Usuario en c.m. tipo primitivas -> CF=PFA que llaman a palabras Forth desde c.m.:

Problema: Muestran las palabras Forth a las que llaman tanto si se hace "Step" como "Over". NO deberían mostrarse en ningún caso:

  • Si se hace "Over" NO se debería mostrar lo que hace la palabra.
  • Si se hace "Step" con una palabra de este tipo -> DEBUG le hace un "Over" automáticamente -> NO se debería mostrar lo que hace la palabra.
Lo detecté hace unos días probando +FLOOP que es todo c.m. y además hace una llamada desde código máquina a F+ que lo muestra cuando la depuras y no debería.

Hoy he hecho más pruebas y veo que es un fallo general. Por como acontece tiene visos de ser un problema del "Over".

Elurdio
Mensajes: 832
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 181 veces
Agradecimiento recibido: 141 veces

Re: Jupiter Ace Forth: Forth Debugger

Mensajepor Elurdio » 06 Abr 2023 12:45

He hecho la palabra EXCSCR en c.m. (pero se podía haber hecho en Forth) que me muestra última pantalla del DEBUG cuando ya ha terminado. Es útil porque a menudo interesa consultarla tras hacer una depuración.

La muestra hasta que pulsas la tecla S y entonces repone la pantalla actual.

Obviamente, si aún no se ha hecho ninguna depuración cuando la usemos, mostrará basura...

excscr.TZX
(170 Bytes) Descargado 4 veces

Elurdio
Mensajes: 832
Registrado: 07 Dic 2021 21:33
Ubicación: Barcelona
Agradecido : 181 veces
Agradecimiento recibido: 141 veces

Re: Jupiter Ace Forth: Forth Debugger

Mensajepor Elurdio » 06 Abr 2023 23:48

Elurdio escribió:Bug (pendiente de Solucionar)
Afecta a las palabras de Usuario en c.m. tipo primitivas -> CF=PFA que llaman a palabras Forth desde c.m.:
Problema: Muestran las palabras Forth a las que llaman tanto si se hace "Step" como "Over". NO deberían mostrarse en ningún caso:
···.


He rastreado un DEBUG de una palabra de este tipo con el depurador del Z80 y la cosa pinta fea. A falta de estudiarlo más a fondo, todo parece indicar que el método que usa el Ace Forth para llamar una palabra Forth desde c.m. (ver aquí) hace que se descuente CONTADOR por defecto (o sea, cuenta uno menos de lo que debería contar).

Este descuento a la baja hace que el "Over" automático que se impone a la palabra por ser una primitiva en c.m. se desactiva (se da por terminado, pues se cumple la condición necesaria, cuando NO debería cumplirse aún) en cuanto la palabra ejecuta la llamada al Forth desde su c.m.

Tengo una idea de una posible solución, pero como no funcione... feo, muy feo!


Volver a “Jupiter Ace”

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 1 invitado