Learn Multi platform Super-H Assembly Programming... Because Why not?

Simple Samples

In this series we'll learn a how to do simple tasks, with a single small asm file (where possible)

Lesson S1 - Sprite movement on the Saturn
In this lesson we'll create a minimal example on the Sega Saturn. We'll create a BIN file we can run on our emulator, which shows a bitmap sprite we can move around the sprite with the joypad

Sat_JoySprite.asm


Setting up the Saturn
We're gong to show a moving bitmap onscreen.

We will draw it with XOR - an inversion operation.

This means if we draw the sprite twice in the same location, the second draw will remove it.
Our BIN program needs to start at address $6004000, we do this with an ORG statement.
Next we need to set up a valid screen.

We need to set various registers on VDP2 (the video device for the bitmap screen). Shown here are sample values which will work on our emulator

We need to set up the Ram layout and screen mode.

We set up the scroll postition and map offset.

We set up the bitmap screen size.

Finally we enable the layer and turn on the screen
Our screen is on, but we've not set up any colors!

The color ram is at address $25F00000, The screen is set up to use 5 bits per channel in the format %-BBBBBGGGGGRRRRR

We transfer each of the 4 colors to CRAM to set up our color screen



Drawing Our sprite
We need to calculate the VRAM Destination for our sprite.

Our screen base is $25E00000. Each pixel is one byte

The Visible screen is 320 pixels wide, but the logical (virtual screen) is 512 bytes wide. Therefore our formula is:

VRAM = $25E00000 + (Ypos * 512) + Xpos
Our sprite source address (bitmap data) is in R13, our vram destination is now in R12

Our width and height in pixels are in R11,R12

We transfer the image one word (4 bytes) at a time from the source, Xor it with the current screen data, and write it back to VRAM.

Our bitmap is included as binary data - it's the same format as DOS VGA!

We define two 32 bit values in our 'UserRam' for the player position

Reading the joystick
We're going to use the SMPC (System Manager and Peripheral Control) hardware to get the joypad buttons.

We'll use the function INTBACK - Interrupt Back (SMPC Status Acquisition) to get the joypad state.

First we set up DDR1 data direction (All bits to input), IOSEL1 (SPMPC mode) and EXLE1 (VDP latch off)
We have to send a fixed sequence of bytes to the SMPC registers in order to prepare the command to request the joystick data.

We write these to IREGs $20100001-$2010000D

The last write to $2010001F actions the data request
Our command has been sent, but we may need to wait a bit for the reply, we test bit 0 of the Status register at $20100063

The results will be in the output registers at address $20100020+
The response is in the registers $20100020/2/4/6/8 ...
Each 16 bit entry at these addresses contains the same 8 bit byte repeated twice.

The first few bytes contain the following data:
  %11110001    = No multitap
  %00000010    = Standard pad
  %RLDUSABC    = Genesis buttons
  %rXYZl---    = Saturn buttons


Whatever the system to allow for easy porting of programs,  These tutorials use a common format for joypad output of RLDU in bits 3-0, and bits 4+ are fire buttons in decreasing importance (Bit 4=Fire 1, Bit 5=Fire 2 if available etc)

We bitshift all the joypad directions and fires into the correct locations with combinations of SHLR and SHLL
We need to calculate the VRAM destination for our drawing

While not all are visible, Our screen is 512 bytes wide, each pixel is 1 byte
The screen base is $25e00000

So our formula is:

VRAM= $25e00000 + Ypos*512 + Xpos

However our Cursor Xpos and Ypos are measured in characters, so we multiply them by 8 via 3 shifts first.
one screen byte is a pixel... but in our font one bit is a pixel! We shift bits out of the right of our source byte, so we work right to left on our screen.

To do this we add 7 to our Vram destination, to move to the rightmost pixel of the line.

We load a byte from our font into R3,

We shift bits our R3, test them and write the correct color (1 or 0) to the screen in R6.

After a line, we move down the screen by adding 512 (+8 to compensate for the moves)

We repeat until our character is drawn
We're done! so we restore all the registers from the stack.

Moving our sprite
Ok! We have all the subroutines we need.

First we draw the initial position of our player sprite, Next we read in from the joystick, and wait for a direction to be pressed.
Throughout the next part of our code R1,R2 contain the X,Y position of the sprite, and R0 contains the joystick directions.

We test each direction buttons bit.
If a direction is pressed, we check the position of the sprite, if it's not at the edge of the screen we change the co-ordinate, moving in the direction specified.
Ok, we need to remove the old sprite from the screen.

As we're using XOR, all we need to do to remove the old sprite is draw again in the old position.
We store the new sprite position, and redraw the sprite.
Finally we pause for a while - otherwise our demo would be too fast! (thanks sh2!!!), and repeat!


Using a crude delay loop like this isn't really very good, we should really wait for VBLank or something!

Still it's good enough for this simple test.


Lesson S2 - Sprite movement on the 32x
In this lesson we'll create a minimal example on the 32x. We'll create a cartridge file we can run on our emulator, which shows a bitmap sprite we can move around the sprite with the joypad

MDHeader_MinJoy.asm
32x_JoySprite.asm

Normally in these tutorials we would have a single ASM file, but this time we need two because of how the 32x works.

The first is a Megadrive/Genesis 68000 'boot strap' which starts up the system, before starting up the 32x part.
We'll assemble the genesis 68000 part with VASM
We'll assemble the 32x part with ASW


The 32x be so dumb! How dumb is it? it can't even read from it's own joypad!!!

To read the joypa
d we have to get the genesis to do it... and pass the result to the 32x via one of the 'Shared registers'

The Genesis bootstrap

At the start of the 68000 memory map is the 'exception table', however on the 32x, this is moved to $00000200

The old table is filled with branches to the 32x startup routine are $000003F0
Next we have our cartridge header, this just contains the description of our cartridge, and a few other bits.
The exception table is now at address $00000200

We only need the reset address, which points to the program code for the 68000 Genesis processor
We now have the 32x header.

Address $000003D4 has the address of the 32x program code in the genesis cartridge
This will be copied to the 32x memory

$3E0 and $3E4 have the execution address of the 32x code for the two SH2 processors. These should be at address $06000000+


 
At address $3F0+ we have the 32x startup code.

As a security feature, Much of this has to byte match the same code in the 32x rom, so is included as binary data.
Our Geneisis 68000 program is very basic, we release the video hardware to the SH2, and drop into an infinate loop

The infinite loop reads in from the joypad
There are two ports which are read and written for the joypad...
Joypad 1 is at address $A10005
Joypad 2 is at address $A10003

First, however, we need to set one of the bits of these ports to WRITE... we do this with 2 ports...
Joypad 1 is at address $A1000B
Joypad 2 is at address $A10009
The Joypad needs a sequence of writes to select the 'sections' of the joypad... this is achieved by writes with bit 6 as a 1

The first batch returns Up, Down, Left, Right... button C and Button B

The second batch returns Button A and Start

The final batch are Button X, Button Y and Button Z... as well as Mode.

Note: some of the buttons are duplicated... eg Up is returned in the first and second batch.
Finally, we shift around the bits, so we have all the buttons in a neat order in a single register.
In the rom file, After our 68000 code we will 'attach' the assembled 32x binary.

We do this 'combining' with a batch file.


The 32x bootstrap!

When it's loaded into the 32x memory, our SH2 program will start at address $06000000

We specified the secondary SH2 would start executing this address... we don't actually plan to use that CPU, so we lock it into an infinite loop.
CPU 1's entry point was defined as $06000004 in the 32x header.

First we wait for the 68000 to release the VDP
We use the Bitmap Mode Register $20004100 to turn on the screen.

Here we select a 256 color screen mode.

The SH2 screen memory needs to start with a 'Line Table'

This defines the memory addresses that are used for each line of the screen. We do this twice, once for each screen buffer.

We'll take a look at this in a moment!
Our screen is nearly ready, it's on, but we need to set up it's colors.

Each color is defined by 16 bits, from address $20004200 - The bits are in the format %-BBBBBGGGGGRRRRR




Graphics routines.

The Line table starts at $24000000, each entry is one word - 224 line entries in total , and contains the offset IN WORDS for the bitmap data of that line.

Effectively we write $100+(Line*160)  repeating for 224 lines.
To force a page flip, we need to flip bit 0 of $2000410A.

This transfers the buffer fat address $24000000 into VRAM.

But there's a catch! the flip won't actually occur until VBLANK, so once we attempt the flip, we wait until it actually occurs by waiting for the read back bit to match the one we wrote.
We have a 'Clear Screen' routine, which will fill the buffer with zero bytes

Drawing Our sprite
We need to calculate the VRAM Destination for our sprite.

Our screen base is $24000200. Each pixel is one byte

The Visible screen is 320 pixels wide,  Therefore our formula is:

VRAM = $24000200 + (Ypos * 320) + Xpos
Our sprite source address (bitmap data) is in R13, our vram destination is now in R12

Our width and height in pixels are in R11,R12

We transfer the image one word (4 bytes) at a time from the source, and write them to the screen

Our bitmap is included as binary data - it's the same format as DOS VGA!

We define two 32 bit values in our 'UserRam' for the player position

Moving our sprite
Ok! We have all the subroutines we need.

First we draw the initial position of our player sprite, and flip the buffer to get it onto the screen


Next we read in from the joystick from the shared register $20004020 ($A15120 on the genesis side)

We wait for a direction to be pressed.
Throughout the next part of our code R1,R2 contain the X,Y position of the sprite, and R0 contains the joystick directions.

We test each direction buttons bit.
If a direction is pressed, we check the position of the sprite, if it's not at the edge of the screen we change the co-ordinate, moving in the direction specified.
We store the updated sprite position.

We need to remove the old sprite from the screen. we run the CLS routine to do this

We redraw the sprite, flip the page buffer and repeat