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:

BitFunctionIf setIf reset
7UnusedDoesn’t matterDoesn’t matter
6Enable displayDisplay onDisplay off
5VBlank interruptsInterrupts generated on VBlankVBlank gives no interrupts
428 row displayScreen is taller than normal (e.g. Codemasters games)Screen is normal size
330 row displayScreen is even taller - but only on PAL systemsScreen is normal size
2Mega Drive mode 5On a Mega Drive, a Mega Drive video mode is selectedNormal Master System video mode
1Doubled spritesEach sprite defined will also show the next tile under itNormal sprites
0Zoomed spritesSprites are stretched to 16x16 pixelsNormal 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:1514131211109876543210
%10IgnoredRegister numberRegister 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:

    ; Turn screen on
    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:

    ; Infinite loop to stop program
    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 >