El taller de Juanan: 0x04 Clear Screen
29/12/2024Hola de nuevo.
Parace que en El taller de Juanan sólo hay rutinas en ensamblador para usar desde Basic; en la siguiente entrega a ésta cambiaremos de tercio, y no de tercio de pantalla.
Voy a mostrar unos efectos de barrido de pantalla, usando casi la misma técnica que usé para el scroll.
Clear screen
He implementado seis tipos de barrido:
- Hacia la izquierda píxel a píxel.
- Hacia la derecha píxel a píxel.
- Mixto píxel a píxel.
- Hacia la izquierda carácter a carácter.
- Hacia la derecha carácter a carácter.
Como ya comenté en la rutina de scroll, al desplazar píxel a píxel no desplazo los atributos, por lo que voy a recuperar la rutina Clear attributes de la entrega anterior para cambiar los colores de la pantalla antes de hacer el barrido. De esta manera vamos a tener dos rutinas, la que cambia los atributos y la que hace el barrido, que cargaré individualmente.
Todas las rutinas son reubicables, por lo que se pueden cargar en otras direcciones distintas a las usadas en los ejemplos.
Píxel a píxel hacia la izquierda
La rutina se compone de cuatro bucles anidados. El principal es de 256 iteraciones para que se desplacen los 256 píxeles de ancho de la pantalla. El segundo es para hacer el desplazamiento en 22 líneas de la pantalla, el tercero para hacerlo en los 8 scanlines de cada línea y el cuarto para las 32 columnas de cada scanline.
Dado que voy a desplazar toda la pantalla, la rutina se simplifica con respecto a la de scroll ocupando tan 51 bytes frente a los 73 de la rutina de scroll.
Main: ld bc, 0x0100 ; B = 256 píxeles de ancho LoopL: push bc ; Preserva BC ld b, 0x16 ; B = 22 líneas ld hl, 0x4000 ; HL = línea 0, scanline 0, columna 0 loopL_line: push bc ; Preserva BC ld b, 0x08 ; B = 8 scanlines * línea push hl ; Perserva HL loopL_scan: push bc ; Preserva BC ld b, 0x20 ; B = 32 columnas * scanline ld a, l ; A = L or 0x1f ; A = columna 31 ld l, a ; L = columna 31 loopL_col: rl (hl) ; Rota izquierda dec l ; L = columna anterior djnz loopL_col ; Bucle hasta recorrer 32 columnas loopL_scanEnd: inc l ; L = columna siguiente inc h ; H = siguiente scanline pop bc ; Recupera BC (bucle scanline) djnz loopL_scan ; Bucle hasta recorrer 8 scanlines loopL_lineEnd: pop hl ; Recupera HL (1er scanline línea) ld a, l ; A = línea add a, 0x20 ; A+= 1 línea ld l, a ; L = línea siguiente jr nc, loopL_end ; ¿Acarreo? No, salta ld a, h ; A = H add a, 0x08 ; A+= 1 tercio ld h, a ; A = tercio siguiente loopL_end: pop bc ; Recupera BC (bucle línea) djnz loopL_line ; Bucle hasta recorrer 12 líneas Loop_end: pop bc ; Recupera BC (bucle 0x0100) dec bc ; BC-= 1 ld a, b ; A = B or c ; ¿BC = 0? jr nz, LoopL ; No, bucle ret ; Vuelve a Basic
Cargo en B las iteraciones del bucle principal (256), preservo BC para volver a usarlo en el siguiente bucle, cargo en B las líneas de la pantalla (22), apunto HL a la primera dirección del área de píxeles (16384), preservo BC para usarlo en el siguiente bucle de scanlines, cargo en B el número de scanlines por línea (8), preservo BC para usarlo en el siguiente bucle de columnas, cargo en B el número de columnas (32), apunto HL a la columna 31 y roto las 32 columnas un píxel hacia la izquierda. Al contrario que en la rutina de scroll, no se restaura el primer píxel en el último, se pierde para que la pantalla se limpie.
Una vez recorridos los 8 scanlines paso a la siguiente línea, y una vez recorridas las 22 líneas vuelvo al principio hasta desplazar los 256 píxeles horizontales de la pantalla.
Para probar el barrido de la pantalla reutilizo el programa Basic de la entrega anterior, Clear attributes, modificando lo necesario.
1 DATA 0,3,15,63,127,123,125,62 2 DATA 0,128,193,243,255,255,187,253 3 DATA 48,120,254,255,249,246,239,255 4 DATA 0,24,124,252,254,254,124,120 5 DATA 31,7,3,1,0,0,0,0 6 DATA 254,255,247,231,3,1,0,0 7 DATA 255,47,223,255,249,240,224,0 8 DATA 224,240,248,240,192,0,0,0 9 DATA 63,127,239,247,238,127,63,0 10 DATA 255,255,190,119,251,255,255,0 11 DATA 248,252,238,222,190,252,248,0 20 DATA 58,176,92,33,0,88,17,1 21 DATA 88,1,191,2,119,237,176,201 30 DATA 1,0,1,197,6,22,33,0,64,197 31 DATA 6,8,229,197,6,32,125,246,31,111 32 DATA 203,22,45,16,251,44,36,193,16,239 33 DATA 225,125,198,32,111,48,4,124,198,8 34 DATA 103,193,16,221,193,11,120,177,32,209 35 DATA 201 40 BORDER 0: PAPER 0: INK 7: CLS : PRINT FLASH 1;AT 10,8;"CARGANDO DATOS" 50 FOR i=0 TO 87: READ a: POKE USR "A"+i,a: NEXT i 60 FOR i=0 TO 15: READ a: POKE 32000+i,a: NEXT i 70 FOR i=0 TO 50: READ a: POKE 32016+i,a: NEXT i 100 BORDER 1: PAPER 5: INK 7: CLS 110 PRINT AT 1,5;CHR$(144)+CHR$(145)+CHR$(146)+CHR$(147): PRINT AT 2,5;CHR$(148)+CHR$(149)+CHR$(150)+CHR$(151) 120 PRINT AT 2,15;CHR$(144)+CHR$(145)+CHR$(146)+CHR$(147): PRINT AT 3,15;CHR$(148)+CHR$(149)+CHR$(150)+CHR$(151) 130 PRINT AT 1,25;CHR$(144)+CHR$(145)+CHR$(146)+CHR$(147): PRINT AT 2,25;CHR$(148)+CHR$(149)+CHR$(150)+CHR$(151) 140 INK 1:PRINT AT 5,10;CHR$(144)+CHR$(145)+CHR$(146)+CHR$(147): PRINT AT 6,10;CHR$(148)+CHR$(149)+CHR$(150)+CHR$(151) 150 PRINT AT 9,0;INK 3;CHR$(152)+CHR$(153)+CHR$(154): PRINT AT 10,10;INK 2;CHR$(152)+CHR$(153)+CHR$(154): PRINT AT 9,20;INK 1;CHR$(152)+CHR$(153)+CHR$(154) 160 INK 2:PAPER 6:FOR f=18 TO 20 STEP 2: PRINT AT f,0;CHR$(152)+CHR$(153)+CHR$(153)+CHR$(154);: FOR c=0 TO 6: PRINT CHR$(152)+CHR$(153)+CHR$(153)+CHR$(154);: NEXT c: NEXT f 170 FOR f=19 TO 21 STEP 2: PRINT AT f,0;CHR$(153)+CHR$(154)+CHR$(152)+CHR$(153);: FOR c=0 TO 6: PRINT CHR$(153)+CHR$(154)+CHR$(152)+CHR$(153);: NEXT c: NEXT f 180 INK 1:PAPER 5:PRINT AT 13,5;"Play On Retro Magazine": PRINT AT 15,8;"Pulse una tecla" 200 PAUSE 0 210 POKE 23728,64+2*8+7 220 RANDOMIZE USR 32000 230 RANDOMIZE USR 32016
En las líneas de la 1 a la 11 está la definición de los gráficos, en la 20 y la 21 la rutina Clear attributes, de la 30 a la 35 la rutina de barrido, en la 50 cargo los gráficos, en la 60 la rutina de cambio de atributos y en la 70 la rutina de barrido. De la línea 100 a la 180 relleno la pantalla y de la 210 a la 230 ejecuto las dos rutinas.
Una vez pulsada una tecla, la pantalla se pone con el fondo en rojo, la tinta en blanco, el brillo activado y hace el barrido hacia la izquierda.
Recordad que estás rutinas solo afectan a 22 líneas. Si el efecto lo hacéis sobre una pantalla de carga, en la línea 21 tenéis que cambiar el tercer byte (191) por 255. En la línea 30 tenéis que cambiar el sexto byte (22) por 24.
Píxel a píxel hacia la derecha
El barrido hacia la derecha es prácticamente igual al barrido hacia la izquierda, sólo cambian cuatro líneas, o lo que es lo mismo 5 bytes, y los nombres de las etiquetas. Marco las líneas que cambian.
Main: ld bc, 0x0100 ; B = 256 píxeles de ancho LoopR: push bc ; Preserva BC ld b, 0x16 ; B = 22 líneas ld hl, 0x4000 ; HL = línea 0, columna 0 loopR_line: push bc ; Preserva BC ld b, 0x08 ; B = 8 scanlines * línea push hl ; Preserva BC loopR_scan: push bc ; Preserva BC ld b, 0x20 ; B = 32 columnas * scanline ld a, l ; A = L and 0xe0 ; A = línea ld l, a ; L = línea loopR_col: rr (hl) ; Rota derecha inc l ; L = columna siguiente djnz loopR_col ; Bucle hasta recorrer 32 columnas loopR_scanEnd: dec l ; L = columna anterior inc h ; H = scanline siguiente pop bc ; Recupera BC (bucle scanlines) djnz loopR_scan ; Bucle hasta recorrer 8 scanlines loopR_lineEnd: pop hl ; Recupera HL (1er scanline línea) ld a, l ; A = L add a, 0x20 ; A+= 1 línea ld l, a ; L = línea siguiente jr nc, loopR_end ; ¿Acarreo? No, salta ld a, h ; A = H add a, 0x08 ; A+= 1 tercio ld h, a ; H = tercio siguiente loopR_end: pop bc ; Recupera BC (bucle líneas) djnz loopR_line ; Bucle hasta recorrer 12 líneas Loop_end: pop bc ; Recupera BC (bucle 0x0100) dec bc ; BC-= 1 ld a, b ; A = B or c ; ¿BC = 0? jr nz, LoopR ; No, bucle ret ; Vuelve a Basic
Voy a usar el mismo programa Basic que he usado en el barrido hacia la izquierda, sólo cambian las líneas de la 30 a la 35.
30 DATA 1,0,1,197,6,22,33,0,64,197 31 DATA 6,8,229,197,6,32,125,230,224,111 32 DATA 203,30,44,16,251,45,36,193,16,239 33 DATA 225,125,198,32,111,48,4,124,198,8 34 DATA 103,193,16,221,193,11,120,177,32,209 35 DATA 201
En concreto cambian 5 bytes con respecto al barrido hacia la izquierda. Con esta rutina la pantalla se borra desplazándose hacia la derecha.
Para que el desplazamiento se haga en las 24 líneas, los cambios son los mismos que en la rutina anterior.
Píxel a píxel mixto
Igual que hice con las rutinas de scroll, voy a hacer una mezcla de las dos anteriores para que una parte de la pantalla vaya hacia la derecha y otra hacia la izquierda.
Main: ld bc, 0x0100 ; B = 256 píxeles de ancho LoopR: push bc ; Preserva BC ld b, 0x0b ; B = 11 líneas ld hl, 0x4000 ; HL = línea 0, columna 0 loopR_line: push bc ; Preserva BC ld b, 0x08 ; B = 8 scanlines * línea push hl ; Preserva BC loopR_scan: push bc ; Preserva BC ld b, 0x20 ; B = 32 columnas * scanline ld a, l ; A = L and 0xe0 ; A = línea ld l, a ; L = línea loopR_col: rr (hl) ; Rota derecha inc l ; L = columna siguiente djnz loopR_col ; Bucle hasta recorrer 32 columnas loopR_scanEnd: dec l ; L = columna anterior inc h ; H = scanline siguiente pop bc ; Recupera BC (bucle scanlines) djnz loopR_scan ; Bucle hasta recorrer 8 scanlines loopR_lineEnd: pop hl ; Recupera HL (1er scanline línea) ld a, l ; A = L add a, 0x20 ; A+= 1 línea ld l, a ; L = línea siguiente jr nc, loopR_end ; ¿Acarreo? No, salta ld a, h ; A = H add a, 0x08 ; A+= 1 tercio ld h, a ; H = tercio siguiente loopR_end: pop bc ; Recupera BC (bucle líneas) djnz loopR_line ; Bucle hasta recorrer 12 líneas
Al igual que en las rutinas anteriores, hago un bucle principal de 256 iteraciones. Para desplazar la pantalla hacia la derecha recorro 11 líneas (0x0B), empezando por la dirección de memoria 0x4000, línea 0, scanline 0, columna 0.
Por cada línea recorro los 8 scanlines (0x08) y por cada scanline las 32 columnas (0x20). Roto cada columna a la derecha RR (HL). Tras rotar todas las columnas apunto L a la columna anterior, DEC L, paso al siguiente scanline, y tras rotar los ocho scanlines paso a la siguiente línea comprobando si cambio de tercio o no.
A diferencia del scroll, no compruebo si en la última rotación del scanline se activa el acarreo, ese bit lo deshecho.
LoopL: ld b, 0x0b ; B = 11 líneas ld hl, 0x4860 ; HL = línea 11, columna 0 loopL_line: push bc ; Preserva BC ld b, 0x08 ; B = 8 scanlines * línea push hl ; Perserva HL loopL_scan: push bc ; Preserva BC ld b, 0x20 ; B = 32 columnas * scanline ld a, l ; A = L or 0x1f ; A = columna 31 ld l, a ; L = columna 31 loopL_col: rl (hl) ; Rota izquierda dec l ; L = columna anterior djnz loopL_col ; Bucle hasta recorrer 32 columnas loopL_scanEnd: inc l ; L = columna siguiente inc h ; H = siguiente scanline pop bc ; Recupera BC (bucle scanline) djnz loopL_scan ; Bucle hasta recorrer 8 scanlines loopL_lineEnd: pop hl ; Recupera HL (1er scanline línea) ld a, l ; A = línea add a, 0x20 ; A+= 1 línea ld l, a ; L = línea siguiente jr nc, loopL_end ; ¿Acarreo? No, salta ld a, h ; A = H add a, 0x08 ; A+= 1 tercio ld h, a ; A = tercio siguiente loopL_end: pop bc ; Recupera BC (bucle línea) djnz loopL_line ; Bucle hasta recorrer 12 líneas Loop_end: pop bc ; Recupera BC (bucle 0x0100) dec bc ; BC-= 1 ld a, b ; A = B or c ; ¿BC = 0? jr nz, LoopR ; No, bucle ret ; Vuelve a Basic
El desplazamiento de la pantalla hacia la izquierda tiene diferencias con el desplazamiento hacia la derecha.
Empiezo en la posición de memoria 0x4860, línea 11, scanline 0, columna 0. A la hora de rotar las columnas lo hago de la 31 a la 0, OR 0x1F, y en lugar de rotar hacia la derecha lo hago hacia la izquierda, RL (HL). Tras rotar la última columna del scanline apunto L a la columna siguiente, INC L.
Para el programa Basic para probar la rutina, parto de cualquiera de los anteriores y modifico las líneas datas de la rutina y el bucle que la carga.
Las líneas de la 30 a la 35 las sustituyo por:
30 DATA 1,0,1,197,6,11,33,0,64,197 31 DATA 6,8,229,197,6,32,125,230,224,111 32 DATA 203,30,44,16,251,45,36,193,16,239 33 DATA 225,125,198,32,111,48,4,124,198,8 34 DATA 103,193,16,221,6,11,33,96,72,197 35 DATA 6,8,229,197,6,32,125,246,31,111 36 DATA 203,22,45,16,251,44,36,193,16,239 37 DATA 225,125,198,32,111,48,4,124,198,8 38 DATA 103,193,16,221,193,11,120,177,32,169 39 DATA 201
En la línea 70 sustituyo TO 50 por TO 90.
Para que esta rutina recorra las 24 líneas de la pantalla, en la línea 30 sustituyo el sexto byte (11) por 12. En la línea 34 hago lo mismo. También en la línea 34 sustituyo el octavo byte (96) por 128.
Carácter a carácter hacia la izquierda
En las rutinas de desplazamiento carácter a carácter prescindo de la rutina que limpia los atributos, las líneas 20 y 21 de los programas Basic. En estas rutinas vamos a desplazar también los atributos y para limpiar la pantalla, desde Basic, vamos a pasar el atributo a aplicar en la dirección 0x5CB0 (23728).
ATTR: equ 0x5cb0 ; Dirección atributo a aplicar al desplazar Main: ld b, 0x20 ; B = 32 desplazamientos LoopL: push bc ; Preserva BC ld b, 0x16 ; B = 22 líneas ld hl, 0x4000 ; HL = línea 0, columna 0, scanline 0 loopL_line: push bc ; Preserva BC push hl ; Preserva HL ld b, 0x08 ; B = 8 scanlines loopL_scan: push bc ; Preserva BC push hl ; Preserva HL ld d, h ; D = H ld e, l ; E = L inc l ; HL = columna 1 ld bc, 0x1f ; BC = 31 columnas ldir ; Desplaza 31 columnas dec hl ; HL = columna 31 ld (hl), 0x00 ; Limpia columna 31 pop hl ; Recupera HL pop bc ; Recupera BC, scanlines inc h ; HL = siguiente scanline djnz loopL_scan ; Bucle hasta recorrer 8 scanlines loopL_attr: pop hl ; Recupera HL push hl ; Preserva HL ld a, h ; A = H, 010TTSSS and 0x18 ; A = 000TT000 rra rra rra ; A = 000000TT or 0x58 ; A = 010110TT, dirección base atributos ld h, a ; H = A ld d, h ; D = H ld e, l ; E = L inc l ; HL = columna 1 ld bc, 0x1f ; BC = 31 columnas ldir ; Desplaza atributos dec hl ; HL = columna 31 ld a, (ATTR) ; A = atributo que limpia ld (hl), a ; Limpia atributo columna 31 loopL_nextLine: pop hl ; Recupera HL pop bc ; Recupera BC, líneas ld a, l ; A = L, línea add a, 0x20 ; A+= 1 línea ld l, a ; L = línea siguiente jr nc, loopL_cont ; ¿Acarreo? No, salta ld a, h ; A = HL add a, 0x08 ; A+= 1 tercio ld h, a ; H = tercio siguiente loopL_cont: djnz loopL_line ; Bucle hasta recorrer 22 líneas pop bc ; Recupera BC, desplazamientos djnz LoopL ; Bucle hasta realizar 32 desplazamientos ret ; Vuelve a Basic
Esta rutina realiza 32 desplazamientos en 22 líneas de la pantalla, en cada uno de sus 8 scanlines desplaza las 32 columnas hacia la izquierda. El desplazamiento de las columnas se hace con LDIR entre las etiquetas loopL_scan y loopL_attr. Una vez que se desplazan los 8 scanlines de la línea, se desplazan los atributos de la línea. El código que desplaza los atributos se encuentra entre las etiquetas loopL_attr y loopL_nextLine.
Para el programa Basic modifico el que puse al inicio del capítulo. Las líneas de la 20 a la 35 las sustituyo por las siguientes:
20 DATA 6,32,197,6,22,33,0,64,197,229 21 DATA 6,8,197,229,84,93,44,1,31,0 22 DATA 237,176,43,54,0,225,193,36,16,238 23 DATA 225,229,124,230,24,31,31,31,246,88 24 DATA 103,84,93,44,1,31,0,237,176,58 25 DATA 176,92,43,119,225,193,125,198,32,111 26 DATA 48,4,124,198,8,103,16,196,193,16 27 DATA 187,201
La línea 60 queda de la siguiente manera:
60 FOR i=0 TO 71: READ a: POKE 32000+i,a: NEXT i
La línea 210 queda así:
210 POKE 23728,8
Borro la línea 70 y la 230.
Igual que en los casos anteriores, esta rutina desplaza 22 líneas, para que desplace 24 hay que cambiar el quinto byte de la línea 20 (22) por 24.
Carácter a carácter hacia la derecha
Esta rutina, al igual que pasaba con las rutinas de desplazamiento píxel a píxel, es muy parecida a la anterior, marco las diferencias.
ATTR: equ 0x5cb0 ; Dirección atributo a aplicar al desplazar Main: ld b, 0x20 ; B = 32 desplazamientos LoopR: push bc ; Preserva BC ld b, 0x16 ; B = 22 líneas ld hl, 0x401f ; HL = línea 0, columna 31, scanline 0 loopR_line: push bc ; Preserva BC push hl ; Preserva HL ld b, 0x08 ; B = 8 scanlines loopR_scan: push bc ; Preserva BC push hl ; Preserva HL ld d, h ; D = H ld e, l ; E = L dec l ; HL = columna 30 ld bc, 0x1f ; BC = 31 columnas lddr ; Desplaza 31 columnas inc hl ; HL = columna 0 ld (hl), 0x00 ; Limpia columna 0 pop hl ; Recupera HL pop bc ; Recupera BC, scanlines inc h ; HL = siguiente scanline djnz loopR_scan ; Bucle hasta recorrer 8 scanlines pop hl ; Recupera HL loopR_attr: push hl ; Preserva HL ld a, h ; A = H, 010TTSSS, dirección base atributos and 0x18 ; A = 000TT000 rra rra rra ; A = 000000TT or 0x58 ; A = 010110TT ld h, a ; H = A ld d, h ; D = H ld e, l ; E = L dec l ; HL = columna 30 ld bc, 0x1f ; BC = 31 columnas lddr ; Desplaza atributos inc hl ; HL = columna 0 ld a, (ATTR) ; A = atributo que limpia ld (hl), a ; Limpia atributo columna 0 loopR_nextLine: pop hl ; Recupera HL pop bc ; Recupera BC, líneas ld a, l ; A = L, línea add a, 0x20 ; A+= 1 línea ld l, a ; L = línea siguiente jr nc, loopR_cont ; ¿Acarreo? No, salta ld a, h ; A = HL add a, 0x08 ; A+= 1 tercio ld h, a ; H = tercio siguiente loopR_cont: djnz loopR_line ; Bucle hasta recorrer 22 líneas pop bc ; Recupera BC, desplazamientos djnz LoopR ; Bucle hasta realizar 32 desplazamientos ret ; Vuelve a Basic
Parto del programa Basic anterior y sustituyo las líneas de la 20 a la 27 por estas:
20 DATA 6,32,197,6,22,33,31,64,197,229 21 DATA 6,8,197,229,84,93,45,1,31,0 22 DATA 237,184,35,54,0,225,193,36,16,238 23 DATA 225,229,124,230,24,31,31,31,246,88 24 DATA 103,84,93,45,1,31,0,237,184,58 25 DATA 176,92,35,119,225,193,125,198,32,111 26 DATA 48,4,124,198,8,103,16,196,193,16 27 DATA 187,201
Solo han cambiado 7 bytes. Para desplazar 24 líneas en lugar de 22 hay que hacer la misma modificación que en la rutina anterior.
Carácter a carácter mixto
Con esta rutina desplazo la mitad superior de la pantalla hacia la derecha, y la mitad inferior hacia la izquierda. Esta rutina es una combinación de las dos anteriores, pero para que pueda ser reubicable hay que aplicar un pequeño truco. Lo muestro por partes.
ATTR: equ 0x5cb0 ; Dirección atributo a aplicar al desplazar Main: ld b, 0x20 ; B = 32 desplazamientos Loop: push bc ; Preserva BC ld b, 0x16 ; B = 22 líneas ld hl, 0x4000 ; HL = línea 0, columna 0, scanline 0 clear: push bc ; Preserva BC push hl ; Preserva HL ld e, 0x08 ; E = 8 scanlines ld a, b ; A = línea cp 0x0b ; ¿Ha desplazado la mitad superior? jr c, clearL_scan ; Si, salta ; Limpieza desplazando a la derecha clearR_scan: push hl ; Preserva HL push de ; Preserva DE ld a, l ; A = L or 0x1f ; A = columna 31 ld l, a ; L = A ld d, h ; D = H ld e, l ; E = L dec l ; HL = columna 30 ld bc, 0x1f ; BC = 31 columnas lddr ; Desplaza 31 columnas inc hl ; HL = columna 0 ld (hl), 0x00 ; Limpia columna 0 pop de ; Recupera DE, scanlines pop hl ; Recupera HL inc h ; HL = siguiente scanline dec e ; E-= 1 jr nz, clearR_scan ; Bucle hasta recorrer 8 scanlines clearR_attr: pop hl ; Recupera HL push hl ; Preservar HL ld a, h ; A = 010TTSSS and 0x18 ; A = 000TT000 rra rra rra ; A = 000000TT or 0x58 ; A = 010110TT (dirección base atributos) ld h, a ; H = 010110TT ld d, h ; D = H ld a, l ; A = L or 0x1f ; A = columna 31 ld l, a ; L = columna 31 ld e, l ; E = L dec l ; HL = columna 30 ld bc, 0x1f ; BC = 31 columnas lddr ; Desplaza 31 columnas inc hl ; HL = columna 0 ld a, (ATTR) ; A = atributo que limpia ld (hl), a ; Limpia columna 0 jr clear_nextLine ; Siguiente línea
La primera parte desplaza la mitad superior de la pantalla hacia la derecha. La diferencia con las rutinas anteriores es que el cambio de línea es común para los dos desplazamientos, por eso no aparece aquí, la última línea salta a la parte de la rutina que hace el cambio de línea.
clear_int: ; Intermedio, el último JR no llega a Loop jr Loop ; Loop ; Limpieza desplazando a la izquierda clearL_scan: push hl ; Preserva HL push de ; Preserva DE ld d, h ; D = H ld e, l ; E = L inc l ; HL = columna 31 ld bc, 0x1f ; BC = 31 columnas ldir ; Desplaza 31 columnas dec hl ; HL = columna 31 ld (hl), 0x00 ; Limpia columna 31 pop de ; Recupera DE, bucle scanlines pop hl ; Recupera HL inc h ; H+= 1, siguiente scanline dec e ; E-= 1 jr nz, clearL_scan ; Bucle hasta recorrer 8 scanlines clearL_attr: pop hl ; Recupera HL push hl ; Preserva HL ld a, h ; A = H, 010TTSSS and 0x18 ; A = 000TT000 rra rra rra ; A = 000000TT or 0x58 ; A = 010110TT ld h, a ; H = A ld d, h ; D = H ld e, l ; E = L inc l ; L+= 1, HL = columna 1 ld bc, 0x1f ; BC = 31 columnas ldir ; Desplaza 31 columnas dec hl ; HL-=1, HL = columna 31 ld a, (ATTR) ; A = atributo que limpia ld (hl), a ; Limpia atributo columna 31 clear_nextLine: pop hl ; Recupera HL ld a, l ; A = L, LLLCCCCC and 0xe0 ; A = LLL00000 add a, 0x20 ; A+= 1 línea ld l, a ; L = A jr nc, clear_end ; ¿Acarreo? No, salta ld a, h ; A = H, 010TTSSS add a, 0x08 ; A+= 1 tercio ld h, a ; H = tercio siguiente clear_end: pop bc ; Recupera BC, bucle líneas djnz clear ; Bucle hasta recorrer 22 líneas pop bc ; Recupera BC, bucle desplazamientos djnz clear_int ; Bucle hasta realizar 32 desplazamientos ret ; Vuelve a Basic
La segunda parte desplaza la mitad inferior de la pantalla hacia la izquierda y realiza el cambio de línea. La penúltima línea DJNZ clear_int debería saltar al inicio de la rutina Loop, pero está fuera de rango, ese es el motivo de la etiqueta clear_int, para poder saltar a ella y desde ella a Loop y que la rutina sea reubicable.
Para el programa Basic parto del anterior, sustituyendo las líneas de la 20 a la 27 por estas:
20 DATA 6,32,197,6,22,33,0,64,197,229 21 DATA 30,8,120,254,11,56,55,229,213,125 22 DATA 246,31,111,84,93,45,1,31,0,237 23 DATA 184,35,54,0,209,225,36,29,32,233 24 DATA 225,229,124,230,24,31,31,31,246,88 25 DATA 103,84,125,246,31,111,93,45,1,31 26 DATA 0,237,184,35,58,176,92,119,24,45 27 DATA 24,186,229,213,84,93,44,1,31,0 28 DATA 237,176,43,54,0,209,225,36,29,32 29 DATA 237,225,229,124,230,24,31,31,31,246 30 DATA 88,103,84,93,44,1,31,0,237,176 31 DATA 43,58,176,92,119,225,125,230,224,198 32 DATA 32,111,48,4,124,198,8,103,193,16 33 DATA 133,193,16,192,201
En la línea 60 cambio TO 71 por TO 134.
Esta rutina, como las anteriores, recorre 22 líneas. Para que recorra 24 líneas el quinto byte de la línea 20 (22) hay que cambiarlo por 24. El quinto byte de la línea 21 (11) hay que cambiarlo por 12.
Podéis ver el resultado final en este vídeo.
Junto al código ensamblador y Basic, también podéis descargar los ejemplos que he preparado para realizar los efectos en pantallas de carga desplazando las 24 líneas.
BASICEnsambladorZX Spectrum