Título: TUTORIAL: Desarrollo compatible para todas las familias de MSX (II) Publicado por: jltursan en 20 de Febrero de 2007, 09:05:39 pm PARTE II
50/60 Hz Se pueden encontrar MSX en los cinco continentes, eso implica que el código que uno haga, debe (o debería) de tener en cuenta los diferentes velocidades de sincronismo bajo las cuales van a funcionar las máquinas. Las combinaciones que podemos encontrar, incluyendo el estandar de TV son las siguientes:
Como internacional se toma la combinación PAL/50Hz. Si nos encontramos en un equipo MSX2 o superior podremos averiguar la velocidad del refresco leyendo el bit 1 del registro 9 del VDP, si contiene 1 estamos en una máquina a 50Hz, si vale 0 estamos en una funcionando a 60Hz (obtenemos el mismo resultado consultando el bit 7 de la posición $2B). De igual forma podremos cambiar la velocidad de refresco modificando el contenido de dicho bit en el registro. La opción a seguir, que el programa se adapte al refresco o bien que nosotros modifiquemos el refresco para adaptarlo al funcionamiento de nuestro programa......; bien, eso ya es algo a decidir por parte del programador y un tema bastante complejo, más adelante comentaré las implicaciones que puede tener el refresco al trabajar sobre la pantalla. La BIOS Poco y mucho que decir ;), la existencia de la BIOS viene determinada por la necesidad de que exista un interfaz que abstraiga las diferencias en el hardware de manera que un desarrollador no necesite adaptar su código a dichas diferencias. Si queremos estar seguros que nuestro código vaya a funcionar en el 100% de las máquinas deberá utilizar siempre la BIOS, de no hacerlo así tendremos que estar muy seguros de lo que hacemos y testearlo en el mayor número de máquinas posible....y aún así nunca podremos estar seguros :P. El R800 y el T9769C El R800 fue el microprocesador diseñado por ASCII para que los equipos TurboR de Panasonic pudieran mantener la compatibilidad con el Z80 oficial impuesto por la norma y disfrutar a la vez de todas las prestaciones de un micro de "casi" 16 bits. La velocidad de la CPU es de 7,16Mhz; pero debido a que la mayoría de las instrucciones se ejecutan en sólo 1 ciclo, a diferencia de los 4 ciclos de media en un Z80 estandar, el resultado práctico es el de tener un Z80 a 28,64Mhz. Esto da como resultado que si ejecutamos un programa concebido para que se ejecute a los "humildes" 3,57Mhz del Z80 que incorporan el resto de los modelos de MSX, vamos a encontrarnos con que todo va a ir 4 veces más rápido si no hemos tenido la precaución de sincronizarlo con el barrido de pantalla. Del programador depende decidir si aprovecha esto a su favor o no. Si el programa definitivamente debe de ejecutarse a su velocidad original, no quedará otro remedio que desactivar el R800 mediante una simple llamada a la BIOS de los TurboR: Código: CHGCPU: equ $0180 ld a,$80 call CHGCPU Un posible ejemplo de rutina de detección en la que también se maneja información acerca de la familia y de la frecuencia de refresco, podría quedar como sigue (gracias a Dioniso por el código): Código: CHGCPU: equ $0180 GETCPU: equ $0183 VDP_09: equ $FFE8 ;MSX Version ld a,($2d) or a jr z,MSX1 cp 3 ;turbo-r jr nz,guess_hz call GETCPU or a jr z,guess_hz ld a,$80 call CHGCPU ;modo z80 en turbo-r ;detectamos herzios guess_hz: ld a,1 ld (MODELO),a ;MSX 2 o superior - podemos modificar los herzios. ld a,(VDP_09) ; leemos el contenido del registro 9 del VDP bit 1,a jr z,its_60hz xor a ld (HERZIOS),a ; 0=50hz (variable para herzios) jr end_check_version its_60hz: ld a,1 ld (HERZIOS),a ;1=60hz jr end_check_version MSX1: xor a ld (MODELO),a end_check_version: Con ello obtendriamos en la variable MODELO, el modelo de MSX (0=MSX1, 1=MSX2 ó superior) y en la variable HERZIOS, la velocidad de refresco (0=50Hz, 1=60Hz) Si no queremos modificar permanentemente la velocidad podemos optar por mantenerla parcialmente; como ejemplo podríamos tomar una rutina de cálculo compleja, en este caso activariamos el R800 al no acceder a la pantalla y así aprovechar esa velocidad extra de proceso. Otro de los problemas relacionados con el uso del R800 es que no es del todo 100% compatible con las características no oficiales del Z80, más concretamente los bits 3 y 5 del registro F (que normalmente contienen la copia o variantes de los respectivos bits 3 y 5 del registro A) no tienen el mismo comportamiento. Igualmente mnemónicos no oficiales como SLL han desaparecido, éste concretamente han sido reemplazado por TST, que tampoco es oficial. :P La conclusión es que lo mejor que se puede hacer es limitarse a usar en lo posible las operaciones documentadas por Zilog y dejar de lado las no oficiales (aunque en algunos casos sea tentador usar un LD IXL,a ;)). Similar al problema de velocidad que plantea el R800, es el caso del T9769C de Toshiba que incorporan los modelos de Panasonic MSX2+, este micro puede funcionar a 2 velocidades, 3.57MHz y 5.37MHz. Es evidente que aunque nos parezca atractiva la posibilidad de activar el modo "turbo", no debemos desarrollar nuestro software específicamente para que corra a esta velocidad; sería muy lento para el resto de los mortales... :P Tengo sobrefrecuenciado mi Pentium 4, ¿como puedo hacer lo mismo con mi MSX?? Como ya he dicho, esto se podrá hacer únicamente sobre los modelos de Panasonic FS-A1FX/WX/WSX. A través del acceso a un par de puertos exclusivos de estos modelos, podremos conmutar la velocidad de la CPU: ($40) = 8 (ID Panasonic) ($41) bit 0: Velocidad CPU (0=5.37MHz, 1=3.57MHz) Ejemplo (obtenido del MAP) de como se activaría el modo turbo: Código: ld a,8 out ($40),a ; se envía el código de fabricante 8 (Panasonic) al puerto $40 in a,($40) ; se lee el valor que acabamos de enviar cpl ; complemento de todos los bits de ese valor cp 8 ; si no volvemos a obtener el valor originalmente enviado (8), jr nz,Not_WX ; no se trata de un WX/WSX/FX, no hay modo turbo :(. xor a ; si lo es, escribimos 0 en el puerto $41 out ($41),a ; y activamos el modo de alta velocidad Puertos de acceso al VDP Aunque en el 99,99% de los casos se trata de los puertos $98 y $99, la norma determina que pueden ser otros y su valor estará definido en VDP.DR ($0006) y VDP.DW ($0007). Según el TH esta sería la tabla de equivalencias de los puertos de video en el MSX : Para todos los MSX: Código: ---------------------------------------------------------------------- | Port | Address | Function | ---------------------------------------------------------------------- | port #0 (READ) | n | read data from VRAM | | port #0 (WRITE) | n' | write data to VRAM | | port #1 (READ) | n + 1 | read status register | | port #1 (WRITE) | n'+ 1 | write to control register | ---------------------------------------------------------------------- Exclusivamente para modelos superiores a un MSX1: Código: ---------------------------------------------------------------------- | Port | Address | Function | ---------------------------------------------------------------------- | port #2 (WRITE) | n'+ 2 | write to palette register | | port #3 (WRITE) | n'+ 3 | write to indirectly specified register | ---------------------------------------------------------------------- Siendo n el valor obtenido a partir de VDP.DR y n' el obtenido de VDP.DW. Si se quiere hacer un juego 100% compatible, nuestro código deberá leer el contenido de estas variables y utilizar los valores obtenidos como puertos de acceso al VDP en el resto de programa. El ejemplo aplicado a un SETWRT: Código: ld a,[7] ld c,a inc c ld a,l di out (c),a ld a,h and $3F or $40 ei out (c),a Si no recurrimos a código automodificable, esto supone una pérdida importante de velocidad y por eso es habitual el uso directo de los puertos estandar. Acceso contenido a la VRAM (o como acceder a la VRAM sin usar la BIOS y no morir en el intento) Uno de los casos típicos en los que puede que decidamos obviar el uso de la BIOS (es un poco lenta) es el del acceso a la VRAM. En la mayoria de los modelos de MSX1, dicho acceso a la VRAM debe de hacerse con contención ya que la velocidad del Z80 a la hora de enviar los datos es superior a la del TMS9918/28/29 para procesarlos. En los TMS9938/58 que incorporan las siguientes generaciones de MSX, no es necesario esperar entre acceso y acceso; pero si queremos que nuestro programa funcione, además de hacerlo en modelos MSX2 o superiores, en cualquier MSX1, tendremos que respetar este método. Este retardo entre acceso y acceso (OUT) al VDP, en términos de ciclos del Z80, deberá ser equivalente a 28 ciclos. ¿Y por qué hay que esperar 28 ciclos? Según el datasheet oficial del TMS9918 (http://www.robsy.net/tms9918.pdf), el retardo base en los accesos al VDP es de 2 microsegundos, por lo que en principio habrá que calcular cuantos ciclos de espera supondrán para un Z80 a 3,579545Mhz. Si tenemos que: 1T/3579545 = 0,000000279 segundos/ciclo Entonces ya podemos saber a cuantos ciclos equivalen esos 2 microsegundos: 0,000002/0,000000279 = 7,16845 ciclos Así pues, suponen 7,16 ciclos del Z80 de los MSX; hay que esperar entre 7-8 ciclos de media. Además de esto en función del modo de pantalla habrá que esperar (o no) un poco más antes de poder acceder al dato en la VRAM (tanto da leer como escribir). Para screen 2 tenemos un retardo extra que va de 0 a 6 microsegundos, por lo que si convertimos a ciclos, en el peor de los casos tendremos que esperar hasta 28T-29T. La excepción la tenemos únicamente durante una ventana que dura aproximadamente 4300 microsegundos (a 60Hz) tras el retrazado vertical, en ese intervalo el retardo extra es cero. Durante ese intervalo basta con esperar esos 7-8 ciclos iniciales. Asumiendo esta limitación y si tomamos como ejemplo el OUT más práctico (que no el más rápido), el envio de datos sucesivos seguiría este patrón: Código: outi nop nop outi nop nop ... ... ... Como el tiempo empleado en el OUTI hay que tenerlo en cuenta (18 ciclos), nos bastaría con añadir otros 10 ciclos más (2 NOP de 5 ciclos cada uno) para garantizar el correcto funcionamiento en cualquier modelo de MSX1 y en cualquier momento del refresco de pantalla. Se puede ajustar algo más y reducir los NOPs a sólo uno; pero bueno, si queremos estar 100% seguros tendremos que hacerlo así. Antes he comentado que existe una ventana de 4300 microsegundos tras el retrazado vertical cuando el refresco es de 60Hz, en ese intervalo los datos pueden ser enviados sin retardos extras y a la máxima velocidad posible. ¿Cuantos bytes podriamos transferir teóricamente durante ese intervalo?. Para calcularlo primero averiguamos cuantos ciclos tenemos disponibles durante ese lapso de tiempo : (4,3ms * 3579545) / 1000ms = 15392 ciclos Sabemos que un OUTI consume 18 ciclos: 15392 / 18 (ciclos OUTI) = 855 instrucciones OUTI Así pues, podriamos encadenar 855 OUTIs uno detrás de otro sin esperas, lo que da poco más de 1/6 de pantalla o 3 lineas y pico de caracteres. A partir de ahí ya no tenemos ninguna garantía de que no se produzca corrupción del flujo de datos, tendremos que empezar a intercalar retardos. Este resultado es aproximado, en el siguiente apartado se tratará de calcular con más precisión este valor. Por cierto, en este intervalo podriamos usar un OTIR (marginado él); pero claro ya nos podriamos ir olvidando de esos 855 bytes; eso sí, a costa de la pérdida de velocidad ganaríamos bastante memoria. Diferencias en el acceso al VDP en equipos con refresco 50Hz/60Hz Siguiendo con el punto anterior, si estamos aprovechando el retrazado vertical para volcar datos a granel tendremos que tener en cuenta de la frecuencia a la que estemos trabajando, 50Hz ó 60Hz. Estas diferencias están determinadas por el tratamiento específico que tiene el VDP de la pantalla según corresponda: Para los equipos funcionando a 60Hz (realmente son 59,94Hz), tenemos que la pantalla está diferenciada en estas zonas: Linea Descripcion 192 Pantalla activa 24,5 Borde inferior 3 Intervalo inferior 3 Intervalo vertical 13 Intervalo superior 27 Borde superior ------ 262,5 lineas en total Si dejamos de lado las 192 que conforman la pantalla activa, nos quedan 70,5 lineas durante las cuales podremos trabajar sin retardos. Ahora calculamos cuantos ciclos de CPU corresponden a cada linea: 1 pantalla / 60 = 0,016683350016683350016683350016683 seg. 1 linea = 0,016683350016683350016683350016683 seg / 262,5 = 0,0000635556 seg. La duración total del retrazado será: 0,0000635556 * 70.5 = 0,0044806711473378140044806711473378 seg. de VBlank (4480 microsegundos, más o menos los 4300 del datasheet :P) Como previamente ya habiamos calculado a cuantos segundos equivalía 1T, tenemos que: 0,0000635556 / 0,000000279 = 227,79T por linea y finalmente: 227,79T * 70.5 = 16059,748T por VBlank Resumiendo: 1 linea = 0,0000635556 seg. = 227,8T 1 VBlank = 70,5 lineas = 16059,75T Ya podemos calcular el número de OUTIs: 16059,75T / 18 = 892 OUTIs Esta es la peor de las situaciones ya que es la que deja menos tiempo libre al Z80. Lo recomendable pues, es desarrollar teniendo en mente la compatibilidad y restricciones de velocidad de un refresco a 60Hz y así hacer más fácil la posterior adaptación a PAL. Con un refresco de 50Hz, las zonas serán las mismas; pero no así su extensión: Linea Descripcion 192 Pantalla activa 48,5 Borde inferior 3 Intervalo inferior 3 Intervalo vertical 13 Intervalo superior 54 Borde superior ------ 313,5 lineas En el caso del PAL, nos quedan 121,5 lineas durante las cuales podremos trabajar sin retardos. Ahora calculamos cuantos ciclos de CPU corresponden a cada linea: 1 pantalla = 0,02 seg. 1 linea = 0,02 seg / 313,5 = 0,0000637958 seg. La duración total del retrazado será: 0,0000637958 * 121,5 = 0,0077511961 seg. de VBlank (7751 microsegundos) Ahora calculamos los T por linea: 0,0000637958 / 0,000000279 = 228,65T por linea Deberían salir idénticas cantidades si los valores de 1/50 y 1/59,94 fueran exactos :(); pero el baile de decimales continua... y finalmente: 228,65T * 121,5 = 27782,06T por VBlank Resumiendo nuevamente: 1 linea = 0,0000637958 seg. = 228,65T 1 VBlank = 121,5 lineas = 27782,06T Ya podemos calcular el número de OUTIs: 27782,06 / 18 = 1543 OUTIs Está claro que la frecuencia de 50Hz nos da mucho más margen; pero es mejor no acostumbrase...:P ** Gracias a ARTRAG por corregir los valores del número de lineas por fotograma y por los valores más aproximados de refresco :) ** Bibliografía en la web: ASCII MSX Technical Handbook (http://www.konamiman.com/msx/msx-s.html#msx2th) TMS9918A/TMS9928A/TMS9929A Datasheet (http://www.robsy.net/tms9918.pdf) Sega Master System VDP documentation (http://cgfm2.emuviews.com/txt/msvdp.txt) Z80 Flag Affection (http://www.z80.info/z80sflag.htm) MSX I/O ports overview (http://map.tni.nl/resources/msx_io_ports.php) MSX1 Memory Map (http://fms.komkon.org/MSX/Docs/MSXMemory-1.txt) MSX2 Memory Map (http://fms.komkon.org/MSX/Docs/MSXMemory-2.txt) Título: Re: TUTORIAL: Desarrollo compatible para todas las familias de MSX Publicado por: jltursan en 20 de Febrero de 2007, 09:12:14 pm Aquí está la nueva extensión del tutorial, me he visto obligado a partirlo porque ya no cabía. Como siempre, si alguien caza algún fallo o ve algo con lo que no esté de acuerdo que lo diga por estos lares. ;)
Título: Re: TUTORIAL: Desarrollo compatible para todas las familias de MSX Publicado por: MsxKun en 20 de Febrero de 2007, 09:35:12 pm Buen currazo, si señor :D
Título: Re: TUTORIAL: Desarrollo compatible para todas las familias de MSX Publicado por: SapphiRe en 21 de Febrero de 2007, 02:56:10 pm Yo aún diría más: Un currazo impresionante!! ¿Dónde está el smiley de quitarse el sombrero? :D
Título: Re: TUTORIAL: Desarrollo compatible para todas las familias de MSX Publicado por: Dioniso en 21 de Febrero de 2007, 03:35:10 pm Sólo dos cosas:
1-Se podría quitar el XOR A en: MSX1: xor a ld (MODELO),a 2-Faltaría leer el bit 7 de $2b para ver si se trata de un MSX1 a 60hz (japonés, ...) o a 50hz (europeo, ...) Título: Re: TUTORIAL: Desarrollo compatible para todas las familias de MSX Publicado por: e_sedes en 21 de Febrero de 2007, 04:15:14 pm Estupendo curro, si señor. :D
Título: Re: TUTORIAL: Desarrollo compatible para todas las familias de MSX Publicado por: Dioniso en 21 de Febrero de 2007, 04:40:24 pm De acuerdo con todos. Vaya curro y gracias por el curro.
Título: Re: TUTORIAL: Desarrollo compatible para todas las familias de MSX (II) Publicado por: SapphiRe en 21 de Febrero de 2007, 04:52:02 pm JL, te vamos a dar el premio Curro Jiménez ;D ;D ;D ;D ;D
vale, ya me callo... ;D ;D ;D ;D ;D ;D Título: Re: TUTORIAL: Desarrollo compatible para todas las familias de MSX (II) Publicado por: theNestruo en 20 de Diciembre de 2012, 10:54:20 pm Perdón por reflotar este hilo tan antiguo. En realidad quería reflotar otro más antiguo aún (la primera parte de éste), pero estaba cerrado. En aquel hilo se hablaba de la RAM: que sólo podemos asumir que existe RAM en la tercera página, y que el mínimo de RAM "en la práctica" es 16KB, ya que sólo se conoce un modelo con 8KB.
Mi pregunta es relativa a la detección de RAM disponible. Hasta ahora partía de $E000 para mis variables, pero como necesito los 16KB, ¿es posible determinar de alguna forma si sólo hay 8KB en vez de 16KB? Mi idea es tener controlado el fallo (mostrar un mensaje tipo "Hola, usuario de Casio PV7 :-P") en vez de dejar que el código avance y falle de cualquier manera. Y aprocevhando que estoy aquí, ya os pregunto también por las variables de sistema. En los listados de variables de sistema que he visto, dichas variables empiezan en $F380 y están bastante juntitas hasta llegar a CURLIN $F41C. De ahí saltan hasta $F91F ("Character set SlotID", seguida de "Character set address"). Entre medias hay algo más de 1KB que ¿se podría aprovechar? (tranquilos, prometo no hacerlo :-P). Y en el caso de que sí se pudiera utilizar, ¿se podría pisar también todo lo que hay después de BDRCLR $F3EB (hasta CURLIN)? Tiene pinta de ser utilizado sólo por el BASIC... Un saludo! Título: Re: TUTORIAL: Desarrollo compatible para todas las familias de MSX (II) Publicado por: Mortimer en 21 de Diciembre de 2012, 08:18:38 am Pues para determinar si hay 16KB disponibles, tan fácil como comprobar si la posición C000h es capaz de conservar lo que escribamos. Cómo no sabemos lo que hay a priori, lo suyo es leer la posición, modificar el valor (Complementando por ejempo), escribir, y leer de nuevo para comprobar si se ha guardado el cambio. También he mirado la lista de variables de sistema y la FC48: Bottom of equiped RAM te serviría. He hecho pruebas en el Blue con 32, 16 y 8KB y funciona correctamente, sólo tendrías que mirar si es igual o menor C000 para saber si tienes al menos 16KB.
En cuanto al hueco que comentas en el área de sistema entre F41Ch y F91Fh, sí qué parece que que es un área que sólo utiliza BASIC, así que en teoría, sí podrías utilizarlo, aunque claro, habría que hacer muchas pruebas para asegurarse de que luego es 100% compatible, quizás se podría mirar si las funciones BIOS que usan hacen referencia a esta área para asegurarse. Saludos! Título: Re: TUTORIAL: Desarrollo compatible para todas las familias de MSX (II) Publicado por: theNestruo en 21 de Diciembre de 2012, 06:08:14 pm Pues para determinar si hay 16KB disponibles, tan fácil como comprobar si la posición C000h es capaz de conservar lo que escribamos. Mi duda venía justo de ahí, que no sabía si se podía determinar directamente escribiendo/leyendo o habría algún tipo de "multiplexado" (i.e.: que escribas en la C000 y luego eso se pueda leer en la C000 y en la E000) con lo que el método de detección manual no funcionaría.Muchas gracias! Título: Re: TUTORIAL: Desarrollo compatible para todas las familias de MSX (II) Publicado por: nanochess en 01 de Enero de 2013, 05:55:20 pm La verdad es que detectar entre 8 y 16K o más sería bastante simple:
LD HL,$C000 LD (HL),$55 LD HL,$E000 LD (HL),$AA LD HL,$C000 LD A,(HL) CP $55 JP Z,.1 ; Salta si tiene 16K o más ... ; Sigue aquí si tiene 8K. y esta rutinita maneja todos los casos (mapeado duplicado y tambíen inexistente) :) Lo más simpático es que thenestruo tiene razón, sólo hay un modelo de MSX con 8K de RAM y quien sabe si alguien aún lo use. Título: Re: TUTORIAL: Desarrollo compatible para todas las familias de MSX (II) Publicado por: MsxKun en 01 de Enero de 2013, 11:39:53 pm Lo más simpático es que thenestruo tiene razón, sólo hay un modelo de MSX con 8K de RAM y quien sabe si alguien aún lo use. Me se de uno o dos. Título: Re: TUTORIAL: Desarrollo compatible para todas las familias de MSX (II) Publicado por: nitrofurano en 15 de Junio de 2013, 03:18:41 pm
Brasil: PAL-M (60Hz) - http://en.wikipedia.org/wiki/PAL#PAL-M_.28Brazil.29 |