How to print UDG characters with console directives in newlib?
How to print UDG characters with console directives in newlib?
I have been able to redefine characters and use them with the classic lib through
the printf command by using ASCII codes starting from 128.
This does not work if I use the new lib. Which characters correspond to user defined characters when using the new lib?
Fabrizio
the printf command by using ASCII codes starting from 128.
This does not work if I use the new lib. Which characters correspond to user defined characters when using the new lib?
Fabrizio
Are you using startup=0/1 ? (default=0).Fabrizio wrote:I have been able to redefine characters and use them with the classic lib through
the printf command by using ASCII codes starting from 128.
This does not work if I use the new lib. Which characters correspond to user defined characters when using the new lib?
These drivers do not have any concept of UDGs. Instead you can change the font address to point at graphics characters and print those.
There are a couple of ways to do this. One will work with both startup=0 and startup=1:
Code: Select all
// zcc +zx -vn -clib=new test.c -o test -create-app
// zcc +zx -vn -clib=sdcc_iy -SO3 --max-allocs-per-node200000 test.c -o test -create-app
#pragma printf = "" // list converters you need to reduce printf size
#include <stdio.h>
#include <stropts.h>
#include <arch/zx.h>
unsigned char udg[] = {
0x55,0xaa,0x55,0xaa, 0x55,0xaa,0x55,0xaa, // hash
0xaa,0xaa,0xaa,0xaa, 0xaa,0xaa,0xaa,0xaa, // vertical strips
};
void *old_font;
void main(void)
{
zx_cls(INK_BLACK | PAPER_WHITE);
printf("Testing... ");
old_font = (void*)ioctl(1, IOCTL_OTERM_FONT, udg - 'A'*8);
printf("ABABAB\n");
ioctl(1, IOCTL_OTERM_FONT, old_font);
printf("Back to normal...\n");
}
This second method will only work with startup=1. This is because the font will be changed in the text stream with control codes and only startup=1 understands control codes.
Code: Select all
// zcc +zx -vn -startup=1 -clib=new test.c -o test -create-app
// zcc +zx -vn -startup=1 -clib=sdcc_iy -SO3 --max-allocs-per-node200000 test.c -o test -create-app
#pragma printf = "%c" // list converters you need to reduce printf size
#include <stdio.h>
#include <stropts.h>
#include <arch/zx.h>
unsigned char udg[] = {
0x55,0xaa,0x55,0xaa, 0x55,0xaa,0x55,0xaa, // hash
0xaa,0xaa,0xaa,0xaa, 0xaa,0xaa,0xaa,0xaa, // vertical strips
};
extern unsigned char font_8x8_rom[]; // this points at the zx font in rom
#define NEWFONT_W "\x02%c%c"
#define ARG_W(x) (x)&0xff, (x)/256
void main(void)
{
zx_cls(INK_BLACK | PAPER_WHITE);
printf("Testing... " NEWFONT_W "ABABAB\n" NEWFONT_W "Back to normal...\n",
ARG_W((unsigned int)(udg-'A'*8)), ARG_W((unsigned int)(font_8x8_rom-256)));
}
Lastly, startup=30 was added in the past week. This is a lightweight driver that uses the rom routine at 0x10 to print a character and will be much smaller than the other drivers. This startup has no input capability (ie no scanf). Because the rom is used, you must not overwrite the system variables area. The default compile address of 32768 is far away from the system variables area at ~24000.
This one behaves like basic - its control codes will be used and it can print UDG characters. In Sinclair basic, codes 144 and up are UDGs that are stored in upper memory. This example here moves the UDGs to a different place by poking a value into the system variables.
Code: Select all
// zcc +zx -vn -startup=30 -clib=new test.c -o test -create-app
// zcc +zx -vn -startup=30 -clib=sdcc_iy -SO3 --max-allocs-per-node200000 test.c -o test -create-app
#pragma printf = "" // list converters you need to reduce printf size
#include <stdio.h>
#include <stropts.h>
#include <arch/zx.h>
#include <z80.h>
unsigned char udg[] = {
0x55,0xaa,0x55,0xaa, 0x55,0xaa,0x55,0xaa, // hash
0xaa,0xaa,0xaa,0xaa, 0xaa,0xaa,0xaa,0xaa, // vertical strips
};
void main(void)
{
zx_cls(INK_BLACK | PAPER_WHITE);
z80_wpoke(23675, (unsigned int)udg); // move UDGs by poking system variable at 23675
printf("Testing... \x90\x91\x90\x91\x90\x91\nBack to normal...\n");
}
I should add for startup=30 and the rom 0x10 driver, you can also change the font address by poking a word to 23606. If I remember right, this will be the font address of ascii code 0 as well. You can have your own character set and redefine some of them to graphics so you don't have to fiddle with codes 144+.
Thanks for the reply!
I would like to use the simple BASIC-like solution but if I change startup=1 to startup=30 I get wrong/unexpected results and the game freezes.
Remark: My code works fine with -startup=1 except for missing USG support.
I am compiling with
zcc +zx -DDEBUG_CHARACTERS -startup=30 -clib=sdcc_iy -vn -DSPECTRUM_32COL -D__SPECTRUM__ -DAMALLOC -create-app -o %deliverables%\ZXSpectrum_32col_sdcc_experimental.prg %mypath%\sleep_macros.c %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
I am using the very latest nightly build as of August 5th.
I would like to use whatever works with -startup=1 + UDG possibly through simple console directives.
Fabrizio
I would like to use the simple BASIC-like solution but if I change startup=1 to startup=30 I get wrong/unexpected results and the game freezes.
Remark: My code works fine with -startup=1 except for missing USG support.
I am compiling with
zcc +zx -DDEBUG_CHARACTERS -startup=30 -clib=sdcc_iy -vn -DSPECTRUM_32COL -D__SPECTRUM__ -DAMALLOC -create-app -o %deliverables%\ZXSpectrum_32col_sdcc_experimental.prg %mypath%\sleep_macros.c %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
I am using the very latest nightly build as of August 5th.
I would like to use whatever works with -startup=1 + UDG possibly through simple console directives.
Fabrizio
What I am doing is something similar to BASIC.
My code works with classic lib but classic lib makes my game unstable (I suspect classic lib is broken but I am not sure).
#define UDG_BASE 0xFF58
static const char player_up[8] = { 24, 60, 24,102,153, 24, 36,102};
void redefine(unsigned long loc, const unsigned char * data)
{
unsigned short i;
for(i=0;i<8;++i)
{
POKE(loc+i,data);
}
}
redefine(UDG_BASE,player_down); // 0x90
I expect 0x90 to be the UDG with my newly redefined character but with newlib and startup=1 if I print 0x90 I get nothing.
On the other end, this works fine with the classic lib.
If possible I do not want to redefine standard characters.
Fabrizio
My code works with classic lib but classic lib makes my game unstable (I suspect classic lib is broken but I am not sure).
#define UDG_BASE 0xFF58
static const char player_up[8] = { 24, 60, 24,102,153, 24, 36,102};
void redefine(unsigned long loc, const unsigned char * data)
{
unsigned short i;
for(i=0;i<8;++i)
{
POKE(loc+i,data);
}
}
redefine(UDG_BASE,player_down); // 0x90
I expect 0x90 to be the UDG with my newly redefined character but with newlib and startup=1 if I print 0x90 I get nothing.
On the other end, this works fine with the classic lib.
If possible I do not want to redefine standard characters.
Fabrizio
Yes there is no concept of UDG in newlib/startup=0 or 1. It doesn't make a special case for character codes 144 and up so you instead have to change character set to point at your udgs as was done in the examples.
startup=30, on the other hand, is using the basic rom and it will do udgs for character codes 144 and up.
I will have a look at these things later tonight - I am out most of the day unfortunately.
startup=30, on the other hand, is using the basic rom and it will do udgs for character codes 144 and up.
I will have a look at these things later tonight - I am out most of the day unfortunately.
I think the crash is down to different use of the control codes. If a print at using out of bounds coordinates is done, the rom may try to return an error to basic and this would probably cause the program to crash. You have to be a little careful with using startup=30 because it's the rom code that is doing the printing.Fabrizio wrote:I am compiling with
zcc +zx -DDEBUG_CHARACTERS -startup=30 -clib=sdcc_iy -vn -DSPECTRUM_32COL -D__SPECTRUM__ -DAMALLOC -create-app -o %deliverables%\ZXSpectrum_32col_sdcc_experimental.prg %mypath%\sleep_macros.c %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
I see defines for CLS as \x0c. There is no clear screen control code in Sinclair basic (and therefore startup=30). Have a look at http://www.worldofspectrum.org/ZXBasicM ... nappa.html for codes less than 32. There are only a few control codes: 6,8-11,13,16-23. To clear screen you'll have to do it with zx_cls or something and move the cursor to 0,0 if that's needed.
Next the part that is probably causing a problem is print at. To move the cursor you send char code 22 (0x16) followed by ROW then COL which is the opposite of startup=0/1. It's confusing but it's done this way because we want all machines in newlib to use normal x,y order but since startup=30 is the basic rom, it expects y,x order. The coordinates themselves are not offset in any way. So if you want to print at y=0,x=1 you send 22,0,1. This will be the same for ink, paper controls, etc.
I think this is the problem. Let me know if things still do not work.
btw, I simplified your compile line like this to avoid the makefile (I'm using windows):
zcc +zx -vn -DDEBUG_CHARACTERS -DSPECTRUM_32COL -startup=30 -clib=sdcc_iy -SO3 -create-app -o acm @zproject.lst
where zproject.lst contains:
sleep_macros.c
display_macros.c
powerUps.c
enemy.c
invincible_enemy.c
level.c
character.c
text.c
missile.c
strategy.c
input.c
main.c
You probably want to remain consistent across all your targets but just fyi to let you know you can do this.
For the sleep_macros, newlib provides a cross platform delay function in z80.h called z80_delay_ms that will do an exact delay in milliseconds. This doesn't count any interrupts that may slow things down further. It uses platforms' declared clock rate to correctly implement the delay. At this time it's not in the classic lib however.
zcc +zx -vn -DDEBUG_CHARACTERS -DSPECTRUM_32COL -startup=30 -clib=sdcc_iy -SO3 -create-app -o acm @zproject.lst
where zproject.lst contains:
sleep_macros.c
display_macros.c
powerUps.c
enemy.c
invincible_enemy.c
level.c
character.c
text.c
missile.c
strategy.c
input.c
main.c
You probably want to remain consistent across all your targets but just fyi to let you know you can do this.
For the sleep_macros, newlib provides a cross platform delay function in z80.h called z80_delay_ms that will do an exact delay in milliseconds. This doesn't count any interrupts that may slow things down further. It uses platforms' declared clock rate to correctly implement the delay. At this time it's not in the classic lib however.
How can I redefine 13 single characters with startup=1?
I am storing UDG as in BASIC at 0xFF58.
Which characters am I redefining with
printf("\x02%c%c", 0x58, 0xFF)
?
All of them starting from character 0? How do I print character 0?
You mean character '0' or character=0.
In any case the above command messes up all characters...
What I need to do is to define the following new characters and have them associated to some non-letter and non-digit characters:
static const char player_down[8] = { 24, 36, 24,102,153, 24, 36,102};
static const char player_up[8] = { 24, 60, 24,102,153, 24, 36,102};
static const char player_right[8] = { 24, 52, 25,118,152, 24, 20, 20};
static const char player_left[8] = { 24, 44,152,110, 25, 24, 40, 40};
static const char ghost[8] = {129,126,165,129,129,189,129,126};
static const char missile_right[8] = { 0, 0, 15,252,252, 15, 0, 0};
static const char missile_left[8] = { 0, 0,240, 63, 63,240, 0, 0};
static const char invincible_ghost[8] = { 60, 66,165,129, 90, 36, 36, 60};
static const char gun[8] = { 0,128,126,200,248,192,128, 0};
static const char powerUp[8] = { 0, 60, 54,223,231,122, 36, 24};
static const char missile[8] = { 0, 0, 8, 56, 28, 16, 0, 0};
static const char bomb[8] = { 60, 66,165,153,153,165, 66, 60};
static const char bubble[8] = { 24, 60, 60, 60,126, 90, 66, 66};
How can I do this without having to redefine the full font set?
Due to the classic lib + old compiler issues, I need to use the new lib.
startup=30 freezes my program and startup=1 seems to work fine except for the udg.
So I would like to use whatever works with startup=1.
Fabrizio
I am storing UDG as in BASIC at 0xFF58.
Which characters am I redefining with
printf("\x02%c%c", 0x58, 0xFF)
?
All of them starting from character 0? How do I print character 0?
You mean character '0' or character=0.
In any case the above command messes up all characters...
What I need to do is to define the following new characters and have them associated to some non-letter and non-digit characters:
static const char player_down[8] = { 24, 36, 24,102,153, 24, 36,102};
static const char player_up[8] = { 24, 60, 24,102,153, 24, 36,102};
static const char player_right[8] = { 24, 52, 25,118,152, 24, 20, 20};
static const char player_left[8] = { 24, 44,152,110, 25, 24, 40, 40};
static const char ghost[8] = {129,126,165,129,129,189,129,126};
static const char missile_right[8] = { 0, 0, 15,252,252, 15, 0, 0};
static const char missile_left[8] = { 0, 0,240, 63, 63,240, 0, 0};
static const char invincible_ghost[8] = { 60, 66,165,129, 90, 36, 36, 60};
static const char gun[8] = { 0,128,126,200,248,192,128, 0};
static const char powerUp[8] = { 0, 60, 54,223,231,122, 36, 24};
static const char missile[8] = { 0, 0, 8, 56, 28, 16, 0, 0};
static const char bomb[8] = { 60, 66,165,153,153,165, 66, 60};
static const char bubble[8] = { 24, 60, 60, 60,126, 90, 66, 66};
How can I do this without having to redefine the full font set?
Due to the classic lib + old compiler issues, I need to use the new lib.
startup=30 freezes my program and startup=1 seems to work fine except for the udg.
So I would like to use whatever works with startup=1.
Fabrizio
Yes that will move the font address to 0xff58 but starting at ascii code 0, which is not printable without some pain.Fabrizio wrote:How can I redefine 13 single characters with startup=1?
I am storing UDG as in BASIC at 0xFF58.
Which characters am I redefining with
printf("\x02%c%c", 0x58, 0xFF)
?
Better maybe is to fit it in with how you're doing it now and map that to code 144 by subtracting 144*8 from that address:
#define UDG_START (0xff58 - 144*8)
printf("\x02%c%c", UDG_START & 0xff, UDG_START/256);
Then if you print codes 144, etc you will see those udgs.
But you will have to change the font address back again to print regular text as in the example with "font_8x8_rom-256".
Because startup=1 does not know about UDGs, it can only print patterns out of one font. The method above has you changing font to print graphics, then back again to print regular text.How can I do this without having to redefine the full font set?
So I would like to use whatever works with startup=1.
However, if you have the memory space you can easily construct a new font in ram by copying the one in rom and then you can put your udg graphics into this ram character set, point the driver at this character set and you won't have to change font at all. The total memory space requirement is 8 bytes per character. If you copy ascii codes 32-127 from the rom, then add your udgs (say N of them) to codes 128 to 128+N-1 then your total memory requirement is 8*(128-32+N) bytes.
Here's an easy way to do it, maybe not the prettiest. Suppose you have UDG_N udgs:
Now you can print like normal and if you print codes 128+ you will see UDGs.
Code: Select all
#define UDG_N 10
// make space for a custom character set
unsigned char my_font[(128-32+UDG_N)*8];
// at initialization copy rom font into this character set
#include <string.h>
extern unsigned char font_8x8_rom[]; // this points at the zx font in rom ascii code 32
memcpy(my_font, font_8x8_rom, (128-32)*8);
// now put your UDG_N udgs into code 128+
memcpy(&font_8x8_rom[128*8], udg_definitions, UDG_N*8);
// point the driver's font to your custom character set
ioctl(1, IOCTL_OTERM_FONT, (void*)(my_font - 256));
Thanks!
Probably you meant:
memcpy(my_font+(128-32)*8, udg_definitions, UDG_N*8);
instead of
memcpy(&font_8x8_rom[128*8], udg_definitions, UDG_N*8);
because with my change I get the redefined characters even if not the order I was expecting.
I can easily fix it anyway.
This is what I did:
#define UDG_N 13
#include <stropts.h>
unsigned char my_font[(128-32+UDG_N)*8];
extern unsigned char font_8x8_rom[];
static const char udg_definitions[] = {
24, 36, 24,102,153, 24, 36,102, // player_down
24, 60, 24,102,153, 24, 36,102,
24, 52, 25,118,152, 24, 20, 20,
24, 44,152,110, 25, 24, 40, 40,
129,126,165,129,129,189,129,126,
0, 0, 15,252,252, 15, 0, 0,
0, 0,240, 63, 63,240, 0, 0,
60, 66,165,129, 90, 36, 36, 60,
0,128,126,200,248,192,128, 0,
0, 60, 54,223,231,122, 36, 24,
0, 0, 8, 56, 28, 16, 0, 0,
60, 66,165,153,153,165, 66, 60,
24, 60, 60, 60,126, 90, 66, 66
};
memcpy(my_font, font_8x8_rom, (128-32)*8);
memcpy(&font_8x8_rom[128*8], udg_definitions, UDG_N*8);
ioctl(1, IOCTL_OTERM_FONT, (void*)(my_font - 256));
Then I used:
GHOST_IMAGE._imageData = 139;
INVINCIBLE_GHOST_IMAGE._imageData = 134;
BOMB_IMAGE._imageData = 138;
PLAYER_IMAGE._imageData = 128;
POWERUP_IMAGE._imageData = 136;
GUN_IMAGE._imageData = 135;
MISSILE_IMAGE._imageData = 137;
LEFT_ENEMY_MISSILE_IMAGE._imageData = 133;
RIGHT_ENEMY_MISSILE_IMAGE._imageData = 132;
BUBBLE_IMAGE._imageData = 140;
Regards
Fabrizio
Probably you meant:
memcpy(my_font+(128-32)*8, udg_definitions, UDG_N*8);
instead of
memcpy(&font_8x8_rom[128*8], udg_definitions, UDG_N*8);
because with my change I get the redefined characters even if not the order I was expecting.
I can easily fix it anyway.
This is what I did:
#define UDG_N 13
#include <stropts.h>
unsigned char my_font[(128-32+UDG_N)*8];
extern unsigned char font_8x8_rom[];
static const char udg_definitions[] = {
24, 36, 24,102,153, 24, 36,102, // player_down
24, 60, 24,102,153, 24, 36,102,
24, 52, 25,118,152, 24, 20, 20,
24, 44,152,110, 25, 24, 40, 40,
129,126,165,129,129,189,129,126,
0, 0, 15,252,252, 15, 0, 0,
0, 0,240, 63, 63,240, 0, 0,
60, 66,165,129, 90, 36, 36, 60,
0,128,126,200,248,192,128, 0,
0, 60, 54,223,231,122, 36, 24,
0, 0, 8, 56, 28, 16, 0, 0,
60, 66,165,153,153,165, 66, 60,
24, 60, 60, 60,126, 90, 66, 66
};
memcpy(my_font, font_8x8_rom, (128-32)*8);
memcpy(&font_8x8_rom[128*8], udg_definitions, UDG_N*8);
ioctl(1, IOCTL_OTERM_FONT, (void*)(my_font - 256));
Then I used:
GHOST_IMAGE._imageData = 139;
INVINCIBLE_GHOST_IMAGE._imageData = 134;
BOMB_IMAGE._imageData = 138;
PLAYER_IMAGE._imageData = 128;
POWERUP_IMAGE._imageData = 136;
GUN_IMAGE._imageData = 135;
MISSILE_IMAGE._imageData = 137;
LEFT_ENEMY_MISSILE_IMAGE._imageData = 133;
RIGHT_ENEMY_MISSILE_IMAGE._imageData = 132;
BUBBLE_IMAGE._imageData = 140;
Regards
Fabrizio