You might think we've finished, now that the graphics are set up, because that's all there is in Hello World. However, we still need to do two things:
- Turn on the screen
It's been turned off since we started - the VDP initialisation data turned it off. - Stop the program
If we don't stop it, it will keep going and bad things will happen.
Turning on the screen - VDP register 1
The VDP registers control all aspects of the VDP's operation. Register 1 includes turning the screen on and off. In fact, it controls lots of things:
Bit | Function | If set | If reset |
---|---|---|---|
7 | Unused | Doesn't matter | Doesn't matter |
6 | Enable display | Display on | Display off |
5 | VBlank interrupts | Interrupts generated on VBlank | VBlank gives no interrupts |
4 | 28 row display | Screen is taller than normal (e.g. Codemasters games) | Screen is normal size |
3 | 30 row display | Screen is even taller - but only on PAL systems | Screen is normal size |
2 | Mega Drive mode 5 | On a Mega Drive, a Mega Drive video mode is selected | Normal Master System video mode |
1 | Doubled sprites | Each sprite defined will also show the next tile under it | Normal sprites |
0 | Zoomed sprites | Sprites are stretched to 16x16 pixels | Normal sprites |
Charles MacDonald's "Sega Master System VDP documentation" describes all of the registers in very good detail. Anyway, there is far too much information here for us to remember every time, so it makes sense for us to add comments to make it clear what we're doing. We don't want to enable any of these features except for "Enable display".
To write to a VDP register, we use the VDP control port. This time, the high two bits must be %10. The rest is somewhat different as we aren't setting an address:
Bit: | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
% | 1 | 0 | Ignored | Register number | Register value |
The whole thing is done in 16 bits, so we don't need to use the VDP data port. We do need to write it in little-endian format again, though. So, to set register 1 to value %01000000, we do this:
ld a,%01000000
; ||||||`- Zoomed sprites -> 16x16 pixels
; |||||`-- Doubled sprites -> 2 tiles per sprite, 8x16
; ||||`--- Mega Drive mode 5 enable
; |||`---- 30 row/240 line mode
; ||`----- 28 row/224 line mode
; |`------ VBlank interrupts
; `------- Enable display
out ($bf),a
ld a,$81
out ($bf),a
I suggest you use a comment like this each time you write to a VDP register because I think it's too hard to remember what all the bits do.
Time to stop - an infinite loop
OK, now our code is almost finished. We've done everything we wanted to do; but there's one more thing we have to do. The Z80 will execute all the code we've written, and then when it gets to the end it will keep on going and going forever, never stopping. We don't want that, we want it to stop; so what we'll do is put it in an infinite loop. Normally, infinite loops are a bad thing because they stop your program ever continuing; you'll probably create a few by accident and have to figure out why they're happening and fix the bug causing them. But here, we want one. We want the processor to keep doing the same thing (nothing) over and over again forever, which we will achieve by making a jump point to itself:
Loop:
jp Loop
When the Z80 gets to the instruction jp Loop
it will jump to the label Loop:
. When it gets there, it will find the instruction jp Loop
and will jump to the label Loop:
. When it gets there, it will find the instruction jp Loop
and will jump to the label Loop:
... and so on forever.
Now we've added that infinite loop, we know the program will never get past it; so here is a safe place to put data. Why does it matter where you put data? Because you have to make sure that the data is never accidentally interpreted as code. The Z80 can't tell if what it's looking at is sensible program code or data, it assumes everything is program code. So you have to make sure that the place you insert data is outside the program code and that execution will never accidentally get to your data. For a simple program like this one (with no "functions", just one code block) we put it after the program. We could equally have put it before the "main:" label, and at the start of the program execution would have jumped straight past it to that label.
I can put the data in any order I like because it doesn't matter - it's not necessary to put it in the order it's used. In larger projects you may choose to order the data logically to make it easier to navigate, and maybe split the data up according to what it's for.
Anyway, you may have noticed that the program is now finished. Press F10 to run it again.
< Tilemap | Lesson 1 | Enhancing our program >