Mega-Gen
Garage
The Home of Megadrive Game Development.
Written 29th June 2018, by S.A.Ray
MD Dev part 1: Input and text.
IMPORTANT: I'm assuming you have read the previous "MD/Genesis Game development" page, so if you haven't then you should do so before continuing.
​
Don't forget that this tutorial is a companion to the wiki instructions for SGDK over on its Github page. We are going to refer to the two tutorials called "Hello world example" and "Input".
The wiki may be updated at some point, but until then I hope this series serves as a second reference point. Lord knows I wish it had been there for me!
​
​
The program structure and "game loop".
In the tutorial "Hello world example" the final program is basically a while loop that resides in the main() function, and by giving it a condition that always evaluates to true makes it an infinite loop. You do your program initialization before entering this loop, and at the end of each loop we make a call to VDP_waitVSync()...which waits for a "vertical blank" of the screen before continuing.
​
Moving on we are going to add three main functions to segragate program initialization, game processing( input, logic and sound ) and game rendering( graphics )...
​
int main()
{
game_setup();
while(1)
{
game_process();
game_render();
​
VDP_waitVSync();
}
return(0);
}
So in the interest of good programming practice we are going to keep the main() function clutter free and do the majority of our work in the three functions prefixed with "game_". Here are the prototypes...
​
​
int game_setup();
int game_process();
int game_render();
​
Recording joypad input
Okay, this is going to be a bit long-winded, so grab a drink and lets get cracking. Stephane has already covered the JOY_init() and JOY_setEventHandler() functions, so apart from placing those calls inside the game_setup() function we just need to setup a C structure to hold the current state of each joypad button. For our needs we are just concerned with getting input from joypad 1...
​
struct JOYPAD_STATES
{
unsigned char start_pressed;
unsigned char up_pressed;
unsigned char down_pressed;
unsigned char left_pressed;
unsigned char right_pressed;
unsigned char A_pressed;
unsigned char B_pressed;
unsigned char C_pressed;
};
​
struct JOYPAD_STATES joypad;
Now that we have a place to store our joypad input, lets return to the JOY_setEventHandler() function and poll the button presses for joypad 1. I've whipped up a helper function to make this easier, so along with the updated event handler here is the code for that helper function( as always, don't forget to provide its prototype )...
void myJoyHandler( u16 joy, u16 changed, u16 state )
{
if (joy == JOY_1)
{
updateButtonState( changed, state, BUTTON_START, &joypad.start_pressed );
updateButtonState( changed, state, BUTTON_A, &joypad.A_pressed );
updateButtonState( changed, state, BUTTON_B, &joypad.B_pressed );
updateButtonState( changed, state, BUTTON_C, &joypad.C_pressed );
updateButtonState( changed, state, BUTTON_UP, &joypad.up_pressed );
updateButtonState( changed, state, BUTTON_DOWN, &joypad.down_pressed );
updateButtonState( changed, state, BUTTON_LEFT, &joypad.left_pressed );
updateButtonState( changed, state, BUTTON_RIGHT, &joypad.right_pressed );
}
}
void updateButtonState( u16 changed, u16 state, u16 button_ID, unsigned char* button )
{
if (state & button_ID)
{
*button = 1;
}
else if (changed & button_ID)
{
*button = 0;
}
}
Just a quick word on my lack of commenting here - I want to keep the code snippets as light as possible, so if I do get to put these tutorials on github they most certainly will have comments!
​
Anyways. Thankfully, once we provide the event handler we dont have to worry about it again, save for when we want to check the joypad structure for a button press or release. At this point we need to update our game_setup function...
void game_setup()
{
JOY_init();
JOY_setEventHandler( &myJoyHandler );
VDP_setTextPalette( 3 );
}
I will cover palettes probably in the next tutorial, so for now just accept the call to VDP_setTextPalette() and know that it provides blue text. For now you can change the given value to 0-3, giving white, red, green and blue, but further down the road we'll need to use the blue palette to fit in with our final demo.
​
Lets finish up today's "workshop" by displaying the state of our joypad...
Displaying some text...
Without going into it too deeply, the Megadrive's Visual Display Processor( VDP ) works with two main areas of memory; the "VRAM" and another area dedicated to three plane layers - the background, the foreground and the sprite layer. Later we will use Gens' debug tools to inspect - visually - the contents of the ram and layers, but for now the font characters are stored in the VRAM while they are displayed on the foreground layer...
​
To tell the Megadrive to display text on the foreground layer we need to make a call to VDP_drawText(), for which we provide the text to display and then the x & y positions on the foreground layer. Have a look at the following function...
void display_joypad_input()
{
if( joypad.start_pressed )
{ VDP_drawText( "Start: 1 ", 10, 5 ); }
else
{ VDP_drawText( "Start: 0", 10, 5 ); }
if( joypad.A_pressed )
{ VDP_drawText( "A: 1 ", 10, 7 ); }
else
{ VDP_drawText( "A: 0", 10, 7 ); }
if( joypad.B_pressed )
{ VDP_drawText( "B: 1 ", 10, 8 ); }
else
{ VDP_drawText( "B: 0", 10, 8 ); }
if( joypad.C_pressed )
{ VDP_drawText( "C: 1 ", 10, 9 ); }
else
{ VDP_drawText( "C: 0", 10, 9 ); }
if( joypad.up_pressed )
{ VDP_drawText( "U: 1 ", 10, 11 ); }
else
{ VDP_drawText( "U: 0", 10, 11 ); }
if( joypad.down_pressed )
{ VDP_drawText( "D: 1 ", 10, 12 ); }
else
{ VDP_drawText( "D: 0", 10, 12 ); }
if( joypad.left_pressed )
{ VDP_drawText( "L: 1 ", 10, 13 ); }
else
{ VDP_drawText( "L: 0", 10, 13 ); }
if( joypad.right_pressed )
{ VDP_drawText( "R: 1 ", 10, 14 ); }
else
{ VDP_drawText( "R: 0", 10, 14 ); }
}
I can't help but think that there is another helper function we could write to cut down on the repetitive code here, but there it is. When called it displays the state of each button on the joypad, so let us add that to our game_render() function...
void game_render()
{
display_joypad_input();
VDP_waitVSync();
}
...and we're all done. Run the generated output file through Gens and try out the input. The text on screen should display a 1 if a button is pressed or a 0 if released.
​
Thats it for today. In the next part we shall introduce the amazing VDP chip by exploring its four colour palettes and take a look at another site called Sega Retro, a fantastic resource for the inner workings of Sega machines. We shall also use the GENS emulator to see whats going on inside the VDP...
​
Have fun! ^_^
​
...