Graphics for the Amiga/Atari ST versions of Wicked were by Neil Strudwick.
For the purposes of labelling the Amiga disassembly of Wicked, Maptapper was originally used to find some of the sprites and graphics locations in memory. A savestate(.uss) from WinUAE 4.9.1 was used to dump 512kb of Amiga memory. The graphics use 4 bitplanes, and are in AmigaILBM/AtariST Mode. Later, when the graphics index table was found, a bespoke python program was written to decode and write the more obfuscated graphics.
Addresses in green have been verified in the disassembly.
| Image | Name | Address | Size |
|---|---|---|---|
| lose1 | $39784 | 16×16 | |
| lose2 | $39804 | 16×16 | |
| lose3 | $39884 | 16×16 | |
| lose4 | $39904 | 16×16 | |
| lose5 | $39984 | 16×16 | |
| lose6 | $39A04 | 16×16 | |
| lose7 | $39A84 | 16×16 | |
| lose8 | $39B04 | 16×16 | |
| win1 | $39B84 | 16×16 | |
| win2 | $39C04 | 16×16 | |
| win3 | $39C84 | 16×16 | |
| win4 | $39D04 | 16×16 | |
| win5 | $39D84 | 16×16 | |
| win6 | $39E04 | 16×16 | |
| win7 | $39E84 | 16×16 | |
| win8 | $39F04 | 16×16 | |
| eyebg1 | $39F84 | 16×32 | |
| eyebg2 | $3A384 | 16×28 | |
| eyebg3 | $3A704 | 16×24 | |
| eyebg4 | $3A944 | 16×20 | |
| iris1 | $3AB24 | 16×32 | |
| iris2 | $3AD24 | 16×26 | |
| iris3 | $3AEC4 | 16×21 | |
| pupil1 | $3B014 | 16×16 | |
| pupil2 | $3B094 | 16×16 | |
| pupil3 | $3B114 | 16×16 | |
| eyelids1 | $3B194 | 16×48 | |
| eyelids2 | $3B754 | 16×48 | |
| eyelids3 | $3BD14 | 16×48 | |
![]() | playerup1 | $3C2D4 | 16×31 |
![]() | playerup2 | $3C4C4 | 16×31 |
![]() | playerup3 | $3C6B4 | 16×31 |
![]() | playerdown1 | $3C8A4 | 16×31 |
![]() | playerdown2 | $3CA94 | 16×31 |
| playerdown3 | $3CC84 | 16×31 | |
![]() | playerleft1 | $3CE74 | 16×31 |
![]() | playerleft2 | $3D064 | 16×31 |
![]() | playerleft3 | $3D254 | 16×31 |
![]() | playerright1 | $3D444 | 16×31 |
![]() | playerright2 | $3D634 | 16×31 |
![]() | playerright3 | $3D824 | 16×31 |
![]() | playerupright1 | $3DA14 | 16×31 |
![]() | playerupright2 | $3DC04 | 16×31 |
![]() | playerupright3 | $3DDF4 | 16×31 |
![]() | playerdownright1 | $3DFE4 | 16×31 |
![]() | playerdownright2 | $3E1D4 | 16×31 |
![]() | playerdownright3 | $3E3C4 | 16×31 |
![]() | playerupleft1 | $3E5B4 | 16×31 |
![]() | playerupleft2 | $3E7A4 | 16×31 |
![]() | playerupleft3 | $3E994 | 16×31 |
![]() | playerdownleft1 | $3EB84 | 16×31 |
![]() | playerdownleft2 | $3ED74 | 16×31 |
![]() | playerdownleft3 | $3EF64 | 16×31 |
![]() | playermultifire1 | $3F154 | 16×32 |
![]() | playermultifire2 | $3F354 | 16×32 |
![]() | playermultifire3 | $3F554 | 16×32 |
![]() | playermultifire4 | $3F754 | 16×32 |
![]() | playershield1 | $3F954 | 16×31 |
![]() | playershied2 | $3FB44 | 16×31 |
![]() | playershield3 | $3FD34 | 16×31 |
| scrollleftend | $3FF24 | 16×16 | |
| scrollrightend | $40024 | 16×16 | |
| scrollbar | $40124 | 16×14 | |
| powercrystal1 | $40304 | 16×16 | |
| powercrystal2 | $40384 | 16×16 | |
| powercrystal3 | $40404 | 16×16 | |
| powercrystal4 | $40484 | 16×16 | |
![]() | guardian1_1 | $40504 | 16×47 |
![]() | guardian1_2 | $4096C | 16×47 |
![]() | guardian1_3 | $40DD4 | 16×47 |
![]() | guardian0_1 | $4123C | 16×46 |
![]() | guardian0_2 | $4168C | 16×46 |
![]() | guardian0_3 | $41ADC | 16×46 |
![]() | guardian0_4 | $41F2C | 16×46 |
![]() | guardian0_5 | $4237C | 16×46 |
![]() | guardian0_6 | $427CC | 16×46 |
![]() | guardian0_7 | $42C1C | 16×46 |
![]() | guardian2_1 | $4306C | 16×49 |
![]() | guardian2_2 | $4337C | 16×49 |
![]() | guardian2_3 | $4368C | 16×49 |
![]() | guardian2_4 | $4399C | 16×49 |
![]() | guardian2_5 | $43CAC | 16×49 |
![]() | handeye1 | $43FBC | 16×9 |
![]() | handeye2 | $44004 | 16×9 |
![]() | handeye3 | $4404C | 16×9 |
![]() | handeye4 | $44094 | 16×9 |
![]() | handeye5 | $440DC | 16×9 |
| servantdeath1 | $44124 | 16×26 | |
| servantdeath2 | $442C4 | 16×26 | |
| servantdeath3 | $44464 | 16×26 | |
| servantdeath4 | $44604 | 16×26 | |
| servantdeath5 | $447A4 | 16×26 | |
| servantdeath6 | $44944 | 16×26 | |
| worm1 | $44AE4 | 16×14 | |
| worm2 | $44BC4 | 16×14 | |
| worm3 | $44CA4 | 16×14 | |
| worm4 | $44D84 | 16×14 | |
| skull1 | $44E64 | 16×32 | |
![]() | skull2 | $45064 | 16×32 |
![]() | skull3 | $45264 | 16×32 |
![]() | skull4 | $45464 | 16×32 |
![]() | skull5 | $45664 | 16×32 |
![]() | skull6 | $45864 | 16×32 |
| dagger1 | $45A64 | 16×32 | |
| dagger2 | $45C64 | 16×32 | |
| planchette | $45DFC | 16×16 | |
| teardrop1 | $45EFC | 16×13 | |
| teardrop2 | $45FCC | 16×13 | |
| teardrop3 | $4609C | 16×13 | |
| teardrop4 | $4616C | 16×13 | |
| teardrop5 | $4623C | 16×13 | |
| teardrop6 | $4630C | 16×16 | |
| teardrop7 | $4640C | 16×16 | |
| teardrop8 | $4650C | 16×16 | |
| teardrop9 | $4660C | 16×16 | |
| crystalvanish1 | $4670C | 16×13 | |
| crystalvanish2 | $46774 | 16×13 | |
| crystalvanish3 | $467DC | 16×13 | |
| crystalvanish4 | $46844 | 16×13 | |
| crystalvanish5 | $468AC | 16×13 | |
| crystalvanish6 | $46914 | 16×13 | |
| servant0_1 | $4697C | 16×16 | |
| servant0_2 | $469FC | 16×16 | |
| servant0_3 | $46A7C | 16×16 | |
| servant0_4 | $46AFC | 16×16 | |
| servant0_5 | $46B7C | 16×16 | |
| servant0_6 | $46BFC | 16×16 | |
| servant0_7 | $46C7C | 16×16 | |
![]() | servant2_1 | $46CFC | 16×16 |
![]() | servant2_2 | $46D7C | 16×16 |
![]() | servant2_3 | $46DFC | 16×16 |
![]() | servant2_4 | $46E7C | 16×16 |
![]() | servant1_1 | $46EFC | 16×16 |
![]() | servant1_2 | $46F7C | 16×16 |
![]() | servant1_3 | $46FFC | 16×16 |
![]() | servant1_4 | $4707C | 16×16 |
![]() | servant1_5 | $470FC | 16×16 |
![]() | servant1_6 | $4717C | 16×16 |
![]() | servant1_7 | $471FC | 16×16 |
![]() | servant1_8 | $4727C | 16×16 |
![]() | servant1_9 | $472FC | 16×16 |
![]() | servant1_10 | $4737C | 16×16 |
![]() | servant5_1 | $473FC | 16×16 |
![]() | servant5_2 | $4747C | 16×16 |
![]() | servant5_3 | $474FC | 16×16 |
![]() | servant5_4 | $4757C | 16×16 |
![]() | servant5_5 | $475FC | 16×16 |
![]() | servant4_1 | $4767C | 16×15 |
![]() | servant4_2 | $476F4 | 16×15 |
![]() | servant4_3 | $4776C | 16×15 |
![]() | servant4_4 | $477E4 | 16×15 |
![]() | servant4_5 | $4785C | 16×15 |
![]() | servant6_1 | $478D4 | 16×14 |
![]() | servant6_2 | $47944 | 16×14 |
![]() | servant6_3 | $479B4 | 16×14 |
![]() | servant6_4 | $47A24 | 16×14 |
![]() | servant6_5 | $47A94 | 16×14 |
![]() | servant6_6 | $47B04 | 16×14 |
![]() | servant3_1 | $47B74 | 16×16 |
![]() | servant3_2 | $47BF4 | 16×16 |
![]() | servant3_3 | $47C74 | 16×16 |
![]() | servant3_4 | $47CF4 | 16×16 |
![]() | servant3_5 | $47D74 | 16×16 |
![]() | servant3_6 | $47DF4 | 16×16 |
![]() | servant3_7 | $47E74 | 16×16 |
![]() | servant3_8 | $47EF4 | 16×16 |
![]() | servant3_9 | $47F74 | 16×16 |
![]() | guardian3_1 | $47FF4 | 16×58 |
![]() | guardian3_2 | $48564 | 16×58 |
![]() | guardian3_3 | $48AD4 | 16×58 |
![]() | guardian3_4 | $49044 | 16×58 |
![]() | guardian3_5 | $495B4 | 16×58 |
![]() | guardian4_1 | $49B24 | 16×29 |
![]() | guardian4_2 | $49EC4 | 16×29 |
![]() | guardian4_3 | $4A264 | 16×29 |
![]() | guardian4_4 | $4A604 | 16×29 |
![]() | guardian5_1 | $4A9A4 | 16×31 |
![]() | guardian5_2 | $4AD84 | 16×31 |
![]() | guardian5_3 | $4B164 | 16×31 |
![]() | guardian5_4 | $4B544 | 16×31 |
![]() | guardian5_5 | $4B924 | 16×31 |
![]() | guardian5_6 | $4BD04 | 16×31 |
![]() | guardian5_7 | $4C0E4 | 16×31 |
![]() | guardian5_8 | $4C4C4 | 16×31 |
| expl2_1 | $4C8A4 | 16×15 | |
| expl2_2 | $4C994 | 16×15 | |
| expl2_3 | $4CA84 | 16×15 | |
| expl2_4 | $4CB74 | 16×15 | |
| expl2_5 | $4CC64 | 16×15 | |
| expl2_6 | $4CD54 | 16×15 | |
| expl2_7 | $4CE44 | 16×15 | |
| expl2_8 | $4CF34 | 16×15 | |
![]() | guardian6_1 | $4D024 | 16×31 |
![]() | guardian6_2 | $4D414 | 16×31 |
![]() | guardian6_3 | $4D7E4 | 16×31 |
![]() | guardian6_4 | $4DBC4 | 16×31 |
![]() | guardian6_5 | $4DFA4 | 16×31 |
| energypointer | $4E384 | 16×9 | |
| timerpointer | $4E3CC | 16×9 | |
| ServantFire_redwhiteballs1 | $4E414 | 16×7 | |
| ServantFire_redwhiteballs2 | $4E444 | 16×7 | |
| ServantFire_redwhiteballs3 | $4E474 | 16×7 | |
| cross1 | $4E4A4 | 16×7 | |
| cross2 | $4E4E4 | 16×7 | |
| flyingevilspore1 | $4E514 | 16×7 | |
| flyingevilspore2 | $4E54C | 16×7 | |
| flyingevilspore3 | $4E584 | 16×7 | |
| minisun1 | $4E5BC | 16×8 | |
| minisun2 | $4E5FC | 16×8 | |
| minipowercrystal | $4E63C | 16×8 | |
| minimoon | $4E67C | 16×8 | |
| goodspore1 | $4E6BC | 16×8 | |
| goodspore2 | $4E6FC | 16×8 | |
| goodspore3 | $4E73C | 16×8 | |
| evilspore1 | $4E77C | 16×8 | |
| evilspore2 | $4E7BC | 16×8 | |
| evilspore3 | $4E7FC | 16×8 | |
| evilportal0 | $4E83C | 16×14 | |
| evilgrowth0_1 | $4E8AC | 16×14 | |
| evilgrowth0_2 | $4E91C | 16×14 | |
| evilgrowth0_3 | $4E98C | 16×14 | |
| evilgrowth0_4 | $4E9FC | 16×14 | |
| evilgrowth0_5 | $4EA6C | 16×14 | |
| evilgrowth0_6 | $4EADC | 16×14 | |
| evilgrowth0_7 | $4EB4C | 16×14 | |
| goodportal0 | $4ED0C | 16×14 | |
| goodgrowth0_1 | $4ED7C | 16×14 | |
| goodgrowth0_2 | $4EDEC | 16×14 | |
| goodgrowth0_3 | $4EE5C | 16×14 | |
| goodgrowth0_4 | $4EECC | 16×14 | |
| goodgrowth0_5 | $4EF3C | 16×14 | |
| goodgrowth0_6 | $4EFAC | 16×14 | |
| goodgrowth0_7 | $4F01C | 16×14 | |
| shotgrowth0_duplicate | $4F1DC | 16×14 | |
| shotgrowth0_1 | $4F24C | 16×14 | |
| shotgrowth0_2 | $4F2BC | 16×14 | |
| shotgrowth0_3 | $4F32C | 16×14 | |
| shotgrowth0_4 | $4F39C | 16×14 | |
| shotgrowth0_5 | $4F40C | 16×14 | |
| shotgrowth0_6 | $4F47C | 16×14 | |
| shotgrowth0_7 | $4F4EC | 16×14 | |
| evilportal3 | $4F6AC | 16×14 | |
| evilgrowth3_1 | $4F71C | 16×14 | |
| evilgrowth3_2 | $4F78C | 16×14 | |
| evilgrowth3_3 | $4F7FC | 16×14 | |
| evilgrowth3_4 | $4F86C | 16×14 | |
| evilgrowth3_5 | $4F8DC | 16×14 | |
| evilgrowth3_6 | $4F94C | 16×14 | |
| evilgrowth3_7 | $4F9BC | 16×14 | |
| goodportal3 | $4FB7C | 16×14 | |
| goodgrowth3_1 | $4FBEC | 16×14 | |
| goodgrowth3_2 | $4FC5C | 16×14 | |
| goodgrowth3_3 | $4FCCC | 16×14 | |
| goodgrowth3_4 | $4FD3C | 16×14 | |
| goodgrowth3_5 | $4FDAC | 16×14 | |
| goodgrowth3_6 | $4FE1C | 16×14 | |
| goodgrowth3_7 | $4FE8C | 16×14 | |
| unknown1 | $5004C | 16×14 | |
| shotgrowth3_1 | $500BC | 16×14 | |
| shotgrowth3_2 | $5012C | 16×14 | |
| shotgrowth3_3 | $5019C | 16×14 | |
| shotgrowth3_4 | $5020C | 16×14 | |
| shotgrowth3_5 | $5027C | 16×14 | |
| shotgrowth3_6 | $502EC | 16×14 | |
| shotgrowth3_7 | $5035C | 16×14 | |
| evilportal2 | $5051C | 16×14 | |
| evilgrowth2_1 | $5058C | 16×14 | |
| evilgrowth2_2 | $505FC | 16×14 | |
| evilgrowth2_3 | $5066C | 16×14 | |
| evilgrowth2_4 | $506DC | 16×14 | |
| evilgrowth2_5 | $5074C | 16×14 | |
| evilgrowth2_6 | $507BC | 16×14 | |
| evilgrowth2_7 | $5082C | 16×14 | |
| goodportal2 | $509EC | 16×14 | |
| goodgrowth2_1 | $50A5C | 16×14 | |
| goodgrowth2_2 | $50ACC | 16×14 | |
| goodgrowth2_3 | $50B3C | 16×14 | |
| goodgrowth2_4 | $50BAC | 16×14 | |
| goodgrowth2_5 | $50C1C | 16×14 | |
| goodgrowth2_6 | $50C8C | 16×14 | |
| goodgrowth2_7 | $50CFC | 16×14 | |
| unknown2 | $50EBC | 16×14 | |
| shotgrowth2_1 | $50F2C | 16×14 | |
| shotgrowth2_2 | $50F9C | 16×14 | |
| shotgrowth2_3 | $5100C | 16×14 | |
| shotgrowth2_4 | $5107C | 16×14 | |
| shotgrowth2_5 | $510EC | 16×14 | |
| shotgrowth2_6 | $5115C | 16×14 | |
| shotgrowth2_7 | $511CC | 16×14 | |
| evilportal1 | $5138C | 16×14 | |
| evilgrowth1_1 | $513FC | 16×14 | |
| evilgrowth1_2 | $5146C | 16×14 | |
| evilgrowth1_3 | $514DC | 16×14 | |
| evilgrowth1_4 | $5154C | 16×14 | |
| evilgrowth1_5 | $515BC | 16×14 | |
| evilgrowth1_6 | $5162C | 16×14 | |
| evilgrowth1_7 | $5169C | 16×14 | |
| goodportal1 | $5185C | 16×14 | |
| goodgrowth1_1 | $518CC | 16×14 | |
| goodgrowth1_2 | $5193C | 16×14 | |
| goodgrowth1_3 | $519AC | 16×14 | |
| goodgrowth1_4 | $51A1C | 16×14 | |
| goodgrowth1_5 | $51A8C | 16×14 | |
| goodgrowth1_6 | $51AFC | 16×14 | |
| goodgrowth1_7 | $51B6C | 16×14 | |
| (duplicate) | $51D2C | 16×14 | |
| shotgrowth1_1 | $51D9C | 16×14 | |
| shotgrowth1_2 | $51E0C | 16×14 | |
| shotgrowth1_3 | $51E7C | 16×14 | |
| shotgrowth1_4 | $51EEC | 16×14 | |
| shotgrowth1_5 | $51F5C | 16×14 | |
| shotgrowth1_6 | $51FCC | 16×14 | |
| shotgrowth1_7 | $5203C | 16×14 | |
| lifestar1 | $52200 | 16×14 | |
| lifestar2 | $52268 | 16×14 | |
| Eyelids bottom – Index 87 | $522DC | 24×16 | |
| indicator1 – Index 88 | $52452 | 16×14 | |
| indicator2 – Index 89 | $524c2 | 16×14 | |
| indicator3 – Index 90 | $52532 | 16×14 | |
| indicator4 – Index 91 | $525A2 | 16×14 | |
| indicator5 – Index 92 | $52612 | 16×14 | |
| indicator6 – Index 93 | $52682 | 16×14 | |
![]() | StarMap Graphics – Compass – Index 1 | $526f2 | 32×31 |
![]() | StarMap tile background 0 – Index 2 | $528db | 32×32 |
![]() | StarMap Graphics – Galaxy – Index 3 | $529f1 | 32×29 |
![]() | StarMap Graphics – Comet – Index 4 | $52ad6 | 32×25 |
![]() | StarMap Graphics – Star Pattern – Index 5 | $52b79 | 32×27 |
![]() | StarMap tile background 1 – Index 75 | $52c34 | 32×32 |
![]() | StarMap tile background 2 – Index 76 | $52d48 | 32×32 |
![]() | StarMap Graphics – Cherub facing left – Index 77 | $52e5d | 32×32 |
![]() | StarMap Graphics – Cherub facing right – Index 78 | $5304c | 32×32 |
![]() | StarMap Graphics – Sexton – Index 79 | $531d2 | 32×32 |
![]() | StarMap Graphics – Star pattern – Index 80 | $53337 | 32×32 |
![]() | StarMap Graphics – Lizard up – Index 81 | $533c0 | 32×30 |
![]() | StarMap Graphics – Lizard down – Index 82 | $53572 | 32×30 |
![]() | StarMap Graphics – SeaMonster facing right – Index 83 | $53723 | 32×31 |
![]() | StarMap Graphics – SeaMonster facing left – Index 84 | $5390c | 32×31 |
| Bird/Snake Energy/Time Meter – Index 8 | $53ae8 | 16×95 | |
![]() | Eye of Infinity Stone Circle – Index 0 | $53d98 | 224×199 |
![]() | Wicked Sarsen Stones – Index 7 | $578fa | 288×68 |
![]() | Sunset Stone Shadow – Index 6 | $59b46 | 320×2 |
![]() | Large Player Circle 1 | $5a546 | 64×64 |
![]() | Large Player Circle 2 | $5ad46 | 64×64 |
![]() | Large Player Flames 1 | $5b546 | 64×64 |
![]() | Large Player Flames 2 | $5bd46 | 64×64 |
![]() | Large Player Flames 3 | $5c546 | 64×64 |
![]() | Vitruvius Man | $5cd46 | 64×64 |
![]() | Beast 3 | $5d546 | 64×64 |
![]() | Beast 4 | $5dd46 | 64×64 |
![]() | Beast 5 | $5e546 | 64×64 |
![]() | Beast 6 | $5ed46 | 64×64 |
![]() | Beast 7 | $5f546 | 64×64 |
![]() | Beast 2 | $5fd46 | 64×64 |
![]() | Beast 1 | $60546 | 64×64 |
![]() | Sun Winking – Index 56 | $60d46 | 32×26 |
![]() | Sun1 (title screen?) – Index 86 | $60ee6 | 64×64 |
![]() | Sun 2 | $616e6 | 64×64 |
![]() | Sun 3 | $61ee6 | 64×64 |
![]() | Sun 4 | $626e6 | 64×64 |
![]() | Sun1 (in Constellation Won?) – Index 55 | $62ee6 | 64×64 |
| Tarot 0 | $636e6 | 32×64 | |
| Tarot 1 | $63ae6 | 32×64 | |
| Tarot 2 | $63ee6 | 32×64 | |
| Tarot 3 | $642e6 | 32×64 | |
| Tarot 4 | $646e6 | 32×64 | |
| Tarot 5 | $64ae6 | 32×64 | |
| Tarot 6 | $64ee6 | 32×64 | |
| Tarot 7 | $652e6 | 32×64 | |
| Glyph 0 – Index 10 | $656e6 | 16×15 | |
| Glyph 1 – Index 11 | $65704 | 16×15 | |
| Glyph 2 – Index 12 | $65722 | 16×15 | |
| Glyph 3 – Index 13 | $65740 | 16×15 | |
| Glyph 4 – Index 14 | $6575e | 16×15 | |
| Glyph 5 – Index 15 | $6577c | 16×15 | |
| Glyph 6 – Index 16 | $6579a | 16×15 | |
| Glyph 7 – Index 17 | $657b8 | 16×15 | |
| Glyph 8 – Index 18 | $657d6 | 16×15 | |
| Glyph 9 – Index 19 | $657f4 | 16×15 | |
| Small Glyph 0 – Index 57 | $65812 | 8×8 | |
| Small Glyph 1 – Index 58 | $65832 | 8×8 | |
| Small Glyph 2 – Index 59 | $65852 | 8×8 | |
| Small Glyph 3 – Index 60 | $65872 | 8×8 | |
| Small Glyph 4 – Index 61 | $65892 | 8×8 | |
| Small Glyph 5 – Index 62 | $658b2 | 8×8 | |
| Small Glyph 6 – Index 63 | $658d2 | 8×8 | |
| Small Glyph 7 – Index 64 | $658f2 | 8×8 | |
| Small Glyph 8 – Index 65 | $65912 | 8×8 | |
| Small Glyph 9 – Index 66 | $65912 | 8×8 | |
| Glyph A – Index 20 | $65952 | 16×16 | |
| Glyph B – Index 21 | $659d2 | 16×16 | |
| Glyph C – Index 22 | $65a52 | 16×16 | |
| Glyph D – Index 23 | $65ad2 | 16×16 | |
| Glyph E – Index 24 | $65b52 | 16×16 | |
| Glyph F – Index 25 | $65bd2 | 16×16 | |
| Glyph G – Index 26 | $65c52 | 16×16 | |
| Glyph H – Index 27 | $65cd2 | 16×16 | |
| Glyph I – Index 28 | $65d52 | 16×16 | |
| Glyph J – Index 29 | $65dd2 | 16×16 | |
| Glyph K – Index 30 | $65e52 | 16×16 | |
| Glyph L – Index 31 | $65ed2 | 16×16 | |
| Glyph M – Index 32 | $65f52 | 16×16 | |
| Glyph N – Index 33 | $65fd2 | 16×16 | |
| Glyph O – Index 34 | $66052 | 16×16 | |
| Glyph P – Index 35 | $660d2 | 16×16 | |
| Glyph Q – Index 36 | $66152 | 16×16 | |
| Glyph R – Index 37 | $661d2 | 16×16 | |
| Glyph S – Index 38 | $66252 | 16×16 | |
| Glyph T – Index 39 | $662d2 | 16×16 | |
| Glyph U – Index 40 | $66352 | 16×16 | |
| Glyph V – Index 41 | $663d2 | 16×16 | |
| Glyph W – Index 42 | $66452 | 16×16 | |
| Glyph X – Index 43 | $664d2 | 16×16 | |
| Glyph Y – Index 44 | $66552 | 16×16 | |
| Glyph Z – Index 45 | $665d2 | 16×16 | |
| Glyph copyright – Index 85 | $66652 | 16×16 | |
| Glyph dot – Index 46 | $666d2 | 16×16 | |
| Glyph dash – Index 47 | $66752 | 16×16 | |
| Glyph colon – Index 48 | $667d2 | 16×16 | |
![]() | Hi-Score Ouija Board (top) – Index 49 | $66852 | 88×67 |
![]() | Hi-Score Ouija Board (left) – Index 50 | $684d7 | 32×63 |
![]() | Hi-Score Ouija Board (right) – Index 51 | $68e7e | 32×63 |
![]() | Hi-Score Ouija Board (bottom) – Index 52 | $6a059 | 88×66 |
![]() | Hi-Score scroll bar – Index 53 | $6a059 | 48×15 |
| Hi-Score scroll top (shadow) – Index 94 | $6a343 | 48×8 | |
![]() | Hi-Score scroll background – Index 54 | $6a412 | 48×20 |
![]() | Hi-Score scroll bottom – Index 95 | $6a5d7 | 48×7 |
![]() | Grave Headstone Anim 1 – Index 67 | $6a698 | 16×6 |
![]() | Grave Headstone Anim 2 – Index 68 | $6a740 | 16×18 |
![]() | Grave Headstone Anim 3 – Index 69 | $6a908 | 16×32 |
![]() | Grave Headstone Anim 4 – Index 70 | $6ac20 | 16×41 |
![]() | Grave Headstone Anim 5 – Index 71 | $6b010 | 16×48 |
![]() | Grave Grass – Index 72 | $6b4a8 | 16×18 |
![]() | Grave Gravestone top – Index 73 | $6b670 | 40×20 |
![]() | Grave Gravestone bottom – Index 74 | $6b9b7 | 64×53 |
| Colour palette 1 (in-game) | Colour palette 2 (devil) |
| Total chip memory: $80000 (512kb) Chip memory offset in savestate: $8AC Copperlist address in chip memory: $37750 Copperlist offset in savestate: $37FFC Copperlist 2 address in chip memory: $D528 Copperlist 2 offset in savestate: $DDD4 | Total chip memory: $80000 (512kb) Chip memory offset in savestate: $8AC Copperlist address in chip memory: $800 Copperlist offset in savestate: $10AC Copperlist 2 address in chip memory: $D4E0 Copperlist 2 offset in savestate: $DD8C |
| Chip Addr: Copper instructions ; Comments $37750: 00E0 0001 00E2 0000 ; BPL1PT = 0x00010000 $37758: 00E4 0001 00E6 1F40 ; BPL2PT = 0x00011F40 $37760: 00E8 0001 00EA 3E80 ; BPL3PT = 0x00013E80 $37768: 00EC 0001 00EE 5DC0 ; BPL4PT = 0x00015DC0 $37770: ffff fffe ; End of copperlist $37770: FFFF FFFE ; Wait for vpos >= 0xFF and hpos >= 0xFE | Chip Addr: Copper instructions ; Comments $00000800: 00E0 0001 00E2 0000 ; BPL1PT = 0x00010000 $00000808: 00E4 0001 00E6 1F40 ; BPL2PT = 0x00011F40 $00000810: 00E8 0001 00EA 3E80 ; BPL3PT = 0x00013E80 $00000818: 00EC 0001 00EE 5DC0 ; BPL4PT = 0x00015DC0 $00000820: 1801 FF00 ; Wait for vpos >= 0x18 and hpos >= 0x00 $00000824: 0180 0000 ; COLOR00 = 0x0000 $00000828: ffff fffe ; End of copperlist $00000828: FFFF FFFE ; Wait for vpos >= 0xFF and hpos >= 0xFE |
00: Hex: #000000 RGB (0, 0, 0) 01: Hex: #777777 RGB (119, 119, 119) 02: Hex: #779977 RGB (119, 153, 119) 03: Hex: #77BB99 RGB (119, 187, 153) 04: Hex: #FF0000 RGB (255, 0, 0) 05: Hex: #BB0000 RGB (187, 0, 0) 06: Hex: #FFDD00 RGB (255, 221, 0) 07: Hex: #DD9900 RGB (221, 153, 0) 08: Hex: #FFBB00 RGB (255, 187, 0) 09: Hex: #BB77FF RGB (187, 119, 255) 10: Hex: #BBBBBB RGB (187, 187, 187) 11: Hex: #55FFBB RGB (85, 255, 187) 12: Hex: #00BB00 RGB (0, 187, 0) 13: Hex: #770000 RGB (119, 0, 0) 14: Hex: #0000FF RGB (0, 0, 255) 15: Hex: #FFFFFF RGB (255, 255, 255) | ![]() 00: Hex: #000000 RGB (0, 0, 0) 01: Hex: #337777 RGB (51, 119, 119) 02: Hex: #779977 RGB (119, 153, 119) 03: Hex: #77BB99 RGB (119, 187, 153) 04: Hex: #FF0000 RGB (255, 0, 0) 05: Hex: #BB0000 RGB (187, 0, 0) 06: Hex: #FFDD00 RGB (255, 221, 0) 07: Hex: #DD9900 RGB (221, 153, 0) 08: Hex: #FFBB00 RGB (255, 187, 0) 09: Hex: #000000 RGB (0, 0, 0) 10: Hex: #000055 RGB (0, 0, 85) 11: Hex: #555577 RGB (85, 85, 119) 12: Hex: #DD0000 RGB (221, 0, 0) 13: Hex: #BB0000 RGB (187, 0, 0) 14: Hex: #FF0000 RGB (255, 0, 0) 15: Hex: #BB0000 RGB (187, 0, 0) |
| Colour palette 3 (eye) | |
| Total chip memory: $80000 (512kb) Chip memory offset in savestate: $D2C Copperlist address in chip memory: $37774 Copperlist offset in savestate: $384A0 Copperlist 2 address in chip memory: $D590 Copperlist 2 offset in savestate: $E2BC | |
| Chip Addr: Copper instructions ; Comments $00037774: 00E0 0001 00E2 8000 ; BPL1PT = 0x00018000 $0003777C: 00E4 0001 00E6 9F40 ; BPL2PT = 0x00019F40 $00037784: 00E8 0001 00EA BE80 ; BPL3PT = 0x0001BE80 $0003778C: 00EC 0001 00EE DDC0 ; BPL4PT = 0x0001DDC0 $00037794: ffff fffe ; End of copperlist $00037794: FFFF FFFE ; Wait for vpos >= 0xFF and hpos >= 0xFE | |
![]() 00: Hex: #000000 RGB (0, 0, 0) 01: Hex: #DDDDDD RGB (221, 221, 221) 02: Hex: #99DD00 RGB (143, 221, 0) 03: Hex: #55BB55 RGB (85, 187, 85) 04: Hex: #007700 RGB (0, 119, 0) 05: Hex: #333399 RGB (51, 51, 153) 06: Hex: #BB0000 RGB (187, 0, 0) 07: Hex: #DD0000 RGB (221, 0, 0) 08: Hex: #990000 RGB (153, 0, 0) 09: Hex: #550000 RGB (85, 0, 0) 10: Hex: #773300 RGB (119, 51, 0) 11: Hex: #995500 RGB (153, 85, 0) 12: Hex: #BB7700 RGB (187, 119, 0) 13: Hex: #DD9900 RGB (221, 153, 0) 14: Hex: #FFBB00 RGB (255, 187, 0) 15: Hex: #FFDD77 RGB (225, 221, 119) | ![]() 00: Hex: #000000 RGB (0, 0, 0) 01: Hex: #337777 RGB (51, 119, 119) 02: Hex: #779977 RGB (119, 153, 119) 03: Hex: #77BB99 RGB (119, 187, 153) 04: Hex: #FF0000 RGB (255, 0, 0) 05: Hex: #BB0000 RGB (187, 0, 0) 06: Hex: #FFDD00 RGB (255, 221, 0) 07: Hex: #DD9900 RGB (221, 153, 0) 08: Hex: #FFBB00 RGB (255, 187, 0) 09: Hex: #00000 RGB (0, 0, 0) 10: Hex: #000055 RGB (0, 0, 85) 11: Hex: #555577 RGB (85, 85, 119) 12: Hex: #999999 RGB (153, 153, 153) 13: Hex: #7799BB RGB (119, 153, 187) 14: Hex: #BBBBFF RGB (187, 187, 255) 15: Hex: #557799 RGB (85, 119, 153) |
The functions SUB_RenderText_2E47E and SUB_DrawToScreen_2E4F0 use a lookup table at $3402A, which is decoded as:
| Index | Graphic | Width | Height | Mode | Pointer | Notes |
|---|---|---|---|---|---|---|
| 0 | Eye of Infinity Stone Circle | $d | $c6 | $3 | $53d98 | Run-length encoding (via SUB_2E6CA) |
| 1 | StarMap Graphics – Star Wheel | $1 | $1e | $3 | $526f2 | Run-length encoding (via SUB_2E6CA) |
| 2 | StarMap Graphics – TileBackground | $1 | $1f | $3 | $528db | Run-length encoding (via SUB_2E6CA) |
| 3 | StarMap Graphics – Galaxy | $1 | $1c | $3 | $529f1 | Run-length encoding (via SUB_2E6CA) |
| 4 | StarMap Graphics – Comet | $1 | $18 | $3 | $52ad6 | Run-length encoding (via SUB_2E6CA) |
| 5 | StarMap Graphics – Star Pattern | $1 | $1a | $3 | $52b79 | Run-length encoding (via SUB_2E6CA) |
| 6 | $13 | $1 | $0 | $59b46 | Default glyph render | |
| 7 | Wicked Sarsen Stones | $11 | $43 | $3 | $578fa | Run-length encoding (via SUB_2E6CA); likely sprite/large overlay |
| 8 | Bird/Snake Energy/Time Meter | $0 | $5e | $3 | $53ae8 | Run-length encoding (via SUB_2E6CA) |
| 9 | (padding) | $0 | $0 | $0 | $00000 | (empty/ invalid entry) |
| 10 | Glyph 0 | $0 | $e | $ffff | $656e6 | Direct blit |
| 11 | Glyph 1 | $0 | $e | $ffff | $65704 | Direct blit |
| 12 | Glyph 2 | $0 | $e | $ffff | $65722 | Direct blit |
| 13 | Glyph 3 | $0 | $e | $ffff | $65740 | Direct blit |
| 14 | Glyph 4 | $0 | $e | $ffff | $6575e | Direct blit |
| 15 | Glyph 5 | $0 | $e | $ffff | $6577c | Direct blit |
| 16 | Glyph 6 | $0 | $e | $ffff | $6579a | Direct blit |
| 17 | Glyph 7 | $0 | $e | $ffff | $657b8 | Direct blit |
| 18 | Glyph 8 | $0 | $e | $ffff | $657d6 | Direct blit |
| 19 | Glyph 9 | $0 | $e | $ffff | $657f4 | Direct blit |
| 20 | Glyph A | $0 | $f | $0 | $65952 | Default glyph render |
| 21 | Glyph B | $0 | $f | $0 | $659d2 | Default glyph render |
| 22 | Glyph C | $0 | $f | $0 | $65a52 | Default glyph render |
| 23 | Glyph D | $0 | $f | $0 | $65ad2 | Default glyph render |
| 24 | Glyph E | $0 | $f | $0 | $65b52 | Default glyph render |
| 25 | Glyph F | $0 | $f | $0 | $65bd2 | Default glyph render |
| 26 | Glyph G | $0 | $f | $0 | $65c52 | Default glyph render |
| 27 | Glyph H | $0 | $f | $0 | $65cd2 | Default glyph render |
| 28 | Glyph I | $0 | $f | $0 | $65d52 | Default glyph render |
| 29 | Glyph J | $0 | $f | $0 | $65dd2 | Default glyph render |
| 30 | Glyph K | $0 | $f | $0 | $65e52 | Default glyph render |
| 31 | Glyph L | $0 | $f | $0 | $65ed2 | Default glyph render |
| 32 | Glyph M | $0 | $f | $0 | $65f52 | Default glyph render |
| 33 | Glyph N | $0 | $f | $0 | $65fd2 | Default glyph render |
| 34 | Glyph O | $0 | $f | $0 | $66052 | Default glyph render |
| 35 | Glyph P | $0 | $f | $0 | $660d2 | Default glyph render |
| 36 | Glyph Q | $0 | $f | $0 | $66152 | Default glyph render |
| 37 | Glyph R | $0 | $f | $0 | $661d2 | Default glyph render |
| 38 | Glyph S | $0 | $f | $0 | $66252 | Default glyph render |
| 39 | Glyph T | $0 | $f | $0 | $662d2 | Default glyph render |
| 40 | Glyph U | $0 | $f | $0 | $66352 | Default glyph render |
| 41 | Glyph V | $0 | $f | $0 | $663d2 | Default glyph render |
| 42 | Glyph W | $0 | $f | $0 | $66452 | Default glyph render |
| 43 | Glyph X | $0 | $f | $0 | $664d2 | Default glyph render |
| 44 | Glyph Y | $0 | $f | $0 | $66552 | Default glyph render |
| 45 | Glyph Z | $0 | $f | $0 | $665d2 | Default glyph render |
| 46 | Glyph dot | $0 | $f | $0 | $666d2 | Default glyph render |
| 47 | Glyph dash | $0 | $f | $0 | $66752 | Default glyph render |
| 48 | Glyph colon | $0 | $f | $0 | $667d2 | Default glyph render |
| 49 | Hi-Score Ouija Board (top) | $b | $43 | $3 | $66852 | Run-length encoding (via SUB_2E6CA); likely sprite/large overlay |
| 50 | Hi-Score Ouija Board (left) | $4 | $3f | $3 | $67b4f | Run-length encoding (via SUB_2E6CA) |
| 51 | Hi-Score Ouija Board (right) | $4 | $3f | $3 | $684d7 | Run-length encoding (via SUB_2E6CA) |
| 52 | Hi-Score Ouija Board (bottom) | $b | $42 | $3 | $68e7e | Run-length encoding (via SUB_2E6CA) |
| 53 | Hi-Score Scroll bar | $6 | $f | $3 | $6a059 | Run-length encoding (via SUB_2E6CA) |
| 54 | Hi-Score Scroll background | $6 | $14 | $3 | $6a412 | Run-length encoding (via SUB_2E6CA) |
| 55 | Sun1 (in Constellation Won?) | $3 | $3f | $0 | $62ee6 | Default glyph render |
| 56 | Sun Wink | $1 | $19 | $0 | $60d46 | Default glyph render |
| 57 | Glyph 0 (small) | $0 | $7 | $1 | $65812 | Handled in BitplaneMaskedWrite (mode 1 branch) |
| 58 | Glyph 1 (small) | $0 | $7 | $1 | $65832 | Handled in BitplaneMaskedWrite (mode 1 branch) |
| 59 | Glyph 2 (small) | $0 | $7 | $1 | $65852 | Handled in BitplaneMaskedWrite (mode 1 branch) |
| 60 | Glyph 3 (small) | $0 | $7 | $1 | $65872 | Handled in BitplaneMaskedWrite (mode 1 branch) |
| 61 | Glyph 4 (small) | $0 | $7 | $1 | $65892 | Handled in BitplaneMaskedWrite (mode 1 branch) |
| 62 | Glyph 5 (small) | $0 | $7 | $1 | $658b2 | Handled in BitplaneMaskedWrite (mode 1 branch) |
| 63 | Glyph 6 (small) | $0 | $7 | $1 | $658d2 | Handled in BitplaneMaskedWrite (mode 1 branch) |
| 64 | Glyph 7 (small) | $0 | $7 | $1 | $658f2 | Handled in BitplaneMaskedWrite (mode 1 branch) |
| 65 | Glyph 8 (small) | $0 | $7 | $1 | $65912 | Handled in BitplaneMaskedWrite (mode 1 branch) |
| 66 | Glyph 9 (small) | $0 | $7 | $1 | $65932 | Handled in BitplaneMaskedWrite (mode 1 branch) |
| 67 | Grave Headstone Anim 1 | $2 | $6 | $0 | $6a698 | Default glyph render |
| 68 | Grave Headstone Anim 2 | $2 | $12 | $0 | $6a740 | Default glyph render |
| 69 | Grave Headstone Anim 3 | $2 | $20 | $0 | $6a908 | Default glyph render |
| 70 | Grave Headstone Anim 4 | $2 | $29 | $0 | $6ac20 | Default glyph render |
| 71 | Grave Headstone Anim 5 | $2 | $30 | $0 | $6b010 | Default glyph render |
| 72 | Grave Grass | $2 | $12 | $0 | $6b4a8 | Default glyph render |
| 73 | Grave Gravestone top | $5 | $14 | $3 | $6b670 | Run-length encoding (via SUB_2E6CA) |
| 74 | Grave Gravestone bottom | $8 | $35 | $3 | $6b9b7 | Run-length encoding (via SUB_2E6CA); likely sprite/large overlay |
| 75 | StarMap tile background 1 | $1 | $1f | $5 | $52c34 | Run-length encoding (via SUB_2E6CA) |
| 76 | StarMap tile background 2 | $1 | $1f | $5 | $52d48 | Run-length encoding (via SUB_2E6CA) |
| 77 | StarMap Graphics – Cherub facing left | $1 | $1f | $3 | $52e5d | Run-length encoding (via SUB_2E6CA) |
| 78 | StarMap Graphics – Cherub facing right | $1 | $1f | $3 | $5304c | Run-length encoding (via SUB_2E6CA) |
| 79 | StarMap Graphics – Sexton | $1 | $1f | $3 | $531d2 | Run-length encoding (via SUB_2E6CA) |
| 80 | StarMap Graphics – Star pattern | $1 | $1f | $3 | $53337 | Run-length encoding (via SUB_2E6CA) |
| 81 | StarMap Graphics – Lizard up | $1 | $1d | $3 | $533c0 | Run-length encoding (via SUB_2E6CA) |
| 82 | StarMap Graphics – Lizard down | $1 | $1d | $3 | $53572 | Run-length encoding (via SUB_2E6CA) |
| 83 | StarMap Graphics – Seamonster facing right | $1 | $1e | $3 | $53723 | Run-length encoding (via SUB_2E6CA) |
| 84 | StarMap Graphics – Seamonster facing left | $1 | $1e | $3 | $5390c | Run-length encoding (via SUB_2E6CA) |
| 85 | Glyph Copyright Symbol | $0 | $f | $0 | $66652 | Default glyph render |
| 86 | Sun1 | $3 | $3f | $0 | $60ee6 | Default glyph render |
| 87 | Bottom of Eye of Infinity bottom eyelid | $3 | $f | $3 | $522dc | Run-length encoding (via SUB_2E6CA) |
| 88 | $0 | $d | $6 | $52452 | Masked rendering (via PrepareMaskedRendering_2E594) | |
| 89 | Eye Arrow Marker? | $0 | $d | $6 | $524c2 | Masked rendering (via PrepareMaskedRendering_2E594) |
| 90 | Eye Arrow Marker? | $0 | $d | $6 | $52532 | Masked rendering (via PrepareMaskedRendering_2E594) |
| 91 | Eye Arrow Marker? | $0 | $d | $6 | $525a2 | Masked rendering (via PrepareMaskedRendering_2E594) |
| 92 | Eye Arrow Marker? | $0 | $d | $6 | $52612 | Masked rendering (via PrepareMaskedRendering_2E594) |
| 93 | Eye Arrow Marker? | $0 | $d | $6 | $52682 | Masked rendering (via PrepareMaskedRendering_2E594) |
| 94 | Hi Score scroll top (shadow) | $6 | $8 | $3 | $6a343 | Run-length encoding (via SUB_2E6CA) |
| 95 | Hi Score scroll bottom | $6 | $7 | $3 | $6a5d7 | Run-length encoding (via SUB_2E6CA) |
Modes 3 and 5 involve RLE decoding, then plane assembly for interleaved [p0,p1,p2,p3] words.
Rendering the data
All graphics are stored for 4 bitplanes (16 palette indices). Per table row:
Width: Number of 16-pixel cells for 16-bit formats (modes 0, 2, 3, 5, FFFF), or number of 8-pixel cells for byte-masked mode (mode 1). If the field is 0, treat it as 1 cell.
Height: Number of rows. Use it as-is (do not add 1).
Mode: Selects how to decode the bytes into 4 bitplanes.
Note on Palettes: Wicked uses multiple palettes depending on the game context (e.g., UI, sprites, backgrounds). The rendered colors may vary if the wrong palette is applied to an index.
Mode 0000, Mode 0002, and Mode 0006
Format: 4 planes, 16 pixels wide per cell.
Per cell per row: 8 bytes = 4 words in this order: P0, P1, P2, P3.
Row layout: Left-to-right cells, then next row.
Pixel color index at x: (P0>>bit)&1 | (((P1>>bit)&1)<<1) | (((P2>>bit)&1)<<2) | (((P3>>bit)&1)<<3) with bit 15 at the leftmost pixel.
Resulting width: 16 * Width (or 16 if Width is 0).
Notes:
- Mode 0002 uses a straight copy path in the code, but the data layout is the same as mode 0000 for offline rendering.
- Mode 0006 uses a masked write, but the source words are still P0, P1, P2, P3 per cell for offline rendering.
- Example: Index 67 (mode 0, Width=0001, Height=0006) renders as a 32×7 pixel sprite.
Mode FFFF (Direct Blit)
Format: 1-bit bitmap (single plane).
Per row per cell: 1 word. Set plane P0 to the word, P1..P3 zero.
Resulting width: 16 * Width (or 16 if Width is 0).
Notes: Used for large glyphs (e.g., indices 10–19 for digits 0–9, 16×15 pixels).
Mode 0001 (Byte-Masked)
Format: 4 planes, 8 pixels wide per cell.
Per cell per row: 4 bytes in this order: B0, B1, B2, B3 mapping to planes P0..P3.
Pixel color index at x: (B0>>bit)&1 | (((B1>>bit)&1)<<1) | (((B2>>bit)&1)<<2) | (((B3>>bit)&1)<<3) with bit 7 at the leftmost pixel.
Resulting width: 8 * Width (or 8 if Width is 0).
Notes: Used for small 8×8 glyphs (e.g., indices 57–66 for digits 0–9).
Mode 0003 and Mode 0005 (Stream with RLE and Reformat)
The data stream begins with two control bytes: C_hi and C2.
Decode bytes until you hit the special stop marker:
- If (byte & 0xF0) != C_hi: Treat the byte as a literal and append it.
- Else read a count L:
- If L == 0: The original byte is literal, append it.
- Else let N = byte & 0x0F:
- N == 0: Append L zero bytes.
- N == 1: Stop the RLE pass and go to reformat step.
- N > 1: Append N copies of byte L.
Reformat step (offline simplification that matches the draw code outcome):
- Let U = Width * Height (using rules at top; Width of 0 means 1).
- Interpret the decoded byte buffer as 4 contiguous blocks of U words (big-endian), i.e., P0[0..U-1], P1[0..U-1], P2[0..U-1], P3[0..U-1]. Pad with zeros if short.
- For cell i (row-major): Use words P0[i], P1[i], P2[i], P3[i] to render 16 pixels as in Mode 0000.
Note: The in-game routine has an extra mirroring copy for some effects; the block interleave above produces the correct final pixels for offline rendering. Example: Index 75 (mode 5, Width=0001, Height=001f) renders a 32×31 pixel sprite.
Text Renderer Notes (SUB_RenderText_2E47E)
Each printed character advances by 8 in X.
Newline is a literal ‘#’: X resets to start of line and adds 0x0AA0 to Y.
Special remaps: ‘.’ to 0x002E, ‘-‘ to 0x002F, ‘:’ to 0x0030.
Assumptions and Cross-Checks
Mode 0003 and 0005: Use the stream RLE followed by the 4-block interleave described above.
Width, Height, Mode: Taken from per-index comments in the data file or fallback metadata. For glyph blocks (e.g., indices 20–48), Mode 0000 and Height=000e produce correct 15-row glyphs.
Mode FFFF: Entries are 1-bit words per row, decoding as expected for simple shapes (e.g., indices 10–19, large digits 0–9).
Mode 0001: Decodes to 8-pixel-wide cells using 4 bytes per cell (e.g., indices 57–66, small digits 0–9).
Mode 0002 and 0006: Use the same per-cell P0..P3 word layout for offline decode.
Python Wicked Graphics Export Tool
This python code was used to decode the graphics, and a text file called graphicsdata.txt in the same directory as this code of the disassembled area of memory containing the graphics in the graphics table at $3402A with a comment after each label to explain the width, height and mode for that entry in the table. For example:
GraphicsIndex_87:
; Graphics Index 87, Width=0003, Height=000f, Mode=0003, Notes=Special case (via SUB_2E6CA)
DC.L $a0010263,$80000001,$ffc00130,$7c00001f ;522dc
(etc.)
It also requires Pillow (pip install Pillow) to be installed so that you can save the graphics out as .png files.
Run the program with
python wicked_renderer.py
or
python wicked_renderer.py –scale 2
(for outputting graphics at 2x size)
import struct
from PIL import Image
import re
import argparse
# Wicked palette (default)
WICKED_PALETTE = [
(0, 0, 0), # 0: #000000
(51, 119, 119), # 1: #337777
(119, 153, 119),# 2: #779977
(119, 187, 153),# 3: #77BB99
(255, 0, 0), # 4: #FF0000
(187, 0, 0), # 5: #BB0000
(255, 221, 0), # 6: #FFDD00
(221, 153, 0), # 7: #DD9900
(255, 187, 0), # 8: #FFBB00
(0, 0, 0), # 9: #000000
(0, 0, 85), # 10: #000055
(85, 85, 119), # 11: #555577
(153, 153, 153),# 12: #999999
(119, 153, 187),# 13: #7799BB
(187, 187, 255),# 14: #BBBBFF
(85, 119, 153) # 15: #557799
]
# UI palette for cursors (indices 88–93, if needed)
WICKED_PALETTE_UI = [
(0, 0, 0), # 0: Black #000000
(255, 255, 255),# 1: White #FFFFFF
(255, 255, 255),# 2: White #FFFFFF
(255, 255, 255),# 3: White #FFFFFF
(255, 255, 255),# 4: White #FFFFFF
(0, 0, 0), # 5: Black #000000
(0, 0, 0), # 6: Black #000000
(0, 0, 0), # 7: Black #000000
(0, 0, 0), # 8: Black #000000
(0, 0, 0), # 9: Black #000000
(0, 0, 0), # 10: Black #000055
(0, 0, 0), # 11: Black #555577
(0, 0, 0), # 12: Black #999999
(0, 0, 0), # 13: Black #7799BB
(0, 0, 0), # 14: Black #BBBBFF
(0, 0, 0) # 15: Black #557799
]
def parse_assembler_data(data_text):
"""Parse Amiga Assembler data (DC.L, DC.W, DC.B, DS.L) into byte arrays."""
indices = {}
current_index = None
current_data = bytearray()
width, height, mode = None, None, None
lines = data_text.splitlines()
line_idx = 0
while line_idx < len(lines):
line = lines[line_idx]
orig_line = line
line = line.split(';', 1)[0].strip()
if not line:
line_idx += 1
continue
# Check for any label ending with a colon, excluding assembler directives
if line.endswith(':') and not any(line.startswith(d) for d in ['DC.L', 'DC.W', 'DC.B', 'DS.L']):
if current_index is not None:
indices[current_index] = {
'data': bytes(current_data),
'width_minus_1': width,
'height_minus_1': height,
'mode': mode
}
current_index = line[:-1] # Remove the colon
current_data = bytearray()
# Look for metadata in the next line
if line_idx + 1 < len(lines):
next_line = lines[line_idx + 1]
match = re.search(r'(Width|Depth|W)=([0-9a-fA-F]+)[\s,;:]*(Height|Depth|H)=([0-9a-fA-F]+)[\s,;:]*(Mode|M)=([0-9a-fA-F]+)', next_line, re.IGNORECASE)
if match:
width = int(match.group(2), 16)
height = int(match.group(4), 16)
mode = int(match.group(6), 16)
if mode == 0xFFFF or mode == -1:
mode = -1
print(f"Parsed metadata for {current_index}: Width={width:04x}, Height={height:04x}, Mode={mode:04x}")
else:
print(f"Warning: Could not parse metadata for {current_index}: {orig_line}")
print(f"Next line: {next_line}")
width, height, mode = None, None, None
else:
print(f"Warning: Could not parse metadata for {current_index}: {orig_line}")
print("No next line available")
width, height, mode = None, None, None
elif 'DC.L' in line:
values_part = line.split('DC.L')[1].strip()
if values_part:
values = [v.strip() for v in values_part.split(',') if v.strip()]
for v in values:
try:
val = int(v.replace('$', '0x'), 16)
if mode == 6:
# Take first 4 bytes for mode 6, skip duplicates
current_data.extend(struct.pack('>I', val)[:4])
else:
current_data.extend(struct.pack('>I', val))
except ValueError as e:
print(f"Warning: Skipping invalid DC.L value '{v}' in line: {line}")
elif 'DC.W' in line:
values_part = line.split('DC.W')[1].strip()
if values_part:
values = [v.strip() for v in values_part.split(',') if v.strip()]
for v in values:
try:
val = int(v.replace('$', '0x'), 16)
current_data.extend(struct.pack('>H', val))
except ValueError as e:
print(f"Warning: Skipping invalid DC.W value '{v}' in line: {line}")
elif 'DC.B' in line:
values_part = line.split('DC.B')[1].strip()
if values_part:
values = [v.strip() for v in values_part.split(',') if v.strip()]
for v in values:
try:
val = int(v.replace('$', '0x'), 16)
current_data.append(val)
except ValueError as e:
print(f"Warning: Skipping invalid DC.B value '{v}' in line: {line}")
elif 'DS.L' in line and mode != 6: # Skip DS.L for mode 6
values_part = line.split('DS.L')[1].strip()
if values_part:
try:
count = int(values_part.replace('$', '0x'), 16)
current_data.extend([0] * count * 4)
except ValueError as e:
print(f"Warning: Skipping invalid DS.L count '{values_part}' in line: {line}")
line_idx += 1
if current_index is not None:
indices[current_index] = {
'data': bytes(current_data),
'width_minus_1': width,
'height_minus_1': height,
'mode': mode
}
return indices
def decode_wicked_bitmap(mode, width_minus_1, height_minus_1, data):
"""Decode bitmap for any mode."""
cols = width_minus_1 + 1
rows = height_minus_1 + 1
U = cols * rows
cell_size_px = 16 if mode in [0, 2, 3, 5, -1] else 8
expected_size = 8 * U if mode in [0, 2, 3, 5] else 4 * U
if mode in [3, 5]:
by_plane = bytearray()
pos = 0
if len(data) < 2:
print(f"Warning: Data too short for mode {mode}: {len(data)} bytes")
return bytearray(expected_size * 2)
ctrl_hi = data[0]
ctrl2 = data[1]
pos += 2
while pos < len(data):
b = data[pos]
pos += 1
if (b & 0xF0) == (ctrl_hi & 0xFF):
c = data[pos] if pos < len(data) else 0
pos += 1
n = b & 0x0F
if c == 0:
by_plane.append(b)
elif n == 0:
by_plane.extend([0] * c)
elif n == 1:
break
else:
by_plane.extend([c] * n)
else:
by_plane.append(b)
if len(by_plane) != expected_size * 2:
print(f"Warning: Decoded size {len(by_plane)} bytes, expected {expected_size * 2}")
if len(by_plane) < expected_size * 2:
by_plane.extend([0] * (expected_size * 2 - len(by_plane)))
else:
by_plane = by_plane[:expected_size * 2]
interleaved = bytearray(len(by_plane))
if ctrl2 != 0:
plane_size = 2 * U
for i in range(U):
base = i * 2
p0 = by_plane[base:base+2]
p1 = by_plane[plane_size + base:plane_size + base+2]
p2 = by_plane[2*plane_size + base:2*plane_size + base+2]
p3 = by_plane[3*plane_size + base:3*plane_size + base+2]
interleaved[i*8:i*8+8] = p0 + p1 + p2 + p3
else:
interleaved = by_plane
return interleaved
elif mode in [0, 2]:
if len(data) < expected_size * 2:
print(f"Warning: Data size {len(data)} bytes, expected {expected_size * 2}")
data = data + bytearray(expected_size * 2 - len(data))
elif len(data) > expected_size * 2:
print(f"Warning: Data size {len(data)} bytes, expected {expected_size * 2}")
data = data[:expected_size * 2]
return data
elif mode in [1, 6]:
if len(data) < expected_size:
print(f"Warning: Data size {len(data)} bytes, expected {expected_size}")
data = data + bytearray(expected_size - len(data))
elif len(data) > expected_size:
print(f"Warning: Data size {len(data)} bytes, expected {expected_size}")
data = data[:expected_size]
if mode == 6:
print(f"Mode 6 data for index (truncated): {list(data[:56])}") # Debug first 56 bytes
return data
elif mode == -1:
expected_size = 2 * U * 4
if len(data) < expected_size // 4:
print(f"Warning: Data size {len(data)} bytes, expected {expected_size // 4}")
data = data + bytearray((expected_size // 4) - len(data))
elif len(data) > expected_size // 4:
print(f"Warning: Data size {len(data)} bytes, expected {expected_size // 4}")
data = data[:expected_size // 4]
return data
else:
raise ValueError(f"Unsupported mode: {mode}")
def simulate_masked_render(interleaved, mode, cols, rows, bitplanes):
"""Simulate masked rendering for modes 0, 1, 2, 3, 5, 6."""
cell_bytes = 8 if mode in [0, 2, 3, 5] else 4
cell_px = 16 if mode in [0, 2, 3, 5] else 8
stride = 40
if mode == 6:
print(f"Mode 6 bitplane input: {list(interleaved[:56])}") # Debug input
for row in range(rows):
line_offset = row * stride
for col in range(cols):
cell_data = interleaved[(row * cols + col) * cell_bytes : (row * cols + col + 1) * cell_bytes]
mask = 0
for i in range(4):
try:
val = struct.unpack('>H', cell_data[i*2:i*2+2])[0] if mode in [0, 2, 3, 5] else cell_data[i]
mask |= val
except (struct.error, IndexError):
print(f"Warning: Failed to unpack cell data at row {row}, col {col}, mode {mode}")
val = 0
mask = ~mask & ((1 << cell_px) - 1)
for p in range(4):
plane_offset = line_offset + col * (cell_px // 8)
try:
if mode in [0, 2, 3, 5]:
dest_val = struct.unpack('>H', bitplanes[p][plane_offset:plane_offset+2])[0]
dest_val &= mask
new_val = struct.unpack('>H', cell_data[p*2:p*2+2])[0]
dest_val |= new_val
bitplanes[p][plane_offset:plane_offset+2] = struct.pack('>H', dest_val)
else:
dest_val = bitplanes[p][plane_offset]
dest_val &= mask & 0xFF
new_val = cell_data[p]
dest_val |= new_val
bitplanes[p][plane_offset] = dest_val
except (struct.error, IndexError):
print(f"Warning: Failed to unpack bitplane data at plane {p}, offset {plane_offset}")
continue
return bitplanes
def simulate_direct_render(data, cols, rows, bitplanes):
"""Simulate direct rendering for mode -1."""
stride = 40
U = cols * rows
for p in range(4):
plane_data = data[p * (U * 2) : (p + 1) * (U * 2)]
for row in range(rows):
offset = row * stride
try:
word = struct.unpack('>H', plane_data[row * 2 : row * 2 + 2])[0] if len(plane_data[row * 2 : row * 2 + 2]) == 2 else 0
bitplanes[p][offset:offset+2] = struct.pack('>H', word)
except struct.error:
print(f"Warning: Failed to unpack plane {p}, row {row}, expected 2 bytes, got {len(plane_data[row * 2 : row * 2 + 2])}")
bitplanes[p][offset:offset+2] = struct.pack('>H', 0)
return bitplanes
def pixels_from_bitplanes(bitplanes, rows, width_px):
"""Extract 2D pixels from 4 bitplanes."""
stride = 40
pixels = []
for row in range(rows):
row_pixels = []
line_offset = row * stride
for px_byte in range(width_px // 8):
offset = line_offset + px_byte
try:
p0_byte = bitplanes[0][offset]
p1_byte = bitplanes[1][offset]
p2_byte = bitplanes[2][offset]
p3_byte = bitplanes[3][offset]
except IndexError:
print(f"Warning: Bitplane index out of range at offset {offset}")
p0_byte = p1_byte = p2_byte = p3_byte = 0
for bit in range(7, -1, -1):
index = ((p0_byte >> bit) & 1) + 2*((p1_byte >> bit) & 1) + 4*((p2_byte >> bit) & 1) + 8*((p3_byte >> bit) & 1)
row_pixels.append(index)
pixels.append(row_pixels)
if row < 14:
print(f"Pixel row {row}: {row_pixels[:8]}")
return pixels
def save_bitmap_to_png(pixels, filename, palette, index_name, scale_factor=1):
"""Save 2D pixel array as PNG, scaled by scale_factor."""
height = len(pixels)
width = len(pixels[0]) if pixels else 1
img = Image.new('P', (width, height))
# Use UI palette for cursor indices if needed (adjust if specific names are cursors)
selected_palette = WICKED_PALETTE_UI if index_name in ['88', '89', '90', '91', '92', '93'] else WICKED_PALETTE
img.putpalette([c for rgb in selected_palette for c in rgb])
for y in range(height):
for x in range(width):
img.putpixel((x, y), pixels[y][x])
# Scale image by scale_factor
scaled_img = img.resize((width * scale_factor, height * scale_factor), Image.NEAREST)
scaled_img.save(filename, 'PNG')
return width * scale_factor, height * scale_factor # Return scaled dimensions
# Parse command-line arguments
parser = argparse.ArgumentParser(description="Render Amiga graphics to PNG")
parser.add_argument('--scale', type=int, choices=[1, 2], default=1, help="Scale factor for output PNG (1 for normal size, 2 for double size)")
args = parser.parse_args()
# Parse assembler data
try:
with open('graphicsdata.txt', 'r') as f:
assembler_data = f.read()
except FileNotFoundError:
print("Error: 'graphicsdata.txt' not found in current directory")
exit(1)
indices = parse_assembler_data(assembler_data)
# Process each index
for index_name, info in indices.items():
mode = info['mode']
width_minus_1 = info['width_minus_1']
height_minus_1 = info['height_minus_1']
data = info['data']
if None in (mode, width_minus_1, height_minus_1):
print(f"Skipping Index {index_name}: Incomplete metadata (mode={mode}, width_minus_1={width_minus_1}, height_minus_1={height_minus_1})")
continue
cols = width_minus_1 + 1
rows = height_minus_1 + 1
width_px = cols * (16 if mode in [0, 2, 3, 5, -1] else 8)
try:
# Decode
decoded = decode_wicked_bitmap(mode, width_minus_1, height_minus_1, data)
# Initialize bitplanes
bitplanes = [bytearray(40 * rows) for _ in range(4)]
# Render
if mode in [0, 1, 2, 3, 5, 6]:
bitplanes = simulate_masked_render(decoded, mode, cols, rows, bitplanes)
else:
bitplanes = simulate_direct_render(decoded, cols, rows, bitplanes)
# Extract pixels
pixels = pixels_from_bitplanes(bitplanes, rows, width_px)
# Save PNG
filename = f"{index_name}_{args.scale}x.png"
scaled_width, scaled_height = save_bitmap_to_png(pixels, filename, WICKED_PALETTE, index_name, scale_factor=args.scale)
print(f"Saved {filename} ({scaled_width}x{scaled_height} pixels, mode {mode}, scaled {args.scale}x)")
# Print all rows for mode 6
if mode == 6:
for i, row in enumerate(pixels[:14]):
print(f"Index {index_name}, Row {i}: {row[:8]}")
except ValueError as e:
print(f"Error processing Index {index_name}: {e}")
except Exception as e:
print(f"Unexpected error in Index {index_name}: {e}")











































































































































































