SDCC for 6502

Programming the 6502 microprocessor and its relatives in assembly and other languages.
BillG
Posts: 710
Joined: 12 Mar 2020
Location: North Tejas

SDCC for 6502

Post by BillG »

http://sdcc.sourceforge.net/index.php#News

They just announced work in progress to support the 6502.
User avatar
Proxy
Posts: 746
Joined: 03 Aug 2018
Location: Germany

Re: SDCC for 6502

Post by Proxy »

it says "mos6502" so i assume they don't mean the W65C02, which is a shame.
User avatar
drogon
Posts: 1671
Joined: 14 Feb 2018
Location: Scotland
Contact:

Re: SDCC for 6502

Post 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
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/
resman
Posts: 154
Joined: 12 Dec 2015
Location: Lake Tahoe
Contact:

Re: SDCC for 6502

Post 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.
barnacle
Posts: 1831
Joined: 19 Jan 2004
Location: Potsdam, DE
Contact:

Re: SDCC for 6502

Post 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
BillG
Posts: 710
Joined: 12 Mar 2020
Location: North Tejas

Re: SDCC for 6502

Post by BillG »

Try puts instead of printf and see how much baggage you omit...
barnacle
Posts: 1831
Joined: 19 Jan 2004
Location: Potsdam, DE
Contact:

Re: SDCC for 6502

Post 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
barnacle
Posts: 1831
Joined: 19 Jan 2004
Location: Potsdam, DE
Contact:

Re: SDCC for 6502

Post 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
barnacle
Posts: 1831
Joined: 19 Jan 2004
Location: Potsdam, DE
Contact:

Re: SDCC for 6502

Post 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
User avatar
BigEd
Posts: 11464
Joined: 11 Dec 2008
Location: England
Contact:

Re: SDCC for 6502

Post 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.)
User avatar
GARTHWILSON
Forum Moderator
Posts: 8775
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: SDCC for 6502

Post 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.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
barnacle
Posts: 1831
Joined: 19 Jan 2004
Location: Potsdam, DE
Contact:

Re: SDCC for 6502

Post 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.
barnacle
Posts: 1831
Joined: 19 Jan 2004
Location: Potsdam, DE
Contact:

Re: SDCC for 6502

Post 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
User avatar
BigEd
Posts: 11464
Joined: 11 Dec 2008
Location: England
Contact:

Re: SDCC for 6502

Post by BigEd »

> secret incantations
That's good to know, thanks!
barnacle
Posts: 1831
Joined: 19 Jan 2004
Location: Potsdam, DE
Contact:

Re: SDCC for 6502

Post 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
Post Reply