Writing stuff in Assembly for Sega Genesis #3

In this post I will describe how to display an image from tiles on the Sega Genesis emulator using assembler.
The splash picture of Demens Deum in Exodus emulator will look like this:

The process of outputting a PNG image using tiles is done in steps:

  1. Reducing the image to fit the Sega screen
  2. Convert PNG to assembler data-code, with division into colors and tiles
  3. Loading the color picker into CRAM
  4. Loading tiles / patterns into VRAM
  5. Loading tile indices at Plane A / B addresses in VRAM

You can reduce the image to fit Sega’s screen using your favorite graphics editor, for example Blender.

Convert PNG

To convert images, you can use the ImaGenesis tool, to work under wine you need Visual Basic 6 libraries, they can be installed using winetricks (winetricks vb6run), or RICHTX32.OCX can be downloaded from the Internet and put into the application folder for correct operation. < / p>

In ImaGenesis, you need to select a 4-bit chroma, export colors and tiles in two assembly files. Next, in the file with colors, you need to put each color in a word (2 bytes), for this you use the dc.w opcode.

For example CRAM splash screen:

 Colors: 
  dc.w $0000 
  dc.w $0000 
  dc.w $0222 
  dc.w $000A 
  dc.w $0226 
  dc.w $000C 
  dc.w $0220 
  dc.w $08AA 
  dc.w $0446 
  dc.w $0EEE 
  dc.w $0244 
  dc.w $0668 
  dc.w $0688 
  dc.w $08AC 
  dc.w $0200 
  dc.w $0000 

Leave the tile file as it is, it already contains the correct format for loading. An example of a part of a tile file:

 Tiles: 
	dc.l	$11111111	; Tile #0 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111	; Tile #1 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 

As you can see from the example above, the tiles are an 8×8 grid of CRAM color palette indices.

Colors in CRAM

Loading into CRAM is performed by setting the color load command at a specific CRAM address to the vdp control port. The command format is described in the Sega Genesis Software Manual (1989), I just add that it is enough to add to the address 0x20000 to go to the next color.

Next, you need to load the color into the data port (vdp data); The easiest way to understand loading is with the example below:

VDPCRAMFillLoop: 
    lea Colors,a0 ; pointer to Colors label 
    move.l #15,d7; colors counter 
VDPCRAMFillLoopStep: 
    move.l  d0,vdp_control_port ;  
    move.w  (a0)+,d1; 
    move.w  d1,(vdp_data_port); 
    add.l #$20000,d0 ; increment CRAM address 
    dbra d7,VDPCRAMFillLoopStep 

Tiles in VRAM

Next comes the loading of tiles / patterns into VRAM. To do this, select an address in VRAM, for example 0x00000000. By analogy with CRAM, we address the VDP control port with a command to write to VRAM and a starting address.

After that, you can upload longwords to VRAM, compared to CRAM, you do not need to specify an address for each longword, since there is a VRAM auto-increment mode. You can enable it using the register flag VDP 0x0F (dc.b $ 02)

TilesVRAM: 
  lea Tiles,a0 
  move.l #$40200000,vdp_control_port; write to VRAM command 
  move.w #6136,d0 ; (767 tiles * 8 rows) counter 
TilesVRAMLoop: 
  move.l (a0)+,vdp_data_port; 
  dbra d0,TilesVRAMLoop 

Tile indices in Plane A / B

Now we have to fill the screen with tiles by their index. For this, VRAM is filled at the Plane A / B address, which is put in the VDP registers (0x02, 0x04). For more information about tricky addressing, see Sega’s manual, in my example the VRAM address is 0xC000, let’s unload the indexes there.

Your picture will fill the VRAM off-screen space anyway, so after rendering the screen space, your render should stop rendering and resume when the cursor moves to a new line. There are many ways to implement this set, I used the simplest version of counting on two registers of the image width counter, the cursor position counter.

Example code:

 FillBackground: 
  move.w #0,d0     ; column index 
  move.w #1,d1     ; tile index 
  move.l #$40000003,(vdp_control_port) ; initial drawing location 
  move.l #2500,d7     ; how many tiles to draw (entire screen ~2500) 

imageWidth = 31 
screenWidth = 64 

FillBackgroundStep: 
  cmp.w	#imageWidth,d0 
  ble.w	FillBackgroundStepFill 
FillBackgroundStep2: 
  cmp.w	#imageWidth,d0 
  bgt.w	FillBackgroundStepSkip 
FillBackgroundStep3: 
  add #1,d0 
  cmp.w	#screenWidth,d0 
  bge.w	FillBackgroundStepNewRow 
FillBackgroundStep4: 
  dbra d7,FillBackgroundStep    ; loop to next tile 

Stuck: 
  nop 
  jmp Stuck 

FillBackgroundStepNewRow: 
  move.w #0,d0 
  jmp FillBackgroundStep4 
FillBackgroundStepFill: 
  move.w d1,(vdp_data_port)    ; copy the pattern to VPD 
  add #1,d1 
  jmp FillBackgroundStep2 
FillBackgroundStepSkip: 
  move.w #0,(vdp_data_port)    ; copy the pattern to VPD 
  jmp FillBackgroundStep3 

After that, it remains only to collect rum using vasm, launching the simulator, and see the picture.

Debug

Not everything will work out right away, so I would like to recommend the following Exodus emulator tools:

  1. m68k processor debugger
  2. Changing the number of cycles of the m68k processor (for slow-mo mode in debugger)
  3. Viewers CRAM, VRAM, Plane A / B
  4. Carefully read the documentation for m68k, used opcodes (not everything is as obvious as it seems at first glance)
  5. See examples of code / game disassembly on github
  6. Implement processor execution sabrutines, process them

Pointers to processor execution sabrutines are put in the title of the rum, there is also a project on GitHub with an interactive runtime debugger for Sega, called genesis-debugger.

Use all available tools, nice old school coding and may Blast Processing come with you!

Links

https://gitlab.com/demensdeum/segagenesissamples/-/tree/main/6Image/vasm
http://devster.monkeeh.com/sega/imagenesis/
https://github.com/flamewing/genesis-debugger

Sources

https://www.chibiakumas.com/68000/helloworld .php # LessonH5
https://huguesjohnson.com/programming/genesis/tiles-sprites/