Basic 8 ROM para Commodore 128

Pensando en estrenar una MiniPro TL866 me propuse grabar una function ROM para la Commodore 128 de mi viejo. Alguna vez habíamos charlado sobre la posibilidad de poner el Basic 8 en el zócalo interno, así que probé la versión disponible en internet con el emulador VICE antes de grabar nada. El resultado fue decepcionante.

La versión en cuestión se activa manteniendo pulsada la tecla Control al encender o reiniciar. A partir de ahí muestra el copyright habitual de la C128, va en busca de un sector de arranque, luego muestra un cartelón de Basic 8 y, finalmente, muestra un copyright con el agregado de la referencia a WalruSoft. Todo el proceso lleva casi 9 segundos.

Para peor, se cuelga si es activada en modo de 40 columnas, y no funciona como ROM externa, es decir que no puede colocarse en un cartucho ni en el zócalo disponible en las expansiones de memoria de Commodore. Seguro de que existe una manera de hacerlo mejor, me puse a refrescar mis conocimientos sobre el tema con la invaluable ayuda del 128 Internals (Abacus), Mapping the Commodore 128 (COMPUTE! Publications) y especialmente EPROM’s Programmer Handbook (CMS).

CONSIDERACIONS GENERALES

Lo primero es comprender que una function ROM en la C128 puede activarse en dos momentos diferentes durante el ciclo de arranque: al comienzo de la rutina RESET o más tarde, durante el inicio del BASIC, por la rutina PHOENIX. Para ello existen dos vectores al comienzo de la ROM —denominados Coldstart y Warmstart— a los que se accede mediante un JSR. Esto permite a una ROM tomar el control de la máquina o realizar alguna tarea preparatoria y continuar el arranque devolviendo el control mediante un RTS. Que una ROM se active dependerá de su ID, que es el siguiente byte presente a continuación de los vectores. Si es cero, el Kernal no la activará nunca, aunque es posible acceder a ella manualmente luego de terminado el arranque.

La cosa se pone interesante cuando el ID es distinto de cero. En ese caso, la rutina PHOENIX cederá el control a la ROM mediante un JSR al vector Coldstart. Si ésta le devuelve la gentileza, intentará como último paso cargar un sector de arranque desde la unidad 8 y, en caso de no encontrarlo, dará por finalizada su tarea con un RTS, lo que nos deja en manos del editor de BASIC. Sobre este punto volveremos para evitar que se intente cargar el sector de arranque cuando optemos por activar Basic 8.

Dijimos que una function ROM podía activarse también tempranamente durante la rutina RESET. Esto sólo ocurrirá si dicha ROM lleva un ID igual a 1. De aquí surgen dos cuestiones a tener en cuenta. La primera y principal es que si la ROM no toma el control al ser activada durante RESET, volverá a ser activada por PHOENIX. Y la segunda es que en ambos casos se utilizará el vector Coldstart, es decir que Warmstart no es utilizado en ningún caso. Quizás la intención original haya sido que RESET llamase a Coldstart y PHOENIX a Warmstart, pero es sólo una conjetura.

VOLVER A LA FUENTE

Existieron dos ediciones de Basic 8. Primero fue publicado por Patech Software en 1987 (aunque los copyrights de WalruSoft dicen 1986) y luego en 1989 por Free Spirit Software, sin diferencias a la vista (sólo el mensaje de copyright de la aplicación WIOS dice ahora 1986,89). El código en sí fue levemente alterado con un parche que se carga en $B00 ("p.basic8.patch") y dos saltos desde el código principal $B00 y $B10. Increíblemente, ¡la versión para ROM disponible en internet tiene el código actualizado pero no el parche! Un cuelgue garantizado al momento en que se intente acceder a alguno de esos cambios.

El cargador original ("BOOT ED") está escrito en BASIC y carga tres bloques de código: el bloque principal (a partir de $1300), el parche (a partir de $0B00) y las imágenes de los punteros del ratón (a partir de $0E00). En el camino, cambia la definición de algunas teclas de función, invoca el cartelón de Basic 8 mediante la orden @WALRUS,0 e imprime un mensaje de copyright donde agrega la mención a WalruSoft. Un detalle antipático es que, además de poner el editor en modo mayúsculas/minúsculas, intenta desactivar la posibilidad de revertirlo manualmente mediante la impresión de CHR$(8). Dicho código produce el efecto deseado, ¡en la Commodore 64! Para la Commodore 128, el correcto habría sido CHR$(11).

Con toda esta información, podemos pensar en qué queremos lograr con esta ROM nueva. A mi modo de ver, optar por utilizar Basic 8 implica que no vamos a cargar otra aplicación, por lo que el acceso al sector de arranque debe ser anulado. Tampoco es necesario el cartelón de WalruSoft cada vez que usemos el editor, y a éste es mejor dejarlo en mayúsculas, como estamos acostumbrados a usarlo. El hecho de que Basic 8 cuelgue en modo de 40 columnas es propio de su código, así que para evitarlo vamos a hacer que la ROM sólo pueda activarse estando en modo de 80 columnas, y pulsando la tecla Alt al encender o reiniciar.

Dado que Basic 8 es un agregado al BASIC 7, vamos a dejar que nuestra ROM arranque durante PHOENIX, cargue el código como si lo hubiésemos cargado desde diskette y devuelva el control. Para eso necesitamos escribir una rutina que copie los tres bloques de código máquina, más el cargador en BASIC, modificado a nuestro gusto, y luego coloque la secuencia "rU"+CHR$(13) para que al volver al editor de BASIC lo active por nosotros. El último paso restante es el de anular el arranque desde diskette. Resulta que no está previsto en la rutina PHOENIX pasar por alto esa secuencia, pero si observamos el comportamiento, surge una solución elegante. Dijimos que PHOENIX cede el control a nuestra ROM mediante con JSR; nosotros lo devolveremos a su vez con RTS, PHOENIX buscará un sector de arranque y —si no lo encuentra— cederá el control al editor de BASIC con otro RTS. La respuesta está, entonces, en quitar de la pila la dirección de retorno a PHOENIX, para que nuestro RTS vaya directo al editor. A fin de evitar confusiones, actualizé también el mensaje «copr. 1986» en el cartel que genera la instrucción @WALRUS por «(c)1986,89». Los demás detalles pueden leerse en el código que incluyo a continuación.

* = $8000
            nop                 ;vector coldstart
            nop
            nop
            jmp     start       ;vector warmstart
            !byte   2           ;ROM ID (activar durante PHOENIX)
            !text   "CBM"
start       bit     $d7         ;estado de la tecla 40/80 columnas
            bpl     return
            lda     $d3         ;estado de las teclas de control
            and     #$08        ;Alt pulsada?
            beq     return
            sei
            lda     $ff00       ;quitar I/O del medio
            ora     #$01
            sta     $ff00
            lda     #$00        ;mover bloque principal a $1300
            sta     $fb
            sta     $fd
            lda     #>code
            sta     $fc
            lda     #$13
            sta     $fe
            ldx     #$5e        ;5d páginas de código máquina +1 de BASIC
            ldy     #$00
loop_pge    lda     ( $fb ), y
            sta     ( $fd ), y
            iny
            bne     loop_pge
            inc     $fc
            inc     $fe
            dex
            bne     loop_pge
            ldy     #$00        ;mover punteros del ratón a $E00
loop_ptr    lda     pointers, y
            sta     $e00, y
            iny
            bne     loop_ptr
            ldy     #$1f        ;mover el parche a $B00
loop_ptc    lda     patch, y
            sta     $b00, y
            dey
            bpl     loop_ptc
            lda     #$70        ;cambiar el comienzo del BASIC ($7001)
            sta     $2e
            lda     #$52        ;poner la secuencia "rU" + RETURN en el buffer del teclado
            sta     $34a
            lda     #$d5
            sta     $34b
            lda     #$d
            sta     $34c
            lda     #$3
            sta     $d0
            pla                 ;quitar la dirección de retorno de PHOENIX de la pila
            tax                 ;y así pasar directo al editor
            pla
            tay
            pla
            pla
            tya
            pha
            txa
            pha
return      rts

* = $8100
code        !binary "p.basic8.prg",$5d00,2 ;código principal seguido del cargador BASIC
            !byte $00,$27,$70,$0A,$00,$8F,$20,$42
            !byte $41,$53,$49,$43,$20,$38,$20,$46
            !byte $55,$4E,$43,$54,$49,$4F,$4E,$20
            !byte $52,$4F,$4D,$20,$42,$59,$20,$44
            !byte $49,$45,$53,$54,$52,$4F,$00,$35
            !byte $70,$14,$00,$FE,$25,$3A,$9E,$20
            !byte $34,$38,$36,$34,$00,$5E,$70,$1E
            !byte $00,$F9,$20,$31,$2C,$22,$40,$54
            !byte $45,$58,$54,$3A,$46,$41,$53,$54
            !byte $22,$AA,$C7,$28,$31,$33,$29,$3A
            !byte $F9,$20,$34,$2C,$22,$22,$3A,$F9
            !byte $20,$38,$2C,$22,$22,$00,$97,$70
            !byte $28,$00,$99,$20,$22,$93,$11,$22
            !byte $3B,$A6,$32,$33,$29,$3B,$22,$9F
            !byte $57,$41,$4C,$52,$55,$53,$4F,$46
            !byte $54,$20,$9A,$42,$41,$53,$49,$43
            !byte $20,$38,$9F,$20,$31,$30,$30,$38
            !byte $36,$31,$20,$42,$59,$54,$45,$53
            !byte $20,$46,$52,$45,$45,$22,$00,$BE
            !byte $70,$32,$00,$99,$20,$A6,$32,$38
            !byte $29,$3B,$22,$28,$43,$29,$31,$39
            !byte $38,$36,$2C,$38,$39,$20,$57,$41
            !byte $4C,$52,$55,$53,$4F,$46,$54,$20
            !byte $49,$4E,$43,$2E,$22,$00,$C4,$70
            !byte $3C,$00,$A2,$00,$00,$00
            !align $ff,0,$ff
pointers    !binary "p.ptrdefs.prg",$100,$302 ;punteros para el ratón
patch       !binary "p.basic8.patch.prg",$20,2 ;parche edición 1989
            !align $ffff,$ffff,$ff

* = $ffff   !byte $ff

* = code + $474d 
            !pet "(c)1986,89" ;actualizar el mensaje del comando @WALRUS
            !to "Basic 8 [DST].bin", plain

Descargar / Download