A appearance at the tools, techniques and challenges of creating a retro-style 'crack intro'
Updated: October 12, 2026
Download Now View on pouet.net
In the delayed 80s, I spent an embarrassing figure of hours hunched complete my ZX Spectrum, typing in Sinclair BASIC programs and rudimentary assembler routines copied from magazines, trying to re-create the Amiga expert intros I’d seen circular at a friend’s house. Obviously, they never came equal slightly near - but now, many decades later, I’ve eventually managed it! I’m now the arrogant author of a hand-rolled retro demoscene intro that runs on a inventory 128Kb Spectrum. It features all those delayed 80s tropes: A gentle scrolltext (with ‘leet tradition fonts!), a three-channel AY soundtrack, bouncing VU meters and the essential parallax starfield. It’s currently 10287 bytes of code, or 19552 bytes as a complete strip depiction - which, stated I’ve been operating on this since December 13th final year, method my average charge of code-production is a paltry ~ 455 bytes per week!
This blog article is a specialized overview of how I got there, several chill tricks I picked up, and everything I had to study (and re-learn) concerning the Speccy and Z80 gathering alongside the way. It simultaneously rates as one of my favorite side-projects ever, and additionally the one that most frequently nearly triggered a ft put through my detect in frustration. I’ll indicate on all that at the end, but for now if you fair desire to see the completed product, the download clasp is at the top of the part and the included .tap cassette strip depiction should burden into any emulator. There’s additionally a YouTube capture, links to the manufacturing on pouet.net, and you can run it from inside a web browser by visiting my TNFS location portal - inspect choice 4 for latest uploads.
Be Kind, Rewind
But let’s skip rear briefly to the delayed 80s. Coming from the earth of an 8-bit, cassette strip loading computer, seeing an Amiga for the archetypal period was akin encountering item from external space. Like everyone who saw one rear in the day, I was blown distant by the amazing graphics and audio on display, not to citation the archive of ground-breaking games. But I established myself equal additional captivated by the mysterious (and totally badass) “intros” displayed at the commencement of pirated games.
Created by the software piracy groups that had cracked the copy safety and distributed the floppy-disk based games, they were mini plant of art in their own correct - music, graphics and normally several cool-looking consequence to display off the group’s prowess. There’s a awesome gathering of these intros (or “cracktros”) archived on YouTube.
Watch a few of those videos and you’ll quickly notice the “design” tropes that nearly all cracktro shares: a cool, edgy logo at the top of the display (bonus points if it’s dripping lifeblood or in any way “metal”), several arrogant content displayed on top of an consequence of several kind - normally a scrolling parallax starfield, or rotating 3d entity - and a additional scroll-text complete of cry outs, greetings to chap pseudonymous crackers and adverts for forbidden BBS systems galore. Needless to say, to a kid in the 80s/early 90s this was perchance the coolest item ever.
I idea this one was the complete bomb whenever I archetypal saw it. Sine-scroller, pirate BBS adverts, chill logo alongside a blade - and of course, a starfield. There’s item for everyone here.
The music from this is burned into my brain. I think beautiful much everyone of my classmates knew this intro stated the fame of Lemmings in the first 90s. Skid Row as a collection are motionless energetic to this day and this is likely the quintessential Amiga cracktro of the period.
Endless Piracy. Edgy. Another chill logo, sine-wave scroller, starfield and boasting text. The classics never go out of style.
Another awesome blend of all the essential ingredients. Cool logo, equal cooler content font, scrolling starfield and a scroll-text operating alongside the bottom. Vintage stuff.
The demoscene grew out of these intros. The cracking eventually faded into the background, and coders, “graphicians” and musicians all contributed to feature-length productions that showcased their abilities, contested alongside another groups and pushed the device to its limits. In amazing feats of ingenuity and clean “code as an art form”, the environment lives on today, alongside groups producing stunning productions akin this and this - withstand in mind, the two of those are operating on a 7Mhz residence device armed alongside a floppy disk, 1Mb of recollection and a audio part from 1985.
But much as I regard the sheer accomplishment and dedication that takes, it’s the first “single screen” scrolly-text-and-starfield cracktros I’ve continually established captivating. They took on a kind of mystical norm for me, hinting at a environment and a civilization I could lone always cognition from the exterior - which is exactly why I spent all those hours in Sinclair BASIC, trying in vain to re-create them, and dreaming of the day I’d eventually have an Amiga of my own.
The Jump Back
I eventually did commerce up to a range of Amiga systems, joined a few “scene” groups myself and threw myself into a variety of activities until exams and the end of my teenage years beckoned. I dabbled alongside Z80 code rear then, and at one item knew adequate 68k gathering to bluff my way through - partially from the Amiga’s heyday, partially from a few “embedded systems” modules at University anywhere I had to program a set of traffic lights or lift-controllers. Given there’s not much call for that kind of item at my $DAYJOB, though, it had all beautiful much faded from my memory.
I have kept operating on a bunch of projects in my spare time, and one of my favourites is my TNFS site - kind of an old-school BBS scheme or interactive “website” for the Spectrum, which grew out of a fun experiment to see how far I could shove Sinclair BASIC. It relies on a contemporary network cardstock fitted to the Spectrum, which is how I came to be chatting to “Desertkun”, another TNFS-protocol enthusiast. He’s now producing a awesome new WiFi network card for the Speccy, and was benevolent adequate to dispatch me a few of the prototypes to test out for him.
I was massively inspired by his hardware and low-level coding chops, and during one of our video calls anywhere we’d been comparing our betterment setups, he asked why I didn’t compose any Z80 gathering myself. A throw-away comment, but the difficulty was set! Now, I couldn’t visage re-writing the entire location in asm, but I figured I could create a self-contained intro to advance it in the manner of the first “BBStros”, and finally be among the chill kids alongside a environment manufacturing of my own :)
I mean, how difficult could it be ? (ooooh - foreshadowing!)
Dev Environment
My betterment surroundings through this was beautiful straightforward, and is the identical stack I use for evolving my TNFS site. I fair used a Makefile-driven build scheme which ran my code through the Pasmo assembler, my fork of zmakebas to build BASIC files, and the FUSE emulator for testing. For final evaluation on the genuine hardware, I uploaded my .tap records to my local TNFS server anywhere I could run them on my +3.
A few additional tools I have to call out since they ended up making a big difference - one is the amazing web-based “asm-editor” I discovered during looking up Z80 instructions. Not lone does this location have a awesome citation of all the Z80 instructions, but it additionally has an publishing company anywhere you can run example code and see how the recollection and/or assorted registers alter state. Take for example, the djnz education - you can build it and stage through the example iteration build one education at a time. Or try my example of an add education in the screenshot to see how the transport emblem plant whenever a bit gets shifted out of a register. Although it uses a slightly distinct syntax to Pasmo (so I couldn’t autumn my code immediately in it), it massively helped whenever manually strolling through things.
And lastly, likely the most important debugging tools I used on this project - a notepad and pen, and my two dogs (say “Hi!” to Biscuit & Pebble…) I’ll compose several additional concerning this in the wrap-up, but this really was a extremely different project for me, coming from decades of much higher-level languages and constructs. Most of my z80 code I lone always really understood following example out the bytes and bitmasks, keeping a array of enroll flags and example chunks of display recollection out by hand. And I established my evening walks alongside the two dogs provided ideal rubber duck exercises, although I’m not certain what they the two made of my prolonged mutterings concerning the hl enroll pair. Either way, debugging-by-dog is extremely recommended!
The Starting Point
So, let’s get started. I followed a awesome “Hello, world!” example from futurice.com to validate my build environment. Beyond that, the archetypal item I wanted to do was burden in my logo and backdrop depiction I’d use as a “stage” for the remainder of the planned intro. I wasn’t really certain what I was going to do at this point, fair that I wanted my logo at the top, and a scrolltext alongside the base row of the screen. As my TNFS location header already looked kinda demoscene-y, I made a quick edit putting in location two blue guide-bars inner which I’d put additional content.
A Spectrum SCREEN$ document (usually displayed as loading screens) is fair a dump of display memory, starting from byte 16384, and operating for 6912 bytes. The weird layout of this is extremely applicable later, but in command to display it all you need to do is burden your document into that recollection location. After a few hour’s activity of reviewing Speccy internals and following assorted guides, I had a extremely uncomplicated starting item for my demo that served as the basis for everything alternatively that came after.
org $8000 screen EQU $4000 ; location of the archetypal byte of display memory, 16384 in dec scrlen EQU 6912 ; total dimension of the display recollection inc. attributes ula EQU $FE ; I/O harbor for the ULA to authority the border black EQU 0 ; Spectrum color number, the exact identical ones BASIC uses start: ld a,black ; Set the border color to dreary using the ULA out (ula),a ; Copy my logo header image to display memory ld hl,header ; HL = location of my display document in this code ld de,screen ; DE = commencement of display memory ld bc,scrlen ; BC = 6912 bytes to copy ldir ; obstacle copy, see https://jnz.dk/z80/ldir.html ; occupied iteration - remainder of intro goes current :P hold: halt ; sleep until the next interrupt (1/50 second) jr hold ; afterward jump rear and sleep again, for ever header: incbin "assets/header.scr" ; my logo header/background end start
Not much to appearance at now, but this uncomplicated starting item introduced me to crucial concepts akin interfacing alongside the Spectrum’s hardware (ULA part to commencement with, and AY audio part later) via the out instruction, how to drag in binary records using Pasmo’s INCBIN directive, using the Z80’s obstacle copy ldir instruction, the use of register pairs for 16-bit addressing, and uncomplicated branching to labels.
Hello World
The next stage was to imprint item on the display and examine several of the Speccy’s built-in ROM routines, which are slightly analagous to syscalls on contemporary functioning systems. There are plentifulness of disassemblies and guides to the Spectrum ROM accessible on the internet, among the finest commented and connected one is at https://skoolkid.github.io/rom/. Using this as a citation alongside the assorted “Hello, world!” tutorials showed me how this kind of item is built up:
at EQU 22 ; "at" authority character used to stance the cursor row EQU 11 ; coordinates anywhere to imprint the message col EQU 9 prstring EQU $203C ; anywhere the pr_string regular lives in ROM ld de,msg ; DE = location of the archetypal character ld bc,msglen ; BC = how many characters to print call prstring ; imprint the lot ; occupied iteration is the same... msg: db at,row,col ; equal of "PRINT AT 11,9" in BASIC db "Hello, world!" msglen EQU $-msg ; 16 bytes in all, pasmo shortcut, $=program counter
I ended up mostly not using the ROM routines in my code as they dragged in all sorts of things I didn’t need, but it was massively helpful to comprehend how they worked as they introduced me to yet additional commonly-used constructs. For example, the PR_STRING regular defined here shows how you can build a iteration complete all character. If you’re fair starting out, I extremely propose going complete the 8 lines in that regular alongside a notepad and pen, operating out the contents of the registers and following the iteration constructs.
I additionally made a type of it in asm-editor you can try. If you click that link, you’ll see a type of the code loaded into the web editor. Click “Build”, and afterward stage through the code using the “Step” clasp - although no content volition be really displayed (I skipped complete the call to the Spectrum ROM “restart” vector that does this), you’ll see the assorted states of the registers and for example the BC enroll counting downward as all byte of the cord is processed.
Adding Some Style
Problem is, the Speccy’s built-in font is not exactly dripping alongside demoscene credibility. I wanted to use several stylish fonts and this turned out to be excellent uncomplicated - a quick hunt turned up Jim Blimey’s notable font collection, which included a ready-to-use set of assembler records and several instructions which shortly led me to these additions in my code:
; CHARS is the location the ROM's printing regular says to discover the font. ; Picking our own font is a matter of over-writing this chars EQU $5C36 ; from Jim's docs - POKE 23607=5C36 in hex ld hl,myfont-256 ; HL = font location - 256 as per docs ld (chars),hl ; from now on the ROM draws alongside it! ; remainder of code is the same myfont: include "textfont.asm" ; "Cartoon" font from jimblimey.com
There we go! Now that’s looking a small additional elite. Sorry, I average 31337. Strap in folks, we’re leaving lamer city… So at this point, a duration or so in to my dabblings and I’d started to get a sensible understanding of average idioms and was starting to awareness akin this was a project that power really go somewhere. The issue started whenever I moved on to trying to execute genuine demo effects: It became extremely apparent, extremely quickly, fair how much I didn’t cognize concerning how this all worked. Not to citation the fact that at this level, you’re really closely exposed to the bodily hardware intricacies, including the scheme decisions and trade-offs that the hardware designers had to create whenever operating alongside the extremely constricted specs of the first 1980s.
The archetypal specified issue - and the logic of likely 90% of my ache - is the Speccy’s extremely weird display layout. Rather than norm bitmapped display you power anticipate on additional contemporary hardware, it is alternatively divided into 3 sections, all alongside a set of interleaved rows (so if you keep adding 1 to a display address, eventually alternatively of reaching the next row, you’ll alternatively end up 8 rows down, and so on until you hit the base of the division and afterward iteration rear to the row underneath anywhere you started!) followed by a obstacle of “attribute” bytes which decide the colours of all 8x8 block. The trait blocks are the logic why all 8x8 character division can lone have two colours (a PAPER and INK attribute) which led to the garish “colour-clash” of spectrum games and distinctive manner anywhere artists try to keep inner 8x8 blocks as much as possible. But it was the layout of the bitmap part that is specified a intellect twister.
You can see this illustrated whenever a equivalent says in a loading display from tape. You can see all division built up alongside the interleaved rows one at a time, and afterward eventually the color and another attributes (like flashing ). There’s a lot of item at the awesome Break Into Program site. In the defence of the designers, they were operating alongside extremely constricted hardware and I don’t think the Speccy was always imagined as a games machine. When you commencement shuffling bytes around, you realise that this method is really optimised for displaying content which is neatly arranged into 8x8 character boundaries. For example, using this “scrambled” bit order, if you have a display location in the hl enroll pair, you can quickly get to the next pixel row of a stated location by merely incrementing the “high” byte. There’s a bunch of another “cheap moves” you can create by merely shifting bits around:
| Move | Instruction |
|---|---|
| Right 8 pixels | inc l |
| Left 8 pixels | dec l |
| Down 1 pixel line | inc h |
| Up 1 pixel line | dec h |
| Down 1 character row | add 32 to l |
It was a sensible choice for a device that would likely expend a lot of it’s period displaying BASIC program listings - it’s lone whenever you commencement trying to compose demo or sprite routines that it starts to awareness really weird. This additionally method you’ll beautiful quickly need a regular to change X,Y addresses to the applicable recollection addresses. Again, L Break Into Program has a helpful regular and if you disassemble additional or small any spectrum equivalent or demo, you’ll discover a type of this procedure somewhere. And yes, this all became horribly applicable afterward on…
Admittedly, not the most engaging display - but this was the archetypal period I had made item move on the Spectrum’s display alongside assembly! I watched this alongside pride additional times than I attention to acknowledge :)
I had a snazzy logo and “Hello World” text, but that doesn’t create for a extremely engaging intro! The archetypal consequence I wanted was the scrolltext - without that, it’s not really an intro. Now, the Amiga had all kinds of hardware sprites, blitter operations, tradition chips but the Spectrum had none of that. I was going to have to activity out how to do things the difficult way - “banging on the metal”, exactly by shifting bits about the display recollection by hand and having the CPU do all the work.
This is anywhere understanding the Speccy’s weird display and character division layout is so important. It turns out there’s a neat trick as lengthy as I remain inner one character row - there’s a RL instruction, which takes a byte and “rotates” it left. It inserts any is in the transport emblem into the rightmost bit, and any gets shifted out ends up in the transport bit again. That method if you cord a bunch of them together, you can move a set of pixels throughout the display - the instant they “fall off” one character cell, they end up in the transport emblem and afterward the next rl education inserts them at the commencement of the next character division to the left. Repeat 32 times, and you’ve fair moved a pixel (or set of pixels) throughout a row of the screen:
linestart EQU $4880 ; leftmost byte of the row 12 rows down lineend EQU linestart+31 ; rightmost byte pillar ($489F) linelen EQU 32 ; one entire row is 32 bytes (character cells) across dot EQU %00000001 ; the dot "shape", fair one pixel lit at the far correct end newdot: ld a,dot ; burden the dot form (only rightmost pixel lit) into a ld (lineend),a ; shop it in the rightmost byte of the line, pixel is now lit frame: halt ; delay for the next frame, alternatively it whizzes throughout too accelerated to see ld hl,lineend ; hl = final (rightmost) byte of the line ld b,linelen ; iteration counter, 32 bytes to activity through or a ; trick to apparent the transport flag, or against itself leaves a enroll ; unchanged but clears assorted bits including the transport flag ; see https://jnz.dk/z80/or_r.html shift: rl (hl) ; rotate this byte remaining (00000001 -> 00000010) dec hl ; item hl at the next byte to the left djnz shift ; iteration construct: b=b-1 and afterward jump rear to change tag until b=0 jr c,newdot ; now we've shifted the entire line, so if the transport emblem is set now it means ; the dot has fair remaining the screen, so we jump rear to the start jr frame ; alternatively it is motionless location on the screen, so keep shifting!
Hell yeah. It’s nearly a appropriate scrolltext now!
Wrapping this in another iteration that increments h all period (remember - that gets us moving downward a scanline) gets us moving an complete character cell. Here you can see it moving an complete row of my “Hello world” message. There was nevertheless a lot additional activity needed to create a appropriate scrolltext, alongside all letter appearing smoothly. I started off by fair printing an complete character at a period on the right-hand border of the display - it worked, but was beautiful ugly as you saw all new character appear at formerly in the right-hand flank of the screen, scrolling 8 pixels complete and afterward the next character flashed up. Instead, I wanted to create all character appear pixel by pixel and that required a bit additional bit juggling. The regular I ended up alongside efficiently streams all character in, row by line, pixel by pixel using the font data immediately alternatively of calling the ROM imprint character routine.
It does this by looking up the ASCII code for a character, and afterward fetching the corresponding font depiction into a 8-byte buffer, one byte for all row of data. The font data starts alongside the area character (character code 32), so a lookup is merely font code basis location + (ascii code - 32) * 8 to get to the bits for that letter.
Arranged into an 8x8 grid, the bits for the letter H in the font data appearance akin this. For all row of bits, I change the row remaining using a neat trick. The byte is loaded into the a register, and afterward by adding itself to itself - add a,a we change the value remaining (in binary, multiplying by 2 moves everything complete one bit) so that the pixel I desire to diagram ends up dropping into the transport flag. This is an idiomatic way of doing it in Z80 code as it’s faster and smaller than the sla instruction. And all sequence quickly adds up whenever you’re re-drawing 256 bytes all frame!
The character buffer afterward looks akin this, alongside the top row shifted. Now, alongside the hl enroll brace pointing at the accurate location on the screen, I can afterward use the rl trick discussed above. This shifts a scanline from the base character row of the display remaining by one pixel and draws my pixel in the rightmost stance by moving it in from the transport flag. Looping 32 times complete this moves the complete row by 1 pixel alongside my new pixel inserted at the right.
I afterward reiterate this stage again using inc h to move downward one scan row until the complete character pillar has been drawn. At this point, the character buffer looks akin this, and all 8 lines of the base display row have all been shifted remaining by one bit by looping complete all 32 character columns. The entire item afterward repeats another 7 times until the complete character has been smoothly scrolled onto the screen.
So fair to re-state this since I think it’s value the accent - fair to scroll a communication throughout the base of the screen, we’re having to iteration complete and re-draw 256 pixels all frame! It shows how much faster gathering is than the BASIC translator built into the spectrum’s ROM. I established it amazing to see my code eventually operating and imagining all the bits flying about and registers flapping away. It really makes me value fair how much attempt went into creating the games I played rear then.
The first Amiga component I converted. A appropriate 1980s ear-worm!
Of course, any fine intro needs several music. On the Amiga, the norm was the “tracker” component format, which has a lengthy and involved history of distinct forks, versions and tweaks. A opus is built up from a sequence of patterns, all made up of rows of instructions. Each education normally includes note or throw information, which example to perform and assorted parameters specified as volume, effects - or in the case of several instructions, which stance in the opus to jump to. In many ways, it’s nearly akin a programming tongue for music. Using ProTracker as an example, a representative note division is 4 bytes, shown on-screen as item akin C-2 01 C20. That breaks downward into three fields: the note and octave (C-2), the example figure (01), and the consequence and its indicator (C20 - in this case, “set the quantity to $20”, which is fractional of the maximum $40).
For an notable display of what’s imaginable alongside a tracker and to see what the note data looks akin on-screen, inspect out the activity of h0ffman, and in particular his winning “Professional Tracker” admission from the Revision 2014 competition. He’s an undisputed expert of the discipline (or art!) of tracking, and his “Secrets of Protracker” conversation gives a awesome overview of what’s possible.
The Amiga’s audio hardware far eclipsed item alternatively on the market at the time, and the ZX Spectrum is paltry by comparison. The first 48K models lone offered a rudimentary “beeper”, but the afterward 128K models included a 3-channel AY-3-8912 audio chip. This was extremely famous in 8-bit micros of the time, and a more-or-less identical type was additionally used in the Atari ST. While it lone offers essential waveforms and audio processing, it does allow multi-channel “chip music”, and so it became the backbone of the ZX Spectrum demoscene. It additionally spawned a assortment of SoundTracker clones on the Spectrum itself, although most musicians these days favor to activity in contemporary tools akin Vortex Tracker II, which runs natively under Windows.
The Conversion
Pex looking rear on his demos - this starts at the item anywhere he features my letter and plays a bit of my shield version!
For my intro, I wanted to harbor an Amiga component - and I had the ideal one in mind. One of my favorite Amiga music disks was “Sounds Of Gnome”, released in 1989 by the Swedish duo “Mahoney & Kaktus”. Mahoney additionally wrote NoiseTracker, among the archetypal really extensively used trackers on the Amiga, and the program that served as my teenage introduction to making device music of my own. Back in 2018 I made a rock cover of among the another tunes on that disk, and wrote to Mahoney (real name “Pex”) concerning it. He ended up showing my “music video” at a demo conference, which I am eternally grateful for! So it seemed lone fitting that I mangle another opus from that gathering alongside a Spectrum conversion… Sorry, guys! I picked the track “Milkman” which was written by “Glue Master”, an affiliate of Mahoney’s who had a few of his tracks featured on the compilation.
It’s an ideal track for converting: It’s abbreviated (only 7 patterns in total), most of those patterns portion the identical chord sequence, and it looked fairly uncomplicated to compression the first 4 channels downward into the Spectrum’s 3. Plus, it’s an incredibly catchy tune and I knew it fine - I’ve been listening to it for 35+ years and frequently jam alongside to it on guitar or bass. The conversion started off beautiful easily: all I really had to do was copy the patterns note-for-note from the Amiga mod, which I had open in OpenMPT as my Windows tracker. Below is a meeting alongside the two trackers open on the identical form - the first Amiga mod on the left, and my 3-channel Spectrum conversion on the right.
Note the 11.7 in the 3rd conduit of the Spectrum version. This is a “pitch glide down” consequence in Vortex Tracker syntax, which drops the throw by concerning a quarter-tone. It’s repeated on the next line, so the general consequence is a glide downward from the A# that leads in to the next bass note. The first Amiga mod does the identical item using its own syntax - those are the 202s you can see on the identical notes, at the base of the 4th track.
Using fair a sole “beep” audio for everything was adequate to inspect everything was working, and following a few weeks of hacking on it and re-arranging things in the evenings, I had a essential type which echoed amazingly fine for a archetypal draft:
I have to provision a particular citation to this forum article which was a enormous assistance getting me started. It’s likely the finest introduction to Vortex Tracker I’ve established - many, many gratitude to the first author for taking the period to compose it all up!
The genuine difficulty was making it audio as near to the first as possible. That meant construction up a archive of better-sounding samples for the distinct parts, and making extended use of Vortex Tracker’s “ornaments”. Vortex Tracker comes alongside a awesome choice of sounds, and fortunately I’m additionally a weirdo who likes to hear to chiptunes in the backdrop during I work! So whenever I established item particularly enticing, I submitted it distant for afterward pilfering inspiration. A Vortex Tracker example isn’t really a “sample” in the Amiga awareness of a recorded audio - it’s a definition of how a note sounds, one row per frame. Each row can mix in a spirit or the pale noise generator (useful for snare effects and suchlike), alongside alongside assorted another parameters.
Ornaments, on the another hand, provision you additional authority complete what throw a note plays - and this enables really helpful tricks akin “arpeggio chords”. For example, an ornament of 0,4 makes the conduit toggle quickly (25 times a second) between the note being played and the one 4 semitones complete it. It happens so quickly that it appears much akin two notes played at the identical time, which method you can build up full-sounding triad chords using lone two channels. Take the beginning chord in my completely converted version, Cm: It plays C in the archetypal conduit and the insignificant 3rd (D#) in the second, afterward I use an ornament to the second conduit to bring it up 4 semitones to the 5th (G). That gives all three notes of the chord, during keeping the 3rd conduit liberated for another parts.
As fine as making much additional productive use of the constricted 3 channels, this additionally gives Spectrum music that trait “warbling” sound, which I discover really appealing in a retro benevolent of way! Between tricks akin this and samples that mix tones alongside pale noise (to create a blended “bass-snare” sound, for example), I managed to condense the complete track downward to 3 channels. Many months later, I have to say I’m really pleased alongside the final result.
Playback
The first location that was residence to Vortex Tracker, records and another resources appears to be offline at the moment. I managed to catch a copy of everything I needed from archive.org - you’ll desire the PTxTools.7z document for the playback routines discussed here.
The playback was uncomplicated in theory: As this overview notes, you basically fair drag in the participant routine, contain your component document and call the playback regular formerly per example in your chief loop. The capture is, none of the origin code versions supplied in the archive worked alongside my Pasmo assembler. Fortunately, there is a pre-compiled binary entity shipped alongside the origin (VTII10bG in the PTxTools/PT3Play directory). This fair have to be loaded in at location $C000 - this cannot be changed, as it has many of complete addresses baked in.
My code to grip component playback now looks item akin this, alongside the binary component loaded in place:
; Define the commencement location of the participant regular and functions we need player EQU $C000 ; hard-coded org location of binary player plinit EQU player+3 ; init function in docs, component location in hl plplay EQU player+5 ; perform and move on one frame ; from the docs - this "analyzes PT3 component header and prepares ; corresponding note and quantity tables" ld hl,moduleimg call plinit ; chief loop hold: halt call plplay call scroll ; another effects / routines go here... jr hold moduleimg: incbin "assets/music.pt3"
Well, that didn’t activity as hoped. TNFStro - tortoise edition.
This brings me to the chief event. No self-respecting cracktro is complete without a starfield!
My first, naive method to the traditional starfield consequence was to dispersed random stars throughout the screen, afterward move downward all scan-line applying the identical rl trick I’d used for the scroll-text. I figured I could get parallax by shifting all row a distinct amount: One pixel for the archetypal row, two for the second, three for the third, afterward rear to one again, all the way downward the display to provision the belief of multiple “fields” of stars moving at distinct speeds.
It turned out there were two important problems alongside that approach. Firstly, shifting an complete row smeared any alternatively was on it alongside alongside the stars - which I’d expected, and had submitted distant as a issue for forthcoming me. The bigger matter was speed: by the period I’d completed the archetypal three rows I’d already performed hundreds of change operations, and it was nowhere near accelerated adequate to fit inner a sole frame. Everything crawled throughout the screen, and the music playback slowed downward so much that you could really comprehend the arpeggio ornaments warbling distant as distinct notes.
Clearly I was going to have to keep track of the idiosyncratic stars instead. I established this routine, which was more-or-less what I had in intellect and started me off alongside the idea of a construction to clasp the province of all star. It additionally uses a screen location lookup much akin the one from “Break Into Program”.
Dammit. Back alongside another burial dirge rendition :(
That got things moving, but the co-ordinate lookup and tracking proved costly to do (it has to run for all star, all example as fine as keep track of the old stance for erasing) and I’d been ignoring the bigger inquiry how to display material on top of the starfield. My first idea was merely to diagram the stars and afterward coat content complete them all frame, which blew the example prosperity fair as comprehensively as my archetypal attempt.
If I wanted item additional engaging than an bare starfield, I’d have to keep track of which cells were occupied by content and evade example stars in them in the archetypal place, fairly than over-painting them all time. After many of scribbling in my notepad and visualising rows of bits, I eventually solved the two challenges by “cheating” fairly liberally.
So now, I don’t create a random starfield at all. Instead, I built a array of pre-calculated Y and X stance pairs:
stdata: defb 109, 25, 88,182, 42, 96, 155,226 defb 59,254, 137,169, 160, 84, 125,193 ; ... and so on, for 32 stars ...
This saves faffing concerning alongside an RNG, and formerly I have a layout that looks fine I cognize it’ll continually appearance good. I can additionally “fudge” things and justify no two stars portion the identical y co-ordinate which sidesteps a entire category of problems akin stars sharing a scanline, and having to agreement alongside them overtaking all other.
The “random” layout is generated by a uncomplicated Ruby script:
#!/usr/bin/env ruby PAIRS = 32 PER_LINE = 4 # pairs per line Y_RANGE = 40..175 # exclude rows taken up by my logo & bars X_RANGE = 0..255 raise "y range too small" if Y_RANGE.size < PAIRS # no copy y values ys = Y_RANGE.to_a.sample(PAIRS) pairs = ys.map { |y| [y, rand(X_RANGE)] } pairs.each_slice(PER_LINE) do |row| body = row.map { |y, x| format('%3d,%3d,', y, x) }.join(' ') puts "defb #{body.chomp(',')}" end
When the regular initialises, I change those co-ordinate pairs into a array that tracks the province of all star. So fairly than storing a stance and translating it to a display location all frame, all of my celebrity records now looks akin this (offsets from the basis address):
STSCR EQU 0 ; display location of the byte the celebrity is in, e.g. $4123 STOCC EQU 2 ; a pointer to the "occupancy check" array cell STMASK EQU 4 ; the star's bit pattern, e.g. %00010000 STSTEP EQU 5 ; "speed" - num. of pixels this celebrity travels per frame
The key item is that there are no X/Y co-ordinates at all. For all celebrity I shop the display byte it sits in and the bit disguise inside that byte, so it’s already in the form the display writes need. That method no location conversion per frame, fair uncomplicated bit shifting and rotation.
Moving a celebrity is afterward deceased simple. I rotate its disguise remaining alongside rlca, and whenever the bit rotates out of bit 7, it lands rear in bit 0 alongside the transport emblem set. That’s the indication I need to move the celebrity one character division to the left, which is among the “cheap moves” from earlier.
Because I’m tracking which division all celebrity is in, the collision inspect comes nearly for free. That’s what the second pointer is for: STOCC (Star Occupancy) moves in stage alongside STSCR through a array holding one byte per character cell. The “typewriter” regular sets a byte to 1 for all division containing a content character or the cursor, so checking whether a celebrity is hidden rearward content is a sole or instruction. If the division is occupied, I skip example the celebrity but transport on rotating and strolling it alongside until it reaches a division anywhere it can be displayed:
ld l,(ix+STOCC) ld h,(ix+STOCC+1) ld a,(hl) ; A = the occupancy inspect for this cell or a ; test A against zero jr nz,stmove ; it's occupied, so depart the display alone
Parallax is motionless handled by moving all celebrity a distinct figure of pixels per frame, but the disbursal is a lot lesser this period as I’m lone touching the cells the stars really occupy, fairly than all byte of all row.
That’s looking additional akin it! The typewriter regular is a uncomplicated copy of the font data from memory, and is extremely cheap as we can use the “cheap moves” to quickly fetch the 8x8 block.
I additionally skip example any area character so it leaves the “occupancy cell” unmarked. This method the stars keep drifting through the gaps in the content - you can see current the odd celebrity passing in between the words. I switched to ALL CAPS penning for the final type which helps the consequence equal additional as the font is fairly blocky, so you don’t really notice stars skipping the spaces inner a glyph at all.
A few likelihood and ends that didn’t justify a division of their own…
VU Meters
Some kind of explanatory display bouncing alongside alongside the audio playback was another average first demo trope. I did initially regard a real-time explanatory equaliser, but that would have been far too complex, likely in conditions of spare cycles per frame, and definitely in conditions of my constricted mental capability at this point! So I settled for a uncomplicated set of bouncing meter bars instead.
It’s value a citation mostly since it gives me an excuse to nexus to the notable chibiakumas website! I fair affection the delayed 90s web scheme on display - one can contend that readability and accessibility obtain a hit, but it’s fair so much fun to encounter a location that motionless looks akin this. It’s a apparent labor of affection and there’s a prosperity of satisfied there as well, if your eyeballs are up to it.
Their guide on Making Sound alongside the AY-3-8910 was what showed me how to get the data I needed to diagram the meter bars. The examples compose a enroll figure to $FFFD to choose a register, and I realised that formerly a enroll is selected, I could use the in education to peruse its value rear out alternatively of penning a new one. As the leaf says, R8, R9 and R10 clasp the conduit amplitudes, so they nourish nicely into the height indicator for example a bar. Getting the amplitude for conduit A is afterward as uncomplicated as:
ld a,8 ; Channel A register call ayread ; ... remainder of code current ... ayread: ld bc,$FFFD ; Register choose port out (c),a in a,(c) ret
Twinkle, Twinkle, Little Star
This is a uncomplicated consequence that makes the header logo a small additional interesting. I’m not certain anywhere I initially got the idea - I’ve certainly seen item akin it on a few delayed 80s/early 90s demos, though. All I wanted was to choice random celebrity dots in the logo backdrop and enliven them.
Here’s the header depiction loaded in ZXPaintbrush alongside the character division grid showing. By blessed chance, there are four stars which all sit neatly inner a sole character division alongside adequate surrounding space, so they can be used to provision an animated “twinkle” consequence by example a cross out from the centre item and rear in again.
These dots map to the following co-ordinates, which mark the top scanline of all fully-grown celebrity (two pixels up from the centre):
twkdata: defb 1,5 ; char row 0, col 5, centre y=3 ink 5 defb 1,20 ; char row 0, col 20, centre y=3 ink 1 bright defb 26,7 ; char row 3, col 7, centre y=4 ink 4 defb 25,22 ; char row 3, col 22, centre y=3 ink 6 bright
The “twinkle” regular itself is uncomplicated enough: it adds a pixel on either axis to develop the star, waits a few frames, afterward shrinks it rear downward again. The engaging bit was how I made the twinkle command pseudo-random. Remember that my starfield was entirely pre-calculated from a data array generated by a Ruby script, which meant I’d never really needed a random figure generator in assembly. From Jonathan Cauldwell’s “How To Write Spectrum Games” e-book, I established another awesome “hack” which merely walks through the commencement of the Spectrum’s ROM, picking out random-looking bytes as it goes. As he says, he uses it in most of his games, and it does the job in a really uncomplicated way - as lengthy as you remain below the obstacle of structured, repeating data that sits 8Kb into the ROM.
twkrand: push hl ld hl,(twkseed) ld a,h and 31 ; keep it inside the archetypal 8k of ROM ld h,a ld a,(hl) ; the "random" byte inc hl ld (twkseed),hl pop hl ret
Out Of Space
About midway through penning the intro, I hit a fairly predictable problem: I had run out of memory. In an attempt to keep everything self-contained in one obstacle of code, I was pulling all of the resources (the display background, the participant binary, the music document etc.) into the program itself, and afterward using the ldir obstacle copy education to move all one into its accurate location. Since that meant efficiently storing all obstacle twice, and the participant regular had a hard-coded commencement location of $C000, I quickly ran out of area and my code started crashing into the playback routine. The simplest choice was to divided things out into distinct code blocks, burden all one immediately to the location it needed to live at, and cover the lot in a multi-stage BASIC strip loader:
10 CLEAR 32767 20 BORDER 0: PAPER 0: INK 0: CLS 30 LOAD "" CODE 40 LOAD "" CODE 50 LOAD "" CODE 60 RANDOMIZE USR 32768
This loads the chief program, afterward the participant regular immediately into $C000, and eventually the backdrop display direct into display memory. It does average the backdrop starts appearing before the demo has completely loaded, but as I ideate most group volition be seeing this operating in an emulator or on my TNFS site, it doesn’t really matter. Plus, at this item I was in danger of disappearing downward yet additional rabbit holes (I motionless really desire to activity out how to do multi-colour border effects!) and I’d never have got this item released.
This entire project is a really weird one to appearance rear at. On the one hand, I’ve fulfilled a younker ambition and have item I’m extremely arrogant of. It’s additionally beautiful different these days to end up alongside a binary anywhere you can say beautiful much what all sole byte is doing and the narrative of how it got there. But on the another hand - I now have an answer for why I don’t code in assembly: It’s since I value (what’s left) of my sanity! The item is, in command to compose code akin this you have to commencement thinking akin the device itself. You have to visualise the registers and the position flags flapping away, graphics as lines of bits slotted into different orders, loops and conditionals as binary logic and so on. Getting this near to the hardware certainly brought concerning a greater awareness of attachment to the hardware of my childhood, and it’s uncomplicated to see why gathering coders and sceners get so passionate concerning it. That said, I’m in no hurry to commencement re-writing my build of activity in assembly!
The projects I really bask are those that provision me an excuse to study item for the sheer hell of it, and to dilatory downward a small from the ever-increasing speed and demands of the contemporary activity world. Going rear to Z80 code is likely concerning the furthest I could have taken this, abbreviated of going into hardware hacking. I established it at times an intriguing puzzle, frequently a frustrating exercise that nearly had me throwing in the towel at multiple stages. This project has certainly taken the longest to go from the “idea” phase of any stated characteristic to any benevolent of meaningful output.
True, I didn’t expend all day of the final twelvemonth operating on it - my genuine coding period was likely lone a few hours complete a brace of evenings a week. But I’ve certainly been thinking concerning it and pondering it most days (as my dogs volition attest). The biggest breakthroughs and “aha!” moments normally didn’t arrive from rearward the keyboard; they came from scribbles in my notebook and thinking concerning processor states during doing item unrelated.
It has nevertheless stated me enormous appreciation and regard for the coders who drag this material off and create it appearance easy. And that goes twice for those kids and area coding superstars who managed all of this rear in the 80s, and had to activity it all out by test themselves. I lone managed to get this far since of many books, online tutorials, GitHub repositories alongside example code, YouTube videos… I can’t ideate how much longer this would have all taken me if the lone resources I had were my Spectrum, a cassette strip recorder and a notepad.
So, as I diagram a row under this project - for now! - I lift a glass, and in the words of h0ffman: “Shout out to the hackers, graphicians and protrackers.”
The opinions and views expressed on this website are my own and do not necessarily indicate the views of my employer, former or present.





















