It works now -- once I rebuilt the whole project (including asm parts) with the --cpu=65c02 flag and rebuilt the library (I'd made a few changes to the crt0.s to put the interrupt service routine back, which I had disabled while debugging)
I shall leave this here in case someone else runs into this so hopefully their Google search will hit it
*** END EDIT ***
I'm building an FPGA based SoC, inside a Lattice (formerly SiliconBlue) ICE40 FPGA. I'm using the 65c02 core from https://github.com/hoglet67/verilog-6502 (which is itself a fork of the 6502 found here https://github.com/Arlet/verilog-6502 ). The whole shebang is running off the primary oscillator which is at 25 MHz.
It does seem to work OK; so far I'm using block ram and have a 9600 baud UART implemented, and some LEDs for status:
LEDs - at 0xE000
UART - 0xE001 (transmit/receive byte), 0xE002 (transmit busy), receive byte interrupts
Interrupt status byte - 0xE003 (UART and timer can set a bit here when they cause an interrupt)
32-bit timer - 0xE004-E007
Internal FPGA block RAM is from 0xF000-0xFFFF and 0x0000 to 0x0FFF, external static ram at 0x1000 and up.
I followed the steps in https://cc65.github.io/doc/customizing.html to create the new platform.
I'm not very experienced with the 65C02 - nearly all of my 8-bit work has been Z80 based, but I want to learn about the 6502 as well as learn Verilog, and it seems to make better use of memory bandwidth than the Z80 (especially when you've got very fast block ram to hand that doesn't need any wait states). I mostly want to use C when I need to manipulate strings, as this is pretty tedious and slow to write in asm, and someone's already written the code anyway.
However, sprintf doesn't seem to work right. The output I expect would be an infinite loop of:
val1 = 123, val2 = 159
Instead I get
val1 = , val2 =
The code in main() is:
Code: Select all
#include <stdlib.h>
#include <stdio.h>
#include "uart.h"
int main() {
int val1 = 123;
int val2 = 345;
char buf[128];
sprintf(buf, "val1 = %d, val2 = %x\r\n", val1, val2);
while(1) {
uart_tx(buf);
}
return 0;
}
Code: Select all
.export _uart_tx
.exportzp _uart_data: near
.define TX_BYTE $e001
.define TX_IDLE $e002
.zeropage
_uart_data: .res 2, $00 ; Reserve zero page pointer
.segment "CODE"
.proc _uart_tx: near
; store the pointer
sta _uart_data
stx _uart_data + 1
ldy #0
; busy wait until the UART is idle
@rdy_loop:
lda TX_IDLE
bne @rdy_loop
; send a character, exit if zero terminator
lda (_uart_data), y
beq @done
sta TX_BYTE
iny
jmp @rdy_loop
@done:
rts
.endproc
Code: Select all
MEMORY {
ZP: start = $0, size = $100, type = rw, define = yes;
BUFFERS: start = $200, size = $0100, type = rw, define = yes;
RAM: start = $300, size = $0C00, define = yes;
ROM: start = $F000, size = $1000, file = %O;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp, define = yes;
DATA: load = ROM, type = rw, define = yes, run = RAM;
BSS: load = RAM, type = bss, define = yes;
HEAP: load = RAM, type = bss, optional = yes;
STARTUP: load = ROM, type = ro;
ONCE: load = ROM, type = ro, optional = yes;
CODE: load = ROM, type = ro;
RODATA: load = ROM, type = ro;
VECTORS: load = ROM, type = ro, start = $FFFA;
}
FEATURES {
CONDES: segment = STARTUP,
type = constructor,
label = __CONSTRUCTOR_TABLE__,
count = __CONSTRUCTOR_COUNT__;
CONDES: segment = STARTUP,
type = destructor,
label = __DESTRUCTOR_TABLE__,
count = __DESTRUCTOR_COUNT__;
}
SYMBOLS {
# Define the stack size for the application
__STACKSIZE__: type = weak, value = $0200;
}