How MD201602 works

The majority of this post will be on MD201602‘s DYCP scrollers since the logo movement relies on what is a technique which requires very little processing time; there are multiple copies of the bitmap in memory generated when the code starts, with each stored at a different character offset horizontally so the routine swinging the logos can take a value from the curve, strip the lower seven bits off for the hardware scroll register and use what’s left to select which version of the logo to use. No data is moved in realtime so this takes a couple of scanlines at most, have a look at lines 379 to 398 of the source code since that sets up the top logo, writing directly to the registers.

The DYCP scrollers themselves are… well, just traditional DYCPs really and I’ve tried to optimise things as much as possible without losing that “spirit” too, which is why the routine isn’t going to set a record! The character set is five pixels high and, similarly to the logos, converted on start up by a routine called font_xvert so the top pixel row of each character is in a contiguous table, then the second row and so on; this format allows any of the characters to be accessed quickly, with the renderer for the first scroll drawn looking like this:

ldy dycp_cosinus,x
ldx dycp_buffer_1+$00
beq dd1_char_01
lda dycp_xfont+$000,x
sta dycp_workspace+$008,y
lda dycp_xfont+$100,x
sta dycp_workspace+$009,y
lda dycp_xfont+$200,x
sta dycp_workspace+$00a,y
lda dycp_xfont+$300,x
sta dycp_workspace+$00b,y
lda dycp_xfont+$400,x
sta dycp_workspace+$00c,y

The Y register is, appropriately enough, being used to designate the height that the character will be written into dycp_workspace (a standard character set arranged in columns of six characters on screen) whilst X is selecting which character to draw – the first line of every character is stored at dycp_xfont, the second line kept one 256 byte page on from that original position and the third, fourth and fifth are each a page further into memory than the one before them. The BEQ in the source fragment above means that if the content of the scrolling message in dycp_buffer_1 at that point is zero, the character is empty and the rendering can be skipped entirely. (And with that in mind, it probably isn’t difficult to understand why the greetings scroller is spaced out!)

Finally, there’s the colour bar in the middle of the screen using the same kind of vertical splitting that MD201509 employs and, to make the scrollers look like they’re passing behind it, the screen RAM on that central line of the five being used by the DYCPs is constantly being manipulated; if the character in the second scroller is using the first half of the cosine curve then the value written to that column on the central line of the screen is taken from line_cache (which is a copy of what’s initially generated at that point on the screen) but if the position is 128 to 255 then the routine writes a zero there instead which is always a blank character. Filling the first eight bytes of dycp_workspace which starts at $5000 with $FF from the VICE monitor or using an Action Replay cartridge once the code is executing will make that blank space visible and shows how this works more clearly, the solid blocks move across the screen and it looks like this:

So… that’s the basics at least and the source code is available to prod around too, but if anyone has a question please get in touch through the “usual channels”. I might also be persuaded to post source code for a more generic DYCP routine if enough people want it?

How MD201512 works

To be honest, there’s nothing going on in MD201512 that hasn’t been well documented online previously but we’ll go through it from top to bottom just because we can…

The picture is in AFLI format, that’s high resolution bitmap but with 8,000 bytes of colour RAM rather than the regular 1,000 and a routine which exploits a “feature” of the vertical smooth scroll, forcing the VIC-II to fetch colour data one every scanline instead of once every eighth. The image was drawn by prof4d (who is no doubt wondering why I’m so obsessed with his work since he was the artist behind the picture “borrowed” for MD201509 as well) and originated on the Spectrum. It was chosen primarily for being “wintery” without having anything specifically Christmassy going on and, more importantly, for featuring a snowman.

Conversion was handled by yet another cheap and cheerful, half-completed Windows-based utility which takes SCR files (which are essentially the raw binary of a Spectrum SCREEN$) and reworks the data into a format the C64 can deal with; said converter completely ignores the bright bit of each attribute cell but I’d rather fix that manually. The application of extra colour during conversion to AFLI was reasonably stubtle – by my standards at least – so there’s more PAL blending going on than anything else and those striped areas like the upper part of the sky should appear as solid colours on a real C64.

After the picture is out of the way the code gets to the scrolling message and this is a variation on the main part of MD201509 where the background colour is split several times across each scanline; what makes it different is that the unrolled loop producing those splits deliberately takes sixty four cycles rather than the sixty three available on a PAL scanline so the result is that the splits are “skewed” and produces that nice, almost plasma-like effect.

Finally there’s the sprites in the lower border and these are just moving around horizontally whilst colour registers changed once per scanline, recycling the colour table already in use for the scroller. There’s literally no time left on each scanline despite the “loop” already being unrolled, have a look for a comment saying “split the sprite colours” for this routine and it’s literally just load and store commands using all three registers with no padding between each line; yes it could have been optimised but there was no necessity to do so since it did what I wanted already!

How Septic works

Because it’s rather simple in the graphical department there aren’t many points of interest to that side of Septic apart from mentioning that the Apple II’s graphics are “backwards” compared to other 8-bits with the lowest used bit of a byte being the leftmost pixel; display RAM is arranged in an “interesting” manner too and it only uses seven bits per byte for pixels as well with the eighth selecting one of two colour pairs for the artifact-powered colour display. I wrote a very cheap and cheerful Windows bitmap to Apple II picture converter a while back which can sort of handle artifacted colour too, but that is currently nowhere near user friendly enough that I’d consider releaseing it for public consumption.

The character set didn’t even need the “specialist” tools though, it was just drawn seven pixels wide with one pixel gaps in reverse order, flipped horizontally in Promotion and shovelled into my regular bitmap to raw binary converter which can, amongst other things, write the data out in rows; the first 64 bytes of the binary file contain the top pixel line of each character in turn, the second 64 are the second line and so on. Look for the partially unrolled bmp_draw in the source code and it should hopefully demonstrate more clearly why having the font in this format is useful.

So having put the minimal graphics code aside we get to the meat of Septic, that one channel music driver. When making sound, the Apple II’s processor has to sit there and click the speaker at regular intervals so one relatively simple-to-follow means of achieving that is a loop along the lines of…

		ldx duration
		ldy pitch
sound_loop	lda speaker
		dey
		bne sound_loop
		dex
		bne sound_loop-$02

…where the LDA makes the speaker click, the Y register is used to time the gap between those clicks and X is a duration counter governing how many times the central loop is executed. The above code will work, but there’s a problem; the higher the pitch, the quicker the central loop runs and the shorter the duration of the note. To make something workable with this method, each possible frequency requires it’s own duration value for one “beat” of the music driver which would in turn be multiplied by the the number of beats that the note is actually going to play for!

So instead the sound loop in Septic was based on a web-found listing that popped up in a discussion at the Atari Age forums which has a better approach. Originally I disassembled the program which was POKEd into memory and meant to be called from BASIC to get some source code, but Anders Carlsson offered up his own, improved version so I migrated my driver over to his instead. That routine, called be_snd_on_play in includes/be_driver.asm, has a central loop which runs for whatever the duration is and within this loop a second timer counts down to zero and, when it hits that value, the speaker is clicked and the timer reset. All my code does after that is wrap a simple, one channel music driver around the sound generator and define a series of labels to make music entry easier. That data looks like this chunk of includes/be_mps.asm:

be_pattn_03	!byte g_2,dur01
		!byte g_1,dur05
		!byte off,dur04
		!byte g_3,dur01
		!byte g_2,dur05
		!byte off,dur04
		!byte g_2,dur01
		!byte g_1,dur05
		!byte off,dur04
		!byte g_3,dur01
		!byte g_2,dur05
		!byte off,dur04
		!byte g_2,dur01
		!byte g_1,dur05
		!byte off,dur04
		!byte g_3,dur01
		!byte g_2,dur05
		!byte off,dur04

		!byte g_2,dur01
		!byte g_1,dur04
		!byte ax2,dur01
		!byte ax1,dur04
		!byte g_3,dur01
		!byte g_2,dur01
		!byte g_2,dur01
		!byte g_1,dur05
		!byte off,dur02
		!byte eod

The label g_3 means a G in octave three, ax1 is an octave one A sharp, off rather unsurprisingly stops the current sound being played, dur04 is a duration of four “beats” (the actual meaning of which can which can be tweaked in the music driver itself) and eod is used to mark the end of the current pattern or track data. Assembling and testing Septic‘s code is more fiddly than the C64 or Atari 8-bit releases I’ve previously pushed to GitHub because the newly assembled binary needs to be manually pushed into a disk image but, in theory at least, it should be possible for someone else to pick up the driver, plug new data into it or possibly expand on what’s there.

There are a metric bucketload of improvements that could be made of course – options like multiple channels or more complex sounds are certainly possible – but I don’t have anywhere near the musical ability to do the required tuning to keep everything sounding right and currently lack the hardware for testing more involved sound engines. More importantly, the “plan” was to provide Apple II folks with something relatively simple to make music with that could perhaps be built upon, so hopefully that slightly tenuous goal was met.