Hacking the Color BASIC PRINT command – part 2

See Also: part 1 and part 2.

Here is what I have learned so far…

When BASIC wants to output a character, it jumps to a ROM routine called CHROUT. This is one of a few documented ROM calls in Color BASIC. Here is how it is documented in the EDTASM+ manual:

I have used this ROM call in previous blog posts, showing how to output “HELLO WORLD” or whatever using assembly:

That small program makes the X register point to the first byte of the MSG data. It then loads that byte into register A and increments X. If A is 0 (the end of string marker), it branches to DONE. If not, it jumps to the subroutine pointed to at $A002 which outputs whatever is in A. It then goes back to do it again.

I wrote a two-part post about Color BASIC RAM hooks awhile ago. There I explored these extra spots where later BASIC ROMs (like Extended, DISK, and the CoCo 3 ROM) can patch in and make the original Color BASIC call new code first. That new code did things like add support for DISK devices or the (gone in CoCo 3) DLOAD command. I assume it is also used to patch in the CoCo 3’s high resolution 40/80 column text screens, as well.

The idea is before Color BASIC does its thing with the CHROUT routine, it will jump to “new code” and let it run first, and that code may then return back to let the normal code process. When disk devices were added as device #1 to device #15, this jump goes to code in Disk Extended BASIC which checks the incoming device number. If it is 1-15, new code is called that takes care of writing data to a disk file. If not, it returns back and Color BASIC handles the devices it knows about (device 0 for console, device -1 is the cassette, and device -2 if the serial/printer port).

For the task Erico was inquiring about, I thought I could patch in a new routine that would check for device 0 (writing to the screen) and then if it was, look for a special character that now means “transparent”. Instead of putting the character on the screen then moving the cursor position over by one, it would just move the cursor position and leave the screen alone.

Here is what I came up with in a quick and dirty hack:

* lwasm consmove.asm -fbasic -oconsmove.bas --map

* Convert any lowercase characters written to the
* screen (device #0) to uppercase.

DEVNUM equ $6f      device number being used for I/O
CURPOS equ $88      location of cursor position in RAM
RVEC3 equ $167      console out RAM hook

    org $3f00

init
    lda RVEC3       get op code
    sta savedrvec   save it
    ldx RVEC3+1     get address
    stx savedrvec+1 save it

    lda #$7e        op code for JMP
    sta RVEC3       store it in RAM hook
    ldx #newcode    address of new code
    stx RVEC3+1     store it in RAM hook

    rts             done

newcode
    * Do this only if DEVNUM is 0 (console)
    tst DEVNUM      is DEVNUM 0?          
    bne continue    not device #0 (console)

    * If here, device #0 (console)
    cmpa #'r        compare A to lowercase 'r'
    bne continue    if not, continue

    leas 2,s        remove PC from stack since we won't be returning there.

    * Now this is the start of what Color BASIC ROM does for PUTCHR:
LA30A
    pshs x,b,a
    ldx CURPOS      X points to current cursor position
    leax 1,x        increment X, skipping that location.
    jmp $a344       jump back into Color BASIC ROM code.

continue
savedrvec rmb 3     call regular RAM hook
    rts             just in case...

    end

The “init” code saves out whatever is in the console out RAM hook (three bytes, either “rts” if not being used, or “jmp abcd” to jump to new code). It then patches in a “jmp” to the new code in this program, then returns back. This installs the new code into the RAM hook.

Now when anything tries to print through CHROUT, BASIC first calls whatever the RAM hook points to. That will be the “newcode” here.

This new code first checks if the device is 0, indicating we are printing to the screen. If it is not, it branches to the “continue” spot where the original RAM hook jump was copied, allowing it to jump there and proceed as normal.

Next it checks to see if the character to print is a lowercase ‘r’ (I chose ‘r’ for “right”). If not, it returns.

Now things get weird. This is not a normal RAM hook because I can’t just “do stuff” then return to the original ROM code. That original code would still want to output whatever is in register A to the screen. To make mine work, I need to take over some of the functions of the ROM and then jump back into the normal ROM code so it can continue after I have done my thing.

In a normal RAM hook, BASIC would use a JSR (jump subroutine) to get there, then have that code return back. But for this hack to work, my new code has to jump directly back into Color BASIC. Because of this, there is a return address (where RTS will return to) on the stack that we won’t be using. Doing “leas 2,s” moves the stack pointer so it skips that. This allows me to jump directly out of this new code back into the ROM.

Now we have to do whatever code the ROM would have done before processing the character. I looked at the disassembly and see it pushes three registers to save them, then at the end it will pull them back off the stack to restore them.

So I do that in my code.

Next, all I need to do is increment the cursor position then jump back into the Color BASIC ROM just past where it would normally put the character on the screen. From there on, everything will be normal.

I may not have explained this very well, but I am sure someone can help me in the comments.

Patching BASIC with BASIC

Thanks to LWTools, here is a simple BASIC loader for this test code:

10 READ A,B
20 IF A=-1 THEN 70
30 FOR C = A TO B
40 READ D:POKE C,D
50 NEXT C
60 GOTO 10
70 END
80 DATA 16128,16170,182,1,103,183,63,43,190,1,104,191,63,44,134,126,183,1,103,142,63,24,191,1,104,57,13,111,38,15,129,114,38,11,50,98,52,22,158,136,48,1,126,163,68,16174,16174,57,-1,-1

If you load this into a CoCo or emulator, then RUN it, the code will be loaded at address &H3F00. Type “EXEC &H3F00” to install the new RAM hook and now any print of lowercase ‘r’ skips to the right without erasing anything.

10 CLS0
20 PRINT@0,"THISrrrrWILLrrrrWORK"
30 GOTO 30

That simple program will clear to a black screen, then you will see “THIS” then four blocks to the right “WILL” then four blocks past that “WORK”. Normally it would erase those blocks with spaces, but the lowercase ‘r’ now means “just skip to the right”.

It’s not perfect, but it does prove the concept.

More to come…

One thought on “Hacking the Color BASIC PRINT command – part 2

  1. William Astle

    For bonus points, handle the case where it’s running on a Coco3 in Width 40/80. Without misbehaving on a Coco1/2. “Handle” can mean “don’t do transparent characters” or “make it work properly”. Hint: “make it work properly” will be harder than you expect – the SECB code for handling screen output is quite terrible.

    Reply

Leave a Reply to William AstleCancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.