Can you guess what's in the extra 64K RAM ?
Easter is coming....
Bunnies? 😁
Start menu music from Atari ST ?
Digital speech intro from C64 ?
The both would be perfect but whatever you do would be great. ;)
Quote from: flolore on 05:56, 13 April 25Start menu music from Atari ST ?
Digital speech intro from C64 ?
The both would be perfect but whatever you do would be great. ;)
Oh you had to go and bring back memories...
https://youtu.be/8_Y_5gnY68A
Since the sound chip for the atari.st and cpc are quite similar, why not? I'm thinking of the intro to Crazy Cars 2, for example.
Else, there are this music used on a 128k edition for ZX Spectrum, from atari.st, i don't know this one...
https://www.youtube.com/watch?v=REa6UJ0Y1so&t=243s to 3:00
Finally there are many possibilties ;)
I've used the Atari ST musics indeed. They can be found on N.Guillaumin's website :
turrican - [n/a] (https://nguillaumin.github.io/ym-jukebox/)
I've converted the YM files to AYC format using the good old YMCruncher from F-Key, and they're played using the good old AYC Player from Overlanders.
The music for the title is not the sampled one. I'm not sure samples can be converted to YM format.
Oooh goody!
Great Job Jean-Marie, thank you so must for this 128k version, and for secret agent, ninja turtles 2, & more than you did in the past ! :)
All tunes were composed by Jochen Hippel, after you know who. Except title music whose composer is unknown.
This is a PARADOS disc (80 tracks), so you'll need it installed in ROM 7. PARADOS can be downloaded from here (https://www.cpcwiki.eu/index.php/ParaDOS).
Alternatively, I guess you can just copy/paste the whole files on your U2/M4/Gotek or whatnot.
RUN"TURRICAN" to start. RUN"CHEAT" for invincibility.
Controls:
• H to pause the game, followed by S to switch between Music and SFX.
• CTRL+ESC to abort current game.
• Hold Fire button to activate the Lightning ray.
• ENTER to launch an energy line.
• SHIFT to launch a grenade
• Joystick Down+RETURN to turn into a gyroscope.
• Hold Fire button+Joystick Down to activate a mine.
Alternate download link : Turrican (128k) PARADOS B6E1C04A.dsk (https://1drv.ms/u/c/df7f34b0a9412a65/ETSECDW1sHhMn6eqvb6_VLcBYE5FUSBRg43x5LNcNLxpnQ?e=2vq4Rf)
Happy Easter!✝️
It's masterful ;) with the music in play, it's a whole different game but even better!!
Thank you so much JMB ;)
Great work! Apparently playing the music doesn't affect the performance, or not too much.
Thanks a lot JMB !
Let's see what we can do about gfx ... ;)
Downloaded and played it yesterday.
Turrican was always one of my favourite games.
And now it reaches a higher level in the gameplay, much more arcade like.
It's a fantastic addition. :)
Thanks for your great work Jean-Marie
Cracking job! I've not had a chance to play through it on hardware yet, but i'm looking forward to it when i'm near my CPC again.
How do we run this in retro vitrual machine? In rom manager I dont see rom 7 slot. From 6 goes to 8. Which version of parados is needed?
From the console, you must type :
load "Parados 1-2.ROM" rom7:#C000
You'll have to navigate to the correct directory beforehand.
The thing is: this is a 3"1/2 disc, and the drive B in RVM is a second 3". I'm not sure we can change that.
I'd recommend using Amspirit (https://amspirit.fr/amspirit-v1-0-rc1/) instead, the sound rendering is really top-notch.
Or use the CPC Plus and the Parados CPR.
Quote from: Jean-Marie on 23:21, 20 April 25From the console, you must type :
load "Parados 1-2.ROM" rom7:#C000
You'll have to navigate to the correct directory beforehand.
The thing is: this is a 3"1/2 disc, and the drive B in RVM is a second 3". I'm not sure we can change that.
I'd recommend using Amspirit (https://amspirit.fr/amspirit-v1-0-rc1/) instead, the sound rendering is really top-notch.
I cant seem to find the "hammer" icon in RVM. I remember it in old versions. I tried Amspirit, and it crashes when i insert the parados rom. Am I doing something wrong?
cpc+ workaround worked.
Yeah, only version 2.0 has the "developer tools", but you can still download it from their website. They removed it on later versions. But even if you manage to install PARADOS, the B drive seems reluctant to read the DSK, as it is not a 3"1/2 drive.
With AMSPIRIT, edit the file AmspiriT_Config.txt and change the content with the following :
VERSION=v1.01_RC_x64;
LANGUE=1;
MODELE=2;
PAYS=0;
ACTIVE_DRIVE=1;
EXTENSION_MEMOIRE=5;
VIDEO_TYPE=1;
CRTC_TYPE=0;
CRTC_VERSION=0;
MARQUE=7;
MONITEUR=0;
FILTRE_VIDEO=1;
AFFICHAGE_VIDEO=0;
SORTIE_SON=1;
VOL_SOUND=10;
MELANGE_VOIES=0;
ETAT_IMPRIMANTE=1;
MAPPING_JOYSTICK=0;
MAPPING_CLAVIER=1;
OPEN_CONSOLE=0;
AUTOLOAD FLOPPY=1;
AUTOLOAD_TAPE=1;
PROTECTED_TAPE=1;
BACKUP_TAPE=1;
PROTECTED_FLOPPY_A=0;
BACKUP_FLOPPY_A=0;
FRQ_CPU=1799.992065;
FRQ_SOUND=62500;
ID_EMULATOR=0;
PROTECTED_DISK=0;
SCREEN_WIDTH=767;
SCREEN_HEIGHT=540;
OFFSET_X=172;
OFFSET_Y=55;
ZOOM_VIDEO=100;
ROM_SYSTEM=6128\OS_6128[ENG].rom;
ROM_BASIC=6128\BASIC_6128[ENG].rom;
ROM_AMSDOS=AMSDOS.rom;
ROM_EXTERNE_0=;
ROM_EXTERNE_1=;
ROM_EXTERNE_2=;
ROM_EXTERNE_3=;
ROM_EXTERNE_4=;
ROM_EXTERNE_5=;
ROM_EXTERNE_6=;
ROM_EXTERNE_7=ParaDOS 1-2.ROM;
ROM_EXTERNE_8=;
ROM_EXTERNE_9=;
ROM_EXTERNE_10=;
ROM_EXTERNE_11=;
ROM_EXTERNE_12=;
ROM_EXTERNE_13=;
ROM_EXTERNE_14=;
ROM_EXTERNE_15=;
FICHIER_FLOPPY_A=;
FICHIER_FLOPPY_B=;
FICHIER_CASSETTE=;
Don't forget to copy the Parados file in the ROM folder.
The config panel should look like enclosed screenshot.
The sad news is that I discovered a small bug with Amspirit this night : the SHIFT key, which is used to launch a grenade, seems to be ineffective. It might be considered as a "dead key" by the emulator, and doesn't produce a scan code...
Quote from: Jean-Marie on 11:28, 21 April 25The sad news is that I discovered a small bug with Amspirit this night : the SHIFT key, which is used to launch a grenade, seems to be ineffective. It might be considered as a "dead key" by the emulator, and doesn't produce a scan code...
Shift key works. You just have to click the Keyobard button to be CPC and not PC/CPC
Your approach worked (and no extra ram needed. I see you have 1024 in the picture). So the parados rom can only be inserted by the config file not in the settings menu? Seems like a bug to me :D
No, you can also press the button with the 3 dots right to the ROM number, and select the file of your choice.
Quote from: vasilisk on 13:12, 21 April 25Shift key works. You just have to click the Keyobard button to be CPC and not PC/CPC
Ah! you taught me something :laugh: Thanks.
Quote from: Jean-Marie on 13:25, 21 April 25No, you can also press the button with the 3 dots right to the ROM number, and select the file of your choice.
Strange. Because last night, as you see i posted earlier, whenever I tried that, the emulator crashed. Today, it loads the rom without problem.
If the emulator crashed, it is a bug indeed. It would be interesting to reproduce it, so we can report it to the author.
Quote from: Jean-Marie on 13:51, 21 April 25If the emulator crashed, it is a bug indeed. It would be interesting to reproduce it, so we can report it to the author.
Ok. I found out why it crashed. I was trying to load the rom which was not in the ROM folder. Apparently if the rom file is not in the ROM folder the emulator crashes. I am not sure if it is a bug or the author wanted the roms to be placed ONLY in the rom folder.
Ok, I've managed to reproduce it. It's not a crash stricto sensu, more like a scary message of error before terminating the program. No need to bother him, I guess.
I get no message. It crashes immediately. Also I see you are trying to load parados from inside the ROM folder. Copy the ParaDOS 1-2.ROM outside the ROM folder and try to load it from the settings menu.
I deleted the file from the folder, and it showed me this message telling me in french that PARADOS is missing from the ROM folder.
I surmise that you don't see the message because the author has not yet translated it in English.
It is possible that all error messages only show up on French versions of Windows.
Eh, I'm getting good! About time, I'm 53, lol.
Work in progress ...
https://i.postimg.cc/43HhPcTr/image006.png
https://i.postimg.cc/qRf657Jg/image007.png
(https://i.postimg.cc/cKZH4rCm/image006.png) (https://postimg.cc/cKZH4rCm)
(https://i.postimg.cc/DWgzWnbK/image007.png) (https://postimg.cc/DWgzWnbK)
Very bad ass. I like it.
Work in Progress
(https://i.postimg.cc/pXnqcXKt/run4-Export.gif)
(https://i.postimg.cc/d3b4Bz1F/image.png)
(https://i.postimg.cc/7Y7kk4Sg/image2.png)
Quote from: OneVision on 09:00, 28 April 25Work in Progress
(https://i.postimg.cc/pXnqcXKt/run4-Export.gif)
(https://i.postimg.cc/d3b4Bz1F/image.png)
(https://i.postimg.cc/7Y7kk4Sg/image2.png)
Very nice, it would be awesome to see the running animation side by side with the original for comparison.
Ola !
(https://i.postimg.cc/kGYv3GyF/run5-Export.gif)
Quote from: OneVision on 11:09, 28 April 25Ola !
(https://i.postimg.cc/kGYv3GyF/run5-Export.gif)
Wow dude! amazing improvement!
That is really quite the improvement for this game!
Do you think there could be the possibility to map the special attack (return key) to the second button of a joystick/joypad?
That would further improve the experience when playing with a joystick.
Thanks!
Yes, I can do that for version 2. In the meantime, if you can poke your memory, try :
POKE &DE5D,9
POKE &DE5E,&20
Quote from: jackic on 19:11, 29 April 25Do you think there could be the possibility to map the special attack (return key) to the second button of a joystick/joypad?
That'd be amazing..
Thanks, there are so many games that would benefit of this... Turrican 1 and 2, Dragon Spirit, the list would be really long.
I'm rather a member of the team : Button 1 fire, Button 2 jump.
And by the way, how would the game be playable on the GX with only 2 buttons ? I guess it's not, and only playable on a PLUS.
Quote from: Jean-Marie on 19:52, 29 April 25Yes, I can do that for version 2. In the meantime, if you can poke your memory, try :
POKE &DE5D,9
POKE &DE5E,&20
Thanks, just in case this can be of help to someone, you can achieve this on original hardware extracting "turrican.scr" and changing the offset 12A0 from "02 04" to "09 20", I did this by basically by brute forcing, couldn't have done it without the pokes.
Quote from: OneVision on 10:24, 01 May 25I'm rather a member of the team : Button 1 fire, Button 2 jump.
And by the way, how would the game be playable on the GX with only 2 buttons ? I guess it's not, and only playable on a PLUS.
Well, you can use both buttons for attack and "special attack" and jump pushing up or, alternatively, map button 2 for jumps and the up direction for "special attack". but then you will not be able to do the spin attack, achieved by crouching+"special attack".
Yeah Turrican is very special with lots of extra weapons and therefore many keys/buttons to activate.
Anyway, having to jump by pressing UP is not ideal and is the reason why so many arcade conversions or gameplays were somewhat "flawed" back in the days of 8-16 bits europeans computers.
I remember being able to play Turrican on my A500 with a Megadrive pad (3 buttons then) but still having to press up to jump was very awkward.
Doesn't work for me. 6128 with ULifAC and |PARA activated, I see file contents, do the RUN"TURRICAN" but I get a black screen :-[
You don't even see the loading screen?
Can someone do a video? I'd love to see this as life getting in the way currently I don't have a chance yet to try it out.
Quote from: Jean-Marie on 15:18, 02 May 25You don't even see the loading screen?
No, as soon as I type RUN"TURRICAN", I get a black screen. Not sure if anyone else has tried it in ULifAC.
edit:Works with Cheats, get the main screen, get the music, when it is about to load, again black screen
Quote from: nikos_a on 11:55, 04 May 25edit:Works with Cheats, get the main screen, get the music, when it is about to load, again black screen
Interesting. If it works when you RUN"CHEAT.BAS", it means there is a problem during ROM 7 initialization, as the BASIC loader bypasses the ROM init.
Also, ROM 7 is initialized once again when a Level file needs to be loaded.
Can you run the file TEST.BIN on the enclosed DSK, and tell us what are the values you're seeing?
It should display the first usable byte of memory, the last usable byte, and the current drive.
&BCCE KL INIT BACK
Action: Finds and initialises a specific background ROM
Entry: C contains the ROM select address of the ROM, DE holds
the address of the first usable byte of memorv, HL
holds the address of the last usable byte of memory
Exit: DE holds the address of the new first usaUe byte of
memory, HL holds the address of the new last usable
byte. AF and B are corrupt, and all other registers
are preserved
Notes: The ROM select address must be in the range of 0 to 15
(or 1 to 7 for the 464) although address 7 is tor the
AMSDOS/CPM ROM if present. The ROM's initialisation
routine is then called and some memory may be reserved
for the ROM by adjusting the values of DE and HL before
returning control to KL INlT BACK
Sorry for the late answer but here it is
I can confirm the same scenario as nikos.
Could you type the following commands at start up?
PRINT HEX$(PEEK(&BE7E))
PRINT HEX$(PEEK(&BE7D))
It displays the address where the current drive unit is stored in memory (normally A700h).
Here
I see nothing unusual so far. Let's try something else : enclosed is the "original" version I have worked on, a cracked Parados disc by CBS. Are you able to play the game with it? Or does it hang when the first level is loaded?
Same thing. Only now, the menu screen is kind of distorted. Although this version is without the music i think
Okay, I've had an idea. I've put the files on a classic 178K AMSDOS Dsk.
Obviously, I could not store all the files, but you can still play the 5 first levels.
Can you turn off PARADOS and run the disc please? This way, we'll know if the problem stems from PARADOS!
Quote from: Jean-Marie on 03:34, 12 May 25Okay, I've had an idea. I've put the files on a classic 178K AMSDOS Dsk.
Obviously, I could not store all the files, but you can still play the 5 first levels.
Can you turn off PARADOS and run the disc please? This way, we'll know if the problem stems from PARADOS!
Ok. I will try it when I return. This is the no music version ori the music one?
It's the latest version with music and other improvements. The left/right scrolling is triggered sooner, so you'll see incoming enemies more clearly (following a suggestion of Onevision).
RUN"FIRE2" at startup to use a Joystick with 2 buttons. Pressing both buttons launch a grenade.
RUN"FIRE3" to use a 3 buttons joystick !
More speed optimizations in the code.
Thanks! You are making a good game into an amazing one! Almost better than the sequel!
@Jean-Marie I also run the print commands and I get the same values as in the screenshot you attached.
I also run the AMSDOS image and ... SUCCESS! And this is just a beauty.
One recommendation, is it possible to map the action button that is currently on the Return key to Spacebar instead? For those with one button joystick it is easier to extend your fingers and press spacebar.
The Spacebar is normally mapped as the fire button for keyboard users, but I can tinker something I guess.
I've got a question regarding the Ulifac/Usifac. From what I've understood (correct me if I'm wrong), you can use it kinda like a hard drive, or rather a SSD. So would it be possible to create a Turrican folder on the SD card, and copy all the game files in it?
I only use emulators, so it's a bit abstract to me. I'm joining the game files in enclosure.
This is THE option for me. Loads super fast and plays very well. No loading times. Run perfectly from files, perfectly!
Thanks to JMB hard work, we were able to tune up the HUD !
(https://i.postimg.cc/ry019rcb/Turrican128-New-HUD.png)
(https://i.postimg.cc/G3Wkgmmp/Turrican128-New-HUD2.png)
Quote from: OneVision on 09:12, 15 May 25Thanks to JMB hard work, we were able to tune up the HUD !
*image snip*
The new graphics look great, but can a version be maintained that preserves the original game's graphics too?
@Devlin , okay, it should be doable.
Quote from: Devlin on 09:58, 15 May 25The new graphics look great, but can a version be maintained that preserves the original game's graphics too?
You have the original game for that.
Quote from: Jean-Marie on 10:20, 12 May 25It's the latest version with music and other improvements. The left/right scrolling is triggered sooner, so you'll see incoming enemies more clearly (following a suggestion of Onevision).
RUN"FIRE2" at startup to use a Joystick with 2 buttons. Pressing both buttons launch a grenade.
RUN"FIRE3" to use a 3 buttons joystick !
More speed optimizations in the code.
Very exciting, can hardly wait. ;D
Thanks for all your hard work, Jean-Marie.
Definitively give it a try as soon as I'm back in my CPC cave.
Quote from: Egg Master on 18:36, 15 May 25Quote from: Devlin on 09:58, 15 May 25The new graphics look great, but can a version be maintained that preserves the original game's graphics too?
You have the original game for that.
I want the music, but not the updated graphics? I like how it looks originally.
having the option (even as a separate d/l) would be nice regardless.
Could you supply please a native trainer, with infinite weapons, lives and a skip level key ?
Do you plan a better ending part ?
Quote from: Devlin on 04:40, 18 May 25Quote from: Egg Master on 18:36, 15 May 25Quote from: Devlin on 09:58, 15 May 25The new graphics look great, but can a version be maintained that preserves the original game's graphics too?
You have the original game for that.
I want the music, but not the updated graphics? I like how it looks originally.
having the option (even as a separate d/l) would be nice regardless.
The first download in this thread has the dsk of the full game with original graphics and music as far as I know.
Quote from: Devlin on 04:40, 18 May 25I want the music, but not the updated graphics? I like how it looks originally.
You want.. It's their improved design, not the restaurant menu.
I wish they would offer their own vision and experience of the game, because there are some great things to improve to not waste time doing what people want.
Quote from: kawickboy on 17:03, 18 May 25Could you supply please a native trainer, with infinite weapons, lives and a skip level key ?
It will be avalable in version 2. The same trainer designed by Daren White when pressing the
VON keys.
Note that pressing those keys no longer work because I had to overwrite this part of the code with my own, for switching between SFX and music.
Quote from: kawickboy on 17:03, 18 May 25Do you plan a better ending part ?
No, it's good enough IMO. What would you like?
I can release a version with the original graphics, it's not a big deal. I can understand some people are attached to the classic design.
À better ending more in the amiga/st release spirit. The tower isn't so well drawn. Even the C64 does have something good.
With that new sprite, in-game music, and two-button support this version of Turrican beats all other 8 bits. Thanks
@Jean-Marie !
BTW I can't seem to run the game from SD or Gotek with M4 cartridge plugged-in. It keeps frozen after title screen. Without M4, all is good.
Thanks for reporting this bug (and for your support!). I'm afraid I can't be very helpful here as I only use emulators, so this M4 thingummy is a bit of a mystery to me :-X
Quote from: Jean-Marie on 19:06, 13 May 25I've got a question regarding the Ulifac/Usifac. From what I've understood (correct me if I'm wrong), you can use it kinda like a hard drive, or rather a SSD. So would it be possible to create a Turrican folder on the SD card, and copy all the game files in it?
I only use emulators, so it's a bit abstract to me. I'm joining the game files in enclosure.
Hi Jean-Marie,
Yes, the ULIFAC allow direct access to the USB stick, so you can use it as a mass storage device (like a hard drive). I do not have an M4, but my understanding is that it applies to this device as well.
I noticed that on the CPC version, we cannot throw an energy line while being in "sawblade" mode.
Still, I tend to remember that was possible on Amiga. Can anyone confirm this please?
Looking over the code, it appears the programmer did it on purpose. But removing the blocking instructions doesn't seem to cause any trouble.
Quote from: Jean-Marie on 01:57, 09 June 25I noticed that on the CPC version, we cannot throw an energy line while being in "sawblade" mode.
Still, I tend to remember that was possible on Amiga. Can anyone confirm this please?
Looking over the code, it appears the programmer did it on purpose. But removing the blocking instructions doesn't seem to cause any trouble.
Yes I confirm that it's not possible on CPC and possible on Amiga.
Last contribution for gfx rework, the little logo on the main menu.
(https://i.postimg.cc/QVGkRW0v/Small-Logo.png)
I also reworked walkers but unfortunately, we can't replace them easily in the code.
Old Walker
(https://i.postimg.cc/TYQnjwbd/Walker2.png)
New Walker
(https://i.postimg.cc/DzRN25fM/Walker3-Export.gif)
Very nice, do you plan to change other enemies sprites ?
Quote from: kawickboy on 09:32, 10 June 25Very nice, do you plan to change other enemies sprites ?
Unfortunately it's not as easy as it seems as said to me JMB.
But there are a lot of sprites which are already good in the game and do not need to be redone IMHO.
When/where can we download this with the new HUD and logo?
Great work everyone!!
Quote from: Xyphoe on 03:12, 12 June 25When/where can we download this with the new HUD and logo?
Great work everyone!!
I think it hasn't been published yet
Here is version 2 with some improved graphics thanks to
@OneVision. Rejoice!
RUN"TURRICAN" to use the regular controls.
RUN"FIRE1" to use the Space Bar instead of the Return key.
RUN"FIRE2" to use a Joystick with 2 buttons. Press both buttons to launch a grenade.
RUN"FIRE3" if you're the lucky owner of a Joystick with 3 buttons!
Or RUN"CHEAT" to display a small menu allowing you to choose your controls, and turn on the Trainer mode optionally.
ULIFAC users will have to create a Turrican folder on their MicroSD card, and drop the
Turrican Files in it.
M4 users should do the same, but it appears to be problematic. If you're a M4 owner and manage to run the game successfully, we are eager to share your knowledge!
And here is my Excel working file if you're bored 🥱
Quote from: Jean-Marie on 14:56, 12 June 25Here is version 2 with some improved graphics thanks to @OneVision. Rejoice!
RUN"TURRICAN" to use the regular controls.
RUN"FIRE1" to use the Space Bar instead of the Return key.
RUN"FIRE2" to use a Joystick with 2 buttons. Press both buttons to launch a grenade.
RUN"FIRE3" if you're the lucky owner of a Joystick with 3 buttons!
Or RUN"CHEAT" to display a small menu allowing you to choose your controls, and turn on the Trainer mode optionally.
ULIFAC users will have to create a Turrican folder on their MicroSD card, and drop the Turrican Files in it.
M4 users should do the same, but it appears to be problematic. If you're a M4 owner and manage to run the game successfully, we are eager to share your knowledge!
THANK YOU!! Fantastic work!
And I should add, as an additional control on FIRE2 ... the Gyroscope is now Down + 'Fire button 2'
Took me a little working out!
Do you think this is now the 'final' version? In terms of graphics/art/music etc? Apart from minor changes to get it working on various devices / bug fixes?
Cheers!
Quote from: Xyphoe on 03:59, 13 June 25Do you think this is now the 'final' version?
Yes, this will be the final version, I'm fed up with Turrican! :D
Quote from: Jean-Marie on 12:59, 13 June 25Quote from: Xyphoe on 03:59, 13 June 25Do you think this is now the 'final' version?
Yes, this will be the final version, I'm fed up with Turrican! :D
Cool! We'll play it as the main game/feature on the Amstream (live stream) this evening then on my YouTube!
Quote from: Jean-Marie on 14:56, 12 June 25M4 users should do the same, but it appears to be problematic. If you're a M4 owner and manage to run the game successfully, we are eager to share your knowledge!
Tried in my CPCP6128 with M4 and no problems at the moment! :P
Quote from: Jean-Marie on 14:56, 12 June 25Here is version 2 with some improved graphics thanks to @OneVision. Rejoice!
RUN"TURRICAN" to use the regular controls.
RUN"FIRE1" to use the Space Bar instead of the Return key.
RUN"FIRE2" to use a Joystick with 2 buttons. Press both buttons to launch a grenade.
RUN"FIRE3" if you're the lucky owner of a Joystick with 3 buttons!
Or RUN"CHEAT" to display a small menu allowing you to choose your controls, and turn on the Trainer mode optionally.
ULIFAC users will have to create a Turrican folder on their MicroSD card, and drop the Turrican Files in it.
M4 users should do the same, but it appears to be problematic. If you're a M4 owner and manage to run the game successfully, we are eager to share your knowledge!
Thanks! I am trying to split the files onto 2 disks
and even though the full game works, I do no know which files should be present on disk in order to trigger the ending, I must say that, for now, I just cheated my way through all levels (pressing esc to load next level), so maybe this prevents the ending to trigger. Edit: I just tried cheating my way through the official release and get the same results so I will try beating the last level and see what I get. Edit 2: I managed to beat the last level normally and the ending does trigger properly. Again, thanks!
The Ending sequence is included in the last level, so you'll have to complete the level to see it.
Pressing ESC on that level will crash the game, as you probably noticed, since it tries to load a non-existing next level.
At some point, I'll try to create a version on 2 discs. It's more complicated than it seems.
Quote from: MiguelSky on 21:48, 13 June 25Tried in my CPCP6128 with M4 and no problems at the moment! :P
Ah! We've got a winner ;D Now it's questioning time.
What's the version of your Firmware (|VERSION) ?
Is your M4 ROM located in ROM slot 6 or 7?
Do you use the so-called "
Modified lower ROM for CPC6128" ?
Yes, I've installed the modified lower ROM because I had problems with my motherboar model
Thanks for your help! So CPC6128 users, you'll need to download the Modified Lower ROM for CPC6128.
There is an Auto-installer available here (https://www.cpcwiki.eu/index.php/M4_Board).
I guess you should also upgrade your firmware to the latest version (2.0.8 ).
According to the documentation, M4 ROM must be in ROM slot 6.
Unfortunately I missed
@Xyphoe 's Amstream last night, but I had a quick look today, and this version looks amazing - a great reason for me to start building my 3-button joystick!
Many thanks for all your hard work
@Jean-Marie
Hey! Yea I managed to play through the entire game last night all the way to the end - including the ending sequence (which is great already)!
You can catch up with it the stream as a normal/public YouTube video here - https://www.youtube.com/live/JO3oryZ3to4
There is a bug, which is present in the original game anyway, with the World 4 Stage 1 boss - which you can see watching from the 2:26:30 mark .... if you sit using the laser, when he does a crush attack the programmer implement a 'screen shake' which unfortunately shifts the screen up but doesn't reset it down, moving the player sprite........ and yea - see what happens! I don't know if you can fix that or not? :D
Ouch, yes that's weird. I never experienced that with that boss. Generally, I kill him in a matter of seconds by launching a grenade on the wall. I remembered a time when I saw the player disappeared & reappeared and thought we could live with that. I'll have to look closer into that, not sure if I can resolve it if it's an original bug!
I can confirm it's an original bug. If you keep using the "death ray" during the boss fight, the screen shaking will act up for some reason. The good news is that it ain't a show-stopper, because you can simply jump to set back the screen position correctly.
I'll try to tackle it anyway, thanks for reporting it.
I played it until Level 2-2 via M4 Board on my CPC 6128 Plus.
M4 rom is set to the slot 7.
M4 firmware v2.0.8
The game was already pretty good before.
But with the efforts of Jean-Marie supported by OneVision now it is just brilliant.
Thank you so much for one of the best CPC games. :)
I have a M4 with a CPC6128, I created the folder "Turrican" with all the files inside and tried to start the game, but it freezes after the initial screen (before loading the first level)! :(
For emulators other than WinApe (which has PARADOS built in) does this new version of Turrican work elsewhere? :)
Quote from: Mr. DVG on 23:59, 15 June 25I have a M4 with a CPC6128, I created the folder "Turrican" with all the files inside and tried to start the game, but it freezes after the initial screen (before loading the first level)! :(
For emulators other than WinApe (which has PARADOS built in) does this new version of Turrican work elsewhere? :)
Have you tried using the "
Modified lower ROM for CPC6128" ? It solves many problems as far as I understand.
Also, are you using the latest firmware version (2.0.8 )?
Regarding emulators, I tested the game successfully with Caprice64, AmSpirit and ACE-DL.
You just need to copy the PARADOS rom file in the ROM folder of the emulator, and configure ROM slot 7 with Parados.
I've found an elegant solution to solve the shaking bug in Level 4.1 during the boss fight.
When the boss starts shaking the screen, i turn off the laser ray momentarily.
This makes the fight slightly more difficult, but that's a good thing as it was too easy anyway :)
So, this is Version 3 I guess.
Remember you can switch to SFX mode by pressing H followed by S !
Great Job JMB. Thank you so much for all your passion and dedication to improving the games you do ! ;)
Mr. DVG : (https://www.cpcwiki.eu/forum/profile/?u=939)Works perfectly with caprice32 by Colin Pitrat, too.
QuoteHave you tried using the "Modified lower ROM for CPC6128" ? It solves many problems as far as I understand.
Also, are you using the latest firmware version (2.0.8 )?
Regarding emulators, I tested the game successfully with Caprice64, AmSpirit and ACE-DL.
You just need to copy the PARADOS rom file in the ROM folder of the emulator, and configure ROM slot 7 with Parados.
For emulators I understood what I have to do, but for the M4 I think I have big problems, as I have never interacted with any options and various configurations... I'll try to explain better...::)
I load the games from an internal interface that reads the contents of the folders and starts with the command RUN "M4". The firmware seems to be ok, but I don't understand what to do when you say "Modified lower ROM for CPC6128"...???
@Mr. DVG , first you should make sure to use the latest firmware version, which is 2.0.8.
type
|VERSION and tell us what you see.
I noticed the same code, thus the same bug, is repeated in the second level with the giant fist.
I've applied the same fix, so here is Version 4 already!
Quote from: Xyphoe on 21:45, 14 June 25Hey! Yea I managed to play through the entire game last night all the way to the end - including the ending sequence (which is great already)!
You can catch up with it the stream as a normal/public YouTube video here - https://www.youtube.com/live/JO3oryZ3to4
There is a bug, which is present in the original game anyway, with the World 4 Stage 1 boss - which you can see watching from the 2:26:30 mark .... if you sit using the laser, when he does a crush attack the programmer implement a 'screen shake' which unfortunately shifts the screen up but doesn't reset it down, moving the player sprite........ and yea - see what happens! I don't know if you can fix that or not? :D
Thanks for featuring the game on your stream !
Best !
OneVision notified me that musics from levels 3.2 and 3.3 were inverted.
This one was easy to fix. It seems to be a mistake from Sir Guillaumin's website (https://nguillaumin.github.io/ym-jukebox/).
So here is Version 5.
Just some feedback from myself as I have not seen anyone reporting using the game with the ULIFAC. I could start the game (mass storage / file version) without any issues. I have not played far into the game but it seems to work just fine. Merci beaucoup, Jean-Marie !
And finally, an AMSDOS version spread on 2 regular DSK files. Glory !
Simply flip side when you see the border turn to White.
Does anybody here know how to use 2 disk faces on M4 ? I'm still unable to play it on real hardware.
Use the "files" version
Quote from: VincentGR on 10:38, 18 June 25Use the "files" version
Unfortunately, doesn't work either.
Featured on IndieRetroNews : https://www.indieretronews.com/2025/06/check-out-this-awesome-turrican-128k.html?spref=tw (https://www.indieretronews.com/2025/06/check-out-this-awesome-turrican-128k.html?spref=tw)
Quote from: Jean-Marie on 17:56, 17 June 25OneVision notified me that musics from levels 3.2 and 3.3 were inverted.
This one was easy to fix. It seems to be a mistake from Sir Guillaumin's website (https://nguillaumin.github.io/ym-jukebox/).
So here is Version 5.
Looking forward to trying this tonight. I assume it works fine with a real 464 running a DDi5?
Thanks! I see no reason why it wouldn't work with DDI5, but I'm clearly not an expert of all those modern devices!
Quote from: Jean-Marie on 17:56, 17 June 25OneVision notified me that musics from levels 3.2 and 3.3 were inverted.
This one was easy to fix. It seems to be a mistake from Sir Guillaumin's website (https://nguillaumin.github.io/ym-jukebox/).
So here is Version 5.
Amazing !
I noticed during Xyphoe's stream that the track used in 5.1 and 5.2 are also inverted.
Thanks for this amazing version of Turrican 1 anyway !
Now, that is quite strange!
I just checked the Atari ST longplay, and it appears that the musics from those levels (5.1 and 5.2) are inverted when compared with the Amiga ones :-\
Soooo, I don't know what to do ! Should I match the ST or Amiga design? You decide folks!
Quote from: Jean-Marie on 19:11, 20 June 25Now, that is quite strange!
I just checked the Atari ST longplay, and it appears that the musics from those levels (5.1 and 5.2) are inverted when compared with the Amiga ones :-\
Soooo, I don't know what to do ! Should I match the ST or Amiga design? You decide folks!
The Amiga was the lead platform for the 16 bits, so the order should be from that.
The overall lead platform was the C64 and by Manfred Trentz, but I don't think the music matches, not sure though.
Yeah, that makes sense I guess.
So Version 6 it is!
Quote from: Jean-Marie on 20:10, 20 June 25Yeah, that makes sense I guess.
So Version 6 it is!
So cool, thanks !
I tried the "non ParaDOS" version on CPCEC and it now runs great, as long as I choose the 6128 ROM (not the CPC+ as it launches with a corrupted image).
Then I set the overclock CPU option to 2x and it's very, very, very smooth ! Now with the much better centered scrolling, it feels so good !
The only downside is that the lightning effect during the storm in 1.2 is very hard on the eyes but that was also in the original version with the same result if the CPU is set to 2x, the flashes occurs a lot more than before because of the speed going much higher. If you are epilepsy sensitive,
do not overclock your CPC during the second level (including in the original version of Turrican 1 !).
My only wish is for a much slower timer because even without any overclock, the CPC version of Turrican 1 timer is way, way too fast ^^.
I just had a look at that Timer, and there's something amiss indeed 🤔
Looking at the Atari video, it should be decremented every second, but it's a bit faster on the CPC.
For some unknown reason, Daren White didn't rely on Interrupts to clock the Timer. This would have given the best precision, since there are 300 interrupts per second. You just need to set a value to 300, decrement it on every interrupt, and decrement the Timer when that value reaches 0.
I might have a look into that, thanks for reporting it.
org &04c8
ld a,(&dea0) ;;counter
and &07
jp nz,l04de
ld a,&03 ;;number of digits to display
ld bc,&c50f ;;Timer string offset in VRAM
ld de,&01 ;;decrement step
ld hl,&ded8 ;;Timer value (24 bit)
call &c622 ;;Decrement function
I just noticed something about Turrican (1989)... It was really influenced by Metroid (1986), right? I think both games share the turning into a ball feature.
But I suspect Turrican plays a bit differently... and maybe it offers some new mechanics of its own?
Yeah, Metroid and
Psycho-Nich Oscar from what I've read. Maybe
Galivan too?
Quote from: cwpab on 17:48, 21 June 25I just noticed something about Turrican (1989)... It was really influenced by Metroid (1986), right? I think both games share the turning into a ball feature.
But I suspect Turrican plays a bit differently... and maybe it offers some new mechanics of its own?
Turrican plays like a game someone wrote after seeing screenshots of Metroid. It's superficially similar, with things like the ball mechanic, but doesn't really play in the same way.
Quote from: cwpab on 17:48, 21 June 25I just noticed something about Turrican (1989)... It was really influenced by Metroid (1986), right? I think both games share the turning into a ball feature.
But I suspect Turrican plays a bit differently... and maybe it offers some new mechanics of its own?
Quote from: andycadley on 18:29, 21 June 25Quote from: cwpab on 17:48, 21 June 25I just noticed something about Turrican (1989)... It was really influenced by Metroid (1986), right? I think both games share the turning into a ball feature.
But I suspect Turrican plays a bit differently... and maybe it offers some new mechanics of its own?
Turrican plays like a game someone wrote after seeing screenshots of Metroid. It's superficially similar, with things like the ball mechanic, but doesn't really play in the same way.
Manfred Trenz, who coded the original C64 game, was indeed inspired by Psycho Nics Oscar (for the futuristic armor & weapons) & Metroid (for its vast levels and of course the morh ball) but also the Super Mario Bros. games for the ease of control, which can already be seen in The Great Giana Sisters game he worked on in 1987, a Mario rippoff (still fun in its own, I loved the DS game -now playable on Steam-).
Fun fact : despite getting a notice from Nintendo to demand Rainbow Arts to pull The Great Giana Sisters from stores, they later agreed to get a new, much improved reboot of the game on the Nintendo DS with a ton of levels, including... the entirety of the original levels.
This is version 7 featuring a more forgiving Timer decremented every second. Hooray!
Yeah !
Quote from: Jean-Marie on 15:31, 21 June 25I just had a look at that Timer, and there's something amiss indeed 🤔
Looking at the Atari video, it should be decremented every second, but it's a bit faster on the CPC.
For some unknown reason, Daren White didn't rely on Interrupts to clock the Timer. This would have given the best precision, since there are 300 interrupts per second. You just need to set a value to 300, decrement it on every interrupt, and decrement the Timer when that value reaches 0.
I might have a look into that, thanks for reporting it.
org &04c8
ld a,(&dea0) ;;counter
and &07
jp nz,l04de
ld a,&03 ;;number of digits to display
ld bc,&c50f ;;Timer string offset in VRAM
ld de,&01 ;;decrement step
ld hl,&ded8 ;;Timer value (24 bit)
call &c622 ;;Decrement function
Hi Jean-Marie,
I have a little request : could it be possible for you to speed up the scrolling of Turrican ? In order to match a little better the Amiga/C64 versions ?
Cheers, and hats off for this great remaster !
Quote from: dlfrsilver on 21:41, 22 June 25could it be possible for you to speed up the scrolling of Turrican ?
Thank you
@dlfrsilver. I did all that I could to have the game faster. For the scrolling, it used LDIR/LDDR instructions that I have unrolled, so it should be a tad faster, even if it's hardly noticeable. However, I'm not a scrolling expert, and maybe I missed obvious things.
The disassembled and (very poorly) commented code can be found in the Excel file enclosed. If someone can come up with improvements, feel free to participate.
The scrolling functions can be found in the
Optimization tab at:
SCROLL UP: 124C
SCROLL DOWN:130E
SCROLL LEFT: 1372
SCROLL RIGHT: 12B7
Although, to be honest, I'd really like to close the Turrican chapter, as I've been playing it ad nauseam since February!
Quote from: Jean-Marie on 00:46, 23 June 25Quote from: dlfrsilver on 21:41, 22 June 25could it be possible for you to speed up the scrolling of Turrican ?
For the scrolling, it used LDIR/LDDR instructions that I have unrolled, so it should be a tad faster, even if it's hardly noticeable. However, I'm not a scrolling expert, and maybe I missed obvious things.
@Jean-Marie : I am very impressed. Developing a program from scratch is one thing. But successfully redesigning an existing program (in machine code) with all its constraints and limitations, and even improving it, is a whole other level.
@dlfrsilver : If software scrolling is used for a game (like it is here), it will consume a big amount of computation time, probably more than anything else. Adapting this to hardware scrolling would need a redesign of big parts of the program. One example: In Turrican sprites are never erased. They are just overwritten by the next background. If the background is scrolled without being redrawn, each sprite will create a tail of unerased parts.
As far as I've seen, scrolling is done in a two-step process: First from the level map a screen-fitting tile map is copied. Then all tiles from this tile map are drawn to the screen. This second step looks like this:
ORG #0DA9
;; ... setting registers, initializing loops
;; This block copies one tile of 2x8 bytes to the screen buffer.
LDI : LDI : ADD B : LD D,A : DEC E : DEC E ;; 14
LDI : LDI : ADD B : LD D,A : DEC E : DEC E ;; 14
LDI : LDI : ADD B : LD D,A : DEC E : DEC E ;; 14
LDI : LDI : ADD B : LD D,A : DEC E : DEC E ;; 14
LDI : LDI : ADD B : LD D,A : DEC E : DEC E ;; 14
LDI : LDI : ADD B : LD D,A : DEC E : DEC E ;; 14
LDI : LDI : ADD B : LD D,A : DEC E : DEC E ;; 14
LDI : LDI : SUB C : LD D,A ;; 12
;; ;; ---
;; ;; 110
So one tile needs 110 NOPs, the surrounding loop structure needs additional 24 NOPs, and 32x16=512 tiles need to be drawn. This takes 68'608 NOPs or about 3.5 frames. With this approach, the frame rate will never be higher than 12.5 hz. And still, sprites, movement, music, game logic are missing.
Maybe somebody else sees something how this can be improved.
While experimenting with the Turrican tile drawing / scrolling, I had one idea to speed it up: Only draw half of the image, and later draw the remaining half. This somehow works, the drawing gets faster by one frame. But now every object is followed by some shadow object. Looks interesting, but does not really improve the game play. You can never be sure how many enemies are on screen.
ORG #0DA9
LD A,(#DE74) : CP A,#30 ;; Check which screen buffer should be updated.
;; This is not an address, but the value for CRTC register 12.
LD IX,#E72D : JP Z,$+6 : LD LX,#2F ;; Load address of list with screen row addresses to IX.
;; There are two entries per list row,
;; one for screen #8000 and one for #C000.
;; Update local frame counter.
LD A,0 :store_a : INC A : LD (store_a-1),A ;; 6 bytes
LD HL,(#C7F1) ;; Load address of tile map.
LD D,#60 ;; High byte of tile buffer address.
LD C,#10 ;; Number of rows.
row_loop:
EXX
LD E,(IX+#00) ;; Load start address of screen row from list to DE'.
LD D,(IX+#01) ;;
LD A,#04 : ADD LX : LD LX,A ;; Go to next address in list.
LD B,#10 ;; High byte of 2 scanline offsets in B'.
EXX
LD B,#20 ;; Number of chars.
char_loop:
XOR A : RLD ;; 6 ;; Load upper 4 bits of tile number to A. Also shift tile number in (HL) by 4 bits.
ADD D ;; 1 ;; Add tile buffer base address to get high byte of tile address in A.
EX AF,AF' : LD A,(HL) ;; 3 ;; Load shifted tile number as low byte of tile address in A'
INC HL ;; 2 ;; Go to next tile in tile map.
;; ;; -- ;;
;; ;; 12 ;;
EXX ;; 1
LD L,A : EX AF,AF' : LD H,A ;; 3 ;; Transfer tile address from A/A' to HL.
LD A,(store_a-1) : BIT 1,A : JR Z,even_frame ;; 4 ;;
odd_frame: ;; ....... ;;
LD C, #30 + 8 ;; 2 . ;; High byte of 6 scanline offsets plus 8. This will be reduced by 8 LDIs.
LD A,D ;; 1 . ;; Store high byte of screen buffer address.
JR registers_are_set ;; 3 . ;;
even_frame: ;; . . ;;
LD C, #38 + 8 ;; . 2 ;; High byte of 7 scanline offsets plus 8. This will be reduced by 8 LDIs.
INC L : INC L ;; . 2 ;; Go to next tile line.
LD A,D : ADD #08 : LD D,A ;; . 4 ;; Store high byte of next screen buffer address.
JR registers_are_set ;; . 3 ;;
DEFS 1 ;; -- -- ;; Fill unused bytes.
registers_are_set: ;; 10 15 ;; Average time: 12.5 NOPs
;; This block copies one tile of 2x8 bytes to the screen buffer.
;; It needs 60 NOPs. Loop overhead is additional 12+1+3+12.5+1+4 = 33.5 NOPs.
;; The loop is executed 32x16 = 512 times to show a single frame.
;; This takes 47'872 NOPs or ~2.4 frames.
LDI : LDI : ADD B : LD D,A : DEC E : DEC E ;; 14 ;; Copy two bytes. Then go to next scanline.
INC L : INC L ;; 2
LDI : LDI : ADD B : LD D,A : DEC E : DEC E ;; 14
INC L : INC L ;; 2
LDI : LDI : ADD B : LD D,A : DEC E : DEC E ;; 14
INC L : INC L ;; 2
LDI : LDI : SUB C : LD D,A ;; 12 ;; Copy the last two bytes. Go back to the first scanline.
;; ;; ---
;; ;; 60
EXX ;; 1
DJNZ char_loop ;; 4|3
;; The tile map is a bit bigger than the screen.
;; Skip 4 invisible tiles at the border.
INC HL : INC HL : INC HL : INC HL
DEC C : JP NZ,row_loop
Wow, thanks for your contribution, that's an interesting idea. I'll give it a try soon. You seem to have understood the code better than I did, bravo!
Also, I think there might be a mistake here:
LD A,(store_a-1) : BIT 1,A : JR Z,even_frame
Shouldn't it be Bit 0,A (or better: RRA), or have I misunderstood something?
Quote from: Jean-Marie on 00:46, 23 June 25Quote from: dlfrsilver on 21:41, 22 June 25could it be possible for you to speed up the scrolling of Turrican ?
Thank you @dlfrsilver. I did all that I could to have the game faster. For the scrolling, it used LDIR/LDDR instructions that I have unrolled, so it should be a tad faster, even if it's hardly noticeable. However, I'm not a scrolling expert, and maybe I missed obvious things.
The disassembled and (very poorly) commented code can be found in the Excel file enclosed. If someone can come up with improvements, feel free to participate.
The scrolling functions can be found in the Optimization tab at:
SCROLL UP: 124C
SCROLL DOWN:130E
SCROLL LEFT: 1372
SCROLL RIGHT: 12B7
Although, to be honest, I'd really like to close the Turrican chapter, as I've been playing it ad nauseam since February!
Thanks a lot ! :D
I asked because some people said that it had the same scrolling speed as the original with no changes.
It's very interesting to see what can be done to improve scrolling. :)
Have you taken a look at the Turrican 2 display? It seems faster to me.
Quote from: Egg Master on 10:58, 23 June 25Have you taken a look at the Turrican 2 display? It seems faster to me.
It could be due to the screen size, which is smaller. A lot of games use software scrolling, so it could be interesting to have a look, although Daren White did quite a good job. I'm not sure it can be noticeably improved without changing a lot of things.
I'm just curious, are the input routines at the start or end of a frame? If not at the end could they be moved to the end to reduce any perceived lag? As in take input at end of frame so next frame if generated based on those latest inputs?
Quote from: Jean-Marie on 09:59, 23 June 25I think there might be a mistake here:
LD A,(store_a-1) : BIT 1,A : JR Z,even_frame
Shouldn't it be Bit 0,A (or better: RRA), or have I misunderstood something?
First I wanted to use bit 0. Then I recognized that the game uses double buffering. With checking bit 0, the first buffer will always show the first half, and the second buffer will always show the second half. But it is necessary to overwrite both halfs in each buffer. This is done by checking bit 1. Now the first buffer will show the first half, then the second buffer will also show the first half. After this, both buffers will show the second half.
Quote from: lmimmfn on 12:28, 23 June 25are the input routines at the start or end of a frame?
The keyboard is checked by the Interrupt Service Routine once per frame, during the the Vertical Retrace. So 50 times per second. During that Time, the code will set CRTC registers for the Split screen effect, play the Music or SFX, check the keyboard inputs and modify the keyboard buffer accordingly, and change ink number 15 (for the water flow effect I think).
org &ce00
push af
push bc
push de
push hl
push ix
push iy
call &cf25 ;;set CRTC reg 12 & 13
ld a,i ;;interrupt counter
inc a
cp 6
jp c,lce16
xor a
lce16:
ld i,a
cp 5
jp nz,lcee1
jp @Player
DB &75,&DE
ld d,3 ;;3 channels
ld iy,&de76
lce29: ;;SFX Player
ld a,(iy+1)
or a ;;cp a: turn off SFX
jp z,lceb0
ld c,(iy)
ld ixh,a
ld ixl,c
ld a,(ix)
or a
jp z,lce91
dec (ix)
ld a,3
sub d
ld e,a
add a
ld c,(ix+1)
call &cf92
ld a,e
add a
inc a
ld c,(ix+2)
call &cf92
ld c,(ix+3)
ld a,6
call &cf92
ld a,e
add 8
ld c,(ix+4)
call &cf92
ld c,(iy+2)
ld a,(ix+3)
or a
jp z,lce74
ld c,(iy+3)
lce74:
ld a,(&de75)
and c
ld (&de75),a
ld l,(ix+1)
ld h,(ix+2)
ld c,(ix+5)
ld b,(ix+6)
add hl,bc
ld (ix+1),l
ld (ix+2),h
jr lceb0
lce91:
ld a,(&de75)
or (iy+4)
ld (&de75),a
ld a,(ix+7)
ld (ix+1),a
ld a,(ix+8)
ld (ix+2),a
ld a,(ix+9)
ld (ix),a
ld (iy+1),0
lceb0:
ld a,5
add iyl
ld iyl,a
dec d
jp nz,lce29
ld a,(&de75)
ld c,a
ld a,7 ;;PSG Mixer register
call &cf92 ;;turn off AY channels
@SkipSFX:
call &ceec ;;scan keyboard
ld a,(&1b2e) ;;change ink #15
cp 2
ld bc,&4b1a
jp z,lced4
ld bc,&5502
lced4:
ld a,c
ld (&1b2e),a
ld a,b
ld bc,&7f0f
out (c),c
out (c),a
ld hl,&04C9 ;;decrement Timer count
dec (hl)
lcee1:
pop iy
pop ix
pop hl
pop de
pop bc
pop af
ei
ret
Yeah, the sprites are rendered with a "ghost" effect, but the scrolling is flawless indeed !
That's interesting nonetheless, and could pave the way to new ideas.
Thanks a lot for taking the time to explain us the inner working of the scrolling
@lightforce6128
To avoid the ghost effect, I also tried another approach. Each tile is stored with 16 bytes in a table starting at #6000. If the space of four tiles is combined, the drawing code for this tile can be unrolled. Then each tile gets a small and fast drawing routine.
This reduces the calculation time by almost 40%!The drawbacks are:
- The tiles need to be redesigned. The number shrinks from 256 to 64. The drawing routine is fast, but less flexible. This will require some fiddling.
- The level maps need to be redesigned to make use of the reduced number of tiles.
- Some tiles are animated (e.g. the parts of the energy beam). These animations need to be adapted to the new tile format (or deactivated).
Just for checking the speed I created a patch that will only use 32 tiles (those without animation) and fill them with random pixels. This reduces the calculation time from 3.5 frames to 2.2 frames. Now there are no longer any ghost effects, but the landscape looks a bit chaotic.
NOLIST
MACRO DRAW_NEXT_TILE
;; ;; NOPs ;; bytes ;;
XOR A,A : LD L,A ;; 1 ;; 1 ;; Clear flags. Set L to zero.
LD A,(DE) : INC DE ;; 4 ;; 2 ;; Load next tile number.
;;RRA : RR L : RRA : RR L ;; 6 ;; 6 ;; Shift tile number into tile address in HL.
AND A,7 : DEFS 4 ;; ;; ;; As current workaround: Mask tile number.
ADD A,C : LD H,A ;; 2 ;; 2 ;;
JP (HL) ;; 1 ;; 1 ;; Execute tile.
;; ;; -- ;; -- ;;
;; ;; 15 ;; 13 ;;
ENDM
ORG #0DA9
LD C,#60 ;; 2 ;; High byte of tile buffer in C.
LD DE,(#C7F1) ;; 6 ;; Start address of tile map in DE.
EXX ;; 1 ;;
LD DE, 4 * #800 - 2 * -1 ;; 3 ;; Offset to next char in DE'.
XOR A,A : LD L,A ;; 2 ;; Set low byte of screen buffer to zero in L'.
LD A,(#DE74) : RLA : RLA : XOR A,#40 : LD H,A ;; 9 ;; Convert CRTC value of visible screen to high byte of hidden screen buffer in H'.
EXX ;; 1 ;;
;; ;; -- ;;
;; ;; 24 ;;
row_loop:
LD B,#20 ;; 2 ;; Number of chars in B.
DRAW_NEXT_TILE ;; - ;; Count NOPs in (last) tile, not here.
DEFS 127-46 ;; - ;; Fill unused bytes.
return_to_row_loop: ;; - ;;
INC DE : INC DE : INC DE : INC DE ;; 8 ;; Skip 4 invisible tiles at the border.
EXX : BIT 2,H : EXX : JR Z,row_loop ;; 7-1 ;; If not 16 rows have been drawn, continue loop.
;; ;; ---- ;;
;; ;; 17-1 ;; This is executed 16 times: (16*17)-1 = 271
ORG #6000
;; Define random pixel bytes.
LET a_value = #00
LET bc_value = #0000
REPEAT 32
;; INFO: There are not only 32 possible tiles, but 64. But the second half
;; is updated with animation. Currently this is not compatible with the
;; below unrolled code. So only the first 32 tiles can be used.
;; The following is the code to draw one tile. Besides unrolled drawing commands
;; it also contains some loop code. Each tile automatically fetches the next one.
;; ;; NOPs ;; bytes ;;
EXX ;; 1 ;; 1 ;;
LD A,a_value : LD BC,bc_value ;; 5 ;; 5 ;; Set up often used bytes.
LD (HL),A : INC L : LD (HL),C : SET 3,H ;; 7 ;; 5 ;; Draw two bytes.
LD (HL),B : DEC L : LD (HL),A : SET 4,H ;; 7 ;; 5 ;; ...
LD (HL),C : INC L : LD (HL),B : RES 3,H ;; 7 ;; 5 ;;
LD (HL),A : DEC L : LD (HL),C : SET 5,H ;; 7 ;; 5 ;;
LD (HL),B : INC L : LD (HL),A : SET 3,H ;; 7 ;; 5 ;;
LD (HL),C : DEC L : LD (HL),B : RES 4,H ;; 7 ;; 5 ;;
LD (HL),A : INC L : LD (HL),C : RES 3,H ;; 7 ;; 5 ;;
LD (HL),B : DEC L : LD (HL),A ;; 5 ;; 3 ;; Draw the last two bytes.
ADD HL,DE ;; 3 ;; 1 ;; Go to the next char.
EXX ;; 1 ;; 1 ;;
DJNZ $+5 : JP return_to_row_loop ;; 4+2 ;; 5 ;; Continue or leave the loop.
DRAW_NEXT_TILE ;; 15 ;; 13 ;; Draw the next tile.
;; ;; -- ;; -- ;;
;; ;; 83+2 ;; 64 ;;
;; Now the tiles need four times the space they needed before. But drawing is faster by 37%.
;; Drawing one tile with additional loop code takes 83 NOPs. All 32x16 = 512 tiles
;; will need 42'496 NOPs or ~2.2 frames.
;; Update random pixel bytes.
LET a_value = a_value + 83 AND 255
LET bc_value = bc_value + 7901
REND
This looks great, thanks for sharing your knowledge! Although, yeah, redesigning the level maps would be quite heavy. I need to think about it.
An Amsdos release. Thanks. There is a turn disk message so ?
@jmb11 @lightforce6128 I could give a try with level 1 and 64 tiles max. What kind of file would be needed ? A Tiled file with a .tmx and a .tsx ?
Quote from: kawickboy on 07:22, 24 June 25An Amsdos release. Thanks. There is a turn disk message so ?
Not exactly : you need to turn the disk when the border becomes white.
Not sure if this will be any use, but a few years ago I looked at trying to improve the background character printing routine in Turrican, and produced a bit of test code with reformatted characters (which I've attached) This only improved the speed by about half a screen refresh though, and it revealed that I'd need to spend a lot more time on it than I was prepared to spend to actually integrate it, so I didn't take it any further. It would need to be used in conjuction with other improvements to be useful.
The approach was to alter the format of the character data in a way that meant there was only the need of a single set or reset to traverse the character on screen, and also to 'interleave' the character data to reduce the calculation done to find the character data. This is 20nops faster per character between both the print and locating the char address in memory.
Here's the replacement code:
org #0dbf ; 128k v7
exx
ld e,(ix+#00)
ld d,(ix+#01)
;ld bc,#0004
;add ix,bc
exx
.l0dc0
ld b,#20
.l0dcf
;xor a
;rld
;add d
;ex af,af'
ld a,(hl)
inc hl
exx
ld h,&60
add a,a
ld l,a
jr nc,skip
set 3,h
.skip
;ex af,af'
;ld h,a
;ld a,d
ldi
ld a,(hl)
ld (de),a
set 3,d
inc h
ldd
ld a,(hl)
ld (de),a
set 4,d
inc h
ldi
ld a,(hl)
ld (de),a
res 3,d
inc h
ldd
ld a,(hl)
ld (de),a
set 5,d
inc h
ldi
ld a,(hl)
ld (de),a
set 3,d
inc h
ldd
ld a,(hl)
ld (de),a
res 4,d
inc h
ldi
ld a,(hl)
ld (de),a
res 3,d
inc h
ldd
ld a,(hl)
ld (de),a
res 5,d
inc e
inc e
;jr nz,skip2
;inc d
;skip2
exx
djnz l0dcf
exx
ld a,e
or a
jr nz,skip2
inc d
.skip2
exx
inc hl
inc hl
inc hl
inc hl
dec c
jp nz,l0dc0
defs 9
I've attached a zip that contains that source as well the reformatted characters so you can try it in the game (the v7 posted earlier). You will need to set a breakpoint somewhere outside the screen update routine (say at &451 is where it is called) then compile CharPrint_Test_v7.asm and Level1Chars_Test.asm to memory. Being half a frame faster doesn't really produce much of tangible benefit. In theory, it just raises the bar for it slowing down from 10fps to 8fps. One of the problems revealed if you try it is that there is code in the game rewriting the flashing characters regularly, so that corrupts some of the background chars in the new format, so would need to be modified for this to be any use.
The zip also contains the routine I used to re-order the character data. It is not optimized at all, but might help explain the reformatting.
Thanks
@Axelay , I will have a close look at it. Half a frame is a great improvement, don't be modest !
Is there any way that we could have a CPR version of the game ?/thank you.
Quote from: amijim on 22:21, 24 June 25Is there any way that we could have a CPR version of the game ?
Sorry, I don't know how to create a CPR file, apart from using NOCART (https://www.cpcwiki.eu/index.php/Nocart), but it wouldn't work with a Parados DSK I guess.
Quote from: Axelay on 11:54, 24 June 25The approach was to alter the format of the character data in a way that meant there was only the need of a single set or reset to traverse the character on screen, and also to 'interleave' the character data to reduce the calculation done to find the character data. ... One of the problems revealed if you try it is that there is code in the game rewriting the flashing characters.
Quote from: OneVision on 08:10, 24 June 25I could give a try with level 1 and 64 tiles max. What kind of file would be needed ? A Tiled file with a .tmx and a .tsx ?
@Axelay : Changing H instead of L never came to my mind. This allows to use LDI/LDD without adjusting the pointers with DEC/INC. And it still uses the original data structure, although the data is organized differently. I did an analysis of the animated tiles. For the first level it is the spike, the waterfall, and the flame. The flame is just copied from two sets of prepared flame tiles. Although the copying routine needs to be adapated to the interlacing, this should not be difficult. But the two other tile sets are really animated by some kind of scrolling. Should also be doable, but is more complex.
@OneVision : I appreciate your dedication. I would be afraid of this task. This is something completely different from programming. I do not know the .tmx and .tsx file formats. But for the special unrolled code you would need to provide these numbers:
- Three often used pairs of colors for this tile (from the palette used in Turrican). This will give three times a left pixel and a right pixel, e.g. numbered as 1, 2, 3.
- Also available, but fixed is the pair bright red / white (6/26). This could be number 4.
- Also available, but fixed is the pair dark blue / background (sky blue or black) (1/11|0). This could be number 5.
- Finally a list of 16 number, each selecting one of the above 5 pairs of color.
- Combined: A tile is a list of 3x2 + 16 = 22 numbers.
I'm aware that there is no tile editor or graphics program to create an output like this. So it would be easier to use a regular program and to convert the tiles afterwards. But it is important to keep in mind that each tile may only be build up from 3 indiviual and 2 fixed pairs of colors / left/right pixels.
Besides reducing the number of tiles to 64, there is still the problem with the animated tiles. It seems that there are three animation routines for level 1. This probably is different for other levels. As for the approach of Axelay, here it is also possible to adapt the animation routines, although it would not be easy.
Below you find a tile map with the marked animations:
The drawing of the energy lines would need to be modified too (not sure if this is what you call the flames?).
The code can be found in cell H950 in my Excel file.
l09d4: ;;actually located @9BBh
ld a,(&19b2)
sub &02
ld (&19b2),a
cp &20
jp nc,l09e8
ld ix,&19b4
call l09ff
l09e8:
ld a,(&19b3)
add &02
ld (&19b3),a
cp &20
jp nc,l0803
ld ix,&19c4
call l09ff
jp l0803
l09ff:
ld c,a
ld hl,(&c7f1)
add l
ld l,a
adc h
sub l
ld h,a
ld b,&10
l0a09:
ld a,(ix)
or a
jr z,l0a57
ld d,&5f
ld e,(hl)
ld a,(de)
or a
jp z,l0a41
cp &d3
jr z,l0a41
ld (ix),0
cp &7f
jp nz,l0a57
push hl
push ix
push bc
sub a
sub b
add &10
ld b,a
ld (&97e8),bc
ld ix,&97e8
call &0cee
pop bc
pop ix
pop hl
jp l0a57
l0a41:
ld a,(ix)
ld (hl),a
inc a
cp &f3
jp nz,l0a4d
ld a,&f0
jr l0a54
l0a4d:
cp &f6
jp nz,l0a54
ld a,&f3
l0a54:
ld (ix),a
l0a57:
ld de,&24
add hl,de
inc ixl
djnz l0a09
ret
Also, the "Death ray" when keeping Fire1 pressed. The code can be seen in cell H1107 and is located at offset AF5h.
ld a,&20
REPEAT 9
ld (hl),a
add hl,de
ENDM
ld a,&81
ld (&97ed),a
ld a,(&97f8)
add a
add &76
ld l,a
adc &ff
sub l
ld h,a
ld e,(hl)
inc hl
ld d,(hl)
ld bc,(&97f0)
push de
ld a,b
add a
add a
ld e,a
add a
add a
ld l,a
ld h,0
ld d,h
add hl,hl
add hl,de
ld e,c
add hl,de
ld de,(&c7f1)
add hl,de
pop de
ld (&97f6),de
ld a,1
jr $+6
l0b52:
ld a,(&97fa)
inc a
ld (&97fa),a
ld c,a
ld a,(&97f9)
cp c
jr c,l0bdf
ld b,&5f
ld c,(hl)
ld a,(bc)
cp &d3
jr z,l0b7d
or a
jp z,l0b7d
cp &7f
jp nz,l0bdf
ld ix,&97f0
call &0cee
jr l0bdf
The plants at the beginning were flickering when I tested the code, yet these are no animated tiles?
Quote from: Jean-Marie on 04:15, 25 June 25The drawing of the energy lines would need to be modified too (not sure if this is what you call the flames?).
Also, the "Death ray" when keeping Fire1 pressed.
The plants at the beginning were flickering when I tested the code, yet these are no animated tiles?
I tested with a debugger while playing a bit. Only the tiles marked as target in the above tile map (arrow pointing toward it or showing movement direction) actually changed. All other tiles are static as soon as the level has been loaded.
The energy line (keep Fire pressed) is made from four tiles from the lower right corner. But only palette animation on pen 15 is used to rapidly change its colour between blue and white. I guess, the visible animation effect is done by randomly using different tiles. But these tiles themselves are not animated.
The animated flame is probably below the space ship.
The death ray (Enter) uses six tiles from the upper right corner, also without tile animation.
With the code snippet from Axelay I also saw the flickering plants. But this is not caused by tile animation. There must be a small bug in the code, although I cannot spot it. I suspect these initialization commands that calculates the tile starting address:
ld h,&60
add a,a
ld l,a
jr nc,skip
set 3,h
.skip
The plant tiles are above 128, sky and earth tiles are below 128. Maybe the flickering also occurs for other tiles above 128, but in the starting screen they are not used?
great project but I can't validate the palette... I know it is the original version's one... Devs often did screw up the palettes on the CPC versions, because they were so hard on the C64 and speccy and amiga and didn't care about CPC, quite often...
Haven't checked if you can really modify the palette but if you can :
to use both bright and pastel yellows and greens is a waste... all it does is giving garish saturated hues with too few visual gain, on the other hand the blues are not used as they shoul, as it is where to look at to replace the large grey gradiants of the amiga version.
If you really want to use CPC's medium and only grey, grade it with Black, dark blue, grey, pastel blue, perhaps pastel cyan and white. else you can use the DarkBlue-Mauve-pastelblue gradiant to get a nice greyish substitute. For those earth/rocks tiles, don't don't don't use bright Red as it is overly Saturax bleeding eyes of doom inducting... proper earthy rocks ramps would be Dark Red, Dark Yellow, Orange, pastel yellow and white : better contrast, less saturated thing. yellow, green and Cyan can't decently field both medium and pastel ones in general, I suggest to opt for medium green, pastel yellow, pastel or medium cyan (not both)... pastel yellow goes better with medium green than pastel green anyway as it provides a hue shift.
a good ramp to use can be Dark cyan + sea green + pastel yellow (+black and bright white)... not sure it can fit well into turrican though.
Please get rid of the medium plain flashy blue... it doesn't mix well with CPC's unique grey they are not supposed to be gradiants of each others. keep it to water or some effects here and there...
You could thin the helmet a slight bit (see my posted mockup). mdoe0 needs to sacrifice some details from the amiga version...
BTW this is a good hack idea, and while every one can have opinion on the way to do it, priority to the ones doing the thing. I'm just giving my (maybe wrong) opinion as always, it engages just me, you can well ignore it.
***Yeah i do'nt quite use the official names always, Dark/medium/pastel = plain/bright/Pastel.... grey is "white 13" and white is "bright white 26". most "Bright" ones actually mean "saturax cartoonish garish" and must be limited... human eye doesn't see contrast in green as in blue or red, hence greens, yellows and cyans (they contain green) lack contrast between the brights and pastels, compaired to reds, magentas and blues.... yeah we have 5 greens but really can't get a proper usable gradiants for more than 2 and half. must shift hue into cyan or yellow at some point.
@lightforce6128 Thank you for all the details. I'm not sure though I have understood everything !
I'll start with what I know : usually I use a software like Tiled to edit 2D levels. I need 2 things : a tiles bank (the .tsx stands for tileset I guess) and of course the tile map (.tmx which is where goes what tile).
I'm afraid I didn't understand your needs in terms of colors used in each tile. What I was proposing is the following : if I can be provided with the tilemap of level 1-1 with all the tiles, I can rearrange/downgrade it to have the same game feel and gameplay with only 64 tiles by simplifying.
For animated tiles : if the compute boost is significant, it can be decided to simply cancel animated tiles. We can all say it's better to have faster scrolling and smoother animation rather than a few animated tiles.
Well here's the thing : I can rearrange level 1-1 if I can be given a .png file of the whole level with the bank of all tiles. Then I can start working on it.
Sorry if I missed some important points in your optimisations, and I'm open to discuss it by voice on Discord or other.
Thank you !
Quote from: lightforce6128 on 05:13, 25 June 25With the code snippet from Axelay I also saw the flickering plants. But this is not caused by tile animation. There must be a small bug in the code, although I cannot spot it. I suspect these initialization commands that calculates the tile starting address:
ld h,&60
add a,a
ld l,a
jr nc,skip
set 3,h
.skip
The plant tiles are above 128, sky and earth tiles are below 128. Maybe the flickering also occurs for other tiles above 128, but in the starting screen they are not used?
Hmm. I don't believe that is correct. I see a routine updating the characters in the tileset is triggered by a call from &45a to &1b03. I placed a RET at &1b03, the chars stopped updating, I then assembled the code & graphics I posted earlier to memory, and there is no longer a graphical problem with the plants or any other characters I had seen an issue with previously.
@MacDeath, Yes, you can modify the palette after a level has been loaded. You'll need to put a breakpoint at
1B00h (this is the entry point), then change the 16 values starting at
1B1Fh (see screenshot). Actually, ink #15 cannot be changed, as it is used to produce the blinking effect of the Lazer Ray, and it is changed on every frame.
There is a catch however, as those values are not color values but an index in a table of 27 colors (the palette), located at
DE85h. For some (bad) reason, the programmer used an indirection method.
org #de85 ;;palette table
db #14,#04,#15,#1c,#18,#1d,#0c,#05
db #0d,#16,#06,#17,#1e,#00,#1f,#0e
db #07,#0f,#12,#02,#13,#1a,#19,#1b
db #0a,#03,#0b
Thus, if you peek at the first value at 1B1F (this will be ink #0), it contains the index value 11 (0Bh).
If you add 0Bh to the palette table at DE85h, you'll get DE90h and it contains 17h, which is the background blue (those are Gate Array color values). A bit cumbersome, innit?!
As for you mockup, no offense but I prefer the current version. Your colors for the ground seem washed up, like on Commodore 64, while we CPC users are accustomed to vivid colors! But it's a matter of taste I guess, difficult to please everyone.
Quote from: Axelay on 11:44, 25 June 25Quote from: lightforce6128 on 05:13, 25 June 25With the code snippet from Axelay I also saw the flickering plants. But this is not caused by tile animation.
Hmm. I don't believe that is correct. I see a routine updating the characters in the tileset is triggered by a call from &45a to &1b03. I placed a RET at &1b03, the chars stopped updating, I then assembled the code & graphics I posted earlier to memory, and there is no longer a graphical problem with the plants or any other characters I had seen an issue with previously.
I'm sorry. :-[ It clearly is caused by tile animation. What I forgot was that the memory layout for your approach is different, so the animation routines animate the wrong tile parts - in this case one small part of the plants.
Deactivating the animation routines with RET is one first workaround. The next thing would be to adapt the animation routines. Currently two of them (spike, flame) make use of LDI, but this will not work with the new memory layout. So another approach is needed. As far as i remember the waterfall already uses something like LD A,(DE) : LD (HL),A , what is slower, but more flexible.
Quote from: OneVision on 09:19, 25 June 25usually I use a software like Tiled to edit 2D levels. I need 2 things : a tiles bank (the .tsx stands for tileset I guess) and of course the tile map (.tmx which is where goes what tile).
I had a look a the software Tiled. Looks quite comfortable. From game memory I could extract the tiles bank and the tile map.
I called the tile bank "tile map". It contains the tiles in a stretched resolution of 8x8 pixels, although mode 0 only gives 4x8 pixels per tile. There is also a one pixel wide grid separating the tiles from each other. As far as I've seen, Tiled should be able to work with something like this.
I called the tile map "level map". It contains tile indices encoded as grays (tile 0 is black, tile 1 is darkest gray, tile 128 is medium gray, tile 255 is white). For better orientation, I also created a colorized version.
While for some experiments it is okay to work with a debugger in game memory, for later use in the game it would be better to work with the level file instead. But it seems the level files are somehow decoded or compressed.
Quote from: OneVision on 09:19, 25 June 25I'm afraid I didn't understand your needs in terms of colors used in each tile.
For better explanation I created an example. I selected one of the not so complex tiles (a part of a plant). Then I showed the two fixed global color pairs / 2-pixel stamps and three tile-specific color pairs / 2-pixel stamps. With these I recreated the tile as good as possible.
The two global color pairs will be useless in most cases. Actually they are a pointer offset used in the algorithm to manipulate the screen address. But maybe they can be of use at least sometimes.
Three tile-specific color pairs is really not much. This will cause difficulties with most of the tiles. Currently I'm trying to squeeze out a fourth tile-specific color pair, what would be of great help for many symmetric tiles.
All in all this tile drawing algorithm is significantly faster than the original one (it saves 1.3 frames), but imposes severe restrictions on tile design. Is this a good compromise?
Well, as long as it preserves the original graphic design, I'd say "go on"! And thanks a lot for your efforts. Will it solve the animated sprites problem too?
Quote from: lightforce6128 on 23:58, 25 June 25ut it seems the level files are somehow decoded or compressed.
Yes, I forgot to mention Daren applied a form of compression on the level files, and I was unable to decode it. This might add a further complication. If you look at my Excel file, you'll see I add to compute a
Delta value (most often +B1h), which is the difference between an address in RAM, minus the address when the file is opened at 1B00h.
Three small optimizations:
- By reorganizing tiles in memory I could simplify a calculation.
- Loading a value twice from memory must not be worse than fiddling around with single bits.
- Using the command 'JP (IX)' also saves a bit of time and memory.
This opens up a tiny small bit of extra space (3 bytes) that can be used to supply each tile with a tile-specific 4th and 5th color pair. This greatly enhances the possibilities for designing the tiles.
NOLIST
;; Patch for Turrican128 v7. Run game, get rid of first enemies, then hold the game (key 'H').
;; Now assemble the patch over the running game. Continue game with key 'H'.
;; Changed code with unrolled tile drawing. This saves ~1.3 frames.
;; But the number of tiles is reduced from 256 to 64, what needs
;; a complete redesign of the level maps.
MACRO DRAW_NEXT_TILE
;; ;; NOPs ;; bytes ;;
LD A,(DE) : AND A,#07 : ADD A,C : LD H,A ;; 6 ;; 5 ;; Calculate high byte of tile address.
LD A,(DE) : AND A,#C0 : LD L,A ;; 5 ;; 4 ;; Calculate low byte of tile address.
INC DE ;; 2 ;; 1 ;; Go to next position in tile map.
JP (HL) ;; 1 ;; 1 ;; Execute tile.
;; ;; -- ;; -- ;;
;; ;; 14 ;; 11 ;;
ENDM
ORG #0DA9
;; Initialize registers.
;; ;; NOPs ;; bytes ;;
LD C,#60 ;; 2 ;; 2 ;; High byte of tile buffer in C.
LD DE,(#C7F1) ;; 6 ;; 4 ;; Start address of tile map in DE.
LD IX,return_to_row_loop ;; 4 ;; 4 ;; Adress to leave char loop.
EXX ;; 1 ;; 1 ;;
LD DE, 4 * #800 - 2 * -1 ;; 3 ;; 3 ;; Offset to next char in DE'.
XOR A,A : LD L,A ;; 2 ;; 2 ;; Set low byte of screen buffer to zero in L'. Also clear carry.
LD A,(#DE74) : RLA : RLA : XOR A,#40 : LD H,A ;; 9 ;; 8 ;; Convert CRTC value of visible screen to high byte of hidden screen buffer in H'.
EXX ;; 1 ;; 1 ;;
;; ;; -- ;; -- ;;
;; ;; 28 ;; 25 ;;
;; Loop through all char rows.
;; ;; NOPs ;; bytes ;;
row_loop: ;; ;; ;;
LD B,#20 ;; 2 ;; 2 ;; Number of chars in B.
DRAW_NEXT_TILE ;; - ;; 11 ;; Count NOPs in (last) tile, not here.
DEFS 127-25-12-11 ;; - ;; 79 ;; Fill unused bytes.
return_to_row_loop: ;; - ;; ;;
INC DE : INC DE : INC DE : INC DE ;; 8 ;; 4 ;; Skip 4 invisible tiles at the border.
EXX : BIT 2,H : EXX : JR Z,row_loop ;; 7-1 ;; 6 ;; If not 16 rows have been drawn, continue loop.
;; ;; ---- ;; --- ;;
;; ;; 17-1 ;; 102 ;; This is executed 16 times: (16*17)-1 = 271
ORG #6000
LET a_value = #00
LET bc1_value = #0000
LET bc2_value = #0000
REPEAT 32
;; INFO: There are not only 32 possible tiles, but 64. But the second half
;; is updated with animation. Currently this is not compatible with
;; the below unrolled code. So only the first 32 tiles can be used.
;; INFO: Besides the five color bytes defined per tile, the two fixed
;; bytes in DE can also be used. They contain these colors:
;;
;; Byte in D:
;; 06 #4C bright red
;; 26 #4B white
;; Byte in E:
;; 01 #44|#50 dark blue
;; 11 #57 sky blue (background, may also be black)
;; The following is the code to draw one tile. Besides unrolled drawing commands
;; it also contains some loop code. Each tile automatically fetches the next one.
;; ;; NOPs ;; bytes ;;
EXX ;; 1 ;; 1 ;;
LD A,a_value : LD BC,bc1_value ;; 5 ;; 5 ;; Set up three often used bytes.
LD (HL),A : INC L : LD (HL),C : SET 3,H ;; 7 ;; 5 ;; Draw 2x8 bytes. Forward in line 0.
LD (HL),B : DEC L : LD (HL),A : SET 4,H ;; 7 ;; 5 ;; Backward in line 1.
LD (HL),C : INC L : LD (HL),B : RES 3,H ;; 7 ;; 5 ;; Forward in line 3.
LD (HL),A : DEC L : LD (HL),C : SET 5,H ;; 7 ;; 5 ;; Backward in line 2.
LD BC,bc2_value ;; 3 ;; 3 ;; Optional: Update two often used byte.
LD (HL),B : INC L : LD (HL),A : SET 3,H ;; 7 ;; 5 ;; Forward in line 6.
LD (HL),C : DEC L : LD (HL),B : RES 4,H ;; 7 ;; 5 ;; Backward in line 7.
LD (HL),A : INC L : LD (HL),C : RES 3,H ;; 7 ;; 5 ;; Forward in line 5.
LD (HL),B : DEC L : LD (HL),A ;; 5 ;; 3 ;; Backward in line 4.
ADD HL,DE ;; 3 ;; 1 ;; Go to the next char.
EXX ;; 1 ;; 1 ;;
DJNZ $+4 : JP (IX) ;; 4+1 ;; 4 ;; Continue or leave the loop.
DRAW_NEXT_TILE ;; 14 ;; 11 ;; Draw the next tile.
;; DEFS 3 ;; - ;; - ;; If optional update is not used, pad with zeros.
;; ;; -- ;; -- ;;
;; ;; 85+1 ;; 64 ;;
;; Now the tiles need four times the space they needed before. But drawing is faster by 36.6%.
;; Drawing one tile with additional loop code takes 85 NOPs. All 32x16 = 512 tiles
;; will need 43'520 NOPs or ~2.2 frames.
LET a_value = a_value + 83 AND 255
LET bc1_value = bc1_value + 7901
LET bc2_value = bc2_value + 11483
REND
Nice find! Indeed ld a,(de) is only 2 µs after all.
Quote from: lightforce6128 on 00:12, 26 June 25Quote from: OneVision on 09:19, 25 June 25I'm afraid I didn't understand your needs in terms of colors used in each tile.
For better explanation I created an example. I selected one of the not so complex tiles (a part of a plant). Then I showed the two fixed global color pairs / 2-pixel stamps and three tile-specific color pairs / 2-pixel stamps. With these I recreated the tile as good as possible.
The two global color pairs will be useless in most cases. Actually they are a pointer offset used in the algorithm to manipulate the screen address. But maybe they can be of use at least sometimes.
Three tile-specific color pairs is really not much. This will cause difficulties with most of the tiles. Currently I'm trying to squeeze out a fourth tile-specific color pair, what would be of great help for many symmetric tiles.
All in all this tile drawing algorithm is significantly faster than the original one (it saves 1.3 frames), but imposes severe restrictions on tile design. Is this a good compromise?
Ok I see what you mean now.
It implies severe graphics modifications/degradations though and I'm not willing to do that on my free time, I dont feel compeled to it. Anyway thanks for the explanation and your time !
Quote from: Jean-Marie on 00:59, 26 June 25Quote from: lightforce6128 on 23:58, 25 June 25ut it seems the level files are somehow decoded or compressed.
Yes, I forgot to mention Daren applied a form of compression on the level files, and I was unable to decode it. This might add a further complication. If you look at my Excel file, you'll see I add to compute a Delta value (most often +B1h), which is the difference between an address in RAM, minus the address when the file is opened at 1B00h.
I had a look on what happens after the data has been loaded. There is a not too long routine that does the processing. I tried to understand it. There are still some strange things going on. But this routine can be used to decompress a level file loaded with BASIC. I also transferred the algorithm to a small C++ program on PC side. One file (level I) is shifted by one byte, the rest is decompressed consistently.
The delta value you mentioned could be to reserve some space between the compressed data and the uncompressed data to avoid that the latter one catches up with the first one.
If somebody is interested in this, here is the commented assembler routine. I tried to understand as good as possible what is going on there. But some small details are intractable.
NOLIST
;; The following algorithm (from Turrican128 v7) uses run-length encoding for decompression.
;; To lower the probability for collisions with normal data bytes, each run-length encoded
;; sequence is prefixed with two marker bytes instead of only one. Decompression is done
;; backwards so that compressed and decompressed data can share a common buffer (in-place).
;;
;; The algorithm is controlled by several variables stored at its end. They can be set at
;; once e.g. by copying their values from a list via LDIR.
ORG #B000
;; As first step the algorithm copies the compressed data from its original
;; location to an internal buffer. This might be of help if compressed
;; data should be uncompressed multiple times (the algorithm might overwrite
;; the internal buffer) or compressed and uncompressed data are stored
;; in different RAM banks.
LD BC,(compressed_data_length)
LD DE,(compressed_data_begin)
LD HL,(compressed_data_original_begin)
INC BC ;; Copy one more byte. Why?
LDIR
LD HL,(compressed_data_end) ;; Source pointer to compressed data, starting at its end.
LD IX,(decompressed_data_end) ;; Target pointer to uncompressed data, starting at its end.
LD BC,(compressed_data_begin) ;; Comparison value for source pointer in HL to leave routine.
LD DE,(escape_sequence) ;; Comparison value with escape sequence.
read_next_byte:
LD A,(HL)
CP E : JR Z,check_for_second_byte_of_escape_sequence ;; Check for first byte of escape sequence.
store_single_byte:
LD (IX+#00),A : DEC HL : DEC IX ;; If no escape byte found, store single byte.
;; ;; Source and target pointer are decremented.
;; ;; INFO: The source pointer could leave the compressed data block.
;; ;; This hints to that for the below comparison begin-1
;; ;; should be used.
check_for_completion:
PUSH AF
LD A,H : CP B : JR NZ,not_at_begin ;; Check high byte of source pointer.
LD A,L : CP C : JR Z,reached_begin_of_source ;; Check low byte of source pointer.
not_at_begin:
POP AF
JR read_next_byte ;; If begin of compressed data has not been reached, loop.
reached_begin_of_source:
POP AF
LD A,(last_decompressed_value) : OR A ;; Store last transferred byte (why?) and set flags accordingly.
NOP : NOP ;; Reserve some space to alter this part of the routine.
RET ;; Finally leave routine.
LD HL,(unused_jump_address_to_leave_routine) : JP (HL) ;; If the previous command is overwritten e.g. with NOP, instead jump to a given address.
check_for_second_byte_of_escape_sequence:
DEC HL : LD A,(HL)
CP D : JR Z,store_byte_multiple_times ;; Check for second byte of escape sequence.
INC HL : LD A,(HL) ;; If the escape sequence is incomplete, just store the first byte.
JR store_single_byte
store_byte_multiple_times:
PUSH BC
DEC HL : LD B,(HL) ;; Load counter.
DEC HL : LD A,(HL) ;; Load value to store multiple times.
store_value_multiple_times:
LD (IX+#00),A : DEC IX
DJNZ store_value_multiple_times
INC IX ;; Compensate last 'DEC IX'.
;; INFO: This looks a bit strange. Usually one would decrement HL to go
;; to the next value or to begin-1 to stop the loop. Instead,
;; incrementing IX and leaving HL at its position will have an
;; effect like reducing the length of the block by one, but
;; then adding/overwriting the missing value like a normal value.
;; This seems to be just unnecessarily complex.
POP BC
JR check_for_completion
;; Check assertion.
IF $ - #B059 : PRINT "Algorithm is too long." : STOP : ENDIF
;; Each run-length encoded sequence is prefixed with these two bytes.
;; The low byte is expected first, then the high byte.
escape_sequence:
DEFW #A5CC
;; Address of the original compressed data.
compressed_data_original_begin:
DEFS 2
;; Address of the compressed data to work with.
;; May be equal to 'compressed_data_original_begin'.
compressed_data_begin:
DEFS 2
;; Length of the compressed data in bytes.
compressed_data_length:
DEFS 2
;; This address is not used with the current routine. But altering it by
;; overwriting the RET command with a NOP will lead to using this address
;; instead.
unused_jump_address_to_leave_routine:
DEFS 2
;; Address of the last byte of the decompressed data. Because the algorithm
;; works backwards, it will start here with decompression.
decompressed_data_end:
DEFS 2
;; Address of the last byte of the compressed data. Because the algorithm
;; works backwards, it will start here with decompression.
compressed_data_end:
DEFS 2
;; This variable is an output of the program. It is not clear why this is stored
;; in memory, because the value is as well available in register A itself.
last_decompressed_value:
DEFS 1
;; in assembler:
;; - change origin (ORG) of assembler snippet to #8000
;; - remove first block with LDIR; no copying necessary
;; - replace second block with register initialization:
;; LD IX,#7FFF ;; target
;; LD HL,#68CA ;; source
;; LD DE,#A5CC ;; escape sequence #A5CC
;; LD BC,#2000 ;; source_end (or begin, because addresses are decremented)
;; - assemble updated snippet
;; in BASIC:
;; - MEMORY &1FFF : LOAD "TURRICAN.LVA",&2000
;; - CALL &8000
;; result:
;; - first byte of decomrpessed block: #3969
;; - levelmap at #3D68 with size 137x54
;; - tilemap at #6003
I had quite a similar case when I worked on Pinball Magic+, where the levels data were decompressed with a simple RLE algorithm after the loading was completed.
However, I didn't want to work out a RLE encoder to compress my newly created levels, so I simply compressed them with ZX0, and replaced the original RLE decoder with the ZX0 one.
As a bonus, I gained some free room on the DSK.
Is there a version please that works with the Dandanator Elite + please?
Quote from: spikemorrissey on 17:14, 30 June 25Is there a version please that works with the Dandanator Elite + please?
I have no idea what is a
Dandanator Elite+ and, at this point, I'm afraid to ask!
Can't you just drop the
Turrican Files in a folder on your SD card, just like with the Ulifac or M4 ?
Quote from: Jean-Marie on 00:05, 01 July 25Quote from: spikemorrissey on 17:14, 30 June 25Is there a version please that works with the Dandanator Elite + please?
I have no idea what is a Dandanator Elite+ and, at this point, I'm afraid to ask!
Can't you just drop the Turrican Files in a folder on your SD card, just like with the Ulifac or M4 ?
This is the link to it - do not be afraid! https://rancanuoteam.com/producto/cpc-elite/
I can send .dsk files up to 190k on to the rom generator however it won't add for example pinball dreams which is 419k in one version and in another two disks each of 209k.
Quote from: Jean-Marie on 00:05, 01 July 25Quote from: spikemorrissey on 17:14, 30 June 25Is there a version please that works with the Dandanator Elite + please?
I have no idea what is a Dandanator Elite+ and, at this point, I'm afraid to ask!
Can't you just drop the Turrican Files in a folder on your SD card, just like with the Ulifac or M4 ?
Dandanator Elite + . is a device that uses exclusive removable cartridges with a retro design paying homage to the 3-disc, each cartridge has a capacity of 512Kb in internal Flash memory.
Quote from: spikemorrissey on 02:58, 01 July 25This is the link to it - do not be afraid! https://rancanuoteam.com/producto/cpc-elite/ (https://rancanuoteam.com/producto/cpc-elite/)
I can send .dsk files up to 190k on to the rom generator however it won't add for example pinball dreams which is 419k in one version and in another two disks each of 209k.
You probably need to address this with the developers of the Dandanator hardware/software. They need to add support for large images or multi-load.
I managed to have the Waterfall working with Axelay's optimized routine.
The game feels definitely more snappy !
Now I need to tackle the exhausting flames, and then the spikes.
org &1b8c
ld de,(&6d8e)
ld hl,(&6c8e)
ld (&6d8e),hl
ld hl,(&6e8e)
ld (&6c8e),hl
ld hl,(&6f8e)
ld (&6e8e),hl
ld (&6f8e),de
ld l,&90
call @SlideTiles
ld l,&92
call @SlideTiles
ld l,&94
call @SlideTiles
;;code for spikes & flames
nop
ret
@SlideTiles:
ld h,&6D
ld a,(hl)
ld ixh,a ;;save last line of tile
inc l
ld a,(hl)
ld ixl,a ;;save last line of tile
ld h,&68
ld d,h:ld e,l
inc d
ldd ;;6891->6991
ld a,(hl):ld (de),a ;;6890->6990
inc h:ld d,&6B
ldi ;;6990->6B90
ldd ;;6991->6B91
inc h:ld d,&6F
ldi ;;6A90->6F90
ldd ;;6A91->6F91
inc h:ld d,&6A
ldi ;;6B90->6A90
ldd ;;6B91->6A91
inc h:ld d,&6D
ldi ;;6C90->6D90
ldd ;;6C91->6D91
inc h:inc h:dec d
ldi ;;6E90->6C90
ldd ;;6E91->6C91
inc h:inc d:inc d
ldi ;;6F90->6E90
ldd ;;6F91->6E91
ld h,&68
ld a,ixh ;;move last line to 1st line
ld (hl),a
inc l
ld a,ixl ;;move last line to 1st line
ld (hl),a
ret
org #0dbf ; 128k v7
exx
ld e,(ix+#00)
ld d,(ix+#01)
;ld bc,#0004
;add ix,bc
exx
.l0dc0
ld b,#20
.l0dcf
;xor a
;rld
;add d
;ex af,af'
ld a,(hl)
inc hl
exx
ld h,&60
add a,a
ld l,a
jr nc,skip
set 3,h
.skip
;ex af,af'
;ld h,a
;ld a,d
ldi
ld a,(hl)
ld (de),a
set 3,d
inc h
ldd
ld a,(hl)
ld (de),a
set 4,d
inc h
ldi
ld a,(hl)
ld (de),a
res 3,d
inc h
ldd
ld a,(hl)
ld (de),a
set 5,d
inc h
ldi
ld a,(hl)
ld (de),a
set 3,d
inc h
ldd
ld a,(hl)
ld (de),a
res 4,d
inc h
ldi
ld a,(hl)
ld (de),a
res 3,d
inc h
ldd
ld a,(hl)
ld (de),a
res 5,d
inc e
inc e
;jr nz,skip2
;inc d
;skip2
exx
djnz l0dcf
exx
ld a,e
or a
jr nz,skip2
inc d
.skip2
exx
ld a,4
add l
ld l,a
adc h
sub l
ld h,a
dec c
jp nz,l0dc0
jr &0E28
defs 13-4-2-3 ; up to &e2b, next instruction ld a,(c7f5)
org #6000
db #00,#00,#50,#08,#10,#8c,#e4,#70
db #b0,#00,#64,#34,#3c,#34,#f8,#3c
db #70,#7c,#10,#f0,#34,#64,#7c,#f0
db #38,#b4,#78,#b4,#78,#7c,#78,#f4
db #bc,#fc,#bc,#f0,#f0,#7c,#3c,#fc
db #fc,#fc,#3c,#7c,#bc,#fc,#fc,#fc
db #fc,#7c,#50,#4c,#50,#30,#f0,#98
db #88,#00,#00,#00,#3c,#a8,#3c,#f0
db #fc,#00,#fc,#a8,#fc,#a8,#3c,#bc
db #00,#30,#50,#b0,#f0,#3c,#70,#b4
db #b4,#7c,#78,#7c,#3c,#fc,#00,#00
db #00,#00,#fc,#a8,#fc,#fc,#50,#30
db #b0,#cc,#d8,#3c,#fc,#78,#bc,#fc
db #7c,#bc,#fc,#fc,#7c,#7c,#fc,#28
db #50,#d8,#3c,#b4,#b4,#70,#00,#00
db #00,#00,#fc,#fc,#bd,#fc,#00,#00
db #00,#00,#00,#00,#41,#41,#c3,#c3
db #3f,#3f,#00,#15,#3f,#3f,#2a,#00
db #3f,#3f,#3f,#3f,#3f,#3f,#6a,#d4
db #00,#cc,#b4,#00,#04,#58,#fc,#28
db #64,#d8,#fc,#a8,#14,#a8,#f0,#7c
db #00,#00,#c0,#7e,#00,#00,#fc,#fc
db #c0,#95,#c0,#95,#c0,#95,#7e,#6a
db #d4,#95,#7e,#3f,#7e,#7e,#7e,#6a
db #e8,#c0,#e8,#c0,#7e,#7e,#00,#15
db #3f,#3f,#3f,#d4,#fc,#e8,#d4,#fc
db #c0,#c0,#7e,#bd,#3f,#3f,#7e,#fc
db #fc,#fc,#a8,#00,#fc,#fc,#fc,#fc
db #fc,#fc,#40,#6a,#00,#40,#c0,#c0
db #c0,#c0,#c0,#c0,#c0,#d4,#00,#00
db #00,#00,#7e,#00,#e8,#7e,#3f,#7e
db #c0,#c0,#c0,#95,#6a,#7e,#00,#00
db #00,#00,#10,#18,#64,#4c,#30,#b4
db #6c,#58,#8c,#18,#34,#b4,#bc,#7c
db #b4,#7c,#64,#70,#b4,#98,#7c,#b4
db #e4,#18,#3c,#f4,#78,#7c,#3c,#7c
db #78,#b4,#78,#b4,#78,#7c,#3c,#fc
db #bc,#fc,#bc,#fc,#bc,#fc,#fc,#fc
db #fc,#fc,#e4,#70,#b0,#cc,#64,#f0
db #8c,#88,#88,#00,#7c,#a8,#3c,#78
db #fc,#a8,#fc,#a8,#fc,#a8,#7c,#bc
db #50,#30,#50,#64,#70,#b4,#d8,#3c
db #78,#3c,#f0,#fc,#7c,#fc,#00,#10
db #a8,#00,#fc,#a8,#fc,#fc,#b0,#64
db #b4,#64,#98,#3c,#bc,#f0,#78,#7c
db #7c,#bc,#fc,#7c,#3c,#fc,#fc,#28
db #e4,#d8,#78,#9c,#78,#d8,#00,#00
db #00,#00,#7e,#bd,#95,#e8,#00,#00
db #00,#00,#41,#41,#82,#82,#c3,#c3
db #3f,#7e,#00,#3f,#bd,#3f,#3f,#00
db #c0,#c0,#3f,#7e,#3f,#7e,#6a,#d4
db #00,#64,#f4,#00,#44,#d8,#7c,#28
db #64,#58,#7c,#a8,#14,#a8,#78,#7c
db #00,#15,#c0,#7e,#00,#00,#7e,#7e
db #95,#7e,#95,#7e,#3f,#7e,#7e,#6a
db #7e,#95,#7e,#3f,#7e,#7e,#7e,#6a
db #e8,#95,#e8,#95,#7e,#7e,#00,#15
db #3f,#c0,#3f,#c0,#c0,#c0,#95,#3f
db #c0,#c0,#3f,#3f,#3f,#3f,#3f,#bd
db #3f,#fc,#a8,#00,#7e,#fc,#a8,#54
db #fc,#bd,#40,#6a,#00,#40,#c0,#c0
db #c0,#c0,#c0,#95,#c0,#7e,#fc,#fc
db #3f,#3f,#c0,#a8,#40,#2a,#3f,#a8
db #c0,#c0,#7e,#3f,#15,#a8,#00,#00
db #00,#00,#64,#34,#70,#70,#b4,#fc
db #b0,#f0,#64,#70,#3c,#bc,#bc,#7c
db #b4,#bc,#64,#34,#38,#8c,#7c,#bc
db #64,#70,#3c,#f4,#3c,#7c,#fc,#bc
db #3c,#7c,#bc,#7c,#78,#7c,#bc,#fc
db #3c,#fc,#bc,#fc,#bc,#fc,#fc,#fc
db #fc,#7c,#d8,#cc,#8c,#4c,#64,#30
db #cc,#70,#98,#a0,#fc,#28,#3c,#bc
db #fc,#00,#fc,#a8,#fc,#a8,#7c,#a8
db #14,#b0,#00,#b0,#8c,#b4,#98,#b4
db #f0,#7c,#b4,#7c,#b4,#fc,#00,#64
db #fc,#00,#fc,#fc,#fc,#a8,#38,#24
db #14,#b0,#b0,#b4,#b4,#3c,#f0,#3c
db #7c,#3c,#3c,#7c,#fc,#a8,#bc,#28
db #cc,#b4,#98,#3c,#3c,#3c,#00,#cc
db #00,#3f,#c0,#c0,#3f,#6a,#fc,#00
db #00,#00,#82,#82,#82,#82,#c3,#c3
db #6a,#d4,#3f,#d4,#e8,#95,#e8,#3f
db #6a,#c0,#c0,#d4,#c0,#d4,#c0,#d4
db #00,#04,#28,#00,#00,#4c,#b4,#00
db #30,#cc,#bc,#a8,#54,#28,#b0,#fc
db #40,#95,#c0,#7e,#3f,#00,#b5,#b5
db #3f,#7e,#95,#7e,#3f,#a8,#7e,#6a
db #7e,#95,#7e,#3f,#7e,#7e,#7e,#6a
db #e8,#95,#e8,#3f,#7e,#7e,#00,#3f
db #6a,#95,#6a,#95,#6a,#c0,#95,#95
db #95,#6a,#d4,#3f,#bd,#3f,#3f,#fc
db #fc,#fc,#fc,#00,#00,#fc,#3f,#3f
db #fc,#00,#40,#6a,#00,#40,#3f,#3f
db #c0,#c0,#c0,#7e,#c0,#fc,#00,#fc
db #54,#95,#7e,#6a,#c0,#c0,#bd,#00
db #3f,#3f,#fc,#fc,#15,#a8,#3f,#3f
db #00,#00,#24,#d8,#b0,#98,#70,#7c
db #8c,#70,#24,#cc,#b4,#3c,#f8,#3c
db #b4,#bc,#8c,#98,#78,#64,#7c,#bc
db #24,#cc,#78,#3c,#f8,#7c,#bc,#fc
db #78,#3c,#78,#3c,#3c,#fc,#fc,#fc
db #78,#7c,#7c,#fc,#fc,#7c,#fc,#fc
db #fc,#7c,#30,#b0,#64,#4c,#d8,#cc
db #0c,#98,#4c,#00,#7c,#28,#3c,#bc
db #fc,#a8,#fc,#a8,#fc,#a8,#bc,#a8
db #50,#64,#14,#64,#18,#3c,#18,#b4
db #f0,#f4,#b4,#7c,#3c,#fc,#00,#30
db #fc,#00,#fc,#fc,#fc,#fc,#b0,#cc
db #50,#30,#64,#b4,#78,#3c,#78,#7c
db #fc,#bc,#7c,#7c,#7c,#fc,#fc,#28
db #64,#70,#e4,#b4,#6c,#b4,#00,#00
db #00,#3f,#3f,#3f,#c0,#c0,#fc,#00
db #41,#41,#00,#00,#41,#41,#c3,#c3
db #6a,#d4,#15,#7e,#e8,#95,#bd,#2a
db #95,#7e,#95,#7e,#6a,#7e,#6a,#d4
db #00,#04,#a0,#00,#10,#98,#b4,#a8
db #30,#98,#7c,#28,#54,#28,#b0,#7c
db #00,#95,#c0,#7e,#2a,#00,#d4,#d4
db #95,#7e,#95,#7e,#3f,#7e,#7e,#6a
db #7e,#95,#7e,#3f,#7e,#7e,#7e,#6a
db #e8,#95,#e8,#3f,#7e,#7e,#00,#3f
db #3f,#fc,#6a,#c0,#3f,#3f,#95,#95
db #c0,#c0,#bd,#3f,#3f,#3f,#3f,#fc
db #fc,#fc,#fc,#00,#15,#fc,#00,#00
db #fc,#2a,#40,#6a,#00,#40,#3f,#3f
db #c0,#c0,#c0,#d4,#3f,#fc,#00,#00
db #00,#fc,#3f,#d4,#6a,#3f,#7e,#00
db #c0,#c0,#3f,#3f,#6a,#7e,#00,#00
db #00,#00,#f0,#7c,#b0,#1c,#38,#b4
db #a4,#f0,#b0,#3c,#7c,#f8,#fc,#f8
db #b4,#bc,#b4,#38,#b4,#78,#7c,#fc
db #78,#3c,#fc,#fc,#bc,#7c,#78,#b4
db #bc,#fc,#bc,#7c,#bc,#bc,#fc,#fc
db #bc,#fc,#fc,#fc,#bc,#fc,#fc,#fc
db #f8,#7c,#14,#78,#f0,#f0,#b4,#f0
db #b4,#28,#b4,#28,#fc,#00,#fc,#fc
db #fc,#00,#fc,#a8,#a8,#00,#7c,#fc
db #00,#b0,#50,#64,#d8,#3c,#cc,#4c
db #f0,#fc,#f0,#7c,#b4,#7c,#50,#30
db #fc,#a8,#fc,#a8,#fc,#fc,#78,#30
db #38,#64,#50,#3c,#78,#3c,#3c,#3c
db #f8,#50,#14,#3c,#a8,#00,#bc,#7c
db #3c,#3c,#b4,#70,#6c,#b4,#10,#b4
db #3f,#3f,#c0,#c0,#f8,#7c,#fc,#fc
db #00,#00,#00,#00,#41,#41,#c3,#c3
db #00,#fc,#7e,#fc,#fc,#00,#fc,#bd
db #c0,#c0,#fc,#fc,#6a,#d4,#7e,#fc
db #00,#10,#a8,#00,#00,#98,#7c,#00
db #10,#98,#7c,#28,#00,#a8,#14,#bc
db #3f,#3f,#3f,#fc,#fc,#fc,#7e,#7e
db #00,#54,#95,#7e,#00,#00,#7e,#6a
db #7e,#95,#7e,#3f,#7e,#7e,#7e,#6a
db #e8,#95,#e8,#3f,#7e,#7e,#7e,#7e
db #fc,#6a,#c0,#fc,#c0,#6a,#6a,#d4
db #fc,#fc,#d4,#7e,#fc,#3f,#3f,#7e
db #fc,#fc,#fc,#fc,#00,#54,#7e,#bd
db #a8,#00,#95,#c0,#00,#95,#c0,#c0
db #c0,#c0,#d4,#fc,#c0,#fc,#00,#fc
db #fc,#fc,#7e,#fc,#e8,#a8,#bd,#00
db #fc,#fc,#6a,#7e,#6a,#7e,#00,#e8
db #00,#00,#3c,#bc,#38,#d8,#f8,#7c
db #70,#3c,#f0,#78,#7c,#78,#bc,#bc
db #f0,#7c,#3c,#a4,#78,#3c,#fc,#fc
db #b0,#3c,#bc,#7c,#bc,#7c,#78,#7c
db #bc,#fc,#bc,#fc,#bc,#78,#bc,#fc
db #bc,#7c,#bc,#fc,#fc,#fc,#fc,#fc
db #b4,#fc,#00,#b4,#3c,#b4,#3c,#78
db #b4,#28,#b4,#28,#fc,#00,#fc,#fc
db #fc,#00,#fc,#a8,#00,#00,#7c,#bc
db #00,#38,#00,#b0,#f0,#3c,#e4,#70
db #b4,#7c,#b4,#3c,#7c,#7c,#14,#30
db #7c,#a8,#fc,#a8,#fc,#a8,#50,#30
db #b0,#cc,#00,#14,#3c,#28,#00,#54
db #a0,#00,#00,#50,#00,#00,#bc,#fc
db #b4,#3c,#3c,#98,#14,#78,#50,#3c
db #3f,#3f,#c0,#c0,#3f,#6a,#fc,#fc
db #00,#00,#82,#82,#82,#82,#c3,#c3
db #00,#54,#fc,#fc,#a8,#00,#fc,#fc
db #fc,#fc,#fc,#fc,#6a,#d4,#fc,#fc
db #00,#10,#a8,#00,#00,#98,#f4,#00
db #00,#98,#bc,#28,#00,#a8,#14,#bc
db #3f,#3f,#3f,#fc,#fc,#fc,#fc,#fc
db #00,#00,#7e,#fc,#00,#00,#7e,#fc
db #fc,#fc,#fc,#fc,#fc,#7e,#fc,#6a
db #e8,#95,#e8,#3f,#7e,#fc,#3f,#3f
db #3f,#6a,#95,#fc,#c0,#e8,#e8,#c0
db #c0,#c0,#bd,#3f,#fc,#3f,#3f,#3f
db #fc,#fc,#fc,#fc,#00,#54,#7e,#bd
db #a8,#00,#95,#c0,#00,#95,#c0,#c0
db #c0,#c0,#7e,#fc,#3f,#fc,#00,#fc
db #fc,#fc,#3f,#3f,#e8,#a8,#2a,#00
db #fc,#fc,#fc,#fc,#15,#a8,#00,#54
db #00,#00,#b0,#3c,#78,#74,#e4,#70
db #3c,#9c,#64,#b4,#7c,#bc,#bc,#bc
db #7c,#fc,#f0,#3c,#3c,#b0,#7c,#fc
db #38,#b4,#bc,#7c,#bc,#fc,#bc,#f0
db #bc,#7c,#bc,#7c,#78,#fc,#fc,#fc
db #bc,#7c,#fc,#fc,#fc,#fc,#fc,#fc
db #78,#7c,#b0,#b4,#b0,#70,#f0,#b0
db #f0,#b4,#f0,#28,#fc,#00,#7c,#fc
db #fc,#a8,#fc,#00,#fc,#00,#fc,#bc
db #14,#64,#50,#64,#b0,#b4,#4c,#64
db #f0,#7c,#f0,#fc,#78,#3c,#50,#64
db #7c,#28,#fc,#fc,#fc,#fc,#b0,#64
db #b0,#64,#50,#b4,#f0,#f0,#78,#3c
db #fc,#b4,#78,#b4,#fc,#00,#f8,#7c
db #70,#3c,#34,#78,#98,#b4,#44,#70
db #15,#3f,#c0,#c0,#3f,#6a,#fc,#a8
db #82,#82,#41,#41,#82,#82,#c3,#c3
db #54,#7e,#6a,#d4,#bd,#a8,#e8,#95
db #95,#7e,#d4,#fc,#6a,#d4,#6a,#fc
db #00,#44,#28,#00,#00,#4c,#7c,#00
db #44,#4c,#7c,#28,#00,#a8,#b4,#bc
db #c0,#95,#c0,#7e,#3f,#7e,#d4,#d4
db #00,#7e,#95,#7e,#a8,#00,#7e,#6a
db #7e,#95,#7e,#3f,#7e,#7e,#7e,#6a
db #e8,#95,#e8,#3f,#7e,#7e,#15,#3f
db #c0,#3f,#c0,#c0,#c0,#c0,#c0,#3f
db #3f,#3f,#6a,#3f,#95,#3f,#3f,#7e
db #fc,#fc,#fc,#a8,#00,#54,#fc,#fc
db #a8,#00,#95,#c0,#00,#95,#c0,#c0
db #c0,#c0,#95,#fc,#c0,#fc,#00,#fc
db #54,#95,#3f,#fc,#e8,#a8,#fc,#00
db #fc,#fc,#fc,#fc,#15,#a8,#54,#c0
db #00,#00,#b0,#b4,#b4,#b4,#3c,#58
db #78,#b4,#b0,#f0,#3c,#bc,#bc,#fc
db #b4,#fc,#b0,#b4,#78,#64,#7c,#fc
db #b0,#b4,#7c,#7c,#f8,#7c,#7c,#b4
db #bc,#7c,#fc,#fc,#bc,#fc,#bc,#fc
db #fc,#7c,#bc,#fc,#bc,#fc,#fc,#fc
db #bc,#7c,#70,#30,#64,#98,#b0,#f0
db #30,#f0,#30,#a0,#7c,#28,#7c,#fc
db #fc,#a8,#fc,#00,#fc,#00,#fc,#bc
db #50,#30,#00,#b0,#64,#b4,#24,#f0
db #b4,#3c,#f0,#7c,#f0,#7c,#10,#64
db #7c,#28,#fc,#fc,#fc,#a8,#b4,#64
db #b4,#64,#b0,#b4,#f0,#b4,#f0,#3c
db #fc,#3c,#f0,#7c,#7c,#a8,#bc,#b4
db #98,#b4,#d8,#3c,#78,#3c,#00,#98
db #15,#3f,#c0,#c0,#f8,#7c,#fc,#a8
db #00,#00,#00,#00,#41,#41,#c3,#c3
db #bd,#d4,#6a,#d4,#e8,#7e,#e8,#95
db #6a,#c0,#c0,#d4,#c0,#d4,#c0,#d4
db #00,#04,#28,#00,#00,#18,#f4,#00
db #44,#18,#fc,#28,#00,#a8,#78,#fc
db #c0,#95,#c0,#7e,#3f,#2a,#3d,#3d
db #15,#7e,#95,#7e,#7e,#00,#7e,#6a
db #7e,#95,#7e,#3f,#7e,#7e,#7e,#6a
db #e8,#95,#e8,#3f,#7e,#7e,#15,#3f
db #7e,#bd,#3f,#3f,#c0,#6a,#3f,#6a
db #c0,#c0,#6a,#3f,#bd,#3f,#3f,#7e
db #3f,#7e,#fc,#a8,#00,#7e,#fc,#fc
db #bd,#00,#95,#c0,#00,#95,#c0,#c0
db #c0,#c0,#c0,#fc,#c0,#fc,#00,#fc
db #00,#95,#7e,#bd,#fc,#fc,#bd,#00
db #fc,#fc,#6a,#7e,#6a,#7e,#e8,#c0
db #00,#00,#54,#a8,#d4,#d4,#c0,#c0
db #00,#c0,#6a,#d4,#3f,#6a,#40,#2a
db #40,#2a,#40,#7e,#6a,#95,#7e,#2a
db #00,#15,#c0,#7e,#00,#00,#95,#bd
db #c0,#c0,#00,#54,#a8,#00,#00,#00
db #00,#00,#00,#00,#07,#c3,#c3,#c3
db #6a,#fc,#fc,#fc,#c3,#c3,#fc,#3f
db #3f,#3f,#3f,#3f,#3f,#bd,#bd,#3f
db #3f,#3f,#3f,#3f,#3f,#3f,#fc,#3f
db #3f,#3f,#fc,#fc,#54,#fc,#fc,#fc
db #fc,#fc,#fc,#fc,#fc,#a8,#11,#00
db #73,#a2,#00,#00,#45,#f3,#11,#a2
db #00,#b6,#00,#00,#00,#a2,#45,#28
db #11,#11,#00,#22,#db,#a2,#00,#22
db #11,#28,#00,#b6,#11,#f3,#00,#51
db #8a,#a2,#00,#73,#73,#00,#00,#73
db #b3,#00,#3f,#3f,#3f,#3f,#3f,#3f
db #c0,#d4,#95,#7e,#c0,#d4,#00,#00
db #4a,#0f,#c0,#c0,#0f,#0f,#64,#48
db #84,#98,#50,#64,#98,#a0,#4c,#0c
db #8c,#0c,#1d,#6a,#c0,#3e,#41,#41
db #64,#48,#84,#98,#50,#64,#98,#a0
db #c0,#80,#00,#80,#c0,#80,#40,#80
db #40,#00,#40,#00,#6a,#a8,#00,#00
db #94,#20,#a4,#a8,#04,#08,#44,#88
db #10,#20,#b0,#48,#84,#70,#00,#50
db #a0,#00,#c0,#c0,#c0,#95,#d4,#fc
db #fc,#fc,#aa,#00,#55,#ff,#10,#a0
db #58,#00,#18,#00,#80,#00,#00,#60
db #00,#64,#b0,#48,#05,#05,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#aa,#00,#00,#ff
db #00,#00,#6a,#7e,#d4,#7e,#c0,#c0
db #15,#c0,#6a,#d4,#6a,#d4,#40,#2a
db #6a,#7e,#40,#7e,#6a,#95,#7e,#2a
db #00,#3f,#c0,#7e,#a8,#00,#95,#bd
db #c0,#c0,#00,#54,#a8,#00,#00,#00
db #00,#00,#00,#00,#ad,#0f,#0f,#0f
db #bd,#3f,#3f,#3f,#0f,#0f,#fc,#bd
db #fc,#bd,#fc,#fc,#fc,#7e,#7e,#fc
db #fc,#fc,#3f,#bd,#3f,#fc,#fc,#bd
db #fc,#fc,#fc,#fc,#00,#fc,#ac,#fc
db #5c,#fc,#fc,#5c,#fc,#00,#51,#00
db #51,#b6,#00,#00,#11,#b6,#45,#00
db #45,#3c,#00,#00,#00,#28,#11,#a2
db #45,#14,#00,#22,#36,#a2,#00,#28
db #51,#a2,#00,#36,#51,#b6,#00,#51
db #33,#28,#00,#36,#73,#00,#00,#f3
db #8a,#00,#3f,#3f,#3f,#3f,#3f,#7e
db #d4,#fc,#6a,#d4,#c0,#7e,#00,#00
db #c0,#c0,#85,#0f,#c0,#85,#e4,#48
db #84,#d8,#50,#64,#98,#a0,#8c,#cc
db #4c,#1c,#1d,#95,#3f,#3e,#82,#82
db #e4,#48,#84,#d8,#50,#64,#98,#a0
db #0c,#08,#04,#08,#0c,#08,#0c,#08
db #40,#00,#6a,#a8,#6a,#a8,#00,#00
db #94,#20,#a4,#a8,#04,#08,#44,#88
db #10,#20,#b0,#c8,#c4,#70,#00,#50
db #a0,#00,#c0,#c0,#c0,#95,#d4,#3c
db #3c,#7c,#ff,#aa,#aa,#55,#44,#20
db #80,#00,#c4,#70,#c4,#00,#00,#04
db #b0,#8c,#30,#48,#0a,#00,#00,#0a
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#aa,#aa,#55,#55
db #3f,#3f,#3f,#3f,#95,#3f,#c0,#c0
db #15,#c0,#6a,#d4,#6a,#d4,#40,#2a
db #6a,#7e,#40,#7e,#6a,#95,#7e,#2a
db #3f,#3f,#c0,#7e,#fc,#a8,#95,#bd
db #b0,#b4,#00,#a8,#54,#00,#00,#00
db #00,#00,#00,#00,#fc,#fc,#fc,#fc
db #fc,#fc,#fc,#fc,#fc,#fc,#54,#fc
db #fc,#fc,#3f,#3f,#fc,#7e,#3f,#7e
db #fc,#fc,#bd,#fc,#7e,#fc,#fc,#fc
db #fc,#fc,#fc,#a8,#00,#54,#fc,#5c
db #fc,#5c,#fc,#5c,#a8,#00,#22,#00
db #11,#f3,#00,#00,#51,#b6,#51,#28
db #11,#28,#22,#00,#11,#a2,#9e,#00
db #51,#73,#00,#22,#51,#28,#45,#a2
db #11,#a2,#00,#3c,#73,#28,#00,#9e
db #b6,#00,#b3,#f3,#73,#00,#67,#b6
db #f3,#b6,#fc,#fc,#c0,#c0,#c0,#d4
db #40,#d4,#6a,#d4,#fc,#a8,#00,#00
db #0f,#85,#4a,#4a,#4a,#4a,#b0,#48
db #84,#70,#14,#b0,#70,#28,#9d,#95
db #6a,#3e,#9d,#6a,#6a,#7e,#82,#82
db #b0,#48,#84,#70,#14,#b0,#70,#28
db #10,#00,#20,#20,#10,#20,#30,#20
db #40,#00,#6a,#a8,#3f,#a8,#00,#00
db #b4,#a8,#b4,#a8,#48,#84,#8c,#4c
db #64,#98,#50,#24,#18,#a0,#00,#00
db #00,#00,#d4,#fc,#fc,#7e,#d4,#30
db #30,#74,#55,#00,#00,#aa,#24,#70
db #70,#00,#08,#00,#4c,#00,#00,#40
db #00,#04,#00,#04,#00,#05,#40,#85
db #00,#0a,#00,#80,#40,#80,#05,#80
db #05,#00,#0f,#0f,#55,#aa,#aa,#aa
db #00,#00,#54,#a8,#d4,#7e,#c0,#c0
db #15,#c0,#6a,#d4,#6a,#d4,#40,#2a
db #6a,#7e,#40,#7e,#6a,#95,#7e,#2a
db #15,#3f,#c0,#7e,#fc,#00,#95,#bd
db #3f,#3f,#00,#2a,#15,#00,#00,#00
db #00,#00,#00,#00,#a9,#03,#03,#03
db #fc,#fc,#fc,#fc,#03,#03,#54,#fc
db #3f,#fc,#fc,#fc,#fc,#fc,#fc,#3f
db #3f,#3f,#bd,#fc,#3f,#3f,#bd,#bd
db #3f,#3f,#fc,#a8,#00,#54,#fc,#fc
db #fc,#fc,#5c,#fc,#a8,#00,#8a,#00
db #45,#3c,#00,#00,#45,#b6,#51,#28
db #11,#b6,#00,#00,#45,#a2,#51,#a2
db #11,#14,#00,#8a,#f3,#a2,#00,#22
db #45,#28,#00,#b6,#36,#a2,#00,#36
db #73,#28,#8a,#b6,#9e,#00,#11,#b6
db #22,#f3,#6a,#95,#fc,#e8,#95,#7e
db #3f,#3f,#6a,#d4,#3f,#7e,#00,#00
db #85,#c0,#0f,#c0,#0f,#c0,#b0,#48
db #84,#70,#50,#30,#30,#a0,#1d,#6a
db #95,#d4,#48,#3f,#95,#d4,#41,#41
db #b0,#48,#84,#70,#50,#30,#30,#a0
db #00,#88,#44,#88,#88,#00,#cc,#00
db #40,#00,#6a,#a8,#6a,#a8,#00,#00
db #b4,#a8,#b4,#a8,#48,#84,#8c,#4c
db #64,#98,#b0,#8c,#4c,#70,#00,#00
db #00,#00,#d4,#fc,#fc,#7e,#d4,#f0
db #f0,#f4,#55,#00,#55,#aa,#c8,#d8
db #18,#00,#c4,#a0,#18,#a0,#00,#e4
db #10,#c8,#44,#48,#40,#00,#40,#00
db #00,#80,#00,#00,#00,#00,#40,#00
db #40,#00,#c0,#c0,#55,#00,#55,#aa
db #d4,#00,#c0,#c0,#c0,#c0,#c0,#c0
db #6a,#c0,#6a,#d4,#6a,#d4,#40,#2a
db #6a,#7e,#40,#7e,#6a,#95,#7e,#2a
db #3f,#3f,#c0,#7e,#fc,#a8,#95,#bd
db #fc,#fc,#2a,#00,#00,#15,#00,#00
db #00,#00,#00,#00,#a9,#03,#03,#03
db #fc,#fc,#fc,#fc,#03,#03,#00,#54
db #7e,#3f,#3f,#fc,#fc,#fc,#bd,#3f
db #fc,#fc,#fc,#bd,#bd,#7e,#fc,#7e
db #fc,#fc,#a8,#00,#00,#fc,#fc,#5c
db #fc,#fc,#fc,#5c,#fc,#00,#14,#00
db #51,#f3,#b6,#00,#db,#79,#f3,#14
db #51,#28,#79,#00,#00,#73,#f3,#00
db #14,#a2,#00,#28,#11,#b6,#00,#28
db #73,#28,#45,#b6,#b6,#28,#00,#b6
db #db,#3c,#11,#b6,#73,#00,#79,#79
db #73,#3c,#40,#fc,#fc,#95,#3f,#d4
db #c0,#d4,#fc,#fc,#fc,#fc,#85,#0f
db #4a,#c0,#4a,#4a,#4a,#4a,#50,#24
db #18,#a0,#00,#50,#a0,#00,#48,#6a
db #95,#7e,#9c,#bc,#3c,#3c,#41,#41
db #50,#24,#18,#a0,#00,#50,#a0,#00
db #54,#00,#00,#a8,#54,#00,#54,#00
db #40,#00,#6a,#a8,#b0,#28,#00,#00
db #14,#00,#14,#00,#04,#08,#44,#88
db #10,#20,#00,#b0,#70,#00,#00,#00
db #00,#00,#d4,#0f,#0f,#5e,#7e,#fc
db #fc,#fc,#aa,#55,#aa,#aa,#50,#28
db #08,#00,#0c,#00,#18,#00,#00,#04
db #10,#8c,#b0,#8c,#40,#05,#00,#0a
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#55,#55,#ff,#00
db #a8,#00,#c0,#c0,#fc,#e8,#fc,#fc
db #fc,#fc,#6a,#d4,#6a,#d4,#40,#2a
db #40,#2a,#54,#fc,#fc,#fc,#fc,#a8
db #fc,#fc,#fc,#fc,#fc,#a8,#fc,#fc
db #fc,#fc,#a8,#00,#00,#54,#00,#00
db #00,#00,#00,#00,#fc,#fc,#fc,#fc
db #fc,#fc,#fc,#fc,#fc,#fc,#00,#54
db #fc,#fc,#3f,#fc,#fc,#fc,#fc,#fc
db #7e,#fc,#bd,#3f,#fc,#fc,#bd,#3f
db #fc,#fc,#a8,#00,#54,#fc,#fc,#fc
db #fc,#5c,#fc,#fc,#fc,#a8,#51,#22
db #14,#f3,#a2,#00,#73,#79,#b6,#51
db #51,#a2,#f3,#22,#00,#f3,#28,#00
db #00,#a2,#00,#a2,#51,#b6,#00,#a2
db #b6,#a2,#45,#b6,#f3,#39,#11,#b6
db #b6,#00,#51,#79,#f3,#22,#51,#3c
db #f3,#28,#6a,#3f,#d4,#d4,#fc,#d4
db #fc,#fc,#a8,#54,#a8,#00,#85,#0f
db #0f,#c0,#4a,#c0,#0f,#85,#50,#64
db #98,#a0,#00,#14,#28,#00,#9d,#95
db #3f,#3e,#3c,#7c,#7c,#7c,#82,#82
db #50,#64,#98,#a0,#00,#14,#28,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #40,#00,#6a,#a8,#fc,#a8,#00,#00
db #14,#00,#14,#00,#04,#08,#44,#88
db #10,#20,#00,#b0,#70,#00,#00,#00
db #00,#00,#d4,#c0,#c0,#d4,#7e,#fc
db #fc,#fc,#00,#55,#aa,#00,#14,#28
db #98,#a0,#4c,#20,#d8,#20,#50,#64
db #00,#04,#00,#24,#0a,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#55,#aa,#aa
db #c0,#a8,#c0,#c0,#c0,#c0,#c0,#c0
db #6a,#c0,#6a,#d4,#6a,#d4,#40,#2a
db #6a,#7e,#40,#7e,#6a,#95,#7e,#2a
db #3f,#3f,#c0,#7e,#fc,#a8,#95,#bd
db #3f,#3f,#54,#00,#00,#a8,#00,#00
db #00,#00,#00,#00,#ad,#0f,#0f,#0f
db #bd,#3f,#3f,#3f,#0f,#0f,#00,#fc
db #fc,#fc,#7e,#fc,#3f,#3f,#bd,#bd
db #fc,#fc,#7e,#bd,#fc,#3f,#fc,#fc
db #3f,#7e,#fc,#00,#00,#54,#5c,#fc
db #fc,#fc,#5c,#fc,#a8,#00,#79,#00
db #51,#79,#9e,#00,#db,#f3,#73,#79
db #11,#a2,#b3,#00,#51,#b6,#b6,#00
db #51,#28,#00,#a2,#45,#f3,#00,#a2
db #db,#28,#11,#b6,#36,#a2,#00,#f3
db #11,#b6,#51,#3c,#b6,#00,#b3,#79
db #db,#14,#15,#3f,#3f,#e8,#c0,#a8
db #6a,#c0,#7e,#bd,#6a,#7e,#4a,#c0
db #85,#4a,#4a,#85,#0f,#c0,#b0,#8c
db #4c,#70,#00,#78,#b4,#00,#1d,#3f
db #6a,#d4,#1d,#6a,#95,#d4,#82,#82
db #b0,#8c,#4c,#70,#00,#78,#b4,#00
db #3c,#28,#3c,#28,#3c,#28,#3c,#28
db #40,#00,#6a,#a8,#b0,#28,#00,#00
db #14,#00,#14,#00,#48,#84,#8c,#4c
db #64,#98,#50,#64,#98,#a0,#00,#00
db #00,#00,#d4,#c3,#c3,#d6,#95,#0c
db #0c,#5c,#aa,#aa,#ff,#00,#b0,#b4
db #90,#00,#c4,#70,#c4,#70,#00,#e4
db #b0,#c8,#00,#60,#00,#00,#40,#00
db #05,#00,#05,#00,#00,#00,#00,#00
db #00,#0a,#00,#00,#00,#aa,#55,#aa
db #c0,#d4,#c0,#c0,#c0,#c0,#c0,#c0
db #6a,#c0,#6a,#d4,#6a,#d4,#40,#2a
db #6a,#7e,#40,#7e,#6a,#95,#7e,#2a
db #3f,#3f,#c0,#7e,#fc,#a8,#95,#bd
db #b0,#b4,#15,#00,#00,#2a,#00,#00
db #00,#00,#00,#00,#07,#c3,#c3,#c3
db #6a,#fc,#fc,#fc,#c3,#c3,#00,#fc
db #bd,#7e,#7e,#bd,#7e,#fc,#bd,#bd
db #3f,#3f,#3f,#bd,#7e,#fc,#7e,#3f
db #7e,#fc,#fc,#00,#00,#54,#fc,#fc
db #5c,#fc,#fc,#fc,#a8,#00,#f3,#00
db #11,#79,#22,#00,#51,#79,#db,#b6
db #45,#a2,#8a,#00,#51,#3c,#73,#00
db #11,#b6,#00,#a2,#51,#3c,#11,#22
db #51,#a2,#00,#b6,#f3,#28,#00,#73
db #00,#22,#67,#b6,#36,#00,#e7,#b6
db #73,#f3,#40,#c0,#95,#3f,#6a,#a8
db #54,#fc,#6a,#d4,#c0,#d4,#4a,#85
db #0f,#0f,#85,#85,#c0,#85,#b0,#c8
db #c4,#70,#00,#b0,#70,#00,#c8,#3f
db #3f,#94,#1d,#95,#3f,#3e,#41,#41
db #b0,#c8,#c4,#70,#00,#b0,#70,#00
db #a0,#a0,#f0,#a0,#a0,#a0,#a0,#a0
db #40,#00,#6a,#a8,#fc,#a8,#00,#00
db #b4,#a8,#b4,#a8,#48,#84,#8c,#4c
db #64,#98,#50,#64,#98,#a0,#00,#00
db #00,#00,#d4,#03,#03,#56,#95,#cc
db #cc,#dc,#55,#ff,#55,#00,#64,#34
db #88,#00,#90,#00,#c0,#98,#00,#60
db #50,#60,#10,#c8,#0a,#80,#0a,#80
db #40,#00,#40,#0a,#05,#0a,#00,#0a
db #00,#80,#00,#00,#aa,#ff,#55,#55
I've slightly redrawn a few tiles from the first stripe (too much white).
The effect looks closer to the original now.
org #0dbf ; 128k v7
exx
ld e,(ix+#00)
ld d,(ix+#01)
;ld bc,#0004
;add ix,bc
exx
.l0dc0
ld b,#20
.l0dcf
;xor a
;rld
;add d
;ex af,af'
ld a,(hl)
inc hl
exx
ld h,&60
add a,a
ld l,a
jr nc,skip
set 3,h
.skip
;ex af,af'
;ld h,a
;ld a,d
ldi
ld a,(hl)
ld (de),a
set 3,d
inc h
ldd
ld a,(hl)
ld (de),a
set 4,d
inc h
ldi
ld a,(hl)
ld (de),a
res 3,d
inc h
ldd
ld a,(hl)
ld (de),a
set 5,d
inc h
ldi
ld a,(hl)
ld (de),a
set 3,d
inc h
ldd
ld a,(hl)
ld (de),a
res 4,d
inc h
ldi
ld a,(hl)
ld (de),a
res 3,d
inc h
ldd
ld a,(hl)
ld (de),a
res 5,d
inc e
inc e
;jr nz,skip2
;inc d
;skip2
exx
djnz l0dcf
exx
ld a,e
or a
jr nz,skip2
inc d
.skip2
exx
ld a,4
add l
ld l,a
adc h
sub l
ld h,a
dec c
jp nz,l0dc0
jr &0E28
defs 13-4-2-3 ; up to &e2b, next instruction ld a,(c7f5)
org #6000
db #00,#00,#50,#08,#10,#8c,#e4,#70
db #b0,#00,#64,#34,#3c,#34,#f8,#3c
db #70,#7c,#10,#f0,#34,#64,#7c,#f0
db #38,#b4,#78,#b4,#78,#7c,#78,#f4
db #bc,#fc,#bc,#f0,#f0,#7c,#3c,#fc
db #fc,#fc,#3c,#7c,#bc,#fc,#fc,#fc
db #fc,#7c,#50,#4c,#50,#30,#f0,#98
db #88,#00,#00,#00,#3c,#a8,#3c,#f0
db #fc,#00,#fc,#a8,#fc,#a8,#3c,#bc
db #00,#30,#50,#b0,#f0,#3c,#70,#b4
db #b4,#7c,#78,#7c,#3c,#fc,#00,#00
db #00,#00,#fc,#a8,#fc,#fc,#50,#30
db #b0,#cc,#d8,#3c,#fc,#78,#bc,#fc
db #7c,#bc,#fc,#fc,#7c,#7c,#fc,#28
db #50,#d8,#3c,#b4,#b4,#70,#00,#00
db #00,#00,#fc,#fc,#bd,#fc,#00,#00
db #00,#00,#00,#00,#41,#41,#c3,#c3
db #3f,#3f,#00,#15,#3f,#3f,#2a,#00
db #3f,#3f,#3f,#3f,#3f,#3f,#6a,#d4
db #00,#cc,#b4,#00,#04,#58,#fc,#28
db #64,#d8,#fc,#a8,#14,#a8,#f0,#7c
db #00,#00,#c0,#7e,#00,#00,#fc,#fc
db #c0,#95,#c0,#95,#c0,#95,#7e,#6a
db #d4,#95,#7e,#3f,#7e,#7e,#7e,#6a
db #e8,#c0,#e8,#c0,#7e,#7e,#00,#15
db #3f,#3f,#3f,#d4,#fc,#e8,#d4,#fc
db #c0,#c0,#7e,#bd,#3f,#3f,#7e,#fc
db #fc,#fc,#a8,#00,#fc,#fc,#fc,#fc
db #fc,#fc,#40,#6a,#00,#40,#c0,#c0
db #c0,#c0,#c0,#c0,#c0,#d4,#00,#00
db #00,#00,#7e,#00,#e8,#7e,#3f,#7e
db #c0,#c0,#c0,#95,#6a,#7e,#00,#00
db #00,#00,#10,#18,#64,#4c,#30,#b4
db #6c,#58,#8c,#18,#34,#b4,#bc,#7c
db #b4,#7c,#64,#70,#b4,#98,#7c,#b4
db #e4,#18,#3c,#f4,#78,#7c,#3c,#7c
db #78,#b4,#78,#b4,#78,#7c,#3c,#fc
db #bc,#fc,#bc,#fc,#bc,#fc,#fc,#fc
db #fc,#fc,#e4,#70,#b0,#cc,#64,#f0
db #8c,#88,#88,#00,#7c,#a8,#3c,#78
db #fc,#a8,#fc,#a8,#fc,#a8,#7c,#bc
db #50,#30,#50,#64,#70,#b4,#d8,#3c
db #78,#3c,#f0,#fc,#7c,#fc,#00,#10
db #a8,#00,#fc,#a8,#fc,#fc,#b0,#64
db #b4,#64,#98,#3c,#bc,#f0,#78,#7c
db #7c,#bc,#fc,#7c,#3c,#fc,#fc,#28
db #e4,#d8,#78,#9c,#78,#d8,#00,#00
db #00,#00,#7e,#bd,#95,#e8,#00,#00
db #00,#00,#41,#41,#82,#82,#c3,#c3
db #3f,#7e,#00,#3f,#bd,#3f,#3f,#00
db #c0,#c0,#3f,#7e,#3f,#7e,#6a,#d4
db #00,#64,#f4,#00,#44,#d8,#7c,#28
db #64,#58,#7c,#a8,#14,#a8,#78,#7c
db #00,#15,#c0,#7e,#00,#00,#7e,#7e
db #95,#7e,#95,#7e,#3f,#7e,#7e,#6a
db #7e,#95,#7e,#3f,#7e,#7e,#7e,#6a
db #e8,#95,#e8,#95,#7e,#7e,#00,#15
db #3f,#c0,#3f,#c0,#c0,#c0,#95,#3f
db #c0,#c0,#3f,#3f,#3f,#3f,#3f,#bd
db #3f,#fc,#a8,#00,#7e,#fc,#a8,#54
db #fc,#bd,#40,#6a,#00,#40,#c0,#c0
db #c0,#c0,#c0,#95,#c0,#7e,#fc,#fc
db #3f,#3f,#c0,#a8,#40,#2a,#3f,#a8
db #c0,#c0,#7e,#3f,#15,#a8,#00,#00
db #00,#00,#64,#34,#70,#70,#b4,#fc
db #b0,#f0,#64,#70,#3c,#bc,#bc,#7c
db #b4,#bc,#64,#34,#38,#8c,#7c,#bc
db #64,#70,#3c,#f4,#3c,#7c,#fc,#bc
db #3c,#7c,#bc,#7c,#78,#7c,#bc,#fc
db #3c,#fc,#bc,#fc,#bc,#fc,#fc,#fc
db #fc,#7c,#d8,#cc,#8c,#4c,#64,#30
db #cc,#70,#98,#a0,#fc,#28,#3c,#bc
db #fc,#00,#fc,#a8,#fc,#a8,#7c,#a8
db #14,#b0,#00,#b0,#8c,#b4,#98,#b4
db #f0,#7c,#b4,#7c,#b4,#fc,#00,#64
db #fc,#00,#fc,#fc,#fc,#a8,#38,#24
db #14,#b0,#b0,#b4,#b4,#3c,#f0,#3c
db #7c,#3c,#3c,#7c,#fc,#a8,#bc,#28
db #cc,#b4,#98,#3c,#3c,#3c,#00,#cc
db #00,#3f,#c0,#c0,#3f,#6a,#fc,#00
db #00,#00,#82,#82,#82,#82,#c3,#c3
db #6a,#d4,#3f,#d4,#e8,#95,#e8,#3f
db #6a,#c0,#c0,#d4,#c0,#d4,#c0,#d4
db #00,#04,#28,#00,#00,#4c,#b4,#00
db #30,#cc,#bc,#a8,#54,#28,#b0,#fc
db #40,#95,#c0,#7e,#3f,#00,#b5,#b5
db #3f,#7e,#95,#7e,#3f,#a8,#7e,#6a
db #7e,#95,#7e,#3f,#7e,#7e,#7e,#6a
db #e8,#95,#e8,#3f,#7e,#7e,#00,#3f
db #6a,#95,#6a,#95,#6a,#c0,#95,#95
db #95,#6a,#d4,#3f,#bd,#3f,#3f,#fc
db #fc,#fc,#fc,#00,#00,#fc,#3f,#3f
db #fc,#00,#40,#6a,#00,#40,#3f,#3f
db #c0,#c0,#c0,#7e,#c0,#fc,#00,#fc
db #54,#95,#7e,#6a,#c0,#c0,#bd,#00
db #3f,#3f,#fc,#fc,#15,#a8,#3f,#3f
db #00,#00,#24,#d8,#b0,#98,#70,#7c
db #8c,#70,#24,#cc,#b4,#3c,#f8,#3c
db #b4,#bc,#8c,#98,#78,#64,#7c,#bc
db #24,#cc,#78,#3c,#f8,#7c,#bc,#fc
db #78,#3c,#78,#3c,#3c,#fc,#fc,#fc
db #78,#7c,#7c,#fc,#fc,#7c,#fc,#fc
db #fc,#7c,#30,#b0,#64,#4c,#d8,#cc
db #0c,#98,#4c,#00,#7c,#28,#3c,#bc
db #fc,#a8,#fc,#a8,#fc,#a8,#bc,#a8
db #50,#64,#14,#64,#18,#3c,#18,#b4
db #f0,#f4,#b4,#7c,#3c,#fc,#00,#30
db #fc,#00,#fc,#fc,#fc,#fc,#b0,#cc
db #50,#30,#64,#b4,#78,#3c,#78,#7c
db #fc,#bc,#7c,#7c,#7c,#fc,#fc,#28
db #64,#70,#e4,#b4,#6c,#b4,#00,#00
db #00,#3f,#3f,#3f,#c0,#c0,#fc,#00
db #41,#41,#00,#00,#41,#41,#c3,#c3
db #6a,#d4,#15,#7e,#e8,#95,#bd,#2a
db #95,#7e,#95,#7e,#6a,#7e,#6a,#d4
db #00,#04,#a0,#00,#10,#98,#b4,#a8
db #30,#98,#7c,#28,#54,#28,#b0,#7c
db #00,#95,#c0,#7e,#2a,#00,#d4,#d4
db #95,#7e,#95,#7e,#3f,#7e,#7e,#6a
db #7e,#95,#7e,#3f,#7e,#7e,#7e,#6a
db #e8,#95,#e8,#3f,#7e,#7e,#00,#3f
db #3f,#fc,#6a,#c0,#3f,#3f,#95,#95
db #c0,#c0,#bd,#3f,#3f,#3f,#3f,#fc
db #fc,#fc,#fc,#00,#15,#fc,#00,#00
db #fc,#2a,#40,#6a,#00,#40,#3f,#3f
db #c0,#c0,#c0,#d4,#3f,#fc,#00,#00
db #00,#fc,#3f,#d4,#6a,#3f,#7e,#00
db #c0,#c0,#3f,#3f,#6a,#7e,#00,#00
db #00,#00,#f0,#7c,#b0,#1c,#38,#b4
db #a4,#f0,#b0,#3c,#7c,#f8,#fc,#f8
db #b4,#bc,#b4,#38,#b4,#78,#7c,#fc
db #78,#3c,#fc,#fc,#bc,#7c,#78,#b4
db #bc,#fc,#bc,#7c,#bc,#bc,#fc,#fc
db #bc,#fc,#fc,#fc,#bc,#fc,#fc,#fc
db #f8,#7c,#14,#78,#f0,#f0,#b4,#f0
db #b4,#28,#b4,#28,#fc,#00,#fc,#fc
db #fc,#00,#fc,#a8,#a8,#00,#7c,#fc
db #00,#b0,#50,#64,#d8,#3c,#cc,#4c
db #f0,#fc,#f0,#7c,#b4,#7c,#50,#30
db #fc,#a8,#fc,#a8,#fc,#fc,#78,#30
db #38,#64,#50,#3c,#78,#3c,#3c,#3c
db #f8,#50,#14,#3c,#a8,#00,#bc,#7c
db #3c,#3c,#b4,#70,#6c,#b4,#10,#b4
db #3f,#3f,#c0,#c0,#f8,#7c,#fc,#fc
db #00,#00,#00,#00,#41,#41,#c3,#c3
db #00,#fc,#7e,#fc,#fc,#00,#fc,#bd
db #c0,#c0,#fc,#fc,#6a,#d4,#7e,#fc
db #00,#10,#a8,#00,#00,#98,#7c,#00
db #10,#98,#7c,#28,#00,#a8,#14,#bc
db #3f,#3f,#3f,#fc,#fc,#fc,#7e,#7e
db #00,#54,#95,#7e,#00,#00,#7e,#6a
db #7e,#95,#7e,#3f,#7e,#7e,#7e,#6a
db #e8,#95,#e8,#3f,#7e,#7e,#7e,#7e
db #fc,#6a,#c0,#fc,#c0,#6a,#6a,#d4
db #fc,#fc,#d4,#7e,#fc,#3f,#3f,#7e
db #fc,#fc,#fc,#fc,#00,#54,#7e,#bd
db #a8,#00,#95,#c0,#00,#95,#c0,#c0
db #c0,#c0,#d4,#fc,#c0,#fc,#00,#fc
db #fc,#fc,#7e,#fc,#e8,#a8,#bd,#00
db #fc,#fc,#6a,#7e,#6a,#7e,#00,#e8
db #00,#00,#3c,#bc,#38,#d8,#f8,#7c
db #70,#3c,#f0,#78,#7c,#78,#bc,#bc
db #f0,#7c,#3c,#a4,#78,#3c,#fc,#fc
db #b0,#3c,#bc,#7c,#bc,#7c,#78,#7c
db #bc,#fc,#bc,#fc,#bc,#78,#bc,#fc
db #bc,#7c,#bc,#fc,#fc,#fc,#fc,#fc
db #b4,#fc,#00,#b4,#3c,#b4,#3c,#78
db #b4,#28,#b4,#28,#fc,#00,#fc,#fc
db #fc,#00,#fc,#a8,#00,#00,#7c,#bc
db #00,#38,#00,#b0,#f0,#3c,#e4,#70
db #b4,#7c,#b4,#3c,#7c,#7c,#14,#30
db #7c,#a8,#fc,#a8,#fc,#a8,#50,#30
db #b0,#cc,#00,#14,#3c,#28,#00,#54
db #a0,#00,#00,#50,#00,#00,#bc,#fc
db #b4,#3c,#3c,#98,#14,#78,#50,#3c
db #3f,#3f,#c0,#c0,#3f,#6a,#fc,#fc
db #00,#00,#82,#82,#82,#82,#c3,#c3
db #00,#54,#fc,#fc,#a8,#00,#fc,#fc
db #fc,#fc,#fc,#fc,#6a,#d4,#fc,#fc
db #00,#10,#a8,#00,#00,#98,#f4,#00
db #00,#98,#bc,#28,#00,#a8,#14,#bc
db #3f,#3f,#3f,#fc,#fc,#fc,#fc,#fc
db #00,#00,#7e,#fc,#00,#00,#7e,#fc
db #fc,#fc,#fc,#fc,#fc,#7e,#fc,#6a
db #e8,#95,#e8,#3f,#7e,#fc,#3f,#3f
db #3f,#6a,#95,#fc,#c0,#e8,#e8,#c0
db #c0,#c0,#bd,#3f,#fc,#3f,#3f,#3f
db #fc,#fc,#fc,#fc,#00,#54,#7e,#bd
db #a8,#00,#95,#c0,#00,#95,#c0,#c0
db #c0,#c0,#7e,#fc,#3f,#fc,#00,#fc
db #fc,#fc,#3f,#3f,#e8,#a8,#2a,#00
db #fc,#fc,#fc,#fc,#15,#a8,#00,#54
db #00,#00,#b0,#3c,#78,#74,#e4,#70
db #3c,#9c,#64,#b4,#7c,#bc,#bc,#bc
db #7c,#fc,#f0,#3c,#3c,#b0,#7c,#fc
db #38,#b4,#bc,#7c,#bc,#fc,#bc,#f0
db #bc,#7c,#bc,#7c,#78,#fc,#fc,#fc
db #bc,#7c,#fc,#fc,#fc,#fc,#fc,#fc
db #78,#7c,#b0,#b4,#b0,#70,#f0,#b0
db #f0,#b4,#f0,#28,#fc,#00,#7c,#fc
db #fc,#a8,#fc,#00,#fc,#00,#fc,#bc
db #14,#64,#50,#64,#b0,#b4,#4c,#64
db #f0,#7c,#f0,#fc,#78,#3c,#50,#64
db #7c,#28,#fc,#fc,#fc,#fc,#b0,#64
db #b0,#64,#50,#b4,#f0,#f0,#78,#3c
db #fc,#b4,#78,#b4,#fc,#00,#f8,#7c
db #70,#3c,#34,#78,#98,#b4,#44,#70
db #15,#3f,#c0,#c0,#3f,#6a,#fc,#a8
db #82,#82,#41,#41,#82,#82,#c3,#c3
db #54,#7e,#6a,#d4,#bd,#a8,#e8,#95
db #95,#7e,#d4,#fc,#6a,#d4,#6a,#fc
db #00,#44,#28,#00,#00,#4c,#7c,#00
db #44,#4c,#7c,#28,#00,#a8,#b4,#bc
db #c0,#95,#c0,#7e,#3f,#7e,#d4,#d4
db #00,#7e,#95,#7e,#a8,#00,#7e,#6a
db #7e,#95,#7e,#3f,#7e,#7e,#7e,#6a
db #e8,#95,#e8,#3f,#7e,#7e,#15,#3f
db #c0,#3f,#c0,#c0,#c0,#c0,#c0,#3f
db #3f,#3f,#6a,#3f,#95,#3f,#3f,#7e
db #fc,#fc,#fc,#a8,#00,#54,#fc,#fc
db #a8,#00,#95,#c0,#00,#95,#c0,#c0
db #c0,#c0,#95,#fc,#c0,#fc,#00,#fc
db #54,#95,#3f,#fc,#e8,#a8,#fc,#00
db #fc,#fc,#fc,#fc,#15,#a8,#54,#c0
db #00,#00,#b0,#b4,#b4,#b4,#3c,#58
db #78,#b4,#b0,#f0,#3c,#bc,#bc,#fc
db #b4,#fc,#b0,#b4,#78,#64,#7c,#fc
db #b0,#b4,#7c,#7c,#f8,#7c,#7c,#b4
db #bc,#7c,#fc,#fc,#bc,#fc,#bc,#fc
db #fc,#7c,#bc,#fc,#bc,#fc,#fc,#fc
db #bc,#7c,#70,#30,#64,#98,#b0,#f0
db #30,#f0,#30,#a0,#7c,#28,#7c,#fc
db #fc,#a8,#fc,#00,#fc,#00,#fc,#bc
db #50,#30,#00,#b0,#64,#b4,#24,#f0
db #b4,#3c,#f0,#7c,#f0,#7c,#10,#64
db #7c,#28,#fc,#fc,#fc,#a8,#b4,#64
db #b4,#64,#b0,#b4,#f0,#b4,#f0,#3c
db #fc,#3c,#f0,#7c,#7c,#a8,#bc,#b4
db #98,#b4,#d8,#3c,#78,#3c,#00,#98
db #15,#3f,#c0,#c0,#f8,#7c,#fc,#a8
db #00,#00,#00,#00,#41,#41,#c3,#c3
db #bd,#d4,#6a,#d4,#e8,#7e,#e8,#95
db #6a,#c0,#c0,#d4,#c0,#d4,#c0,#d4
db #00,#04,#28,#00,#00,#18,#f4,#00
db #44,#18,#fc,#28,#00,#a8,#78,#fc
db #c0,#95,#c0,#7e,#3f,#2a,#3d,#3d
db #15,#7e,#95,#7e,#7e,#00,#7e,#6a
db #7e,#95,#7e,#3f,#7e,#7e,#7e,#6a
db #e8,#95,#e8,#3f,#7e,#7e,#15,#3f
db #7e,#bd,#3f,#3f,#c0,#6a,#3f,#6a
db #c0,#c0,#6a,#3f,#bd,#3f,#3f,#7e
db #3f,#7e,#fc,#a8,#00,#7e,#fc,#fc
db #bd,#00,#95,#c0,#00,#95,#c0,#c0
db #c0,#c0,#c0,#fc,#c0,#fc,#00,#fc
db #00,#95,#7e,#bd,#fc,#fc,#bd,#00
db #fc,#fc,#6a,#7e,#6a,#7e,#e8,#c0
db #00,#00,#54,#a8,#d4,#d4,#c0,#c0
db #00,#c0,#6a,#d4,#3f,#6a,#40,#2a
db #40,#2a,#40,#7e,#6a,#95,#7e,#2a
db #00,#15,#c0,#7e,#00,#00,#95,#bd
db #c0,#c0,#00,#54,#a8,#00,#00,#00
db #00,#00,#00,#00,#07,#c3,#c3,#c3
db #6a,#fc,#fc,#fc,#c3,#c3,#fc,#3f
db #3f,#3f,#3f,#3f,#3f,#bd,#bd,#3f
db #3f,#3f,#3f,#3f,#3f,#3f,#fc,#3f
db #3f,#3f,#fc,#fc,#54,#fc,#fc,#fc
db #fc,#fc,#fc,#fc,#fc,#a8,#11,#00
db #73,#a2,#00,#00,#45,#f3,#11,#a2
db #00,#b6,#00,#00,#00,#a2,#45,#28
db #11,#11,#00,#22,#db,#a2,#00,#22
db #11,#28,#00,#b6,#11,#f3,#00,#51
db #8a,#a2,#00,#73,#73,#00,#00,#73
db #b3,#00,#3f,#3f,#3f,#3f,#3f,#3f
db #c0,#d4,#95,#7e,#c0,#d4,#00,#00
db #4a,#0f,#85,#85,#4a,#0f,#64,#48
db #84,#98,#50,#64,#98,#a0,#4c,#0c
db #8c,#0c,#1d,#6a,#c0,#3e,#41,#41
db #64,#48,#84,#98,#50,#64,#98,#a0
db #c0,#80,#00,#80,#c0,#80,#40,#80
db #40,#00,#40,#00,#6a,#a8,#00,#00
db #94,#20,#a4,#a8,#04,#08,#44,#88
db #10,#20,#b0,#48,#84,#70,#00,#50
db #a0,#00,#c0,#c0,#c0,#95,#d4,#fc
db #fc,#fc,#aa,#00,#55,#ff,#10,#a0
db #58,#00,#18,#00,#80,#00,#00,#60
db #00,#64,#b0,#48,#05,#05,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#aa,#00,#00,#ff
db #00,#00,#6a,#7e,#d4,#7e,#c0,#c0
db #15,#c0,#6a,#d4,#6a,#d4,#40,#2a
db #6a,#7e,#40,#7e,#6a,#95,#7e,#2a
db #00,#3f,#c0,#7e,#a8,#00,#95,#bd
db #c0,#c0,#00,#54,#a8,#00,#00,#00
db #00,#00,#00,#00,#ad,#0f,#0f,#0f
db #bd,#3f,#3f,#3f,#0f,#0f,#fc,#bd
db #fc,#bd,#fc,#fc,#fc,#7e,#7e,#fc
db #fc,#fc,#3f,#bd,#3f,#fc,#fc,#bd
db #fc,#fc,#fc,#fc,#00,#fc,#ac,#fc
db #5c,#fc,#fc,#5c,#fc,#00,#51,#00
db #51,#b6,#00,#00,#11,#b6,#45,#00
db #45,#3c,#00,#00,#00,#28,#11,#a2
db #45,#14,#00,#22,#36,#a2,#00,#28
db #51,#a2,#00,#36,#51,#b6,#00,#51
db #33,#28,#00,#36,#73,#00,#00,#f3
db #8a,#00,#3f,#3f,#3f,#3f,#3f,#7e
db #d4,#fc,#6a,#d4,#c0,#7e,#00,#00
db #c0,#4a,#0f,#0f,#4a,#85,#e4,#48
db #84,#d8,#50,#64,#98,#a0,#8c,#cc
db #4c,#1c,#1d,#95,#3f,#3e,#82,#82
db #e4,#48,#84,#d8,#50,#64,#98,#a0
db #0c,#08,#04,#08,#0c,#08,#0c,#08
db #40,#00,#6a,#a8,#6a,#a8,#00,#00
db #94,#20,#a4,#a8,#04,#08,#44,#88
db #10,#20,#b0,#c8,#c4,#70,#00,#50
db #a0,#00,#c0,#c0,#c0,#95,#d4,#3c
db #3c,#7c,#ff,#aa,#aa,#55,#44,#20
db #80,#00,#c4,#70,#c4,#00,#00,#04
db #b0,#8c,#30,#48,#0a,#00,#00,#0a
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#aa,#aa,#55,#55
db #3f,#3f,#3f,#3f,#95,#3f,#c0,#c0
db #15,#c0,#6a,#d4,#6a,#d4,#40,#2a
db #6a,#7e,#40,#7e,#6a,#95,#7e,#2a
db #3f,#3f,#c0,#7e,#fc,#a8,#95,#bd
db #b0,#b4,#00,#a8,#54,#00,#00,#00
db #00,#00,#00,#00,#fc,#fc,#fc,#fc
db #fc,#fc,#fc,#fc,#fc,#fc,#54,#fc
db #fc,#fc,#3f,#3f,#fc,#7e,#3f,#7e
db #fc,#fc,#bd,#fc,#7e,#fc,#fc,#fc
db #fc,#fc,#fc,#a8,#00,#54,#fc,#5c
db #fc,#5c,#fc,#5c,#a8,#00,#22,#00
db #11,#f3,#00,#00,#51,#b6,#51,#28
db #11,#28,#22,#00,#11,#a2,#9e,#00
db #51,#73,#00,#22,#51,#28,#45,#a2
db #11,#a2,#00,#3c,#73,#28,#00,#9e
db #b6,#00,#b3,#f3,#73,#00,#67,#b6
db #f3,#b6,#fc,#fc,#c0,#c0,#c0,#d4
db #40,#d4,#6a,#d4,#fc,#a8,#00,#00
db #0f,#85,#4a,#4a,#85,#0f,#b0,#48
db #84,#70,#14,#b0,#70,#28,#9d,#95
db #6a,#3e,#9d,#6a,#6a,#7e,#82,#82
db #b0,#48,#84,#70,#14,#b0,#70,#28
db #10,#00,#20,#20,#10,#20,#30,#20
db #40,#00,#6a,#a8,#3f,#a8,#00,#00
db #b4,#a8,#b4,#a8,#48,#84,#8c,#4c
db #64,#98,#50,#24,#18,#a0,#00,#00
db #00,#00,#d4,#fc,#fc,#7e,#d4,#30
db #30,#74,#55,#00,#00,#aa,#24,#70
db #70,#00,#08,#00,#4c,#00,#00,#40
db #00,#04,#00,#04,#00,#05,#40,#85
db #00,#0a,#00,#80,#40,#80,#05,#80
db #05,#00,#0f,#0f,#55,#aa,#aa,#aa
db #00,#00,#54,#a8,#d4,#7e,#c0,#c0
db #15,#c0,#6a,#d4,#6a,#d4,#40,#2a
db #6a,#7e,#40,#7e,#6a,#95,#7e,#2a
db #15,#3f,#c0,#7e,#fc,#00,#95,#bd
db #3f,#3f,#00,#2a,#15,#00,#00,#00
db #00,#00,#00,#00,#a9,#03,#03,#03
db #fc,#fc,#fc,#fc,#03,#03,#54,#fc
db #3f,#fc,#fc,#fc,#fc,#fc,#fc,#3f
db #3f,#3f,#bd,#fc,#3f,#3f,#bd,#bd
db #3f,#3f,#fc,#a8,#00,#54,#fc,#fc
db #fc,#fc,#5c,#fc,#a8,#00,#8a,#00
db #45,#3c,#00,#00,#45,#b6,#51,#28
db #11,#b6,#00,#00,#45,#a2,#51,#a2
db #11,#14,#00,#8a,#f3,#a2,#00,#22
db #45,#28,#00,#b6,#36,#a2,#00,#36
db #73,#28,#8a,#b6,#9e,#00,#11,#b6
db #22,#f3,#6a,#95,#fc,#e8,#95,#7e
db #3f,#3f,#6a,#d4,#3f,#7e,#00,#00
db #85,#0f,#85,#85,#0f,#85,#b0,#48
db #84,#70,#50,#30,#30,#a0,#1d,#6a
db #95,#d4,#48,#3f,#95,#d4,#41,#41
db #b0,#48,#84,#70,#50,#30,#30,#a0
db #00,#88,#44,#88,#88,#00,#cc,#00
db #40,#00,#6a,#a8,#6a,#a8,#00,#00
db #b4,#a8,#b4,#a8,#48,#84,#8c,#4c
db #64,#98,#b0,#8c,#4c,#70,#00,#00
db #00,#00,#d4,#fc,#fc,#7e,#d4,#f0
db #f0,#f4,#55,#00,#55,#aa,#c8,#d8
db #18,#00,#c4,#a0,#18,#a0,#00,#e4
db #10,#c8,#44,#48,#40,#00,#40,#00
db #00,#80,#00,#00,#00,#00,#40,#00
db #40,#00,#c0,#c0,#55,#00,#55,#aa
db #d4,#00,#c0,#c0,#c0,#c0,#c0,#c0
db #6a,#c0,#6a,#d4,#6a,#d4,#40,#2a
db #6a,#7e,#40,#7e,#6a,#95,#7e,#2a
db #3f,#3f,#c0,#7e,#fc,#a8,#95,#bd
db #fc,#fc,#2a,#00,#00,#15,#00,#00
db #00,#00,#00,#00,#a9,#03,#03,#03
db #fc,#fc,#fc,#fc,#03,#03,#00,#54
db #7e,#3f,#3f,#fc,#fc,#fc,#bd,#3f
db #fc,#fc,#fc,#bd,#bd,#7e,#fc,#7e
db #fc,#fc,#a8,#00,#00,#fc,#fc,#5c
db #fc,#fc,#fc,#5c,#fc,#00,#14,#00
db #51,#f3,#b6,#00,#db,#79,#f3,#14
db #51,#28,#79,#00,#00,#73,#f3,#00
db #14,#a2,#00,#28,#11,#b6,#00,#28
db #73,#28,#45,#b6,#b6,#28,#00,#b6
db #db,#3c,#11,#b6,#73,#00,#79,#79
db #73,#3c,#40,#fc,#fc,#95,#3f,#d4
db #c0,#d4,#fc,#fc,#fc,#fc,#85,#0f
db #4a,#c0,#4a,#4a,#4a,#4a,#50,#24
db #18,#a0,#00,#50,#a0,#00,#48,#6a
db #95,#7e,#9c,#bc,#3c,#3c,#41,#41
db #50,#24,#18,#a0,#00,#50,#a0,#00
db #54,#00,#00,#a8,#54,#00,#54,#00
db #40,#00,#6a,#a8,#b0,#28,#00,#00
db #14,#00,#14,#00,#04,#08,#44,#88
db #10,#20,#00,#b0,#70,#00,#00,#00
db #00,#00,#d4,#0f,#0f,#5e,#7e,#fc
db #fc,#fc,#aa,#55,#aa,#aa,#50,#28
db #08,#00,#0c,#00,#18,#00,#00,#04
db #10,#8c,#b0,#8c,#40,#05,#00,#0a
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#55,#55,#ff,#00
db #a8,#00,#c0,#c0,#fc,#e8,#fc,#fc
db #fc,#fc,#6a,#d4,#6a,#d4,#40,#2a
db #40,#2a,#54,#fc,#fc,#fc,#fc,#a8
db #fc,#fc,#fc,#fc,#fc,#a8,#fc,#fc
db #fc,#fc,#a8,#00,#00,#54,#00,#00
db #00,#00,#00,#00,#fc,#fc,#fc,#fc
db #fc,#fc,#fc,#fc,#fc,#fc,#00,#54
db #fc,#fc,#3f,#fc,#fc,#fc,#fc,#fc
db #7e,#fc,#bd,#3f,#fc,#fc,#bd,#3f
db #fc,#fc,#a8,#00,#54,#fc,#fc,#fc
db #fc,#5c,#fc,#fc,#fc,#a8,#51,#22
db #14,#f3,#a2,#00,#73,#79,#b6,#51
db #51,#a2,#f3,#22,#00,#f3,#28,#00
db #00,#a2,#00,#a2,#51,#b6,#00,#a2
db #b6,#a2,#45,#b6,#f3,#39,#11,#b6
db #b6,#00,#51,#79,#f3,#22,#51,#3c
db #f3,#28,#6a,#3f,#d4,#d4,#fc,#d4
db #fc,#fc,#a8,#54,#a8,#00,#85,#0f
db #0f,#c0,#4a,#c0,#0f,#85,#50,#64
db #98,#a0,#00,#14,#28,#00,#9d,#95
db #3f,#3e,#3c,#7c,#7c,#7c,#82,#82
db #50,#64,#98,#a0,#00,#14,#28,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #40,#00,#6a,#a8,#fc,#a8,#00,#00
db #14,#00,#14,#00,#04,#08,#44,#88
db #10,#20,#00,#b0,#70,#00,#00,#00
db #00,#00,#d4,#c0,#c0,#d4,#7e,#fc
db #fc,#fc,#00,#55,#aa,#00,#14,#28
db #98,#a0,#4c,#20,#d8,#20,#50,#64
db #00,#04,#00,#24,#0a,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#55,#aa,#aa
db #c0,#a8,#c0,#c0,#c0,#c0,#c0,#c0
db #6a,#c0,#6a,#d4,#6a,#d4,#40,#2a
db #6a,#7e,#40,#7e,#6a,#95,#7e,#2a
db #3f,#3f,#c0,#7e,#fc,#a8,#95,#bd
db #3f,#3f,#54,#00,#00,#a8,#00,#00
db #00,#00,#00,#00,#ad,#0f,#0f,#0f
db #bd,#3f,#3f,#3f,#0f,#0f,#00,#fc
db #fc,#fc,#7e,#fc,#3f,#3f,#bd,#bd
db #fc,#fc,#7e,#bd,#fc,#3f,#fc,#fc
db #3f,#7e,#fc,#00,#00,#54,#5c,#fc
db #fc,#fc,#5c,#fc,#a8,#00,#79,#00
db #51,#79,#9e,#00,#db,#f3,#73,#79
db #11,#a2,#b3,#00,#51,#b6,#b6,#00
db #51,#28,#00,#a2,#45,#f3,#00,#a2
db #db,#28,#11,#b6,#36,#a2,#00,#f3
db #11,#b6,#51,#3c,#b6,#00,#b3,#79
db #db,#14,#15,#3f,#3f,#e8,#c0,#a8
db #6a,#c0,#7e,#bd,#6a,#7e,#4a,#c0
db #85,#4a,#4a,#85,#0f,#c0,#b0,#8c
db #4c,#70,#00,#78,#b4,#00,#1d,#3f
db #6a,#d4,#1d,#6a,#95,#d4,#82,#82
db #b0,#8c,#4c,#70,#00,#78,#b4,#00
db #3c,#28,#3c,#28,#3c,#28,#3c,#28
db #40,#00,#6a,#a8,#b0,#28,#00,#00
db #14,#00,#14,#00,#48,#84,#8c,#4c
db #64,#98,#50,#64,#98,#a0,#00,#00
db #00,#00,#d4,#c3,#c3,#d6,#95,#0c
db #0c,#5c,#aa,#aa,#ff,#00,#b0,#b4
db #90,#00,#c4,#70,#c4,#70,#00,#e4
db #b0,#c8,#00,#60,#00,#00,#40,#00
db #05,#00,#05,#00,#00,#00,#00,#00
db #00,#0a,#00,#00,#00,#aa,#55,#aa
db #c0,#d4,#c0,#c0,#c0,#c0,#c0,#c0
db #6a,#c0,#6a,#d4,#6a,#d4,#40,#2a
db #6a,#7e,#40,#7e,#6a,#95,#7e,#2a
db #3f,#3f,#c0,#7e,#fc,#a8,#95,#bd
db #b0,#b4,#15,#00,#00,#2a,#00,#00
db #00,#00,#00,#00,#07,#c3,#c3,#c3
db #6a,#fc,#fc,#fc,#c3,#c3,#00,#fc
db #bd,#7e,#7e,#bd,#7e,#fc,#bd,#bd
db #3f,#3f,#3f,#bd,#7e,#fc,#7e,#3f
db #7e,#fc,#fc,#00,#00,#54,#fc,#fc
db #5c,#fc,#fc,#fc,#a8,#00,#f3,#00
db #11,#79,#22,#00,#51,#79,#db,#b6
db #45,#a2,#8a,#00,#51,#3c,#73,#00
db #11,#b6,#00,#a2,#51,#3c,#11,#22
db #51,#a2,#00,#b6,#f3,#28,#00,#73
db #00,#22,#67,#b6,#36,#00,#e7,#b6
db #73,#f3,#40,#c0,#95,#3f,#6a,#a8
db #54,#fc,#6a,#d4,#c0,#d4,#4a,#85
db #0f,#0f,#85,#85,#c0,#85,#b0,#c8
db #c4,#70,#00,#b0,#70,#00,#c8,#3f
db #3f,#94,#1d,#95,#3f,#3e,#41,#41
db #b0,#c8,#c4,#70,#00,#b0,#70,#00
db #a0,#a0,#f0,#a0,#a0,#a0,#a0,#a0
db #40,#00,#6a,#a8,#fc,#a8,#00,#00
db #b4,#a8,#b4,#a8,#48,#84,#8c,#4c
db #64,#98,#50,#64,#98,#a0,#00,#00
db #00,#00,#d4,#03,#03,#56,#95,#cc
db #cc,#dc,#55,#ff,#55,#00,#64,#34
db #88,#00,#90,#00,#c0,#98,#00,#60
db #50,#60,#10,#c8,#0a,#80,#0a,#80
db #40,#00,#40,#0a,#05,#0a,#00,#0a
db #00,#80,#00,#00,#aa,#ff,#55,#55
org &1b8c
ld de,(&6d8e)
ld hl,(&6c8e)
ld (&6d8e),hl
ld hl,(&6e8e)
ld (&6c8e),hl
ld hl,(&6f8e)
ld (&6e8e),hl
ld (&6f8e),de
ld l,&90
call @SlideTiles
ld l,&92
call @SlideTiles
ld l,&94
call @SlideTiles
;;code for spikes & flames
nop
ret
@SlideTiles:
ld h,&6D
ld a,(hl)
ld ixh,a ;;save last line of tile
inc l
ld a,(hl)
ld ixl,a ;;save last line of tile
ld h,&68
ld d,h:ld e,l
inc d
ldd ;;6891->6991
ld a,(hl):ld (de),a ;;6890->6990
inc h:ld d,&6B
ldi ;;6990->6B90
ldd ;;6991->6B91
inc h:ld d,&6F
ldi ;;6A90->6F90
ldd ;;6A91->6F91
inc h:ld d,&6A
ldi ;;6B90->6A90
ldd ;;6B91->6A91
inc h:ld d,&6D
ldi ;;6C90->6D90
ldd ;;6C91->6D91
inc h:inc h:dec d
ldi ;;6E90->6C90
ldd ;;6E91->6C91
inc h:inc d:inc d
ldi ;;6F90->6E90
ldd ;;6F91->6E91
ld h,&68
ld a,ixh ;;move last line to 1st line
ld (hl),a
inc l
ld a,ixl ;;move last line to 1st line
ld (hl),a
ret
That looks very promising and more fluid. :)
The animation for the exhausting flames is working too.
This is brilliant work (again)!!
This's crazy awesome!
BTW, is there any chance to apply
@MacDeath 's palette (https://www.cpcwiki.eu/forum/index.php?msg=251802)? I especially liked the new terrain color suggestion.
While I'm not a fan of this new palette, if many people request it then Vox populi, vox Dei.
Now, there are also people who will prefer the regular one. So maybe a poll should be taken?
Quote from: Jean-Marie on 11:22, 04 July 25So maybe a poll should be taken?
or just an option when starting the game? Not sure if that's feasible.
Quote from: eto on 11:49, 04 July 25or just an option when starting the game?
Yeah, I guess I could include alternate Levels A,B & C with a new palette, since only the first 3 levels are concerned. I will check this out; for the moment I'm struggling to find 100 bytes free to make the Spikes work with Axelay's optimized scrolling!
Quote from: eto on 11:49, 04 July 25or just an option when starting the game? Not sure if that's feasible.
That would be great, for the new palette.
The spikes are working too. Now, I'll see if I can tidy up the code, and next week I'll try to write the changes in the Level A file, which should be very challenging.
Quote from: Jean-Marie on 17:05, 05 July 25next week I'll try to write the changes in the Level A file, which should be very challenging.
Hopefully it will not be challenging. For compression you can use any language, and modern computers will process the data in any language (also slow scripting languages) more than fast enough.
Whenever you find a sequence of the same byte A of length N, replace it with these four bytes: A,N,#A5,#CC. Only apply this if the sequence is longer than 4 bytes. That's all. This is not the best compression, but a fast one that will work acceptably on sprites and level maps with much background.
Example:
"1,2,3,4, a,a,a,a,a,a ,5,6,7,8" gets "1,2,3,4, a,6,#A5,#CC ,5,6,7,8" (spaces only for better readability)
Hopefully the used escape sequence #A5,#CC is not used anywhere in the data to compress. If it is, either the escape sequence can be changed to something else, or it has to be encoded as sequence of length 1 (what will cost 3 bytes):
Example:
"1,2,3, #A5,#CC ,4,5,6" gets "1,2,3, #A5,1,#A5,#CC, #CC ,4,5,6" (replace the #A5 with a sequence of length 1)
Two questions remain:
- Where should the file be loaded? Usually at the begin of the buffer that will contain the uncompressed data later.
- And which routine says the decompressor where the compressed data is located? Needed are begin and end of the data.
I think the real difficulties will come with the levels where Parallax is used. Daren used animated tiles to create the effect, where background tiles are "slided" in the Tile Map. This is quite ingenious.
Level I (4.1) looks like a real nightmare with a slew of animated tiles (not only for the parallax).
It's quite easy to carry out LDI/LDD instructions on some linear memory, but with Axelay's optimization, the tiles offsets become shuffled, and that makes thing more complex and lengthy :-[
;;TURRICAN.LVI
org &1b78
ld ix,&1d07
ld b,&0f
ld a,(&c7f3)
ld c,a
l1b82:
ld l,(ix)
ld h,(ix+1)
ld a,l
cp &20
jp nc,l1ba6
bit 0,c
jp z,l1b94
inc h
l1b94:
bit 1,c
jp z,l1b9a
dec l
l1b9a:
bit 2,c
jp z,l1ba0
dec h
l1ba0:
bit 3,c
jp z,l1ba6
inc l
l1ba6:
ld (ix),l
ld (ix+1),h
inc ixl
inc ixl
djnz l1b82
ld a,(&c7f3)
rra
jp nc,l1bfc
ld de,&1920
ld hl,&6cb0
REPEAT 8:ldi:ENDM
ld de,&6cb0
call @LDIR-24-24
ld hl,&1920
REPEAT 8:ldi:ENDM
ex de,hl
ld e,&20
call @LDIR-8-8
ld de,&6cd0
call @LDIR-24-24
ld hl,&1920
call @LDIR-8-8
l1bfc:
ld a,(&c7f3)
bit 1,a
jp z,l1c28
ld ix,&6cb0
ld b,&10
l1c0a:
ld e,(ix)
ld d,(ix+1)
ld l,(ix+&20)
ld h,(ix+&21)
ld (ix),h
ld (ix+1),e
ld (ix+&20),d
ld (ix+&21),l
inc ixl
inc ixl
djnz l1c0a
l1c28:
ld a,(&c7f3)
bit 2,a
jp z,l1c72
ld de,&1920
ld hl,&6cc8
REPEAT 8:ldi:ENDM
ld de,&6ccf
ld l,&c7
call @LDDR-24-24
ld e,&b0
ld hl,&1920
REPEAT 8:ldi:ENDM
ex de,hl
ld e,&20
ld l,&e8
call @LDIR-8-8
ld de,&6cef
ld l,&e7
call @LDDR-24-24
ld e,&d0
ld hl,&1920
call @LDIR-8-8
l1c72:
ld a,(&c7f3)
bit 3,a
jp z,l1c9e
ld ix,&6cb0
ld b,&10
l1c80:
ld e,(ix)
ld d,(ix+1)
ld l,(ix+&20)
ld h,(ix+&21)
ld (ix),d
ld (ix+1),l
ld (ix+&20),h
ld (ix+&21),e
inc ixl
inc ixl
djnz l1c80
l1c9e:
ld a,(&dea0)
ld hl,&3e15
rra
jp c,l1cac
ld l,&55
l1cac:
ld de,&6c00
call @LDIR-64-64
ld ix,(&6c7e)
ld iy,(&6c7c)
ld e,&7f
ld hl,&6c7b
call @LDDR-60-60
ld (&6c42),iy
ld (&6c40),ix
ld hl,&6be0
ld de,&1920
call @LDIR-16-16
ld a,(&dea0)
rra
ret c
ld de,&6be0
call l1cef
ld hl,&1920
l1cef:
ld b,&08
l1cf1:
ld c,(hl)
inc l
ld a,(hl)
inc l
exx
ld b,&87
ld c,a
ld a,(bc)
exx
ld (de),a
inc e
ld a,c
exx
ld c,a
ld a,(bc)
exx
ld (de),a
inc e
djnz l1cf1
ret
REPEAT 9:rst 0:ENDM
ORG &9D80
REPEAT 95:ldi:ENDM
@LDIR:
ret
ORG &9E40
REPEAT 95:ldd:ENDM
@LDDR:
ret
Quote from: Jean-Marie on 10:02, 06 July 25I think the real difficulties will come with the levels where Parallax is used. Daren used animated tiles to create the effect, where background tiles are "slided" in the Tile Map. This is quite ingenious. Level I (4.1) looks like a real nightmare with a slew of animated tiles (not only for the parallax).
When I first saw this, I was really impressed. Paralax scrolling was not really common for the CPC. Until recently I thought it would use some kind of optical illusion, where the tiles contain the same background, but shifted differently then the tile offset. But this could not be used here, because it would interfere with reusing tiles throughout a level.
I did some further analysis and now have a better understanding about how the drawing works. There are three levels of image generation, not two!
- The map of e.g. size 137x51 contains indices of blocks (not of tiles!).
- There are 256 blocks, each containing 4x4 tile indices.
- Finally there are 256 tiles, each containing 4x8 pixels.
With this trick the game achieves a compression ratio of 118:1 (or reduces the size by 99.2%). The drawn map has a size of 2192x1632 or 3.6 megapixel, while the three data structures only need 14.8 kilobytes.
By looking at tiles and blocks of level I (4.1) I found there are several blocks showing paralax background. But there are only four tiles (index 203 to 206). The whole paralax scrolling magic is done only in these four tiles!
For better visualization I append the map, blocks, and tiles of level A (1.1). I suppose this is not a big spoiler because almost everybody will know level 1.1 more than good enough.
I've managed to have the first level written on disc. It includes Axelay's optimized routines, with corrections for the animated tiles.
I had it working by removing the compression process altogether. The drawback is that each level files will be 26 Kb :/
I worked out a small Visual Basic macro to compress data with the RLE algorithm; it's working nicely but but there are still some problems during the decompression conspicuously. I'll try to investigate further.
Quote from: Jean-Marie on 09:17, 10 July 25I've managed to have the first level written on disc. It includes Axelay's optimized routines, with corrections for the animated tiles.
I had it working by removing the compression process altogether. The drawback is that each level files will be 26 Kb :/
I worked out a small Visual Basic macro to compress data with the RLE algorithm; it's working nicely but but there are still some problems during the decompression conspicuously. I'll try to investigate further.
I don't have time to look into this at all, so apologies if these suggestions are unworkable or have already been considered, but would it be possible to leave the level data as is and just have an assembly routine in memory that reformats the tile data after loading? Or if there is no spare memory for that to be permanently in memory, or there are additional things you have needed to add to make it work, like for the animated tiles, could that simply be appended to the existing level data file, so then you only perhaps need add an extra call to some temporary code added to the data load that sorts things out after the original level data load has done it's 'usual' process?
Your "shuffled" data is working nicely as it is, I was able to adapt the code displaying the waterfall and the exhaust flames without too much overheads (Thanks again BTW!).
For the spikes, I had to append a "clean" copy of the original tiles (non-shuffled) at the end of the level file, indeed, as it was too complicated & lengthy to modify the original code to make it work with the shuffled tiles.
The problem stems from writing the new file on the disc, as Daren used some intricated code to uncompress the Data. But hopefully, I'll find a solution. I just need to have a serious look at the decoding routine.
Quote from: Jean-Marie on 12:51, 10 July 25The problem stems from writing the new file on the disc, as Daren used some intricated code to uncompress the Data. But hopefully, I'll find a solution. I just need to have a serious look at the decoding routine.
Because I already spent some time on this: Can you describe a situation where it does not work?
As far as I remember, there is one access to tile 0 that cleans it after loading, for whatever reason.
Also some other tiles are used to transport other data (e.g. font data) and are reused afterwards for animation purposes.
What is also important for decoding is not the process itself, but where the compressed data is placed before decompression starts. Because you applied several changes, this position will be different from before. Until now I did not find the code location where this position is stored. It surely needs to be altered.
I'm progressing : I have located a part of the code which caused some troubles, at 1B39h.
It moved the tiles of the spikes & flames to a buffer used for the display. This was messing up with the shuffled data obviously. Actually, this part of the code can be safely removed because I use no buffer for drawing the spikes & flames.
Only the end of the procedure remains mysterious to me (after the palette setting).
org &1B39
ld bc,&002c
ld de,&d7cc
ld hl,&d7cb
ld (hl),&0004
ldir
;;ld bc,&0030
;;ld de,&6dc0
;;ld hl,&6930 ;;move spikes tiles to buffer
;;ldir
;;ld bc,&0040
;;ld de,&6d40
;;ld hl,&6cb0 ;;move flames tiles to buffer
;;ldir
ld hl,&1b1f
call &d600 ;;set palette
ld b,&0006
ld ix,&243e
ld de,&0006
ld hl,&24fe
l1b6e:
ld c,&0010
l1b70:
ld (ix+&0000),l
ld (ix+&0001),h
inc ix
inc ix
l1b7a:
ld a,(hl)
inc hl
cp &00d3
jp z,l1b85
add hl,de
jp l1b7a
l1b85:
dec c
jp nz,l1b70
djnz l1b6e
ret
Quote from: lightforce6128 on 18:54, 10 July 25Until now I did not find the code location where this position is stored. It surely needs to be altered.
I found the decoding routine at B000h, and the BC register seems to contains the size of the compressed data. I guess I'll have to modify it since the size is a bit larger with the new shuffled data.
org #b000
ld hl,(#b05b) ;;hl=1BB0
ld de,(#b05d) ;;de=1AFF
ld bc,(#b05f) ;;bc=481A (data size?)
inc bc
ldir
ld ix,(#b063) ;;ix=7FFF
ld hl,(#b065) ;;hl=6319
ld de,(#b059) ;;de=A5CC (escape code)
ld bc,(#b05d) ;;bc=1AFF
lb01d:
ld a,(hl)
cp e
jr z,#b03f
ld (ix+#00),a
dec hl
dec ix
push af
ld a,h
cp b
jr nz,lb030
ld a,l
cp c
jr z,lb033
lb030:
pop af
jr lb01d
lb033:
pop af
ld a,(#b067)
or a
nop
nop
ret
Quote from: Jean-Marie on 20:06, 10 July 25Quote from: lightforce6128 on 18:54, 10 July 25Until now I did not find the code location where this position is stored. It surely needs to be altered.
I found the decoding routine at B000h, and the BC register seems to contains the size of the compressed data. I guess I'll have to modify it since the size is a bit larger with the new shuffled data.
But not only the size. The location is important as well. The starting point is not the begin of the data (what stays the same), but its end, because processing is done backwards. The end of the compressed data is definitely no longer where it was before.
Just popping in to say, I prefer the original palette. (No offence to MacDeath - that is nice too, but it's just personal preference)
I got the first level working under its newly compressed form :) Many thanks to
@lightforce6128 for the hints!
After looking at the drawing of tiles, now I had a look at drawing of sprites. This is by far more complex and distributes over several longer routines. The following BASIC-like pseudo-code gives a coarse overview about what is happening there. The given addresses map to the begin of routines or code blocks.
0DA9: copyTilesToScreen()
0E28: FOR EACH sprite IN spriteList DO
0E3A: IF sprite IS ExtrasSphere THEN
0E60: copySmallerExtrasSphereSpriteToATemporaryNormalSprite()
----: END IF
AEF0: WITH sprite DO
A7C0: mirrorHorizontally()
9F00: mirrorVertically()
AF04: clipHorizontally()
AF48: clipVertically()
AFC3: copyNonTransparentBytesToScreen()
----: END WITH
0EA9: NEXT
0EB2: drawSpriteWithoutClipping(mine)
0EB9: drawSpriteWithoutClipping(player)
0EE8: drawSpriteWithoutClipping(shield)
DE00: FOR EACH explosion in explosionList
DE2A: getAnimation()
DE11: drawSpriteWithoutClipping(explosion)
DE14: updateAnimation()
----: NEXT
0EEE: swapScreenBuffers()
----:
DF02: SUB drawSpriteWithoutClipping(sprite) : ... : END SUB
Also I would like to, I cannot provide a well commented disassembly. This would be by far too much work. But at least some discoveries I would like to share:
- There is not any kind of caching or reuse.
- In each frame, the extras spheres are converted to normal sprites again.
- In each frame, each single sprite (that needs to) is mirrored again.
- The converted sprites with the extras sphere are transparent at more than 50% of the bytes. Nevertheless each transparent byte is built up during the conversion and skipped later while drawing the sprite. In each frame, for each single extras sphere.
- There are smaller and bigger explosions. It seems, the latter one is not used.
- The smaller explosions skip their last animation step due to a programming error.
For the loop for drawing a sprite I can provide a disassembly. I think this can be optimized. Using this routine takes ~3000 NOPs to draw a single sprite. This is 15% of a frame. Drawing 6 sprites eats up a whole frame. Bad things will happen if an extras box is destroyed and releases a bunch of extras spheres.
ORG #AFC3
;; This routine draws a single sprite after mirroring and clipping have been applied.
;;
;; B - clipped height (full height is 24)
;; C - clipped width (full width is 6)
;; DE' - source address
;; HL' - target address
;; (source_skip-2) - bytes to skip in source for each row
;; (target_skip-2) - bytes to advance target for each row
;; (jump_condition) - set to #CA (JP Z) or #C2 (JP NZ)
draw_sprite:
y_loop: ;; ;;
LD A,C ;; 1 ;;
EXX ;; 1 ;;
;; ;; | ;;
x_loop: ;; | ;; To write one byte, this inner loop takes 17 NOPs.
EX AF,AF' ;; |----- 1 ;; Save A.
LD A,(DE) : INC DE ;; | +-4-+ ;; Load sprite data and update source pointer.
OR A : JR Z,$+3 ;; | 3 4 ;; Is byte opaque?
LD (HL),A ;; | +---2 ;; Write byte.
INC HL ;; | 2 ;; Update target pointer.
EX AF,AF' ;; | 1 ;; Restore A.
DEC A : JP NZ,x_loop ;; |----- 4 ;;
;; ;; | ;;
EX DE,HL ;; 1 ;; Swap registers to allow DE=DE+BC.
LD BC,#0000 :source_skip ;; 3 ;; Skip bytes in source data.
ADD HL,BC ;; 3 ;;
EX DE,HL ;; 1 ;;
;; ;; | ;;
LD BC,#07FA :target_skip ;; 3 ;; Go to next row and skip bytes on screen.
ADD HL,BC ;; 3 ;;
;; ;; | ;;
BIT 6,H ;; 2 ;; Was the screen memory left?
jump_condition: JP Z,$+7 ;; 3 ;;
LD BC,#C040 : ADD HL,BC ;; (6/8) ;; Then correct target pointer.
;; ;; | ;;
EXX ;; 1 ;;
DJNZ y_loop ;; 4-1 ;;
;; ;; ---- ----- ;;
;; ;; 26-1 15|18 ;; (6*(15+18)/2+26+6/8)*24-1 = ~3017 NOPs per sprite.
Quote from: Jean-Marie on 20:06, 10 July 25Only the end of the procedure remains mysterious to me (after the palette setting).
I tried to understand what is going on there, but also without success. The additional code somehow builds up an index (a table with addresses) for another, more complex data structure. Maybe this is somehow related to sprites, maybe it is something completely different.
ORG #1B39
prepare_tiles:
LD HL,#1B1F : CALL set_table_of_firmware_inks ;; Set palette.
LD BC,#002C : LD DE,#D7CC : LD HL,#D7CB : LD (HL),#04 : LDIR ;; Fill buffer with vertical stripes (blue-yellow). Why?
LD BC,#0030 : LD DE,#6DC0 : LD HL,#6930 : LDIR ;; Create copy of sprite. This overwrites font data "789".
LD BC,#0040 : LD DE,#6D40 : LD HL,#6CB0 : LDIR ;; Create copy of big flame. This overwrites font data "_012".
LD B,#06 ;; Counter for outer loop.
LD IX,#243E ;; Target address. Table reaches below source address (length: 192, 96 entries).
LD DE,#0006 ;; Source step size.
LD HL,#24FE ;; Source address.
outer_loop: ;;
LD C,#10 ;; Counter for inner loop.
inner_loop: ;;
LD (IX+#01),H : LD (IX+#00),L : INC IX : INC IX ;; Store source address in target entry.
search_marker_loop: ;;
LD A,(HL) : INC HL ;;
CP #D3 : JP Z,marker_found ;;
ADD HL,DE ;; Go to next source record.
JP search_marker_loop ;;
marker_found: ;;
DEC C : JP NZ,inner_loop ;;
DJNZ outer_loop ;;
;; Here:
;; IX - The target pointer has reached the begin of the source at #24FE.
;; HL - The source pointer has reached the begin of sprite data at #2BD2.
;; (#243E) - Target entry 0 points to a value #D3.
;; (#243E+n*2) - Other target entries point to a value behind #D3.
;; ((#243E+n*2)) - a sequence like ( [type] [adr_low] [adr_high] [?] [?] [?] [?] )+
RET
ORG #D600
set_table_of_firmware_inks:
;; HL - Table of 17 firmware inks (for 16 pens and border).
LD E,(HL) ;; E is loaded, but not used.
LD D,#10 ;; Pen counter, 16..1.
pen_loop: ;;
LD A,D : SUB #10 : NEG : LD C,A ;; Pen, 0..15.
LD A,(HL) ;; Read firmware ink from table.
CALL set_pen_to_firmware_ink ;; Set pen to ink.
INC HL ;; Go to next firmware ink.
DEC D : JP NZ,pen_loop ;;
LD C,#10 ;; Finally select border (pen 16).
LD A,(HL) ;; Read final firmware ink.
;; ;; Fall through to subroutine without CALL.
set_pen_to_firmware_ink:
;; A - firmware ink
;; C - pen
PUSH HL ;;
ADD #85 : LD L,A ;; Calculate low byte of address (+#85).
ADC #DE : SUB L : LD H,A ;; Calculate high byte of address (+#DE).
LD A,(HL) ;; Read hardware ink.
POP HL ;;
AND #1F : OR #40 ;; Mask ink and add command bit for Gate Array.
LD B,#7F : OUT (C),C : OUT (C),A ;; Set ink.
;; ;;
RET ;; Jump back to pen loop or to caller of 'set_table_of_firmware_inks'.
Thank you for the comments.
In the AFC3 loops, I think we can get rid of the following instructions:
EX AF,AF' ;; Restore A.
DEC A : JP NZ,x_loop
and use the register B' as a loop counter. The BC' register is overwritten just after, as we can see.
So the code would become :
ORG &AFC3
y_loop:
LD A,C
EXX
LD B,A ;;B'=x_loop counter
x_loop:
LD A,(DE) : INC DE ;; Load sprite data and update source pointer.
OR A : JR Z,$+3 ;; Is byte opaque?
LD (HL),A ;; Write byte.
INC HL ;; Update target pointer.
DJNZ x_loop
EX DE,HL ;; Swap registers to allow DE=DE+BC.
LD BC,#0000 :source_skip ;; Skip bytes in source data.
ADD HL,BC
EX DE,HL
LD BC,#07FA :target_skip ;; Go to next row and skip bytes on screen.
ADD HL,BC
BIT 6,H ;; Was the screen memory left?
jump_condition: JP Z,$+7
LD BC,#C040 : ADD HL,BC ;; Then correct target pointer.
EXX
DJNZ y_loop
I have not tested it yet.
Consequently, we can also replace : LD BC,#0000 :source_skip ;; Skip bytes in source data.
with
LD C,0 :source_skip
since the B' register will always be 0 after the loop, and the C' value will never be greater than 6.
ORG &AFC3
y_loop:
LD A,C
EXX
LD B,A ;;B'=x_loop counter
x_loop:
LD A,(DE) : INC DE ;; Load sprite data and update source pointer.
OR A : JR Z,$+3 ;; Is byte opaque?
LD (HL),A ;; Write byte.
INC HL ;; Update target pointer.
DJNZ x_loop
EX DE,HL ;; Swap registers to allow DE=DE+BC.
LD C,0 :source_skip ;; Skip bytes in source data.
ADD HL,BC
EX DE,HL
LD BC,#07FA :target_skip ;; Go to next row and skip bytes on screen.
ADD HL,BC
BIT 6,H ;; Was the screen memory left?
jump_condition: JP Z,$+7
LD BC,#C040 : ADD HL,BC ;; Then correct target pointer.
EXX
DJNZ y_loop
Some more ideas to speed up sprite drawing:
- From Jean-Marie: Use B as loop counter and remove EX AF,AF'. => 287 NOPs saved.
- From Jean-Marie: Shorten source skip assignment from LD BC to LD B. => 24 NOPs saved.
- Increment of screen address is guaranteed not to cross a 256 bytes border. So INC HL can be replaced with INC L. => 144 NOPs saved.
- Not for all, but for some sprites it is guaranteed that they are stored within a 256 bytes page (approximately half of them). In this cases INC DE can be replaced with INC E. => 144/2 = 72 NOPs saved.
- Sprites are placed on a mode 1 chars grid. Often there is no vertical clipping. Then it is known in advance when the screen address will overflow. This does not need to be checked on every line. => 120 NOPs saved.
- The screen address is updated with INC HL at the end of each row, although no more bytes will be drawn. => 48|24 NOPs saved.
- Adding source skip is done with a 16-bit addition. This can be shortened with an 8-bit addition, and it can be shortened even more if the sprite data is inside one 256 bytes page. => 60 NOPs saved.
EX DE,HL : LD BC,0 :source_skip : ADD HL,BC : EX DE,HL ;; 8
LD A,E : ADD A,0 :source_skip : LD E,A : JR NC,$+3 : INC D ;; 7
LD A,E : ADD A,0 :source_skip : LD E,A ;; 4
In sum, these things would save 731 NOPs or 24% of sprite drawing time. And I think, even more than this is possible.
That may be a silly question.
How to activate Joystick.
Sorry if that was asked before, just point me to the
Thread/ Post.
Steven
If you press the Fire button on the title screen, the game will start in Joystick mode. If you press Space, it will start in keyboard mode.
Now I've added a few refinements:
- RUN'TURRICAN" will start the game with the original controls. RETURN key will act as FIRE2 and launch an energy bar, and SHIFT will launch a grenade.
- RUN"FIRE1" will act the same, except that the RETURN key is replaced by the SPACE key.
- RUN"FIRE2" handles a joystick with 2 buttons. The second button is used for releasing Energy bars. SHIFT is still used to launch grenades.
- RUN"FIRE3" handles a joystick with 3 buttons. The third button is used to launch grenades.
Quote from: Jean-Marie on 00:18, 28 July 25If you press the Fire button on the title screen, the game will start in Joystick mode. If ...
I could have bet my ass that I've already tried exactly that. May be a problem with my homemade joystick.
I'll try it again.
Stefan
At least, the two disk version works as expected.
Could have been a faulty contact at the Sub-D plug.
Will investigate tomorrow.
Steven
Quote from: St-BeidE(DE/GB) on 21:35, 28 July 25At least, the two disk version works as expected.
Could have been a faulty contact at the Sub-D plug.
Will investigate tomorrow.
Steven
Nope. Could not wait.
It was my fault. Version 7 works as expected.
Dont know what I made wrong.
Sorry for the false information.
Steven