Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sdcc: Integer out of range #290

Closed
Fabrizio-Caruso opened this issue Jul 18, 2017 · 23 comments
Closed

sdcc: Integer out of range #290

Fabrizio-Caruso opened this issue Jul 18, 2017 · 23 comments

Comments

@Fabrizio-Caruso
Copy link

Hi

When compiling my game (https://github.com/Fabrizio-Caruso/ASCII-CHASE)
with sccz80 and sdcc I get two different results/bugs.

Remark 1: My code is probably OK because it compiles and works fine if compiled with CC65.
Remark 2: Both compilers produce lots of wrong warnings.

  • sccz80 produces wrong warnings about pointer/pointer type mismatch of poniter to a typedef struct
  • sdcc only produces warnings about double not available in spectrum.h code

The game compiled with sccz80 is nearly OK. It is a very slow playable game with a crash later in the game that seems totally unrelated to the bug produced by sdcc. Moreover the crash is avoidable with a hack (e.g., inserting a sleep(1) command just before the point of the crash). This is a different issue for me and maybe I should open a different bug report.

On the other hand, with the game compiled with sdcc,
as soon as the game starts, the code produces an "integer out of range" after displaying the highscore (which is 0 stored in a long int).
I wonder whether this is a long int issue with the compiler.

I am compiling the game with:

zcc +zx -SO3 -vn -DAMALLOC -clib=ansi -lmalloc -lndos -create-app -o %deliverables%\ZXSpectrum.prg %mypath%\display_macros.c %mypath%\powerUps.c %mypath%\enemy.c %mypath%\invincible_enemy.c %mypath%\level.c %mypath%\character.c %mypath%\text.c %mypath%\missile.c %mypath%\strategy.c %mypath%\input.c %mypath%\main.c

and

zcc +zx -v -DAMALLOC -compiler=sdcc --reserve-regs-iy -clib=ansi -lmalloc -lndos -create-app -o %deliverables%\ZXSpectrum_SDCC.prg %mypath%\display_macros.c %mypath%\powerUps.c %mypath%\enemy.c %mypath%\invincible_enemy.c %mypath%\level.c %mypath%\character.c %mypath%\text.c %mypath%\missile.c %mypath%\strategy.c %mypath%\input.c %mypath%\main.c

Fabrizio

@Fabrizio-Caruso Fabrizio-Caruso changed the title Integet out of range [sdcc] Integer out of range Jul 18, 2017
@Fabrizio-Caruso Fabrizio-Caruso changed the title [sdcc] Integer out of range sdcc: Integer out of range Jul 18, 2017
@aralbrec
Copy link
Member

sdcc only produces warnings about double not available in spectrum.h code

This is going to be normal. The classic library uses "double" for float types but sdcc only supports one float type which it wants to call "float". So it will automatically change "double" to "float" with a warning. Even though your program doesn't use doubles, there are prototype functions in the headers with doubles in them.

The proper way to deal with this is to use implementation defined types "float_t" and "double_t" for all references to float and double so that they can be defined to whatever is needed for any compiler. However the classic library has a long history that extends beyond these new additions to the standard. The newlib does use these types and that's how it eliminates these warnings.

tldr; you can ignore these warnings.

On the other hand, with the game compiled with sdcc,
as soon as the game starts, the code produces an "integer out of range" after displaying the highscore

I haven't had time to compile your code today but I will try tomorrow. I think this is not a bug but rather an interaction with the spectrum rom that is causing a crash.

If the spectrum rom's interrupt routine is running, the compiler and library cannot use the IY register. The rom interrupt routine uses it to index into a memory block and perform writes that have to do with key scanning. If the program changes IY, you will get random writes to memory which will lead to a crash. You're telling sdcc to not use IY when you add "--reserve-regs-iy" to the compile line. However, this does not stop sdcc from using IY; it only gets sdcc to make its best effort not to use IY.

So actually sdcc is not stable on the spectrum without the rom interrupts being disabled or the interrupt routine being replaced. Unfortunately the classic library relies on the interrupts running because it has dependencies on the system software for key scanning, etc. So there's a bit of a pickle there for the spectrum target in particular. The newlib was written to be independent of all system software to solve all these problems but it only supports 7 or 8 targets rather than the 50 you'd want to target, and it doesn't yet have some of the generic libraries you are using (instead it prefers special portable libraries to do direct key, mouse, joystick scanning etc).

Anyway I will take a look tomorrow to see if some of this can be cleared up.

@aralbrec
Copy link
Member

On the other hand, with the game compiled with sdcc, as soon as the game starts, the code produces an "integer out of range" after displaying the highscore

The sleep() function is modifying the ix register when it shouldn't. After eliminating sleep the program gets further and then crashes again. It may be that one of the integer multiplication functions is not saving ix for zsdcc.

@Fabrizio-Caruso
Copy link
Author

Ideally I would like to target the most common computers with enough ram to run my game, i.e., at least the Spectrum, MSX1 and Amstrad CPC. My game is not strongly dependent on conio.h because I have separated the "graphic drivers" from the business logic. I only use conio as default driver for my targets that do not have specialized code for the graphics (I even wrote a sprite "driver" for the C64). So, if a portable alternative to conio.h is available, I can adapt my code to it without changing too many lines.

@aralbrec
Copy link
Member

What kind of graphics driver do you need?

@Fabrizio-Caruso
Copy link
Author

@aralbrec, I call "drivers" the hardware-dependent portions of my code (..._macros.h/c) which are separated from the rest of the code by an abstraction layer (APIs).
So, if an alternative to conio.h for printing characters (or even sprites) exists, then I can easily adapt my code.
Is conio.h the culprit? How can I print characters in a portable way? printf causes the screen to scroll if I print the 24th line.

@aralbrec
Copy link
Member

So, if an alternative to conio.h for printing characters (or even sprites) exists, then I can easily adapt my code.

It's the cross-platform nature that is the sticking point. Different targets have varying levels of support so if you want to go after all of them with one version of source code you have to aim at the lowest common denominator in the library. That is probably conio but as we've seen there are some issues in there and I'm not sure where the bugs are yet. Part of the issues are coming from the very rapid development over the past year and some of the bugs associated with these changes are being found now.

But you're mentioning sprites and not just ascii. The zx spectrum platform is one of the best supported in z88dk. It has sprites, sound effects, input devices, and other things but they are not necessarily cross-platform. The msx and cpc are less supported as an interest has not been taken by the users to develop it further. Msx users are still doing plain asm by and large except for a few using native z80 c compilers who are unable to port libraries. The cpc has centered around sdcc and cpctelera which is a game-engine (to its detriment actually because sdcc on its own is less capable than as zsdcc in z88dk).

Another very well supported system is the sega master system which has a game engine library called devkitSMS in z88dk. If you have an emulator for it, you can try some examples from the z88dk examples directory: https://github.com/z88dk/z88dk/tree/master/libsrc/_DEVELOPMENT/EXAMPLES/sms/AstroForce

For other systems, the standard c library is quite complete and you'd have to add something to do non-standard things. The classic library, which you've been using, tries to provide things like conio, an ansi terminal, b&w graphics to as many targets as possible. The newlib doesn't do this yet but instead is offering target-specific libraries like devkitSMS for the sms, sp1/bifrost/nirvana for the spectrum, and non-ansi terminals with a set of control codes for text.

So if you are willing to specialize those macros for each target, you can get better results for several systems. The msx too, since it has hardware sprites so you could probably knock up some simple sprite action with little code written. The cpc is like the spectrum in that everything is done in software so if you want sprites there, there is a lot of work involved or you can use cpctelera.

@Fabrizio-Caruso
Copy link
Author

Fabrizio-Caruso commented Jul 22, 2017

Thanks for your hints!
My ultimate goal is to specialize most of my "driver" code but I would rather start with something simple (character based conio or standard graphics) and then little by little specialize some of my "macros".
This is what I have done with the 6502 targets. Simple conio.h has been so far my default implementation. My game is not character-based even though it looks like a character-based game. I have one hardware sprite version for the C64.
Character graphics is simple to implement and that is what conio is my default implementation.

If conio.h is causing such issue and no solution can be found for the Z80 targets. I will have to find an alternative. I can use standard printf but it scrolls the text if used on the 24th line. How can I replace conio with standard C? I only need to print a character at position (x,y) and read input from the keyboard without waiting.

Moreover it seems that conio is super slow on the ZX Spectrum target. Even the splash screen is printed slowly. It should appear instantly. My game runs much faster on any 6502 target by a factor of maybe 10.

I think it is important to provide generic libraries in order to have portable code at least within the Z88dk targets. Even better if the code is portable even outside of the Z88dk boundary.

@suborb
Copy link
Member

suborb commented Jul 23, 2017

The sleep() function is modifying the ix register when it shouldn't. After eliminating sleep the program gets further and then crashes again. It may be that one of the integer multiplication functions is not saving ix for zsdcc.

sleep() is now marked as SAVEFRAME it ends up using l_long_sub which uses ix. There must be another C library function being called that uses long (internally perhaps) and isn't marked as such, or an assembler one that doesn't save ix itself.

Moreover it seems that conio is super slow on the ZX Spectrum target. Even the splash screen is printed slowly. It should appear instantly. My game runs much faster on any 6502 target by a factor of maybe 10.

I think that's because the ANSI screen printing for +zx isn't optimal - it's very generic code to handle the configurable widths/font packing etc. A quick switch over to the non ANSI library goes a lot quicker for +zx:

#define gotoxy(a,b)     printf("\x16%c%c",a+32,b+32)

@Fabrizio-Caruso
Copy link
Author

@suborb How do I compile my code in order to have printf work as described by you?
I compile with
zcc +zx -pragma-define:ansicolumns=32 -D__SPECTRUM__ -DSPECTRUM_32COL -vn -DAMALLOC -clib=sdcc_iy -create-app -o %deliverables%\ZXSpectrum_32col_experimental.prg %mypath%\display_macros.c %mypath%\powerUps.c %mypath%\enemy.c %mypath%\invincible_enemy.c %mypath%\level.c %mypath%\character.c %mypath%\text.c %mypath%\missile.c %mypath%\strategy.c %mypath%\input.c %mypath%\main.c

and printf does not move the cursors. It prints garbage before the string.

I also have an additional problem if I use sdcc_iy in_InKey is not found.
How do I ready from the keyboard without waiting for the input?

@aralbrec
Copy link
Member

aralbrec commented Jul 24, 2017

@suborb How do I compile my code in order to have printf work as described by you?
I compile with

zcc +zx -pragma-define:ansicolumns=32 -D__SPECTRUM__ -DSPECTRUM_32COL -vn -DAMALLOC -clib=sdcc_iy -create-app -o %deliverables%\ZXSpectrum_32col_experimental.prg %mypath%\display_macros.c %mypath%\powerUps.c %mypath%\enemy.c %mypath%\invincible_enemy.c %mypath%\level.c %mypath%\character.c %mypath%\text.c %mypath%\missile.c %mypath%\strategy.c %mypath%\input.c %mypath%\main.c

You're mixing up the two libraries here. If there is clib=sdcc_iy, clib=sdcc_ix or clib=new in your compile line, you are choosing newlib. But newlib does not have the ansi terminal driver which you are selecting options for with pragma-define:ansicolumns=32 and -DSPECTRUM_32COL.

To use the plain driver so that suborb's print works and with the classic library, this compile line should be used:

zcc +zx -vn -DAMALLOC -lndos -create-app -o %deliverables%\ZXSpectrum_32col_experimental.prg %mypath%\display_macros.c %mypath%\powerUps.c %mypath%\enemy.c %mypath%\invincible_enemy.c %mypath%\level.c %mypath%\character.c %mypath%\text.c %mypath%\missile.c %mypath%\strategy.c %mypath%\input.c %mypath%\main.c

Btw, instead of having that long compile line, you can list your source files in a .lst file like this:

zproject.lst

display_macros.c
powerUps.c
enemy.c
invincible_enemy.c
level.c
character.c
text.c
missile.c
strategy.c
input.c
main.c

And then compile with:

zcc +zx -vn -DAMALLOC -lndos -create-app -o %deliverables%\ZXSpectrum_32col_experimental.prg @zproject.lst

This assumes you are compiling in the same directory those source files sit in. Otherwise you can add path to the .lst file too.

I also have an additional problem if I use sdcc_iy in_InKey is not found.
How do I ready from the keyboard without waiting for the input?

Your compile line was choosing newlib because you had clib=sdcc_iy in it. In newlib the correct functions from input.h are in_inkey() and others like in_test_key() (check if key is pressed without waiting). Notice the lower case inkey. This is different from classic where it is in_Inkey(). Yes there was a case change when the newlib was written in order to maintain a common style across the entire library.

Have a look at some of the links in this post. They will make things clearer about compile lines, the two libraries and the different terminals.

@Fabrizio-Caruso
Copy link
Author

Thanks a lot for your support! Unfortunately I am not still able to use in_inkey() :-(

I am not sure I have understood: sdcc is a c lib which is called after the compiler.

What should I include in order to have in_inkey() working in my code?
So far I have used:
#include <input.h>
and/or
#include <spectrum.h>

If I use your command line + some other necessary options
zcc +zx -vn -D__SPECTRUM__ -DAMALLOC -lmalloc -lndos -create-app -o %deliverables%\ZXSpectrum_32col_experimental.prg ...(c files)..

then in_inkey() is not found and the sccz80 (not sdcc) produces the following errors:

sccz80:"d:\Userfiles\fcaruso\Documents\GitHub\ASCII-CHASE\main.c" L:587 Warning:#55:Implicit definit
ion of function 'in_inkey' it will return an int. Prototype it explicitly if this is not what you wa
nt.
Error at file 'C:\Users\fcaruso\AppData\Local\Temp\zccB1F812.asm' line 1363: symbol '_in_inkey' not
defined
Error at file 'C:\Users\fcaruso\AppData\Local\Temp\zccB1F812.asm' line 1365: symbol '_in_inkey' not
defined
Error at file 'C:\Users\fcaruso\AppData\Local\Temp\zccB1F812.asm' line 1428: symbol '_in_inkey' not
defined
Error at file 'C:\Users\fcaruso\AppData\Local\Temp\zccB1F812.asm' line 2123: symbol '_in_inkey' not
defined
4 errors occurred during assembly
Errors in source file c:/z88dk/\lib\spec_crt0.asm:
Error at file 'C:\Users\fcaruso\AppData\Local\Temp\zccB1F812.asm' line 1363: symbol '_in_inkey' not
defined
^ ---- call _in_inkey
Error at file 'C:\Users\fcaruso\AppData\Local\Temp\zccB1F812.asm' line 1365: symbol '_in_inkey' not
defined
^ ---- call _in_inkey
Error at file 'C:\Users\fcaruso\AppData\Local\Temp\zccB1F812.asm' line 1428: symbol '_in_inkey' not
defined
^ ---- call _in_inkey
Error at file 'C:\Users\fcaruso\AppData\Local\Temp\zccB1F812.asm' line 2123: symbol '_in_inkey' not
defined
^ ---- call _in_inkey

@Fabrizio-Caruso
Copy link
Author

Fabrizio-Caruso commented Jul 24, 2017

Thanks a lot for the support guys!

I compile with sdcc compiler (and use getchar just to compile because in_inkey() is not found)
zcc +zx -startup=1 -compiler=sdcc -vn -D__SPECTRUM__ -DAMALLOC -lmalloc -lndos -create-app -o %deliverables%\ZXSpectrum_32col_experimental.prg %mypath%\display_macros.c %mypath%\powerUps.c %mypath%\enemy.c %mypath%\invincible_enemy.c %mypath%\level.c %mypath%\character.c %mypath%\text.c %mypath%\missile.c %mypath%\strategy.c %mypath%\input.c %mypath%\main.c

the game starts but it is unplayable due to getchar() and the lack of a command to clear the screen (which should not be a problem because it may exist as a control code for printf).

For in_inkey() I have tried to include:
input.h -> lib found but in_inkey not found by the compiler/linker
spectrum.h -> same as above
arch/zx.h -> arch/zh.h not found

Maybe I am doing something stupid in my code...

Remark:
My current code has no conio.h dependence and it is now ONLY plain ANSI C (but it uses newlib control sequences in printf).

@aralbrec
Copy link
Member

aralbrec commented Jul 24, 2017

I am not sure I have understood: sdcc is a c lib which is called after the compiler.

What should I include in order to have in_inkey() working in my code?
So far I have used:
#include <input.h>
and/or
#include <spectrum.h>

If I use your command line + some other necessary options
zcc +zx -vn -D__SPECTRUM__ -DAMALLOC -lmalloc -lndos -create-app -o %deliverables%\ZXSpectrum_32col_experimental.prg ...(c files)..

This compile line is using the classic library :)

NEWLIB ONLY HERE

These compile lines will use newlib, with "clib=new" using sccz80 as compiler and "clib=sdcc_iy" using zsdcc as compiler:

zcc +zx -vn -clib=sdcc_iy  -SO3 --max-allocs-per-node200000 -create-app -o %deliverables%\ZXSpectrum_32col_experimental.prg ...(c files)..
zcc +zx -vn -clib=new -create-app -o %deliverables%\ZXSpectrum_32col_experimental.prg ...(c files)..

What is implied here is "-startup=0" which selects a default terminal type. The default is a 32-column driver that does not understand embedded control codes. Change this by adding "-startup=1" like this:

zcc +zx -vn -startup=1 -clib=sdcc_iy  -SO3 --max-allocs-per-node200000 -create-app -o %deliverables%\ZXSpectrum_32col_experimental.prg ...(c files)..
zcc +zx -vn -startup=1 -clib=new -create-app -o %deliverables%\ZXSpectrum_32col_experimental.prg ...(c files)..

This will compile with a 32 column driver that does understand control codes as described here. An example using control codes can be seen here.

In both zsdcc compile lines I am adding high optimization (" -SO3 --max-allocs-per-node200000") which you can eliminate to speed up the compile. zsdcc can be slow otherwise but it does lead to much better code generation.

I'm not sure how big your program is but the default settings have the program compiled at address 32768, place the stack pointer at 65368 which grows downward with the heap automatically sized to extend from the end of your program to address 65368-512 (the default stack size setting is 512 bytes which is, admittedly, quite large). You can see how far your program extends from 32768 by looking at the size of the *_CODE.bin file produced. As your program size increases, available heap size shrinks so if your program is big you need to make sure the memory map stays ok, otherwise you have to do something about using ram below 32768. You can also specify which printf (and scanf) converters are compiled into the binary. By default you will get anything non-floating point like %ld %lx %o,... which, if you do not use, only increases the size of your program. If program size is not a problem you don't have to worry about these things. You control what converters are included with a pragma like #pragma printf = "%s %c %u %lu %llu". Pragmas are best placed in a separate file as described here under the "changing the memory layout" heading. I deliberately showed the "u, lu, llu" converters to let you know that these are treated independently by the pragma.

If you will be printing to the screen directly, eg by using the display file manipulators, you can eliminate stdio entirely by compiling with "-startup=31". This will shrink the size of your program considerably. You can still use sprintf/sscanf and family but printf/scanf will not compile.

Besides the control codes available to printf, there are functions that will clear screen and so on directly. These interact directly with the display file so they are done without the knowledge of the terminal driver. If you clear the screen, eg, the terminal driver will not move its current cursor position back to 0,0. For that to happen you should clear the terminal window using a control code \x0c IIRC or by sending an ioctl() command - ioctl(1, IOCTL_OTERM_CLS) IIRC so that the terminal is doing the work and is aware of what is going on. Nothing will crash if you use the direct functions but just be aware the terminal driver will not know what you are up to.

These functions can be found in arch/zx.h. Some of these functions are new, as in the past few days new, so you may need to update to gain access to some of them.

There are two function types - those that deal with the full screen like zx_cls() and those that deal with a window on screen like zx_cls_wc(). The "wc" indicates a window and you define a window using a struct r_Rect8 from rect.h. This coordinates in this struct are character coordinates, ie x in 0-31 and y in 0-23. There is no bounds checking for sane numbers. Besides these there tend to be things that operate on pixels only, attributes (colours) only or both. So zx_cls() will clear pixels and colours, zx_cls_pix() will clear pixels only and zx_cls_attr() will clear colours only. The scroll up functions all scroll up by character amounts except the scroll up pix ones which scroll up by a pixel amount (multiply character amounts by 8). The functions zx_visit_wc_attr and zx_visit_wc_pix iterate over the characters defined in a rectangle and call your function for each character square with its attribute address or screen address as argument. This was intended as a means to save a region on screen for menus and later restore the screen when the menu is erased.

The test program I used for some of these functions is this one:

zzz.c

// zcc +zx -vn -startup=31 -clib=sdcc_iy -SO3 --max-allocs-per-node200000 zzz.c -o zzz -create-app
// zcc +zx -vn -startup=31 -clib=new zzz.c -o zzz -create-app

#include <arch/zx.h>
#include <input.h>
#include <rect.h>

struct r_Rect8 r = { 10, 18, 10, 9 };

void pause(void)
{
   in_wait_nokey();
   in_wait_key();
}

unsigned char byte;

void visit_attr(unsigned char *m)
{
   *m = byte;
}

void visit_pix(unsigned char *m)
{
   unsigned char i;
   unsigned char *p = (unsigned char *)(15360 + 'A'*8);
   
   for (i = 0; i < 8; ++i)
   {
      *m = *(p++);
      m += 256;
   }
}

void main(void)
{
   unsigned char i;
   
   zx_cls(INK_RED | PAPER_YELLOW);
   pause();
   zx_cls_pix(0xaa);
   pause();
   zx_cls_attr(INK_BLACK | PAPER_CYAN);
   pause();
   zx_cls_wc(&r, INK_YELLOW | PAPER_MAGENTA);
   pause();
   zx_cls_wc_pix(&r, 0x55);
   pause();
   zx_cls_wc_attr(&r, INK_BLACK | PAPER_WHITE);
   pause();
   zx_scroll_wc_up_attr(&r, 3, INK_WHITE | PAPER_BLACK);
   pause();
   for (i=0; i<24; ++i)
      zx_scroll_wc_up_pix(&r, 1, 0);
   pause();
   byte = INK_GREEN | PAPER_BLACK;
   zx_visit_wc_attr(&r, visit_attr);
   pause();
   zx_visit_wc_pix(&r, visit_pix);
   pause();
   zx_scroll_up_attr(5, INK_RED | PAPER_MAGENTA);
   pause();
   for (i=0; i<192; ++i)
      zx_scroll_up_pix(1, 0);
   pause();
}

(Again some of these functions are recent so you may need to update to compile this)

These direct functions would best combine with the display file manipulators to display your text, bypassing stdio completely. However, this is not the portable solution :)

CLASSIC ONLY HERE

zcc +zx -vn -D__SPECTRUM__ -DAMALLOC -lmalloc -lndos -create-app -o %deliverables%\ZXSpectrum_32col_experimental.prg ...(c files)..

This compile line is a classic compile. The "-D__SPECTRUM__" is unnecessary because this is defined when the target is specified ("+zx").

This compile is using the native spectrum driver which understands these control codes. It is a 32/64 column driver that comes up in 64 column mode. You can switch to 32 columns by printing the sequence 1,32 eg with printf("\x01\x20");

In a classic compile, input.h defines in_Inkey(), not in_inkey(). Newlib is defining in_inkey() (here and here) to maintain consistency in the library (remember newlib is an opportunity for us to revisit all library code which has grown in a somewhat undisciplined manner over the years). In general you can tell what is in classic by looking at its header files in z88dk/include and what is in newlib by look at its headers in z88dk/include/_DEVELOPMENT - from here choose a sub-directory corresponding to the compiler being used (they all support the same functions).

Most of the tool-related things spoken about above also apply to the classic lib. Eg, the pragmas.

@Fabrizio-Caruso
Copy link
Author

Fabrizio-Caruso commented Jul 24, 2017

@aralbrec Thanks again!
but I need both printf control codes and whatever function can read the keyboard without waiting.

I have followed the suggestion to use printf with control codes. I have now rewritten my code so that printf with control codes is used instead of conio functions.

I need to have a way to read keyboard input without waiting..

If I compile with clib=sdcc_iy:
zcc +zx -startup=1 -compiler=sdcc -clib=sdcc_iy -vn -DDEBUG_CHARACTERS -D__SPECTRUM__ -DAMALLOC -lmalloc -lndos -create-app -o %deliverables%\ZXSpectrum_32col_experimental2.prg ...c files...

then printf stops understanding control codes for printf but I can get in_inkey. So this is not what I want if I want to use printf but it can be a solution if I use some other solution to print characters.

If I compile it without sdcc_iy
zcc +zx -startup=1 -compiler=sdcc -vn -DDEBUG_CHARACTERS -D__SPECTRUM__ -DAMALLOC -lmalloc -lndos -create-app -o %deliverables%\ZXSpectrum_32col_experimental.prg ...c files...

then printf understands contro codes but I cannot get in_inkey but I can get in_Inkey which waits for the input. :-( So it does not work either.

Should I re-write my functions ditch control codes and use arch/zx.h functions?

Could you please help me to get whatever solution that allows my plain C to do just:

  1. printf with control codes (to print characters at x,y and similar things like clear the screen or select colors)
  2. in_inkey to read from the keyboard

@aralbrec
Copy link
Member

aralbrec commented Jul 24, 2017

but I need both printf control codes and in_inkey to work or any alternative solution to have a way to print characters on the screen and read from the keyboard.
I have followed the suggestion to use printf with control codes. I need to have a way to read keyboard input.

There is no restriction in mixing any of the functions with normal console io but your compile line is mixed up between classic and newlib. You have to separate the two in your mind as they are completely independent and are not equivalent.

As a newlib compile,

If I compile with clib=sdcc_iy:
zcc +zx -startup=1 -compiler=sdcc -clib=sdcc_iy -vn -DDEBUG_CHARACTERS -D__SPECTRUM__ -DAMALLOC -lmalloc -lndos -create-app -o %deliverables%\ZXSpectrum_32col_experimental2.prg ...c files...

-clib=sdcc_iy suggests you want a newlib compile but -compiler=sdcc -DDEBUG_CHARACTERS -D__SPECTRUM__ -DAMALLOC -lmalloc -lndos is all related to classic compiles. (Is -DDEBUG_CHARACTERS your define?)

The proper compile line when using newlib would be this:

zcc +zx -vn -startup=1 -clib=sdcc_iy -create-app -o %deliverables%\ZXSpectrum_32col_experimental2.prg ...c files...

"-lmalloc -lndos" is not necessary with the newlib because everything is in the main zx.lib. "-DAMALLOC" is not necessary because newlib always does automatic malloc by default with this behaviour modified via pragma. "-compiler=sdcc" is not needed because this is already bundled in with "-clib=sdcc_iy".

For this compile line, "-startup=1" is giving you a 32 column driver that understands control codes. DO NOT LOOK AT THE CLASSIC CONTROL CODES as documented for the classic driver. Although they will be similar to the newlib, there are differences. For the newlib control codes look at this page for information instead. The newlib also allows commands to be sent to the driver by ioctl() but there isn't a page fully documenting this yet (parameters, for example) but I also don't think you will need it. If you do, just ask.

input.h will get you functions that directly scan the instantaneous state of the keyboard like:

  • in_inkey() - return ascii code of a single keypress or 0 if no key or multiple keys are pressed. Relatively slower because it does an ascii translation.
  • in_key_pressed(scancode) - fast method for checking if a particular key is pressed. Will work even if multiple keys are pressed simultaneously.
  • in_test_key() - return non-zero if any key is pressed (including shift keys!)
  • in_wait_nokey() - wait until no keys are pressed
  • in_wait_key() - wait for any key to be pressed
  • in_stick_keyboard(scancode struct) - bundles scancodes for up, down, left, right, fire so that the keyboard can be treated like a joystick.

arch/zx.h defines quite a few functions for interacting directly with the display.

As a classic compile,

If I compile with
zcc +zx -startup=1 -compiler=sdcc -vn -DDEBUG_CHARACTERS -D__SPECTRUM__ -DAMALLOC -lmalloc -lndos -create-app -o ...c files...

I get in_inkey but printf won't work any more (all characters are displayed on the first column)

This is using a startup value for the newlib. The classic lib does not have the same crts or startup values.

So a proper compile line using zsdcc would be:

zcc +zx -compiler=sdcc --reserve-regs-iy -vn -DDEBUG_CHARACTERS -D__SPECTRUM__ -DAMALLOC -lmalloc -lndos -create-app -o ...c files...

There may be issues with using zsdcc and interrupts being enabled in the classic compile. To get around these you should install an interrupt service routine that replaces basic's. Leave that one for later.

A compile with sccz80 which should have no issues:

zcc +zx -vn -DDEBUG_CHARACTERS -D__SPECTRUM__ -DAMALLOC -lmalloc -lndos -create-app -o ...c files...

The classic compile gives you the 32/64 column driver with these control codes.

input.h will contain in_Inkey() and other functions. Note that this is a different input.h from the newlib's input.h. Both libraries are completely independent.

@Fabrizio-Caruso
Copy link
Author

Fabrizio-Caruso commented Jul 25, 2017

@aralbrec Thanks a lot! Your support is helping me a lot!
I want to port my game to as many Z80 targets as possible.
With your help I am going to get my game ported to the Spectrum.
With your latest hint I have managed to get the game half-working! Thanks!

In
https://www.z88dk.org/wiki/doku.php?id=platform:zx#the_standard_zx_spectrum_console_driver
it says: "...The screen scrolls when line 24 is “hit”..."

So the control directives suggested by suborb should not be usable on the 24th line if the doc is correct
but it looks like the doc is wrong and my code with suborb suggested function, does work.

My first goal is to implement my game for as many Z80 targets as possible. That is why conio would have been the best option for me. For the moment I do not need fancy graphics. I am happy with simple character graphics. I will add fancier stuff later.

P.S.:
I hope you guys will implement one day a portable and efficient solution like conio for both the classic and newlib. conio would make things so much easier... With a good conio implementation or just a wrapper, a Windows or CC65 program would just compile. Games with just character graphics would be portable across different platforms.

Fabrizio

@aralbrec
Copy link
Member

In
https://www.z88dk.org/wiki/doku.php?id=platform:zx#the_standard_zx_spectrum_console_driver
it says: "...The screen scrolls when line 24 is “hit”..."

So the control directives suggested by suborb should not be usable on the 24th line if the doc is correct
but it looks like the doc is wrong and my code with suborb suggested function, does work.

The doc means line number 24 as opposed to the 24th line. Lines 0-23 are on the display but if you move to line number 24, like if you print a \n on line 23, then the scroll will happen.

The newlib grants more control over the terminal so that you can put it in page made, which means the cursor will wrap to 0,0 instead of scroll, and you can tell it not to clear the screen when it does so.

I will add conio to the to-do list for newlib and hopefully the issues in classic can be sorted out.

@Fabrizio-Caruso
Copy link
Author

Fabrizio-Caruso commented Jul 25, 2017

@aralbrec and @suborb THANKS guys!
I have maneged to compile the first version of my game for the Spectrum that looks working!

The code is pretty much standard ANSI C + classic printf directives + classic input.h.

It may have some hidden bugs coming from either my code or the Z88DK libraries. I will disturb you again if some problems arise again (most likely).

@suborb, my code is written for the most part in a way to separate input/output and anything hardware-specific from the business logic (95% of the code). Thanks to this design, I have been able to get my code for more than a dozen targets (mostly 6502 for the moment).
The business logic part is pure ANSI C with a certain care for memory usage and optimization (unsigned char whenever possible, fewer parameters passed, more globals than usual, etc.). The goal is to have CC65 and Z88DK and CMOC (for 6809) produce decent code.

@Fabrizio-Caruso
Copy link
Author

Fabrizio-Caruso commented Jul 27, 2017

@aralbrec , @suborb so VT100=ANSI
are described in
https://github.com/z88dk/z88dk/blob/master/doc/ZXSpectrumZSDCCnewlib_02_HelloWorld.md
and they are NOT the same as the console directives under
"The standard ZX Spectrum console driver"
in https://www.z88dk.org/wiki/doku.php?id=platform:zx.
even though they look very similar to me.

So if I want to write my code for different Z88DK targets I should either use conio or VT100/ANSI console directives and not the console directives I have used so far.
How do I use VT100/ANSI console? What does clib=ansi mean?
Where do I get doc on the different clib options and when to use them?

I wonder what I need to change in my code in order to port it to MSX, CPC and maybe some other targets.
I have only used @suborb's idea (there was a minor issue with the coordinates order in his code but I fixed it) + a directive to clear the screen and a directive to set the 32 column mode.

@Fabrizio-Caruso
Copy link
Author

Fabrizio-Caruso commented Jul 27, 2017

@aralbrec, @suborb
I have managed to make my code work with both native and vt100 console directives:

NATIVE DIRECTIVES:
zcc +zx -vn -SO3 -DSPECTRUM_NATIVE_DIRECTIVES -DSPECTRUM_32COL -D__SPECTRUM__ -DAMALLOC -lmalloc -lndos -create-app -o ...

VT100/ANSI:
zcc +zx -SO3 --max-allocs-per-node200000 -startup=1 -clib=sdcc_iy -vn -DSPECTRUM_32COL -D__SPECTRUM__ -DAMALLOC -create-app -o ...

The main code difference is
#if defined(SPECTRUM_NATIVE_DIRECTIVES)
#define gotoxy(x,y) printf("\x16%c%c",y+32,x+32);
#else
#define gotoxy(x,y) printf("\x16%c%c",x+1,y+1);
#endif

Performance-wise they are equivalent. At least I cannot tell the difference while playing the game.
So native directives with the old compiler are doing a decent job but I guess they are less portable. Aren't they?

I would like to port my game to other Z80 computers. I would like to start with MSX and CPC.
Is there a chance that VT100/ANSI could work on these targets?

@aralbrec
Copy link
Member

aralbrec commented Jul 27, 2017

Another post with similar topic: #292 (comment)

@aralbrec , @suborb so VT100=ANSI
are described in
https://github.com/z88dk/z88dk/blob/master/doc/ZXSpectrumZSDCCnewlib_02_HelloWorld.md
and they are NOT the same as the console directives under
"The standard ZX Spectrum console driver"
in https://www.z88dk.org/wiki/doku.php?id=platform:zx.
even though they look very similar to me.

The first one is the native spectrum driver using newlib. The second one is the native spectrum driver for classic. Neither is the vt100.

The vt100 control codes are described on pages like this one. Not all of these codes will be supported by the vt100 driver in z88dk but the basic ones should be. To position the cursor on screen, you'll be sending:

Esc[Line;Columnf | Move cursor to screen location v,h
printf("\x1b[%c;%cf", line+1, col+1);

(I think vt100 coordinates are 1-based).

To compile using the vt100 driver in the classic library follow instructions here:
https://www.z88dk.org/wiki/doku.php?id=platform:zx#the_vt_ansi_console_driver

What does clib=ansi mean?
Where do I get doc on the different clib options and when to use them?

"clib" is a shorthand for selecting a set of options from the target's cfg file. When you choose a target machine on the compile line, like +zx, the file zx.cfg will be read to do default settings, select libraries and so on. This is how z88dk distinguishes between target machines. If you look at that zx.cfg file you will see various CLIB options in there. When you select "-clib=ansi" on the compile line, all these options are applied:

CLIB ansi -Cc-standard-escape-chars -pragma-need=ansiterminal -lzx_clib

That's what clib does. These clib values can vary from target to target, however, some are constant across all targets. For example, clib=sdcc_ix,sdcc_iy,new always select the newlib for all targets. clib=ansi is also present for all targets using the ansi terminal in classic compiles.

I would like to port my game to other Z80 computers. I would like to start with MSX and CPC.
Is there a chance that VT100/ANSI could work on these targets?

Give it a try, but try it first on the spectrum!

@aralbrec
Copy link
Member

Maybe I'll just add for the zx you have this:

classic compile + native 32/64 column driver
****classic compile + ansi driver (-clib=ansi ...)
newlib compile + 32 column native driver (clib=sdcc_iy -startup=1 ...)

**** This one is supposed to be portable across machines but may be slow.

@Fabrizio-Caruso
Copy link
Author

This issue was solved long ago. Thanks a lot for the support!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants