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:

Code: Select all

:06FFFA00000200020002FB
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

Code: Select all

	while ((*ACIAStatus) & 0x02) {}

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! :lol:  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 :D

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