Retro Challenge update #5

This week I exchanged the Z80 core in my RC2014 Z80 computer emulator, with an MC6809 core. I couldn’t seem to find a decent HD6309 core but, as you probably know from the previous blog posts, the HD6309 will run MC6809 binaries! For the time being, I’ll have to live without all the fancy new HD6309 instructions in the emulator but at least I’ll be able to write simple programs for the HD6309 computer.

The hardware the emulator mimics is just the MC6809 core and a very simple UART. The UART isn’t based on any existing design but has a simple interface. It has a transmit and receive register and a status register for the transmitter and receiver. The UART serves as a placeholder for the actual SC16C550B UART. I’ll adapt the bootloader to suit the actual hardware later. The first goal is to get a simple bootloader working.

6809/6309 instruction set

I had never programmed in 6809 assembler until this weekend ūüôā . The only CPUs I have assembler experience with is the Z80, AVR, ARM Cortex and 16-bit/32-bit x86. They have a very different notation, especially when it comes to the addressing modes.

For instance, loading the AX register with the memory contents of the address in the DI register on the x86 is:

MOV AX,ES:[DI]

On the 6809/6309, loading the A register with the memory contents of the address in the X register is:

LDA ,X

The 6809 will also allow you to increment the X register by one in the same instruction:

LDA ,X+

or by two:

LDA ,X++

I also created a few bugs related to immediate versus indirect loads. On the 6809/6309, loading the X register with a number ($1234), the syntax should be:

LDX #$1234

and not:

LDX $1234

which will load the 16-bit register X with the memory contents at address $1234.

It seems that there aren’t very many online resources that have example programs for the 6809/6309, so it took me a while to get the details figured out. I must say that, even without the additional 6309 instructions, the capabilities of the 6809 are already impressive.

Bootloader

The bootloader is nothing fancy. It checks the RAM (0x0000 .. 0xDFFF) by writing the all-zeros and all-ones pattern to each location and reads it back. If the memory check fails, it will send ‘E’ to the UART and stop. It will continue if the test was successful.

The next step is to setup the stack pointer, so we’re able to call subroutines, and write the startup banner to the UART to show the operator the computer is working.

The loader will wait for a two-byte start address and a two-byte end address, describing the block of memory to be loaded. The addresses are formatted most-significant byte first. The start and end addresses are printed to the console via the UART.

After obtaining the start and end addresses, the loader expects to receive the bytes that make up the memory block. The end address is one byte past the block; it does not get written. When the block is complete, the loader jumps to the start of the block.

; HD6309 COMPUTER - Retrochallenge 04/2017
;
; BOOTLOADER V1.0
; By Niels A. Moseley
;

; ==================================================
; SYSTEM CONSTANTS
; ==================================================

RAMSTART  EQU $0000
RAMEND    EQU $E000
STACK     EQU $DFF0

; ==================================================
; UART ADDRESSES
; ==================================================
SERIALTX  EQU $E000
SERIALRX  EQU $E001
SERIALTXS EQU $E002
SERIALRXS EQU $E003


; ==================================================
; MONITOR ADDRESSES
; ==================================================
LDENDADDR EQU $DFFE
JMPADDR   EQU $DFFC

    ORG $F000   ; START OF ROM

; ==================================================
; OUTPUT A CHARACTER TO THE CONSOLE
; BLOCKS ON UART TX FULL
; ==================================================
OUTC:
    PSHS B
OUTC_1:
    LDB SERIALTXS
    BNE OUTC_1
    STA SERIALTX
    PULS B
    RTS

; ==================================================
; GET A CHARACTER FROM THE CONSOLE
; BLOCKS ON UART RX EMPTY
; ==================================================    

INBYTE:
    PSHS B
INBYTE_1:
    LDB SERIALRXS
    BEQ INBYTE_1
    LDA SERIALRX
    PULS B
    RTS

; ==================================================
; WRITE A REGISTER AS HEX TO UART
; BLOCKS ON UART TX FULL
; ==================================================        
PRINTAHEX:
    PSHS A
    PSHS A
    ; convert MS nibble
    LSRA
    LSRA
    LSRA
    LSRA
    CMPA #9
    BLS PRINTAHEX_1
    ADDA #7             ; 'A'-'9'-1 : 65 - 57 - 1 = 7
PRINTAHEX_1:
    ADDA #'0'
    JSR OUTC
    ; convert LS nibble
    PULS A
    ANDA #$0F
    CMPA #9
    BLS PRINTAHEX_2
    ADDA #7
PRINTAHEX_2:
    ADDA #'0'
    JSR OUTC
    PULS A
    RTS

; ==================================================
; PRINT STRING POINTED TO BY X
; BLOCKS ON UART TX FULL
; ==================================================     
PRINTSTRING:
    PSHS A
PRINTSTRING_1:
    LDA ,X+
    BEQ PS_END
    JSR OUTC
    JMP PRINTSTRING_1
PS_END:
    PULS A
    RTS
    
; ==================================================
; DUMMY INTERRUPT SERVICE ROUTINE
; ==================================================

DUMMYISR:
    RTI
    
; ==================================================
; START OF PROGRAM
; ==================================================

START:
    ORCC #%01010000 ; disable interrupts
    
    ; CHECK MEMORY 0 .. 0xDFFF
    LDX #RAMSTART
MEMCHKLP:
    CLRA
    STA ,X
    LDA ,X
    CMPA #$00
    BNE MEMERROR     ; jump if not zero
    LDA #$FF
    STA ,X
    LDA ,X+
    CMPA #$FF
    BNE MEMERROR     ; jump if not equal
    CMPX #RAMEND
    BNE MEMCHKLP
    JMP MEMOK
    
MEMERROR:
    LDA #'E'
    STA SERIALTX
STOP:    
    JMP STOP
    
    ; PRINT THE SIGN-ON MESSAGE
MEMOK:    
    LDS #STACK      ; LOAD THE STACK
    LDX #SIGNON
    JSR PRINTSTRING

; ==================================================    
; START OF UPLOAD CODE
; ==================================================    
;
; PROTOCOL:
; * LOAD START ADDRESS, MSB FIRST
; * LOAD END ADDRESS, MSB FIRST
; * DATA 
; * JUMP TO START ADDRESS
;   
    
BOOTLOADER:
    CLRB
    JSR INBYTE
    TFR A,B    
    JSR INBYTE
    EXG A,B
    STD JMPADDR ; store jump address

    ; print the load address
    LDX #LOADADDRSTR
    JSR PRINTSTRING
    JSR PRINTAHEX
    EXG A,B
    JSR PRINTAHEX
    LDX #EOLSTR
    JSR PRINTSTRING
    
    ; get end address bytes
    JSR INBYTE
    TFR A,B
    JSR INBYTE
    EXG A,B    
    STD LDENDADDR   ; store length
    
    ; print the end address
    LDX #ENDADDRSTR
    JSR PRINTSTRING
    JSR PRINTAHEX
    EXG A,B
    JSR PRINTAHEX
    LDX #EOLSTR
    JSR PRINTSTRING
    
    ; load the program
    LDX JMPADDR
LOADLP:
    CMPX LDENDADDR
    BEQ LDEND
    JSR INBYTE
    STA ,X+
    JMP LOADLP
        
LDEND:
    LDX #LOADEDSTR
    JSR PRINTSTRING
    JMP [JMPADDR]

SIGNON:
    .db 12                              ; form feed
    .ascii "HD6309 Bootloader v1.0"
    .db 13,10
    .ascii "By Niels Moseley"
    .db 13,10,0
    
LOADADDRSTR:
    .asciz "Start: $"

ENDADDRSTR:
    .asciz "End  : $"

LOADEDSTR:
    .ascii "Loaded!"    ; note: must be followed by 13,10,0!!
EOLSTR:    
    .db 13,10,0
    
; ==================================================    
; RESET AND INTERRUPT VECTOR TABLE
; ==================================================

    ORG $FFF2
SWI3VECTOR:  FDB DUMMYISR
SWI2VECTOR:  FDB DUMMYISR
FIRQVECTOR:  FDB DUMMYISR
IRQVECTOR:   FDB DUMMYISR
SWIVECTOR:   FDB DUMMYISR
NMIVECTOR:   FDB DUMMYISR
RESETVECTOR: FDB START


The mandatory screenshot of the bootloader running in the emulator, waiting for the start address:

emulator_bootloader

A simple test program

I wrote a simple test program to test the bootloader. It prints a simple banner and echos all the characters coming in from the UART.

; ==================================================
; SYSTEM CONSTANTS
; ==================================================

RAMSTART  EQU $0000
RAMEND    EQU $E000
STACK     EQU $DFF0

; ==================================================
; UART ADDRESSES
; ==================================================
SERIALTX  EQU $E000
SERIALRX  EQU $E001
SERIALTXS EQU $E002
SERIALRXS EQU $E003


    ORG $0000   ; START OF RAM

    JMP START
    
; ==================================================
; OUTPUT A CHARACTER TO THE CONSOLE
; BLOCKS ON UART TX FULL
; ==================================================
OUTC:
    PSHS B
OUTC_1:
    LDB SERIALTXS
    BNE OUTC_1
    STA SERIALTX
    PULS B
    RTS

; ==================================================
; GET A CHARACTER FROM THE CONSOLE
; BLOCKS ON UART RX EMPTY
; ==================================================    

INBYTE:
    PSHS B
INBYTE_1:
    LDB SERIALRXS
    BEQ INBYTE_1
    LDA SERIALRX
    PULS B
    RTS

; ==================================================
; WRITE A REGISTER AS HEX TO UART
; BLOCKS ON UART TX FULL
; ==================================================        
PRINTAHEX:
    PSHS A
    PSHS A
    ; convert MS nibble
    LSRA
    LSRA
    LSRA
    LSRA
    CMPA #9
    BLS PRINTAHEX_1
    ADDA #7             ; 'A'-'9'-1 : 65 - 57 - 1 = 7
PRINTAHEX_1:
    ADDA #'0'
    JSR OUTC
    ; convert LS nibble
    PULS A
    ANDA #$0F
    CMPA #9
    BLS PRINTAHEX_2
    ADDA #7
PRINTAHEX_2:
    ADDA #'0'
    JSR OUTC
    PULS A
    RTS

; ==================================================
; PRINT STRING POINTED TO BY X
; BLOCKS ON UART TX FULL
; ==================================================     
PRINTSTRING:
    PSHS A
PRINTSTRING_1:
    LDA ,X+
    BEQ PS_END
    JSR OUTC
    JMP PRINTSTRING_1
PS_END:
    PULS A
    RTS
    
; ==================================================
; START OF PROGRAM
; ==================================================
    
START:
    ORCC #%01010000 ; disable interrupts    
    LDS #STACK      ; LOAD THE STACK
    LDX #SIGNON
    JSR PRINTSTRING
    
    ; echo input to the output
ECHOE:
    JSR INBYTE
    JSR OUTC
    JMP ECHOE
    
SIGNON:
    .ascii "Hello from a loaded program!"
    .db 13,10,0,0

And here is the screenshot of it working in the emulator:

emulator_bootloader2

Toolchain

I’m using the LWTOOLS toolchain. I’m pretty impressed by the quality of the software. Although I haven’t tried them yet, it does offer support for the additional instructions the HD6309 offers over the MC6809.

Up next…

I’m running a little behind; I still need to make the configuration files for the GALs and order most of the components. This will be unnecessary if the PCB does not arrive on time — fingers crossed!

P.S. This week I’ll create a repository on my GITHUB account that will hold the emulator source code and the two 6809 programs; stay tuned!

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s