*** EDIT ***
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:
#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;
}
The code in uart_tx.s is:
Code:
.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
The linker cfg is this:
Code:
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;
}
Incidentally, are there any good resources (google has failed me in this respect) to find out how to make a more comprehensive custom platform - in other words, what functions are absolutely required to support the C libraries? Bear in mind I don't have a ROM of any sort (basically, the output binary from cc65 is my ROM)