[z88dk-dev] sdcc_compilation

Bridge to the z88dk-developers mailing list
Post Reply
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

[z88dk-dev] sdcc_compilation

Post by alvin »

May be repeated later as the forum bridge seems not to be working currently...




I got fairly far by poking around in sdcc for a couple of hours -- I may even be able to compile later this evening as I have a few hours tonight too.


I've been making incremental changes here and there to generate the correct output for z80asm:


* Suppressed output of the initialized section and caused the external names of the initializer section to be their real C names when output to the assembler. The consequence is the initializer section becomes the data_compiler section that we're using.


* Changed the mappings.i entry for z80asm to reflect current directives.


* Suppressed output of "PUBLIC" declarations when they were imported as EXTERN by headers. When an extern function is declared in a header, sdcc is currently generating both PUBLIC and EXTERN declarations for it. With asxxx this is ok because both use the same directive "GLOBAL". So now PUBLIC declarations are only generated if the symbols are not EXTERN.


* Changed equates generated by sfr registers (sdcc is using sfr registers to specify 8-bit or 16-bit ports). sdcc was outputting in the format "io_port = 0xnn" when z80asm needs "defc io_port = 0xnn". The mappings.i file is a means to generate directives across assemblers but there isn't a means to do equates across assemblers -- this may be something that should be added to sdcc but my changes are in the code at the point the sfrs are emitted.


* I've renamed many sections SDCC has defined to what we are using:


CODE -> code_compiler
DATA -> bss_compiler
GSINIT -> code_crt_init (?? I believe this is correct intention - code executed before main)
GSFINAL -> code_crt_exit (?? I believe this is correct intention - code executed prior to exit)
INITIALIZER -> data_compiler


Some sections in SDCC I have no idea what their purpose is:


XSEG, STACK, RSEG_ABS, HOME, CABS_ABS, DABS_ABS


These I have assigned to an "IGNORE" section. If sdcc puts something in them they will show up in a separate binary output by z80asm. If something shows up we can figure out what these sections are for.



And a bug found in sdcc, which I will report in a few days if Philip doesn't catch it:


char b[]="hello";
b[3]='i';


=>


;test.c:29: b[3]='i';
ld hl,(_b + 0x0003)
ld (hl),$69


The code should be "ld hl,_b + 0x0003", ie not a memory read.




On the zcc side, we should be running sdcc's output through sdcc's peephole optimizer rather than ours.



Here's a sample output generated with: "zcc +zx -a -clib=sdcc_ix test.c". The known double string bug is present.


=====


#include <stdio.h>
#include <stropts.h>
#include <arch/spectrum.h>
#include <string.h>
#include <stdlib.h>


char buffer[100]; // max edit buffer size is 64


char b[]="hello";
char *c="again";


int d[]={0,1,2,3};


__sfr __at 0x78 IoPort;
__sfr __banked __at 0x00fe Key1;


main()
{
unsigned int i;


IoPort = 1;
Key1 = 10;
d[1] = 5;
b[3]='i';



zx_border(INK_WHITE);
ioctl(1, IOCTL_OTERM_CLS);

for (i=0; ; ++i)
{
printf("%5u %0#5x %I\n", i, i, i + ((unsigned long)(~i) << 16));

if (rand() > 32100)
{
printf("\nEnter a message:\n");

fflush(stdin);
scanf("%[^\n]", buffer);

printf("\nMessage received and reversed:\n%s\n\n", strrev(buffer));
}
}
}


======


MODULE C__Users_alvin_AppData_Local_Temp_s14e0_


; Generated using the z80asm/z88dk tokens.
EXTERN __muluchar_rrx_s
EXTERN __mulschar_rrx_s
EXTERN __mulint_rrx_s
EXTERN __mullong_rrx_s
EXTERN __divuchar_rrx_s
EXTERN __divschar_rrx_s
EXTERN __divsint_rrx_s
EXTERN __divuint_rrx_s
EXTERN __divulong_rrx_s
EXTERN __divslong_rrx_s
EXTERN __rrulong_rrx_s
EXTERN __rrslong_rrx_s
EXTERN __rlulong_rrx_s
EXTERN __rlslong_rrx_s


;--------------------------------------------------------
; Public variables in this module
;--------------------------------------------------------
PUBLIC _main
PUBLIC _d
PUBLIC _c
PUBLIC _b
PUBLIC _buffer
;--------------------------------------------------------
; Externals used
;--------------------------------------------------------
EXTERN _realloc
EXTERN _malloc
EXTERN _free
...lots of EXTERNs from headers...
EXTERN _stderr
EXTERN _stdout
EXTERN _stdin
;--------------------------------------------------------
; special function registers
;--------------------------------------------------------
defc _IoPort = 0x0078
defc _Key1 = 0x00fe
;--------------------------------------------------------
; ram data
;--------------------------------------------------------

SECTION bss_compiler


_buffer:
DEFS 100
;--------------------------------------------------------
; absolute external ram data
;--------------------------------------------------------

SECTION IGNORE


;--------------------------------------------------------
; global & static initialisations
;--------------------------------------------------------

SECTION IGNORE



SECTION code_crt_init



SECTION code_crt_exit



SECTION code_crt_init


;--------------------------------------------------------
; Home
;--------------------------------------------------------

SECTION IGNORE



SECTION IGNORE


;--------------------------------------------------------
; code
;--------------------------------------------------------

SECTION code_compiler


;test.c:22: main()
; ---------------------------------
; Function main
; ---------------------------------
_main_start:
_main:
push ix
ld ix,0
add ix,sp
push af
push af
;test.c:26: IoPort = 1;
ld a,$01
out (_IoPort),a
;test.c:27: Key1 = 10;
ld a,$0A
ld bc,#_Key1
out (c),a
;test.c:28: d[1] = 5;
ld hl,$0005
ld ((_d + 0x0002)), hl
;test.c:29: b[3]='i';
ld hl,(_b + 0x0003)
ld (hl),$69
;test.c:32: zx_border(INK_WHITE);
ld a,$07
push af
inc sp
call _zx_border
inc sp
;test.c:33: ioctl(1, IOCTL_OTERM_CLS);
ld hl,$0102
push hl
ld l, $01
push hl
call _ioctl
pop af
pop af
;test.c:35: for (i=0; ; ++i)
ld de,$0000
l_main00104:
;test.c:37: printf("%5u %0#5x %I\n", i, i, i + ((unsigned long)(~i) << 16));
ld a,e
cpl
ld c,a
ld a,d
cpl
ld b,a
ld l,$00
ld h,$00
ld a,$10
l_main00116:
sla c
rl b
adc hl, hl
dec a
jr NZ,l_main00116
ld (ix-4),e
ld (ix-3),d
ld (ix-2),$00
ld (ix-1),$00
ld a,(ix-4)
add a, c
ld (ix-4),a
ld a,(ix-3)
adc a, b
ld (ix-3),a
ld a,(ix-2)
adc a, l
ld (ix-2),a
ld a,(ix-1)
adc a, h
ld (ix-1),a
ld bc,___str_0
push de
ld l,(ix-2)
ld h,(ix-1)
push hl
ld l,(ix-4)
ld h,(ix-3)
push hl
push de
push de
push bc
call _printf
ld hl,#10
add hl,sp
ld sp,hl
call _rand
ld b,l
ld c,h
pop de
ld a,$64
cp a, b
ld a,$7D
sbc a, c
jp PO, l_main00118
xor a, $80
l_main00118:
jp P,l_main00105
;test.c:41: printf("\nEnter a message:\n");
ld hl,___str_1
push de
push hl
call _printf
pop af
pop de
;test.c:43: fflush(stdin);
push de
ld hl,_stdin
ld c, (hl)
inc hl
ld b, (hl)
push bc
call _fflush
pop af
pop de
;test.c:44: scanf("%[^\n]", buffer);
ld bc,_buffer
ld hl,___str_2
push de
push bc
push hl
call _scanf
pop af
pop af
pop de
;test.c:46: printf("\nMessage received and reversed:\n%s\n\n", strrev(buffer));
ld hl,_buffer
push de
push hl
call _strrev
pop af
ld c,l
ld b,h
pop de
ld hl,___str_3
push de
push bc
push hl
call _printf
pop af
pop af
pop de
l_main00105:
;test.c:35: for (i=0; ; ++i)
inc de
jp l_main00104
___str_0:
DEFM "%5u %0#5x %I"
DEFB $0A
DEFB $00
___str_1:
DEFB $0A
DEFM "Enter a message:"
DEFB $0A
DEFB $00
___str_2:
DEFM "%[^"
DEFB $0A
DEFM "]"
DEFB $00
___str_3:
DEFB $0A
DEFM "Message received and reversed:"
DEFB $0A
DEFM "%s"
DEFB $0A
DEFB $0A
DEFB $00

SECTION code_compiler


___str_4:
DEFM "hello"
DEFB $00
___str_5:
DEFM "again"
DEFB $00

SECTION data_compiler


_b:
DEFM "hello"
DEFB $00
_c:
DEFW ___str_5
_d:
DEFW $0000
DEFW $0001
DEFW $0002
DEFW $0003

SECTION IGNORE


====
Philipp Klaus Krause

Post by Philipp Klaus Krause »

On 30.01.2015 20:19, alvin albrecht wrote:
And a bug found in sdcc, which I will report in a few days if Philip
doesn't catch it:

char b[]="hello";
b[3]='i';

=>

;test.c:29: b[3]='i';
ld hl,(_b + 0x0003)
ld (hl),$69

The code should be "ld hl,_b + 0x0003", ie not a memory read.
Can't reproduce this, just tried

char b[]="hello";

void f(void)
{
b[3]='i';
}

with current sdcc, get

_f_start::
_f:
;test.c:5: b[3]='i';
ld hl,#_b+3
ld (hl),#0x69
ret
_f_end::

If you have a compileable example that shows the bug with current sdcc,
please file a bug report.

Philipp
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Sounds great, congratulations !
I wish I had some time now.. :)
Ha, yeah that's the way it goes.

I'm now successfully compiling sdcc programs with both the sdcc_ix library and the sdcc_iy library. The compile line is the same but library selection is different:

The new z88dk library with sccz80:

zcc +zx -vn -clib=new test.c -o test

The new z88dk library with sdcc_ix:

zcc +zx -vn -clib=sdcc_ix test.c -o test

The new z88dk library with sdcc_iy:

zcc +zx -vn -clib=sdcc_iy test.c -o test

The library uses one index register (ix), which conflicts with sdcc's use of ix as frame pointer. So the sdcc_ix library routines are a bit heavier as the C interface to the library routines has to save ix around some library calls. The sdcc_iy library has been assembled with -IXIY flag, meaning the library now uses IY instead of IX. That means the C interface is much lighter. The sdcc_iy build should always be preferred for this reason but sometimes that's not possible because of target limitations. Sometimes targets will not allow use of one or the other index register for hw reasons.

For the zx target, eg, using sdcc_iy means the rom interrupt has to be disabled as it is using IY to index into the system vars area. It took me a few mins to figure out why one of the test programs was not working and this was the reason.

I may have some time tonight to indicate a list of changes to sdcc that I made. These are just preliminary hacks and won't be how some of the modifications should ultimately be made, but it will get you running.

And a bug found in sdcc, which I will report in a few days if Philip
doesn't catch it:

char b[]="hello";
b[3]='i';

=>

;test.c:29: b[3]='i';
ld hl,(_b + 0x0003)
ld (hl),$69

The code should be "ld hl,_b + 0x0003", ie not a memory read.
Can't reproduce this, just tried

char b[]="hello";

void f(void)
{
b[3]='i';
}

with current sdcc, get

_f_start::
_f:
;test.c:5: b[3]='i';
ld hl,#_b+3
ld (hl),#0x69
ret
_f_end::

If you have a compileable example that shows the bug with current sdcc,
please file a bug report.
That's odd, I will look into it again and compare with the output generated for asxxx. It could be the compile options we are using (in particular we have the don't use iy flag set).

I found a few more while compiling test programs:


;beepfx.c:99: ioctl(1, IOCTL_OTERM_FCOLOR, INK_WHITE | PAPER_BLUE);
ld hl,$000F
push hl
ld hl,$1002
push hl
ld l, $01
push hl
call _ioctl
ld hl,#6
add hl,sp
ld sp,hl

The "ld l,$01; push hl" is pushing a 16-bit function parameter that doesn't have its MSB zeroed. In the program the ioctl rejects the colour change because the colour is out of range.


extern void fzx_state_init(struct fzx_state *fs, struct fzx_font *ff, struct r_Rect16 *window);
extern struct fzx_font *ff_ao_Orion;

;fzx_reader.c:70: fzx_state_init(&fs, FONT_CHOICE, &window[0]);
ld de,_fs
ld hl,_window
push hl
ld hl,_ff_ao_Orion
ld c, (hl)
inc hl
ld b, (hl)
push bc
push de
call _fzx_state_init
ld hl,#6
add hl,sp
ld sp,hl

"__ff_ao_Orion" (FONT_CHOICE) is the parameter that should be directly pushed, not the value stored there. In the test program, text output is garbled as a result.


I'll put these things together for bug reports in the coming days. The conference sounds fun -- I hope it goes well!


Besides that there are a couple of issues on our end:

A little while back the code was changed to output the 0 defcs for pragmas defined as 0. For some reason, the sdcc builds do not output the 0 defcs so there is another place where the defc pragmas are being written.

I'm having a little bit of trouble suppressing the existing lib include and library paths from z80asm's command line. Here's the process for one of the sdcc compiles:

1 file(s) copied.
1 file(s) copied.

copy c:\z88dk\Lib\Config\\..\..\/libsrc/_DEVELOPMENT/target/zx/zx_crt.opt C:\Users\alvin\AppData\Local\Temp\sqqk_1.opt

copy C:\Users\alvin\AppData\Local\Temp\sqqk_1.opt C:\Users\alvin\AppData\Local\Temp\sqqk_1.asm

zcpp -I. -DZ80 -DSPECTRUM -D__SPECTRUM__ -D__SPECTRUM -D__SDCC -D__SDCC_IY -Ic:\z88dk\Lib\Config\\..\..\/include/_DEVELOPMENT -DZ88DK_USES_SDCC=1 test.c C:\Users\alvin\AppData\Local\Temp\sqqk_.i2

zpragma < C:\Users\alvin\AppData\Local\Temp\sqqk_.i2 > C:\Users\alvin\AppData\Local\Temp\sqqk_.i

sdcc -mz80 --reserve-regs-iy --no-optsdcc-in-asm --c1mode --asm=z80asm < C:\Users\alvin\AppData\Local\Temp\sqqk_.i -o C:\Users\alvin\AppData\Local\Temp\sqqk_.asm

copt c:\z88dk\Lib\Config\\..\..\/lib/z80rules.2 < C:\Users\alvin\AppData\Local\Temp\sqqk_.asm > C:\Users\alvin\AppData\Local\Temp\sqqk_.op1

copt c:\z88dk\Lib\Config\\..\..\/lib/z80rules.1 < C:\Users\alvin\AppData\Local\Temp\sqqk_.op1 > C:\Users\alvin\AppData\Local\Temp\sqqk_.opt

z80asm -sdcc -eopt -ns -Mo -Ic:\z88dk\Lib\Config\\..\..\/lib C:\Users\alvin\AppData\Local\Temp\sqqk_.opt

z80asm -a -m -Mo -Lc:\z88dk\Lib\Config\\..\..\/lib/clibs -Ic:\z88dk\Lib\Config\\..\..\/lib -otest -Ic:\z88dk\Lib\Config\\..\..\/libsrc/_DEVELOPMENT/target/zx -Lc:\z88dk\Lib\Config\\..\..\/libsrc/_DEVELOPMENT/lib -izx_sdcc_iy C:\Users\alvin\AppData\Local\Temp\sqqk_1.opt C:\Users\alvin\AppData\Local\Temp\sqqk_.o


The config file has "-nostdlib" flag set to prevent the existing lib's include and library paths from being added to the command line. However, that doesn't seem to work for the link step (the last call to z80asm above). In that step, you can see the existing lib's paths being added. That could cause errors.

Also the "--reserve-regs-iy" flags we're passing to sdcc should probably be done on a target-by-target basis as many targets don't have to be so restrictive.



------------------------------------------------------------------------------
Dive into the World of Parallel Programming. The Go Parallel Website,
sponsored by Intel and developed in partnership with Slashdot Media, is your
hub for all things parallel software development, from weekly thought
leadership blogs to news, videos, case studies, tutorials and more. Take a
look and join the conversation now. http://goparallel.sourceforge.net/
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

We don't seem to have the source for zpragma and z80nm committed?


The sdcc source hacks, which is not the right way to do things in most cases. I've been thinking about using the invocation name to do things differently ("zsdcc") or using the assembler type to choose to do things differently but the latter is not available in all parts of the sdcc source.



Z80/MAPPINGS.I:

L316
static const ASM_MAPPING _z80asm_mapping[] = {
{ "global", "PUBLIC %s" },
{ "extern", "EXTERN %s" },
{ "slabeldef", "%s:" },
{ "labeldef", "%s:" },
{ "tlabeldef", "l%N%05d:" },
{ "tlabel", "l%N%05d" },
{ "fileprelude",
"; Generated using the z80asm/z88dk tokens.\n"
"\tEXTERN __muluchar_rrx_s\n"
"\tEXTERN __mulschar_rrx_s\n"
"\tEXTERN __mulint_rrx_s\n"
"\tEXTERN __mullong_rrx_s\n"
"\tEXTERN __divuchar_rrx_s\n"
"\tEXTERN __divschar_rrx_s\n"
"\tEXTERN __divsint_rrx_s\n"
"\tEXTERN __divuint_rrx_s\n"
"\tEXTERN __divulong_rrx_s\n"
"\tEXTERN __divslong_rrx_s\n"
"\tEXTERN __rrulong_rrx_s\n"
"\tEXTERN __rrslong_rrx_s\n"
"\tEXTERN __rlulong_rrx_s\n"
"\tEXTERN __rlslong_rrx_s\n"
},
{ "functionheader",
"; ---------------------------------\n"
"; Function %s\n"
"; ---------------------------------"
},
{ "functionlabeldef", "%s:" },
{ "zero", "$00" },
{ "one", "$01" },
{ "ascii", "DEFM \"%s\"" },
{ "ds", "DEFS %d" },
{ "db", "DEFB" },
{ "dbs", "DEFB %s" },
{ "dw", "DEFW" },
{ "dws", "DEFW %s" },
{ "immed", "#" },
{ "constbyte", "$%02X" },
{ "constword", "$%04X" },
{ "immedword", "$%04X" },
{ "immedbyte", "$%02X" },
{ "hashedstr", "%s" },
{ "lsbimmeds", "%s & $FF" },
{ "msbimmeds", "%s / 256" },

{ "bankimmeds", "\nBANK_%s\n" },
{ "module", "\nMODULE %s\n" },
{ "area", "\nSECTION %s\n" },
{ "areadata", "\nSECTION %s\n" },
{ "areacode", "\nSECTION %s\n" },
{ "areahome", "\nSECTION %s\n" },
{ NULL, NULL }
};


Z80/MAIN.C:

L256
case ASM_TYPE_Z80ASM:
dbuf_printf (&buffer, "code_compiler_bank_%d", token.val.int_val);
break;

L512
_G.asmType = ASM_TYPE_Z80ASM;


L812 -- this affects section names for all z80 ports so not the right way
PORT z80_port = {
TARGET_ID_Z80,
"z80",
"Zilog Z80", /* Target name */
NULL, /* Processor name */
{
glue,
FALSE,
NO_MODEL,
NO_MODEL,
NULL, /* model == target */
},
{ /* Assembler */
_z80AsmCmd,
NULL,
"-plosgffwy", /* Options with debug */
"-plosgffw", /* Options without debug */
0,
".asm"},
{ /* Linker */
_z80LinkCmd, //NULL,
NULL, //LINKCMD,
NULL,
".rel",
1,
_crt, /* crt */
_libs_z80, /* libs */
},
{ /* Peephole optimizer */
_z80_defaultRules,
z80instructionSize,
0,
0,
0,
z80notUsed,
z80canAssign,
z80notUsedFrom,
},
{
/* Sizes: char, short, int, long, long long, ptr, fptr, gptr, bit, float, max */
1, 2, 2, 4, 8, 2, 2, 2, 1, 4, 4},
/* tags for generic pointers */
{0x00, 0x40, 0x60, 0x80}, /* far, near, xstack, code */
{
"IGNORE", //"XSEG",
"IGNORE", //"STACK",
"code_compiler", //"CODE",
"bss_compiler", //"DATA",
NULL, /* idata */
NULL, /* pdata */
NULL, /* xdata */
NULL, /* bit */
"IGNORE", //"RSEG_ABS", //"RSEG (ABS)",
"code_crt_init", //"GSINIT", /* static initialization */
NULL, /* overlay */
"code_crt_exit", //"GSFINAL",
"IGNORE", //"HOME",
NULL, /* xidata */
NULL, /* xinit */
NULL, /* const_name */
"IGNORE", //"CABS_ABS", //"CABS (ABS)", /* cabs_name */
"IGNORE", //"DABS_ABS", //"DABS (ABS)", /* xabs_name */
NULL, /* iabs_name */
"IGNORE", //"INITIALIZED", /* name of segment for initialized variables */
"data_compiler", //"INITIALIZER", /* name of segment for copies of initialized variables in code space */
NULL,
NULL,
1, /* CODE is read-only */
1 /* No fancy alignments supported. */
},
{NULL, NULL},
{
-1, 0, 0, 4, 0, 2},
/* Z80 has no native mul/div commands */
{
0, -1},
{
z80_emitDebuggerSymbol},
{
255, /* maxCount */
3, /* sizeofElement */
/* The rest of these costs are bogus. They approximate */
/* the behavior of src/SDCCicode.c 1.207 and earlier. */
{4, 4, 4}, /* sizeofMatchJump[] */
{0, 0, 0}, /* sizeofRangeCompare[] */
0, /* sizeofSubtract */
3, /* sizeofDispatch */
},
"_",
_z80_init,
_parseOptions,
_z80_options,
NULL,
_finaliseOptions,
_setDefaultOptions,
z80_assignRegisters,
_getRegName,
NULL,
_keywords,
0, /* no assembler preamble */
NULL, /* no genAssemblerEnd */
0, /* no local IVT generation code */
0, /* no genXINIT code */
NULL, /* genInitStartup */
_reset_regparm,
_reg_parm,
_process_pragma,
NULL,
_hasNativeMulFor,
hasExtBitOp, /* hasExtBitOp */
oclsExpense, /* oclsExpense */
TRUE,
TRUE, /* little endian */
0, /* leave lt */
0, /* leave gt */
1, /* transform <= to ! > */
1, /* transform >= to ! < */
1, /* transform != to !(a == b) */
0, /* leave == */
FALSE, /* Array initializer support. */
0, /* no CSE cost estimation yet */
_z80_builtins, /* builtin functions */
GPOINTER, /* treat unqualified pointers as "generic" pointers */
1, /* reset labelKey to 1 */
1, /* globals & local statics allowed */
9, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */
PORT_MAGIC
};


SDCCglue.c:

L215 -- just for z88dk, changes initializer names to real c names
SNPRINTF (newSym->name, sizeof (newSym->name), "__xinit_%s", sym->name);
// SNPRINTF (newSym->rname, sizeof (newSym->rname), "__xinit_%s", sym->rname);
SNPRINTF (newSym->rname, sizeof (newSym->rname), "%s", sym->rname);

L319 -- just for z80asm, changes equates to defc
// dbuf_printf (&map->oBuf, "%s\t%s\t0x%04x\n", sym->rname, equ, SPEC_ADDR (sym->etype));
dbuf_printf (&map->oBuf, "defc %s\t%s\t0x%04x\n", sym->rname, equ, SPEC_ADDR (sym->etype));

L1688 - just for z80asm, suppress PUBLICs when symbol was imported as EXTERN
- this one may not be fully correct (consider header with extern and implementation in file)
for (sym = setFirstItem (publics); sym; sym = setNextItem (publics))
if (! IS_EXTERN (sym->etype)) tfprintf (afile, "\t!global\n", sym->rname);

L1975 -- just for z88dk, suppresses the initialized section
/* copy the initialized segment */
// if (initialized)
// {
// fprintf (asmFile, "%s", iComments2);
// fprintf (asmFile, ";%s ram data\n", mcs51_like ? " internal" : "");
// fprintf (asmFile, "%s", iComments2);
// dbuf_write_and_destroy (&initialized->oBuf, asmFile);
// }



------------------------------------------------------------------------------
Dive into the World of Parallel Programming. The Go Parallel Website,
sponsored by Intel and developed in partnership with Slashdot Media, is your
hub for all things parallel software development, from weekly thought
leadership blogs to news, videos, case studies, tutorials and more. Take a
look and join the conversation now. http://goparallel.sourceforge.net/
pscust
Well known member
Posts: 194
Joined: Thu Jun 23, 2011 3:34 pm

Post by pscust »

On Sat, Jan 31, 2015 at 5:05 AM, alvin (alvin_albrecht@...) <
lists@...> wrote:
We don't seem to have the source for zpragma and z80nm committed?
z80nm is committed and supports the last z80asm object file format. What
problem are you facing with it?
pscust
Well known member
Posts: 194
Joined: Thu Jun 23, 2011 3:34 pm

Post by pscust »

On Sat, Jan 31, 2015 at 5:05 AM, alvin (alvin_albrecht@...) <
lists@...> wrote:
We don't seem to have the source for zpragma and z80nm committed?
z80nm is committed and supports the last z80asm object file format. What
problem are you facing with it?
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

* most sdcc primitives are committed. No float yet, no longlong yet. For the 8-bit divisions I stuck with 16-bit divides (like the sdcc source) as I'm not sure if sdcc requires 16-bit results. We do have 8-bit divide math in the library if that's not the case.

* I removed "--reserve-regs-iy" from the flag list zcc automatically adds when compiling with sdcc and instead moved that decision to the target.

I found out that if sdcc uses iy, it expects it to be preserved across function calls, just like ix.

So for the zx target, when building with sdcc_ix, "--reserve-regs-iy" is not applied. The library will use ix, and if user code uses iy (or the sp1 library is used), the command line must have "--reserve-regs-iy" manually added. If the ROM interrupt is running, iy should also be reserved.

When building with sdcc_iy, "--reserve-regs-iy" is always applied.

For the embedded target, the situation is the same.


I've managed to compile all of my test programs with sdcc but a few bugs did show up.

1. sdcc is frequently generating out of range jrs. I don't know if it is relying on the underlying assembler to change these to jumps? But we'll need this feature Paulo :)

2. incorrect code is being generated for ioctls:

;beepfx.c:100: ioctl(1, IOCTL_OTERM_BCOLOR, INK_WHITE | PAPER_BLUE);
ld hl,+$000F
push hl
ld hl,+$1202
push hl
ld l, +$01
push hl
call _ioctl
ld hl,#6
add hl,sp
ld sp,hl

The first parameter is an int but sdcc is generating "ld l, +$01" which has MSB != 0.

3. incorrect code generation in the open_memstream.c example where a local variable is not properly fetched:

;open_memstream.c:31: fseek(stream, 0, SEEK_SET);
ld hl,+$0000
push hl
ld de,+$0000
push de
push hl
push de ; <--- stream == 0
call _fseek
ld hl,#8
add hl,sp
ld sp,hl

I'll try to put these together concisely for a bug report.


But other than that, it's looking like optional sdcc compilation is around the corner.



Some other notes:

You can pass any compile time flags to sdcc just by adding them to the zcc compile line. In particular, to change the optimization level, just add "--max-allocs-per-node200000". Note no spaces there so that zcc passes it along properly. The default optimization is 3000.

The sccz80 compiled programs were all smaller by up to a couple hundred bytes but the sdcc compiled programs were faster. I expected to see that as the code I am writing is tailored for sccz80 and the combination of better calling conventions for library routines (of which there are many calls in these brief programs) and sccz80's use of subroutines to do work does save bytes. But whether that translates to larger examples of typical c code, I'd like to see.



------------------------------------------------------------------------------
Dive into the World of Parallel Programming. The Go Parallel Website,
sponsored by Intel and developed in partnership with Slashdot Media, is your
hub for all things parallel software development, from weekly thought
leadership blogs to news, videos, case studies, tutorials and more. Take a
look and join the conversation now. http://goparallel.sourceforge.net/
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

3. incorrect code generation in the open_memstream.c example where a local variable is not properly fetched:

;open_memstream.c:31: fseek(stream, 0, SEEK_SET);
ld hl,+$0000
push hl
ld de,+$0000
push de
push hl
push de ; <--- stream == 0
call _fseek
ld hl,#8
add hl,sp
ld sp,hl
This one is caused by a bad rule in z80rules.2 of the peephole optimizer.

Before optimization, output from sdcc:

CORRECT:

push de
ld hl,+$0000
push hl
ld hl,+$0000
push hl
ld hl,+$0000
push hl
push de
call _fseek
ld hl,#8
add hl,sp
ld sp,hl

AFTER Z80RULES.2 IS APPLIED

INCORRECT:

push de
ld hl,+$0000
push hl
ld de,+$0000
push de
push hl
push de
call _fseek
ld hl,#8
add hl,sp
ld sp,hl



------------------------------------------------------------------------------
Dive into the World of Parallel Programming. The Go Parallel Website,
sponsored by Intel and developed in partnership with Slashdot Media, is your
hub for all things parallel software development, from weekly thought
leadership blogs to news, videos, case studies, tutorials and more. Take a
look and join the conversation now. http://goparallel.sourceforge.net/
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I've found all the changes necessary to make sdcc compatible with z80asm.

===
z80/mappings.i:

L316
z80asm_mappings


z80/main.c

L256
pragma changes bank?

L269
home bank when bankswitching?

L515
set correct asm type

L857
z80_port section names changed

SDCCglue.c:

L215
change initializer names to real c names

L309
change '=' to defc

L1489
change '=' to defc

L1692
keep called extern functions out of the public declarations
(fails for extern in header followed by definition in c file)

L1975
suppress the initialized segment
(set initialized to zero?)


z80/gen.c:

L586
change '=' to defc

L1858
(no change but strtoul means hex constants need 0x)

L2337
!immedword or !immedwords?

===


There is one outstanding bug that I posted in sdcc's bug tracker. The next thing will be how best to make those changes and whether those changes are of interest to sdcc or we should fork. I can see two ways to activate the 'z88dk/z80asm options'. One is to detect what name was used to start sdcc. If the name is 'zsdcc' then we choose the z88dk options. Another way is to introduce another asm option "--asm=z80asm_z88dk". Changes have to be made not only to be compatible with z80asm but also to be compatible with z88dk. So leaving a separate --asm=z80asm will allow the necessary changes to be made to work with z80asm but keeping the sdcc section assignments, etc. Basically like running sdcc as it is now but with z80asm as assembler. For z80asm_z88dk we'd also make changes to the section assignments and in particular to how the initialized/initializer sections are output.

For the peephole optimizer, I don't think it's too hard to change the rules to zilog syntax (asxx is doing "ld r, n (iy)", etc, instead of "ld r,(iy+n)"). The other thing is that the z80 code generator has hard-coded the immediate constant symbol "#" instead of using the !immed macro defined for the backend assemblers. This isn't a problem for us except I had to defined the immediate mappings in mappings.i to use '+' instead of "#". I don't know if that means the peephole rules will not always be able to be applied as is. The thing we could do is go through the entire z80 code generator and replace the hard-coded # symbols with !immed macros and then the peephole rules would only have to deal with + instead of a possible mixture of + and #. I don't know how Philip would feel about that or if this is unwise for some reason.



------------------------------------------------------------------------------
Dive into the World of Parallel Programming. The Go Parallel Website,
sponsored by Intel and developed in partnership with Slashdot Media, is your
hub for all things parallel software development, from weekly thought
leadership blogs to news, videos, case studies, tutorials and more. Take a
look and join the conversation now. http://goparallel.sourceforge.net/
Philipp Klaus Krause

Post by Philipp Klaus Krause »

On 03.02.2015 06:57, alvin (alvin_albrecht@...) wrote:
I've found all the changes necessary to make sdcc compatible with
z80asm.

=== z80/mappings.i:

L316 z80asm_mappings


z80/main.c

L256 pragma changes bank?

L269 home bank when bankswitching?

L515 set correct asm type

L857 z80_port section names changed

SDCCglue.c:

L215 change initializer names to real c names

L309 change '=' to defc

L1489 change '=' to defc

L1692 keep called extern functions out of the public declarations
(fails for extern in header followed by definition in c file)

L1975 suppress the initialized segment (set initialized to zero?)


z80/gen.c:

L586 change '=' to defc

L1858 (no change but strtoul means hex constants need 0x)

L2337 !immedword or !immedwords?

===


There is one outstanding bug that I posted in sdcc's bug tracker.
The next thing will be how best to make those changes and whether
those changes are of interest to sdcc or we should fork. I can see
two ways to activate the 'z88dk/z80asm options'. One is to detect
what name was used to start sdcc. If the name is 'zsdcc' then we
choose the z88dk options. Another way is to introduce another asm
option "--asm=z80asm_z88dk". Changes have to be made not only to be
compatible with z80asm but also to be compatible with z88dk. So
leaving a separate --asm=z80asm will allow the necessary changes to
be made to work with z80asm but keeping the sdcc section assignments,
etc. Basically like running sdcc as it is now but with z80asm as
assembler. For z80asm_z88dk we'd also make changes to the section
assignments and in particular to how the initialized/initializer
sections are output.

For the peephole optimizer, I don't think it's too hard to change the
rules to zilog syntax (asxx is doing "ld r, n (iy)", etc, instead of
"ld r,(iy+n)"). The other thing is that the z80 code generator has
hard-coded the immediate constant symbol "#" instead of using the
!immed macro defined for the backend assemblers. This isn't a
problem for us except I had to defined the immediate mappings in
mappings.i to use '+' instead of "#". I don't know if that means the
peephole rules will not always be able to be applied as is. The
thing we could do is go through the entire z80 code generator and
replace the hard-coded # symbols with !immed macros and then the
peephole rules would only have to deal with + instead of a possible
mixture of + and #. I don't know how Philip would feel about that or
if this is unwise for some reason.



------------------------------------------------------------------------------
You can prepare patches, and then we can see what we can apply in sdcc.

Personally, I'm not a big fan of supporting different types of assembler
syntax in code generation, since it causes problems with the pephole
optimizer. IMO, it would be better to translate the assembler syntax
after peephole optimization. It looks to me as if that should be easy
with regular expressions, so in the long term, that looks like a better
solution to me. The two options then would be translation in sdcc (using
some regular expression library, maybe the one from boost) or using
external tools (e.g. sed).

Philipp
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Personally, I'm not a big fan of supporting different types of assembler
syntax in code generation, since it causes problems with the pephole
optimizer.
Yes I can see that.
IMO, it would be better to translate the assembler syntax
after peephole optimization. It looks to me as if that should be easy
with regular expressions, so in the long term, that looks like a better
solution to me. The two options then would be translation in sdcc (using
some regular expression library, maybe the one from boost) or using
external tools (e.g. sed).
I agree we could probably do this with a post-translation. The only downside is we would not be able to make z88dk-specific changes that may be required in the source down the road. I'm also not sure if that will ever happen or be necessary.

I think what I will do is do the necessary source code changes for now since the changes will only take me an evening to do. I'm keen on seeing sdcc 100% working so this will solve that problem :) Then we can have a look again and decide if the changes are too invasive / bad idea / better to pursue post-processing.

Regarding the peephole optimization. Another idea is to revert back to '#' for immediates and then after peephole optimization is complete, perform a text substitution '#'->'+' when z80asm is the target. Then no changes need to be made to the code generator and the only difference in the peephole rules will be changing to zilog syntax. This would mean we would have to maintain a separate peeph-z80-zilog.def rules file but maintenance would be much simpler.

I think I can confine modifications to the z80 backend only. The thought I have now is, in the z80 backend when --asm=z80-asm is parsed I change to the z80asm mappings but also check if sdcc was started with name "zsdcc". If that is the case I enable the z88dk-required changes. I think I can do that without impacting the rest of sdcc by supplying a custom "glue" function that will be a simpler modified version of SDCCglue's glue(). This way all changes stay in src/z80 and stay away from the code generator.

I'll do it and we can see how it looks.

One other quick question: it seems like the code generator is relying on the assembler to change out of range jr to jp. Is this the case? In three of my test programs out of 11, out of range jrs were generated without any additional code added at our end.



------------------------------------------------------------------------------
Dive into the World of Parallel Programming. The Go Parallel Website,
sponsored by Intel and developed in partnership with Slashdot Media, is your
hub for all things parallel software development, from weekly thought
leadership blogs to news, videos, case studies, tutorials and more. Take a
look and join the conversation now. http://goparallel.sourceforge.net/
Philipp Klaus Krause

Post by Philipp Klaus Krause »

On 03.02.2015 19:38, alvin (alvin_albrecht@...) wrote:
One other quick question: it seems like the code generator is
relying on the assembler to change out of range jr to jp. Is this
the case? In three of my test programs out of 11, out of range jrs
were generated without any additional code added at our end.
No, quite the opposite: The code generator only emits jp (except for a
few cases where it can be sure that jr or djnz is in range - this only
happens if the jump target comes from the same line of C source as the
jump; the only instructions for which it happens are >>, <<, memset(),
strcpy(), strncpy(), strchr()).
The peephole optimizer changes jp to jr if it makes sense.

Philipp
Post Reply