Comal para Spectrum ------------------- A manera de introducción: El lenguaje de programación Comal (Common Algorithmic Language) es un lenguaje de programación basado en algoritmos, desarrollado en Dinamarca por Benedict Løfstedt y Børge Christensen en 1973. Comal fue pensado en sus días para su uso en la enseñanza, se puede considerar más como un Basic pero con las caracteristicas de lenguajes estructurados como el Pascal y por lo tanto "GOTO less" (sin la sentencia GOTO). Tampoco usa GOSUB, en su lugar usa procedimientos. Su objetivo fue evitar los llamados malos habitos de programación como el uso excesivo de POKEs, GOTOs y listados poco claros debido al uso de líneas con multiples sentencias. Durante los 80, la era de los 8 bits, hubieron versiones comerciales de Comal para casi todas las maquinas conocidas en ese entonces como BBC Micro (Comal ROM), Commodore PET y C=64 (Unicomal), Amiga, CP/M (Comal-80), IBM PC y por supuesto el ZX Spectrum. Actualmente hay una versión "free" para PC: Open Comal de Jos Visser. Dos son las versiones actualmente conocidas para Spectrum: Comal 1.00 y Comal 2.0. Debido a que no hay mayores datos sobre ambas versiones, he tenido que recurrir a información extraida de los mismos programas así como al manual del ZX SPECTRUM, al del Beta Basic y Open Comal para PC, además de algunas paginas WEB. Comal 1 Esta versión es de Freddy Kristiansen, quien hizo una industria de software de computadoras de un solo hombre, llamada FK. Soft, que mantuvo desde enero de 1983 a agosto del 89. Existe también otra versión llamada 'ZX Comal' del mismo autor. Al cargarse el programa aparece el siguiente mensaje en danés: "CASSETTEN K0RE" "SPECTRUM COMAL loades." "© 1984, FK. Soft." "Af Freddy Kristiansen" A continuación carga el programa 'COMAL' propiamente en c.m. que se ejecuta con un USR 65025. Aparece en pantalla el mensaje: 'COMAL 1.00 © 1984, By FK. Soft' y ya estamos listos para programar en Comal. Comal 2 Esta versión fue publicada el año 85, bajo el sello J.J.Computing. Su cargador Basic muestra lo sigte: BORDER 0: PAPER 0: LOAD "" SCREEN$: LOAD "" CODE: INK 7 :RAND. USR 65025 Carga primero una pantalla de presentación y a continuación 'Comal MC', lo ejecuta y da el sigte. mensaje: 'COMAL 2.0 By J.J.Computing' Comandos: REPEAT, UNTIL‚ WHILE, ENDWHILE, ELIF, ELSE, ENDIF, EXIT, SCAN, EDIT, PROC, ENDPROC, EXEC, CHAIN, IMPORT, CASE, WHEN, OTHERWISE, ENDCASE, CODE, FILL, SELECT, ENTER, :-, :+, :=, OF, REF, CLOSED, DO, THEN, TO, STEP, DEF FN, CAT, FORMAT, MOVE, ERASE, OPEN, CLOSE, MERGE, VERIFY, BEEP, CIRCLE, INK, PAPER, FLASH, BRIGHT, INVERSE, OVER, OUT, COPY, DEL, STOP, READ, DATA, RESTORE, NEW, BORDER, CONT, DIM, //, FOR, AUTO, RENUM, INPUT, LOAD, LIST, LET, PAUSE, NEXT, POKE, PRINT, PLOT, RUN, SAVE, RANDOM, IF, CLS, DRAW, CLEAR, ENDLOOP, LOOP Funciones y operadores: RND, KEY$, PI, FN, POINT, SCREEN$, ATTR, AT, TAB, VAL$, ORD, VAL, LEN, SIN, COS, TAN, ASN, ACS, ATN, LN, EXP, INT, SQR, SGN, ABS, PEEK, IN, USR, STR$, CHR$, BIN, NOT, OR, AND Todas las sentencias son tipeadas letra a letra. Hay un evaluador sintactico que corrige automaticamente algunas omisiones al tipear una instrucción compuesta como IF o que impide el ingreso de la línea hasta que se corrija el error sintáctico. Todos los listados de programas son automaticamente indentados y capitalizados. Debido a que Comal toma todos los códigos del 128 al 255 para sus propios tokens, los unicos graficos disponibles para definir por el usuario son los CHR$ 152 - 158 (udg's "i" - "o") los cuales corresponden ahora a 7 de los símbolos graficos obtenibles en modo G y para obtener los otros 7 hay que imprimirlos en modo inverso. Solo existen tres modos de cursor: - 'K' espera la introducción de una orden en la línea de edición - 'L' modo minusculas se pasa a este modo una vez se ha tipeado algo en modo 'K' - 'C' modo mayusculas (CS + 2) No hay modo 'E' ni 'G'. Los símbolos gráficos extras se obtienen con CHR$. Operadores logicos y matematicos: x-y - x menos y x*y - x por y x/y - x entre y x^y - x elevado a la y x OR y - x o y x AND y - x e y x+y - x más y x=y - x igual y xy - x mayor y x<=y - x menor igual y x>=y - x mayor igual y x<>y - x distinto de y NOT x - no x Comandos directos y de edición: Estos comandos no pueden ser usados dentro de listados, solo como ordenes directas. AUTO sintaxis: auto {n}{,s} Activa la numeración automática de líneas a partir de 'n'. Si se omite 's', salta de 10 en 10 por defecto. AUTO sin parámetros empieza desde la línea 10. 'n' puede ser un valor de 0 a 9999. Auto se interrumpe solo si pasa del máximo valor posible (9999) o si en vez de ingresar un comando, se borra el numero en curso y se pulsa ENTER. RENUM sintaxis: renum {n}{,s} Este comando permite renumerar los listados desde 'n', pudiendo medir el intervalo de salto entre líneas con 's'. RENUM sin parámetros empieza desde la línea 10. EDIT sintaxis: edit {n} No edita lineas en curso, solo edita la que es especificada por 'n'. EDIT sin parámetros empieza desde la primera línea del programa en curso y prosigue. Si no se desea seguir editando, basta con borrar toda la línea y pulsar ENTER para detener el proceso. DEL sintaxis: del x {,y} Borra todas las líneas especificadas desde "x" hasta "y". DEL x - borra solamente la línea especificada por 'x'. Es el único modo permitido a diferencia del Basic Sinclair donde basta tipear un número y pulsar ENTER para eliminar una línea. Nota: Sin parámetros devuelve error. También sucede si 'x' ó 'y' no existen en el listado. SCAN verifica que no se hayan cometido errores en el listado. Si detecta alguno, genera un mensaje de error indicando la línea, de lo contrario muestra solo el cursor 'K'. NEW Borra los listados en memoria y reinicia el sistema mostrando la pantalla inicial con el mensaje de COMAL, pero no resetea los atributos de pantalla. CONT Permite continuar la ejecución de un programa si es interrumpido generalmente con BREAK. LIST sintaxis: list {#c{;}}{n} donde #c = el canal del dispositivo al que se enviará el listado. Genera el listado a partir de la línea 'n'. Sin parametros lista desde el inicio. No existe LLIST, en su lugar debe usarse LIST #3, o el canal elegido por OPEN. RUN No tiene parámetros. Ejecuta el programa desde su primera linea. SAVE sintaxis: save a$ Salva el programa bajo el nombre a$. Es un comando directo y da error si se usa dentro de un listado. VERIFY sintaxis: verify a$ Verifica que se ha salvado correctamente el programa a$. Es una orden directa. LOAD sintaxis: load a$ Carga el programa a$. Como SAVE y VERIFY, da error dentro de un listado de programa. MERGE sintaxis: merge x$ Carga listados Comal previamente salvados y los mezcla con el existente en memoria. ENTER sintaxis: enter x$ Permite ingresar líneas de programa desde un archivo previamente salvado, el cual debe contener solo lineas de programa pero ningún comando directo. Al igual que MERGE, reemplaza lineas existentes por las que carga del archivo x$. Sentencias directas y de programa: Estas ordenes pueden ser usadas tanto directamente como dentro de listados. // Equivale a REM. Permite insertar comentarios en cualquier parte del listado. Nota: Solo en su propia línea, ya que no admiten lineas multisentencia. Ejemplos: 100 // esta es una linea valida 299 PRINT: // devuelve error al ejecutarse con RUN ; (pto. y coma) Equivale a ':', solo para ingresar más sentencias LET en una misma línea. Es posible añadir más sentencias con ':' pero no serán aceptadas al ejecutarse el programa con RUN y devolverá error. Una línea tampoco debe terminar en ':'. LET (operadores de asignación) sintaxis1: {let} var{$} := {; ...} LET es opcional y permite definir las variables ya sea numéricas o de cadena. sintaxis2: {let} var :+ / :- Esta variante de LET, permite sumar o restar un valor 'v' a una variable var. Ejemplos: LET a$:= "alfa" a:= 1;b:= 5;c:= 200 a:+ 14;b:- 2 Nota: si se tipea LET, este debe ir solo al comienzo y no después de un ';'. Así es válido '10 LET a:=3; b:=a+4' pero no "10 LET a:=5; LET b:=a+2;" ya que el verificador sintactico no lo admite. PRINT sintaxis: print {#n;}{attr;}{tab x;}{,}{datos}{;}{'} donde: #n = canal al cual se enviarán los datos (2 por defecto) attr = atributos de impresión: INK, PAPER, FLASH, INVERSE, BRIGHT y OVER tab = tabula la impresión de datos datos = expresiones validas de texto, números o variables de cadena y numéricas Admite toda la sintaxis Sinclair de la sentencia PRINT del Basic. AT sintaxis: print {#n;}at x,y;{datos}{;} Posiciona el cursor de impresión de datos en la pantalla. Sintaxis Sinclair. Nota: No existe LPRINT, en su lugar PRINT #3, o el canal elegido con OPEN. COPY Orden típica del Spectrum, permite volcar la pantalla a la impresora. BEEP sintaxis: beep x,y Toca una nota 'y' por el altavoz durante 'x' segundos. OUT sintaxis: out x,y Envía un byte 'n' por el port 'm' a nivel de procesador. PAUSE sintaxis: pause n Genera una pausa de 'n' imágenes (50 o 60 por segundo). Si n=0, espera hasta que se pulse una tecla. POKE sintaxis: poke m,n Escribe el byte 'n' en la posición de memoria 'm'. RANDOM sintaxis: random {n / usr n} donde: n = un valor de 0 a 65535 RANDOM fija la variable SEED para generar valores con la función RND. Sin parámetros equivale a n=0. Con USR, sirve para llamar y ejecutar subrutinas de codigo máquina. CLEAR sintaxis: clear {n} Cambia la ramtop a la posición 'n'. Sin parametros, borra todas las variables liberando el espacio que ocupan, además de hacer un CLS. CHAIN sintaxis: chain a$ Carga y ejecuta el programa x$. Puede usarse como orden directa o dentro de un programa. Equivale a RUN x$ de otros lenguajes de programación. CODE Solo puede ser usado en asociación con SAVE, LOAD y VERIFY. Las pantallas (SCREEN$) solo pueden ser salvadas y cargadas con CODE 16384,6912. SAVE sintaxis: save a$ code x,y Salva 'y' bytes a partir de la dirección 'x' bajo nombre a$. VERIFY sintaxis: verify a$ code {x} Verifica que los bytes se han salvado correctamente. LOAD sintaxis: load a$ code {x} Carga el código máquina previamente salvado como a$. Nota: en el caso de LOAD y VERIFY, si se interrumpe el proceso, ya sea BREAK o error, CODE es reemplazado por ORD y devuelve error si se intentara volver a ejecutar la orden, hay que editarla y volver a tipear CODE. SELECT sintaxis: select #n SELECT OUT especifica que todo sea enviado hacia un dispositivo ya sea la pantalla o la impresora por ejemplo mediante un canal '#n'. SELECT IN especifica que todo sea leido desde un periferico seleccionado por '#n'. Nota: Open Comal admite la sintaxis 'select a$, donde a$ es el' nombre de archivo elegido para enviar o recibir los datos. Instrucciones diversas: Estas son usadas solo dentro de listados de programas, no como ordenes directas. Manejo de datos: INPUT sintaxis: input {#n;}{attr;}{tab x;}{,}{txt}{;}{(v)};var{$} Lee datos desde el teclado y los almacena como variables numéricas o de cadena. Nota: Admite toda la sintaxis del INPUT de Sinclair, excepto la forma Input LINE. Tampoco parece haber forma de interrumpir la introducción de datos como en Basic. DIM sintaxis: dim var{$}(i1,...,ik) Crea una matriz numérica var o de cadena var$ de ik dimensiones. La borra y reinicia si esta existiese ya. Nota: a diferencia del Basic Sinclair solo se admite un DIM por línea, excepto eso tiene la misma sintaxis. READ sintaxis: read v1,v2,...,vk Asigna a las variables v1 a vk con los valores existentes en la lista de DATA. DATA sintaxis: data d1,d2,...,dk Cada sentencia DATA contiene una lista de expresiones, numéricas o de cadenas, que serán leidas por READ. RESTORE sintaxis: restore {n} Restaura el puntero de DATA a partir de la linea n. Sin parametros, n=0. STOP Detiene (interrumpe o finaliza) el programa. Toma de decisiones: IF sintaxis1: IF v THEN s1 Es la forma corta y es identica a la sintaxis Sinclair. sintaxis2: IF v THEN s1 {ELIF v then s2} {ELSE s3} ENDIF Es la llamada forma larga y se usa para trabajar con bloques de líneas 's1' CASE sintaxis: CASE e OF WHEN e1,e2,...en sc ... {OTHERWISE sc} ENDCASE donde: e = variable numerica o alfanumerica a evaluar e1...en = expresiones con las que 'e' es comparada sc = la sentencia o serie de ordenes a ejecutarse si se cumple la condición WHEN otherwise = para cualquier otra condición no cubierta en la lista WHEN CASE permite la selección entre alternativas y es muy similar a IF, excepto que ofrece una mayor lista de decisiones y de acciones a realizar. Nota: 'e1...en' no pueden incluir signos como <.<=.>.>= o <> del modo 'when <2'. WHEN evalua solo si e = e1 para ejecutar la serie de ordenes dadas como alternativas. Ejemplo valido en OPen Comal: 10 FOR f:=1 TO 10 DO 20 PRINT "Kees is ";f 30 CASE f OF 40 // test kees 50 WHEN 1 60 PRINT "een" 70 WHEN <=2 80 PRINT "kleiner gelijk 2:" 90 WHEN 6, 7 100 PRINT "zes of zeven" 110 WHEN >9 120 PRINT "groter negen" 130 OTHERWISE 140 PRINT "none of the above" 150 ENDCASE 160 // 170 ENDFOR El mismo ejemplo en Comal para Spectrum: 10 FOR f:=1 TO 10 DO 20 PRINT "Caso es ";f 30 CASE f OF 40 // prueba caso 50 WHEN 1 60 PRINT "uno" 70 WHEN 6, 7 80 PRINT "seis y siete" 90 OTHERWISE 100 IF f<=2 THEN 105 PRINT "menor igual 2" 110 ELIF f>9 THEN 120 PRINT "mayor nueve" 130 ELSE 140 PRINT "ninguno de los anteriores" 145 ENDIF 150 ENDCASE 160 // 170 ENDFOR Bucles: Comal admite 4 tipos de bucles a elegir, incluyendo el clásico for - next. FOR/NEXT sintaxis: FOR v := n1 TO n2 {STEP s} DO sc NEXT n El bucle tiene una iteración cuyo numero de veces es fijado bajo el control de un indice 'v'. Cualquier variable con ese nombre es borrada y su valor final es reemplazado por del bucle FOR. Nota: otras versiones de Comal usan DOWNTO en vez de STEP -1 y ENDFOR en vez de NEXT. Al igual que con IF también existe la forma corta y la larga. Comal para Spectrum admite solo la forma larga. También se mantiene la norma Sinclair de que el nombre sea una sola letra. LOOP/ENDLOOP/EXIT sintaxis: loop sc {IF v THEN} exit endloop Las sentencias dentro de este bucle se repiten indefinidamente. El único modo de salir es mediante una orden EXIT. Nota: Otras versiones usan la forma 'EXIT WHEN v' no admitida por el Spectrum Comal. REPEAT/UNTIL sintaxis: repeat sc until e Típica sentencia pascaliana. El bucle se repite hasta que se cumple la condición 'e'. WHILE/ENDWHILE sintaxis: while e do sc endwhile El bucle se repite mientras se cumpla la condición 'e'. Procedimientos: Comal soporta procedimientos y funciones que pueden admitir parámetros y pueden ser puestos en cualquier parte del listado a diferencia del Pascal. En Comal 1, los procedimientos solo pueden ser usados dentro de un programa, no son admitidos como ordenes directas. Comal 2 permite esto último tras hacer RUN. DEF FN sintaxis: def fn f{$}({par})= exp donde: f{$} = nombre de la función, la cual debe ser una sola letra par = los argumentos que pueda o no requerir la función exp = la expresion matematica o alfanumerica de la función Crea una función nueva que puede ser numerica 'f' o de cadena 'f$'. FN sintaxis: fn f{$}({par}) Permite llamar y utilizar las nuevas funciones creadas con DEF. PROC sintaxis: PROC nm {(par, {REF par})}{CLOSED} sc ENDPROC donde: nm = nombre de procedimiento, puede ser de más de una letra par = el o los argumentos que pueda o no requerir el procedimiento sc = la sentencia o serie de ordenes que conforman el cuerpo del procedimiento Nota: 'nm'no puede terminar en '$' o cualquier otro simbolo que no sea alfabetico. END PROC Marca el punto en que se cierra el procedimiento iniciado por la orden PROC. REF Cuando la opción REF antecede a un argumento, se dice que este es "pasado por referencia". Se usa cuando deseamos extraer resultados del procedimiento en vez de solo darle datos. Nota: debe haber un REF por cada variable de referencia. Ejemplo: 1066 PROC aap(a, REF b, REF c) CLOSED Es un modificador que especifica que todas las variables dentro del procedimiento son clasificadas locales pero impide el uso de variables globales dentro del mismo. EXEC sintaxis: {exec} nm Incluida por compatibilidad con viejas versiones de Comal. Llama un procedimiento 'nm'. IMPORT sintaxis: import v {(p)} {, v {(p)} , ...} Permite importar variables del programa principal a un procedimiento cerrado con CLOSED. Solo puede ser usado dentro del cuerpo del procedimiento, de lo contrario da error. Ejemplo: IMPORT alfa, n$() Ejemplo de uso de procedimientos PROC: 10 PRINT "programa ejemplo de COMAL" 20 REPEAT 25 EXEC writeoptions 30 INPUT "Opcion a elegir": Opci 40 INPUT "X="; X 50 INPUT "Y="; Y 60 EXEC auswahl(Opci, X, Y) 70 UNTIL opci=5 80 PROC writeoptions 90 PRINT "1. Suma" 100 PRINT "2. Resta" 110 PRINT "3. Producto" 120 PRINT "4. Division" 130 PRINT "5. Fin" 140 ENDPROC writeoptions 150 PROC auswahl(A,B,C) 160 CASE A OF 170 WHEN 1 175 PRINT "Suma ="; B+C 180 WHEN 2 185 PRINT "Resta ="; B-C 190 WHEN 3 195 PRINT "Producto ="; B*C 200 WHEN 4 205 PRINT "Division ="; B/C 210 WHEN 5 215 PRINT "Fin" 220 OTHERWISE 225 PRINT "Falsa Opcion" 230 ENDCASE 240 ENDPROC auswahl Comandos gráficos: Estas sentencias de programa pueden ser usadas también como comandos directos. CLS Borra la pantalla BORDER sintaxis: border n Fija el color del borde a 'n'. PAPER sintaxis: paper n INK sintaxis: ink n Fijan el color para el fondo de la pantalla y la tinta del priemr plano. FLASH sintaxis: flash n BRIGHT sintaxis: bright n INVERSE sintaxis: inverse n donde n = 0/1 Activan (1)/desactivan (0) el parpadeo, brillo y modo de video inverso. OVER sintaxis: over n Activa (1)/desactiva (0) la posibilidad de sobre imprimir caracteres o puntos de alta resolución. PLOT sintaxis: plot {attr;} x,y donde: attr = atributos de impresión: INK, PAPER, FLASH, INVERSE, BRIGHT y OVER igual que PRINT x,y = las coordenadas absolutas en alta resolución Traza un punto en modo alta resolución. Usa la misma sintaxis que en el Basic Sinclair. DRAW sintaxis: draw {attr;} x,y {,z} dibuja una linea con coordenadas relativas 'x,y' y con giro de angulo 'z'. CIRCLE sintaxis: circle {attr;} x,y,r dibuja circulo en las coordenadas absolutas 'x,y' con radio 'r'. FILL sintaxis: fill {attr;} x,y Rellena áreas de figuras cerradas, con el color de papel o tinta especificados por 'attr'. Comandos de disco: OPEN sintaxis: open #n,x$ donde: n = flujo de canal del 0 al 12. Otros valores dan error. x$ = nombre de canal "k","s" o "p". Otros nombres dan error. Designa un flujo 'n' a un canal de dispositivo 'x$'. CLOSE sintaxis: close {#n} Cierra el canal especificado 'n'. Sin parametros cierra todos los canales. CAT FORMAT MOVE ERASE Ninguno de estos comandos es admitido por la versión de cassette. Funciones de cadena: VAL x$ - extrae el valor numérico de una cadena x$ ORD x$ - da el código asci de del primer caracter en x$, 0 si x$="" LEN x$ - da el numero de caracteres de largo de una cadena CHR$ x - da el caracter cuyo codigo es x STR$ x - convierte numero a cadena KEY$ - lee el teclado y devuelve el caracter de la tecla pulsada SCREEN$ (x,y) - da el caracter que se halle en la pantalla en las coords. AT x,y VAL$ x$ - como VAL, pero devuelve una cadena. Función típica del Spectrum. x$ ({x} TO {y}) - tipica formula Sinclair para fragmentar una cadena x$ Funciones numéricas: ABS x - valor absoluto ACS x - arco coseno de x ASN x - arco seno ATN x - arco tangente COS x - coseno EXP x - da el número e elevado a la x INT x - parte entera de x LN x - logaritmo natural de x RND {*x} - genera un valor aleatorio. SGN x - devuelve -1,0,1 dependiendo del signo del argumento x SIN x - seno SQR x - raiz cuadrada de x TAN x - tangente PI - ACS(-1) POINT (x,y) - da 1 si el pixel en alta res. 'x,y' es tinta, o 0 si es papel ATTR (x,y) - da el atributo de las coords. AT x,y de impresión PEEK x - lee el contenido de la dirección de memoria x IN x - lee el dato del port x USR x - llama a la subrutina de código máquina o da la dirección de un UDG BIN b - permite ingresar numeros binarios y devuelve su valor decimal Mensajes de error: En Comal hay dos tipos de mensaje de error: los que son producidos por un comando directo y los que se dan durante la ejecución de un listado de programa. Estos últimos añaden "in line" al mensaje más el número de línea en que el error se ha dado. CASE value not found - no se encuentra el valor para CASE Variable not found - variable no encontrada Subscript wrong - subindice erróneo Out of memory - fuera de memoria Out of screen - fuera de la pantalla Number too big - número demasiado grande Looping error - error de bucle End of file - fin de fichero STOP in line - Stop en la línea ... Invalid argument - argumento no válido Integer out of range - entro fuera de rango Nonsense in COMAL - sin significado en Comal BREAK - se ha interrumpido un proceso de dispositivos o un escrolado de pantalla Out of DATÁ - no quedan datos Invalid file name - nombre de archivo no válido No room for line - no hay espacio para la línea ... Undefined procedure - el procedimiento no ha sido definido Invalid I/O device - dispositivo de entrada/salida no válido Invalid colour - color no válido BREAK in line - se interrumpió en la línea ... Invalid line number - número de línea no válido Invalid stream - serie de canal no válido Undefined function - función no definida (FN sin DEF) Parameter error - parámetro erroneo Tape loading error - error de carga en la cinta Francisco León zx_if1@hotmail.com