Ficheros con el Opus Discovery ------------------------------ El siguiente articulo está dividido en dos partes. El primero es una adaptación del articulo "No lo olvides, archivalo" pensado esta vez en el Discovery. La segunda parte trata ampliamnete sobre cómo el Opus maneja los ficheros tanto secuenciales como directos. I - No lo olvides, archivalo... (texto condensado y adaptado originalmente publicado en Input Sinclair) Todo programa que maneje gran cantidad de datos ha de contar con alguna forma conveniente de almacenar los datos. Una forma es usar sentencias DATA para almacenar un listín telefonico sencillo, pero si la informacion queda dentro del programa mismo, uno tiene que interrumpir el programa (con BREAK) para modificarlo, lo cual no es muy satisfactorio. Es mucho mejor almacenar la informacion en cinta o disco, los datos en este caso se almacenan en un fichero. Guardando los datos separadamente del "nucleo" del programa se tiene loq ue se llama una memoria virtual: es decir que el programa base puede ser usado con ficheros diferentes, cada uno con su propio numero de registros o campos. El almacenamiento secuencial de datos es uno de los metodos preferidos, ya que su programacion es sencilla y los principios en que se basa son faciles de entender y los datos pueden ser convertidos a un formato que permite su traslado de un programa a otro o de un ordenador a otro inclusive. En un fichero secuencial los datos se almacenan en serie, con un solo byte por separador entre datos. Este articulo explica como crear tus propios ficheros de modo de poderlos incorporar en tus programas. Creacion de fichero: "Listin telefonico" En este programa ejemplo pensado para el Opus Discovery 1 y 2, las entradas admitidas son nombre, apellido y numero. La informacion puede ser enviada directamente al fichero, pero es mas conveniente almacenarlo en cadenas para salvar posteriormente toda ella. La primera parte del programa, establece un sencillo bucle que permite introducir los datos. El maximo da datos a ingresar es 50, si se desea más, se deben cambiar las dimensiones de la línea 10: 10 DIM a$(50,15): DIM b$(50,15): DIM t$(50,12): DIM n(1): DIM s$(15) 20 LET n=0 30 LET n=n+1 40 INPUT "Nombre ";a$(n) 50 INPUT "Apellido ";b$(n) 60 INPUT "Numero de Telefono ";t$(n) 70 IF a$(n)<>s$ AND n<50 THEN GO TO 30 Para salvar los datos como matriz: 80 CLS : PRINT "Almacenando los datos" 100 LET n(1)=n: SAVE *1,"contador" DATA n() 110 SAVE *1,"nombre" DATA a$() 120 SAVE *1,"apellido" DATA b$() 130 SAVE *1,"telefono" DATA t$() 140 PRINT "Datos almacenados" 150 STOP Si se desea salvar los datos a cinta, omitir entonces todos los "*1" del listado ya que de lo contrario, se salvará todo al disco 1. Lectura del fichero: La rutina para leer el contenido de un fichero y trasladarlo a la memoria, es el inverso al de escritura. Para cargar la matriz de datos de cinta o disco, tipear: 200 CLEAR 210 LOAD *1,"contador" DATA n() 220 LOAD *1,"nombre" DATA a$() 230 LOAD *1,"apellido" DATA b$() 240 LOAD *1,"telefono" DATA t$() 250 FOR l=1 TO n(1) 260 PRINT a$(l),b$(l),t$(l) 270 NEXT l Una mejora al programa es añadirle la opcion de elegir en cuál unidad se desea salvar los datos (en el caso de tener más de una unidad de disco): 90 INPUT "Disco (1/2)";j 100 SAVE * j,... y para cargar los datos: 210 LOAD * j,... El metodo anterior SAVE DATA tiene el inconveniente de que hay que guardar toda la cadena aunque solo se use una parte de la misma. La versión que sigue a continuación, almacena cada elemento de la cadena separadamente y se detiene cuando los datos se acaban. 10 DIM a$(50,15): DIM b$(50,15): DIM t$(50,12): DIM s$(15) 20 LET n=0 30 LET n=n+1 40 INPUT "Nombre ";a$(n) 50 INPUT "Apellido ";b$(n) 60 INPUT "Numero de Telefono ";t$(n) 70 IF a$(n)<>s$ AND n<50 THEN GO TO 30 80 CLS : PRINT "Almacenando los datos" 110 OPEN #4,1,"fichero" OUT 120 PRINT #4;n 125 FOR l=1 TO n 130 PRINT #4;a$(l)'b$(l)'t$(l) 140 NEXT l 150 CLOSE #4 160 PRINT "Datos almacenados" 170 STOP Esta version trabaja solo con discos. La línea 110 abre el canal de comunicaciones con el disco. Discovery-dos permite el uso de cualquier canal del 4 al 15. Es muy importante teclear las líneas tal como se muestran. La línea 110 requiere una coma como separador, mientras la línea 130 exige un pto. y coma luego de la orden PRINT# y el apostrofe como separador de elementos. La version de la rutina para cargar de nuevo los datos se parece a su procedimiento de escritura excepto que en vez de PRINT#, se pone INPUT# y las variables son separadas por pto. y coma para su correcta lectura: 200 DIM a$(50,15): DIM b$(50,15): DIM t$(50,12) 210 OPEN #4,1,"fichero" IN 215 INPUT #4;n 220 FOR l=1 TO n 230 INPUT #4;a$(l);b$(l);t$(l) 235 PRINT a$(l),b$(l),t$(l) 240 NEXT l 250 CLOSE #4 Marca de fin de fichero (EOF) El último programa usaba la variable N en el bucle que controla el procedimiento de lectura, pero no siempre es posible saber de antemano cuantos datos hay que leer. Un modo de salvar el inconveniente es usando una marca de "fin de fichero". El Opus Discovery tiene un metodo muy peculiar para capturar el fin de fichero, similar a la funcion EOF(). Por ahora veremos como se pone una marca muda para el mismo fin. Podría ser la la palabra "FIN", o ZZZ, ó 999, o lo que uno quiera usar y que no se espere dentro del cuerpo del fichero. Ahora se elimina DIM s$(15), ya innecesaria. He aquí el programa completo desde la línea 5 para poder compararlo con las versiones anteriores: 10 DIM a$(50,15): DIM b$(50,15): DIM t$(50,12) 20 LET n=0 30 LET n=n+1 40 INPUT "Nombre ";a$(n) 50 INPUT "Apellido ";b$(n) 60 INPUT "Numero de Telefono ";t$(n) 70 IF a$(n, TO 3)<>"fin" AND n<50 THEN GO TO 30 80 CLS : PRINT "Almacenando los datos" 110 OPEN #4,1,"fichero" OUT 120 LET l=0 125 LET l=l+1 130 PRINT #4;a$(l)'b$(l)'t$(l) 140 IF a$(l, TO 3)<>"fin" AND l<50 THEN GO TO 125 150 CLOSE #4 160 PRINT "Datos almacenados" 170 STOP 200 DIM a$(50,15): DIM b$(50,15): DIM t$(50,12) 210 OPEN #4,1,"fichero" IN 215 LET l=0 220 LET l=l+1 230 INPUT #4;a$(l);b$(l);t$(l) 235 PRINT a$(l),b$(l),t$(l) 240 IF a$(l, TO 3)<>"fin" AND l<50 THEN GO TO 220 250 CLOSE #4 II - Opus Discovery-dos: El dispositivo de almacenamiento de datos oficial de Sinclair fue el Microdrive, con el cual se podían manejar ya sea ficheros secuenciales, o matrices individuales, como ya se ha visto, en cambio con el Opus Discovery se corrigieron dichas limitaciones. Ahora sí es posible trabajar con ficheros de texto en modo directo mediante OPEN/CLOSE y leer y escribir en ellos con INPUT#, INKEY$# y PRINT#, manteniendo una sintaxis simple y estandar. En el caso de los ficheros secuenciales, para detectar el fin de fichero, uno debe usar ya sea una marca muda o una rutina que atrape el final del fichero como un error más. Opus - Ficheros secuenciales En el Discovery, el nombre del fichero es de 10 caracteres como máximo. Nota: Para la explicacion de los comandos del Opus Discovery, ver su manual. Como ejemplo vamos a crear un fichero que contenga los numeros del 1 al 100: 100 OPEN #4,1,"num.dat" OUT 120 FOR n=1 TO 100 130 PRINT #4; n 140 NEXT n 150 PRINT #4;-1 160 CLOSE #4 170 STOP La línea 150 genera la marca muda de fin de fichero. Una variante del ejemplo anterior, salvando los datos como cadenas. Podemos añadir la sigte línea al ejemplo anterior: 105 DIM n$(3) y modificamos la línea que salva los datos del sigte. modo: 130 LET n$=STR$ n: PRINT #4; n$ 150 PRINT #4;CHR$ 0 Una vez que el fichero ha sido creado y guardado en el disco, se puede recuperar o accesar cuando se desee. La sigte. rutina permite leer todo el fichero salvado hasta alcanzar la marca muda que sirve de EOF: 300 OPEN #4,1,"num.dat" IN 320 INPUT #4;n 330 IF n=-1 THEN GO TO 360 340 PRINT n 350 GO TO 320 360 PRINT "fin de fichero" 370 CLOSE #4 En el caso de haber salvado los datos como cadenas, las modificaciones serán: 320 INPUT #4;n$ 330 IF n$=CHR$ 0 THEN GO TO 360 340 PRINT n$ Nota: en el caso de solo variables numericas sin ningun caracter alfabetico, la marca de fin de fichero debe de ser un número que no forme parte de la lista a salvar, en este caso '-1', para su correcta lectura. En el caso de los valores alfanuméricos, yo suelo usar CHR$ 0, pero cualquier otro valor sirve. Ahora para leer datos separadamente, como muestra el sigte. ejemplo: 1000 OPEN #6,1,"prb.dat" OUT 1010 PRINT #6;"prueba"'"#2","numero";128 1020 CLOSE #6 1050 OPEN #6,1,"prb.dat" IN 1060 INPUT #6; n$; m$: PRINT n$'m$ 1070 CLOSE #6 1075 STOP Nota: si la cadena salvada tuviera caracteres como las comillas (") por ejemplo, la sentencia INPUT deberá incluir la isntrucción LINE para su correcta lectura. Ejemplo: 1060 INPUT #6; LINE n$ A continuacion vemos como se lee una cadena previamente salvada, mediante INKEY$ #, es decir caracter a caracter: 850 OPEN #5,1,"chr.dat" IN 860 LET n$=INKEY$#5 870 IF n$= CHR$ 0 THEN GO TO 885 875 IF n$<>CHR$ 13 THEN PRINT n$ 880 GO TO 860 885 CLOSE #5 La rutina se detiene al encontrar la marca muda de EOF, o imprime si no halla un ENTER. Añadiendo datos a un fichero secuencial El Discovery tiene un modo APPEND, así que es posible añadirle nuevos datos a un fichero previamente salvado. APPEND : 1100 REM modo append 1110 OPEN #4,1,"num2.dat" OUT 1120 FOR n=100 TO 0 STEP -1 1130 PRINT #4;n 1140 NEXT n 1150 CLOSE #4 Basta con volverlo a abrir otra vez, pero en modo EXP para seguir agregándole nuevos datos: 1210 OPEN #5,1,"num2.dat" EXP 1220 FOR n=1 TO 100 1230 PRINT #5;n 1240 NEXT n 1245 PRINT #5;-5 1250 CLOSE #5 Esta vez los datos han sido salvados con una marca muda de EOF, a diferencia de antes. Para añadir más datos ahora, habría que modificar la rutina de lectura para que reconozca la marca muda y así no la añada al fichero con los futuros datos que se pondrán ya que entonces el fichero acabará con un montón de marcas mudas en diferentes partes de la misma, cosa que no deseamos que pase. OPEN EXP no reinicia el fichero desde cero, si el nombre dado no existe dará error. Para resetear el puntero a cero basta con abrir otra vez el fichero con OPEN OUT. También es posible añadirle datos de otros ficheros a uno nuevo con MOVE: 1110 OPEN #4,1,"numx2.dat" OUT 1120 MOVE 1,"num.dat" TO #4 1130 PRINT #4;"agregado de datos exitoso" 1150 CLOSE #4 1160 MOVE 1,"numx2.dat" TO #2 EOF - versión 1 El siguiente ejemplo lee los datos hasta encontrar la marca muda en mi caso, uso CHR$ 0: 1300 REM eof 1 1310 OPEN #6,"num2.dat",R 1315 LET a$="" 1320 LET m$=INKEY$#6 1330 LET EOF=(m$=CHR$ 0): IF EOF THEN GO TO 1390 1340 IF m$=CHR$ 13 THEN GO TO 1370 1350 LET a$=a$+m$ 1360 GO TO 1320 1370 LET n=VAL a$ 1380 PRINT n 1385 GO TO 1315 1390 CLOSE #6 1400 STOP EOF - versión 2 En este caso en vez de usar una variable EOF, he elegido crear una FN E() por EOF(). Primero hay que añadir la línea: 1305 DEF FN e(n$)=(n$=CHR$ 0) y luego modificar: 1330 IF FN E(m$) THEN GO TO 1490 Esta subrutina contabiliza los caracteres uno a uno hasta alcanzar la marca muda: 9100 REM contabilizador 9110 LET zz=1 9120 LET EOF=(INKEY$#chn=CHR$ 0): IF EOF THEN RETURN 9140 LET zz=zz+1 9150 GO TO 9120 EOF - versión Opus Este es un método exclusivo del Opus Discovery, el cual requiere de un PRINT # antes de leer el eof, de lo contrario, la función no operará como se desea. 1600 REM eof 1610 OPEN #5,1,"num2.dat" 1620 PRINT #5;: LET eof= NOT USR 432: IF EOF THEN GO TO 1640 1630 INPUT #5;n 1635 PRINT n: GO TO 1620 1640 CLOSE #5 1650 STOP Una mejora es definirlo como FN: 1605 DEF FN e()= NOT USR 432 y modificar: 1620 PRINT #5;: IF FN E() THEN GO TO 1640 Este método hace innecesario el uso de la marca muda para cerrar el fichero y permite trabajar muy bien con OPEN EXP. Opus - ficheros de longitud fija Todos los ejemplos hasta el momento han sido de ficheros de longitud variable, es decir puedes añadirle tantos datos al registro hasta llenar el espacio físico del disco. Es posible sin embargo, limitar la cantidad de datos a guardar con un parámetro extra tanto en OUT como EXP. Ejemplos: 100 OPEN #4,1,"num.dat" OUT 50 120 FOR n=1 TO 100 130 PRINT #4; n 140 NEXT n 160 CLOSE #4 200 MOVE 1,"num.dat" TO #2 Este ejemplo se detendrá con un mensaje de error y no salvará los cien números. Para continuar hacer GOTO 160 y la línea 200 mostrará en pantalla solo 20 datos. Esto es debido a que el "OUT 50" limitó el espacio disponible de bytes a usar como máximo por el fichero. Es más, el último dato queda sin su terminación 13 para separar los datos. Para evitar eso, uno debe hacer RND 51 en vez de 50 para crearle espacio. 100 OPEN #4,1,"num.dat" EXP 50 Esta línea ejemplo permitirá añadirle datos hasta llenar el espacio designado por "EXP 50". Opus - Ficheros de acceso directo Hasta ahora hemos visto cómo leer y añadir nuevos datos a un fichero secuencial. Pasamos pues a ver como trabajar con ficheros directos o aleatorios. Para comprender mejor como opera el Discovery-dos, veamos el siguiente ejemplo: 1800 OPEN #4,1,"dat2.num" RND 3,21 1810 FOR n=1 TO 20 1815 POINT #4;n 1820 PRINT #4; n*2 1830 NEXT n 1840 CLOSE #4 1850 STOP 1900 OPEN #4,1,"dat2.num" RND3 1910 FOR n=1 TO 20 1915 POINT #4;n 1920 INPUT #5; m: PRINT m 1930 NEXT n 1940 CLOSE #4 1950 STOP En el ejemplo de ahora, tenemos que los valores son salvados como numeros, pero manejados como cadenas de 3 digitos (esto es indicado en la opcion RND x al final de OPEN, donde x es la longitud máxima de la cadena a salvar dentro del registro dado) y el número 21 es el número de registros a ingresar + 1. El puntero de datos es fijado por la orden POINT#, siendo aconsejable salvar cadenas de longitud fija para mover el puntero a donde queremos, siempre, de lo contrario acabaríamos leyendo los datos erroneamente. El siguiente ejemplo lee un fichero en modo directo: 2350 OPEN #4,1,"num3.dat" RND 3 2360 INPUT "Registro #:";reg: IF reg=-1 THEN GO TO 2380 2365 POINT #4;reg 2370 INPUT #5; a$: PRINT a$ 2375 GO TO 2360 2380 CLOSE #4 2385 STOP Notese que basta con dar la logitud de la cadena pero no el número de registros cuando se vuelve a abrir un fichero para su lectura directa, y que un fichero previamente salvado como secuencial no puede ser accedido ni modificado de modo directo. Veamos otro ejemplo de trabajo con ficheros de acceso directo: 3000 REM modo rnd write 3005 DIM n$(3) 3010 OPEN #4,1,"numx.rnd" RND 3,11 3020 FOR n=1 TO 10 3030 POINT #4;n 3040 LET n$=STR$ (n*2) 3050 PRINT #4; n$ 3060 NEXT n 3070 CLOSE #4 3080 STOP 3100 REM modo rnd read 3105 DIM n$(3) 3110 OPEN #4,1,"numx.rnd" RND 3 3120 FOR n=1 TO 10 3130 POINT #4;n 3140 INPUT #5; n$ 3150 PRINT n$ 3160 NEXT n 3170 CLOSE #4 3180 STOP 3200 REM modo rnd rd/wr 3210 OPEN #4,"numx.rnd" RND 3 3220 INPUT "registro #:";reg 3225 IF reg<1 THEN GO TO 3300 3226 IF reg>10 THEN INPUT "Registro inexistente intente de nuevo";v$: GO TO 3220 3230 POINT #4;n 3240 INPUT #5; n$ 3250 INPUT "El registro ";(reg)'"contiene el valor: ";(n$)'"Desea modificarlo? ( s/n) ";v$ 3260 IF v$<>"s" AND v$<>"S" THEN GO TO 3220 3270 INPUT "Ingrese nuevo valor: ";n: IF n=0 THEN GO TO 3220 3280 PRINT #4;reg, STR$ n 3290 GO TO 3220 3300 CLOSE #4 8990 STOP Notese que en la línea 3010 se ha fijado el máximo de registros a 11, lo cual permite almacenar y modificar solo 10 registros y por lo tanto no es posible añadirle más registros mediante OPEN RND, pues de intentar abrirlo para agregar nuevos datos, uno acaba reseteando el puntero al comienzo y todo lo antes salvado se pierde. Si se desea que el fichero se cree sin más límite que el espacio libre en el disco se debe reemplazar el numero de registro por el valor -1(*). Ejemplo: 1100 OPEN #4,1,"dat2.rnd" RND 5,-1 1105 DIM b$(5) 1110 LET n=1 1120 INPUT b$: IF b$="" THEN GOTO 1120 1130 POINT #4;n 1140 PRINT #4; b$ 1130 IF b$(TO 3)="fin" THEN GOTO 1170 1160 LET n= n+1: GOTO 1120 1170 CLOSE #4 1180 STOP De este modo la base puede ser leida con: 1100 OPEN #4,1,"dat2.rnd" RND 5 1105 DIM b$(5) 1110 LET n=1 1120 POINT #4;n 1130 INPUT 4;b$ 1140 IF b$(TO 3)="fin" THEN GOTO 1170 1150 PRINT b$ 1160 LET n= n+1: GOTO 1120 1170 CLOSE #4 1180 STOP (*): En el caso del Opus esto solo reserva la mitad del espacio libre en el disco. Para continuar añadiendole datos a futuro bastará con abrirlo y fijar el puntero desde donde se quedó y seguir desde allí o si se requiere modificar cualquiera de los registros con: 1200 OPEN #4,1,"dat2.rnd" RND 5 Si la vez anterior se añadieron 27 registros al fichero entonces con: 2110 LET n=28 se podrá seguir agregando nuevos datos hasta volver a tipear "fin". En conclusión: si se desea trabajar con ficheros directos de longitud libre, es mejor crearlos primero vacios, cerrarlos y volverlos a abrir para recién añadirles la información. Ejemplo: Creamos un fichero con: 10 OPEN #4,1,"juegos.rnd" RND 5,-1: CLOSE #4 una vez cerrado lo reabrimos: 20 OPEN #4,1,"juegos.rnd" RND 5 y a partir de aquí vendría todo el programa de inserción, modificación y eliminacion de los datos. También es posible pasar datos de ficheros directos más chicos a otras bases con mayor espacio disponible mediante MOVE, siempre y cuando ambas bases tengan la misma longitud. Si la base no cupiese del todo, dará un mensaje de error y se interumpirá el proceso, pero no importa, basta con hacer un CLOSE# y el fichero se cerrará con los datos salvados hasta donde hubo sitio. Nota: MOVE TO #2 vuelca el contenido de ficheros directos a la pantalla, pero mostrará los datos sin separación, además mostrará n caracteres con los cuales se ha llenado la base según lo especificado en el parámetro opcional de bytes de inicio. Si el parámetro opcional fuese -1 mostrará otros contenidos del disco teniendo que usar BREAK para detener la acción. BETABASIC y Opus: Discovery-dos salva los numeros como cadenas que ocupan el espacio equivalente a los digitos que lo conforman, así 5, ocupa 1 caracter, mientras 22, 105, 1500 y 45000 ocupan 2, 3, 4, y 5 caracteres respectivamente. Para compactar los valores enteros hay dos funciones en el Basic Microsoft que son muy utiles para salvar numeros, y son MKI$ (make integer) y CVI (convert integer) los cuales sirven para convertir un valor entero a cadena de dos digitos y viceversa. BetaBasic ofrece sus equivalentes en CHAR$() y NUMBER(), las cuales se pueden definir del sigte modo si se carece de BetaBasic: DEF FN C$(n)=CHR$ INT (n/256)+CHR$ (n-INT (n/256)*256) ó FN M$(... DEF FN N(c$)=256*CODE c$(1)+CODE c$(2) ó FN C(... Ejemplos: 100 REM en el programa de escritura 110 DIM x$(2) 120 LET x$=CHAR$(x) 100 REM en el programa de lectura 120 LET a=NUMBER(a$) Existen otras funciones en el Basic Microsoft asociadas a los ficherso directos y que son: MKS$ (make single), CVS (convert single), MKD$ (make double) y CVD (convert double), los cuales solo tienen su equivalente en la funcion FP$() del Betabasic 4. Para poder capturar el EOF debe usarse en su lugar la instrucción: 1620 ON ERROR: IF ERROR = 8 THEN GOTO 1640 BetaBasic tambien ofrece una función EOF(n), la cual no funciona con Discovery-dos, a menos que se trate de la version '3.0 D' exclusiva para el Discovery. De ser este el caso, tendríamos la línea siguiente: 1620 IF EOF(5) THEN GO TO 1640 Para terminar este articulo ofrezco este programa originalmente publicado en el manual del Timex FDD 3000, pero convertido para correr en el Discovery: 10 REM RUN para ejecutar e inicializar por primera vez 20 REM RUN 110 todas las demas veces 30 REM GOTO 120 para continuar tras una interrupcion o error 100 OPEN #4,1,"address.rnd" RND 100,-1: CLOSE #4 110 LET end=20000 115 OPEN #4,1,"addres.rnd" RND 100: DIM a$(100) 120 INPUT "1=Leer,2=Escribir y 3=Fin";h$ 130 IF h$="2" THEN GO TO 500 135 IF h$="3" THEN GO TO 610 140 IF h$<>"1" THEN GO TO 120 150 REM lee un registro 160 INPUT "Registro numero: ";n 170 IF n>65535 OR n<1 THEN GO TO 160 180 GO SUB 1000 190 CLS 200 PRINT AT 4,3;"Nombre ";a$( TO 30) 210 PRINT AT 8,0; "Direccion ";a$(31 TO 60) 220 PRINT AT 12,2; "Telefono ";a$(61 TO 75) 230 PRINT AT 16,2; "Notas ";a$(76 TO ) 240 INPUT " para continuar";h$ 250 GO TO 120 500 REM escribe un registro 510 INPUT "Registro numero: ";n 520 IF n>65535 OR n<1 THEN GO TO 510 530 CLS 540 DIM n$(30): DIM b$(30): DIM p$(15): DIM c$(25) 550 INPUT "Nombre ";n$ 560 INPUT "Direccion ";b$ 570 INPUT "Telefono ";p$ 580 INPUT "Notas ";c$ 590 GO SUB 2000 600 GO TO 120 610 CLOSE #4 615 PRINT "Terminado" 620 GO TO END 1000 REM //lee fichero// 1005 POINT #4;n 1010 INPUT #4; a$ 1930 RETURN 2000 REM //escribe fichero// 2005 POINT #4;n 2010 PRINT #4; n$+b$+p$+c$ 2020 RETURN Francisco Leon zx_if1@hotmail.com