Writing stuff in Assembly for Sega Genesis #5

In this post I will describe the process of reading the joystick, changing the position of the sprite, horizontal flip, the Sega Genesis emulator and potentially the console itself.

Reading of clicks, processing of “events” of the shogi joystick is carried out according to the following scheme:

  1. Request a combination of bits of pressed buttons
  2. Reading the bits of pressed buttons
  3. Processing at the level of game logic

To move the skeleton sprite, we need to store the variables of the current position.

RAM

The variables of the game logic are stored in RAM, so far people have not come up with anything better. Let’s declare the addresses of the variables, change the rendering code:

skeletonXpos = $FF0000
skeletonYpos = $FF0002 
frameCounter = $FF0004 
skeletonHorizontalFlip = $FF0006

    move.w #$0100,skeletonXpos 
    move.w #$0100,skeletonYpos 
    move.w #$0001,skeletonHorizontalFlip 

FillSpriteTable: 
    move.l #$70000003,vdp_control_port 
    move.w skeletonYpos,vdp_data_port  
    move.w #$0F00,vdp_data_port 
    move.w skeletonHorizontalFlip,vdp_data_port 
    move.w skeletonXpos,vdp_data_port 

As you can see, the address available for work starts with 0xFF0000, and ends at 0xFFFFFF, in total we have 64 KB of memory available. Skeleton positions are declared at skeletonXpos, skeletonYpos, horizontal flip at skeletonHorizontalFlip.

Joypad

By analogy with VDP, work with joypads occurs through two ports separately – the control port and the data port, for the first it is 0xA10009 and 0xA10003 so-but. When working with a joypad, there is one interesting feature – first you need to request a combination of buttons for polling, and then, after waiting for the update via the bus, read the necessary clicks. For the C / B buttons and the cross, this is 0x40, an example is below:

ReadJoypad: 
  move.b #$40,joypad_one_control_port; C/B/Dpad 
  nop ; bus sync 
  nop ; bus sync 
  move.b joypad_one_data_port,d2 
  rts 

In register d2, the state of the buttons pressed, or not pressed, in general, what was requested through the date port, will remain. After that, go to the Motorola 68000 register viewer of your favorite emulator, see what the d2 register is, depending on the keystrokes. In a smart way, you can find out in the manual, but we do not believe a word. Further processing of pressed buttons in register d2

HandleJoypad:  
    cmp #$FFFFFF7B,d2; handle left 
    beq MoveLeft  
    cmp #$FFFFFF77,d2; handle right  
    beq MoveRight  
    cmp #$FFFFFF7E,d2; handle up  
    beq MoveUp  
    cmp #$FFFFFF7D,d2; handle down  
    beq MoveDown  
    rts

Of course, you need to check individual bits, not whole words, but for now it will do. Now the simplest thing is left – to write handlers for all events of movement in 4 directions. To do this, we change the variables in RAM, and start the repainting procedure.

Example for moving left + changing horizontal flip:

MoveLeft:  
    move.w skeletonXpos,d0 
    sub.w #1,d0 
    move.w d0,skeletonXpos 
    move.w #$0801,skeletonHorizontalFlip 
    jmp FillSpriteTable

After adding all the handlers and assemblies, you will see how the skeleton moves and rotates around the screen, but too fast, faster than Sonic the hedgehog itself.

Not so fast!

To slow down the speed of the game loop, there are several techniques, I chose the simplest and not affecting work with external ports – counting a digit through a register until it becomes zero.

An example of a slowing loop and a game loop:

StartWaitFrame: 
  move.w #512,frameCounter 
WaitFrame: 
  move.w frameCounter,d0 
  sub.w #1,d0 
  move.w d0,frameCounter 
  dbra d0,WaitFrame 
GameLoop: 
  jsr ReadJoypad 
  jsr HandleJoypad 
  jmp GameLoop 

After that, the skeleton runs slower, which is required. As far as I know, the most common variant of “slowing down” is counting the vertical sync flag, you can count how many times the screen has been rendered, thus binding to a specific fps.

Links

https://gitlab.com/demensdeum/segagenesissamples/-/blob/main/8Joypad/vasm/main.asm

References

https://www.chibiakumas.com/68000/platform2.php
https://huguesjohnson.com/programming/genesis/tiles-sprites/

Writing stuff in Assembly for Sega Genesis #4

In this post I will describe how to draw sprites using the Sega Genesis VDP emulator.
The process for rendering sprites is very similar to rendering tiles:

  1. Loading colors into CRAM
  2. Upload parts of 8×8 sprites to VRAM
  3. Filling the Sprite Table in VRAM

For example, let’s take a skeleton sprite with a sword 32×32 pixels


Skeleton Guy [Animated] by Disthorn

CRAM

Let’s use ImaGenesis to convert it to CRAM colors and VRAM patterns for assembler. After that we get two files in asm format, then we rewrite the colors to the word size, and the tiles must be put in the correct order for rendering.
Interesting information: you can switch the VDP auto-increment through register 0xF to word size, this will remove the address increment from the CRAM color fill code.

VRAM

The shogi manula has the correct tile order for large sprites, but we’re smarter, so we’ll take the indexes from the blog ChibiAkumas , let’s start counting from index 0:

0 4 8 12

1 5 9 13

2 6 10 14

3 7 11 15

Why is everyone upside down? And what do you want, because the prefix is ​​Japanese! It could have been from right to left!
Let’s manually change the order in the sprite asm file:

Sprite: 
	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 #4 
	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 #8 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111111 
	dc.l	$11111122 
	dc.l	$11111122 
	dc.l	$11111166 
	dc.l	$11111166	; Tile #12 
	dc.l	$11111166 
	dc.l	$11111166 
	и т.д. 

Load the sprite like regular tiles / patterns:

SpriteVRAM: 
  lea Sprite,a0 
  move.l #$40200000,vdp_control_port; write to VRAM command 
  move.w #128,d0 ; (16*8 rows of sprite) counter 
SpriteVRAMLoop: 
  move.l (a0)+,vdp_data_port; 
  dbra d0,SpriteVRAMLoop 

To draw the sprite, it remains to fill the Sprite Table

Sprite Table

The sprite table is filled in VRAM, the address of its location is put in the VDP register 0x05, the address is again tricky, you can see it in the manual, an example for the F000 address:

dc.b $78 ; 0x05:  Sprite table at VRAM 0xF000 (bits 0-6 = bits 9-15) 

Ok, now let’s write our sprite to the table. To do this, you need to fill in the data “structure” consisting of four words. You can find a binary description of the structure in the manual. Personally, I made it easier, the sprite sheet can be edited manually in the Exodus emulator.

The structure parameters are obvious from the name, for example XPos, YPos – coordinates, Tiles – starting tile number for drawing, HSize, VSize – sprite sizes by adding 8×8 parts, HFlip, VFlip – hardware horizontal and vertical sprite rotations.

It is very important to remember that sprites can be off-screen, this is the correct behavior. unloading off-screen sprites from memory is quite a resource-intensive task.
After filling in the data in the emulator, they need to be copied from VRAM at 0xF000, Exodus also supports this feature.
By analogy with drawing tiles, first we turn to the VDP control port to start recording at 0xF000, then write the structure to the data port.
Let me remind you that the description of VRAM addressing can be read in the manual, or in the blog Nameless Algorithm .

In short, VDP addressing works like this:
[..DC BA98 7654 3210 …. …. …. ..FE]
Where hex is the position of the bit in the desired address. The first two bits are the type of the requested command, for example 01 – write to VRAM. Then for the address 0XF000 it turns out:
0111 0000 0000 0000 0000 0000 0000 0011 (70000003)

As a result, we get the code:

SpriteTable: 
  move.l #$70000003,vdp_control_port 
  move.w #$0100,vdp_data_port 
  move.w #$0F00,vdp_data_port 
  move.w #$0001,vdp_data_port 
  move.w #$0100,vdp_data_port 

After that, the skeleton sprite will be displayed at coordinates 256, 256. Cool yes?

Links

https://gitlab.com/demensdeum/segagenesissamples/-/tree/main/7Sprite/vasm
https://opengameart.org/content/skeleton-guy-animated

References

https://namelessalgorithm.com/genesis/blog/vdp/
https://www.chibiakumas.com/68000/platform3.php#LessonP27
https://plutiedev.com/sprites

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/

Writing stuff in Assembly for Sega Genesis #1

The first article dedicated to writing games for the classic Sega Genesis console in Motorola 68000 Assembler.

Let’s write the simplest endless loop for Sega. For this we need: an assembler, an emulator with a disassembler, a favorite text editor, a basic understanding of the structure of the Sega rom.

For development, I use my own Gen68KryBaby assembler/disassembler:

https://gitlab.com/demensdeum/gen68krybaby/

The tool is developed in Python 3, for assembly, a file with the extension .asm or .gen68KryBabyDisasm is supplied to the input, the output is a file with the extension .gen68KryBabyAsm.bin, which can be run in the emulator or on a real console (carefully, step away, the console may explode!)

Roms disassembling is also supported, for this you need to put a rom file to the input, without the .asm or .gen68KryBabyDisasm extensions. Opcode support will increase or decrease depending on my interest in the topic, the participation of contributors.

Structure

The Sega rom header occupies the first 512 bytes. It contains information about the game, name, supported peripherals, check sum, and other system flags. I suppose that without a title, the prefix will not even look at the rom, thinking that it is incorrect, like “what are you giving me here?”

After the header comes the Reset subroutine, from which the m68K processor starts its work. Well, it’s just a small matter – to find the opcodes, namely, the execution of nothing (!) And the transition to the subroutine at the address in memory. Googling, you can find the NOP opcode that does nothing and the JSR opcode that performs an unconditional jump to the argument address, that is, it just moves the carriage to where we ask for it, without any whims.

Putting It All Together

One of the games in the Beta version was the donor of the title for the rom, at the moment it is recorded in the form of hex data.

 ROM HEADER:

 00 ff 2b 52 00 00 02 00 00 00 49 90 00 00 49 90 00 00 49 90 00 ... etc. 

The program code so-but is a declaration of the Reset / EntryPoint subroutine in 512 (0x200) bytes, NOP, carriage return to 0x00000200, so we get an infinite loop.

Assembly code of Subroutine Reset / EntryPoint:

 SUBROUTINE_EntryPoint:
    NOP
    NOP
    NOP
    NOP
    NOP
    JSR 0x00000200

Complete example along with rom title:

https://gitlab.com/demensdeum/segagenesissamples/-/blob/main/1InfiniteLoop/1infiniteloop.asm

Next, assembly:

 python3 gen68krybaby.py 1infiniteloop.asm 

Run rom 1infiniteloop.asm.gen68KryBabyAsm.bin in debugger mode of Exodus / Gens emulator, see that m68K correctly reads NOP, and endlessly jumps to EntryPoint at 0x200 on JSR

Sonic should be showing V here, but he left for Wacken .

Links

https://gitlab.com/demensdeum/gen68krybaby/

https://gitlab.com/demensdeum/segagenesissamples

https://www.exodusemulator.com/downloads/release-archive

Sources

ROM Hacking Demo – Genesis and SNES games in 480i < / p>

http://68k.hax.com/

https://www.chibiakumas.com/68000/genesis.php

https://plutiedev.com/rom-header

https: //blog.bigevilcorporation.co.uk/2012/02/28/sega-megadrive-1-getting-started/

https : //opensource.apple.com/source/cctools/cctools-836/as/m68k-opcode.h.auto.html