News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu

Nicky Boom for CPC ?

Started by LTronic, 12:54, 04 August 19

Previous topic - Next topic

0 Members and 2 Guests are viewing this topic.

Should I port it to ?

CPC 6128 "Old" (not sure I can fit it)
12 (31.6%)
Plus machines on a 512K cart (it will fit)
26 (68.4%)
I don't care about this game
0 (0%)

Total Members Voted: 38

LTronic

Hello,

I got my hands over a CPC 6128 Plus I have restored succesfully + ordered a Gotek drive.
As a programmer myself I am trying to put together something on it :)


I hacked a bit around this weekend with SDCC, ConvImgCPC and Arkos Tracker 2 and tried to see what could Nicky Boom look on CPC.

Right now nothing fancy, just some packed still images and intro song I have converted using AT2 (C + assembly) but I am thinking of switching to CPCTelera (would need to integrate Arkos Tracker 2 support into it).


The music is not yet played under interrupts sadly.

Do you think it's possible to achieve a port of Nicky Boom on CPC (using CPCTelera or another framework or raw C/ASM) or not ?
Would some people be interested in this project ?

Don't judge too hard, this is my first try on CPC programming and I'm neither a graphist nor musician.

Youtube link :
https://www.youtube.com/watch?v=LmyWfy3PQiE



Skunkfish

I'd not heard of this game before but after watching a couple a couple of Youtube vids it looks a little terrifying (the main character sprite at least).
The main issue that you will have in porting this to the CPC is replicating the smooth scrolling of the original - targeting the Plus machines would help to resolve that issue.
P.S. Your test looks good, are you using the Plus palette or regular CPC?
An expanding array of hardware available at www.cpcstore.co.uk (and issue 4 of CPC Fanzine!)

LTronic

Quote from: Skunkfish on 13:21, 04 August 19
I'd not heard of this game before but after watching a couple a couple of Youtube vids it looks a little terrifying (the main character sprite at least).
The main issue that you will have in porting this to the CPC is replicating the smooth scrolling of the original - targeting the Plus machines would help to resolve that issue.
P.S. Your test looks good, are you using the Plus palette or regular CPC?


Hello,


I am targeting regular CPC so far. I did some tests on the tilemap meanwhile and yes targeting CPC+ would offer a huge improvement regarding color fidelity.
The Atari ST version does not provide smooth scrolling and is still very playable, so this might be a solution to target "old" CPC.
Also I am not sure CPCTelera framework makes use of Plus features...




teopl

Quote from: LTronic on 12:54, 04 August 19
I hacked a bit around this weekend with SDCC, ConvImgCPC and Arkos Tracker 2 and tried to see what could Nicky Boom look on CPC.
Right now nothing fancy, just some packed still images and intro song I have converted using AT2 (C + assembly) but I am thinking of switching to CPCTelera (would need to integrate Arkos Tracker 2 support into it)

There is example cpctelera project which uses Arkos2 player here http://www.cpcwiki.eu/forum/programming/cpctelera-arkos-2-conversion-problem/

Search for "arkos2_forum_akg.zip" attachment. I tested it with latest Arkos2 version (at the time) and works great!

LTronic

Quote from: teopl on 15:54, 04 August 19
There is example cpctelera project which uses Arkos2 player here http://www.cpcwiki.eu/forum/programming/cpctelera-arkos-2-conversion-problem/

Search for "arkos2_forum_akg.zip" attachment. I tested it with latest Arkos2 version (at the time) and works great!


Amazing, thanks :)
Do you plan to make pull request it to integrate AT2 support to CPCTelera project ?

teopl

Well basically no :) I mean I just made automatic what was already in the Arkos2 tutorial so I guess this will be an easy job for the cpctelera maintainers if they want to do the same.

Also, you can try latest Arkos2 players since new release was in July. (I should probably update myself also)

And you can check - LW (light weight) player instead of AKG which is in the example project.

LTronic

Hello,


I made progress, I have the whole intro working fine with music played under interrupts with CpcTelera + AT2 thanks to your help :)


I need a bit of advice from the gurus, I am working from the game engine rewritten in C by Gregory Montoir a decage ago.
I managed to get everything compiling fine with SDCC but sadly it's way too big, around 50 KB of code.


I'm sure there is room for improvement, the generated Z80 assembly looks really fat especially due to pointer arithmetic and 16-bit calculations.

I already use --opt-code-size and played around with --max-alloc-per-node SDCC options.



Any tip ?
Should I rewrite routine per routine in Z80 assembly ? If so, do you think it will bring enough improvement to make it "loadable" ?


Thanks a lot :)



Sykobee (Briggsy)

Certainly you will want to optimise the inner loops, if only to reduce the size of the code and unnecessary use of 16-bit values, and so on.


Also, does that code have per-level code, or is it one big engine? There's always multi-load if there is a lot of per-level code.

LTronic

Quote from: Sykobee (Briggsy) on 17:52, 08 August 19
Certainly you will want to optimise the inner loops, if only to reduce the size of the code and unnecessary use of 16-bit values, and so on.


Also, does that code have per-level code, or is it one big engine? There's always multi-load if there is a lot of per-level code.


Okay thanks a lot.
Sadly it's a one-engine for everything. I removed "Nicky Boom 2" specific code which enables to hardcode a simplify a bunch of things but it still feels a loooot of code.
My idea was to split the "intro" (logos + main screen + intro) into one binary and the core game in another one, loaded when you start the game.
The intro binary is already OK and functional, but I need to remove the bloat from the core game binary one.


Sykobee (Briggsy)

Well, you could do it as a plus cart - there's 512KB there between code and assets :)


You might need to see what code paths are 'hot' - i.e., ran every game loop, and which ones are run outside of the game loop, which can maybe be treated differently.


You might need to cut down features, e.g., if each enemy has its own code path for its "AI" then maybe you'll have to drop some of the variants.




LTronic

Quote from: Sykobee (Briggsy) on 12:26, 09 August 19
Well, you could do it as a plus cart - there's 512KB there between code and assets :)


You might need to see what code paths are 'hot' - i.e., ran every game loop, and which ones are run outside of the game loop, which can maybe be treated differently.


You might need to cut down features, e.g., if each enemy has its own code path for its "AI" then maybe you'll have to drop some of the variants.


Targeting a cartridge is a really good idea. I was already hesitating to target CPC plus or regular CPC.
I will continue "regular" optimization and 8-bit specific tweaks to make sure I start on a sane basis then might move to CPC+ / .CPR target.


Will keep reporting progress on this thread for sure and thanks for the wise advices ;)

Arnaud

Hello,
first of all, using existing game C code from another platform to convert to CPC is really an interesting project.

Quote from: LTronic on 17:48, 08 August 19
I managed to get everything compiling fine with SDCC but sadly it's way too big, around 50 KB of code.
I already use --opt-code-size and played around with --max-alloc-per-node SDCC options.

About compiler options you can use peepholes optimizations (gain about 400kb) :
Z80CCFLAGS    :=  --peep-file ${CPCT_PATH}/tools/sdcc-3.6.8-r9946/peep/z88dk_speculative_peepholes.def --max-allocs-per-node 8000 --opt-code-size

About manual code optimization, try to use u8 variables if possible, and when using pointer member or global variable several time in function, copy it to local variable :

u8 counter = (u8)object->_counter;

if (timer != counter)
object->_spriteInfo._sprite = spritesState[index + (counter < timer ? 0 : 1)];


And of course you can compress data with zx7



LTronic

#12
Quote from: Arnaud on 18:07, 10 August 19
Hello,
first of all, using existing game C code from another platform to convert to CPC is really an interesting project.

About compiler options you can use peepholes optimizations (gain about 400kb) :
Z80CCFLAGS    :=  --peep-file ${CPCT_PATH}/tools/sdcc-3.6.8-r9946/peep/z88dk_speculative_peepholes.def --max-allocs-per-node 8000 --opt-code-size

About manual code optimization, try to use u8 variables if possible, and when using pointer member or global variable several time in function, copy it to local variable :

u8 counter = (u8)object->_counter;

if (timer != counter)
   object->_spriteInfo._sprite = spritesState[index + (counter < timer ? 0 : 1)];


And of course you can compress data with zx7


Thanks a lot !
Yes indeed, I think it is an interesting challenge.
Using --opt-code-size --max-allocs-per-node 200000 it does generate a 42 KB binary file fitting into memory space, but the code needs then to load a lot of data.
I tried the peephole optimizations, sadly it did not work, I add to disable one of them (was causing addressing error on "ld l, 0" mnemonic).
Rebuild and now the linker fails with a lot of
?ASlink-Warning-Undefined Global popping on what looks like every exported symbol (C functions not being static..).


Any idea ? I looked at the peephole file and it looks very interesting indeed...
Meanwhile I will try your manual optimizations tricks. I am used to the C compiler doing this kind of optimization for me  ;D

Arnaud

#13
Quote from: LTronic on 18:51, 10 August 19
I tried the peephole optimizations, sadly it did not work, I add to disable one of them (was causing addressing error on "ld l, 0" mnemonic).
Well, no ready-made solution.
Rarely peephole generate error, in this case i try to modify my C code until it works (sorry no example in mind to help you).
If peep-hole optimization doesn't work, try manual optimization first.

And i forgot, ternary operator can also do good optimizations

Quote from: LTronic on 18:51, 10 August 19
Rebuild and now the linker fails with a lot of
?ASlink-Warning-Undefined Global popping on what looks like every exported symbol (C functions not being static..).

try :
make cleanall all


Quote from: LTronic on 18:51, 10 August 19
Meanwhile I will try your manual optimizations tricks. I am used to the C compiler doing this kind of optimization for me  ;D

You could have important gain, but you have to modify code, compile, compare with previous compilation, and so on.


LTronic

#14
I am refactoring game into 16KB modules in order to accomodate the memory limits and layout.
Here is the current state of my thoughts, let me know if I am on the right track (or not).
Any idea is welcome.


Though stuff :
- Tilemap is currently 20000 bytes per level, I will need to get it down to 16384 bytes. I am in touch with a guy who wrote a level editor so it might ease the pain.
- Can I use all the RAM in BANK 2 ? I know there is firmware / AMSDOS stuff in.... Once I am into a level it's fine, but I will need to load next level and resort to firmware for disc loading, right ? Any way to restore it ?
- TILES already fit in 16 KB. GAMEOPS too. KERNEL should be okay. Not SPRITES, neither GLOBALS, neither GAME, but I should be able to do it by simplifying the game code a bit.




-----------
- Objects -
-----------
KERNEL : system code (sound, render, syscalls, bankswitching)
GAME : game "main" code
GAMEOPS : game "logic" code
GLOBALS : global variables
TILES : tile bitmap data
TILEMAP : tilemap data
SPRITES : sprite bitmap data


---------------
- Bank Layout -
---------------
BANK0 : KERNEL
BANK1 : USER
BANK2 : DATA
BANK3 : VRAM


-------------
- RAM banks -
-------------
RAM0 : KERNEL (CODE)
RAM1 : GAME (CODE)
RAM2 : GLOBALS (DATA)
RAM3 : VRAM
RAM4 : GAMEOPS (CODE)
RAM5 : TILES (BITMAP)
RAM6 : SPRITES (BITMAP)
RAM7 : TILEMAP (DATA)


---------------------------------------------------------------------
- Code States                                                       -
---------------------------------------------------------------------
-                           BANK 0  BANK 1      BANK 2      BANK 3  -
---------------------------------------------------------------------
BOOT            (0 1 2 3) : KERNEL ?    ?     VRAM
GAME            (0 1 2 3) : KERNEL  GAME        GLOBALS     VRAM
GAMEOPS         (0 4 2 3) : KERNEL  GAMEOPS     GLOBALS     VRAM
COPY_TILEMAP    (0 7 2 3) : KERNEL  TILEMAP     GLOBALS     VRAM
RENDER_TILEMAP  (0 5 2 3) : KERNEL  TILES       GLOBALS     VRAM
RENDER_SPRITES  (0 6 2 3) : KERNEL  SPRITES     GLOBALS     VRAM


-------------------
- State switching -
-------------------
BOOT           -> GAME
GAME           -> GAMEOPS
GAMEOPS        -> GAME
GAME           -> COPY_TILEMAP
COPY_TILEMAP   -> GAME
GAME           -> RENDER_TILEMAP
RENDER_TILEMAP -> GAME
GAME           -> RENDER_SPRITES
RENDER_SPRITES -> GAME



LTronic


Arnaud

Hi,
are your tilemaps and graphics already compressed ?

You can disable firmware and reenable it just for loading level,
but you have to keep this area [0xA67C .. 0xAC00] (AMSDOS reserved area) free.

u16 fwRomPtr= cpct_disableFirmware();
cpct_setInterruptHandler(myInterruptFunction);

// Read Level on disc
cpct_reenableFirmware(fwRomPtr);

ReadLevelOnDiscFunction();

fwRomPtr = cpct_disableFirmware();


LTronic

Quote from: Arnaud on 08:38, 12 August 19
Hi,
are your tilemaps and graphics already compressed ?

You can disable firmware and reenable it just for loading level,
but you have to keep this area [0xA67C .. 0xAC00] (AMSDOS reserved area) free.

u16 fwRomPtr= cpct_disableFirmware();
cpct_setInterruptHandler(myInterruptFunction);

// Read Level on disc
cpct_reenableFirmware(fwRomPtr);

ReadLevelOnDiscFunction();

fwRomPtr = cpct_disableFirmware();



Thanks a lot for the clarification on the AMSDOS area. Makes a lot of sense.


The tilemap is not compressed, I started yesterday writing a tool to reprocess most of the game data to squeeze out the most possible of the limited RAM.
I splitted each 400x50 tilemap level data into 40x50 tilemaps compressed with ZX7B which is large enough since one screen will be a sub-portion of 20x10. The problem so far is that game does modify tilemaps at runtime (you can "create" bridge using wood bonus to progress, destroy platforms and so on...). I have no solution so far except of course recrunching the modified tilemap at runtime, and add some padding between sub-sections of tilemaps since I do not how know 100% for sure how much bytes the crunching process will end up.


Another approach would be to have kind of "delta table" to apply to original tilemap but it could quickly become complicated and costy in terms of CPU usage when rendering tilemaps to check this delta table.


Do you think it is "viable" to crunch tiles data and sprites data (bitmap) and uncrunch at runtime or is it too much ?

LTronic

#18
double post again...


Arnaud

Is the game not linear ?
Can we go back to previous levels or just to special places like store or bonus stage ?

LTronic

You can't go back to a previous level but the tilemap is 400x50 tilemap per level, coded on 8 bits.
I ran some heuristics and while each tiles bitmap is made of 256 tiles, between 177 and 233 tiles are used, so no way to reduce the 8-bit coding to 7-bit of something else.

Arnaud

Well,
i think the easier solution in first time is to make a version for 128k.
You won't need firmware anymore and you'll have 64k more to store all levels.

And once it works, you can try to load levels from disk.


Sykobee (Briggsy)

#22
If 233 tiles are used, then maybe the spare tiles can be used for the mutable tiles, depending on the number of mutable blocks.


A bridge, for example, could be allocated tile 255. There can also be a flag set or unset, depending on bridge visibility. When rendering, check the flag. If unset, render 'air', if set, render 'bridge'. Sure, this might only mean you can have 20-70 bridges, destructable blocks, etc, in a level, and I don't know if that's enough.


Also whilst you might want *all* the graphics, it may be prudent to selectively eradicate some that don't add value. But this is probably not going to gain much except reduce the tileset size (not a massive worry on a cart or multiload 128KB), and allow more mutable blocks. And possibly aid compressibility.


Another option is to analyse the level layout, and see if there are any 'bottlenecks' (single route between map areas) in the layout, where you can split a level into two or more levels. If the map is very open (like Turrican) this won't be an option.


And finally, if you are happy to do some map editing, instead of having 1 byte = 1 tile, consider 1 byte = 2x2 tiles (5KB level, not 20KB), and have a lookup table of those tiles. A common way to save map data on 8-bits. Also on a cart/multi-load 128KB the large level size isn't an issue, although nice to fit it into 16KB.

LTronic

Quote from: Sykobee (Briggsy) on 09:43, 13 August 19
If 233 tiles are used, then maybe the spare tiles can be used for the mutable tiles, depending on the number of mutable blocks.


A bridge, for example, could be allocated tile 255. There can also be a flag set or unset, depending on bridge visibility. When rendering, check the flag. If unset, render 'air', if set, render 'bridge'. Sure, this might only mean you can have 20-70 bridges, destructable blocks, etc, in a level, and I don't know if that's enough.


Also whilst you might want *all* the graphics, it may be prudent to selectively eradicate some that don't add value. But this is probably not going to gain much except reduce the tileset size (not a massive worry on a cart or multiload 128KB), and allow more mutable blocks. And possibly aid compressibility.


Another option is to analyse the level layout, and see if there are any 'bottlenecks' (single route between map areas) in the layout, where you can split a level into two or more levels. If the map is very open (like Turrican) this won't be an option.


And finally, if you are happy to do some map editing, instead of having 1 byte = 1 tile, consider 1 byte = 2x2 tiles (5KB level, not 20KB), and have a lookup table of those tiles. A common way to save map data on 8-bits. Also on a cart/multi-load 128KB the large level size isn't an issue, although nice to fit it into 16KB.


Thanks a lot !


I am into the process of making some tools to optimize / edit the game data, saving every possible byte.


I am more and more considering targeting a cart... Is there any tutorial / instructions regarding building a cart (.CPR) using SDCC toolchain ?

NiNxPe

#24
Level 1 Old :


Level 1 Plus :

Powered by SMFPacks Menu Editor Mod