News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu

[CPCTELERA] Problems placing data in spare bytes of video memory

Started by sararyCow, 12:05, 13 October 18

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

sararyCow

Hi, I'm having trouble trying to place data in spare bytes of video memory.

Sorry if this is a noob question but I read many CPC games are using this spare bytes to place small sprites and data, so I guess is possible to do in CPCTelera (possibly is not a CPCTelera issue at all).

So if I try to place a variable (or const) in an absolute place outside video memory it works. Example:

__at(0x87D0) const u8 dummy=1;

The example above works as expected. But if I try to place it above 0xC000:

__at(0xC7D0) const u8 dummy=1;

This crashes emulator even before the binary finishes loading and resets it.

However if try to place non-initialized data in this area works fine, and data can be read and written during execution as usual:

__at(0xC7D0) const u8 dummy;

I guess this works because in this case data is not being explicitly overwritten during loading (as this address is not reflected in the "Highest address" after compiling) but this way is not possible to place sprites...

So, how should be the right way to do this? There is an alternate way to place data in absolute memory to achieve the same thing (but without crashing  ;D )? Assembler maybe?

Thanks! :)

ronaldo

As I always say, you can use #CPCtelera to produce anything the CPC can handle. #CPCtelera is not an abstraction layer, is just a framework and a low-level library. You may program everything even in machine language if you wanted, using #CPCtelera. Therefore, there is no restriction at all.

Although having no restrictions is a great advantage, it comes with a prospect: you need to know and control every detail about what you are doing. Whenever part of your project goes beyond your knowledge and control, you may well find problems you don't understand like this one. This is the drawback of having full control: you need full knowledge.

However, I prefer to think of it as an opportunity to learn and improve your abilities.

Your problem here comes from not considering the way in which things are placed in memory. When you call make, you are asking the compiler to convert your sources into a Z80 binary file. This file will later be read into memory by firmware (or by a custom loader). Firmware loader, as many other loaders, work in quite a simple way: they read one byte and place it into a memory location, read the next and place it in next memory location and so on. With this behaviour, the only memory address you control directly is the memory load address (the place where the first byte of your binary is place on). If you want parts of your binary in different places of the memory, the only way is to make your binary bigger enough as to overwrite that memory place with the bytes you wanted to be there.

If you create a program that starts at, say, 0x4000, and you want a byte to be placed at 0xC700, your binary has to be at least 0x8700 bytes in size. If your code takes, for instance, 0x700 bytes, you will then have 0x8000 bytes of zeros and, after that, you dummy variable (your 1).

The problem with this is that it can't be loaded into memory with the default firmware. Default loader will start overwritting memory at 0x4000,0x4001... and so on, with your program, then it will be overwritting with zeros, and then, when it arrives to 0xBxxx (don't remember exact place) it will be replacing firmware variables, concretely those being used by the RUN firmware program to annotate how much bytes it has already read from cassette/disk and where in memory is it writting. When this happens, loading fails. Even not using firmware for loading, you may also have problems if your stack is placed at the default location (from 0xC000 downwards). At some moment, stack will be overwritten with zeros read from cassette/disk and your program will crash on next RET statement.

When you create an uninitialized variable, there is no need to enlarge the binary because there is no need to place a value there. That's the reason why it does not crash in this case. Your binary won't be 0x700 bytes of code followed by 0x8000 bytes of zeros.

If you wanted to use spare memory, the easiest way to go is using a pointer to that memory addresses and cpct_memcpy your data there. You may do it at the start of your program. Then you may use the original place of your data (before being copied to spare memory) freely as your data will be placed at spare memory.

sararyCow

I see, thank you for your explanation. I didn't know how the binary loading system works but it is something I kind of figured out last night during my tests... as you say in this kind of projects full knowledge is mandatory!

I related this problem to video memory overwrite but actual problem is firmware overwrite, this makes a lot of sense. I thought this could be somehow avoided but definitely I can go for to the approach you suggest, I just needed to come into focus of the problem.

As always thanks for your explanations ronaldo, CPCTelera is great itself but with your support becomes even better!  :D

gerald

In fact you issue comes from 3 cumulative things- the use of cont- the fact that you initialise the variable- the fixed address after the firmware block

The fixed address in screen are is fine as long as you do not use const and initialisation. If you do so, your final code will go up to that address and you will overwrite the firmware.If you do not initialise it's fine, but there is no use for an uninitialised const  ;)
However, what you could do is to put your variable there, preferably the global ones.If these are initialised, they should be by the ctr at code start.

Arnaud

If i understand this code will work :

__at(0xC7D0) u8 dummy;

void main()
{
   dummy = 1;
}


gerald

Quote from: Arnaud on 11:43, 14 October 18
If i understand this code will work :

__at(0xC7D0) u8 dummy;

void main()
{
   dummy = 1;
}


Yes !

pelrun

Although if you do that the code and data needed is then *already* available elsewhere in ram and there isn't much benefit in moving it up into the higher location, unless you're planning on overwriting the lower memory later on (or it's a variable that has to be repeatedly reset to the original value, in which case you need a permanent copy of the initial value anyway.)


I'd think it'd be easier to leave the initialised variables in the lower locations and use the higher locations for zero-initialised variables instead.

pacomix

What it would be possible @ronaldo is to have a custom loader for this purpose.

Preprocess the source code as first stage of the build pipeline looking for this kind of const initialization, write it to a binary containing ADDRESS + VALUE and finally modifying it so it leaves the source code only with the pointer construct.

It is a bit of work but the result could solve this situation.


Enviado desde mi iPhone utilizando Tapatalk

ronaldo

Of course, @pacomix. Using a custom loader is always the best solution to place things into memory exactly in the way you want them to be.

For that purpose, #CPCtelera 1.5. includes miniload format cassette loader, by CNGSoft. This feature lets you easily create custom cassette loaders, or use some predefined ones in 2 or 3 configuration lines.
I explained this feature in much detail in my latest lesson. The video has been just published today:



It's in Spanish (someone willing to to provide english subtitles? :)).

Hope it helps ;)

Powered by SMFPacks Menu Editor Mod