Oldschool Gaming - reviewing new games on classic computers
The Hex Files :: Part 8 :: written by Jason Kelk :: added 11 Mar 2006

Okay, so last issue we were looking at $D018 and how it controls where our screen and character set data are held in the C64's memory and most of you (I hope) will have noticed that the highest place I said we could put a font was $3800 and the highest place for a screen $3C00, yes? Simiarly, because the sprite data pointers can only have a maximum value of $FF and each sprite is $40 bytes long, the last sprite is at $3FC0. This is because, the VIC-II chip arranges the memory into four chunks of 16K, on powerup, the C64 is pointing to video bank 0, which runs from $0000 to $3FFF, and at the screen at $0400 and a font at $1000. This, incidentally, is why our examples have all had music sitting at $1000, the VIC-II can't "see" this RAM under there for sprites, characters or screen data because a copy of the ROM character set is there, $1000 to $17FF for the upper case font and $1800 to $2000 for the lower.

The easiest way to imagine it is with two pieces of paper. The first has a piece of code written on it and is placed on a desk whilst the second has a picture drawn on it and is held a small distance above the first. We, pretending to be the processor here, can look at the first piece of paper by putting our head underneath the second, but the VIC-II looks straight down and from this angle the first is totally obscured. This technique is called "shadowing" and, although it appears to be adding a limitation to how we use the memory, it actually makes life easier for us in that we can still actually use the memory in some way rather than it being locked off for the characters. We'll come across more examples of shadowing in a different form in a later article.

But back to our initial line of thought, how can we use one of the other three blocks of 16K for our graphics? After all, if we use a bitmap picture (more on those a little later) we need about 9K of space for it, theres only a limited space in bank 0, what with $0000 to $0400 and $1000 to $2000 being unavailable to us. This is where $DD00 comes in to play. One of $DD00's jobs is handling this very problem, the lowest two bits are used to point VIC-II at the correct place. If we just put a value of $03 into $DD00 nothing happens because, oddly, $DD00 actually refers to the first bank as $03 and the last as $00. So if we want to use bank 1 ($4000 to $7FFF) we can do so by setting $DD00 with $02.

Changing into bank 1 is the equivilent of adding $4000 (16384) to all of our character, screen and sprite data pointers. If we just change into bank 1 with no other changes made to the VIC-II we end up looking at a screen at $4400 and a font at $5000. Since there is no shadow of the ROM font in bank 1 that means we just see a mess. And more importantly, we have the entire 16K to ourselves! And simliar rules apply to banks 2 and 3, except that bank 2 starts at $8000 and has a second copy of the ROM font at $9000 to $9FFF and bank 3 starts at $C000 and has the video chip sitting at $D000 to $E000. We can write to this memory, but it requires a little trickery and, since using it is a lot more complex, I won't cover this until later. But bank 1 is the most commonly used bank for graphics since it's all ours with absolutely no strings attached by the C64, so for now we're going to frolic in this new pasture and not worry about the rest.

Now we've got a play space, time to introduce something new to fill it with in the form of bitmaps. Remember I mentioned them last time as well? Right, first off, there are a number of different editors out there, all with different memory layouts. During the planning for these articles, there was some discussion about which would be best to use for our purposes and we've decided to use the format set down by the editor Vidcom. In general a bitmap is split into two parts, the actual picture (which is 8,000 bytes of data, since 40 characters across times 25 down times 8 bytes a character comes to 8,000) and we're only allowed to put this in either $4000 or $6000 in this bank (and the same rule applies to the other banks, the bitmap can only start at a multiple of $2000).

The second part is the colours, with a monocolour bitmap we get two colours every 8x8 pixel square of the picture and these can be represented by the two halves of a byte; $F4 for example will be light grey ($F) and purple ($4). This means we need 1,000 bytes of colour for a monocolour bitmap. Multicolour is different, we get the same system for defining two of the colours, but the $D800 colour map is also available as is the background colour, since multicolour bitmaps work in the same way as multicolour characters with two bits working together to make a colour value of $0 to $3. This means we need a total 2,000 bytes of colour data, one for the screen and the other for $D800. Vidcom arranges its files like this:

$5800 to $5BE7 - colour data for $D800 onwards.
$5C00 to $5FE7 - colour data for wherever the screen is.
$6000 to $7F3F - bitmap.

We're going to take advantage of this. Because any of the sixteen screens in bank 1 can be used as our screen we're not going to bother copying the data from $5C00 to $5FE7, oh no. We're going to tell the VIC-II to look there for it's screen, and by a coincidence... oh look, here's our colour data! Now, if you download and unzip the exsample data theres an example picture included called tropique.prg and a piece of source that I want you to open called pic_show.asm. It should look like this:

		.incbin tropique.prg

		*= $0900

; Black border and screen colours
		lda #$00
		sta $d020
		sta $d021

; Set VIC-II to bank 1
		lda #$02
		sta $dd00

; Turn on bitmap mode
		lda #$3b
		sta $d011

; Turn on multicolour mode
		lda #$18
		sta $d016

; Point the screen at $5C00 and the font at $6000
		lda #$78
		sta $d018

; Copy from $5800-$5BE7 to $D800-$dBE7
		ldx #$00
copycol		lda $5800,x
		sta $d800,x
		lda $5900,x
		sta $d900,x
		lda $5a00,x
		sta $da00,x
		lda $5ae8,x
		sta $dae8,x
		inx
		bne copycol

; Stop but don't exit to BASIC
loop		jmp loop

Start it up and, if it's all typed up correctly, a nice picture of a girl and some stone columns should appear! There's also a file in the archive called pic_demo.asm and, as the filename might suggest, this is a demo based on the previous code we've looked at and the picture viewer. This source isn't documented, but the majority of it is code we've already covered quite extensively so you should feel pretty comfortable with it - but there have been a few alterations made so they need a little explanation; one change makes the scroller faster and therefore more readable, the other extends the length of the message itself so it's no longer limited to 256 bytes.

Lets look at how the scroll is sped up first; open the pic_show.asm source and look for the label scrlloop (it should be line 127) and there is an LDY #$00 just before it; since the Y register isn't used during the scroll movement routine, it's being put to service as a counter to call that code more than once per frame. If you page onwards a little to the label dontmove, there's this;

		iny
		cpy #$03
		bne scrlloop

And that's what speeds the scroller up; the routine is called, Y gets incremented and we go back to scrlloop until Y reaches 3. Changing the value can speed up or slow down the scroller accordingly, so it's worth having a little play to see how it looks, on average moving two to three pixels a frame tends to be readable when the text is the size we're using here.

The other modification is a little more involved and before I start explaining how it works a little disclaimer is probably in order; the technique below is called "self modifying code" and is, generally speaking, considered bad practise by just about every programming course, teacher and book. However, it's very effective and faster to use most of the time and, since I use it personally in preference to the more fiddly "proper" methods, it's covered here.

Previously, we've been using a label called messcount to indicate where we are in the text, that has been totally removed for this code. Instead we have this routine after the loop to shift all the characters to the left:

messread	lda message
		bne textok
		jsr reset
		jmp messread

textok		sta $5c27

		inc messread+$01
		bne nohibyte
		inc messread+$02

So, looking at this new routine it reads from the label message (which is where the scroll text is stored, at $2800 in memory for this code), checks it's not a value of $00 and puts the new character onto the screen. If the accumulator does contain $00, a subroutine called reset is called and the code jumps back to the LDA again to get another byte. Now, some of you are thinking " hang on, that just reads the same byte over and over again" and on it's own you'd be right, the two INC commands after textok are what nudges the LDA on so it reads the text.

How does that work, then? Well, if we look into the memory where the LDA message is stored, it will look like this; $AD, $00, $28. The first byte $AD is the actual LDA command, whilst the second and third bytes are the address, in this case pointing to $2800 since the C64 insists on storing the lower byte of the address first. The two INCs change the address, the first one changes the lower byte (the one that starts as $00 in this case) upwards so the LDA itself steps through memory a byte at a time, whilst the second INC only takes effect at the point when that byte reaches $00 again; it bumps the higher byte of the address up so the scroller can go from $28ff to $2900. Finally, we need a little look at reset (near the end of the code) as well:

reset		lda #message
		sta messread+$02
		rts

This subroutine simply resets messread so that it points to the first byte of the text, the first LDA/STA pair sets the lower byte of the address, the second LDA/STA sets the higher, so the code at messread gets changed to $AD, $00, $28 regardless of what it was previously. This subroutine is also called a little before the CLI in the setup code to make sure that the program behaves itself if someone stops and restarts it and without that call in the setup the text would continue where it left off!

Have fun playing with the code, changing the speed of the scroller and so forth and generally seeing how things work. For the next installment we're going to be starting a whole new project. As usual, if you have any questions to ask then contact me and I'll see what I can do but, for now, goodbye!

The source code for the routines above can be downloaded here for easier reference.

Content copyright © 2004-2014 Oldschool Gaming     Designed and hosted by Enisoc Design