Un compilador de ZX Spectrum BASIC

Todo sobre la creación, diseño y programación de nuevo software para
nuestro Spectrum

Moderador: Sir Cilve Sinclair

sromero
Nonamed
Mensajes: 1221
Registrado: Mar Abr 17, 2007 12:35 pm
Ubicación: Valencia
Contactar:

Re: Un compilador de ZX Spectrum BASIC

Mensaje por sromero » Dom Feb 08, 2009 8:52 pm

Boriel escribió:Las rutinas de Santi (sromero) y mcleod están puestas con ciertas modificaciones para que funcionen con el compilador, pero aparecen mencionados en el fuente :P Espero que no les importe.


Claro que no, hombre.

Un saludo!
NoP / Compiler

Avatar de Usuario
Metalbrain
Freddy Hardest
Mensajes: 592
Registrado: Lun May 07, 2007 8:17 am
Ubicación: Sevilla
Contactar:

Re: Sobre los 3 fallos descubiertos

Mensaje por Metalbrain » Dom Feb 08, 2009 11:35 pm

Boriel escribió:
  • Optimizaciones varias (a nivel semántico y de código intermedio). Esas que nunca he tenido tiempo de hacer. :(


Hablando de optimizaciones, he visto un par en la rutina __STRCMP (en library-asm\string.asm ):

aquí...:

Código: Seleccionar todo

      ld a, h
      or l
      or d
      or e
      ret z      ; Returns 0 (EQ) if HL == DE == NULL

      ld a, h
      or l
      ld a, -1
      ret z      ; Returns -1 if HL is NULL and DE is not NULL

      ld a, d
      or e
      ld a, 1
      ret z      ; Returns +1 if HL is not NULL and DE is NULL


...estás comprobando dos veces si HL es o no nulo, y se puede usar un pequeño salto para bifurcar al comprobar por primera vez, algo así:

Código: Seleccionar todo

      ld a, h
      or l
      jr z,__HLZERO

      ld a, d
      or e
      ld a, 1
      ret z      ; Returns +1 if HL is not NULL and DE is NULL

[...]

__HLZERO:   ld a, d
      or e
      ret z      ; Returns 0 (EQ) if HL == DE == NULL
      ld a, -1
      ret      ; Returns -1 if HL is NULL and DE is not NULL


Debido al salto, la rutina tarda un pelín más si HL vale 0 (12 estados si DE también vale 0, o 3 si no), pero si ese no es el caso (lo cual debería ser lo normal), ganamos 13 estados.

Por otra parte, aquí:

Código: Seleccionar todo

      xor a
      sbc hl, bc   ; Carry if len(b$) > len(a$)
      jr z, __EQULEN   ; Jump if they have the same length
      sbc a, a   ; A = -1 if len(b$) > len(a$), 0 otherwise
      jr nz, __PRESERVEBC
      add hl, bc   ; Restore HL (original length)
      ld b, h      ; len(b$) <= len(a$)
      ld c, l      ; so BC = hl
      
__PRESERVEBC:
      neg         ; A = 1 if len(b$) > len(a$), 0 otherwise
      add a, a   ; A = A * 2 so (2 or 0)
      dec a      ; A = 1 if len(b$) > len(a$), -1 otherwise
      
__EQULEN:
      pop hl      ; Recovers A$ pointer
      push af      ; Saves A in A' for later (Value to return if strings reach the end)


sabemos que A vale 1 desde la última vez que le asignamos dicho valor, así que podemos mover la etiqueta __EQULEN un poquito hacia arriba y prescindir del xor a inicial, ganando 1 byte (y 4 estados si no tenemos la misma longitud en las cadenas). Quedaría así:

Código: Seleccionar todo

      sbc hl, bc   ; Carry if len(b$) > len(a$)
      jr z, __EQULEN   ; Jump if they have the same length
      sbc a, a   ; A = -1 if len(b$) > len(a$), 0 otherwise
      jr nz, __PRESERVEBC
      add hl, bc   ; Restore HL (original length)
      ld b, h      ; len(b$) <= len(a$)
      ld c, l      ; so BC = hl
      
__PRESERVEBC:
      neg         ; A = 1 if len(b$) > len(a$), 0 otherwise
      add a, a   ; A = A * 2 so (2 or 0)
__EQULEN:
      dec a      ; A = 1 if len(b$) > len(a$), -1 if len(b$) < len(a$), 0 if  len(b$) == len(a$)
      pop hl      ; Recovers A$ pointer
      push af      ; Saves A in A' for later (Value to return if strings reach the end)
SevenuP se escribe con u minúscula y P mayúscula.

Avatar de Usuario
Metalbrain
Freddy Hardest
Mensajes: 592
Registrado: Lun May 07, 2007 8:17 am
Ubicación: Sevilla
Contactar:

Re: Un compilador de ZX Spectrum BASIC

Mensaje por Metalbrain » Lun Feb 09, 2009 12:21 pm

Otra optimización más, esta vez en __PRINTCHAR (en library-asm\print.asm)

Aquí:

Código: Seleccionar todo

__PRGRAPH0:
      ld l, a
      ld h, 0      ; HL = A (accumulator)
      add hl, hl
      add hl, hl
      add hl, hl ; HL = a * 8


llegamos siembre con A<80h, así que lo podemos cambiar por:

Código: Seleccionar todo

__PRGRAPH0:
      add a, a
      ld l, a
      ld h, 0      ; HL = a * 2 (accumulator)
      add hl, hl
      add hl, hl ; HL = a * 8


ganando 7 estados.
SevenuP se escribe con u minúscula y P mayúscula.

Boriel
Sabreman
Mensajes: 351
Registrado: Lun May 28, 2007 9:55 am
Ubicación: Tenerife
Contactar:

Re: Sobre los 3 fallos descubiertos

Mensaje por Boriel » Lun Feb 09, 2009 1:31 pm

Metalbrain escribió:Hablando de optimizaciones, he visto un par en la rutina __STRCMP (en library-asm\string.asm ):

aquí...:

Vamos allá :!:

...estás comprobando dos veces si HL es o no nulo, y se puede usar un pequeño salto para bifurcar al comprobar por primera vez, algo así:

Código: Seleccionar todo

      ld a, h
      or l
      jr z,__HLZERO

      ld a, d
      or e
      ld a, 1
      ret z      ; Returns +1 if HL is not NULL and DE is NULL
               [...]
__HLZERO:
               ld a, d
      or e
      ret z      ; Returns 0 (EQ) if HL == DE == NULL
      ld a, -1
      ret      ; Returns -1 if HL is NULL and DE is not NULL


Debido al salto, la rutina tarda un pelín más si HL vale 0 (12 estados si DE también vale 0, o 3 si no), pero si ese no es el caso (lo cual debería ser lo normal), ganamos 13 estados.

Hmmm. 13 estados me parece muchísimo, y vale la pena considerarlo. Lo examino esta tarde, vale? En cualquier caso, en __HLZERO también se puede sustituir LD A, D por OR D y nos ahorramos 3 estados creo.
Por otra parte, aquí:

Código: Seleccionar todo

      xor a
      sbc hl, bc   ; Carry if len(b$) > len(a$)
      jr z, __EQULEN   ; Jump if they have the same length
      sbc a, a   ; A = -1 if len(b$) > len(a$), 0 otherwise
      jr nz, __PRESERVEBC
...

sabemos que A vale 1 desde la última vez que le asignamos dicho valor, así que podemos mover la etiqueta __EQULEN un poquito hacia arriba y prescindir del xor a inicial, ganando 1 byte (y 4 estados si no tenemos la misma longitud en las cadenas). Quedaría así:

He estado revisando el código, y es cierto. Me explico (para que los demás también lo sigan, si les interesa), pensé en su momento que INC/DEC tb. afectaban al CARRY, pero veo que no. Ese "XOR A", limpia el carry (para la resta que sigue a continuación). :!: Dado que el OR de arriba, efectivamente, ya ha limpiado el carry y las sucesivas INC no lo alteran, se puede aprovechar como tú dices. :!: Gracias! :!: Incluiré esa modificación en la 1.0.5

Avatar de Usuario
Metalbrain
Freddy Hardest
Mensajes: 592
Registrado: Lun May 07, 2007 8:17 am
Ubicación: Sevilla
Contactar:

Re: Sobre los 3 fallos descubiertos

Mensaje por Metalbrain » Lun Feb 09, 2009 1:43 pm

Boriel escribió:En cualquier caso, en __HLZERO también se puede sustituir LD A, D por OR D y nos ahorramos 3 estados creo.


No, ambas instrucciones tardan 4 estados, y lo cambié de OR D a LD A, D para que quedase igual que más arriba y fuese así ligeramente más comprimible, pero en realidad da igual una cosa que la otra, ya que venimos con A = 0.

Boriel escribió:pensé en su momento que INC/DEC tb. afectaban al CARRY, pero veo que no.


Aparte de que los INC/DEC no afecten al carry, los que estás usando son de 16 bits que no afectan a ningún flag.
SevenuP se escribe con u minúscula y P mayúscula.

Boriel
Sabreman
Mensajes: 351
Registrado: Lun May 28, 2007 9:55 am
Ubicación: Tenerife
Contactar:

Re: Un compilador de ZX Spectrum BASIC

Mensaje por Boriel » Lun Feb 09, 2009 1:50 pm

Metalbrain escribió:Otra optimización más, esta vez en __PRINTCHAR (en library-asm\print.asm)

Aquí:

Código: Seleccionar todo

__PRGRAPH0:
      ld l, a
      ld h, 0      ; HL = A (accumulator)
      add hl, hl
      add hl, hl
      add hl, hl ; HL = a * 8


llegamos siembre con A<80h, así que lo podemos cambiar por:

Código: Seleccionar todo

__PRGRAPH0:
      add a, a
      ld l, a
      ld h, 0      ; HL = a * 2 (accumulator)
      add hl, hl
      add hl, hl ; HL = a * 8


ganando 7 estados.

Gracias, Metalbrain :!:
Pues sí, 7 estados. Como eso se repite muchas veces, se ahorra mucho. Ej. Para imprimir una cadena de 20 caracteres se pueden ahorrar 140 estados! :P

sromero
Nonamed
Mensajes: 1221
Registrado: Mar Abr 17, 2007 12:35 pm
Ubicación: Valencia
Contactar:

Re: Un compilador de ZX Spectrum BASIC

Mensaje por sromero » Lun Feb 09, 2009 1:56 pm

Una vez esté todo finalizado, tiene pinta que con que 2 ó 3 personas vaya echando vistazos al código ASM se puede depurar y optimizar bastante :-)
NoP / Compiler

juanjo
rst 0
Mensajes: 31
Registrado: Dom Ene 18, 2009 6:03 pm

Re: Un compilador de ZX Spectrum BASIC

Mensaje por juanjo » Lun Feb 09, 2009 2:10 pm

Me esta picando mucho esto, he empezado a hacer un juego de naves. Pero los graficos udg no me bastan, alguien tiene algun link a rutinas de sprites que tengan precision de pixel? O de hacer scroll de fondo tambien.
(Edit: He visto unas rutinas en mhoogle (microhobby 96, pagina 20) pero me he echado un poco p'atras, que complicado!)

Otra cosilla es que se podrian pasar las rutinas de colores en alta resolucion de mcleod_ideafix a un fichero de zxb. No se si a mi me saldria. O quiza prefieras hacerlo tu, mcleod? Si tienes ganas..

Finalmente, echo en falta la instruccion load de basic, para cargar pantallas y bloques de datos. Estaba previsto esto, Boriel?
Última edición por juanjo el Lun Feb 09, 2009 3:49 pm, editado 1 vez en total.

Boriel
Sabreman
Mensajes: 351
Registrado: Lun May 28, 2007 9:55 am
Ubicación: Tenerife
Contactar:

Re: Un compilador de ZX Spectrum BASIC

Mensaje por Boriel » Lun Feb 09, 2009 2:40 pm

juanjo escribió:Finalmente, echo en falta la instruccion load de basic, para cargar pantallas y bloques de datos. Estaba previsto esto, Boriel?

Sí. Es decir, no se podrá usar el clásico LOAD "" SCREEN$, pero sí LOAD "" CODE (o load "" CODE XXXX) para cargar datos binarios. También podría funcionar LOAD "" (para cargar un programa en la zona del BASIC), pero eso más adelante.

Para cargar una pantalla será LOAD "" CODE 16384. A ver si me lo curro. SAVE también estará.

Otra cosa que no he explicado es que intento que sea un compilador exportable (por ej. a .NET) en el futuro. Por eso funciones muy muy específicas del ZX, como SCREEN$ las he sacado fuera, a los archivos library/*.bas . Por ejemplo, ATTR, POINT y SCREEN$ ya las tengo definidas allí (creo que ATTR y POINT ya está incluida en la versión 1.0.4). Esto NO cambia la sintaxis de los programas, porque siguen siendo funciones.

PAUSE era una función y se invocaba con PAUSE(XXX), con los paréntesis obligatorios. Pero la gente se quejó (tanto aquí como en WoS) de que era mejor dejarla como sentencia interna, para que los programas fueran más compatibles con el BASIC de Sinclair, y la dejé así.

Para mantener esta compatibilidad, las funciones que están definidas en los archivos .BAS (externas), se podrán incluir automáticamente en el futuro, con un flag del compilador. Ahora mismo hay que usar #include <attr.bas> por ejemplo, para usar ATTR.

Boriel
Sabreman
Mensajes: 351
Registrado: Lun May 28, 2007 9:55 am
Ubicación: Tenerife
Contactar:

Re: Un compilador de ZX Spectrum BASIC

Mensaje por Boriel » Lun Feb 09, 2009 4:43 pm

juanjo escribió:Me esta picando mucho esto, he empezado a hacer un juego de naves. Pero los graficos udg no me bastan, alguien tiene algun link a rutinas de sprites que tengan precision de pixel? O de hacer scroll de fondo tambien.

Tengo planeadas también rutinas de scroll y roll (circular) pixel a pixel y de 8 en 8 bits. Paciencia. Irán en el library.asm.

Ahora, a muy corto plazo, quería poner (porque me lo pidieron en el foro de soporte), lo de las operaciones binarias. En este hilo ya se discutió pero se dejó apartado. Al final creo que es mejor seguir el convenio FreeBASIC para las rotaciones de bits: shr y shl:

Código: Seleccionar todo

print 8 shr 2

>2


Sin embargo, para el AND y demás, FreeBASIC usa las mismas palabras reservadas AND, OR, NOT. Eso no lo quiero hacer, porque sería incompatible con Sinclair BASIC. Por ejemplo:

Código: Seleccionar todo

10 LET a = 5: LET b = 2
20 IF a AND b THEN PRINT "HOLA"

Imprimirá "HOLA" en Sinclair BASIC (y en el compilador ZX BASIC), pero no en FreeBASIC, que hará un AND bit a bit -que da cero. :?
Por ello se me ha ocurrido que, para las operaciones bit a bit, usar algun de estas:
  • Símbolos (operadores) AND => El símbolo & (como en C y Python), OR => |, NOT => ~ XOR => ^ (Se usa para potencia. Buscar otro):

    Código: Seleccionar todo

    PRINT 5 & 7
    >5

  • Las mismas palabras, pero con alguna distinción, como un punto delante y detrás: .and. .or. .xor. .not.[b]:

    Código: Seleccionar todo

    PRINT 5 .and. 7
    >5

  • Similar a la anterior, usar el prefijo "bin o bit". [b]bitand bitor bitxor o separado, con BIN: BIN AND... La desventaja de esta última es que las expresiones se vuelven difíciles de leer. Ejemplo:

    Código: Seleccionar todo

    PRINT 5 bitand 7
    >5

    Código: Seleccionar todo

    PRINT 5 bin and 7: REM Demasiado "largo"
    >5
Y bien :?: Adelanto ya que mi favorito es el segundo (el del punto).

juanjo
rst 0
Mensajes: 31
Registrado: Dom Ene 18, 2009 6:03 pm

Re: Un compilador de ZX Spectrum BASIC

Mensaje por juanjo » Lun Feb 09, 2009 5:42 pm

Respecto a los operadores, yo prefiero simbolos por separado como & para el and bit a bit, pero igual si que es mas facil de recordar la opcion de los puntos.

Tengo un lio con los arrays, se declaran con el numero de elementos 'dim a(n)' y se referencian con a(0) hasta a(n-1) ? Es que segun veo en el snake.bas, se declaran con n-1 en el tamaño :?:

Boriel
Sabreman
Mensajes: 351
Registrado: Lun May 28, 2007 9:55 am
Ubicación: Tenerife
Contactar:

Re: Un compilador de ZX Spectrum BASIC

Mensaje por Boriel » Lun Feb 09, 2009 6:17 pm

juanjo escribió:Respecto a los operadores, yo prefiero simbolos por separado como & para el and bit a bit, pero igual si que es mas facil de recordar la opcion de los puntos.

Se puede poner que sean ambas cosas, por ejemplo ".and." y "&" o "shr" y ">>". El xor es un problema, porque tradicionalmente es el símbolo "^" (pero ese aquí es potencia).

Tengo un lio con los arrays, se declaran con el numero de elementos 'dim a(n)' y se referencian con a(0) hasta a(n-1) ? Es que segun veo en el snake.bas, se declaran con n-1 en el tamaño :?:

Se pueden declarar así

Código: Seleccionar todo

DIM a(1 TO 5) ' Ojo, no se puede usar "n". No hay arrays dinámicos. N tiene que ser un número

Si especificas solo un número, es como poner DIM a(0 TO N):

Código: Seleccionar todo

DIM a(5): REM Es lo mismo que DIM a(0 TO 5)

Y es desde 0 a 5, ambos inclusive. Yo uso desde 0 a n-1, como en C.

juanjo
rst 0
Mensajes: 31
Registrado: Dom Ene 18, 2009 6:03 pm

Re: Un compilador de ZX Spectrum BASIC

Mensaje por juanjo » Lun Feb 09, 2009 6:31 pm

Ok gracias, solo es que me liaba porque creia que dim a(10) tenia 10 elementos y no 11

sromero
Nonamed
Mensajes: 1221
Registrado: Mar Abr 17, 2007 12:35 pm
Ubicación: Valencia
Contactar:

Re: Un compilador de ZX Spectrum BASIC

Mensaje por sromero » Lun Feb 09, 2009 6:38 pm

juanjo escribió:Ok gracias, solo es que me liaba porque creia que dim a(10) tenia 10 elementos y no 11



¿?

No sé si Boriel se ha rallado, pero DIM A(10) debería tener DIEZ elementos (no 11) de 0 a 9... ¿no?
NoP / Compiler

Boriel
Sabreman
Mensajes: 351
Registrado: Lun May 28, 2007 9:55 am
Ubicación: Tenerife
Contactar:

Re: Un compilador de ZX Spectrum BASIC

Mensaje por Boriel » Lun Feb 09, 2009 7:54 pm

sromero escribió:
juanjo escribió:Ok gracias, solo es que me liaba porque creia que dim a(10) tenia 10 elementos y no 11


¿?
No sé si Boriel se ha rallado, pero DIM A(10) debería tener DIEZ elementos (no 11) de 0 a 9... ¿no?

No, tiene 11 elementos, del 0 al 10 ambos inclusive. Lo que sucede es que si trabajas como en el ZX Spectrum, no usarás el a(0) y lo desperdicias. Esto se suele solucionar tradicionalmente poniendo OPTION BASE 1 en el código (todavía no he implementado el OPTION BASE). O bien poniendo explícitamente DIM a(1 TO 10) (ambos índices). Si se pone sólo el 10, se entiende que es 0 TO 10. Así que son ONCE elementos.

Además, quiero definir el flag --sinclair, que haga todas estas cosas por defecto (OPTION BASE 1, y también los includes de las funciones SCREEN, ATTR y POINT, entre otros).

AÑADO: Trabajar con índice de base 0 es más rápido (como hace C). Trabajar con otros, ya que cada índice de base no 0 lleva implícita una resta extra.

Responder

¿Quién está conectado?

Usuarios navegando por este Foro: Bing [Bot] y 33 invitados