gbdev/rgbds

Document RGBLINK's support for SDAS object files

Opened this issue · 13 comments

RGBDS 0.4.1 added "GBDK support" (aka RGBLINK support for objects output by SDAS) in July 2020, but we've neglected to document it. Update rgblink.1 to not only mention "RGB objects".

Note that #1008 mentions other bullet points for ostensibly complete "C toolchain" usability. I don't know how essential/blocking any of those are to actually using RGBASM+RGBLINK as part of a C+asm multilanguage project.

Give a brief example like this:

# Create a RGB object file with RGBASM
rgbasm -o foo.o foo.asm
# Create a SDAS object file with SDCC
sdcc -c -msm83 -o bar.rel bar.c
# Link both objects together into a ROM with RGBLINK
rgblink -o rom.gb foo.o bar.rel

examples __sdcccall(1) (which is default in SDCC version > 4.1)

in: 8-bit (a)
return: 8-bit (a)
bar.h

uint_8 increment(uint8_t i);
//{
//	return ++i;
//}

rgbds.asm

_increment:
	inc a
	ret

in: 16-bit (de)
return: 16-bit (bc)
bar.h

uint_16 increment(uint16_t i);
//{
//	return ++i;
//}

foo.asm

_increment:
	inc de
	ld b, d
	ld c, e
	ret

in: 16-bit (de), 8-bit (a)
return: 16-bit (bc)
bar.h

uint_16 add(uint16_t a, int8_t b);
//{
//	return a+b;
//}

foo.asm

_add:
	add a, e
	ld c, a
	ld a, d
	adc a, 0
	ld b, a
	ret

excerpt from SDCC documentation (4.3.5):


This calling convention can be chosen per function via __sdcccall(1).
8-bit return values are passed in a, 16-bit values in bc, 32-bit values in debc. Larger return values (as well as
struct and union independent of their size) are passed in memory in a location specified by the caller through a
hidden pointer argument.
For functions that have variable arguments: All parameters are passed on the stack. The stack is not adjusted
for the parameters by the callee (thus the caller has to do this instead).

Image

For Functions that do not have variable arguments: the first parameter is passed in a if it has 8 bits. If it has 16
bits it is passed in de. If it has 32 bits, it is passed in debc. If the first parameter is in a, and the second has 8 bits, it
is passed in e; if the first is in bc or debc, and the second has 8 bits, it is passed in a; if the first is passed in a, and
the second has 16 bits, it is passed in bc; if the first is passed in de, and the second has 16 bits, it is passed in bc; all
other parameters are passed on the stack, right-to-left. Independent of their size, struct / union parameters and all
following parameters are always passed on the stack. The stack is adjusted by the callee (thus explicitly specifying
__z88dk_callee does not make a difference), unless the functionhas variable arguments.


Thanks a lot! Some questions:

  • Do the declarations not need extern?
  • What's the difference between uint_8/uint_16 and uint8_t/uint16_t?

(I should probably get sdcc set up on my machine and try these out before adding them in docs.)

also for better sdcc code you may add __preserves_regs() directive:

so for our example: uint_8 increment(uint8_t i);

uint_8 increment(uint8_t i)  __preserves_regs(b, c, d, e, h, l);

so caller will not generate push on those register if it may need after return from function call

  • Do the declarations not need extern?

AFAIK C doesn't require extern for function

  • What's the difference between uint_8/uint_16 and uint8_t/uint16_t?

I think typo is the difference :D

variable defined in asm

bar.c (https://godbolt.org/z/481n9sqnd)

extern uint8_t counter;
void main(void)
{
  counter++;
}

foo.asm

section "counters", WRAM0
_counter::
  ds 1

variable in HRAM

bar.c (https://godbolt.org/z/njnvff7Mq)

extern __sfr counter;
void main(void)
{
  counter++;
}

foo.asm

section "HRAM counters", HRAM
_counter::
  ds 1

Very cool. I think rgblink(1) should have one basic/minimal "yes this works" example, and these can go elsewhere. Maybe a gbdev.io page. Maybe a new rgbsdcc(5) man page. Maybe an example GitHub repo (but then something ought to link to it).

@Rangi42 I'd say this should stay in the rgbds.gbdev.io man pages? I like the rgbsdcc(5) man page option

As commented in #1008, SDAS object files have advanced a version since 2023 when we first added basic support. So we probably want to ensure RGBASM's support is up-to-date before thoroughly documenting/advertising it. Which is not a top-priority task for 0.9.x. (Maybe for 1.0 though?)

I don't think I have the knowledge or interest necessary to maintain RGBLINK's SDAS object support. And it feels futile to document support for a now-outdated object format. I'm not closing this issue, nor deleting the current outdated support, since someone else might take an interest in maintaining it, but I won't be targeting this for 1.0.

☹️

@AntonioND Do you want to handle it? I could walk you through the current C++ RGBLINK architecture if you like. (All of our remaining "TODO" comments are SDCC-related...) Or if you're more familiar with the GBDK scene than I am, maybe you know someone who could be interested?

@AntonioND Do you want to handle it? I could walk you through the current C++ RGBLINK architecture if you like. (All of our remaining "TODO" comments are SDCC-related...) Or if you're more familiar with the GBDK scene than I am, maybe you know someone who could be interested?

I haven't used GBDK seriously for over 10 years (I used GBDK-2020 once for a test, but that's all). You know a lot more about this than me.

Unfortunately, I'm way too busy with https://github.com/blocksds/sdk, and I will be for the forseeable future. I will ask around, but I think that the gbdef or GBDK discords are probably the best places to ask for help.