Documentation/Examples on how to setup Banks on Spectrum 128

ZX80, ZX 81, ZX Spectrum, TS2068 and other clones
KyotoG
Member
Posts: 16
Joined: Sat Aug 05, 2017 11:10 pm

Post by KyotoG »

It just occurred to me that I can take over the whole of screen (Bank 5 0x4000-0x7FFF) if I use the shadow screen. I see that the screen functions are conditionally compiled with __USE_SPECTRUM_128_SECOND_DFILE which is defined in config_zxn.h as 0. I tried using "#pragma output __USE_SPECTRUM_128_SECOND_DFILE = 1" to override it in my pragma.inc file but I get an compile error that it's already defined. What's the defacto way of overriding this without modifying the config file?
KyotoG
Member
Posts: 16
Joined: Sat Aug 05, 2017 11:10 pm

Post by KyotoG »

Looking at the code, it looks like I'm also going to need to recompile the library, so I think I need to create a new zxn shadow config.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

KyotoG wrote:Looking at the code, it looks like I'm also going to need to recompile the library, so I think I need to create a new zxn shadow config.
Yes that's a library build time switch.

Edit target/zxn/config/config_target.m4
Change "define(`__USE_SPECTRUM_128_SECOND_DFILE', 0)" to "define(`__USE_SPECTRUM_128_SECOND_DFILE', 1)"
(the .bak file is there so you can easily restore later)

Then rebuild the library by going to directory libsrc/_DEVELOPMENT and running:

Winmake zxn (windows)
make TARGET=zxn (other)

Things should work but this is something else I haven't tested. Keep in mind bank 7 must be paged in when anything is drawn to screen because that's where the 2nd display file is.


Now, this 2nd display file on the 128k spectrum is only a standard spectrum display, ie 256x192 pixel with 32x24 attrs. I think you're using a Timex hi colour display 256x192 with 32x192 attrs. Normally you cannot place the Timex display up there. However, I hear that the Next does do this but I don't know if that's right yet. Certainly I expect the emulators won't put a Timex display up there, so your plan probably will not work for now.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

KyotoG wrote:Great info! I think, at least for developing, having the 4x8 font will be useful for debugging. All that I really need is the ability to render a font to the screen at a specified position, and to be able to generate strings with numbers (dec and/or hex) inside it, with at least the font residing in another bank (which looks easy). If the code is small enough I could probably get away with it in the main but a bit of a waste really as my program won't have heavy font usage. A small wrapper to change banks and call into the bank would work best.
If you want to roll your own, there is code in the library to help you put things in the right places on screen.

Eg:

The 64-column driver runs this code to print a character. You can see the driver turns the 64-col x coordinate into a 32-col one and then deals in that as that's the natural screen resolution.

The display file address manipulators turn pixel and character coordinates into memory addresses that you can then use to plot directly to the display. Some more info here.

You can use sprintf to build strings to display and this will be smaller when the stdin/stdout drivers are eliminated. Or you can go lower level and use the stdlib functions like utoa. Into assembly you can look up the l_utoa function in libsrc/_DEVELOPMENT/l/ascii/num_to_text
Oh, I ended up moving the stack to the 126 byte hole (0x8101-0x8180) in between the ISR jump table (0x8000) and Jump instruction (0x8181). Like you said, it should be plenty as I'm not doing any recursion, and only call 2-3 function deep at max.
Keep in mind printf reserves space on the stack to generate portions of output strings. I think it will use up to 50 bytes and more if floating point numbers are printed.


I did try moving the 64-column driver into bank 3. I did succeed, you can see how I did it here:
https://drive.google.com/file/d/0B6XhJJ ... sp=sharing

It wasn't as easy as I thought it would be. It never is :P I don't have time to explain it all in detail but in short:

The library creates a bunch of small sections for individual modules. Things like code_string for string functions, etc. There are sections for the drivers. Moving the sections for the drivers to another bank would move all that code there. To do that you have to change to memory map, so I did that in "mmap.inc". This file is an edited copy of the standard memory map the library uses ( https://github.com/z88dk/z88dk/blob/mas ... el_z80.inc ) with the includes expanded and the driver sections moved to bank 3. A pragma in "zpragma.inc" indicates that a memory map is being supplied.

Next I had to subclass the 64 column driver on startup=5. Subclassing is a simple matter of writing a new function and then jumping to the old one after doing some sort of processing. You can see that in "zx_01_output_char_64_tty_z88dk_mine.asm". The driver may send itself many messages in the course of carrying out some task so this code keeps track of whether it's just been called or if it's in the middle of calling itself using the flag byte. If it's the first time entering, it must save the current bank config and page in bank 3 where the driver code is. On exit from the driver, it must restore the bank config.

Next I had to instantiate the correct drivers in the crt. Another pragma in "zpragma.inc" tells the crt that the driver instantiation section is being provided in "crt_driver_instantiation.asm.m4". This is some m4 magic that sets up data structures for a driver. The instantiation section determines what driver is placed on each file descriptor, starting at 0. I put nothing on 0 (so no stdin) and my own instantiator on fd=1 (stdout). This driver instantiation section was modified from the one in crt5 for startup=5.

Last, I had to supply the macro for the driver instantiation in the "zx_01....asm.m4" file. This is a straight copy of the library's m4 macro for the 64 column driver. The only change is to replace the two instances of "zx_01_output_char_64_tty_z88dk" with "zx_01_output_char_64_tty_z88dk_mine". This way the driver will jump to my routine instead of the library's.

If you want to do this for your code I think there is enough to go on here. But admittedly it is an advanced topic!
KyotoG
Member
Posts: 16
Joined: Sat Aug 05, 2017 11:10 pm

Post by KyotoG »

Thanks for tips. I went into the _DEVELOPMENT folder and run make there but the make TARGET=zxn in the root is obviously more convenient.

Yes, I'm running in Timex hicolour with ULA+ so that could be a show stopper. I'm also in the process of porting some C render code I wrote into z80 and moving it to a bank so, at least for now, I'm going to put a hold on using the shadow screen until I get that all nailed down. Once I have a better grasp of how much memory I have left, I'll decide on which way to go. I hope it's enough, as using the shadow screen means that I won't be able to call into the render code in another 0xc000 bank, so I would have to do something like compile the code ORGed at $4000 that site in a bank and copy it, or load it at runtime.
Post Reply