About C / Z80 optimizations (SDCC)

Página 1/17
| 2 | 3 | 4 | 5 | 6

Por zPasi

Champion (471)

Imagen del zPasi

04-09-2019, 20:47

There is a lot optimizations you can do without going to assembly level, by just writing your C-code differently.

For example

. unsigned char c;
  for (c = 0; c < 10; ++c) {
  	// do something
  }

generates much better Z80 code than

  int n;
  for (n = 0; n < 10; n++) {
  	// do something
  }

But it's not always obvious which code optimizes better. For example, stack variables are terrible on Z80, so statics or globals feel generally a good choice. But using them may also prevent the compiler using register variables.

Then there are different compilers. Code that suits z88dk may be poor for SDCC. Speaking of which, is supposed to be an optimizing compiler. But it supports many processors, and doesn't seem to optimize very well for Z80. But there is also a Z80-only variant ZSDCC. Has anyone tried that one on MSX?

Well, why I'm writing this is that I tried google instructions how to write good optimizing code for SDCC (Z80) and found almost nothing. I guess I just have to write tests myself and learn.

Found this article at least: https://github.com/Fabrizio-Caruso/8bitC/blob/master/8bitC_E...

Login sesión o register para postear comentarios

Por PingPong

Prophet (3448)

Imagen del PingPong

04-09-2019, 21:28

8 bit processors are not so much "C" friendly
the best compiler is hitech C cross compiler afaik

Por akumajo

Resident (34)

Imagen del akumajo

04-09-2019, 21:53

Another link for SDCC optimizations :
https://github.com/z88dk/z88dk/wiki/WritingOptimalCode

Por zPasi

Champion (471)

Imagen del zPasi

05-09-2019, 08:49

PingPong wrote:

8 bit processors are not so much "C" friendly

Not entirely true. There are newer 8-bit processors that are "C-friendly", like AVR, STM-8 etc.

Typical architecture of a C-compiler is not very Z80-friendly, because using stack variables is a kind of default and for Z80 that's the worst kind. Even if you use globals they tend to do stupid things like ld hl,_global_a ; ld e,(hl) ; inc hl ; ld d,(hl) instead of ld de,(_global_a).

But it's not written to stone that a C-compiler should be written like that.

Quote:

the best compiler is hitech C cross compiler afaik

These days Hi-tech C is owned by MicroChip and only support their processors.

The old version supporting Z80 is "available" though: http://www.cpcwiki.eu/forum/programming/hi-tech-c-v4-11-for-z80

I'll check that out (if I get it working, it's old enough to be a MS-DOS software).

Por Timmy

Expert (109)

Imagen del Timmy

05-09-2019, 11:02

As someone who's been using C and assembly to make Spectrum games for the last 10 years (and now also some MSX games), I can just say that it's not very important to write very optimised C code.

However, if you really want incredibly optimised machine code, then you should write it yourself. These compilers also offer you ways to include assembly as well as machine code, and that's the way to go. Don't expect brilliant code if you use C, especially if you don't even know the compiler works. And in this case, every compiler is different. Z88dk is different than SDCC, for example.

There are some low hanging fruits to avoid, like using "int"s, because you obviously shouldn't use 16 bit variables on an 8 bit machine, if necessary.

I use C mostly because it's faster than BASIC (especially on the Spectrum C is a lot faster), and because I can port games from one system to the other easily. (Porting my last MSXDev entry took me a few days.) And because I can write quicker in C than Assembly. That doesn't mean I don't use assembly in my games, in fact a large portion of my games codes are in assembly.

TL;DR: if you are worried about performance or size using C, then you probably shouldn't use it, and write directly in assembly or machinie code instead. I do, but my games have lots of content, but you almost certainly won't need it. (I still use n++ in my loops for example.)

Por DarkSchneider

Paladin (869)

Imagen del DarkSchneider

05-09-2019, 12:03

C generates very good machine code if the compiler is good and is well used.
Some basic is to use always, but when extrictly required, unsigned. In other words, fit things to processor natives.

Using 16-bit arithmetic is not really much concern as Z80 handles it. But must be declared as unsiged, and must fit again the CPU intructions. I.e. if the CPU has BC checks (16-bit) then a while(--) until zero with unsigned int16 would be used natively, if not and only decreasing B is allowed, then must use unsigned char (8-bit).

Also, if the compiler is not polished, it could not optimize the 'for' sentence, this is the case of ASCII MSX-C, changing it to while(--) until zero, uses the native decreasing and is a much more optimized code.

Por ducasp

Master (151)

Imagen del ducasp

05-09-2019, 16:31

SDCC is Kind of a Bless and sometimes kind of a Curse... Evil

In general, it is a great tool, and generates reasonable code... Sometimes it is a mess with nested IF's (3.90 is a lot better on that respect). I wholeheartedly recommend it and Fusion-C and other libraries, those are more than enough for most stuff, even though sometimes performance is not top notch, it is much better than Basic and you can get things done much quicker than on ASM (at least for most people).

You can always avoid some pitfalls like:

- Why the heck have a in/out function in Fusion-C???? This is counter productive, just use sdcc directives that won't need stack and generate code that is leaner (and this could be made as a inline replacement). i.e.: instead of doing a call to InPort, just do:

__sfr __at 0x06 myPort6;

myPort6 = 22;
testVar = myPort6;

This won't stack and will translate directly into out and in....

- Also it is cool to use this to use system variables as if they were C vars:

__at 0xFC9E unsigned int TickCount;

With this I'm assigning JIFFY to TickCount and it will be read as unsigned int...

- Take advantage of __z88dk_fastcall, if your function takes only one parameter, this will avoid stacking data to call the function and unstacking data inside the function... Which can be dead slow specially when you have multiple parameters and Index registers are used to reference variables...

Other than that, I think you might already have figured out some important stuff:

  • Use the data type needed for the operation... Using larger data types when not needed like the loop example you gave is a waste of resources...
  • Think carefully about when having a function is good and when it is evil.... A function for a small task that gets called multiple times might be bad, you have a call and a ret and also stack/unstacking or saving registers for a small task all the time, sometimes more time is wasted on calling / stacking/ returning than doing the function itself...
  • If a text program, be careful to not update screen when not needed... Text Operations through DOS or BIOS call are not really fast...

Por zPasi

Champion (471)

Imagen del zPasi

05-09-2019, 22:41

Timmy wrote:

I use C mostly because it's faster than BASIC (especially on the Spectrum C is a lot faster), and because I can port games from one system to the other easily. ... And because I can write quicker in C than Assembly

Me too. And indeed, even poorly written C with non-optimizing compiler is a lot faster than BASIC. And when writing menus and such you don't have to worry too much.

I use C because if I did everything in Z80 assembly my games would never become finished. I love Z80 assembly but can't afford it Smile

I write parts of them in assembly, though, but trying to keep that minimal.

An old trick is open the compiler-generated asm and just hand-optimize parts where speed really matters. I use that a lot.

DarkSchneider wrote:

C generates very good machine code if the compiler is good and is well used.

Not too often, then Wink

ducasp wrote:

- Why the heck have a in/out function in Fusion-C???? This is counter productive, just use sdcc directives that won't need stack and generate code that is leaner (and this could be made as a inline replacement). i.e.: instead of doing a call to InPort, just do:

__sfr __at 0x06 myPort6;

myPort6 = 22;
testVar = myPort6;

I hadn't noticed that trick. Syntax is weird, but works. Even that is not optimal, but much better than Fusion-C inPort / outPort. I was about to fix those but didn't know how to.

Quote:

- Also it is cool to use this to use system variables as if they were C vars:

__at 0xFC9E unsigned int TickCount;

I've used #defines but that __at is better.

Quote:

- Take advantage of __z88dk_fastcall

That one I knew.

One simple optimization for Fusion-C: take off that --opt-code-size from the makefile. It just makes the code worse, when trying to save a few bytes.

I also tested briefly that ZSDCC. Yes, it does slightly better code than SDCC.

Por ARTRAG

Enlighted (6244)

Imagen del ARTRAG

05-09-2019, 23:13

The latest version of the HI-TECH C cross-compiler for Z80 is v7.80 PL2 released in 2001. It is complete of asm code of all its libraries. Nowaday, you need dosbox to run it. Its code is very good and, supporting the Z180 MMU, it can be used to produce paged roms adding some custom ASM functions.

Por reidrac

Resident (49)

Imagen del reidrac

06-09-2019, 09:16

The SDCC optimizer is not too bad, but it is important to remember that C is not easy to translate to 8-bit CPUs that have a limited number of registers.

For me it has been very useful to read the ASM SDCC generates and then decide what parts need to be hand coded. For example, if it is a function that can do the work mostly using registers, that's a good candidate to use ASM. But if you don't need speed or small footprint, you may keep the C code as well (there's always a balance here, the C code is easier to maintain and modify later).

I also got used to write some "SDCC-isms" because I know some bits translate better than others. Usually local variables are more expensive than global ones. work with bytes and avoid larger types if you can (unsinged char, or import stdint.h and use uint8_t etc that IMHO is clearer), __z88dk_fastcall is quite important too, and so on.

RE: Hi-TECH, if is not open source and portable (runs natively in my system), I'm going to give it a pass and invest more in SDCC (or Z88DK, that has improved a lot lately even if in my experience is less user friendly).

Por PingPong

Prophet (3448)

Imagen del PingPong

06-09-2019, 09:42

Quote:

RE: Hi-TECH, if is not open source and portable (runs natively in my system), I'm going to give it a pass and invest more in SDCC (or Z88DK, that has improved a lot lately even if in my experience is less user friendly).

the problem is that Hitech C produces a so much optimized code that SDCC is far far far beyond.
Plus it has the ability to use register to pass parameter to functions.
In a CPU that has slow indexed addressing this can be a very high difference in speed up & code size.
HiTech C need to run on a DOS BOX environment so it does not matter at all if you have a Windows, Linux, or Mac machine.
even if one could run directly on windows it is not recommended. Old DOS programs have the attitude to poll keyboard in and endless loop while waiting for keystrokes. This has the effect to hog CPU time forcing a core to be at 100% of usage only to poll your keyboard. DOSBOX fix that, keeping the CPU usage near to 0% while wainting for keystrokes.

Página 1/17
| 2 | 3 | 4 | 5 | 6