0

Dreamtracker’s Rabbit Hole

As mentioned in previous postMatej asked me if Dreamtracker app is running on X65. I told him that yes, it should run, but better to test it first myself. I downloaded Dreamtracker in its current version V0.71, fired it up, and well, after a promising start – I could see its main menu for a split second – the program crashed into the Monitor…. Well, that felt embarrasing 😮

This youtube video shows the crashed start:

The Monitor report indicates the crash was caused by executing a BRK instruction. In 6502 CPU the BRK instruction has the opcode $00, and the value is typically used as a filler byte in empty regions. The PC=0xFEAC and RO=0x00 indicates the instruction was executed at a location near the end of the first ROM bank.

I wanted to see exactly from where the CPU came to this instruction address. I don’t know how to do that on a regular 6502-based system, but on my X65 it was not so difficult. NORA FPGA already has a built-in In-Circuit Debugger (ICD). The ICD monitors instruction stream executed by the CPU in real time and stores instruction trace in a 512-element ring buffer, which could be read out to a PC over the USB link any time. NORA can also send ABORT signal the CPU when specific conditions in the instruction stream are encountered. Therefore, all I needed was to (temporarily) modify ICD in the FPGA to stop the CPU clock when the BRK opcode (0x00) is executed. This was done and these are the last seven instructions before the faulting BRK opcode:

Cyc #  -25:  MAH: 0 (low :  0)  CBA: 0  CA: 3c3  CD:68  ctr:1f:----  sta:7f:r--emxPDS     PLA
Cyc # -21: MAH: 0 (low : 0) CBA: 0 CA: 3c4 CD:85 ctr:1f:---- sta:7f:r--emxPDS STA $01
Cyc # -18: MAH: 0 (low : 0) CBA: 0 CA: 3c6 CD:68 ctr:1f:---- sta:7f:r--emxPDS PLA
Cyc # -14: MAH: 0 (low : 0) CBA: 0 CA: 3c7 CD:40 ctr:1f:---- sta:7f:r--emxPDS RTI
Cyc # -8: MAH: 1 (low : 1) CBA: 0 CA:372c CD:a9 ctr:1f:---- sta:7f:r--emxPDS LDA #$08
Cyc # -6: MAH: 1 (low : 1) CBA: 0 CA:372e CD:20 ctr:1f:---- sta:7f:r--emxPDS JSR $feab
Cyc # 0: MAH:41 (RomB: 0) CBA: 0 CA:feab CD: 0 ctr:1f:---- sta:7f:r--emxPDS BRK #$00

Let’s have a look at the instructions. The first four must be a part of an interrupt handler which ends with RTI, Return From Interrupt. Then we have “LDA #$08” which loads the value 0x08 into Acumulator register and “JSR $feab” a Jump to Subroutine at the address 0xFEAB. At this address the processor encounters the BRK instruction and NORA has stopped it. It really looks that the jump to $FEAB in ROM is intentional in the Dreamtracker code. So I searched the Dreamtracker code source and I found it pretty quickly: in the file x16.inc there are the following definitions of jump addresses:

    ...
    SCREEN_SET_MODE		:= $FF5F
    SCREEN_SET_CHARSET		:= $FF62
    screen_set_charset          := $FF62
    extapi			:= $FEAB	<=== HERE
    i2c_write_byte		:= $FEC9
    I2C_WRITE_BYTE		:= $FEC9
    entropy_get			:= $FECF
    ....

Evidently $FEAB is supposed to be extapi in the ROM. We can find several references to extapi throughout the dreamtracker code, for example here in the main:

    ; Check for input from the keyboard, and do something if
    ; a key was pressed (return value is something other than 0)
    check_keyboard:
        ;sei
        ; call ps2data_fetch
        lda #$08
        jsr extapi              <=== HERE
        jsr SCNKEY
        jsr GETIN  ;keyboard
        beq main_application_loop
        sta zp_KEY_PRESSED

I searched extapi in my copy of X16 ROM sources, and found none. Then I searched for $FEAB and found it in vectors.s:

    ; *** this is space for new X16 KERNAL vectors ***
    ;
    ; !!! DO NOT RELY ON THEIR ADDRESSES JUST YET !!!
    ;
        .byte 0,0,0                    ; $FEA8
        .byte 0,0,0                    ; $FEAB      <=== HERE :-(
        .byte 0,0,0                    ; $FEAE
        jmp mciout                     ; $FEB1
        jmp i2c_batch_read             ; $FEB4

Oh, evidently the vector is not used – in my copy of the ROM. I updated to the latest CX16 ROM version R48 and there it was – the extapi function is now defined at that specific address!

    ; *** this is space for new X16 KERNAL vectors ***
    ;
        jmp extapi16                   ; $FEA8
        jmp extapi                     ; $FEAB      <=== HERE :-)
        .byte 0,0,0                    ; $FEAE
        jmp mciout                     ; $FEB1
        jmp i2c_batch_read             ; $FEB4

Now I was pretty confident that after a ROM update in the X65 computer the Dreamtracker would start.

In X65 a ROM update is done from a Linux PC via the USB connection, no additional HW necessary. You could either download the released (already compiled) ROM file from CX16 git repository, or you could build it yourself from the sources. In later case the output file rom.bin is found in directory x16-rom/build/x16. In any case this rom.bin file must be copied into x65’s repository directory sw-spiflash/sbl-for-cx16 (or you could modify variable CX16ROM in Makefile in that directory), and after that you must run make to wrap it:

jara@megalit:~/hw/Open65/sw-spiflash/sbl-for-cx16$ make
ca65 -I../../include -I. --cpu 65c02  ../common/../common/sbl.s
ca65 -I../../include -I. --cpu 65c02  ../common/../common/isafix.s
ca65 -I../../include -I. --cpu 65c02  ../common/../common/vidtext.s
ca65 -I../../include -I. --cpu 65c02  ../common/../common/font8x8.s
ld65 -C ../common/sbl.ld ../common/sbl.o ../common/isafix.o ../common/vidtext.o ../common/font8x8.o -o sbl.bin
ca65 -I../../include -I. --cpu 65c02 ../common/sbl.s -l sbl.lst
ca65 -I../../include -I. --cpu 65c02 ../common/isafix.s -l isafix.lst
ca65 -I../../include -I. --cpu 65c02 ../common/trampoline.s -l trampoline.lst
cat sbl.bin rom.bin >img.bin

This sequence has built a Secondary Bootloader (SBL) for X65 and appended CX16 ROM as SBL’s payload. The SBL provides a splash screen while the CX16 “ROM” is loading, and a startup wrapper code to kick off the CX16 “ROM” in correct parts once it has been loaded in the X65 RAM. The next step is to write the complete file img.bin into the flash memory of X65. This is done by make prog_img as follows:

jara@megalit:~/hw/Open65/sw-spiflash/sbl-for-cx16$ make prog_img 
../../x65prog/x65prog -N -o 256k img.bin
init..
cdone: high
reset..
cdone: low
flash ID: 0xEF 0x40 0x15 0x00
file size: 253952
erase 64kB sector at 0x040000..
erase 64kB sector at 0x050000..
erase 64kB sector at 0x060000..
erase 64kB sector at 0x070000..
programming..
done.                 
reading..
VERIFY OK             
cdone: high
Bye.

With the R48 ROM ready and flashed in the X65 computer, I did not expect further problems. What a surprise then, when it did not boot at once!

After some head-scratching and google-googling, I had a hunch that this problem might be related to CX16’s latest addition of the support for the 65816 CPU. The discussion in this thread tells us how the ROM newly distinguishes between the 6502 and 65816 processors. It is using the following instructions in hex: $18, $E2, $01. The code is interpretted by 65c02 as:

CLC                 ; clear carry flag
NOP  #$01           ; a 2-byte no-op

While 65c816 interprets the same code as:

CLC                 ; clear carry flag
SEP  #$01           ; sets bit 0 of the processor status register, which is the carry flag

he resulting carry flag is zero (cleared) on 65c02, and one (set) on 65c816. Any code that follows can use this information to possibly switch the processor in 16-bit mode, if 65816 has been detected, or use an alternate 8-bit code on 6502.

In case of X65 I got some in-between behaviour. The ROM apperantly detected that it is running on 65816, and then did something unexpected – which I have not so far had the time to investigate. Sorry! There is an information in the CX16 thread that no addressing into different bank than the BANK $00 takes place, but one could not be sure. The original CX16 does not decode the BANK bits of 65816, so a program bug in this case would go invisible.

To get the latest ROM running quicky while not spending my time an another annoying reverse-engineering task at that point, I got creative and hot-fixed the issue in the NORA FPGA. The FPGA has already support for observing the processor instruction stream as it is going, and throwing an ABORT exception whenever a “banned” instruction is about to execute. “Banned” are attempts to run 65816-only instructions while the processor is in the Emulation mode. So far I have used this feature to filter out the RMBx, SMBx, BBRx and BBSx opcodes, which have a completely different meaning in 6502 versus in 65816. For these instruction NORA FPGA raises the ABORT exception in the 65816 processor, and a handler emulates the “banned” instruction step-by-step in software code. Newly I have added the opcodes $E2 (SEP xy) and $C2 (REP xy) to the list of banned opcodes in the Emulation mode. (Note that when the 65816 is in the Native mode (8-bit or 16-bit), no opcodes are banned by NORA – we assume clean code then). Exception handler for banned instructions has been updated to just skip the 2 bytes ($E2 $xy, and $C2 $xy), thus exactly emulating the behaviour of 6502 on 65816 processor in Emulation mode as NOPs. This way we fool the CX16 ROM in thinking it is runing on the “good old” 6502, and it attempts no switching into the Native mode. And yes, after this FPGA hot-fix the ROM started with no problems. We will investigate why it had failed sometime later.

Finally I could run Dreamtracker 0.71 and amuse myself with some of its tones:

So yes, Dreamtracker V0.71 with ROM R48 runs well on X65.

Hot-fix commit on github.

Jarda

Leave a Reply

Your email address will not be published. Required fields are marked *

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