Add initial joystick support.

This commit is contained in:
Jeremy Rand 2016-07-24 18:22:37 -04:00
parent 72cc4e138f
commit 7c3a9a17ad
9 changed files with 623 additions and 146 deletions

View File

@ -7,6 +7,8 @@
objects = {
/* Begin PBXFileReference section */
9D3A9FB81D455CCF004C5897 /* joystick.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = joystick.h; sourceTree = "<group>"; };
9D3A9FB91D455CD8004C5897 /* joystick.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = joystick.c; sourceTree = "<group>"; };
9D6B472E1D3FB16F00F6D704 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = "<group>"; };
9D6B472F1D3FB16F00F6D704 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
9D6B47311D3FB16F00F6D704 /* AppleCommander.jar */ = {isa = PBXFileReference; lastKnownFileType = archive.jar; name = AppleCommander.jar; path = make/AppleCommander.jar; sourceTree = "<group>"; };
@ -57,6 +59,8 @@
9D6B47471D42709200F6D704 /* vbl.h */,
9D6B47481D4270EC00F6D704 /* anim.c */,
9D6B47491D4270EC00F6D704 /* anim.h */,
9D3A9FB91D455CD8004C5897 /* joystick.c */,
9D3A9FB81D455CCF004C5897 /* joystick.h */,
9D6B472F1D3FB16F00F6D704 /* Makefile */,
9D6B47301D3FB16F00F6D704 /* make */,
);
@ -250,6 +254,7 @@
9D6B473D1D3FB16F00F6D704 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};

View File

@ -225,14 +225,7 @@ void drawGemAtXY(uint8_t x, uint8_t y, tGemType gemType, bool starred)
}
void beginStarAnim(void)
{
gStarAnimState.starVisible = true;
gStarAnimState.counter = STAR_CYCLES_VISIBLE;
}
void hideStars(void)
static void hideStars(void)
{
tSquare square;
@ -246,7 +239,7 @@ void hideStars(void)
}
void showStars(void)
static void showStars(void)
{
tSquare square;
@ -260,11 +253,13 @@ void showStars(void)
}
void endStarAnim(void)
void resetStarAnim(void)
{
if (!gStarAnimState.starVisible) {
showStars();
}
gStarAnimState.starVisible = true;
gStarAnimState.counter = STAR_CYCLES_VISIBLE;
}

View File

@ -21,8 +21,7 @@ extern void animInit(void);
extern void drawGemAtSquare(tSquare square);
extern void beginStarAnim(void);
extern void endStarAnim(void);
extern void resetStarAnim(void);
extern void doStarAnim(void);
extern void beginClearGemAnim(void);

192
a2bejwld/joystick.c Normal file
View File

@ -0,0 +1,192 @@
//
// Author: Jeremy Rand
// Date: July 20, 2012
//
// This is the implementation for the Curta emulator UI.
//
#include <stdio.h>
#include <string.h>
#include "joystick.h"
// Defines
#define PREAD 0xFB1E
#define ROM_SWITCH 0xC082
#define RAM_SWITCH 0xC080
#define BTN0 0xC061
#define BTN1 0xC062
#define JOYSTICK_CENTER 127
#define JOYSTICK_THRESHOLD 60
#define LOWER_THRESHOLD (JOYSTICK_CENTER - JOYSTICK_THRESHOLD)
#define UPPER_THRESHOLD (JOYSTICK_CENTER + JOYSTICK_THRESHOLD)
// Globals
static tJoyCallbacks *gJoyCallbacks = NULL;
static tJoyState gJoyState = {
JOY_POS_CENTER,
false,
false
};
static uint8_t gJoystickTemp;
static uint8_t gJoyPollsToNextRead;
static uint8_t gJoyPollsToNextRepeat;
// Implementation
void initJoystick(tJoyCallbacks *callbacks)
{
gJoyCallbacks = callbacks;
gJoyPollsToNextRead = 0;
gJoyPollsToNextRepeat = callbacks->initialPollsPerRepeat;
}
bool isButtonPressed(tJoyButtonNum buttonNum)
{
if (buttonNum == JOY_BUTTON_0) {
__asm__("LDA %w", BTN0);
__asm__("STA %v", gJoystickTemp);
} else if (buttonNum == JOY_BUTTON_1) {
__asm__("LDA %w", BTN1);
__asm__("STA %v", gJoystickTemp);
} else {
return false;
}
return ((gJoystickTemp > 127) ? true : false);
}
static uint8_t joystickLeftRight(void)
{
__asm__("BIT %w", ROM_SWITCH);
__asm__("LDX #0");
__asm__("JSR %w", PREAD);
__asm__("STY %v", gJoystickTemp);
__asm__("BIT %w", RAM_SWITCH);
return gJoystickTemp;
}
static uint8_t joystickUpDown(void)
{
__asm__("BIT %w", ROM_SWITCH);
__asm__("LDX #1");
__asm__("JSR %w", PREAD);
__asm__("STY %v", gJoystickTemp);
__asm__("BIT %w", RAM_SWITCH);
return gJoystickTemp;
}
static void readJoystickState(tJoyState *state)
{
static bool readLeftRight = true;
static uint8_t axisLeftRight = 127;
static uint8_t axisUpDown = 127;
tJoyPos pos = JOY_POS_CENTER;
if (readLeftRight) {
int temp = joystickLeftRight(); // Get left/right position
temp *= 3;
temp /= 4;
axisLeftRight /= 4;
axisLeftRight += temp;
readLeftRight = false;
} else {
int temp = joystickUpDown();
temp *= 3;
temp /= 4;
axisUpDown /= 4;
axisUpDown += temp;
readLeftRight = true;
}
if (axisLeftRight < LOWER_THRESHOLD) {
pos = JOY_POS_LEFT;
} else if (axisLeftRight > UPPER_THRESHOLD) {
pos = JOY_POS_RIGHT;
}
state->button0 = isButtonPressed(0);
state->button1 = isButtonPressed(1);
if (axisUpDown < LOWER_THRESHOLD) {
switch (pos) {
case JOY_POS_LEFT:
pos = JOY_POS_UP_LEFT;
break;
case JOY_POS_RIGHT:
pos = JOY_POS_UP_RIGHT;
break;
default:
pos = JOY_POS_UP;
break;
}
} else if (axisUpDown > UPPER_THRESHOLD) {
switch (pos) {
case JOY_POS_LEFT:
pos = JOY_POS_DOWN_LEFT;
break;
case JOY_POS_RIGHT:
pos = JOY_POS_DOWN_RIGHT;
break;
default:
pos = JOY_POS_DOWN;
break;
}
}
state->position = pos;
}
static bool joystickStateChanged(tJoyState *state1, tJoyState *state2) {
if ((state1->position != state2->position) ||
(state1->button0 != state2->button0) ||
(state1->button1 != state2->button1)) {
return true;
}
return false;
}
bool pollJoystick(void)
{
bool result = false;
tJoyState newState;
if (gJoyPollsToNextRead > 0) {
gJoyPollsToNextRead--;
return result;
}
gJoyPollsToNextRead = gJoyCallbacks->pollsPerRead;
readJoystickState(&newState);
if (joystickStateChanged(&newState, &gJoyState)) {
result = (gJoyCallbacks->joyChanged)(&gJoyState, &newState);
memcpy(&gJoyState, &newState, sizeof(gJoyState));
gJoyPollsToNextRepeat = gJoyCallbacks->initialPollsPerRepeat;
} else {
if (gJoyPollsToNextRepeat == 0) {
result = (gJoyCallbacks->joyNoChange)(&gJoyState);
gJoyPollsToNextRepeat = gJoyCallbacks->pollsPerRepeat;
} else {
gJoyPollsToNextRepeat--;
}
}
return result;
}

69
a2bejwld/joystick.h Normal file
View File

@ -0,0 +1,69 @@
//
// Author: Jeremy Rand
// Date: July 29, 2012
//
// This is an interface for the Apple // joystick. Note that I used to use
// the cc65 joystck interface but found it lacking on real hardware. It
// worked fine on an emulator but I think it was testing the second axis of
// the joystick too quickly leading to inaccuracies.
//
#include <stdint.h>
#include <stdbool.h>
// Defines
#define JOY_POS_CENTER 0
#define JOY_POS_DOWN 1
#define JOY_POS_DOWN_LEFT 2
#define JOY_POS_LEFT 3
#define JOY_POS_UP_LEFT 4
#define JOY_POS_UP 5
#define JOY_POS_UP_RIGHT 6
#define JOY_POS_RIGHT 7
#define JOY_POS_DOWN_RIGHT 8
#define NUM_JOY_POSITIONS 9
#define JOY_BUTTON_0 0
#define JOY_BUTTON_1 1
#define NUM_JOY_BUTTONS 2
// Typedefs
typedef int8_t tJoyButtonNum;
typedef int8_t tJoyPos;
typedef struct tJoyState {
tJoyPos position;
bool button0;
bool button1;
} tJoyState;
typedef struct tJoyCallbacks {
bool (*joyChanged)(tJoyState *oldState, tJoyState *newState);
bool (*joyNoChange)(tJoyState *oldState);
// On the Apple //, you cannot read each axis of the joystick as quickly as you might like.
// You must wait between each read. This is the number of calls into pollJoystick() before
// an axis is polled.
uint8_t pollsPerRead;
// This value tells you how many calls to poll without a change in joystick state for the
// joyNoChange() callback to be called. After a change, initialPollsPerRepeat are used.
// After that, pollsPerRepeat is used.
uint8_t initialPollsPerRepeat;
uint8_t pollsPerRepeat;
} tJoyCallbacks;
// API
extern void initJoystick(tJoyCallbacks *callbacks);
extern bool isButtonPressed(tJoyButtonNum buttonNum);
extern bool pollJoystick(void);

View File

@ -21,9 +21,7 @@ int main(void)
printInstructions();
while (true) {
playGame();
}
playGame();
return 0;
}

Binary file not shown.

Binary file not shown.

View File

@ -15,11 +15,7 @@
#include "anim.h"
#include "dbllores.h"
#include "game.h"
// Defines
#define BTN1 0xC062
#include "joystick.h"
// Forward delcarations
@ -28,6 +24,9 @@ static void refreshSquare(tSquare square);
static void refreshScore(tScore score);
static void refreshLevel(tLevel level);
static bool joystickChangedCallback(tJoyState *oldState, tJoyState *newState);
static bool joystickNoChangeCallback(tJoyState *oldState);
// Globals
@ -50,6 +49,17 @@ static tGameCallbacks gCallbacks = {
};
static tJoyCallbacks gJoyCallbacks = {
joystickChangedCallback,
joystickNoChangeCallback,
25, // Read poll time
40, // Initial no change poll time
10 // Subsequent no change poll time
};
static bool gShouldSave = false;
// Implementation
static void showAndClearDblLoRes(void)
@ -73,7 +83,7 @@ void printInstructions(void)
" Apple Jeweled\n"
" by Jeremy Rand\n"
"\n"
" Use I-J-K-M or the arrow keys to move your selection. Hold the closed\n"
" Use I-J-K-M or the arrow keys to move your selection. Hold the either\n"
" apple key and move your selection to swap two jewels and match 3 or\n"
" more jewels. When you match three jewels, they disappear and new\n"
" jewels will drop from the top.\n"
@ -156,6 +166,53 @@ static void moveUp(void)
}
static void moveUpLeft(void)
{
tSquare oldSquare = gSelectedSquare;
tPos x = SQUARE_TO_X(gSelectedSquare);
tPos y = SQUARE_TO_Y(gSelectedSquare);
if (y == 0)
y = BOARD_SIZE - 1;
else
y--;
if (x == 0)
x = BOARD_SIZE - 1;
else
x--;
gSelectedSquare = XY_TO_SQUARE(x, y);
refreshSquare(oldSquare);
selectSquare(gSelectedSquare);
}
static void moveUpRight(void)
{
tSquare oldSquare = gSelectedSquare;
tPos x = SQUARE_TO_X(gSelectedSquare);
tPos y = SQUARE_TO_Y(gSelectedSquare);
if (y == 0)
y = BOARD_SIZE - 1;
else
y--;
if (x == BOARD_SIZE - 1)
x = 0;
else
x++;
gSelectedSquare = XY_TO_SQUARE(x, y);
refreshSquare(oldSquare);
selectSquare(gSelectedSquare);
}
static void moveDown(void)
{
tSquare oldSquare = gSelectedSquare;
@ -174,6 +231,52 @@ static void moveDown(void)
}
static void moveDownLeft(void)
{
tSquare oldSquare = gSelectedSquare;
tPos x = SQUARE_TO_X(gSelectedSquare);
tPos y = SQUARE_TO_Y(gSelectedSquare);
if (y == BOARD_SIZE - 1)
y = 0;
else
y++;
if (x == 0)
x = BOARD_SIZE - 1;
else
x--;
gSelectedSquare = XY_TO_SQUARE(x, y);
refreshSquare(oldSquare);
selectSquare(gSelectedSquare);
}
static void moveDownRight(void)
{
tSquare oldSquare = gSelectedSquare;
tPos x = SQUARE_TO_X(gSelectedSquare);
tPos y = SQUARE_TO_Y(gSelectedSquare);
if (y == BOARD_SIZE - 1)
y = 0;
else
y++;
if (x == BOARD_SIZE - 1)
x = 0;
else
x++;
gSelectedSquare = XY_TO_SQUARE(x, y);
refreshSquare(oldSquare);
selectSquare(gSelectedSquare);
}
static void moveLeft(void)
{
tSquare oldSquare = gSelectedSquare;
@ -220,9 +323,13 @@ static bool swapUp(void)
return result;
}
resetStarAnim();
result = moveSquareInDir(gSelectedSquare, DIR_UP);
selectSquare(gSelectedSquare);
if (result)
gShouldSave = true;
return result;
}
@ -237,9 +344,13 @@ static bool swapDown(void)
return result;
}
resetStarAnim();
result = moveSquareInDir(gSelectedSquare, DIR_DOWN);
selectSquare(gSelectedSquare);
if (result)
gShouldSave = true;
return result;
}
@ -254,9 +365,13 @@ static bool swapLeft(void)
return result;
}
resetStarAnim();
result = moveSquareInDir(gSelectedSquare, DIR_LEFT);
selectSquare(gSelectedSquare);
if (result)
gShouldSave = true;
return result;
}
@ -271,21 +386,20 @@ static bool swapRight(void)
return result;
}
resetStarAnim();
result = moveSquareInDir(gSelectedSquare, DIR_RIGHT);
selectSquare(gSelectedSquare);
if (result)
gShouldSave = true;
return result;
}
static bool isAppleButtonPressed(void)
{
static uint8_t temp;
__asm__("LDA %w", BTN1);
__asm__("STA %v", temp);
return ((temp > 127) ? true : false);
return (isButtonPressed(JOY_BUTTON_0) || isButtonPressed(JOY_BUTTON_1));
}
@ -369,48 +483,224 @@ void initUI(void)
{
initGameEngine(&gCallbacks);
animInit();
initJoystick(&gJoyCallbacks);
}
static void joystickMove(tJoyPos position)
{
switch (position) {
case JOY_POS_DOWN:
moveDown();
break;
case JOY_POS_DOWN_LEFT:
moveDownLeft();
break;
case JOY_POS_LEFT:
moveLeft();
break;
case JOY_POS_UP_LEFT:
moveUpLeft();
break;
case JOY_POS_UP:
moveUp();
break;
case JOY_POS_UP_RIGHT:
moveUpRight();
break;
case JOY_POS_RIGHT:
moveRight();
break;
case JOY_POS_DOWN_RIGHT:
moveDownRight();
break;
default:
case JOY_POS_CENTER:
break;
}
}
static bool joystickChangedCallback(tJoyState *oldState, tJoyState *newState)
{
if (oldState->position != JOY_POS_CENTER)
return false;
if ((newState->button0) ||
(newState->button1)) {
switch (newState->position) {
case JOY_POS_UP:
return swapUp();
case JOY_POS_DOWN:
return swapDown();
case JOY_POS_LEFT:
return swapLeft();
case JOY_POS_RIGHT:
return swapRight();
default:
break;
}
return false;
}
joystickMove(newState->position);
return false;
}
static bool joystickNoChangeCallback(tJoyState *oldState)
{
if (oldState->button0)
return false;
if (oldState->button1)
return false;
joystickMove(oldState->position);
return false;
}
static bool pollKeyboard(void)
{
bool result = false;
uint8_t ch;
if (!kbhit())
return result;
ch = cgetc();
switch (ch) {
case 'i':
case 'I':
case CH_CURS_UP:
if (isAppleButtonPressed())
result = swapUp();
else
moveUp();
break;
case 'j':
case 'J':
case CH_CURS_LEFT:
if (isAppleButtonPressed())
result = swapLeft();
else
moveLeft();
break;
case 'k':
case 'K':
case CH_CURS_RIGHT:
if (isAppleButtonPressed())
result = swapRight();
else
moveRight();
break;
case 'm':
case 'M':
case CH_CURS_DOWN:
if (isAppleButtonPressed())
result = swapDown();
else
moveDown();
break;
case CH_ESC:
case 'q':
case 'Q':
if (gShouldSave) {
mixedTextMode();
videomode(VIDEOMODE_80x24);
gotoxy(0, 20);
cprintf("\n\nSaving your game so you can continue\r\n later...");
saveGame();
}
quitGame();
break;
case 'r':
case 'R':
refreshScore(0);
startNewGame();
gShouldSave = false;
return true;
case 's':
case 'S':
toggleSound();
break;
case 'h':
case 'H':
getHint();
break;
case '?':
printInstructions();
showAndClearDblLoRes();
drawBoard();
break;
default:
badThingHappened();
break;
}
return result;
}
void playGame(void)
{
static bool firstGame = true;
bool shouldSave = false;
bool gameLoaded = false;
bool checkForGameOver = false;
uint8_t ch;
gScoreBar = 0;
gShouldSave = false;
if (firstGame) {
firstGame = false;
printf("\n\nChecking for a saved game...");
printf("\n\nChecking for a saved game...");
if (loadGame()) {
bool gotAnswer = false;
if (loadGame()) {
bool gotAnswer = false;
printf("\n\nYou have a saved game!\n Would you like to continue it (Y/N)");
while (!gotAnswer) {
ch = cgetc();
switch (ch) {
case 'y':
case 'Y':
printf("\n\nLoading your saved puzzle");
gotAnswer = true;
shouldSave = true;
gameLoaded = true;
break;
case 'n':
case 'N':
gotAnswer = true;
break;
default:
badThingHappened();
break;
}
printf("\n\nYou have a saved game!\n Would you like to continue it (Y/N)");
while (!gotAnswer) {
ch = cgetc();
switch (ch) {
case 'y':
case 'Y':
printf("\n\nLoading your saved puzzle");
gotAnswer = true;
gShouldSave = true;
gameLoaded = true;
break;
case 'n':
case 'N':
gotAnswer = true;
break;
default:
badThingHappened();
break;
}
}
}
@ -421,97 +711,26 @@ void playGame(void)
}
drawBoard();
while (true) {
if ((checkForGameOver) &&
(gameIsOver())) {
endGame();
return;
}
checkForGameOver = false;
resetStarAnim();
beginStarAnim();
while (!kbhit()) {
while (true) {
doStarAnim();
if (pollKeyboard()) {
break;
}
if (pollJoystick()) {
break;
}
}
endStarAnim();
ch = cgetc();
switch (ch) {
case 'i':
case 'I':
case CH_CURS_UP:
shouldSave = true;
if (isAppleButtonPressed())
checkForGameOver = swapUp();
else
moveUp();
break;
case 'j':
case 'J':
case CH_CURS_LEFT:
shouldSave = true;
if (isAppleButtonPressed())
checkForGameOver = swapLeft();
else
moveLeft();
break;
case 'k':
case 'K':
case CH_CURS_RIGHT:
shouldSave = true;
if (isAppleButtonPressed())
checkForGameOver = swapRight();
else
moveRight();
break;
case 'm':
case 'M':
case CH_CURS_DOWN:
shouldSave = true;
if (isAppleButtonPressed())
checkForGameOver = swapDown();
else
moveDown();
break;
case CH_ESC:
case 'q':
case 'Q':
if (shouldSave) {
mixedTextMode();
videomode(VIDEOMODE_80x24);
gotoxy(0, 20);
cprintf("\n\nSaving your game so you can continue\r\n later...");
saveGame();
}
quitGame();
break;
case 'r':
case 'R':
return;
case 's':
case 'S':
toggleSound();
break;
case 'h':
case 'H':
getHint();
break;
case '?':
printInstructions();
showAndClearDblLoRes();
drawBoard();
break;
default:
badThingHappened();
break;
if (gameIsOver()) {
endGame();
showAndClearDblLoRes();
refreshScore(0);
startNewGame();
gShouldSave = false;
}
}
}