Marcos Cruz
Madrid, enero 1989
Voy a explicar cómo transmitir un bloque de bytes desde un QL a un Spectrum provisto de la Interface 1, mediante una conexión RS232.
Una vez visto, en un anterior artículo, cómo realizar la conexión RS232 entre ambos ordenadores, no debería necesitarse otra cosa que realizar la transmisión mediante dos sencillos programas en BASIC. Un bucle de envío en el QL leería los bytes de la memoria, o de un fichero, y los enviaría al Spectrum con PRINT#, mientras el Spectrum los recibiría con INPUT$ (INKEY$ no vale, pues no espera a recibir un carácter, en la Interface 1) y los iría "pokeando" en alguna parte. Pero, si lo hacemos así, nos encontraremos con que la fiabilidad de la transmisión deja bastante que desear; es probable que varios bytes queden alterados en el "trayecto".
A causa de ello, he diseñado un rudimentario protocolo de verificación de bytes transmitidos, con el objeto de minimizar los errores.
El protocolo es el siguiente:
El emisor, el QL, envía cada byte dos veces seguidas. El receptor, el Spectrum, tras recibir ambos bytes, comprueba si son o no iguales; si es así, envía un byte acordado para indicar que la recepción fue correcta y espera un nuevo par de bytes; si los dos bytes recibidos no son iguales, envía otro byte diferente acordado para indicar que se repita el envío. De este modo, la probabilidad de que el receptor acepte por bueno un byte erróneo es mínima (¡para ello, los dos bytes deberían haberse corrompido y quedar ambos iguales!).
Veamos, en primer lugar, el listado SuperBASIC del programa emisor para el QL, que conviene compilar, por cuestiones de velocidad:
En segundo lugar, veamos el listado del programa Basic receptor para el Spectrum:100 : 110 REMark TRANSMITUM Z80 Versión 0.21 120 REMark © Marcos 1988 130 : 140 REMark TURBO Toolkit Versión 1.32 150 : 160 REPeat bucle_principal 170 : 180 CLEAR 190 : 200 REMark Constantes y variables inicializadas -------------------------- 210 : 220 lon_nombre%=32 230 ventana%=4 240 fichero_objeto%=5 250 fichero_spectrum%=6 260 : 270 REMark Cadenas ------------------------------------------------------- 280 : 290 DIM fichero_objeto$(lon_nombre%):fichero_objeto$=OPTION_CMD$ 300 DIM fichero_spectrum$(lon_nombre%):fichero_spectrum$="ser2" 310 DIM byte_objeto$(1) 320 DIM letra$(1) 330 : 340 REMark Pantalla ------------------------------------------------------ 350 : 360 OPEN#ventana%,con_512x14a0x225 370 PAPER#ventana%,4 380 INK#ventana%,0 390 CLS#ventana% 400 BORDER#ventana%,2,7,0 410 : 420 REMark Presentación -------------------------------------------------- 430 : 440 AT#ventana%,0,25 450 PRINT#ventana%,"TRANSMITUM Z80 V 0.21 Marcos 1988" 460 AT#ventana%,0,82 470 letra$=EDIT$(#ventana%,"",0) 480 : 490 REMark Pedir ficheros ------------------------------------------------ 500 : 510 REMark pedir nombre de fichero objeto a transmitir: 520 REPeat pide_fichero 530 CLS#ventana% 540 PRINT#ventana%," Fichero objeto a transmitir: "; 550 fichero_objeto$=EDIT$(#ventana%,fichero_objeto$,lon_nombre%) 560 IF fichero_objeto$="":NEXT bucle_principal 570 REMark detectar errores: 580 flag_fichero=DEVICE_STATUS(fichero_objeto$) 590 IF flag_fichero>-1:NEXT pide_fichero 600 SELect ON flag_fichero=-3,-6,-7,-9,-12,-16:NEXT pide_fichero 610 EXIT pide_fichero 620 END REPeat pide_fichero 630 REMark abrir fichero fuente: 640 OPEN_IN#fichero_objeto%,fichero_objeto$ 650 : 660 CLS#ventana% 670 REMark detectar errores: 680 flag_fichero=DEVICE_STATUS(fichero_spectrum$) 690 SELect ON flag_fichero=0,-3,-6,-7,-9,-11,-12,-16,-20:NEXT bucle_principal 700 REMark abrir fichero de comunicación con Spectrum: 710 OPEN#fichero_spectrum%,fichero_spectrum$ 720 : 730 REMark Transmitir --------------------------------------------------- 740 : 750 TRA 0 760 CLS#ventana% 770 PRINT#ventana%," Transmitiendo"!fichero_objeto$!"al Spectrum" 780 : 790 REPeat transmite_bytes 800 IF EOF(#fichero_objeto%):EXIT transmite_bytes 810 byte_objeto$=INPUT$(#fichero_objeto%,1) 820 REPeat repite_byte_objeto 830 PRINT#fichero_spectrum%,byte_objeto$;byte_objeto$; 840 IF INPUT$(#fichero_spectrum%,1)=CHR$(0):EXIT repite_byte_objeto 850 END REPeat repite_byte_objeto 860 END REPeat transmite_bytes 870 : 880 REMark enviar marca de final: 890 PRINT#fichero_spectrum%,CHR$(0);CHR$(255); 900 : 910 CLOSE#fichero_objeto% 920 CLOSE#fichero_spectrum% 930 TRA 1 940 : 950 END REPeat bucle_principal 960 :
10 REM MARCOS 11/8/88 20 REM Recibir codigo objeto 30 REM con la interface 1 40 REM y la interface Beta 125: 130 FORMAT "n";1: REM dummy 163 RANDOMIZE USR 15363: REM : LOAD "recibe1c"CODE 23296 164: 170 INPUT "Direccion=";org 175 RANDOMIZE org 180 IF org>23999 THEN CLEAR org-1 190 LET org=PEEK 23670+256*PEEK 23671 195 LET f$="f" 200 REM INPUT "Fichero=";f$ 257 FORMAT "b";9600 300 LET final=USR 23296 503 LET fallos=PEEK 23298+256*PEEK 23299 505 PRINT final-org;" bytes recibidos" 507 PRINT fallos;" fallos de recepcion" 509 RANDOMIZE USR 15363: REM : ERASE f$CODE 510 RANDOMIZE USR 15363: REM : SAVE f$CODE org,final-org,org 520 REM ERASE "m";1;f$ 530 REM SAVE *"m";1;f$CODE org,final-org
Las órdenes de grabación y carga están escritas para la interface de disco Beta, pero pueden modificarse para la Interface 1, o el Disciple, o cualquier otra.
Por último, veamos el listado en ensamblador Z80 de la rutina receptora para el Spectrum:
Las siguientes rutinas en ensamblador Z80, que listo por curiosidad, las escribí para recibir código, como en el caso anterior, empleando igualmente la Interface 1 o bien la interface de Indescomp, pero sin protocolo de verificación de errores. Los bytes se reciben "tal cual" o bien en formato hexadecimal, según se explica en cada caso:; ;Recepción de código objeto del Z80 desde el QL ;por medio de la conexión RS232C de la Interface 1 ; ;Marcos 5/8/1988 ; ;El protocolo es el siguiente: ; El QL envía cada byte dos veces seguidas al Spectrum ; Si son iguales, el Spectrum le envía al QL un byte 0 ; Si son diferentes, envía al QL un byte 255 ; Si el QL recibe un byte 0, no repite el envío ; Para indicar fin de transmisión, el QL envía un 0 y un 255 ; ;La dirección+1 última en que se ha guardado un byte, queda en (SEED) ;El número de fallos queda en (23298) ; SEED EQU 23670 SER_FL EQU #5CC7 ; ORG 23296 ;buffer de la impresora ; JR INICIO FALLOS DEFW 0 ;número de fallos en la recepción, para que el Basic los lea ; ;Inicialización: ; INICIO RST 8 ;llamar a la Interface 1... DEFB #31 ;...para insertar sus variables, por si acaso XOR A ;resetear... LD (SER_FL),A ;...la variable SER_FL para eliminar restos del RS232 LD HL,0 ;resetear... LD (FALLOS),HL ;...el número de fallos LD HL,(SEED) ;tomar destino del código, fijado con RANDOMIZE desde Basic ; ;Leer los dos bytes del código objeto: ; BUCLE PUSH HL ;preservar dirección del código objeto LEE1 RST 8 ;llamar a la Interface 1... DEFB #1D ;...para recibir el primer byte JR NC,LEE1 ;si no hay byte disponible, volver a intentarlo PUSH AF ;guardar primer byte recibido LEE2 RST 8 ;llamar a la Interface 1... DEFB #1D ;...para recibir el segundo byte JR NC,LEE2 ;si no hay byte disponible, volver a intentarlo POP BC ;pasar a B el primer byte recibido POP HL ;recuperar dirección del código objeto ; CP B ;¿son iguales los dos bytes recibidos? JR Z,IGUALE ;si es así, saltar ; ;Bytes diferentes, comprobar si son indicadores de fin de transmisión: ; INC A ;¿el es segundo byte 255? JR NZ,DIFERE ;si no, saltar XOR A ;¿es cero... CP B ;...el primer byte recibido? JR NZ,DIFERE ;si no, saltar PUSH HL ;pasar la dirección actual... POP BC ;...a BC para... RET ;...pasarla al BASIC ; ;Bytes diferentes, por error en la transmisión: ; DIFERE LD DE,(FALLOS) ;tomar el número de fallos que se han producido INC DE ;incrementarlo LD (FALLOS),DE ;volverlo a guardar LD A,255 ;indicador de error JR RESPUE ;saltar a enviar el indicador al QL ; ;Bytes iguales: ; IGUALE LD (HL),A ;guardar byte en su dirección INC HL ;apuntar a la siguiente dirección XOR A ;indicador de que no hay error ; ;Enviar respuesta al QL: ; RESPUE PUSH HL ;preservar dirección del código objeto RST 8 ;llamar a la Interface 1... DEFB 30 ;...para enviar el byte en A POP HL ;recuperar dirección del código objeto JR BUCLE ;seguir ;
; ;Recepción de código objeto desde el QL ;por medio de la interface RS232C Indescomp ; ;Marcos 10/6/1988 ; ;Cada byte del código está en formato hexadecimal, ;sin retorno de carro ni separación alguna entre ellos. ;La carga del software de la interface y la fijación de la ;tasa de baudios se hacen desde BASIC ;El código objeto debe finalizar con un caracter 26 de fin de fichero ; ORG 23296 ;buffer de la impresora ; LD HL,(23670) ;destino del código en SEED, para ser fijado con RANDOMIZE ; ;Bucle principal: ; ;Recibir nibble alto: BYTE CALL NIBBLE CP 26-48-7 ;¿fin de transmisión, era el caracter 26? JR NZ,BYTE2 ;si no, seguir ; ;Retornar al BASIC PUSH HL ;devolver la dirección final... POP BC ;...mediante BC... RET ;...al BASIC ; ;Guardar nibble alto: BYTE2 RLCA RLCA RLCA RLCA ;pasar los bits bajos a la zona alta LD (HL),A ;guardar nibble alto para luego ; ;Recibir y guardar nibble bajo: CALL NIBBLE OUT (254),A ;cambiar color del borde OR (HL) ;mezclar nivel alto con el bajo... LD (HL),A ;...y guardar el byte completo INC HL ;siguiente dirección JR BYTE ; ;Recibir el byte: ; NIBBLE PUSH HL CALL 64698 ;recibir un byte POP HL LD A,(64525) ;tomar byte recibido ; ;Traducir el dígito hexadecimal: ; SUB 48 CP 10 RET C SUB 7 RET ; ; ;Recepción de código objeto desde el QL ;por medio de la Interface 1 ; ;Marcos 25/6/1988 ; ;Cada byte del código está en formato hexadecimal, ;sin retorno de carro ni separación alguna entre ellos. ;La fijación de la tasa de baudios se hace desde BASIC ;El código objeto debe finalizar con un caracter 26 de fin de fichero ; ORG 23296 ;buffer de la impresora ; RST 8 ;llamar a la Interface 1... DEFB #31 ;...para insertar sus variables, por si acaso XOR A ;resetear... LD (#5CC7),A ;...la variable SER_FL ; LD HL,(23670) ;destino del código en SEED, para ser fijado con RANDOMIZE ; ;Bucle principal: ; ;Recibir nibble alto: BYTE CALL NIBBLE CP 26-48-7 ;¿fin de transmisión, era el caracter 26? JR NZ,BYTE2 ;si no, seguir ; ;Retornar al BASIC PUSH HL ;devolver la dirección final... POP BC ;...mediante BC... RET ;...al BASIC ; ;Guardar nibble alto: BYTE2 RLCA RLCA RLCA RLCA ;pasar los bits bajos a la zona alta LD (HL),A ;guardar nibble alto para luego ; ;Recibir y guardar nibble bajo: CALL NIBBLE OR (HL) ;mezclar nivel alto con el bajo... LD (HL),A ;...y guardar el byte completo INC HL ;siguiente dirección JR BYTE ; ;Recibir el byte: ; NIBBLE PUSH HL NIBBL2 RST 8 ;llamar a la interface 1... DEFB #1D ;...para recibir un caracter de la entrada RS232C AND A ;¿se ha recibido un caracter nulo? JR Z,NIBBL2 ;si es así, volver a leer POP HL ; ;Traducir el dígito hexadecimal: ; SUB 48 CP 10 RET C SUB 7 RET ; ; ;Recepción de código objeto del Z80 desde el QL ;por medio de la conexión RS232C de la Interface 1 ; ;Marcos 31/7/1988 ; ;El código objeto está en formato "puro", tal cual, sin indicador de ;final ni nada de nada (se hace un BREAK y se acabó) ;La fijación de la tasa de baudios se hace desde BASIC ;La dirección+1 última en que se ha guardado un byte, queda en (SEED) ; ORG 23296 ;buffer de la impresora ; ;Variables del sistema ; SEED EQU 23670 SER_FL EQU #5CC7 ; ;Inicialización: ; RST 8 ;llamar a la Interface 1... DEFB #31 ;...para insertar sus variables, por si acaso XOR A ;resetear... LD (SER_FL),A ;...la variable SER_FL para eliminar restos del RS232 LD HL,(SEED) ;tomar destino del código, fijado con RANDOMIZE desde Basic ; ;Bucle principal: ; BUCLE PUSH HL ;preservar dirección del código objeto RECIBE RST 8 ;llamar a la interface 1... DEFB #1D ;...para recibir un caracter de la entrada RS232C JR NC,RECIBE ;si no hay caracter disponible, volver a intentarlo POP HL ;recuperar dirección del código objeto LD (HL),A ;guardar byte en su dirección INC HL ;apuntar a la siguiente dirección LD (SEED),HL ;guardar la dirección para que el Basic la use luego JR BUCLE ;ir a leer otro byte ;
Alojado en / Hosted at: Sinclair QL Recursos en Castellano Sinclair QL Spanish Resources |