miércoles, 13 de marzo de 2024

Implementación de BEGIN AGAIN/UNTIL

BEGIN - AGAIN es un ciclo infinito que se expresa en assembler como una secuencia de instrucciones terminadas con un JMP de vuelta a la primera instrucción del ciclo.
inicio: inst1
        inst2
         ...
        inst n
        jmp inicio

BEGIN es un palabra inmediata, es decir, es ejecutada durante la compilación. Durante la compilación, el BEGIN simplementa deja en el stack la dirección de la primera celda libre en memoria, donde se deja la palabra construida. Esa es la misma celda que ocupara la primera instrucción dentro del bloque.

Cuando la compilación encuentra el AGAIN (tambien inmediata), este toma la dirección en el stack y la graba en la celda libre, a continuación de la última instrucción del bloque. En J1s, una dirección es también un salto a esa dirección.

Ejemplo

: x begin 1 + again ;

compila en

>see x
 c0e  8001    ; cargar 1 en el stack
 c0f  6203    ; sumar
 c10  c0e     ; salto a la primer instrucción
 c11  608c    ; Retorno (agregada por default a toda palabra)
 c12  0       ; Marca de fin.

 

BEGIN - UNTIL

Este es un ciclo que se repite mientras UNTIL (también inmediata) vea un cero en el tope del stack. Su implementación es la misma del BEGIN - AGAIN, pero con un salto condicional:

: x begin 1 + until ;

compila en

>see x
 c18  8001    ; Cargar 1 en el stack
 c19  6203    ; Sumar
 c1a  2c18    ; Salto condicional si stack == 0 a c18.
 c1b  608c    ; Retorno (agregado por default)
 c1c  0       ; Marca de fin.

Balanceo

La parte interesante es asegurarse de que los BEGIN - AGAIN/UNTIL esten bien anidados.

Las celdas son de 16 bits, pero las direcciones sólo tiene 12 bits. Podemos aprovechar esos 4 bits marcar las direcciones que dejamos en el stack. Para ello usaremos la siguiente asignación:

0x1000 constant sys-begin
0x2000 constant sys-if
0x3000 constant sys-do
...

Entonces, BEGIN coloca en el stack el valor 0x1xxx, donde xxx es la dirección donde ocurre el BEGIN.

Cuando la compilación encuentra el AGAIN/UNTIL, en el stack debe haber una celda con el valor 1 en los bits 15-12. Si tiene cualquier otro valor, hay algo mal anidado y se aborta la compilación.

Recuerde que todo esto pasa durante la compilación de una palabra, no durante la ejecución.

Implementación 

Finalmente, este el código que implementa las estructuras de control.

\ Convierte un valor en inst. de carga
: make-load ( n -- n ) 0x7FFF invert + ;
\ Convierte una dirección en salto condicional
: make-branch ( n -- n ) 0x2000 or ;
\ Convierte un valor en un sys-valor
: make-sys ( addr n -- sys-n ) or ;
\ Revisa el sys-valor en el stack;
: sys-check ( n sys-code -- n ) over 0x7000 and = ;

\  make-clean borra los bits 15-12 (definido en el kernel)

: begin sys-begin here make-sys ; immediate
: until sys-begin sys-check
      if make-clean make-branch ,
      else ." UNTIL desbalanceado" quit then ; immediate
: again sys-begin sys-check
      if make-clean ,
      else ." AGAIN desbalanceado" quit then ; immediate
 


No hay comentarios.:

Publicar un comentario