News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_Cwiiis

Porting a CPC Plus disk game to cart (bank switching + orgams questions)

Started by Cwiiis, 12:50, 21 March 23

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Cwiiis

I've been working on a Plus game and it's basically complete, but I'd really like to be able to publish a cartridge image for it. The problem is, I'd not really considered the memory mapping limitations of the cart when I started and I'm also using Orgams, which may be more limited than other assemblers in this area...

My current memory map is:
&0100 - Screen (2 banks, 384x256 resolution)
&8000 - Code
&C000 - Scratch area

I also use the spare ram at the end of each line. I only have one self-modifying piece of code, that I plan on conditionally replacing for the cart version (possibly I'll just copy it to ram, we'll see).

As I understand it, Plus carts are mapped as lower rom at &0000 and a configurable bank at &C000. My entire program fits in 16K, so my plan was to just have a simple boot-strap at &0000 that jumps to &C000 and then immediately unmap lower rom... But I still need to put my data somewhere... Ideally, I'd like to be able to switch the lower rom to a configurable bank on the cartridge, read what I need to ram and then unmap it - is this possible? i.e. to switch the usual way of doing things and run primarily out of C000 and switch banks on 0000.

My second problem is specifically with Orgams - I have some pre-initialised variables. I could shadow all of them and manually write code to initialise them, but I'd love to be able to use some kind of `org` magic to copy them to ram without having to write a bunch of duplicate labels... Is this possible?

So I have something like:

```
VarA WORD &1234
VarB BYTE "String",0
VarC BYTE 99
etc.
```

and it'd be great to just be able to stick labels around that block, stick an `org &8000` or similar at the top and use the labels to easily copy that to ram and have the compiler treat the variables as if they were at &8000... Is that possible?

andycadley

Why not just map the lower ROM to #8000 using RMR2 and then just leave your memory map as it is? You'd probably need a bootstrap bit of code to set up the memory map but you can have all of that in one lower ROM, copy it into RAM where the screen will eventually end up and execute it from there so you don't page yourself out 

Cwiiis

Quote from: andycadley on 13:00, 21 March 23Why not just map the lower ROM to #8000 using RMR2 and then just leave your memory map as it is? You'd probably need a bootstrap bit of code to set up the memory map but you can have all of that in one lower ROM, copy it into RAM where the screen will eventually end up and execute it from there so you don't page yourself out
Ah, so I didn't realise you could map the lower rom to 8000... That would work nicely! I could move my variables to somewhere after C000 in that case too, that would be fine - I wasn't aware that mapping lower rom to 8000 was an option. I've not really messed with mapping at all, can you map other banks to 8000 or just lower rom?

I suppose my ideal would be bank 0 on the cart is the bootstrap, my code basically as it is is in some other bank that I map to 8000, my variables live in C000 somewhere (or whatever space I have left in 4000) and I can map my data in and out at C000 as necessary.

I don't really have any experience with bank switching, is there a good primer on this? I can see about switching the high banks on the Plus here:  Programming:Cartridges - CPCWiki and there's some general documentation here:  Gate Array - CPCWiki but I don't immediately see anything about this RMR2 you mention...

andycadley

RMR2 is a Plus only thing, so you need the Arnold V documentation to find it: https://www.cpcwiki.eu/index.php/Arnold_V_Specs_Revised

Essentially any of the first 8 cart banks can be set as the Lower ROM and can be paged to appear at either #0000, #4000 or #8000.

The only issue is I don't think you can map the ROM elsewhere and have the ASIC visible which might be a problem. If everything fits in RAM though, you could just copy the contents of the ROM into RAM at startup and then leave all the ROMs disabled.

Cwiiis

Quote from: andycadley on 13:29, 21 March 23RMR2 is a Plus only thing, so you need the Arnold V documentation to find it: https://www.cpcwiki.eu/index.php/Arnold_V_Specs_Revised

Essentially any of the first 8 cart banks can be set as the Lower ROM and can be paged to appear at either #0000, #4000 or #8000.

The only issue is I don't think you can map the ROM elsewhere and have the ASIC visible which might be a problem. If everything fits in RAM though, you could just copy the contents of the ROM into RAM at startup and then leave all the ROMs disabled.
I think this is going to be the way, with banking done at C000. So I guess my bootstrap in bank 0 will map bank 1 to 8000 and just copy 8000-end to itself - presumably, reads will happen from rom but writes will happen to ram - then I should just be able to jump to it and business as usual, with loading from disk replaced with temporarily mapping banks at C000 and decompressing to ram. Thanks for the pointers!

Cwiiis

I guess a concern doing this is that you can't do this running code from the mapped low rom as it'll disappear once you remap it... So the bootstrap sequence will be

- Enable Plus features
- Copy bootstrap to ram (at &4000 seems convenient)
- Jump to bootstrap

Bootstrap:
- Map X low-rom bank at &8000
- Copy &8000 to &8000
- Unmap bank
- Jump to &8000

andycadley

I'd just do:

LD HL,0
LD DE,0
LD BC,#4000
LDIR

At the start of the startup ROM. At which point it won't matter if it gets paged out, since ROM and RAM from #0000 to #3fff will contain the same data anyway.



Cwiiis

So I have my game working nicely from a cpr file I've generated... But only in WinAPE. Booting it on a real machine results in a black screen...

My boot process is as described above, essentially copying my main program rom to &8000 and jumping to it - disk loading is replaced by mapping in an upper rom and copying it to the relevant place in ram, otherwise both upper and lower roms stay unmapped.

I guess I'll try debugging this in a more accurate emulator, but if anyone has any suggestions for common things it might be - My best guess is my initialisation code isn't quite correct and just happens to work ok on WinApe for whatever reason...

dragon

winape don`t emulated well the plus range, for example the asic ppi keyboard bug is ignored, and then later it not work in gx4000/plus range,at also ignored some estructure of the cpr,and some initialization of the roms, so maybe do you think is jump to one page and the gx4000 is mounting another page... 


Try it with arnoldemu it mucho closer to the real hardware probably you take black screen, at least to boot it.

arnold wip - Page 7 (cpcwiki.eu)


andycadley

WinAPE also has a nasty habit of assuming all the registers are set to 0 at power on which isn't necessarily true. So it's worth setting sensible defaults for all the ASIC registers, making sure you have the correct IM enabled, setting all the CRTC registers, clearing any RAM that you're assuming starts at zero etc.

Cwiiis

Quote from: andycadley on 18:35, 22 March 23WinAPE also has a nasty habit of assuming all the registers are set to 0 at power on which isn't necessarily true. So it's worth setting sensible defaults for all the ASIC registers, making sure you have the correct IM enabled, setting all the CRTC registers, clearing any RAM that you're assuming starts at zero etc.
mm, I think it's going to be something along these lines, though tracking down where I'm making a bad assumption is going to be a pain :) Seems CPC-EC also doesn't work with the cpr though, so I can at least fix it in that and hopefully that'll fix it on hardware too...

dthrone

Check your RIFF headers are correct because Winape does not care whilst the real machine and Arnold emulator does.

Cwiiis

I've been stepping through this in CPC-EC at least, as that crashes with the cart. It gets through almost an entire game loop, but at some point, it pushes a register and an entirely different value gets put on the stack. Stack corruption inevitably leads to it falling over almost immediately... But at this point, interrupts are disabled, so... What can cause that?

For what it's worth, I put the stack at &7FFD and it's only 3 words deep at the point it becomes corrupted. I'm pretty sure before that point it goes deeper and is fine. There isn't any other code running at this point, it's literally - bc = &030A, de = &6402; push bc, &9BBA gets put on the stack; push de, &0000 gets put on the stack. Surely pushing registers onto the stack is something incorruptible? I've attached a screenshot of the state.

Cwiiis

Oh no, my stack overlaps with ASIC registers, doesn't it?  :picard: I'll put it somewhere else...

dragon


andycadley

Yeah, the minute I read the description I thought "bet you're pushing to the ASIC" - we've all done it.  :laugh:

dragon

So winape ignored that asic is enabled when write to ram, to the list.

Cwiiis

Quote from: Cwiiis on 13:24, 23 March 23Oh no, my stack overlaps with ASIC registers, doesn't it?  :picard: I'll put it somewhere else...
All good now :) My bootstrap code is simpler having followed Andy's suggestion too. Next project I'll take into account working off a cart from the beginning though, it's lucky I don't need much memory for this!

andycadley

Quote from: dragon on 13:41, 23 March 23So winape ignored that asic is enabled when write to ram, to the list.
I have a feeling WinAPE treats the ASIC like RAM, so if you write a byte to it and then read it back you get the correct value. So as long as you POP while the ASIC is still enabled it will seem correct.

On real hardware it doesn't work like that and writes/reads from unmapped ASIC registers don't give the results you'd expect.

Powered by SMFPacks Menu Editor Mod