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:
- Request a combination of bits of pressed buttons
- Reading the bits of pressed buttons
- 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/