Mis años adolescentes: Portando Small-C a transputer y desarrollando mi sistema operativo
No se trata de un compilador de C moderno que arranca en un procesador reciente. En lugar de eso, leerás sobre cómo puse en marcha un compilador limitado de C en un procesador transputer de 1987, al mismo tiempo que desarrollé un sistema operativo básico, un editor de texto y un ensamblador. Esto sucedió en 1995, cuando tenía 16 años.
Este artículo es posible porque estaba tan orgulloso de mi logro que guardé la mayoría de los archivos originales y el código fuente en un disquete. De hecho, me había olvidado por completo de ello, salvo por mis buenos recuerdos sobre como porté el compilador de C y como hice sofisticado mi sistema operativo de 32 bits, hasta que hace poco hice un emulador de transputer y estuve mirando mis archivos. Para mi total sorpresa, encontré una versión bastante temprana de mis herramientas. Pero vayamos por partes.
El C es muy difícil
Ahora un tema común en Reddit. Aprendí de la forma difícil que saltar del lenguaje BASIC a un lenguaje más moderno no era tan fácil como pensé. Tuve grandes problemas pasando del lenguaje BASIC orientado a línea a la programación estructurada con los lenguajes Pascal y C.
Acostumbrado a leer el código fuente de arriba para abajo, me tomó mucho tiempo comprender que Pascal comienza por el final, mientras que en C la función main() puede aparecer en cualquier parte. Peor, el estilo de forma libre de ambos lenguajes me volvía loco porque estaba demasiado fijo en tener una sentencia IF en una sola línea.
Intente muchas veces leer el libro El Lenguaje de Programación C por Brian Kernighan y Dennis Ritchie. Tal vez tan pronto como 1989 cuando apenas tenía 10 años. A pesar de que podía entender el programa de "Hola Mundo", el siguiente programa que escogieron para mostrar el lenguaje era terrible para aprender (la tabla de conversión de Fahrenheit a Celsius) solo porque era demasiada información para que pudiera procesarla mi joven cerebro. No ayuda que el siguiente ejemplo pusiera el mismo programa en solo dos líneas, un ejemplo temprano de C ofuscado.
No fue hasta que comprendí Pascal que pude volver al C y finalmente me iluminé. Las llaves son el BEGIN y END de Pascal, los diagramas de sintaxis no tienen que estar en cajas bonitas (como Pascal) pero pueden ser ordenados en líneas con referencias recursivas (esto le dio sentido espectacular al apéndice A de The C Programming Language)
El compilador de Small-C
Mi padre en los ochentas básicamente compró cada libro bueno de técnica y programación disponible en la librería local American Book Store ubicada en Ciudad Satélite, justo al norte de la Ciudad de México. Ellos traían libros técnicos norteamericanos a México, incluso si su línea de negocios principal eran textos estudiantiles en inglés para escuelas locales. Recordemos que no existía Internet en ese tiempo, la diseminación del conocimiento era exclusivamente a través de libros, revistas y boletines. Si quería aprender algo acerca de programación, había una gran oportunidad de que un libro de la biblioteca de mi padre tenía suficiente información para darme una buena pista para aprender. Una vez que descubrí que el inglés era el requisito principal, lo aprendí por mi mismo leyendo artículos enteros de revistas Compute! (disponibles en cadenas de tiendas Sanborns), y usando como referencia un diccionario inglés a español.
Al mismo tiempo iba descubriendo las similitudes entre Pascal y C, y encontré el artículo "A Small C compiler for the 8080s" en un libro nombrado curiosamente Dr. Dobb's Programming Journal volumen 5. ¿Quién era el Dr. Dobb? Ni idea, pero me pareció divertido que un tipo llamado Dr. Dobb escribiera sus desarrollos de programación, pero no encontré ningún artículo escrito por el tal Dr. Dobb. En lugar de eso el compilador de C estaba firmado por Ron Cain (quien escribió un gran artículo acerca de su inspiración para Small-C). Por supuesto, Dr. Dobb era una broma de la revista.
Mi cabeza explotó. Este era un compilador de C escrito en el lenguaje C ¡y era capaz de compilarse a si mismo! No era para nada como el lenguaje BASIC, los intérpretes de BASIC se escriben en código máquina, y hasta este día no he oído de compiladores de BASIC escritos en BASIC. Además, la idea de portabilidad: Escribir un programa en C y ejecutarlo en múltiples máquinas, era como si alguien me dijera que todos mis programas podían correrse en todas las computadoras del mundo, era el santo grial (pero por supuesto, no era cierto).
Sin embargo, me lancé en una cruzada para ejecutar este compilador de C en la computadora homebrew Z280 construida por mi padre. Esta computadora acababa de entrar en funcionamiento alrededor de 1991. En este punto, ya tenía un sistema operativo primitivo escrito en código máquina Z280, ofreciendo un directorio, tabla de asignación, y podía escribir y leer archivos completos.
Siendo más hábil en programación, mi primer intento fue teclear todo el compilador de C en una computadora Televideo antigua corriendo MS-DOS y Turbo C. Me tomó días porque el tipo de letra del código fuente era ilegible en partes. Una vez que funcionó, procedí a escribir un editor de texto para la computadora Z280 usando código máquina, y también un ensamblador Z280 para procesar el código generado por Small-C.
En algún punto de inicios de 1992, logré ejecutar el compilador de Small-C en la computadora Z280, completo con editor y ensamblador. Quedé algo decepcionado, porque el código generado era poco óptimo, y tenía que escribir rutinas de apoyo para todo. Peor, tenía apenas compatibilidad con otro código C porque le faltaba mucha sintaxis del lenguaje C real.
El verano del transputer
Era 1992 cuando mi padre construyó el tablero de transputer de 32 bits. Porté el compilador de Tiny Pascal para este, e inspirado por Small-C, reescribí el compilador en Pascal, y llegué tan lejos como para crear un compilador casi completo de Pascal. El transputer tenía la gran ventaja de tener un espacio de memoria lineal, facilitando el desarrollo de compiladores. Además, yo acababa de dominar la generación de árboles de expresiones así que por primera vez podía tener un código optimizado razonable. Puedes leer el artículo completo sobre este compilador de Pascal que hice en 1993.
Con el Pascal dominado, decidí que podía proveer al transputer con un compilador de C, así que volví al Small-C. Decidí que ya había perdido mucho tiempo (ya era 1995), así que hice una conversión rápida al ensamblador del transputer. Dada mi experiencia previa logré hacerlo en dos días, de nuevo usando Turbo C en MS-DOS como base.
Cambié todos los tamaños de palabra de int a 4 bytes, y reemplacé todo el código ensamblador 8080 con código ensamblador de transputer. Aquí hay un breve ejemplo de como trasladé la generación de ensamblador.
/* Add the primary and secondary registers */
/* (results in primary) */
zadd()
{ ol("add");
}
/* Subtract the primary register from the secondary */
/* (results in primary) */
zsub()
{
ol("rev");
ol("sub");
}
/* Multiply the primary and secondary registers */
/* (results in primary */
mult()
{ ol("mul");
}
/* Divide the secondary register by the primary */
/* (quotient in primary, remainder in secondary) */
div()
{ ol("rev");
ol("div");
}
El siguiente paso era correrlo en el transputer. Esta primera versión era grande y lenta (aunque 21 kb. de código no era tan grande gracias a las instrucciones RISC optimizadas del transputer), pero no me molestó, porque sabía que podía implementar el generador de expresiones en árbol y optimizar de forma similar a lo que hice en el compilador de Pascal para reducir el tamaño final del compilador. Se sintió muy bien cuando logré que el compilador de C se redujera a 16 kilobytes.
Este artículo podía haber acabado aquí, ya que pude haber hecho con el compilador de Small-C lo mismo que con el compilador de Pascal, alimentando código fuente al transputer, y recibiendo el código ensamblador de vuelta. Sin embargo, esta historia toma un camino muy diferente aquí.
Un sistema operativo holandés
Recuerdos de 1993, estaba platicando con un amigo acerca de como mi compilador de C para Z280 era muy básico, y no comprendía como implementar las características grandes como struct y union, y estaba buscando un libro que pudiera ayudarme a aprender a escribir compiladores de C.
Me trajo Operating Systems: Design & Implementation por Andrew Tanenbaum, y... quedé decepcionado: El libro no contenía un compilador de C, pero después de leerlo, encontré algo tan increíble que me abrió una ventana completa a un mundo desconocido: Un sistema operativo completo escrito en lenguaje C. El código mostrado para MINIX estaba muy lejos de mi comprensión en ese momento, pero me enseñó como funcionaban los sistemas operativos, sus conceptos básicos y la diferencia entre sistemas monolíticos y de núcleo. Tanenbaum es un gran maestro.
En ese tiempo, mi foco de desarrollo en transputer estaba basado en un programa manejador en la máquina anfitrión Z280. Estaba un poco cansado de reescribir mi manejador, cada vez necesitaba adaptarlo al programa que iba a ejecutar (similar a como el ENIAC II era recableado para cada nueva tarea). Tenía un manejador para el programa de trazado de rayos, otro para el compilador de Pascal, y otro para los programas compilados.
También estaba escribiendo un emulador CP/M alrededor de 1994. El sistema operativo CP/M tenía un concepto que me agradaba: El sistema operativo era el mismo, pero solo cambiaban las rutinas básicas de entrada y salida (BIOS) provistas por el fabricante de la computadora. Muy importante para el éxito temprano del CP/M ya que esta separación hacia posible que Digital Research vendiera el CP/M sin cambios a todos los fabricantes, cobrando $70 dólares por cada copia, y estos fabricantes estaban a cargo de escribir el BIOS sin costo para Digital Research.
Estas tres cosas se mezclaron en mi mente en 1995. Estaba escribiendo un compilador de C para correr en el transputer, pero podía hacer un sistema operativo con el subjunto del lenguaje C, podía invocar rutinas básicas de entrada y salida a través de los canales de enlace del transputer, y la computadora anfitrión podía proveer todo. El BIOS no solo iba a estar separado del sistema operativo, de hecho iba a correr en otra computadora.
De esta forma, la computadora anfitrión (el Z280) proveería acceso de disco, salida de video, salida de gráficos (todo lo que manejaba en programas separados), y el transputer sería el sistema operativo.
Por supuesto, mucha gente alrededor del mundo había tenido esta idea para arrancar sistemas, pero para mí fue una absoluta revelación.
Arrancando un sistema operativo
Comencé a codificar un nuevo programa manejador para proveer los servicios de anfitrión a través de los enlaces del transputer, y codifiqué mi primer sistema operativo de 32 bits usando el compilador adaptado de Small-C. Este sistema operativo administraría la memoria, su propio sistema de asignación de archivos, nombres de archivo (todavía usando sintaxis 8.3), y proveería manejo de archivos a los programas de usuario. Técnicamente estaba haciendo otro sistema operativo monolítico, pero no me paré a pensarlo.
Así como creaba mi sistema operativo, también hice un nuevo editor de texto escrito en lenguaje C, y me decidí por un manejo de pantalla tipo ANSI para permitir colores y desplazamiento del cursor por la pantalla. Además, recodifiqué el ensamblador de transputer en lenguaje C, y en un premio a mi ingeniosidad, el ensamblador ha permanecido casi sin cambios desde esa época.
Me tomó unas pocas semanas, pero estaba encantado cuando arranqué exitosamente la primera iteración del sistema operativo autocontenido. Uno donde podía editar el código fuente, compilarlo, y actualizar el sistema operativo sin volver a compilar cosas en la máquina anfitrión.
Arqueología en tu propio disco
Tengo el vaciado del disco flexible con los archivos para esta iteración temprana de mi sistema operativo de 32 bits. Casi nada de documentación, así que algún trabajo de arqueología digital es necesario porque de hecho olvidé como lo construí. Estos son los archivos en el disco:
Junio 9 a julio 12 fue apenas un mes para desarrollar todo esto. Para arrancar mi primer sistema operativo en disco, probablemente escribí yo mismo los primeros sectores del disco de 1.44mb usando un programa monitor que tenía en la máquina Z280. Pero de cualquier forma, estos son los pasos que estimé para traerlo de vuelta a funcionamiento:
El emulador de transputer correrá MAESTRO.CMG (MAnejo de Entrada y Salida de Transputer por Oscar)
Iniciar una imagen de disco de 1.44 mb: dd if=/dev/zero of=disk.img bs=1 count=1474560
Copiar ARRANQUE.CMG (el sector de arranque) en los primeros 512 bytes de la imagen de disco.
Construir un sistema inicial tipo FAT en el sector 2 con 160 bytes (uno por cada pista, 80 pistas x 2 lados).
Construir una imagen de directorio inicial de 10 sectores. Cada entrada mide 32 bytes.
Añadir una entrada inicial SOM32.BIN que contiene el sistema operativo básico (SOM significa Sistema Operativo Mexicano).
Añadir una segunda entrada INTERFAZ.CMG conteniendo el procesador de línea de comandos. Aquí hay un problema: La versión compilada no está en mis archivos.
Añadir los archivos EDITOR.CMG, ENSG10.CMG y TC2.CMG para completar el entorno. De nuevo falta EDITOR.CMG pero tengo el código fuente en C.
Agregar todos los archivos fuente para compilarlos dentro del entorno.
Spoiler: Resultó que no era tan fácil. Comencé por desarrollar el programa buildboot.c para crear la imagen de disco flexible en el formato de mi sistema operativo, porque estaría construyendo varias imágenes de disco mientras progresaba para hacerlo funcional.
Primeros tres sectores de una imagen de disco funcional para mi sistema operativo transputer. Mi firma de arranque es $12 $34 en el offset 510.
La estructura interna del disco fue deducida gracias al código fuente de mi sistema operativo. Ahora el estado actual de las cosas:
Necesitaba modificar mi emulador de transputer para proveer de acceso a la imagen de disco.
Necesitaba agregar varias instrucciones de transputer y un poco de "paralelismo" porque depende del intercambio de procesos del transputer.
Necesitaba reconstruir el archivo perdido INTERFAZ.CMG para proveer la línea de comandos del sistema operativo.
No podía comenzar en el paso 1 porque requería el paso 2 funcionando para probar el paso 1. Un caso del huevo y la gallina. Resulta que el manejo de procesos en el transputer es uno de sus secretos mejor guardados, y es la causa de que no emulara antes mi sistema operativo. Sin embargo, Michael Brüstle, responsable de transputer.net, tuvo la amabilidad de publicar los documentos internos del T414 donde pude ver como se manejaban las estructuras internas de procesos.
Procesos es una palabra un poco incorrecta, en el transputer esto es más como threads o hilos de ejecución. De cualquier forma, gracias a esos documentos pude emular el manejo de procesos (las instrucciones startp, endp, runp y stopp), junto con la instrucción tin en una implementación simplificada. El transputer requiere código muy complicado para ordenar los timers, pero hice el mínimo posible porque solo hay dos timers en mi sistema operativo.
Después de un rápido vistazo al código fuente, puede ver que tenía cuatro procesos en mi sistema operativo básico:
Refresco de pantalla: Toma un buffer, busca cambios y envía actualizaciones de líneas a la pantalla del anfitrión.
BIOS de nivel medio: Procesa las llamadas de BIOS y envía de regreso mensajes al sistema anfitrión, probablemente porque quería aislar los cambios en el programa manejador.
Ejecución principal: Arranque el sistema operativo y lo llama.
Dormilón: Un proceso de bajo nivel que simplemente duerme.
¿Era fácil? No realmente. Me atoré tres días intentando que lo básico arrancara con mi emulador de transputer. Para algo tan primitivo es increíblemente complicado. Logré que leyera el sector de arranque y se trabara, y eso después de descubrir que la instrucción startp no comienza un proceso, sino que lo agrega a la lista de procesos para correr y lo ejecuta después.
Todavía estaba atorado cuando noté esta secuencia de depuración:
Lee el primer byte de la firma del sector de arranque para buscar un valor $12, y en lugar de eso lee $34. Así que los datos de sector estaban desviados por un byte, porque espera que el primer byte sea el estatus de la lectura del disco. ¡No me entendí yo mismo! Una vez solucionado, saltó correctamente en el sector de arranque, y procedió a leer el sector -1... Así que otro error, pero no, resulta que yo mismo lo inserté para resetear el cache de disco en mi sistema anfitrión.
También había una diferencia desconcertante entre MAESTRO.LEN y ARRANQUE.LEN, uno estaba basado en pista/cabeza/sector, y el otro estaba basado en sectores lógicos. Sin embargo, el código fuente de SOM32.c seguía la convención de pista/cabeza/sector, y como quería compilar el sistema operativo sin cambios, encontré que MAESTRO.LEN contenía la subrutina LEESECTOR que necesitaba para la conversión pista/cabeza/sector.
Después de modificar y reensamblar ARRANQUE.CMG con ese código un poco más antiguo (y en el proceso modificar mi ensamblador de transputer para admitir cadenas entre apóstrofes y hacer otra imagen de disco), pude leer el sector 2 (con el directorio). Ahora me di cuenta de que el primer archivo que agregué fue SOM32.CMG y no SOM32.BIN como se requiere. Tuve que hacer otra imagen de disco.
Quedé encantado cuando vi que leía los sectores que componía el sistema operativo, y la emulación terminó cuando pidió otro servicio sin implementar: 08. Sitúa la forma del cursor, muy común en la era de las tarjetas gráficas CGA/EGA/VGA, donde se podía poner el cursor de texto para ser un rectángulo, o simplemente una rayita parpadeante. También implementé el servicio 07 (poner la posición del cursor), y después de hacer estos esperaba ver algo en la pantalla (porque el procesador de línea de comandos INTERFAZ.CMG aún no estaba en la imagen de disco), pero noté que crasheaba cuando intentaba cargar de nuevo el sistema operativo, y buscaba el sector -12.
El error podía esperar, pero sabía que algo debía aparecer en la pantalla. El proceso de refrescar la pantalla no servía. Noté que la comparación del timer era unsigned int, y en el lenguaje C una comparación unsigned int nunca puede ser menor a cero. Esto se solucionó con una conversión de tipos a int. Una vez que el timer funcionó, todo lo demás dejó de funcionar.
Pensé que había hecho una función para leer la memoria de 32 bits (read_memory_32) en el emulador. Pero en lugar de eso, era una macro, y no tenía parentesis. Así que ejecutaba v | v << 8 | v << 16 | v << 24 - value. ¿Puedes ver el error? Agregué parentesis al resultado de la macro, y fui recibido con este mensaje:
I?serte u? disc? c?? siste?a ?perativ?? y pu?se u?a tec?a???
¡Et voila! Estaba funcionando al fin. Estaba confundido por las letras equivocadas, pero entonces descubrí que mi rutina hexadecimal de 1995 nunca fue una rutina hexadecimal de verdad. Solo convertía valores de 0 a 15 al ASCII 48-63 y yo estaba esperando hexadecimal real en el software de mi emulador. ¡La siguiente prueba fue bien!
Inserte un disco con sistema operativo, y pulse una tecla...
Como le alimentaba una imagen de disco llena de ceros, el código esperaba la firma de arranque. Ahora podía alimentar el sistema operativo propio ¡y funcionó! Pero esperaba el archivo perdido INTERFAZ.CMG para el intérprete de línea de comandos. ¿Cómo compilarlo?
Con un pequeño truco, construí una imagen de disco con INTERFAZ.C y el compilador TC2.CMG renombrado como INTERFAZ.CMG, junto con una copia actual de TC2.CMG y ENSG10.CMG. De esta forma el sistema operativo correría el compilador de C, permitiéndome generar INTERFAZ.LEN (el resultado en ensamblador de la compilación).
Sin embargo, el sistema operativo se rehusaba a cargar INTERFAZ.CMG. Después de leer más vaciados, descubrí que el archivo fuente del sistema operativo era viejo, y el binario era un poco más reciente, tomaba el directorio del segundo bloque del disco, en lugar del sector 3. Ajusté mi generador de imágenes de disco, y después de leer dos sectores de INTERFAZ.CMG se trabó. Después de revisar miles de líneas de código de depuración, encontré que una instrucción "in" ejecutaba otra instrucción "in" incorrectamente. Pude solucionarlo contando mejor los ciclos para el timer porque la rutina de servicio causaba una condición de carrera.
Al fin, el emulador de transputer generó un archivo de 143mb de largo con líneas de depuración, cargó el compilador de C completo en la memoria, y crasheo por un servicio sin manejar.
La gran sorpresa
Fue como una cubetada de agua fría en mi cabeza cuando noté que el compilador estaba diseñado para correr como el compilador de Pascal: Sin un sistema operativo.
Necesitaba otro programa manejador para manejar la apertura de archivos, y esa era la causa de que enviara un protocolo diferente por el canal de enlace, y el crash del sistema operativo. Incidentalmente, también explicaba porque tenía tres librerías STDIO, una era para correr en la computadora anfitrión usando TC.CMG, la segunda era la misma pero para TC2.CMG (porque el prefijo de etiquetas cambió a q en vez de qz), y el tercero era el de arranque para correr programas dentro de mi sistema operativo transputer.
Después de escribir el manejador para el compilador de C en mi emulador de transputer, fui capaz de recompilar todo. Incluyendo SOM32.c porque el binario aparentemente tenía problemas para correr Interfaz.c. Esta es la forma de invocar el compilador de C desde el lado anfitrión:
Preguntará por los nombres de archivo de entrada y salida. El ensamblaje se hace con tasm. Igual, cargaba interfaz.cmg y fallaba, pero ahora tenía una imagen de disco con todo listo. Podía figurarme ahora como arranqué mi compilador de C transputer en mi propio sistema operativo para transputer:
Crear el manejar de servicio para interfazar con el anfitrión (MAESTRO.LEN)
Crear el sector de arranque en ensamblador de transputer (ARRANQUE.LEN)
Compilar el compilador de C usando Turbo-C en MS-DOS.
Compilar a si mismo en MS-DOS y ensamblar el archivo generado (TC.CMG) con la librería STDIO.LEN.
Crear un programa manejador en la máquina Z280 para manejar archivos C para el transputer.
Mejorar el compilador de C para usar el generador de árboles de expresiones (TC2.CMG), y ensamblar con STDIO2.LEN.
Compilar SOM32.c y ensamblar con MENSAJES.LEN. Usar como SOM32.BIN dentro del SO.
Compilar Interfaz.c y ensamblar con STDIO3.LEN. Usar como INTERFAZ.CMG dentro del SO.
Compilar Editor.c y ensamblar con STDIO3.LEN. Usar como EDITOR.CMG dentro del SO.
Compilar TC2.c y ensamblar con STDIO3.LEN. Usar como CC.CMG dentro del SO.
Compilar ENSG10.c y ensamblar con STDIO3.LEN. Usar como ENSG10.CMG dentro del SO.
Ahora ya se puede hacer el desarrollo dentro del sistema operativo transputer.
La pregunta principal es ¿porqué no tenía todos estos archivos listos para reconstruir un disco? La respuesta es fácil, probablemente reescribí los archivos mientras arrancaba el sistema operativo. Porque una vez que logras el último paso, no necesitas más los archivos originales porque ya puedes volar.
Me impresiona como mi yo joven se figuró que STDIO.LEN era un sistema básico de entrada y salida por si mismo, y podía ser modificado de dirigir el manejador del anfitrión a llamar los servicios del sistema operativo en el transputer.
De nuevo una diferencia de opinión entre el código del sector de arranque, la imagen de disco, y el sistema operativo. Una pequeña corrección, y estuve contentísimo de leer esto en la pantalla:
Sistema Operativo Multitarea de 32 bits. v1.00
>>>>> (c) Copyright 1995 Oscar Toledo G. <<<<<
A>
Solo me faltaba el código para entrada del teclado. Lo escribí tan rápido como pude en el emulador, e intenté teclear D (para DIR), y crasheó después de escribir la letra. Listo para leer un archivo de depuración de 182.5 mb, puedes quejarte o seguir trabajando cantando Save Your Tears (Remix).
Te vi en la habitación, una hora después (quiero decir el bug). La interrupción del timer podía suceder enmedio de una instrucción con prefijos, tuve que agregar una variable complete para dar esta información antes de aceptar timers, y la línea de comandos vino a la vida, pero ninguna orden era aceptada. Todo era interpretado como un nombre de archivo para ejecutar.
Mientras buscaba esto, hice una prueba de regresión para ver que todo el emulador funcionaba adecuadamente. Intenté compilar el compilador de Pascal nuevamente y no funcionó... Noté que fue al agregar el time slicing en las instrucciones j y lend, y que funcionaba cuando lo desactivaba. Después de una mirada cuidadosa, otra vez una macro de C fue la culpable. Invocaba la macro write_memory_32 que se expandía en 4 asignaciones de memoria diferentes, pero la referencia a la macro no estaba dentro de llaves, así que solo actualizaba el primer byte, y dañaba la lista de procesos con los otros tres bytes. Este es el código antes de la corrección:
Una vez que el emulador se compiló, esta vez pude hacer DIR en mi sistema operativo:
Sistema Operativo Multitarea de 32 bits. v1.00
>>>>> (c) Copyright 1995 Oscar Toledo G. <<<<<
A>dir
SOM32 .BIN 4,143 23-ene-19<5 10:59:00
INTERFAZ.CMG 2,369 23-ene-19<5 10:59:00
CC .CMG 16,952 23-ene-19<5 10:59:00
INTERFAZ.C 5,885 23-ene-19<5 10:59:00
EDITOR .C 17,093 23-ene-19<5 10:59:00
ENSG10 .C 28,617 23-ene-19<5 10:59:00
EDITOR .CMG 5,295 23-ene-19<5 10:59:00
ENSG10 .CMG 10,876 23-ene-19<5 10:59:00
8 archivos.
1,336,320 bytes libres.
A>
¡Éxito! El sistema operativo que desarrollé hace 30 años ya funcionaba de nuevo. Por supuesto, mi listado de directorio sufría del problema del año 2000, y el mes estaba incorrecto por causa de mi herramienta de imagen de disco.
Un poco de pulido
El emulador de transputer obtiene las pantallas de color del sistema operativo y las replica usando secuencias de escape ANSI en la terminal. Puedes ver que mi editor de texto es un homenaje a Turbo C. Puedes editar los archivos fuente, compilarlos dentro del sistema operativo, ensamblarlos, y re-ejecutar los programas. Para uso más fácil, trasladé las teclas de terminal de macOS a los códigos internos usados por mi sistema operativo para las teclas de función y de flechas.
Como alternativa, puedes compilar los archivos fuente usando el emulador de transputer, y obtener la salida directamente en tu directorio.
Usando los programas
Se puede arrancar el disco prediseñado usando esta línea de comandos o run_os.sh:
./tem -os maestro.cmg disk.img
Como alternativa se provee build_disk.sh para crear un disco con tu configuración de archivos preferida.
Ninguno de los programas usa argumentos de línea de comandos. El intérprete de línea de comandos recibe DIR, VER, MEM y FIN, e invocar ejecutables simplemente tecleando su nombre.
Editor de texto corriendo en transputer
El editor EDITOR.CMG es completamente visual, si estás usando macOS podrás editar fácilmente los archivos y apretar Fn+F1 para ver la ayuda. No lo he comprobado con Linux o Windows.
El compilador de C TC2.CMG, requiere que entres N dos veces (sin parada por errores, y no mostrar el código C), y entonces teclear el nombre del archivo de entrada (por ejemplo, TC2.C), luego el archivo de salida (por ejemplo, TC2.LEN), y oprimir Enter para volver al sistema operativo.
El ensamblador ENSG10.CMG, requiere el nombre del archivo de entrada (por ejemplo, TC2.LEN), luego el nombre de la librería (STDIO3.LEN), presionar Enter solo, y ahora el archivo de salida (por ejemplo, TC2.CMG).
En lugar de teclear todos esos nombres de archivo (yo lo hacía en 1995), puede utilizar el archivo assemble_os.sh y ensamblará todo usando mi ensamblador tasm.
Pensamientos finales
Esta versión temprana de mi sistema operativo era increíblemente pequeña. El sistema operativo se compone de 793 líneas, la interfaz de línea de comandos apenas 298 líneas, el editor 830 líneas, el ensamblador 1473 líneas, y el compilador de C el más grande con 3018 líneas.
Fue mi logro más grande de ese año, un sistema operativo puro de 32 bits trabajando con servicios de comunicación, y aplicaciones integradas auto-sustentables. Bastante más de lejos de solo hostear un compilador de Pascal y un Ray Tracer.
Por supuesto, esta historia continuó con mi sistema operativo creciendo con soporte para acceso a CD-ROM usando estándares High-Sierra e ISO-9660, mi compilador de C creció a K&R completo (incluso compilé algún C ofuscado), y mi editor obtuvo coloreo de sintaxis; pero esa será otra historia.
El código fuente para el sistema operativo transputer, intérprete de línea de comandos, editor de texto, compilador de C y ensamblador. https://github.com/nanochess/transputer