Page 1 of 4
SDCC for 6502
Posted: Sat Mar 12, 2022 5:44 am
by BillG
http://sdcc.sourceforge.net/index.php#News
They just announced work in progress to support the 6502.
Re: SDCC for 6502
Posted: Sat Mar 12, 2022 7:00 am
by Proxy
it says "mos6502" so i assume they don't mean the W65C02, which is a shame.
Re: SDCC for 6502
Posted: Sat Mar 12, 2022 8:03 am
by drogon
Nice to have another C compiler on the block. I used SDCC on an 8-bit PIC project a number of years back and found it to be very good there. It managed to cover the data banking almost transparently, so maybe there is hope for it on the '816 too in the future?
-Gordon
Re: SDCC for 6502
Posted: Sat Mar 12, 2022 5:35 pm
by resman
Great news. I also used SDCC for an embedded 8051 project many years ago and found it very capable. It looks to also have support for the 65C02.
Re: SDCC for 6502
Posted: Wed Feb 15, 2023 6:33 am
by barnacle
I played a little last night with SDCC-4.2.0 (the latest version) in a Linux Mint environment.
First thoughts:
- There is apparently no documentation regarding the 6502, other than a couple of mentions in the sdccman.pdf. Grepping doesn't find anything useful.
- Obviously, putchar() has to be defined if you want to use text input. The error message if you don't complains about a missing '_putchar' but it does not want that leading underscore.
- You're also going to have to include any necessary initialisation of the input mechanism (in my case, it will be a 68B50 uart).
- The mystic incantation (discovered after some trial and error) to build for 6502 is 'sdcc -mmos6502 hello.c' which rather implies that it's assembling base NMOS code (which I'm old-school enough to be happy about; it might not be optimal on a WDC part but it'll run on anything). It didn't like -65c02 or -wdc65c02.
- It seems to be assuming a flat memory model with 64k ram. The hex file starts with a single line:
which points the reset, nmi, and irq vectors to $0200.
- The simple test file (as yet unrun, and note it doesn't have the uart initialisation!)
Code: Select all
#include <stdio.h>
char * ACIAStatus = 0xa000;
char * ACIAData = 0xa001;
int putchar(int ch);
int putchar(int ch)
{
while (((*ACIAStatus) & 0x02) == 0x02) {}
*ACIAData = (char)ch;
return ch;
}
void main (void)
{
printf ("hello world");
}
produces a hex file with code from $0200 to $0d30 or thereabouts. I assume that's because it's dragging in the stdio library - printf is always a code hog even without floating point.
Neil
Re: SDCC for 6502
Posted: Wed Feb 15, 2023 8:21 am
by BillG
Try puts instead of printf and see how much baggage you omit...
Re: SDCC for 6502
Posted: Wed Feb 15, 2023 4:35 pm
by barnacle
As expected: it's a lot smaller. As I said, printf is a memory hog on every small microcontroller; it drags in too much baggage with it even without floating point.
Code: Select all
:06FFFA00000200020002FB
:20020000A2FF9AA9388D0680A9038D0780A9048D0880A9008D0980A900A280207902A9005F
:1A0220008D0A80A90B8D0B80A9008D0C80A904A28020B7022072024C37025E
:20023A008D04808E0580AD00808520AD01808521A000B120A20029024868C902D004E0006D
:1F025A00F0E4AD02808534AD03808535AD04809134AE0580AD048060A203A92C4C02035A
:10032C0068656C6C6F20776F726C640000A001A024
:20027900852C862D852E862FAD06808530AD07808531A000AE0980F013B130912EC8B130A4
:1E029900912EC8D0F4E631E62FCAD0EDAE0880F008B130912EC8CAD0F8A52CA62D601D
:2002B7008D0D808E0E80AE0B80AC0C80AD0C800D0B80F030AD0D808535AD0E808536AD0AEE
:2002D7008085348420A534A0009135A420E635D002E6368A38E901AA98E900A8C000D0020D
:0B02F700E000D0DFAE0E80AD0D806097
:20030200852A862BA000B12AC900F017E62AD002E62BA200203A02C9FFD0E9E0FFD0E5A981
:0A032200FFAA60A200A90A4C3A02EB
:00000001FF
Neil
Re: SDCC for 6502
Posted: Wed Feb 22, 2023 7:53 am
by barnacle
I've been playing a little with the sdcc compiler; it's not bad at first glance but it's missing some obvious optimisations:
Code: Select all
// pointers to the ACIA registers
volatile char * const ACIAStatus = 0xa000;
volatile char * const ACIAData = 0xa001;
int putchar(int ch)
{
// blocking write to the serial port
while ((*ACIAStatus) & 0x02) {}
*ACIAData = (char)ch;
return ch;
}
Those ACIA pointers should be constant pointers to a volatile char - the register content. However, it produces the following assembly:
Code: Select all
_putchar:
sta _putchar_ch_65536_38
stx (_putchar_ch_65536_38 + 1)
; monitor.c: 35: while ((*ACIAStatus) & 0x02) {}
00101$:
lda _ACIAStatus
sta *(__TEMP+0)
lda (_ACIAStatus + 1)
sta *(__TEMP+1)
ldy #0x00
lda [__TEMP+0],y
and #0x02
bne 00101$
; monitor.c: 36: *ACIAData = (char)ch;
lda _ACIAData
sta *_putchar_sloc0_1_0
lda (_ACIAData + 1)
sta *(_putchar_sloc0_1_0 + 1)
lda _putchar_ch_65536_38
sta [*_putchar_sloc0_1_0],y
; monitor.c: 37: return ch;
ldx (_putchar_ch_65536_38 + 1)
lda _putchar_ch_65536_38
; monitor.c: 38: }
rts
where it has obviously defined the pointer as a word in memory; it copies it to a temporary word, and reads the byte using (abs),y. It also loops back and loads the pointer address every time. This is obviously generic so a pointer can change between calls - but one might have hoped that the const keyword would have allowed it to avoid the indirect load and the repeated load. Indeed, the ideal optimisation is perhaps a simple lda abs to the register... oh well, the whole point of C is that you don't have to worry about these things
Another bit which is somewhat confusing this early in the morning is if one replaces the
with the hopefully more self-documenting but theoretically identical operation
Code: Select all
while (0x02 == (*ACIAStatus) & 0x02) {}
which changes the generated assembly to
Code: Select all
00101$:
lda _ACIAStatus
sta *(__TEMP+0)
lda (_ACIAStatus + 1)
sta *(__TEMP+1)
ldy #0x00
lda [__TEMP+0],y
ldx #0x00
and #0x02
pha
pla
cmp #0x02
bne 00115$
cpx #0x00
beq 00101$
Which includes the requested test but leaves an unnecessary pha/pla hanging around.
All very interesting, but this is the first release. At present there is zero documentation either in the installed codebase or on the wiki page associated; I may have to join their forum so at least I can ask some questions.
I think I'll knock up a quick monitor and see how that works. I've got lots of eeprom space
Neil
Re: SDCC for 6502
Posted: Thu Feb 23, 2023 9:44 pm
by barnacle
Another observation: as far as I recall no flags are set by simply loading a variable on the 6502, but require a cmp, cpx, or cpy operation (or playing games with the flag register via the stack).
In which case, this generated code looks suspicious:
Code: Select all
_get_line:
; monitor.c: 61: unsigned char inptr = 0; // eight bits are enough
ldx #0x00
stx _get_line_inptr_65536_46
; monitor.c: 62: char done = 0; // not done yet
stx _get_line_done_65536_46
; monitor.c: 65: while (0 == done)
00113$:
lda _get_line_done_65536_46
beq 00154$
rts
00154$:
; monitor.c: 67: inbuffer[inptr] = '\0';
ldx _get_line_inptr_65536_46
ldy #0x00
tya
sta (_inbuffer+0+0x0000),x
...
It seems to me that the beq after 00113$ is dependent on the flags on entry to get_line, and not on an actual test. There is a jump to 0113$ later based on a comparison (buffer full) but also one later still which is just a jump.
Am I missing something obvious?
(I have not yet been able to get the code onto a real 6502; my development machine doesn't have the same GLIBC as this one and I'm a bit leery of updating it; there's too much on it. Hopefully over the weekend.)
Neil
Re: SDCC for 6502
Posted: Thu Feb 23, 2023 9:48 pm
by BigEd
Hmm, no, you have that wrong unfortunately: loading a register does set N and Z. (Not so on all other micros, but true on 6502. If in doubt run a few instructions in an emulator:
easy6502 is right there in your browser, for example.)
Re: SDCC for 6502
Posted: Thu Feb 23, 2023 10:04 pm
by GARTHWILSON
As stated in the "programming tips" section of the 6502 primer at http://wilsonminesco.com/6502primer/PgmTips.html, an automatic compare-to-zero instruction is built into the following 65c02 instructions: LDA, LDX, LDY, INC, INX, INY, DEC, DEX, DEY, INA, DEA, AND, ORA, EOR, ASL, LSR, ROL, ROR, PLA, PLX, PLY, SBC, ADC, TAX, TXA, TAY, TYA, and TSX. This means that, for example, a CMP #0 after an LDA is redundant, a wasted instruction. A kitten somewhere dies every time you do that!
The only time a 65c02 (CMOS) needs a compare-to-zero instruction after one of these is if you want to compare a register that was not involved in the previous instruction. Store instructions (STA, STX, STY) however do not affect status.
Re: SDCC for 6502
Posted: Fri Feb 24, 2023 6:31 am
by barnacle
Thank you gents! I thought I knew this processor - it's only been forty years since I used one in anger
So I looked in the Zaks that's been open on my desktop for the last month and there it is, plain as day... doh!
I think I got confused with the 8080 which I've been recreating at logic level; that one does require an ALU operation to set the flags and the register move and load instructions don't set flags.
https://www.pastraiser.com/cpu/i8080/i8080_opcodes.html
Thanks again; I'll keep on plugging on at understanding this compiler.
Re: SDCC for 6502
Posted: Fri Feb 24, 2023 8:14 pm
by barnacle
Getting a little further with the secret incantations:
Code: Select all
sdcc -mmos6502 monitor.c --code-loc 0xe000 --xram-loc 0x0200
tells the linker to put the assembled code at 0xe000 (in my eeprom space) and to use from 0x0200 for constants and variables. Default values without --code-loc is to build at 0x0200 and it puts the bss at 0x8000 (where I have an empty hole in the memory map) without the --xram-loc. A --data-loc looked like the obvious switch for the bss but instead it moves the zero page variables...
That done, the code
Code: Select all
*ACIAStatus = 0x95;
*ACIAData = '#';
produces
Code: Select all
; monitor.c: 140: *ACIAStatus = 0x95;
ldx _ACIAStatus
ldy (_ACIAStatus + 1)
stx *(__TEMP+0)
sty *(__TEMP+1)
lda #0x95
ldy #0x00
sta [__TEMP+0],y
; monitor.c: 142: *ACIAData = '#';
ldx _ACIAData
ldy (_ACIAData + 1)
stx *(__TEMP+0)
sty *(__TEMP+1)
lda #0x23
ldy #0x00
sta [__TEMP+0],y
which outputs the expected '#' from the 68B50.
Neil
Re: SDCC for 6502
Posted: Fri Feb 24, 2023 9:09 pm
by BigEd
> secret incantations
That's good to know, thanks!
Re: SDCC for 6502
Posted: Sat Feb 25, 2023 6:33 am
by barnacle
When I get to a more complete state of knowledge, I'll knock up a quick how-to. The main things I need to know are
- How to compile anything! (done)
- How to compile code to a given memory map (done)
- How to handle interrupts and the BRK instruction
- How to include assembly code directly
Neil