Sega Megadrive – 4: Hello, world!

Time to get serious. I’ve got as far as getting my assembler, emulator and debugger working, I’ve learned some basics of 68000 assembly language, and the Megadrive is now initialised and ready to do something. Unfortunately this step wasn’t any easier, the VDP is a complicated beast to get going and has many quirks. Anyway, the aim of this article – however long – is to explain how I got “HELLO WORLD” on screen.

It’s not as simple as printf(“Hello, world!”). The machine has no standard I/O library, no debug text system, and no concept of a font whatsoever. Tiles representing text glyphs need to be created from scratch and moved to the correct positions on the VDP, as do the colour palettes used to paint them.Β I’ll make a start with all of the theory that I’ve learned on palettes, patterns and planes first.


The Megadrive’s VDP represents a colour in 9 bits, using 3 bits each for the red, green and blue components. With 3 bits, each component has 8 possible values, therefore the VDP is capable of displaying 512 colours. Colours must be predefined, and are stored in a section of VDP memory in tables of 16 colours – called palettes. This section of memory is called CRAM (colour RAM), and there’s space for 64 colour entries, therefore the VDP can store 4 palettes of 16 colours at any one time. The palettes can be swapped in and out from main RAM at any time, so this isn’t a global restriction throughout the life of the program. A typical palette is defined something like this:

   dc.w 0x0000 ; Colour 0 - Transparent
   dc.w 0x000E ; Colour 1 - Red
   dc.w 0x00E0 ; Colour 2 - Green
   dc.w 0x0E00 ; Colour 3 - Blue
   dc.w 0x0000 ; Colour 4 - Black
   dc.w 0x0EEE ; Colour 5 - White
   dc.w 0x00EE ; Colour 6 - Yellow
   dc.w 0x008E ; Colour 7 - Orange
   dc.w 0x0E0E ; Colour 8 - Pink
   dc.w 0x0808 ; Colour 9 - Purple
   dc.w 0x0444 ; Colour A - Dark grey
   dc.w 0x0888 ; Colour B - Light grey
   dc.w 0x0EE0 ; Colour C - Turquoise
   dc.w 0x000A ; Colour D - Maroon
   dc.w 0x0600 ; Colour E - Navy blue
   dc.w 0x0060 ; Colour F - Dark green

…and that looks like this:

The colour names were guesses, I’m no artiste. Entry 0 of any palette is used to determine a transparent pixel, and is used as the background colour by default.


Patterns are blocks of image data 8 x 8 pixels in size. Each pixel colour is represented using one nybble – the ID of the colour inside a palette – so a pattern can be represented in 8 longwords of data. Here’s an example, the letter H:

   dc.l 0x11000110
   dc.l 0x11000110
   dc.l 0x11000110
   dc.l 0x11111110
   dc.l 0x11000110
   dc.l 0x11000110
   dc.l 0x11000110
   dc.l 0x00000000

Assuming this pattern uses the palette given in the example above (so colour 1 represents red) and the background colour was white (colour 0 represents transparency, regardless of the value in the palette), we’d expect it to look like this if layed out on a grid:

If the 1’s were replaced with 2, it would be a green H, and if the 0’s were replaced with D it would sit on a maroon background. I haven’t utilised all of the space – there’s one line blank to the right and bottom of the glyph – to ensure that when font patterns are sat adjacent to each other there’s a very small gap, to ensure they are legible.


A plane is a kind of canvas, and the Megadrive’s VDP has 4 of them – two scrolling planes (plane A and plane B), a window plane, and a sprite plane. The scrolling and window planes can display grids made up of tiles of image patterns, positioned at predetermined cells depending on the VDP’s display mode (32×28 or 40×28 cells). The two scrolling planes can scroll lines of pixels (or groups of lines), or the entire contents left or right. The window plane is still a mystery to me, it can be moved around using the X and Y position in VDP registers 17 and 18, but it cannot overlap plane A. I don’t quite understand how it CAN’T overlap plane A, since the A and B planes can only scroll and not move around in their entirety. I’ll revisit this later.

The sprite plane can display patterns at arbitrary X and Y coordinates, and flip them vertically or horizontally. It also features priorities for each sprite, so their draw order can be defined. I’ll write up more about sprites in a further article, there’s quite a lot to them and since I’ll be doing the text display on plane A they’re beyond the scope of this post.

Preparing the VDP for writing data

This bit hurt my brain. In its basic form, moving palette and pattern data to the VDP comprises two operations: set the operation type and destination address through the control port, then move the data through the data port. Sounds simple, but the operation type and address need to be amalgamated into one longword, with a rather obscure bit structure. I’ll try to explain as best as I understand it myself. Here’s the operation/address longword split up into bits and nybbles:


The A’s hold the destination address, the B’s hold the operation type, and the 0’s are always 0. Let’s start with the address. The bits for the destination address need to be laid out in this pattern:

--DC BA98 7654 3210 ---- ---- ---- --FE

where 0 is the least significant bit, F is the most significant. For example, if we wanted to write to the VDP’s memory at address 0xC000 (which is the address of Plane A’s tile information, set via register 2 in our initialisation code), we’d first convert the address to a binary word:

1100 0000 0000 0000

and then rearrange it according to the bit template above:

0000 0000 0000 0000 0000 0000 0000 0011

Next, we need to set up the other bits to describe the type of operation we’re performing. Using six bits, we can describe the following operations:

  • 000000 – VRAM Read
  • 100000 – VRAM Write
  • 000100 – CRAM Read
  • 110000 – CRAM Write
  • 001000 – VSRAM Read
  • 101000 – VSRAM Write

These also need to be laid out into a specific order:

10-- ---- ---- ---- ---- ---- 5432 ----

So if we need to write to a VRAM address, we get:

01-- ---- ---- ---- ---- ---- 0000 ----

Put the address and the operation type together, and we get:

1000 0000 0000 0000 0000 0000 0000 0011

which in HEX is 0x40000003. Now we can move it to the VDP’s control port (I/O addressΒ 0x00C00004) to tell it we’re about to write data to VRAM address 0xC000:

move.l #0x40000003, 0x00C00004

I’m really not sure why this has to be so complex. Perhaps the bits are laid out in order of importance, so that they can be immediately acted on before the rest of the data is received. Perhaps we’re able to write a single word or byte to describe certain operations plus a small amount of data, so the bit layout needs to support this. For example, you only need to write a word of data to tell the VDP to change a register value. In any case, working this out is a bit of a pain when working regularly with the VDP, so I managed to find a javascript tool to calculate the longword for me. You’ll find it in the references section below.

Once the operation type and destination address have been written to the control port, the data itself can now be written to the data port. The VDP data port accepts data in bytes or words only, so if we need to write more data than that (which in 99% of cases, we will) then we could either increment the address manually and write it to the control port again, or make use of a feature called autoincrement. Autoincrement will – as the title vaguely suggests – automatically increment the destination address after each write to the port. Not only does this mean we can feed the data port a whole stream of information in one go, but it also means we can perform a longword write to the port, and it will be treated as two seperate word writes. To enable autoincrement, we set the autoincrement register (VDP register 15) to the amount of bytes we’d like it to increment by, which I’ll set as 2 and leave it:

   move.w #0x8F02, 0x00C00004   ; Set autoincrement to 2 bytes

Writing the data

Writing a palette

Let’s start with writing the palette. Palette 0 belongs in address 0x0000 of CRAM, so first we need to setup the VDP to write to CRAM (operation type 110000). Using the bit template above, a write operation to CRAM address 0x000 gives us 0xC0000003:

   move.l #0xC0000003, 0x00C00004 ; Set up VDP to write to CRAM address 0x0000

Next, assuming that autoincrement is still set to 2 bytes, we can move the palette data to the VDP’s data port at 0x00C00004 in one big loop:

   lea Palette, a0          ; Load address of Palette into a0
   move.l #0x07, d0         ; 32 bytes of data (8 longwords, minus 1 for counter) in palette

   move.l (a0)+, 0x00C00000 ; Move data to VDP data port, and increment source address
   dbra d0, @Loop

A new opcode here – LEA (load effective address) – which is a quicker way (both typing and CPU cycles) of loading the address of a label into an address register, verses using move.l.

We now have the opportunity to get our very first thing on screen, and confirm that everything blindly coded so far (the header, the initialisation code, the palette upload) is correct – we can use VDP register 7 to set the background colour to one of the colours in this palette. Bits 0-3 (first nybble) of register 7 represent the colour ID, and bits 4-7 (second nybble) represent the palette ID. So, using the example palette data above, we can set the background colour to pink (colour 8) using:

   move.w #0x8708, 0x00C00004  ; Set background colour to palette 0, colour 8

Build and run the ROM, and here we go:

Finally, 267 lines of code later, and we have something on screen! Fortunately, getting something a little more interesting than a big coloured window didn’t involve much work from this point.

Writing the patterns

The next step in the Hello World adventure is to design – and move to the VDP – some patterns representing all of the letters required to write the phrase. We’ll need H, E, L, O, W, R, and D.

   dc.l 0x11000110 ; Character 0 - H
   dc.l 0x11000110
   dc.l 0x11000110
   dc.l 0x11111110
   dc.l 0x11000110
   dc.l 0x11000110
   dc.l 0x11000110
   dc.l 0x00000000

   dc.l 0x11111110 ; Character 1 - E
   dc.l 0x11000000
   dc.l 0x11000000
   dc.l 0x11111110
   dc.l 0x11000000
   dc.l 0x11000000
   dc.l 0x11111110
   dc.l 0x00000000

   dc.l 0x11000000 ; Character 2 - L
   dc.l 0x11000000
   dc.l 0x11000000
   dc.l 0x11000000
   dc.l 0x11000000
   dc.l 0x11111110
   dc.l 0x11111110
   dc.l 0x00000000

   dc.l 0x01111100 ; Character 3 - O
   dc.l 0x11101110
   dc.l 0x11000110
   dc.l 0x11000110
   dc.l 0x11000110
   dc.l 0x11101110
   dc.l 0x01111100
   dc.l 0x00000000

   dc.l 0x11000110 ; Character 4 - W
   dc.l 0x11000110
   dc.l 0x11000110
   dc.l 0x11000110
   dc.l 0x11010110
   dc.l 0x11101110
   dc.l 0x11000110
   dc.l 0x00000000

   dc.l 0x11111100 ; Character 5 - R
   dc.l 0x11000110
   dc.l 0x11001100
   dc.l 0x11111100
   dc.l 0x11001110
   dc.l 0x11000110
   dc.l 0x11000110
   dc.l 0x00000000

   dc.l 0x11111000 ; Character 6 - D
   dc.l 0x11001110
   dc.l 0x11000110
   dc.l 0x11000110
   dc.l 0x11000110
   dc.l 0x11001110
   dc.l 0x11111000
   dc.l 0x00000000

Don’t stare at it too long, it makes funny patterns on your brain. There’s one character missing – the SPACE inbetween the words. That’s sort of already been implemented for us – a whole pattern of 0’s (transparency) will do the job, the VDP’s VRAM is already full of zeroes, and every tile ID on planes A, B and W is already set to zero, so an entire screen of blank patterns is already being displayed. If we skip the first pattern (32 bytes) when we write the font to the VDP, then pattern ID 0 will be a blank space.

So, we need to write this data to VRAM (that’s operation type 100000) at an offset of 0x20 (skips the first pattern). Using the bit template in the last section, that should give us the VDP command 0x40100000. 7 characters, 32 bytes each, that’s 56 longwords – let’s go:

   move.l #0x40200000, 0x00C00004 ; Set up VDP to write to VRAM address 0x0020
   lea Characters, a0             ; Load address of Characters into a0
   move.l #0x37, d0               ; 32*7 bytes of data (56 longwords, minus 1 for counter) in the font

   move.l (a0)+, 0x00C00000       ; Move data to VDP data port, and increment source address
   dbra d0, @Loop

Again, this assumes we haven’t touched the autoincrement register, and it’s still set to 2 bytes. Now the font data is in the VDP’s memory, sitting dormant until we set one of the planes up to paint them.

Matching Patterns to Tiles

As mentioned before, pattern #0 is already being drawn to every tile of planes A, B and W. To get some of these characters on screen, we need to change those tiles’ pattern IDs to those of the patterns we’d like to draw. The data that describes how each tile is drawn lives in VRAM, and there’s a block of data for each plane – the addresses for these are set up in VDP registers 2, 3, and 4, for planes A, W and B respectively. For this article, I’ll be drawing the text to plane A, which has been set to address 0xC000 in VDP register 3. All information needed to describe the tile fits into one word, and again we need to shuffle some bits around to match a template:



  • Bit A – Low or high plane (I don’t quite understand this yet)
  • Bits B – Colour palette ID (0, 1, 2 or 3)
  • Bit C – Horizontal flip (0 = drawn as-is, 1 = flip the tile horizontally)
  • Bit D – Vertical flip (0 = drawn as-is, 1 = flip the tile vertically)
  • Bits E – the ID of the pattern to be drawn

So, if we’d like to draw a pattern using colour palette 0, with no flipping, then it’s as easy as writing the pattern ID to the tile’s address. Let’s test it by setting plane A tile 0 to pattern ID 1, which should be the letter H. First, we need to put together the VDP command to write to VRAM (operation type 100000) at address 0xC000 using the bit template – this should give us 0x40000003.

   move.l #0x40000003, 0x00C00004 ; Set up VDP to write to VRAM address 0xC000 (Plane A)
   move.w #0x0001, 0x00C00000     ; Low plane, palette 0, no flipping, tile ID 1

Assemble and run, and we should see the Letter H in the top-left hand corner of the screen:

To keep this article simple, I won’t dwell into changing the palette or applying flipping, there’s no need yet.

Now it should be simple to display the rest of the characters; assuming autoincrement is still set to 2 we can write to consecutive tiles one by one:

   move.l #0x40000003, 0x00C00004 ; Set up VDP to write to VRAM address 0xC000 (Plane A)

   ; Low plane, palette 0, no flipping, plus tile ID...
   move.w #0x0001, 0x00C00000     ; Pattern ID 1 - H
   move.w #0x0002, 0x00C00000     ; Pattern ID 2 - E
   move.w #0x0003, 0x00C00000     ; Pattern ID 3 - L
   move.w #0x0003, 0x00C00000     ; Pattern ID 3 - L
   move.w #0x0004, 0x00C00000     ; Pattern ID 4 - O
   move.w #0x0000, 0x00C00000     ; Pattern ID 0 - Blank space
   move.w #0x0005, 0x00C00000     ; Pattern ID 5 - W
   move.w #0x0004, 0x00C00000     ; Pattern ID 4 - O
   move.w #0x0006, 0x00C00000     ; Pattern ID 6 - R
   move.w #0x0003, 0x00C00000     ; Pattern ID 3 - L
   move.w #0x0007, 0x00C00000     ; Pattern ID 7 - D

A tidier way would be to have a table of the pattern IDs and use a loop to write the data, but since the next article will be about writing a proper text display routine there’s no real need to complicate this supposedly “simple” example any further.
Here’s the finished result:



63 thoughts on “Sega Megadrive – 4: Hello, world!

  1. What a lovely set of Posts wrt Megadrive Programing you have written. You have got me back into Megadrive Programming πŸ™‚ I took your Posts and then made a small String Printer – taking the Horizontal Cell Length into account for ‘next line’.
    I had to manually create a Font Tileset and add in some useful punctuation characters which covers the ASCII Character up to ‘Z’.

    Have you thought about how you are going to convert Bitmap Images to Megadrive Tilesets, Maps and Palettes as yet? I ask as it’s a Question that I need to answer myself.

    Cheers for letting me know about MESS – it’s exactly what I needed! Gens is a bit of a hassle to use I find but MESS is pretty close to what I’d use at work πŸ™‚

    Please keep up the good work!

    • Hi! The next article is on exactly that – converting fonts to patterns, ASCII mapping and drawing text at arbitrary coordinates. I’m about half way through, should be a few more days now.


    • Haha I’ll try my best! I’m a beginner too, these aren’t tutorials as such, just my experiences learning how it’s done. There may be dragons, I’ve already had to make several corrections πŸ™‚

      I’ve had a few looks at Sega16, very nice site!

      • I read most of your site and I love that you want to write a game for all of your retro consoles. That’s a huge and very cool project. And it’s extra awesome that you’re documenting everything you learn as you go. You’re making the Internet a better place!
        I’m also really pleased that you’re starting with the Mega Drive. If you didn’t see it already there is a forum topic over at called Blast Processing.
        Those guys there know a lot about programming for the Mega Drive. If you have any questions you should try posting there.
        Even if you don’t ever finish a game this project is already a win in my books.

      • I’m glad to hear it πŸ™‚ I’m not a programmer but I’d like to offer my help. If there’s anyway you think I might ever be able to lend a hand let me know. I’m having trouble of thinking of an example… say maybe someday you need a particular donor cart for testing or development or some such thing let me know.

    • I’m not familiar with Fusion – does it have any debugging capabilities to show you if it’s crashed? The demo code posted here should build with asm68k and run on Mess or Regen, perhaps if it contains some mistakes other emulators won’t be so kind to it, I’ll have a go with Fusion and see why it’s not working. With Mess, you can step through the code line-by-line, checking the register values and making sure it doesn’t jump to an exception handler.

      As for your own code, only one thing strikes me as a possible issue – you have data in the middle of code. Whilst not usually a problem, you need to watch for alignment. Your block of VDP registers may cause the first instruction afterwards to land on an alignment that isn’t safe for code. Try putting all your data together at the bottom.

      Finally, make absolutely sure you’re assembling with the /p option, otherwise the first 15 bytes of the file will contain some metadata for the SN debuggers which our emulators will trip up on.

      Otherwise, I’ll have to get back to you, since I’ll be working pretty late for the next couple of days πŸ™‚

      • Thanks, I will start organizing my data better. πŸ™‚
        Oh, and your demos work now with fusion when using /p, apparently it is one of the most accurate emulators for SEGA, so that is probably why the 15 bytes of garbage messed it up.

  2. Yupp, just forgot to add move.l #0xC0000003, 0x00C00004 before loading the palette.
    I got hello world written to the screen also, but I should probably take a step back and get to know what I know a little better before moving on now.

  3. Thanks to you, I now have a simple program to play with, that I can assemble and that works. I had to convert the syntax to be able to use the GNU as assembler, as I don’t have Windows, but it was very easy. I came here after having read your post on spritesmind. I already knew this blog but I thought it had been abandoned, and until today I thought that converting the syntax to GNU as syntax would be difficult for me. Thank you.

    • Wow, FileDen seem to have deleted all of my files without notifying me. That’s really unprofessional of them 😦

      Apologies for that, I’ll see if I can find a new host and re-upload everything when I get home from work.

    • Sorry for the delay, I can’t find any of these older files on my PC 😦 I have everything from the next article onwards. I’ll get back to you as soon as I can.

    • Ok, everything from articles 5-10 are back up, I’ll need to reconstruct 1-4 from the newer code. That could take a whole evening though, I won’t be able to do it until next week.

      Really sorry about this! Thanks for the interest in my project, though πŸ™‚

  4. Your blog is fantastic! I’ve been curious about Sega Genesis programming for most of my life and this is the only place I’ve found with really detailed information.

      • Hello again. I just noticed your reply from almost exactly a year ago πŸ™‚

        I’m mainly just interested in understanding exactly how everything works to the lowest possible level; writing a game would be cool but I doubt I could dedicate enough time to make anything halfway decent.

        I commend your efforts.

  5. Yup, thanks for your work! , im getting deep into my megadrive thanks to this great tutorial. I found a mistake, when you descrive the binary codes for operation type to get data into VDP, they are wrong.

    And its confusing because if you follow intructions, you’ll end whit a 0x80000003 instead of a 0x40000003. I’ve found the correct values in the SEGA2 file, now all fits correctly.

    VRAM WRITE 0 0 0 0 0 1
    CRAM WRITE 0 0 0 0 1 1
    VSRAM WRITE 0 0 0 1 0 1
    VRAM READ 0 0 0 0 0 0
    CRAM READ 0 0 1 0 0 0
    VSRAM READ 0 0 0 1 0 0

    • Hi Fran! Yes there are many mistakes here, my framework has had several revisions with a lot of corrections since I wrote this blog! I was learning at the time πŸ™‚

      The method for generating the VDP write address isn’t correct either, I ran into trouble writing to larger addresses when trying to create a bitmap display ROM since I missed out the last two bits of the address. The quickest way is to have a load of macros with known addresses so that it’s done at assembly time, but to calculate at runtime:

      ; Address bit pattern: --DC BA98 7654 3210 ---- ---- ---- --FE
      add.l #vram_addr_plane_a, d1 ; Add VRAM address offset
      lsl.l #0x2, d1 ; Shift bits 14/15 of address to bits 16/17
      lsr.w #0x2, d1 ; Shift lower word back
      swap d1 ; Swap address hi/lo
      ori.l #vdp_cmd_vram_write, d1 ; OR in VRAM write command
      move.l d1, vdp_control ; Move dest address to VDP control port


      vdp_cmd_vram_write equ 0x40000000
      vram_addr_plane_a equ 0xC000

      as long as the VDP register for plane A address matches (in my current revision, reg 0x03 = 0x30).

  6. Looking forward to actually working through this one. Although I’ve got some code working on the Megadrive (just increasing a register by 1 every few cycles) I don’t think you can really claim to be programming on the Megadrive until you’ve got Hello World displayed on the screen!

  7. little error in the article, when you say
    Put the address and the operation type together, and we get:
    “1000 0000 0000 0000 0000 0000 0000 0011”
    the correct one is actually
    “0100 0000 0000 0000 0000 0000 0000 0011”
    the hex is still right so it’s just a typo

  8. can’t for the life of me get this to work and show a pink background.
    debug goes around in circles from rte at one address to invalid at the next and just loops between the 2 addresses.
    Changing to RTS stops the invalid opcode and no 2 address debug loop but either way no pink background 😦

    For this section i mean

    ; Called when a HBLANK happens

    ; Called when a VBLANK happens

    ; End of ROM

    have it broken into main, header, init, palette and characters asm files (haven’t gotten to characters but its included for now with letter F I made using your info). When I look at the VDP registers in debug auto increment is not switched on (where your article shows the code should set this to increment at 2), its assembling main which first up includes all the files, after it does the header it does the init then it goes to main with the source from this page. Another guy had the same issue a bit further up but claims he resolved it, but I get the exact same debug RTS loop using his code modified with the fix he mentioned as he said he forgot a line.

    really making me want to give up when I get so stuck this early on

    • Hello there! I’m just off out but if you could zip up your code so far I’ll dig through it this evening and see if we can get you back on track. Until you finally have something on screen it’s a really daunting process, don’t let it get the better of you!

      • awesome, would appreciate that heaps. I’ve used google drive and this is the link ..should work

        main,init,palette,header,characters are ‘my’ files (they’re basically all you’re source code with me copying across, attempting to make sense of each line, compiling and hoping to run successfully)

        the one called others.asm which is from the user “DoNotWant” just above who shared his source code (his header info was different) which also doesn’t work, and gives a similar result in debug. Using the header from Pong of course doesn’t work either.

        Theres a bunch of noob notes and a lot just copied straight from you.

        anyways, i’ve attempted to learn a few languages (c, python) and go back to them every now and then but always give up due to boredom, I really like your blog as it takes you step by step through what you went through yourself, and you can get results straight away. I was only successful in some visual basic 6 with a few simple projects from some books I purchased back in 2006 (I was 18) and the only other real programming done was attempting to make my own missions on the original C&C95 game by westwood which was partially successful (at times) so this is completely new to me and I know a lot of the stuff i babbled about on the previous post make no sense πŸ˜› so thanks again.

        Just a few more parts I wasn’t clear on, each line of code is generally well explained but this is register 15 of vdp from init

        dc.b 0x0000 ; 15: Autoincrement off

        ” To enable autoincrement, we set the autoincrement register (VDP register 15) to the amount of bytes we’d like it to increment by, which I’ll set as 2 and leave it:

        move.w #0x8F02, 0x00C00004 ; Set autoincrement to 2 bytes”

        Can this source be explained? All I can see is the VDP control port address, what is the ‘raw number’ prior? how does this switch vdp register 15 from the init to auto increment?



        ” we can use VDP register 7 to set the background colour to one of the colours in this palette. Bits 0-3 (first nybble) of register 7 represent the colour ID, and bits 4-7 (second nybble) represent the palette ID. So, using the example palette data above, we can set the background colour to pink (colour 8) using:

        move.w #0x8708, 0x00C00004 ; Set background colour to palette 0, colour 8″

        This is all that is shown for VDP register 7 in the init…

        dc.b 0x08 ; 7: Background colour – bits 0-3 = colour, bits 4-5 = palette

        a value of ‘0x08’ (which is just wacked next to ‘dc.b’ so im guessing the dc.b values are counted from 1st to 23rd as vdp registers? noticed the z80 init was also dc.b too?)

        so if a nybble is a group of 4 bits, all i can see is 4 bits not 8, and the vdp control port address again? so confusing.

        From the tiny bit of C i have learned/used I can see that we have simply named the palette data as a label ‘palette’ and kind of understand that we don’t need to know where that info goes as its handled by the compiler and can simply ask for its address to be stored in a0 as done earlier which gives me a little hope that I’m not totally allergic to source code. (it also sort of answers part of my confusion about address registers on the earlier post)

    • If you’re on Windows 8 or 10, the built-in calculator has a Programmer Mode where you can mess with hexadecimal and binary. It’s basic, but really useful!

  9. Ok, let’s work through!

    > move.w #0x8F02, 0x00C00004 ; Set autoincrement to 2 bytes”

    > Can this source be explained? All I can see is the VDP control port address, what is the β€˜raw number’ prior? how does this switch vdp register 15 from the init to auto increment?

    This is sending one word (16 bits, or 2 bytes) of data through the VDP control port to set up autoincrement to 2. The raw number is two bytes in hex, if you split each byte up you get 0x8F (8 = set register command to tell the VDP what we’re trying to do, F = the register number to set, which is 15) followed by 0x02 (the autoincrement byte count). We ONLY need to send this to the VDP control port, and nothing follows through the VDP data port, because there’s no more information required to perform this job.

    > move.w #0x8708, 0x00C00004 ; Set background colour to palette 0, colour 8

    > so if a nybble is a group of 4 bits,


    > all i can see is 4 bits not 8, and the vdp control port address again? so confusing.

    I’m not sure, but I think you might be confusing each hexadecimal character for a bit. Each hexadecimal character represents 4 bits. There are 8 bits to a byte, so this is a 16 bit (2 byte) value we’re sending through the VDP control port. Quick examples:

    0xF = decimal number 15, represented as a single hexadecimal character, which is 4 bits
    0x10 = decimal number 16, represented as two hexadecimal characters, which is 8 bits (1 byte)
    0x23 = decimal number 35, represented as two hexadecimal characters, which is 8 bits (1 byte)
    0x8ED2 = decimal number 36562, represented as four hexadecimal characters, which is 16 bits (2 bytes)

    So this line:

    > move.w #0x8708, 0x00C00004

    means command 8 (set register command), register 7 (background colour register), palette 0, colour 8.

    Let me know if I’ve misunderstood the question.

    > a value of β€˜0x08’ (which is just wacked next to β€˜dc.b’ so im guessing the dc.b values are counted from 1st to 23rd as vdp registers? noticed the z80 init was also dc.b too?)

    dc.b means “data values of 1 byte”. This is quite tricky to grasp when I started, I’ll do my best to explain.

    Code and data are one and the same. You refer to code via addresses in the same way you refer to data by addresses. It all gets assembled together in one giant binary blob (the ROM). You could have a subroutine that draws some text, and elsewhere you could define the text you want to draw as data, and refer to it in code using a label.

    dc.b tells the assembler that this section of the file as actually data, and is not to be processed as code (e.g., it will not be checked for errors, it cannot be moved for optimisation purposes).

    I’ve downloaded your .rar flle, I’ll have a look at the code now.

    • Ok, we have a winner:

      The problem goes hand in hand with my explanation of code and data above; you’ve pasted bits of data inside sections of code in your init.asm, which means the CPU is going to try to run that as code! The reason you’re seeing a loop in the debugger is because it’s in an exception (your game crashed!).

      The best advice I can offer is to take all of your data sections and move them to a new file, and include that file right at the end of your code, just before the line:

      ; End of ROM

      Once you get used to dealing with code and data, you should also read up about alignment (or message me). Moving your palette.asm and characters.asm includes to the end of the ROM will avoid the problem for now.

      The second problem is that the VDP registers in my example only worked for the two emulators I tested at the time, they are awful examples. I’ve replaced them with register values from my game in development, and all is well. I think the specific register causing the problem here is Shadow & Highlight mode (register 12) but I haven’t verified.

      Here’s the fixed up code, with a working ROM, in case my explanation was terrible:

      (assemble main.asm)

      • Thanks heaps for that!
        I made a simple batch file

        asm68k.exe /p main.asm,TEST.BIN


        So now I can just double click ‘assemble.bat’ everytime I want to attempt a compile.
        …Been a while since the dos days ay

        I will go through it properly later and re-do all your corrections and try not to use your fix as reference too much to assure I’ve actually learned what I was doing wrong.

        Would be good to know exactly why the original VDP data was incorrect? Is there a reason each VDP register is labelled now?

        I’ll of course study up on the rest of the questions I asked as well.

        Once again thanks for spending the time to help me out!

  10. sorry me again.
    Just breaking something down to understand.

    “move.w #0x8708, 0x00C00004

    means command 8 (set register command), register 7 (background colour register), palette 0, colour 8.”

    ok so 8708 = ‘1000 0111 0000 1000’

    so first nybble (1000) = 8 (command 8)
    2nd nybble = (0111) = 7 (register to add colour to, B/G colour register 7)
    3rd nybble = (0000) palette 0? so 0001 would be palette 1 if there were multiple loaded?
    4th nybble = 1000 (colour 8 from palette = pink)

    quote “Bits 0-3 (first nybble) of register 7 represent the colour ID, and bits 4-7 (second nybble) represent the palette ID.”

    I suppose I should play around with this value to verify

      • thanks for that. I suppose it’s probably going to take over a year for me to get some sprites moving on the screen at this rate πŸ˜›

        Hello world is all good, just had to change the @loop/loop: as it was used for B/G colour loop, but no real point going any further until I actually understand it all. Sure gives an instant headache trying to get my head around some of this.

        None the less will definitely be buying a copy of tanglewood

    • It’s not as simple as a list of command numbers, setting the VDP up to do a job using the command port has a strange bit layout. “8” here just happens to be the result of calculating the register set command. The rest are:

      0000b : VRAM read
      0001b : VRAM write
      0011b : CRAM write
      0100b : VSRAM read
      0101b : VSRAM write
      1000b : CRAM read

      The “Hello world” article explains the bit layout (as best as I can!) and how to convert those command values into an actual VDP control port command. Don’t stress yourself over it just yet, it’s quite a brain puzzler when you’re just getting started.

      • Hey sorry to bombard you with so many posts, but there are only a few people on the internet who seem to know (or at least willingly share information) about 86k ASM.. I have taken a step back to simple CPU instructions from the MODE5 tutorial, good bloke and like you sent me heaps of links and information… so after having the CPU manipulate data in registers, copy them across (move), clear the registers once a condition is met (CMPIW) with (BEQ) to a little loop that resets the registers back to zero with (movem) i was feeling pretty stoked to have a few simple instructions doing some “stuff”

        I decided to move onto the VDP registers and opened the documentation and tried for a while to set auto increment to 2 in register 15, and this is my workings out but I came back to your page and I was totally off, I really can’t get my head around it.

        $C00004 = control port address

        “All register values are written to the control port as a word – the first byte being the register number with its most significant bit set, and the second byte being the value.” :

        reg 15 = $0F

        15 = 1111 in binary

        value to set autoincrement = 2
        2 = 0010 in binary

        1111 0010

        BUT according to the documentation the registers read bits from right to left i.e. 8 bits (7 to 0, not 0 to 7)

        0100 1111 = $4F

        So i worked out “move.w #$4F,$C00004”

        but you have “move.w #0x8F02, 0x00C00004”

        isn’t this a long word? which would be move.l? Documentation says it accepts register values as a word? How did you work out this line of code? When i open up the debug for VDP registers the value is still 0x00?

        I definitely owe you a beer after this πŸ˜›

  11. Tried this one, finally got the pink working (forgot the /p switch) but can’t get the Character portion working as just getting a blank/black screen.

    I changed the loop from the Palette to the Character one and also added the Palette code above the Character code but neither showed anything. Cannot download the example code as it doesn’t exist.

    Can you reupload the example code so I can see where I went wrong?

    • Hey there!

      I’m very sorry about the example code, the downfall of FileDen took my old work with it. I really should know better about making backups!

      As for this particular example, I’m about to run a Mega Drive Assembly workshop at the National Videogame Arcade’s GameCity festival at the end of this month, which I’m writing the code and tutorial for. I’ll be sure to post it here when I’m done, since it should be a far more polished and better explained sample since I was still learning back when I wrote this one… wow… 4 years ago!


  12. For some reason the Hello World works perfectly on Gens, displays at half brightness in Regen, and refuses to even run at all on a real Mega Drive. I can’t figure out what is going on.

  13. If anyone else is having issues with getting stuff working on a real Genesis, try changing the VDP registers in init.asm to:

    dc.b 0x14 ; 0: H interrupt on, palettes on
    dc.b 0x74 ; 1: V interrupt on, display on, DMA on, Genesis mode on
    dc.b 0x30 ; 2: Pattern table for Scroll Plane A at VRAM 0xC000 (bits 3-5 = bits 13-15)
    dc.b 0x00 ; 3: Pattern table for Window Plane at VRAM 0x0000 (disabled) (bits 1-5 = bits 11-15)
    dc.b 0x05 ; 4: Pattern table for Scroll Plane B at 0xA000 (bits 0-2)
    dc.b 0x70 ; 5: Sprite table at 0xE000 (bits 0-6)
    dc.b 0x00 ; 6: Unused
    dc.b 0x00 ; 7: Background colour – bits 0-3 = colour, bits 4-5 = palette
    dc.b 0x00 ; 8: Unused
    dc.b 0x00 ; 9: Unused
    dc.b 0x08 ; 10: Frequency of Horiz. interrupt in Rasters (number of lines travelled by the beam)
    dc.b 0x00 ; 11: External interrupts off, V scroll fullscreen, H scroll fullscreen
    dc.b 0x81 ; 12: Shadows and highlights off, interlace off, H40 mode (320 x 224 screen res)
    dc.b 0x3F ; 13: Horiz. scroll table at VRAM 0xFC00 (bits 0-5)
    dc.b 0x00 ; 14: Unused
    dc.b 0x02 ; 15: Autoincrement 2 bytes
    dc.b 0x01 ; 16: Vert. scroll 32, Horiz. scroll 64
    dc.b 0x00 ; 17: Window Plane X pos 0 left (pos in bits 0-4, left/right in bit 7)
    dc.b 0x00 ; 18: Window Plane Y pos 0 up (pos in bits 0-4, up/down in bit 7)
    dc.b 0xFF ; 19: DMA length lo byte
    dc.b 0xFF ; 20: DMA length hi byte
    dc.b 0x00 ; 21: DMA source address lo byte
    dc.b 0x00 ; 22: DMA source address mid byte
    dc.b 0x80 ; 23: DMA source address hi byte, memory-to-VRAM mode (bits 6-7)

    You’ll also need to put your Everdrive to “hard reset” if that’s the flash cart you’re using and clear Plane A if you have a TMSS Genesis (just cover it with blank tiles) since the TMSS screen won’t clear itself out of VRAM

    • Hi,
      Thanks for these values. I was struggling to understand the init values given to the VDP cause was having the same issue on Regen about not printing the letters in full red colour. After using these all looks fine, still need to test on real hardware / everdrive. Time to give the official manual another read for full understanding πŸ™‚

  14. Sorry if this is a bother, seeing as it’s 2018 now, but I’ve been getting into the Genesis for a few days now and I have managed to display a pink screen, however, whenever I edit the code after that (trying to put an H on screen as in the next step and putting the code where I assume it should be put), it doesn’t do anything. I use Gens as an emulator. I’ve been trying to get the hang of loading palettes and patterns to the VDP all day, but I’m obviously doing something wrong…
    This is my code all-in-one. I have compiled the same code with all-in-one and as separate files(Which is why the includes are comments instead and also why I organized the sections with their file names aka “main.asm” etc. Any help is appreciated!

  15. I know that this post is very old by now, but I thought I would drop in my two cents. I’ve worked on this for a little while now and can say that I’ve finally got this thing correctly showing a hot pink background a la Bret Hart. First problem I had was that the color was way off. But thanks to @shicky256 and his updated VDPRegister values, I was able to fix that. Next, my emulator (Exodus) was printing junk in the form of green and white bars to the screen. I know we cleaned up the 68k’s RAM in init to prevent this issue, but I personally found that I had to go and clear the VRAM as well. Below is the code I used to do that:

    move.w #0x8F02, 0x00C00004 ; Set up auto-increment for every 2 bytes
    move.l #0x40000000, 0x00C00004 ; Write VRAM addr 0 to Control Port
    move.l #0x00000000, d0 ; Set d0 to 0
    move.w #0x7FFF, d1 ; Set d1 to 32k-1

    move.w d0, 0x00C00000 ; Clear VRAM
    dbra d1, @ClearVRAM

    Happy coding!

Leave a Reply

Please log in using one of these methods to post your comment: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s