CAPTURANDO ERRORES EN EL SPECTRUM A continuacion una traduccion al castellano del texto original en ingles publicado por COMPUTING TODAY en su edicion de febrero de 1985. " MANEJANDO LOS ERRORES EN EL ZX SPECTRUM por B. Twissell Un ON ERROR podria ser de valiosa ayuda a la hora de depurar un programa. Aqui le mostramos como añadir tal utilidad a su Spectrum. Este articulo describe una corta rutina en codigo maquina que dota al Basic del Spectrum de ON ERROR. Hay muchas formas de causar que un programa BASIC termine: 1) correctamente, llegando al final del programa o mediante STOP. 2) por un fallo en el programa: NEXT sin FOR, No hay DATAs, etc. 3) por un evento imprevisto: Sin memoria, Numero muy grande, etc. 4) por un error de E/S: STOP en INPUT, error de cinta, fin de fich. 5) porque el usuario tipeo BREAK. Cuando uno de esos eventos ocurre, quiza querramos que el programa continue, y posiblemente tomar acciones especiales. Hay muchas cosas que podemos hacer: a) terminar todo. b) ignorar el error y proseguir pese a ello. c) repetir la sentencia que causo el error. d) saltar a otra sentencia. e) ejecutar sentencias (o rutina) y volver a la sent. interrumpida. f) sufrir un cuelgue o un reseteo. Quiza deseemos diferentes acciones para diferentes errores; por ejemplo (b) es adecuado pars (5) pero (d) es mas optimo para (3). La unica accion que nos ofrece el BASIC del Spectrum es solo (a). LA COMPETENCIA Otros micros tienen ON ERROR y ON BREAK pars especificar las acciones a tomar cuando un error ocurre. En principio, los que tengan un Interface 1 pueden escribir una rutina en cod. maquina para añadir tal sentencia al Spectrum. Pero, la rutina descrita aqui es accedida via USR, PEEK y POKE; puede ser usada con o sin Interface 1. Esta nueva utilidad manejara todos los errores que tengan un numero de reporte - asi cosas como "L BREAK" si son capturables, pero no aquellas como “Microdrive not present", etc. El program 1 carga la rutina en cod. maquina en memoria. Si el checksum es correcto ya solo qued salvar el cosigo mediante: SAVE --- CODE 30000, 64 Las rayas deben ser remplazadas con el nombre de su eleccion. La rutina es reubicable, osea que puede ser puesta en cualquier parte de la memoria. Y se puede vovler a cargar tipeando: CLEAR 59999 LOAD --- CODE 60000 o donde sea que uno quiera. Para activar la captura de error, simplemente llamamos a la rutina: RANDOMIZE USR 30000 siendo ese valor remplazado por la direccion en la que uno lo ha cargado. Esa debe ser siempre la primera sentencia del programa, por la obvia razon de que debemos activar la captura de errores antes de que se produzca alguno. Una vez activado, el programa BASIC puede terminar del modo normal ya sea con "OK" o con STOP. La captura de error se desactiva y hay que activarla cada vez que uno ejecute su programa. Cualquier intento de terminar con un reporte de error sera detectado y asi el programa BASIC continuara su ejecucion. CONSIDERACIONES DE DISEÑO La rutina ha sido diseñada para ser lo mas simple posible. Hubiera sldo posible tambien agregarle un POKE para decirle a la rutina algo como la linea a la cual saltar, pero lo rechazamos por el bien de la simplicidad. Quizas lo mas obvio que la rutina puede hacer es un CONTINUE auto matico cada vez que un error ocurre. Eso puede significar por ejm. que BREAK puede ser ignorado, pero si se desea efectuar alguna accion, se puede detectar que un BREAK ha ocurrido examinando el teclado o PEEKeando la variable ERRNR. Pero en muchos casos, CONTINUE repite la sentencia que causo el error. Si el error era, por decir, "Numero muy grande", entonces la sentencia podria fallar otra vez, y el programa acabaria colgandose. Y si u7n VERIFY falla, ya no hay modo de volver a VERIFY a menos que se vuelva a hacer el SAVE primero. Antes que tener muchos casos especiales tratando de copar con todas las eventualidades, la solucion a elegir no tiene caos especiales - traa un error, la ejecucion es siempre proseguida en la siguiente sentencia. Por ejemplo, en 100 LET n = 4 110 LET n = 1/0 120 PRINT n la division resultara en "Numero demasiado grande". El programa no colapsara, sino que proseguira con la sentencia PRINT. Ya que la linea ll0 no se completo, n aun retiene su antiguo valor, y asi 4 se imprime. QUE SIGUE LUEGO? Luego de darse un error, la variable del sistema ERRNR (1 byte en 23610) tendra un valor distinto a 255. ERRNR corresponde a los reportes de error del modo sigte.: ERRNR REPORTE 255 0 (OK) 0,1,...,8 l,2,...,9 9,10,...,26 A,B,...,R ERRNR conservara dicho valor hasta que el sigte. error ocurra, en ese punto sera rescrito con el nuevo numero de error. Para detectar cuando ocurre un error, hay que limpiar primero cualquier error previo POKEando 255 en ERRNR. Cuidado al ejecutar la ultima sentencia sin antes poner ERRNR a 255, ya que se asume que ese valor esta ahi cuando se inicio el programa. Ya que si se diera un error sin antes limpiar ERRNR, el programa podria no finalizar como se espera. Es aconsejable usar STOP cada vez que se desee terminar un programa ya que esto fija ERRNR a 8 y eso siempre funciona. Asi todos los tipos de error del (2) al (5) antes mencionados son capturados y cualquier accion del (a) a (f) puede ser tomada por el codigo mas apropiado del BASIC. El programa 2 da algunos ejemplos. Programa l 10 REM -------------------------------- Implementa ON ERROR en el Spectrum BASIC. ver texto. -------------------------------- 20 CLEAR 29999 30LET address= 30000 40 LET h$= "210f000922b05ceb2a 3d5c732372c93a3a5c3c2802fe09ca03 1321445ccb7e280b3a475c3c772a455c 22425c2100007c32715c220b5c2ab05c e52a425cc39e1b" 50 LET sum=0 60 FOR i=1 TO LEN h$ STEP 2 70 LET byte=l6*FN d(h$(i))+FN d(h$(i+1)) 80 POKE address+(i-1)/2,byte 90 LET sum=sum+byte 100 NEXT i ll0 IF sum<>5087 THEN PRINT FLASH 1'''"Error en linea 40!!!!" 120 DEF FN d(c$)=CODE c$-(CODE "0" AND c$<="9") -(CODE "a"-10 AND c$>="a") Programa 2 10 REM -------------------------------- Ejemplo de ON ERROR. Se asume que la rutina en codigo maquina necesaria esta en la memoria RAM direccion 30000. -------------------------------- 20 RANDOMIZE USR 30000: LET errnr=23610: LET ok=255: LET number too big=5: LET stop in input=16: LET invalid file name=l4: LET tape loading error=26 30 PRINT "Iniciando la matriz."'"No puedes hacer BREAK" '"(trata y veras!!)" 40 DIM n(300) 50 FOR i=1 TO 300 60 POKE errnr,ok 70 LET n(i)=1/INT(10*RND) 80 IF PEEK errnr=number too big THEN PRINT "desbordamiento" 90 NEXT i 100 REM LOOP ll0 POKE errnr,ok 120 INPUT "Entra nombre de archivo"'"STOP (shift-6) es" '"inefectivo..."'LINE f$ 130 IF PEEK errnr=stop in input THEN GO TO 110 I40 REM END LOOP 150 REM LOOP 160 SAVE f$ DATA n() 170 IF PEEK errnr=invalid file name THEN STOP 180 PRINT "Ahora para verificar."'"Si falla, el SAVE" '"se repetira."'"BREAK te sacara." 190 POKE errnr, ok 200 VERIFY f$ DATA n() 210 IF PEEK errnr=tape loading error THEN GO TO 160 220 REM END LOOP 230 PRINT "Terminado" 240 STOP Rutina ON ERROR en codigo maquina ; ------------------------------ ; Implementa ON ERROR en el Spectrum BASIC. ver texto. ; .............................. ; ; Primero que nada esta es una corta ; pieza de codigo para el proceso de ; error. Es una cuestion de elegir ; la direccion de la etiqueta - ; cuando esta es invocada por USR en ; BASIC, BC guardara el valor de ; , y esta 15 bytes despues - ; entonces la direccion de esta ; almacenada en dos lugares: una ; posicion no usada en 23728; y en la ; posicion indicada por ERRSP... ; turnon LD HL,15 ADD HL,BC LD (23723),HL EX DE,HL LD HL,(ERRSP) LD (HL),E INC HL LD (HL),D RET ; ; El interprete BASIC puede detectar ; cualquier error en distintos ; lugares. Pero, la accion a tomar ; es siempre la misma (excepto los ; errores del Interface 1). El ; interprete limpia toda la basura de ; la pila del Z80. Ya que la longitud ; de la pila depende de donde se da ; el error, ERRSP siempre apunta al 2do ; valor que es una direccion. ; Normalmente es 1303H; el codigo en ; esta direccion imprime un mensaje de ; error y se prepara a recibir un comando. ; La captura de error es activada ; (arriba) cambiando esta direccion en la ; pila. Cuando un error se da, la pila es ; limpiada y nuestro codigo es invocado. ; Para los numeros de error 0 (ok) y 9 ; (STOP) dejamos que el programa termine ; saltando al codigo que se supone va a ; ser ejecutado... ; on LD A, (ERRNR) INC A JR Z,L1 CP 9 L1 JP Z,1303H ; ; si un salto va tomar lugar, el bit 7 ; de NSPPC sera 0 - no hace nada. Pero, ; ponemos NEWPPC y NSPPC con el numero ; de linea y la sentencia que sera ; ejecutada luego (eso es uno mas la ; sentencia en curso)... ; LD HL,NSPPC BIT 7,(HL) JR Z,L2 LD A,(SUBPPC) INC A LD (HL),A LD HL,(PPC) LD (NEWPPC),HL ; ; ahora limpiamos un par de cosas que ; ordinariamente deben ser limpiadas ; L2 LD HL,0 LD A,H LD (FLAGX),A LD (DEFADD),HL ; ; finalmente, la direccion de nuestro ; error seran extraidas (POP) de la ; pila; y la ponemos de vuelta ; (recordemos que la hemos almacenado ; en una posicion no usada)... ; LD HL, (23728) PUSH HL ; ; ahora retornamos al interprete, como ; si CONTINUE hubiese sido tipeado, pero ; tras chequear por BREAK... ; LD HL,(NEWPPC) JP 1B9EH " (c)2022 zx_if1@hotmail.com