
Es difícil de creer, pero a veces los errores en los procesadores esencialmente viven más que los procesadores mismos. Recientemente me convencí de esto con el ejemplo del microprocesador 1801BM1A de 16 bits, sobre la base de la cual se creó la familia BK-0010 / 11M de computadoras domésticas en la URSS. Sobre esta familia en Habré escribió repetidamente.
El período de la vida activa de BeKashek cae a fines de los años 80, principios de los 90 del siglo pasado. En estos años, a través de los esfuerzos de numerosos entusiastas individuales, así como de grupos de miembros del círculo y cooperadores, se desarrolló la gama principal de programas de aplicaciones de BC: juegos, utilidades, varios "DOS" (sistemas operativos de disco). Paralelamente al desarrollo del software, se crearon periféricos bajo los cuales se escribió el software de su sistema. En general, el ecosistema de estas computadoras tipo PDP de 16 bits se desarrolló de acuerdo con principios similares, como, por ejemplo, las primeras arquitecturas abiertas de 8 bits basadas en Intel 8080 y el bus S-100 desarrollado. Más tarde, a medida que nos alejamos del papel utilitario del CD, el enfoque en la programación se ha desplazado hacia el demoscene.
El volumen de software para BC se puede estimar visitando sitios públicos con colecciones de programas . Por supuesto, en comparación, por ejemplo, con el ZX-Spectrum, este volumen es mucho más modesto. Sin embargo, incluso ese volumen, al parecer, debería haber sido suficiente para sortear todos los rincones imaginables y grietas del código de máquina. ¿Es posible encontrar algo inusual en el comportamiento del procesador, después de más de treinta años de práctica en su uso? Al final resultó que sí! Esto se discutirá a continuación.
Quizás tenga sentido contar esta historia en orden cronológico. En primer lugar, debo señalar de inmediato que no soy en absoluto un "programador con experiencia", ni por ocupación, ni por pertenecer a una cohorte de entusiastas de la Columbia Británica, sobre quienes escribí anteriormente. Llegué a BC de forma indirecta, en parte por nostalgia de los pasatiempos de la infancia y la juventud (electrónica analógica y digital, la revista Young Technician , UT-88 y otras artesanías e imperfecciones), y en parte por mi interés en la arquitectura y el sistema de comando PDP-11 . No tengo BK "en hardware" y generalmente ejecuto programas para BK y lo depuro en el emulador de bkemu en una tableta para Android.
Hace algún tiempo, me interesé en el programa Kaleidoscope, escrito por Li-Chen Wang-a . El programa fue escrito en código de máquina en 1976, para un microprocesador Intel 8080 como parte de una computadora Altair 8800 con un adaptador de gráficos Cromemco Dazzler . Quería analizar el algoritmo Li-Chen Wang-a en detalle y, al mismo tiempo, transferirlo al BC. Hay que decir que el deseo de portar el caleidoscopio a BC se expresó anteriormente entre los demosceners, e incluso hubo intentos de analizar el algoritmo, pero no tuvieron éxito.
En mi próximo artículo, probablemente analizaré este algoritmo en detalle (y para los impacientes, publicaré un enlace a las fuentes del Caleidoscopio multiplataforma bajo libSDL en C). En el futuro, será suficiente para indicar que el problema se resolvió y que el caleidoscopio se transfirió con éxito a BC. Además, se agregó la generación de sonido al algoritmo en el CD, y, dado que tanto la imagen como el sonido son generados por el mismo código, podemos decir que la imagen en sí suena (toda la demostración cabe en menos de 256 bytes de código de máquina, y, Espero que se presente al público en CAFe Demoparty 2019 en Kazan a fines de octubre).
Después de terminar de escribir y depurar mi programa en el emulador, recurrí a Damir ("Adamych") Nasyrov (es uno de los organizadores de CAFe Demoparty y una persona muy conocida entre los demosceners) con una solicitud para verificar la ejecución del programa en un BC real. Estaba especialmente interesado en la reproducción de sonido, ya que los tiempos en el emulador podrían diferir de los tiempos en el hardware real. Imagine mi decepción cuando Damir me informó que hay una imagen en un BC real, ¡pero no hay sonido!
Las siguientes noches las pasé tratando de restar de la documentación del sistema en el BK-0011M y el diagrama del circuito , donde podría haber un error con el sonido. El sonido en el BC se organiza de manera bastante simple: el sexto bit en el registro de E / S con la dirección octal 177716 (registro de control de la grabadora) se emite a través de un búfer a un altavoz piezoeléctrico (beeper). Además de la sexta categoría, los bits 2 y 5 del mismo registro están conectados al convertidor digital a analógico más simple con 4 resistencias. Desde la salida de este convertidor, el sonido puede ir a la grabadora. Todo es excepcionalmente claro y lógico, pero no hubo un sonido obstinado en un BK real, independientemente de las combinaciones de máscaras de bits que intenté aplicar a la salida de datos a este registro. Paralelamente, todos los emuladores BK que conocía se instalaron y probaron, ¡y el sonido funcionó en todos!
En algún momento, incluso casi logré convencer a Damir de que su BK era defectuoso, pero el comportamiento se repitió en otro BK-0011M en vivo, así como en BK-0010. Se me acabaron las ideas, y los habitantes del canal de telegramas sobre el tema BC tampoco pudieron decir nada ... Sin embargo, el incidente ayudó, como siempre. En el curso de uno de los experimentos, Damir lanzó una demostración en el emulador para asegurarse de que haya sonido en el emulador. ¡Y aquí se dio cuenta de que no solo hay sonido en el emulador, sino también en el BC, sino que también las imágenes en el emulador y en el BC en vivo son diferentes! Aquí debo recordarles que en mi programa, tanto la imagen como el sonido son generados por el mismo código. En consecuencia, todo este tiempo estaba buscando una razón en el lugar equivocado: la razón estaba en el código que generaba los datos para el contenido de la pantalla.
Damir me envió una captura de pantalla, y quedó claro que el algoritmo produce bytes con contenido cero de los 4 bits más altos y, por coincidencia, estos bits se emitieron al sonido (es decir, siempre ceros). Sin embargo, la razón por la cual el algoritmo se comportó de esta manera siguió siendo vaga. Este es el lugar en el código (ensamblador macro11 de PDP-11, registra r0-r5 renombrado):
; renamed registers a = %0 b = %1 c = %2 d = %3 e = %4 h = %5 ... ... asr b ; sets CF bic #177760, b bis b, c bis (h)+, c ; screen address in c movb (c), a ; get a byte from screen RAM bcc 1$ ; check CF bic #177760, a ; keep bits 0-3, clear rest bisb d, a ; fill bits 4-7 br 2$ 1$: bic #177417, a ; keep bits 4-7, clear rest bisb e, a ; fill bits 0-3 2$: ... ...
Por alguna razón, en un BC real, siempre se realizó un salto condicional en la marca de $ 1. Es decir, la instrucción bcc siempre percibió el indicador de acarreo como reinicio, aunque la instrucción de cambio de ASR podría establecer este indicador en 0 o 1. ¿Cómo podría ser esto ?, porque de acuerdo con la documentación del procesador, ni BIC, ni BIS, ni MOVB debería afectar la bandera de transporte?
Además, en todos los emuladores (¡que se escribieron de acuerdo con la documentación del procesador!) Es así: estas instrucciones no tocan el indicador C. Se hizo evidente que el procesador real 1801BM1A no funciona en este caso de acuerdo con la documentación. Queda por confirmar esto.
Para empezar, una solución rápida obvia:
... asr b ; sets CF mfps -(sp) ; store PSW on stack bic #177760, b bis b, c bis (h)+, c ; screen address in c movb (c), a ; get a byte from screen RAM mtps (sp)+ ; restore PSW from stack bcc 1$ ; check CF ...
Guardar banderas en la pila inmediatamente después de la instrucción de cambio y restaurarlas antes del salto condicional resolvió el problema de inmediato, lo que demostró que estaba en el camino correcto. Queda por reducir el "círculo de sospechosos". Para probar la hipótesis, se escribió primero una prueba sintética de este tipo (aquí no se cambió el nombre de los registros; se omitió la inicialización inicial para no saturar el código; emt 64 es una interrupción del programa para imprimir una línea):
... mov #1, r1 jsr pc, test clr r1 jsr pc, test halt test: mov #40000, r2 ; r2 points to screen RAM mov #dummy, r5 ; r5 points to dummy = 200 ; *** begin *** asr r1 ; affects CF bic #177760, r1 bis r1, r2 bis (r5)+, r2 movb (r2), r0 ; *** end *** jsr pc, prt rts pc prt: mov #msg1, r0 bcs l1 mov #msg2, r0 l1: emt 64 rts pc msg1: .asciz /Flag CF set/ msg2: .asciz /Flag CF clear/ dummy: .word 200 ...
Y la prueba ... ¡no funcionó! Programa impreso en pantalla
Marcar CF set
Marcar CF claro
¿Qué resultó? Resultó que la suposición inicial de que el fragmento de código entre el comienzo y el final solo estropea la bandera C es incorrecta y debe aclararse. ¿Cuál es la diferencia entre esta prueba y el código fuente? Y el hecho de que aparecieron otras instrucciones entre el bloque de comandos "sospechosos" y el salto condicional. No afecta al indicador C, pero cambia el estado interno del procesador. Por lo tanto, la siguiente prueba fue así:
... mov #1, r1 jsr pc, test clr r1 jsr pc, test halt test: mov #40000, r2 mov #dummy, r5 ; *** begin *** asr r1 ; affects CF bic #177760, r1 bis r1, r2 bis (r5)+, r2 movb (r2), r0 bcc l1 ; *** end *** mov #msg1, r0 emt 64 rts pc l1: mov #msg2, r0 emt 64 rts pc msg1: .asciz /Flag CF set/ msg2: .asciz /Flag CF clear/ dummy: .word 200 ...
Y ahora esta prueba ya se ha impreso en un BK-0011M real:
Marcar CF claro
Marcar CF claro
En el emulador, como antes,
Marcar CF set
Marcar CF claro
Además es una cuestión de tecnología. Mediante simplificaciones graduales, se obtuvo una prueba mínima en la que se reproduce un error (cito toda la fuente):
.title test .psect code .=.+1000 mov #15, r0 emt 63 sec jsr pc, test clc jsr pc, test halt test: movb r0, r0 bcc l1 mov #msg1, r0 emt 64 rts pc l1: mov #msg2, r0 emt 64 rts pc msg1: .asciz /Flag CF set/ msg2: .asciz /Flag CF clear/ .end
En un BK-0011M real, esta prueba muestra
Marcar CF claro
Marcar CF claro
Es decir, la instrucción MOVB que estaba directamente frente a la instrucción de bifurcación condicional era la culpable, y la aparición del primer operando no es importante. Si, por ejemplo, se inserta NOP entre MOVB y BCC, el comportamiento volverá al documentado y el programa imprimirá
Marcar CF set
Marcar CF claro
Eso hizo posible formular una hipótesis refinada (me cito de un canal de telegramas):
... Con respecto al error: el comportamiento parece haberse aclarado. Como me imagino, MOVB src, dst (por cierto, parece que los operandos no son importantes), debido a algunas características arquitectónicas, estropea temporalmente la bandera C dentro del procesador, pero no fatalmente, porque el porcentaje parece guardar una copia de esta bandera. Como resultado, si entre el MOVB y la rama condicional hay otros comandos (que no afectan a C), por ejemplo, NOP, entonces el comportamiento es como se describe en la documentación.
¿Qué pasó después? Además, los colegas del canal ayudaron a llevar a Vyacheslav (@ K1801BM1, el hombre legendario que previamente invirtió este procesador en el nivel del transistor) a la discusión. La reacción de Vyacheslav (Yuot) cuando probó el comportamiento en un stand con un 1801BM1A real (ortografía y puntuación preservadas):
Stanislav Maslovski:
se necesitan al menos dos comandos para la reproducción
movb y salto condicional en C
Bueno, antes de eso, establezca la bandera C en un estado conocido
Yuot:
Se obtiene la bandera con siempre restablecer
Stanislav Maslovski:
si
ahora inserta nop
Yuot:
Ahora nunca
Yuot:
Alternando 0 1
Esto es una pena
Con la ayuda de Vyacheslav, se descubrieron los detalles, a saber, que la razón del error es que en el procesador, además de la PSW, hay otro registro de 4 bits, que normalmente almacena una copia de las banderas de la PSW. Este registro está conectado con el firmware automático y las transiciones condicionales toman los valores del indicador de él. Al ejecutar las instrucciones MVB, SWAB, MFPS con el registro del receptor, debido a las peculiaridades del procesamiento de la extensión del signo y debido a un error en el microcódigo, se descarta una copia del indicador C en este registro y las transiciones condicionales que utilizan este indicador no funcionan correctamente. Sin embargo, siguiendo las instrucciones a continuación, el valor de registro temporal se restaura desde el PSW. Por eso, la inserción de NOP restaura el comportamiento correcto.
En conclusión, también me gustaría agradecer a los suscriptores del canal de telegramas BK0010 / 11M World por participar en la discusión de este error y por los comentarios realizados sobre el texto del artículo. La foto del título del artículo es cortesía de Manwe_SandS . Más interesante aún, ¡Manwe estaba cerca de descubrir el mismo error, casi al mismo tiempo que Damir y yo estábamos luchando por resolver el problema de sonido!
Ahora depende de lo pequeño (es broma): alinee todos los emuladores con el comportamiento real del procesador. Después de todo, el procesador en sí, por desgracia, ya no se puede reparar.
En esto terminaré. Espero que haya sido interesante.