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 |
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 |
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 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. |
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. |
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 joypad 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 |
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 |
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 |