Título: La rutina de transferencia "refinitiva" Publicado por: Darth_Fistro en 24 de Mayo de 2007, 10:20:54 am Hello! :)
Pues revisando el mogollón de topics que he abierto dando la vara sobre el tema, quiero ir sacando un poco "conclusiones"´para utilizar en juegos spectrumeros (léase tabla de patrones a piñón fijo): He aquí una rutina original cortesía de Mr.Robsy: Código: ; Parameters: ; HL: source address (RAM) ; DE: destiny address (VRAM) ; B: number of 8 byte blocks to be copied RAM2VRAM: ld a,e di out (99h),a ld a,d or a,40h out (99h),a ei ld c,98h LOOP: ld d,b outi ; now only 7xOUTI outi outi outi outi outi outi ld b,d outi jp nz,LOOP ; faster, takes 10T ret Para empezar, Dioniso sugiere cambiar lo siguiente: Código: ld a,l di out ($99),a ld a,h and $3F or $40 out ($99),a ei por: Código: ld a,l di out ($99),a ld a,h out ($99),a ei ahorrándonos unos bytes, teniendo que añadir a la llamada a la rutina, tan sólo el sumar a HL el valor $4000 (si volcamos a la 6144, p.e., pues ld hl,6144+$4000) JL apuntó que para que la rutina funcione en MSX1 debemos intercalar un par de nops en los primeros 6 outis (no me quedó claro por qué en los seis y no en los ocho). La cosa quedaría así. Código: ; Parameters: ; HL: source address (RAM) ; DE: destiny address (VRAM) ; B: number of 8 byte blocks to be copied RAM2VRAM: ld a,l di out ($99),a ld a,h out ($99),a ei ld c,98h LOOP: ld d,b outi ; now only 7xOUTI nop nop outi nop nop outi nop nop outi nop nop outi nop nop outi nop nop outi ld b,d outi jp nz,LOOP ; faster, takes 10T ret Y por último, JL también propone "desrollar" la rutina si disponemos de memoria suficiente repitiendo el bucle de outis, supongo que el número de bytes a volcar tendrá que ser múltiplo del número de outis dentro del bucle. ¿Alguna nueva sugerencia para lograr una rutina veloz e interrumpible? (al fin y al cabo los resultados los sufriréis vosotros, jeje) ;D Título: Re: La rutina de transferencia "refinitiva" Publicado por: Dioniso en 24 de Mayo de 2007, 10:45:24 am Lo que yo sugerí no lo he probado en un MSX real (el más perro del planeta), tan sólo en emulador ... pero no sería posible ahorrarte el
Código: and $3F or $40 si directamente cargas el valor en HL y le sumas $4000? Por ejemplo: quieres escribir en la VRAM en la dirección $2000 pues haces un LD HL,$6000 ...? Sobre los dos NOPs ... No sé si siempre, pero un LD A,0 creo que también vale y te ahorras 1 t-state (ya sé que parece poco pero en una ristra de NOP, NOP, OUT donde no importa A puede ser mucho :D ) Título: Re: La rutina de transferencia "refinitiva" Publicado por: Darth_Fistro en 24 de Mayo de 2007, 11:53:01 am Pues no tengo ni idea, Alfonso, en eso estoy pez y si me lo pones en hexa, peor ;)
Bueno, lo de los nops que faltan en los dos últimos outi ya lo veo: el ld b,d y el jp nz,loop ya introducen retardo por ellos mismos. Si dices que ld a,0 ahorra un estado y funciona en un MSX real, daríamos un pasito en cuanto a velocidad :D ¡Hay que exprimir el VDP! (si hace falta, se le puente un chip Fat Agnus) Título: Re: La rutina de transferencia "refinitiva" Publicado por: pitpan en 24 de Mayo de 2007, 12:29:49 pm Tened en cuenta que el tema del retardo va en función de si estamos o no dentro de la interrupción de v-blank. Dentro de la interrupción, con un único NOP nos curamos en salud. Fuera de ella, son necesarios 2. La idea de sustituir NOP NOP por una única instrucción, podría ser una buena alternativa.
De todos modos, mis rutinas actuales son bastante más cafres, e incluyen joyas como REPT 1024 | OUTI | ENDR. En fin, felicidad en estado líquido. Título: Re: La rutina de transferencia "refinitiva" Publicado por: Darth_Fistro en 24 de Mayo de 2007, 12:39:03 pm Gracias a ambos :)
O sea, que si más o menos transfiero 1kb, con un nop va que chuta. Si quiero tansferir 4kbs, le pongo 2 nops y así aseguro que no aparece la imagen distorsionada. Así a bote pronto, con la rutina anterior, ¿cuántos bytes se pueden mandar en un vblank? Título: Re: La rutina de transferencia "refinitiva" Publicado por: Darth_Fistro en 24 de Mayo de 2007, 12:41:16 pm Edu, sobre tu bucle a lo cafre, dos cosillas:
-¿sólo funciona en emulador? -si no pones nops, tienes entonces siempre que tener cuidado de no mandar más bytes de los convenientes en un solo vblank, ¿verdad? Si no, basura al canto ??? Título: Re: La rutina de transferencia "refinitiva" Publicado por: jltursan en 24 de Mayo de 2007, 01:58:59 pm 1) Efectivamente, los NOPs son solo necesarios cuando nos encontramos fuera del VBlank. Se puede usar NOP o lo que se quiera; pero se tienen que sumar alrededor de 28T contando los consumidos por el propio OUT. Para saber cual es la cantidad exacta (teórica por supuesto ;)) de bytes que se pueden transferir sin necesidad de retardos, echa un vistazo por aquí (http://karoshi.msxgamesbox.com/index.php?topic=668.0). Fuera de este intervalo te arriesgas a que si no retardas, aparezca basura.
2) Para enviar la dirección de inicio de escritura basta con enviar los dos bytes consecutivamente al VDP a través del puerto de comandos, primero se envía el bajo tal cual y luego el alto. El AND $3F se hace únicamente para asegurar que no vamos a superar los 16Kb y el OR $40 porque ese bit (el 6) tiene que estar a 1 para escritura. Ambas operaciones son superfluas, tal como dice Dioniso basta con construirse una direccion de VRAM un poco "especial" previamente (con $4000 escribiriamos en la $0000). Otra cosa, el EI puede estar por encima del último OUT, las interrupciones se activan a continuación de la instrucción que sigue al EI. La rutina quedaria algo así: 3) La rutina de volcado de Robsy estaba pensada para que se intentarán enviar durante el VBlank todos los bytes posibles sin retardo y a partir de ese punto intercalaba retardos hasta alcanzar el total de bytes que quería enviar. Funciona (o debería) en cualquier emulador y equipo real. Código: ; ; DE: destiny address + $4000 (VRAM) ; ld a,e di out (99h),a ld a,d ei out (99h),a Título: Re: La rutina de transferencia "refinitiva" Publicado por: Darth_Fistro en 24 de Mayo de 2007, 03:12:01 pm Gracias por la lección magistral, como siempre :D
Veamos, volvamos a un volcado de 6kbs: -vuelco 800 bytes a saco tras un HALT. -luego puedo volcar con dos nops para asegurarme que nada se corrompe. En todo caso, si se puede hacer así entonces, ¿podría forzar a las máquinas de 60Hz a funcionar a 50Hz y así poder volcar 1500 bytes tranquilamente? La mejora es considerable. ??? Muy interesante el tema este del VDP (nunca había pasado de la BIOS) Título: Re: La rutina de transferencia "refinitiva" Publicado por: Darth_Fistro en 24 de Mayo de 2007, 03:13:04 pm Otra: si se puede forzar un evío de 1500 bytes, volcar la pantalla en cuatro HALTs como cuatro bloques de 1,5 kbs para evitar las "olas" fuera de sincronismo. Could I?
Título: Re: La rutina de transferencia "refinitiva" Publicado por: Darth_Fistro en 24 de Mayo de 2007, 03:14:17 pm ¿Por qué comentas que la rutina de Edu sigue ese modo de funcionamiento? Por lo que parece en el bucle vuelca OUTIs a saco...
Título: Re: La rutina de transferencia "refinitiva" Publicado por: jltursan en 24 de Mayo de 2007, 03:58:45 pm Citar -vuelco 800 bytes a saco tras un HALT. -luego puedo volcar con dos nops para asegurarme que nada se corrompe. Básicamente es así como se hace. El HALT sigue siendo problematico a la hora de sincronizar video; pero bueno, es que es tan cómodo... ::) Citar ¿Por qué comentas que la rutina de Edu sigue ese modo de funcionamiento? Por lo que parece en el bucle vuelca OUTIs a saco... Porque el propio Robsy lo comentó en su día y además yo la desensamblé... ;D Citar En todo caso, si se puede hacer así entonces, ¿podría forzar a las máquinas de 60Hz a funcionar a 50Hz y así poder volcar 1500 bytes tranquilamente? Nunca hagas eso (aunque yo lo hice en el CoT :(), si la máquina es un MSX1 a 60Hz, no vas a poder conmutar la frecuencia y por lo tanto tu juego no funcionaría bien en esos modelos. Lo mejor es prepararte para lo peor y hacerlo pensando en 60Hz, si luego le sobra, mejor que mejor. Citar Otra: si se puede forzar un evío de 1500 bytes, volcar la pantalla en cuatro HALTs como cuatro bloques de 1,5 kbs para evitar las "olas" fuera de sincronismo. Could I? El como distribuyas los volcados ya depende de como tengas montado el juego, el tipo de este, lo que quieras conseguir, etc. Simplemente tu sigue la regla de los 800+NOPs y el resto tendrás que imaginarlo tú... ;-) Recuerda que todo esto es simplemente una guía de como sacarle la máxima velocidad al VDP evitando corrupciones. El como evitar parpadeos y tirones ya depende de como te organices. Título: Re: La rutina de transferencia "refinitiva" Publicado por: Darth_Fistro en 24 de Mayo de 2007, 04:28:29 pm ¡Gracias!
Citar ¿Por qué comentas que la rutina de Edu sigue ese modo de funcionamiento? Por lo que parece en el bucle vuelca OUTIs a saco Porque el propio Robsy lo comentó en su día y además yo la desensamblé... ;D Me refería a la rutina de Edu que he publicado en este topic, parece que es un bucle mandando 8 outis a saco, a no ser que te refieras a la de algún juego en concreto (jejeje, hacker...) ;) Título: Re: La rutina de transferencia "refinitiva" Publicado por: SapphiRe_MSX en 28 de Mayo de 2007, 04:55:33 pm Bueno, yo lo que hago es agrupar todas las transferencias de forma que se realicen siempre en el vblank, para lo cual no necesito meter retardos entre outi y outi. Si tengo que hacer transferencias más grandes (normalmente al inicio de un nivel o algo así) desconecto la visualización de la pantalla, lo cual me permite hacer lo mismo durante todo el tiempo.
Además, ahorro unos cuantos ciclos extra desenrollando el bucle y aprovechándome de que cada outi realiza un dec b de forma automática, por lo que si en cada bucle hago 16 outi más un djnz, realmente estoy decrementando b un total de 17 veces por bucle. Así, si quiero transferir 320 bytes hago el siguiente cálculo: 320 div 16 = 20 -> tengo que dar 20 vueltas al bucle 20 * 17 = 340 -> he de decrementar b en 340 unidades antes de que llegue a cero, luego 340 mod 256 = 84 -> este es el valor con el que tengo que inicializar b En general, si cada bucle se compone de N instrucciones outi, para transferir L bytes (con L múltiplo de N, por supuesto) habría que inicializar b con la siguiente instrucción: Código: ld b,( (L div N) * (N+1) ) mod 256 y como esas operaciones se realizan en tiempo de compilación, no hay problema alguno y ahorramos unos cuantos ciclos extra que en las rutinas que Darth ha publicado en el primer post se llevan las instrucciones ld d,b y ld b,d. Por si alguien se pregunta si podría darse el caso de que estuviéramos realizando un número incorrecto de transferencias, le diré que se tranquilice, ya que N y N+1 son números primos entre sí, por lo que su mínimo común múltiplo es, precisamente, el producto de ambos y el método funciona para cualesquiera L y N (con L múltiplo de N). Ahora no tengo aquí la rutina, así que si os interesa decídmelo y la pongo aquí para quien quiera utilizarla y optimizarla (cosa que molaría mucho). Saludos -- SapphiRe Título: Re: La rutina de transferencia "refinitiva" Publicado por: Darth_Fistro en 29 de Mayo de 2007, 03:29:21 pm ¡Joder, es que esto de ser matemático te da gran ventaja! :D
Muchas gracias, Sapphire, y además veo que no hace falta enviar a Chuck Norris a tu casa para que publiques esa rutina... ¡ya estás tardando! :D Entonces, además del genial truco del desglose de outis, cuando se desconecta la pantalla te puedes desentender del vblank y volcar outis a saco, sin nops ni leches :) Genial para inicializar todo, como dices :D ¡Gracias de nuevo! Título: Re: La rutina de transferencia "refinitiva" Publicado por: SapphiRe_MSX en 29 de Mayo de 2007, 03:41:29 pm ¡Joder, es que esto de ser matemático te da gran ventaja! :D Según para que cosas... ::) ::) Citar Muchas gracias, Sapphire, y además veo que no hace falta enviar a Chuck Norris a tu casa para que publiques esa rutina... ¡ya estás tardando! :D Hombre, si le mandas para acá le pediré un autógrafo para todos los del foro ;D ;D ;D ;D En cuanto tenga algo de tiempo enchufo el portátil y copio la rutina aquí. Citar Entonces, además del genial truco del desglose de outis, cuando se desconecta la pantalla te puedes desentender del vblank y volcar outis a saco, sin nops ni leches :) Genial para inicializar todo, como dices :D Pos si, y si no es así, que alguien me corrija :P Saludos -- Sph. Título: Re: La rutina de transferencia "refinitiva" Publicado por: SapphiRe_MSX en 29 de Mayo de 2007, 05:20:04 pm La rutinita de marras:
Código: ; FAST LDIRVM (en MSX1 sólo funciona dentro del vblank o con la pantalla apagada) FLDIRVM: ld a,[7] ; a = puerto #0 de escritura del VDP ld c,a ; c = puerto #0 de escritura del VDP inc c ; c = puerto #1 de escritura del VDP out [c],e ; Escribimos en el VDP el byte bajo de la direccion de destino set 6,d ; Activamos el sexto bit del byte alto (no seria necesario si ya lo dejamos activado al inicializar DE) res 7,d ; Desactivamos el séptimo bit del byte alto (no seria necesario si ya lo dejamos desactivado al inicializar DE) out [c],d ; Escribimos en el VDP el byte alto de la direccion de destino dec c ; c = puerto #0 de escritura del VDP @@LOOP: outi ; (1) outi ; (2) outi ; (3) outi ; (4) outi ; (5) outi ; (6) outi ; (7) outi ; (8) outi ; (9) outi ; (10) outi ; (11) outi ; (12) outi ; (13) outi ; (14) outi ; (15) outi ; (16) djnz @@LOOP ret Uso: HL = Direccion de origen en RAM DE = Direccion de destino en VRAM (si activamos los bits adecuados de D nos podríamos ahorrar las instrucciones set y res) B = Numero de bloques de 16 bytes a transferir, pero multiplicados por 17 y módulo 256 Es decir, para transferir, por ejemplo, 704 bytes, habría que inicializar B a: 704 div 16 = 44 44 * 17 = 748 748 mod 256 = 236 Si el número de bytes a transferir fuera múltiplo de 256, el primer número (en este caso 44) y el último (en este caso 236) coincidirían. ¿Por qué he escogido usar 16 outis? Por la sencilla razón de que la cantidad máxima que puede enviar esta rutina es de 2048 bytes (256 bloques de 16 bytes), que es, precisamente, el tamaño de una tabla de patrones o colores, pero claro, se puede cambiar y meter el número de outis que sean necesarios, si quieres 32 o incluso 64, modificando adecuadamente los números de las operaciones. Un último consejo: usadla con las interrupciones desactivadas ;D ;D Saludos -- Sph. Título: Re: La rutina de transferencia "refinitiva" Publicado por: SapphiRe_MSX en 30 de Mayo de 2007, 05:19:55 pm ¿Nadie le ha echado un vistazo? ??? ???
Título: Re: La rutina de transferencia "refinitiva" Publicado por: Darth_Fistro en 31 de Mayo de 2007, 10:18:16 am Mil gracias, Sapphire :D La ví ayer,pero no pude responder, y ahora no es que me pueda extender mucho, que estoy en el curro :D
De muerte viene la rutina, por cierto ;) Cosillas: comentas que puede mandar 2048 bytes, supongo que en un vblank ??? Pero por lo que se ha comentado, el máximo que se puede mandar en un vblank en una máquina a 60Hz son 800 bytes y 1536 en una de 50Hz. Aquí me quedo un poco "pillao". ¿Qué no cuadra? ??? Tenía entendido que ese era el tope en un MSX1 (y no veas si me jode, necesito velocidad por un tubo) :) Si te sales del vblank, debería aparecer basura en pantalla, ¿cierto? ¡¡Gracias de nuevo!! Título: Re: La rutina de transferencia "refinitiva" Publicado por: SapphiRe_MSX en 31 de Mayo de 2007, 10:59:10 am Cosillas: comentas que puede mandar 2048 bytes, supongo que en un vblank ??? No, no. Digo que tal y como está, es capaz de transferir hasta un máximo de 2048 bytes, que son, precisamente, 256 bloques de 16 bytes. Si en lugar de 16 outis metes 32, el máximo que es capaz de transferir sería 4096. Esta rutina es muy rápida, pero SÓLO funciona en el vblank y si la pantalla está apagada. Calculo que (en NTSC) se podrían transferir aproximadamente unos 512bytes sin problemas en el vblank con esta rutina, así que si consigues dividir las transferencias en paquetes de medio kb, te serviría perfectamente. De todas formas, creo que alguien por aquí ya ha estado jugueteando con esta rutina para transferir más de lo que yo vuelco en el QBIQS... a ver, a ver... ¿alguien tiene más datos que aportar? ;D ;D ;D Y si la usas, no olvides inicializar b correctamente ;D que ese es el truco :D Título: Re: La rutina de transferencia "refinitiva" Publicado por: jltursan en 31 de Mayo de 2007, 02:31:13 pm Citar Si te sales del vblank, debería aparecer basura en pantalla, ¿cierto? Um, no. Esas cantidades como bien dices, son los límites teóricos de la cantidad de bytes que se pueden volcar a la VRAM usando OUTIs durante el vblank. El vblank es el periodo comprendido entre el fin de la ultima linea de la pantalla activa (empieza por tanto en la 192) y acaba al comienzo de la misma (virtualmente sería la coordenada -1), el resto de la pantalla es visible y no es vblank, para volcar datos durante este intervalo el único requisito es que esperemos un poco entre OUTI y OUTI; pero no tiene porque salir basura si así lo hacemos. Durante el frame o cuadro se pueden enviar muchos más bytes que los que se pueden enviar durante el vblank :); claro, que tampoco es cuestión de pulirse todo el tiempo de la CPU enviando datos a pantalla :P ¡A este paso dentro de poco te vas a convertir en Darth VDP! :-D Título: Re: La rutina de transferencia "refinitiva" Publicado por: SapphiRe_MSX en 31 de Mayo de 2007, 02:36:06 pm por cierto, JL, ¿recuerdas cuando empezamos a intercambiarnos mensajes sobre esta rutina? Fíjate en lo que ha quedado al final ;D ;D
Título: Re: La rutina de transferencia "refinitiva" Publicado por: Darth_Fistro en 31 de Mayo de 2007, 04:08:12 pm ¡Gracias a ambos, monstruos! :D
Ya me ha quedado claro lo del vblank y fuera de él (algo aprendo, lento pero algo) :) pero lo comentaba porque en la rutina de Sapphire los outis van a saco sin nops por medio (a no ser que me pierda algo, eso me parece). Por eso decía que fuera del vblank se podría marear la cosa. Ya tengo claro que vblank=outis a saco, y luego si la transferencia es mayor=creo rutina que vuelca el resto con dos nops 8) Para las cosillas spectrumeras, como quiero música de por medio (cómo mola, jeje) quiero saber más o menos cuántos ciclos consume el Caruso a tres canales, y así poder hacer por ejemplo una rutina tal que así: halt (salta el Caruso -> tralarí, tralará, reggaeton del güeno y tal...) Volcado de x bytes a saco (outis, outis and more outis) Resto del volcado-> usando nops Ahora necesito saber si después del vblank, con el Caruso en marcha, puedo volcar 600 bytes, 500 o lo que sea. Aunque a lo mejor lo suyo es no arriesgar y hacer la rutina con nops y me aseguro que tarde lo que tarde el reproductor no se descuadra el volcado a VRAM. De nuevo, gracias, estoy dando el salto al lado oscuro poco a poco (a empujones, eso sí) ;D Título: Re: La rutina de transferencia "refinitiva" Publicado por: SapphiRe_MSX en 31 de Mayo de 2007, 04:21:23 pm Resto del volcado-> usando nops Claro que, en lugar de nops, puedes aprovechar esos ciclos para ir haciendo otros cálculos... ;D ;D Título: Re: La rutina de transferencia "refinitiva" Publicado por: Darth_Fistro en 31 de Mayo de 2007, 04:50:40 pm Pero... ¿qué cálculos puedo hacer en 8 ciclos? ¿Y cómo controlo el flujo de las instrucciones, qué ejecutar y el orden? :o
Eso suena muy maquiavélico... ¡cuenta, cuenta! :D Título: Re: La rutina de transferencia "refinitiva" Publicado por: SapphiRe_MSX en 31 de Mayo de 2007, 05:02:33 pm Pero... ¿qué cálculos puedo hacer en 8 ciclos? ¿Y cómo controlo el flujo de las instrucciones, qué ejecutar y el orden? :o A ver, los 8 ciclos son un tiempo MÍNIMO de espera. Si tienes que hacer un cálculo donde no intervengan de, hl ó bc, puedes aprovechar esos tiempos entre outi y outi para hacer todo lo que necesites con el acumulador y de. Si necesitases más registros siempre puedes hacer un exx, utilizarlos lo que necesites y repetir el exx para recuperar los valores adecuados de hl y bc. Aunque siendo estrictos, incluso podrías utilizar b, sabiendo que se decrementa tras cada outi. Claro que todo esto sólo lo puedes hacer desenrollando "a lo bestia" (del todo) el bucle de transferencia, e intercalando el cálculo entre los outis... :D Título: Re: La rutina de transferencia "refinitiva" Publicado por: pitpan en 31 de Mayo de 2007, 05:21:32 pm Juanma:
En cuanto al uso del Caruso te recomiendo que la secuencia se la siguiente: HALT CALL DUMP_PSG ; Creo que se llama así ; Aquí, todos los temas de volcado a VRAM ... ; Y aquí, el resto de procesos del Caruso ... De este modo, conseguirás que el sonido sea síncrono pero no pierdas tiempo de v-blank con procesos no relacionados con VRAM. Título: Re: La rutina de transferencia "refinitiva" Publicado por: jltursan en 31 de Mayo de 2007, 07:44:23 pm Citar por cierto, JL, ¿recuerdas cuando empezamos a intercambiarnos mensajes sobre esta rutina? Fíjate en lo que ha quedado al final Grin Sí....y hay que ver como se ha desarrollado la condenada ;) Citar De este modo, conseguirás que el sonido sea síncrono pero no pierdas tiempo de v-blank con procesos no relacionados con VRAM. Eso que te comenta Robsy es muy importante, todo lo dicho se queda en nada si lo primero que haces tras la sincronización no es la transferencia a video. Citar Eso suena muy maquiavélico... ¡cuenta, cuenta! Como ya te explica Sapp, se pueden hacer muchas cosas; pero en este caso, depende 100% de como tengas montada la rutina y de lo que estes haciendo, es posible que puedas aprovechar el tiempo o que no puedas ::). Tu siempre ten presente que los NOPs es una perdida de tiempo :) |