Writing stuff in Assembly for Sega Genesis #2

In this post I will describe how to load colors into Sega’s palette in assembler.
The final result in the Exodus emulator will look like this:

To make the process easier, find a pdf on the Internet called Genesis Software Manual (1989) , it describes the whole process in great detail, in fact, this note is a commentary on the original manual.

In order to write colors to the VDP chip of the Sega emulator, you need to do the following things:

  • Disable TMSS protection system
  • Write correct parameters to VDP registers
  • Write the desired colors to CRAM

For the assembly, we will use vasmm68k_mot and our favorite text editor, for example echo. The assembly is carried out by the command:

 vasmm68k_mot -Fbin minimal.asm -o minimal.gen 

VDP Ports

The VDP chip communicates with the M68K through two ports in RAM – the control port and the data port.
Essentially:

  1. VDP registers can be set through the control port.
  2. Also, the control port is a pointer to that part of the VDP (VRAM, CRAM, VSRAM etc.) through which data is transmitted through the data port

Interesting information: Sega retained compatibility with Master System games, as indicated by MODE 4 from the developer’s manual, in it VDP switches to Master System mode.

Let’s declare ports of control and data:

 vdp_control_port = $ C00004
vdp_data_port = $ C00000 

Disable TMSS protection system

Protection against unlicensed games TMSS has several unlocking options, for example, it is required that the string “SEGA” be in the A1 address register before accessing the VDP.


MOVE.B A1, D0; We get the version of the hardware with the number from A1 to register D0
ANDI.B 0x0F, D0; We take the last bits by the mask so as not to break anything
BEQ.B SkipTmss; If the version is 0, most likely it is a Japanese woman or an emulator without TMSS enabled, then go to the SkipTmss sabrutina
MOVE.L "SEGA" A1; Or write the string SEGA to A1

Write correct parameters to VDP registers

Why set the correct parameters in the VDP registers at all? The idea is that VDP can do a lot, so before rendering, you need to initialize it with the necessary features, otherwise it just won’t understand what they want from it.

Each register is responsible for a specific setting / mode of operation. The Segov manual contains all the bits / flags for each of the 24 registers, a description of the registers themselves.

Let’s take ready-made parameters with comments from the bigevilcorporation blog:

 VDPRegisters:

VDPReg0: dc.b $ 14; 0: H interrupt on, palettes on
VDPReg1: dc.b $ 74; 1: V interrupt on, display on, DMA on, Genesis mode on
VDPReg2: dc.b $ 30; 2: Pattern table for Scroll Plane A at VRAM $ C000
                    ; (bits 3-5 = bits 13-15)
VDPReg3: dc.b $ 00; 3: Pattern table for Window Plane at VRAM $ 0000
                    ; (disabled) (bits 1-5 = bits 11-15)
VDPReg4: dc.b $ 07; 4: Pattern table for Scroll Plane B at VRAM $ E000
                    ; (bits 0-2 = bits 11-15)
VDPReg5: dc.b $ 78; 5: Sprite table at VRAM $ F000 (bits 0-6 = bits 9-15)
VDPReg6: dc.b $ 00; 6: Unused
VDPReg7: dc.b $ 00; 7: Background color - bits 0-3 = color,
                    ; bits 4-5 = palette
VDPReg8: dc.b $ 00; 8: Unused
VDPReg9: dc.b $ 00; 9: Unused
VDPRegA: dc.b $ FF; 10: Frequency of Horiz. interrupt in Rasters
                    ; (number of lines traveled by the beam)
VDPRegB: dc.b $ 00; 11: External interrupts off, V scroll fullscreen,
                    ; H scroll fullscreen
VDPRegC: dc.b $ 81; 12: Shadows and highlights off, interlace off,
                    ; H40 mode (320 x 224 screen res)
VDPRegD: dc.b $ 3F; 13: Horiz. scroll table at VRAM $ FC00 (bits 0-5)
VDPRegE: dc.b $ 00; 14: Unused
VDPRegF: dc.b $ 02; 15: Autoincrement 2 bytes
VDPReg10: dc.b $ 01; 16: Vert. scroll 32, Horiz. scroll 64
VDPReg11: dc.b $ 00; 17: Window Plane X pos 0 left
                    ; (pos in bits 0-4, left / right in bit 7)
VDPReg12: dc.b $ 00; 18: Window Plane Y pos 0 up
                    ; (pos in bits 0-4, up / down in bit 7)
VDPReg13: dc.b $ FF; 19: DMA length lo byte
VDPReg14: dc.b $ FF; 20: DMA length hi byte
VDPReg15: dc.b $ 00; 21: DMA source address lo byte
VDPReg16: dc.b $ 00; 22: DMA source address mid byte
VDPReg17: dc.b $ 80; 23: DMA source address hi byte,
                    ; memory-to-VRAM mode (bits 6-7)

Ok, now let’s go to the control port and write all the flags to the VDP registers:


PrepareToFillVDPRegisters:
    move.l # VDPRegisters, a0; We write the address of the parameter table in A1
    move.l # $ 18, d0; Cycle counter - 24 = 18 (HEX) in D0
    move.l # $ 00008000, d1; Preparing a command to write to the VDP register at index 0, according to the manual - 1000 0000 0000 0000 (BIN) = 8000 (HEX)

FillInitialStateForVDPRegistersLoop:
    move.b (a0) +, d1; We write in D1 the total value of the VDP register from the parameter table, for sending to the VDP control port
    move.w d1, vdp_control_port; We send the final command + value from D1 to the VDP control port
    add.w # $ 0100, d1; Raise the VDP register index by 1 (binary addition +1 to the index according to Sega's manual)
    dbra d0, FillInitialStateForVDPRegistersLoop; Decrease the register counter, continue the loop if necessary 

The most difficult thing is to read the manual and understand in what format the data is fed to the control port, experienced developers will figure it out right away, but inexperienced ones … They will think a little and understand that the syntax for writing registers is as follows:

0B100 (5 bits – register index) (8 bits / byte – value)

0B1000001001000101 – write to the VDP register 2 (00010), the value of the flags 01000101.

Write the desired colors to CRAM

Next, we go to write two colors into the color memory CRAM (Color RAM). To do this, write to the control port a command to access the color at index 0 in CRAM and send the color to the date port. Everyone!

Example:

 VDPControlPortEnableCRAMWriteAccessGreenColorAtIndex0:
    move.l # $ C0000000, vdp_control_port; Access to color at index 0 in CRAM through the control port
    move.w # 228, d0; Color in D0
    move.w d0, vdp_data_port; Sending color to data port

After building and running in the emulator in Exodus, you should have the screen filled with color 228.

Let’s fill with another color, last byte 127.

 VDPControlPortEnableCRAMWriteAccessGreenColorAtIndex127:
  move.l # $ C07f0000, vdp_control_port; Access to color by byte 127 in CRAM through the control port
  move.w # 69, d0; Color in D0
  move.w d0, vdp_data_port; Sending color to data port

Links

https://gitlab.com/demensdeum/segagenesissamples
https://www.exodusemulator.com/
http://sun.hasenbraten.de/vasm/
https://tomeko.net/online_tools/bin_to_32bit_hex.php?lang= en

Sources

https://namelessalgorithm.com/genesis/blog/genesis/
https://plutiedev.com/vdp-commands
https://huguesjohnson.com/programming/genesis/palettes/ < br />
https://www.chibiakumas.com/68000/helloworld.php# LessonH5
https: / /blog.bigevilcorporation.co.uk/2012/03/09/sega-megadrive-3-awaking-the-beast/