1 Commits

Author SHA1 Message Date
6a170522ac sound effects, not yet working 2024-04-18 13:26:18 +02:00
60 changed files with 421 additions and 905 deletions

View File

@ -76,7 +76,7 @@ int main() {
P1SEL |= BIT4 | BIT5 | BIT6 | BIT7; P1SEL |= BIT4 | BIT5 | BIT6 | BIT7;
P1SEL2 |= BIT4 | BIT5 | BIT6 | BIT7; P1SEL2 |= BIT4 | BIT5 | BIT6 | BIT7;
// most significant bit first, enable STE // most significant bit first, enable STE
UCB0CTL0 = UCCKPH | UCSYNC | UCMSB | UCMODE_2; UCB0CTL0 = UCSYNC | UCMSB | UCMODE_2;
UCB0CTL1 = 0x00; UCB0CTL1 = 0x00;
// enable RX interrupt // enable RX interrupt
UC0IE |= UCB0RXIE; UC0IE |= UCB0RXIE;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 603 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

View File

@ -11,7 +11,7 @@ CFLAGS=-Wall -mmcu=$(MCU) -std=gnu99 -I $(TOOLCHAIN_PREFIX)/include -O1 -g0
LDFLAGS=-mmcu=$(MCU) -L $(TOOLCHAIN_PREFIX)/include LDFLAGS=-mmcu=$(MCU) -L $(TOOLCHAIN_PREFIX)/include
$(ARTIFACT).elf: main.o spi.o scheduler.o canvas.o shapes.o game.o buttons.o myrand.o display.o sound.o eeprom.o config.o $(ARTIFACT).elf: main.o spi.o scheduler.o canvas.o shapes.o game.o buttons.o myrand.o display.o sound.o
$(CC) -o $@ $(LDFLAGS) $^ $(CC) -o $@ $(LDFLAGS) $^
$(OBJDUMP) -D $(ARTIFACT).elf > $(ARTIFACT).txt $(OBJDUMP) -D $(ARTIFACT).elf > $(ARTIFACT).txt

View File

@ -8,11 +8,8 @@
#include "shapes.h" #include "shapes.h"
#include "canvas.h" #include "canvas.h"
#include "sound.h" #include "sound.h"
#include "eeprom.h"
bool mutedFlag = true;
static uint8_t buttonsMoveLeftPressed() { static uint8_t buttonsMoveLeftPressed() {
static uint8_t last = 0; static uint8_t last = 0;
uint8_t current = (P2IN & BIT4); uint8_t current = (P2IN & BIT4);
@ -21,10 +18,6 @@ static uint8_t buttonsMoveLeftPressed() {
return res; return res;
} }
bool buttonsConfig1Pressed() {
return buttonsMoveLeftPressed();
}
static uint8_t buttonsMoveRightPressed() { static uint8_t buttonsMoveRightPressed() {
static uint8_t last = 0; static uint8_t last = 0;
uint8_t current = (P2IN & BIT0); uint8_t current = (P2IN & BIT0);
@ -33,10 +26,6 @@ static uint8_t buttonsMoveRightPressed() {
return res; return res;
} }
bool buttonsConfig4Pressed() {
return buttonsMoveRightPressed();
}
static uint8_t buttonsRotateLeftPressed() { static uint8_t buttonsRotateLeftPressed() {
static uint8_t last = 0; static uint8_t last = 0;
uint8_t current = (P2IN & BIT3); uint8_t current = (P2IN & BIT3);
@ -45,10 +34,6 @@ static uint8_t buttonsRotateLeftPressed() {
return res; return res;
} }
bool buttonsConfig2Pressed() {
return buttonsRotateLeftPressed();
}
static uint8_t buttonsRotateRightPressed() { static uint8_t buttonsRotateRightPressed() {
static uint8_t last = 0; static uint8_t last = 0;
uint8_t current = (P2IN & BIT1); uint8_t current = (P2IN & BIT1);
@ -57,23 +42,14 @@ static uint8_t buttonsRotateRightPressed() {
return res; return res;
} }
bool buttonsConfig3Pressed() {
return buttonsRotateRightPressed();
}
static uint8_t buttonsMoveDownPressed() { static uint8_t buttonsMoveDownPressed() {
return P2IN & BIT2; return P2IN & BIT2;
} }
bool isConfigMode() {
return (P2IN & BIT2);
}
void buttonsExec(void *handle) { void buttonsExec(void *handle) {
static uint32_t unmuteTimestamp; static uint32_t unmuteTimestamp;
uint32_t currentTimestamp = getSeconds(); uint32_t currentTimestamp = getSeconds();
static bool unmuteFlag = true;
if (! stoneIsValid()) { if (! stoneIsValid()) {
@ -85,51 +61,49 @@ void buttonsExec(void *handle) {
if (buttonsMoveLeftPressed()) { if (buttonsMoveLeftPressed()) {
stoneMoveLeft(); stoneMoveLeft();
soundCtrl(SOUND_MOTION);
buttonPressed = 1; buttonPressed = 1;
} }
if (buttonsMoveRightPressed()) { if (buttonsMoveRightPressed()) {
stoneMoveRight(); stoneMoveRight();
soundCtrl(SOUND_MOTION);
buttonPressed = 1; buttonPressed = 1;
} }
if (buttonsRotateLeftPressed()) { if (buttonsRotateLeftPressed()) {
stoneRotateLeft(); stoneRotateLeft();
soundCtrl(SOUND_MOTION);
buttonPressed = 1; buttonPressed = 1;
} }
if (buttonsRotateRightPressed()) { if (buttonsRotateRightPressed()) {
stoneRotateRight(); stoneRotateRight();
soundCtrl(SOUND_MOTION);
buttonPressed = 1; buttonPressed = 1;
} }
if (buttonsMoveDownPressed()) { if (buttonsMoveDownPressed()) {
stoneMoveDown(); stoneMoveDown();
soundCtrl(SOUND_MOTION);
buttonPressed = 1; buttonPressed = 1;
} }
if (buttonPressed == 1) { if (buttonPressed == 1) {
canvasShow(); canvasShow();
if (mutedFlag) { if (! unmuteFlag) {
eepromIncGameCounter();
soundCtrl(SOUND_UNMUTE); soundCtrl(SOUND_UNMUTE);
mutedFlag = false; unmuteFlag = true;
} }
unmuteTimestamp = currentTimestamp; unmuteTimestamp = currentTimestamp;
} }
if ((! mutedFlag) && (unmuteTimestamp + MUTE_DELAY < currentTimestamp)) { if (unmuteFlag && (unmuteTimestamp + MUTE_DELAY < currentTimestamp)) {
soundCtrl(SOUND_MUTE); soundCtrl(SOUND_MUTE);
mutedFlag = true; unmuteFlag = false;
} }
} }
void buttonsInit() { void buttonsInit() {
P2DIR &= ~(BIT0|BIT1|BIT2|BIT3|BIT4); P2DIR &= ~(BIT0|BIT1|BIT2|BIT3|BIT4);
}
void buttonsStart() {
schAdd(buttonsExec, NULL, 0, 25); schAdd(buttonsExec, NULL, 0, 25);
} }
bool isGameActive() {
return ! mutedFlag;
}

View File

@ -1,16 +1,7 @@
#ifndef _BUTTONS_H_ #ifndef _BUTTONS_H_
#define _BUTTONS_H_ #define _BUTTONS_H_
#include <stdbool.h>
void buttonsInit(); void buttonsInit();
void buttonsStart();
bool isGameActive();
bool isConfigMode();
bool buttonsConfig1Pressed();
bool buttonsConfig2Pressed();
bool buttonsConfig3Pressed();
bool buttonsConfig4Pressed();
#endif // _BUTTONS_H_ #endif // _BUTTONS_H_

View File

@ -5,8 +5,6 @@
#include "canvas.h" #include "canvas.h"
#include "spi.h" #include "spi.h"
#include "eeprom.h"
#include "../rgb-driver/colors.h"
static uint8_t canvasStorage[CANVAS_WIDTH * CANVAS_HEIGHT]; static uint8_t canvasStorage[CANVAS_WIDTH * CANVAS_HEIGHT];
@ -24,8 +22,6 @@ const canvas_t miniCanvas = {
}; };
void canvasShow() { void canvasShow() {
uint8_t brightness_offset = _brightness_offset * eepromReadBrightness();
// wait for signal waiting for data // wait for signal waiting for data
while (!(P1IN & BIT3)); while (!(P1IN & BIT3));
@ -35,14 +31,14 @@ void canvasShow() {
if ((*((canvas.canvas)+i) & 0x80) != 0) { if ((*((canvas.canvas)+i) & 0x80) != 0) {
*((canvas.canvas)+i) &= ~0x80; *((canvas.canvas)+i) &= ~0x80;
spiSendOctet(i); spiSendOctet(i);
spiSendOctet((*((canvas.canvas)+i) == 0) ? 0 : (*((canvas.canvas)+i) + brightness_offset)); spiSendOctet(*((canvas.canvas)+i));
} }
} }
for (uint8_t i = 0; i < (MINI_CANVAS_WIDTH*MINI_CANVAS_HEIGHT); i++) { for (uint8_t i = 0; i < (MINI_CANVAS_WIDTH*MINI_CANVAS_HEIGHT); i++) {
if ((*((miniCanvas.canvas)+i) & 0x80) != 0) { if ((*((miniCanvas.canvas)+i) & 0x80) != 0) {
*((miniCanvas.canvas)+i) &= ~0x80; *((miniCanvas.canvas)+i) &= ~0x80;
spiSendOctet(i + (CANVAS_HEIGHT*CANVAS_WIDTH)); spiSendOctet(i + (CANVAS_HEIGHT*CANVAS_WIDTH));
spiSendOctet((*((miniCanvas.canvas)+i) == 0) ? 0 : (*((miniCanvas.canvas)+i) + brightness_offset)); spiSendOctet(*((miniCanvas.canvas)+i));
} }
} }
spiSendOctet(0xfe); spiSendOctet(0xfe);

View File

@ -1,135 +0,0 @@
#include <stddef.h>
#include "config.h"
#include "canvas.h"
#include "../rgb-driver/colors.h"
#include "scheduler.h"
#include "buttons.h"
#include "eeprom.h"
#include "display.h"
#include "shapes.h"
#include "sound.h"
static bool configChanged = false;
static bool muted = false;
static void configHandleFlash() {
uint8_t color = eepromReadFlashColor();
canvasFillRow(CANVAS_HEIGHT-1, color);
displaySetValue(color);
if (buttonsConfig2Pressed()) {
configChanged = true;
color += 1;
if (color > _color_end) {
color = 0;
}
eepromSetFlashColor(color);
}
}
static void configHandleResetHighScore() {
displaySetValue(eepromReadHighScore());
if (buttonsConfig2Pressed()) {
configChanged = true;
eepromSetHighScore(0);
}
}
static void configHandleResetGameCounter() {
displaySetValue(eepromReadGameCounter());
if (buttonsConfig2Pressed()) {
configChanged = true;
eepromClearGameCounter(0);
}
}
static void configHandleBrightness() {
displaySetValue(eepromReadBrightness());
stoneDrawConfigPattern();
if (buttonsConfig2Pressed()) {
configChanged = true;
uint8_t brightness = eepromReadBrightness() + 1;
if (brightness > _brightness_shifts) {
brightness = 0;
}
eepromSetBrightness(brightness);
}
}
static void configHandleAmplitude() {
displaySetValue(eepromReadAmplitude());
if (muted) {
muted = false;
soundCtrl(SOUND_START);
soundCtrl(SOUND_UNMUTE);
}
if (buttonsConfig2Pressed()) {
configChanged = true;
uint8_t amplitude = eepromReadAmplitude() + 1;
if (amplitude > 15) {
amplitude = 0;
}
eepromSetAmplitude(amplitude);
soundCtrl(SOUND_COMMAND + SOUND_SUBCMD_AMPLITUDE + amplitude);
}
}
void (*configHandler[])(void) = {
configHandleResetHighScore,
configHandleResetGameCounter,
configHandleFlash,
configHandleBrightness,
configHandleAmplitude
};
void configExec(void *handle) {
static uint8_t configState = 0;
static uint8_t lastConfigState = 255;
if (configState != lastConfigState) {
lastConfigState = configState;
miniCanvasClear();
canvasClear();
if (! muted) {
muted = true;
soundCtrl(SOUND_MUTE);
}
uint8_t row = configState / 3;
uint8_t column = configState % 3;
miniCanvasSetPixel(column, row, _red);
}
if (buttonsConfig1Pressed()) {
configState += 1;
if (configState >= sizeof(configHandler) / sizeof(configHandler[0])) {
configState = 0;
}
}
configHandler[configState]();
if (configChanged) {
miniCanvasSetPixel(0, 3, _red);
if (buttonsConfig4Pressed()) {
eepromCommit();
configChanged = false;
}
} else {
miniCanvasSetPixel(0, 3, _green);
}
canvasShow();
}
void configInit() {
schAdd(configExec, NULL, 0, 100);
}

View File

@ -1,8 +0,0 @@
#ifndef _CONFIG_H_
#define _CONFIG_H_
void configInit();
#endif // _CONFIG_H_

View File

@ -1,153 +0,0 @@
#include <stdint.h>
#include <string.h>
#include <sys/param.h>
#include "eeprom.h"
#include "spi.h"
#include "scheduler.h"
#include "display.h"
#include "canvas.h"
#include "../rgb-driver/colors.h"
#define MAGIC 0xb003
#define HIGHSCORE_ADDR 0x00
#define DUMMY 0x00
#define CMD_READ 0b00000011
#define CMD_WRITE 0b00000010
#define CMD_WRDI 0b00000100
#define CMD_WREN 0b00000110
typedef struct {
uint16_t magic;
uint16_t highScore;
uint16_t gameCounter;
uint8_t flashColor;
uint8_t brightness;
uint8_t amplitude;
} t_configBlock;
typedef union {
t_configBlock v;
uint8_t buffer[sizeof(t_configBlock)];
} eepromBuf_t;
eepromBuf_t buf;
static void writeBuf() {
spiSendBegin(e_SPI_EEPROM);
spiSendOctet(CMD_WREN);
spiSendEnd(e_SPI_EEPROM);
spiSendBegin(e_SPI_EEPROM);
spiSendOctet(CMD_WRITE);
spiSendOctet(HIGHSCORE_ADDR);
for (uint8_t i = 0; i < sizeof(t_configBlock); i++) {
spiSendOctet(buf.buffer[i]);
}
spiSendEnd(e_SPI_EEPROM);
}
static void readBuf() {
spiSendBegin(e_SPI_EEPROM);
spiSendOctet(CMD_READ);
spiReceiveOctet();
spiSendOctet(HIGHSCORE_ADDR);
spiReceiveOctet();
for (uint8_t i = 0; i < sizeof(t_configBlock); i++) {
spiSendOctet(DUMMY);
buf.buffer[i] = spiReceiveOctet();
}
spiSendEnd(e_SPI_EEPROM);
}
void eepromInit() {
readBuf();
if (buf.v.magic != MAGIC) {
memset(buf.buffer, 0, sizeof(t_configBlock));
buf.v.magic = MAGIC;
writeBuf();
}
}
void eepromCommit() {
writeBuf();
}
void eepromShowValues() {
canvasClear();
canvasFillRow(0, _green);
canvasShow();
displaySetValue(buf.v.highScore);
wait(2);
canvasClear();
canvasFillRow(1, _green);
canvasShow();
displaySetValue(MIN(buf.v.gameCounter, 9999));
wait(2);
canvasClear();
canvasFillRow(2, _green);
canvasShow();
displaySetValue(buf.v.flashColor);
wait(2);
canvasClear();
canvasFillRow(3, _green);
canvasShow();
displaySetValue(buf.v.brightness);
wait(2);
canvasClear();
canvasFillRow(4, _green);
canvasShow();
displaySetValue(buf.v.amplitude);
wait(2);
}
uint16_t eepromReadHighScore() {
return buf.v.highScore;
}
void eepromSetHighScore(uint16_t v) {
buf.v.highScore = v;
writeBuf();
}
uint8_t eepromReadFlashColor() {
return buf.v.flashColor;
}
void eepromSetFlashColor(uint8_t v) {
buf.v.flashColor = v;
}
uint8_t eepromReadBrightness() {
return buf.v.brightness;
}
void eepromSetBrightness(uint8_t v) {
buf.v.brightness = v;
}
uint8_t eepromReadAmplitude() {
return buf.v.amplitude;
}
void eepromSetAmplitude(uint8_t v) {
buf.v.amplitude = v;
}
uint16_t eepromReadGameCounter() {
return buf.v.gameCounter;
}
void eepromIncGameCounter() {
buf.v.gameCounter += 1;
writeBuf();
}
void eepromClearGameCounter() {
buf.v.gameCounter = 0;
}

View File

@ -1,24 +0,0 @@
#ifndef _EEPROM_H_
#define _EEPROM_H_
#include <stdint.h>
void eepromInit();
void eepromCommit();
void eepromShowValues();
uint16_t eepromReadHighScore();
void eepromSetHighScore(uint16_t v);
uint8_t eepromReadFlashColor();
void eepromSetFlashColor(uint8_t v);
uint8_t eepromReadBrightness();
void eepromSetBrightness(uint8_t v);
uint8_t eepromReadAmplitude();
void eepromSetAmplitude(uint8_t v);
void eepromIncGameCounter();
uint16_t eepromReadGameCounter();
void eepromClearGameCounter();
#endif // _EEPROM_H_

View File

@ -1,5 +1,3 @@
// #define STATE_DEBUGGING
#include "stddef.h" #include "stddef.h"
#include "stdint.h" #include "stdint.h"
@ -10,58 +8,44 @@
#include "../rgb-driver/colors.h" #include "../rgb-driver/colors.h"
#include "display.h" #include "display.h"
#include "sound.h" #include "sound.h"
#include "eeprom.h"
#include "buttons.h"
#define GAME_CYCLE_TIME 10 #define GAME_CYCLE_TIME 100
#define GAMEOVER_DELAY 10 #define GAMEOVER_DELAY 10
#define MAX_LEVEL 100
static uint16_t delayFactor(uint16_t level) { static uint8_t delayFactor(uint8_t level) {
return MAX_LEVEL + 1 - level; return 11 - level;
} }
typedef enum { typedef enum {
e_BootWait, e_Phase_Game, e_Phase_GameOver
e_Start, e_NewStone, e_Down, e_DownDelay, } phase_t;
e_ClearRowInit, e_ClearRowNext, e_ClearRowCheck, e_ClearRowFlash, e_ClearRowFlashDelay, e_ClearRowWipe,
typedef enum {
e_Start, e_NewStone, e_Down, e_DownDelay, e_ClearRows,
e_GameOver, e_GameOverFill, e_GameOverWipe, e_GameOverDelay e_GameOver, e_GameOverFill, e_GameOverWipe, e_GameOverDelay
} state_t; } state_t;
void gameExec(void *handle) { void gameExec(void *handle) {
static state_t state = e_BootWait; static phase_t phase;
static uint16_t bootWaitTime = 2500 / GAME_CYCLE_TIME; static state_t state = e_Start;
static uint8_t gameOverDelay; static uint8_t gameOverDelay;
static uint8_t rowIndex; static uint8_t rowIndex;
static uint16_t proceedDelay; static uint8_t proceedDelay;
static uint16_t level; static uint8_t level;
static uint16_t filledLines;
static uint16_t score; static uint16_t score;
static bool newHighScoreAchieved;
static uint8_t clearCheckCnt;
#ifdef STATE_DEBUGGING
displaySetValue(state);
#endif
// --- engine begin ------------------------------------------------------- // --- engine begin -------------------------------------------------------
switch (state) { switch (state) {
case e_BootWait:
bootWaitTime -= 1;
if (bootWaitTime == 0) {
state = e_Start;
}
break;
// --- phase: game -------------------------------------------------------- // --- phase: game --------------------------------------------------------
case e_Start: case e_Start:
canvasClear(); canvasClear();
soundCtrl(SOUND_START); soundCtrl(SOUND_START);
level = 1; level = 1;
filledLines = 0;
score = 0; score = 0;
newHighScoreAchieved = false; displaySetValue(score);
phase = e_Phase_Game;
state = e_NewStone; state = e_NewStone;
break; break;
@ -78,84 +62,36 @@ void gameExec(void *handle) {
case e_DownDelay: case e_DownDelay:
proceedDelay--; proceedDelay--;
if (proceedDelay == 0) { if (proceedDelay == 0) {
state = e_Down; rowIndex = 0;
state = e_ClearRows;
} }
break; break;
case e_ClearRows:
state = e_Down;
break;
case e_Down: case e_Down:
if (! stoneMoveDown()) { if (! stoneMoveDown()) {
soundCtrl(SOUND_LOCK); soundCtrl(SOUND_LOCK);
stoneLock(); state = e_NewStone;
state = e_ClearRowInit;
} else { } else {
proceedDelay = delayFactor(level); proceedDelay = delayFactor(level);
state = e_DownDelay; state = e_DownDelay;
} }
break; break;
// --- phase: clear rows --------------------------------------------------
case e_ClearRowInit:
clearCheckCnt = 0;
state = e_ClearRowCheck;
break;
case e_ClearRowNext:
if (clearCheckCnt >= CANVAS_HEIGHT) {
state = e_NewStone;
} else {
clearCheckCnt += 1;
state = e_ClearRowCheck;
}
break;
case e_ClearRowCheck:
if (canvasIsRowFilled(clearCheckCnt)) {
score += level;
if (score > eepromReadHighScore()) {
newHighScoreAchieved = true;
eepromSetHighScore(score);
eepromCommit();
}
state = e_ClearRowFlash;
} else {
state = e_ClearRowNext;
}
break;
case e_ClearRowFlash:
canvasFillRow(clearCheckCnt, eepromReadFlashColor());
state = e_ClearRowFlashDelay;
break;
case e_ClearRowFlashDelay:
state = e_ClearRowWipe;
break;
case e_ClearRowWipe:
canvasWipeRow(clearCheckCnt);
filledLines += 1;
if ((filledLines > 0) && ((filledLines % 10) == 0)) {
if (level < MAX_LEVEL) {
level += 1;
}
soundCtrl(SOUND_FANFARE);
} else {
soundCtrl(SOUND_PLING);
}
state = e_ClearRowNext;
break;
// --- phase: game over --------------------------------------------------- // --- phase: game over ---------------------------------------------------
case e_GameOver: case e_GameOver:
soundCtrl(SOUND_GAMEOVER); soundCtrl(SOUND_GAMEOVER);
rowIndex = CANVAS_HEIGHT; rowIndex = CANVAS_HEIGHT;
phase = e_Phase_GameOver;
state = e_GameOverFill; state = e_GameOverFill;
break; break;
case e_GameOverFill: case e_GameOverFill:
rowIndex--; rowIndex--;
canvasFillRow(rowIndex, newHighScoreAchieved ? _green : _red); canvasFillRow(rowIndex, _red);
if (rowIndex == 0) { if (rowIndex == 0) {
state = e_GameOverWipe; state = e_GameOverWipe;
} }
@ -180,14 +116,21 @@ void gameExec(void *handle) {
// --- engine end --------------------------------------------------------- // --- engine end ---------------------------------------------------------
canvasShow(); canvasShow();
if (phase == e_Phase_Game) {
#ifndef STATE_DEBUGGING uint8_t wipeCnt = 0;
if (isGameActive()) { for (uint8_t r = 0; r < CANVAS_HEIGHT; r++) {
if (canvasIsRowFilled(r)) {
score += level;
displaySetValue(score); displaySetValue(score);
} else { canvasWipeRow(r);
displaySetValue(eepromReadHighScore()); canvasShow();
wipeCnt += 1;
}
}
if (wipeCnt != 0) {
soundCtrl(SOUND_FANFARE);
}
} }
#endif
} }
void gameInit() { void gameInit() {

View File

@ -12,9 +12,6 @@
#include "myrand.h" #include "myrand.h"
#include "spi.h" #include "spi.h"
#include "display.h" #include "display.h"
#include "eeprom.h"
#include "config.h"
#include "sound.h"
int main() { int main() {
@ -31,22 +28,13 @@ int main() {
schInit(); schInit();
spiInit(); spiInit();
eepromInit();
displayInit(); displayInit();
myRandInit(); myRandInit();
canvasInit(); canvasInit();
soundInit();
buttonsInit();
eepromShowValues();
if (isConfigMode()) {
configInit();
} else {
shapesInit(); shapesInit();
gameInit(); gameInit();
buttonsStart(); buttonsInit();
}
__enable_interrupt(); __enable_interrupt();

View File

@ -109,9 +109,3 @@ uint32_t getSeconds() {
return s; return s;
} }
void wait(uint8_t t) {
uint8_t startTime = getSeconds();
while (getSeconds() < (startTime + t));
}

View File

@ -32,6 +32,6 @@ void schExec();
void schUpdate(); void schUpdate();
uint8_t schTaskCnt(); uint8_t schTaskCnt();
uint32_t getSeconds(); uint32_t getSeconds();
void wait(uint8_t t);
#endif /* PONTCOOPSCHEDULER_H_ */ #endif /* PONTCOOPSCHEDULER_H_ */

View File

@ -1,7 +1,6 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h>
#include "shapes.h" #include "shapes.h"
#include "myrand.h" #include "myrand.h"
@ -20,7 +19,6 @@ typedef struct {
orientation_t orientation; orientation_t orientation;
uint8_t x; // column uint8_t x; // column
uint8_t y; // row uint8_t y; // row
bool locked;
} stone_t; } stone_t;
typedef struct { typedef struct {
@ -354,26 +352,17 @@ const orientation_t nextOrientation[5][4] = { // 5 = number of directions to mov
stone_t stone; stone_t stone;
shape_t nextShape; shape_t nextShape;
static shape_t randomNextShape() {
return ((shape_t[]){ e_I, e_O, e_T, e_Z, e_S, e_L, e_J })[myRandGet() % e_ShapeInvalid];
}
void shapesInit() { void shapesInit() {
stone.shape = e_ShapeInvalid; stone.shape = e_ShapeInvalid;
nextShape = randomNextShape(); nextShape = e_Z;
} }
void stoneCreate() { void stoneCreate() {
stone.shape = nextShape; stone.shape = nextShape;
nextShape = randomNextShape(); nextShape = ((shape_t[]){ e_I, e_O, e_T, e_Z, e_S, e_L, e_J })[myRandGet() % e_ShapeInvalid];
stone.orientation = e_0; stone.orientation = e_0;
stone.x = 4; stone.x = 4;
stone.y = 0; stone.y = 0;
stone.locked = false;
}
void stoneLock() {
stone.locked = true;
} }
uint8_t stoneIsValid() { uint8_t stoneIsValid() {
@ -387,12 +376,6 @@ static uint8_t move(direction_t direction) {
if (motions[stone.shape].nullRotation && (direction == e_RotateLeft || direction == e_RotateRight)) { if (motions[stone.shape].nullRotation && (direction == e_RotateLeft || direction == e_RotateRight)) {
return 1; return 1;
} }
// if the stone is already locked, do nothing
if (stone.locked) {
return 0;
}
// check whether the pixels to move to are free // check whether the pixels to move to are free
if (canvasIsPixelFree(stone.x + motions[stone.shape].motion[direction][stone.orientation].set[0].x, if (canvasIsPixelFree(stone.x + motions[stone.shape].motion[direction][stone.orientation].set[0].x,
stone.y + motions[stone.shape].motion[direction][stone.orientation].set[0].y) && stone.y + motions[stone.shape].motion[direction][stone.orientation].set[0].y) &&
@ -456,21 +439,6 @@ void nextStoneDraw() {
motions[nextShape].color); motions[nextShape].color);
} }
static void stoneJustDraw(uint8_t x, uint8_t y, shape_t shape) {
canvasSetPixel(x + motions[shape].draw[0].x,
y + motions[shape].draw[0].y,
motions[shape].color);
canvasSetPixel(x + motions[shape].draw[1].x,
y + motions[shape].draw[1].y,
motions[shape].color);
canvasSetPixel(x + motions[shape].draw[2].x,
y + motions[shape].draw[2].y,
motions[shape].color);
canvasSetPixel(x + motions[shape].draw[3].x,
y + motions[shape].draw[3].y,
motions[shape].color);
}
uint8_t stoneDraw() { uint8_t stoneDraw() {
nextStoneDraw(); nextStoneDraw();
@ -485,22 +453,23 @@ uint8_t stoneDraw() {
canvasIsPixelFree(stone.x + motions[stone.shape].draw[3].x, canvasIsPixelFree(stone.x + motions[stone.shape].draw[3].x,
stone.y + motions[stone.shape].draw[3].y)) { stone.y + motions[stone.shape].draw[3].y)) {
// if so, draw the shape // if so, draw the shape
stoneJustDraw(stone.x, stone.y, stone.shape); canvasSetPixel(stone.x + motions[stone.shape].draw[0].x,
stone.y + motions[stone.shape].draw[0].y,
motions[stone.shape].color);
canvasSetPixel(stone.x + motions[stone.shape].draw[1].x,
stone.y + motions[stone.shape].draw[1].y,
motions[stone.shape].color);
canvasSetPixel(stone.x + motions[stone.shape].draw[2].x,
stone.y + motions[stone.shape].draw[2].y,
motions[stone.shape].color);
canvasSetPixel(stone.x + motions[stone.shape].draw[3].x,
stone.y + motions[stone.shape].draw[3].y,
motions[stone.shape].color);
res = 1; res = 1;
} }
return res; return res;
} }
void stoneDrawConfigPattern() {
stoneJustDraw(1, 0, e_I);
stoneJustDraw(3, 4, e_O);
stoneJustDraw(4, 7, e_T);
stoneJustDraw(5, 10, e_Z);
stoneJustDraw(1, 12, e_S);
stoneJustDraw(5, 15, e_L);
stoneJustDraw(1, 17, e_J);
}
uint8_t stoneMoveDown() { uint8_t stoneMoveDown() {
return move(e_MoveDown); return move(e_MoveDown);
} }

View File

@ -5,7 +5,6 @@
void shapesInit(); void shapesInit();
void stoneCreate(); void stoneCreate();
void stoneLock();
uint8_t stoneIsValid(); uint8_t stoneIsValid();
uint8_t stoneDraw(); uint8_t stoneDraw();
uint8_t stoneMoveDown(); uint8_t stoneMoveDown();
@ -14,6 +13,5 @@ uint8_t stoneMoveRight();
uint8_t stoneRotateLeft(); uint8_t stoneRotateLeft();
uint8_t stoneRotateRight(); uint8_t stoneRotateRight();
void stoneDrawConfigPattern();
#endif // _SHAPES_H_ #endif // _SHAPES_H_

View File

@ -1,12 +1,10 @@
#include <stdint.h> #include <stdint.h>
#include "sound.h" #include "sound.h"
#include "spi.h" #include "spi.h"
#include "eeprom.h"
void soundInit() { void soundInit() {
soundCtrl(SOUND_COMMAND + SOUND_SUBCMD_AMPLITUDE + eepromReadAmplitude());
} }

View File

@ -3,7 +3,7 @@
void spiInit() { void spiInit() {
// SPI in master mode, most significant bit first // SPI in master mode, most significant bit first
UCB0CTL0 = UCCKPH | UCMST | UCMSB; UCB0CTL0 = UCMST | UCMSB;
// SPI timing config // SPI timing config
UCB0CTL1 = UCSSEL_3; UCB0CTL1 = UCSSEL_3;
// Faster than 8 ends up in strange communication errors // Faster than 8 ends up in strange communication errors
@ -19,25 +19,25 @@ void spiInit() {
// BIT7: UCB0SIMO // BIT7: UCB0SIMO
P1SEL |= BIT5 | BIT6 | BIT7; P1SEL |= BIT5 | BIT6 | BIT7;
P1SEL2 |= BIT5 | BIT6 | BIT7; P1SEL2 |= BIT5 | BIT6 | BIT7;
// P1DIR |= BIT5 | BIT7; P1DIR |= BIT5 | BIT7;
// Device Select Lines: 0: Canvas, 1: Display, 2: Sound, 4: EEPROM // Device Select Lines: 0: Canvas, 1: Display, 2: Sound
P1DIR |= BIT0 | BIT1 | BIT2 | BIT4; P1DIR |= BIT0 | BIT1 | BIT2;
// Disable all of them // Disable all of them
P1OUT |= BIT0 | BIT1 | BIT2 | BIT4; P1OUT |= BIT0 | BIT1 | BIT2;
// enable SPI module // enable SPI module
UCB0CTL1 &= ~UCSWRST; UCB0CTL1 &= ~UCSWRST;
} }
void spiSendBegin(t_SpiDeviceSelector d) { void spiSendBegin(t_SpiDeviceSelector d) {
uint16_t bit = ((uint16_t[]){ BIT0, BIT1, BIT2, BIT4 })[d]; uint16_t bit = ((uint16_t[]){ BIT0, BIT1, BIT2 })[d];
P1OUT &= ~bit; P1OUT &= ~bit;
} }
void spiSendEnd(t_SpiDeviceSelector d) { void spiSendEnd(t_SpiDeviceSelector d) {
while (UCB0STAT & UCBUSY); while (UCB0STAT & UCBUSY);
uint16_t bit = ((uint16_t[]){ BIT0, BIT1, BIT2, BIT4 })[d]; uint16_t bit = ((uint16_t[]){ BIT0, BIT1, BIT2 })[d];
P1OUT |= bit; P1OUT |= bit;
} }
@ -48,9 +48,3 @@ void spiSendOctet(uint8_t v) {
UCB0TXBUF = v; UCB0TXBUF = v;
} }
uint8_t spiReceiveOctet() {
while (!(UC0IFG & UCB0RXIFG));
uint8_t v = UCB0RXBUF;
return v;
}

View File

@ -4,13 +4,12 @@
#include <stdint.h> #include <stdint.h>
typedef enum { e_SPI_CANVAS, e_SPI_DISPLAY, e_SPI_SOUND, e_SPI_EEPROM } t_SpiDeviceSelector; typedef enum { e_SPI_CANVAS, e_SPI_DISPLAY, e_SPI_SOUND } t_SpiDeviceSelector;
void spiInit(); void spiInit();
void spiSendBegin(t_SpiDeviceSelector d); void spiSendBegin(t_SpiDeviceSelector d);
void spiSendEnd(t_SpiDeviceSelector d); void spiSendEnd(t_SpiDeviceSelector d);
void spiSendOctet(uint8_t v); void spiSendOctet(uint8_t v);
uint8_t spiReceiveOctet();

View File

@ -1,70 +0,0 @@
# Tetris - Hardware and Software
![](./docs/IMG_4936.jpg)
Update Amplifier (separate input circuitry per PSG, it appears, that a silent PSG has a DC level on its output which is summarized to the AC output of the working PSG, so two input circuits with individual couping capacitor):
![](./docs/IMG_4941.jpg)
Update of the power switch of the amplifier (at appears, that the small transistor couldn't deliver enough current):
![](./docs/IMG_4958.jpeg)
This Tetris implementation consists of a hardware and a software (running on that hardware).
The hardware utilizes four MSP430 microcontrollers for 1.) the game play, 2.) the play ground canvas, 3.) the score display and 4.) the sound effects.
Further documentation including calculations and drawing can be found in the `docs` subdirs of the four main subdirs.
## Game Play
Code is in subdir `game-ctrl` (https://gitea.hottis.de/wn/tetris/src/branch/main/game-ctrl).
In the firmware for this MSP430 microcontroller the whole game mechanics, reading the buttons, reading and writing the highscore EEPROM and the control of the peripherial microcontrollers are implemented.
The buttons are debounced using RC circuitry and Schmitt triggers and connected to GPIOs of the microcontroller.
The peripherial microcontrollers and the EEPROM are connected via SPI including individual chip select lines.
![](./docs/game-ctrl.jpg)
## Play Ground Canvas
Code is in subdir `rgb-driver` (https://gitea.hottis.de/wn/tetris/src/branch/main/rgb-driver).
The play ground is implemented using a 10 * 20 matrix of PL9823 RGB LEDs which are controlled by another MSP430 microcontroller. The firmware for this microcontroller is implemented for performance and real time requirements in assembly code. Through some discret logic the signals for PL9823 LEDs are generated. Major challenge was to generated the signals according the datasheet of all 200 (including a mini canvas for the stone preview: 212) LEDs in real time without interrupts.
The communcation with the game play controller is implemented as a sequences of tuples of LED address (0 to 211) and color code. A single octet of 253 where the LED address is expected is taken as the end-of-telegram mark. Readiness to receive a telegram is signaled to the game play controller via a single line connected to a GPIO of the game play controller.
![](./docs/rgb-driver.jpg)
Details are here https://gitea.hottis.de/wn/tetris/src/branch/main/rgb-driver/readme.md
## Score Display
Code is in subdir `display-driver` (https://gitea.hottis.de/wn/tetris/src/branch/main/display-driver).
In the first place, a MAX7221 was meant to be used for connecting a multiple digit seven-segment display. However, it appears, that the MAX7221 requires 3.5V as minimum voltage for the high-level, which caan't be provided by the MSP430 (which runs on 3.3V) and level-shifters haven't been around. Thus, the minimal required amount of functionality of the MAX7221 has been implemented in C on an MSP430. Just four digits are supported.
Communication with the game play controller is just a 16 bit number to be displayed.
![](./docs/display-driver.jpg)
## Sound Effects
Code is in subdir `sound-driver` (https://gitea.hottis.de/wn/tetris/src/branch/main/sound-driver).
An MSP430 microcontroller and two mediaeval AY-3-8913 sound chips are deployed. The sound chips themselve run on 5V, their 8-bit-address/data bus is connected to the port 2 (bit 0 to 7) of the microcontroller. The bus control signal `_CS`, `BC1` and `BDIR` are generated in software and provided via GPIOs.
An amplifier following the proposal of the AY-3-8913 datasheet is implemented using a LM386 chip. A MOSFET BS108 controlled via a GPIO is use the shortcut the input of the amplifier to ground to mute sound effects.
The clock generator proposed by the AY-3-8913 does not work reliably, so an alternative design from "The Art of Electronics" has been used.
![](./docs/sound-driver-1.jpg)
![](./docs/sound-driver-2.png)
![](./docs/sound-driver-3.jpg)
![](./docs/sound-driver-4.jpg)

View File

@ -1,55 +1,38 @@
#include "colors.h" #include "colors.h"
#define DIMM_FACTOR 3
.section ".rodata","a" .section ".rodata","a"
;; color definitions according to ;; color definitions according to
;; https://learn.sparkfun.com/tutorials/lilypad-protosnap-plus-activity-guide/3-custom-color-mixing ;; https://learn.sparkfun.com/tutorials/lilypad-protosnap-plus-activity-guide/3-custom-color-mixing
colors: colors:
.global colors .global colors
;; red, green, blue, padding ;; red, green, blue, padding
.byte 0x00, 0x00, 0x00, 0 ;; off off:
.byte 0x00, 0x00, 0x00, 0
.byte 0x00>>5, 0x00>>5, 0xff>>5, 0 ;; blue blue:
.byte 0x00>>5, 0xff>>5, 0x00>>5, 0 ;; green .byte 0x00>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0
.byte 0xff>>5, 0x80>>5, 0x00>>5, 0 ;; orange green:
.byte 0x80>>5, 0x00>>5, 0xff>>5, 0 ;; violet .byte 0x00>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0
.byte 0x00>>5, 0xff>>5, 0xff>>5, 0 ;; cyan orange:
.byte 0xff>>5, 0xff>>5, 0x00>>5, 0 ;; yellow .byte 0xff>>DIMM_FACTOR, 0x80>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0
.byte 0xff>>5, 0x00>>5, 0x00>>5, 0 ;; red rose:
.byte 0xff>>5, 0xff>>5, 0xff>>5, 0 ;; white .byte 0xff>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0x80>>DIMM_FACTOR, 0
magenta:
.byte 0x00>>4, 0x00>>4, 0xff>>4, 0 ;; blue .byte 0xff>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0
.byte 0x00>>4, 0xff>>4, 0x00>>4, 0 ;; green violet:
.byte 0xff>>4, 0x80>>4, 0x00>>4, 0 ;; orange .byte 0x80>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0
.byte 0x80>>4, 0x00>>4, 0xff>>4, 0 ;; violet azure:
.byte 0x00>>4, 0xff>>4, 0xff>>4, 0 ;; cyan .byte 0x00>>DIMM_FACTOR, 0x80>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0
.byte 0xff>>4, 0xff>>4, 0x00>>4, 0 ;; yellow cyan:
.byte 0xff>>4, 0x00>>4, 0x00>>4, 0 ;; red .byte 0x00>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0
.byte 0xff>>4, 0xff>>4, 0xff>>4, 0 ;; white springgreen:
.byte 0x00>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0x80>>DIMM_FACTOR, 0
.byte 0x00>>3, 0x00>>3, 0xff>>3, 0 ;; blue chartreuse:
.byte 0x00>>3, 0xff>>3, 0x00>>3, 0 ;; green .byte 0x80>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0
.byte 0xff>>3, 0x80>>3, 0x00>>3, 0 ;; orange yellow:
.byte 0x80>>3, 0x00>>3, 0xff>>3, 0 ;; violet .byte 0xff>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0
.byte 0x00>>3, 0xff>>3, 0xff>>3, 0 ;; cyan white:
.byte 0xff>>3, 0xff>>3, 0x00>>3, 0 ;; yellow .byte 0xff>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0
.byte 0xff>>3, 0x00>>3, 0x00>>3, 0 ;; red red:
.byte 0xff>>3, 0xff>>3, 0xff>>3, 0 ;; white .byte 0xff>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0
.byte 0x00>>2, 0x00>>2, 0xff>>2, 0 ;; blue
.byte 0x00>>2, 0xff>>2, 0x00>>2, 0 ;; green
.byte 0xff>>2, 0x80>>2, 0x00>>2, 0 ;; orange
.byte 0x80>>2, 0x00>>2, 0xff>>2, 0 ;; violet
.byte 0x00>>2, 0xff>>2, 0xff>>2, 0 ;; cyan
.byte 0xff>>2, 0xff>>2, 0x00>>2, 0 ;; yellow
.byte 0xff>>2, 0x00>>2, 0x00>>2, 0 ;; red
.byte 0xff>>2, 0xff>>2, 0xff>>2, 0 ;; white
.byte 0x00>>1, 0x00>>1, 0xff>>1, 0 ;; blue
.byte 0x00>>1, 0xff>>1, 0x00>>1, 0 ;; green
.byte 0xff>>1, 0x80>>1, 0x00>>1, 0 ;; orange
.byte 0x80>>1, 0x00>>1, 0xff>>1, 0 ;; violet
.byte 0x00>>1, 0xff>>1, 0xff>>1, 0 ;; cyan
.byte 0xff>>1, 0xff>>1, 0x00>>1, 0 ;; yellow
.byte 0xff>>1, 0x00>>1, 0x00>>1, 0 ;; red
.byte 0xff>>1, 0xff>>1, 0xff>>1, 0 ;; white

View File

@ -2,20 +2,21 @@
#define _COLORS_H_ #define _COLORS_H_
#define _off 0 #define _off 0x00
#define _blue 0x01
#define _green 0x02
#define _orange 0x03
#define _rose 0x04
#define _magenta 0x05
#define _violet 0x06
#define _azure 0x07
#define _cyan 0x08
#define _springgreen 0x09
#define _chartreuse 0x0a
#define _yellow 0x0b
#define _white 0x0c
#define _red 0x0d
#define _blue 1
#define _green 2
#define _orange 3
#define _violet 4
#define _cyan 5
#define _yellow 6
#define _red 7
#define _white 8
#define _brightness_offset 8
#define _brightness_shifts 5
#define _color_end (_brightness_offset * _brightness_shifts)
#endif #endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

View File

@ -123,7 +123,7 @@ init:
;; spi configuration ;; spi configuration
;; USCI B to slave mode, enable STE and most significant bit first ;; USCI B to slave mode, enable STE and most significant bit first
mov.b #UCCKPH|UCSYNC|UCMODE_2|UCMSB, &UCB0CTL0 mov.b #UCSYNC|UCMODE_2|UCMSB, &UCB0CTL0
mov.b #0x00, &UCB0CTL1 mov.b #0x00, &UCB0CTL1
;; make sure the isr will not immediately start ;; make sure the isr will not immediately start

View File

@ -26,37 +26,6 @@ Schematics and legend for signals:
![](./docs/schematics.jpeg) ![](./docs/schematics.jpeg)
### Some more explanations
Consider above schematics and the screen shot "Last octets" from the oscilloscope.
![](./docs/timing.png)
Timer TA1 is running in "up mode" to the value 45 set in compare register `TA1CCR0`. The compare registers `TA1CCR1` is set to 10, `TA1CCR2` is set to 22.
The output mode of the timer is set to "Reset/Set", which means the GPIO associated with `TA1CCR1` (P2.1) and `TA1CCR2` (P2.4) are set at the overflow and
restart of the counter and reset when the counter matches the associated compare value.
So, on P2.1 (D1 on the oscilloscope) we have a long pulse and at P2.4 (D0 on the oscilloscope) we have a short pulse, with synchronous raising edge.
![](./docs/74hc74-function-table.png)
The inverted signal P2.4 is connected to the Clock input of a 74HC74 D-flipflop, the data input of the flipflop is connected to GPIO P1.0 (D2 on the oscilloscope).
The interrupt service routine `shifter_isr` is triggered by the overflow and restart of the timer, this interrupt service routine provides the next bit to be
signaled on P1.0. This bit is stored at the falling edge of P2.4 (long pulse) in the flipflop.
The short pulse (P2.1, D1) is ANDed using a 74HC08 with the inverted output of the flipflop, the long pulse (P2.4, D0) is ANDed with the non-inverted output of
the flipflop, the ANDed results are ORed using a 74HC32.
So, at the output of the OR gate (yellow on the oscilloscope) we get a long pulse for a 1 at P1.0 provided by the ISR and a short pulse for a 0 at P1.0.
The routine `drawscreen` takes color values from the "frame buffer" beginning at `screendata` and translated them into the red, green and blue values and provides these values, first red, then green and finally blue to the ISR via the `DATA_REGISTER`.
The ISR cycles over the `DATA_REGISTER` and presents the bits at P1.0.
Additionally, when the first bit of a full draw screen cycle is presented at P1.0 by the ISR, it also sets the data enable signal at P1.1 and when the last bit has been provided it disabled the data enable signal. This signal is also synchronized using a flipflop and used to enable the short/long pulses using an AND gate.
## Timing ## Timing

View File

@ -4,15 +4,16 @@ OBJDUMP=$(TOOLCHAIN_PREFIX)/bin/msp430-elf-objdump
ARTIFACT=firmware ARTIFACT=firmware
MCU=msp430g2553 MCU=msp430g2553
DEBUGFLAGS= COMMONFLAGS=-Wall -mmcu=$(MCU) -I $(TOOLCHAIN_PREFIX)/include -O0 -g0
# DEBUGFLAGS+= -g3 -ggdb -gdwarf-2
COMMONFLAGS=-Wall -mmcu=$(MCU) -I $(TOOLCHAIN_PREFIX)/include -O0 -g0 $(DEBUGFLAGS)
CFLAGS=$(COMMONFLAGS) -std=gnu99 CFLAGS=$(COMMONFLAGS) -std=gnu99
ASFLAGS=$(COMMONFLAGS) -D__ASSEMBLER__ ASFLAGS=$(COMMONFLAGS) -D__ASSEMBLER__
# for debugging
CFLAGS+= -g3 -ggdb -gdwarf-2
LDFLAGS=-mmcu=$(MCU) -L $(TOOLCHAIN_PREFIX)/include LDFLAGS=-mmcu=$(MCU) -L $(TOOLCHAIN_PREFIX)/include
$(ARTIFACT).elf: main.o scheduler.o spi.o spi_init.o sequencer.o melody_tetris.o melody_tusch1.o psg.o mute.o melody_pling.o config.o $(ARTIFACT).elf: main.o scheduler.o spi.o spi_init.o sequencer.o melody_tetris.o melody_tusch1.o ay_3_8913.o mute.o effects.o
$(CC) -o $@ $(LDFLAGS) $^ $(CC) -o $@ $(LDFLAGS) $^
$(OBJDUMP) -D $(ARTIFACT).elf > $(ARTIFACT).txt $(OBJDUMP) -D $(ARTIFACT).elf > $(ARTIFACT).txt

View File

@ -27,35 +27,6 @@ const uint16_t frequencyCodes[8][12] = {
#define _CS0 BIT2 #define _CS0 BIT2
#define _CS1 BIT0 #define _CS1 BIT0
#define R0 0
#define CHANNEL_A_TONE_PERIOD_FINE_REG R0
#define R1 1
#define CHANNEL_A_TONE_PERIOD_COARSE_REG R1
#define R2 2
#define CHANNEL_B_TONE_PERIOD_FINE_REG R2
#define R3 3
#define CHANNEL_B_TONE_PERIOD_COARSE_REG R3
#define R4 4
#define CHANNEL_C_TONE_PERIOD_FINE_REG R4
#define R5 5
#define CHANNEL_C_TONE_PERIOD_COARSE_REG R5
#define R6 6
#define NOISE_PERIOD_REG R6
#define R7 7
#define _ENABLE_REG R7
#define R10 010
#define CHANNEL_A_AMPLITUDE_REG R10
#define R11 011
#define CHANNEL_B_AMPLITUDE_REG R11
#define R12 012
#define CHANNEL_C_AMPLITUDE_REG R12
#define R13 013
#define ENVELOPE_PERIOD_FINE_REG R13
#define R14 014
#define ENVELOPE_PERIOD_COARSE_REG R13
#define R15 015
#define ENVELOPE_SHAPE_REG R15
uint8_t psgShadowRegisters[2][14]; uint8_t psgShadowRegisters[2][14];
inline static void BUS_OP_NACT() { inline static void BUS_OP_NACT() {
@ -81,11 +52,24 @@ inline static void BUS_OP_CS1_DISABLE() {
BUS_CTRL_REG |= _CS1; BUS_CTRL_REG |= _CS1;
} }
static uint8_t psgReadShadow(uint8_t chip, uint8_t address) { #if 0
static void delay() {
asm volatile (
"push r12\n"
"mov.w #5, r12\n"
"loop:\n"
"dec.w r12\n"
"jnz loop\n"
"pop r12\n"
);
}
#endif
uint8_t psgReadShadow(uint8_t chip, uint8_t address) {
return psgShadowRegisters[chip][address]; return psgShadowRegisters[chip][address];
} }
static void psgWrite(uint8_t chip, uint8_t address, uint8_t data) { void psgWrite(uint8_t chip, uint8_t address, uint8_t data) {
psgShadowRegisters[chip][address] = data; psgShadowRegisters[chip][address] = data;
// according to "State Timing" (p. 15) of datasheet // according to "State Timing" (p. 15) of datasheet

View File

@ -1,26 +0,0 @@
#include <stdint.h>
#include <sys/param.h>
#include "config.h"
typedef struct {
uint8_t melodyAmplitude;
uint8_t effectsAmplitude;
} config_t;
config_t config;
void configSetAmplitude(uint8_t v) {
config.melodyAmplitude = MIN(v, 15);
config.effectsAmplitude = MIN(v+4, 15);
}
uint8_t *configGetMelodyAmplitudePtr() {
return &(config.melodyAmplitude);
}
uint8_t *configGetEffectsAmplitudePtr() {
return &(config.effectsAmplitude);
}

View File

@ -1,12 +0,0 @@
#ifndef _CONFIG_H_
#define _CONFIG_H_
#include <stdint.h>
void configSetAmplitude(uint8_t v);
uint8_t *configGetMelodyAmplitudePtr();
uint8_t *configGetEffectsAmplitudePtr();
#endif // _CONFIG_H_

18
sound-driver/effects.c Normal file
View File

@ -0,0 +1,18 @@
#include "effects.h"
#include "psg.h"
void effectsInit() {
}
void playEffect1() {
psgWrite(1, NOISE_PERIOD_REG, 0);
psgWrite(1, _ENABLE_REG, psgReadShadow(1, _ENABLE_REG) & ~0b00111000);
psgWrite(1, CHANNEL_A_AMPLITUDE_REG, 020);
psgWrite(1, CHANNEL_B_AMPLITUDE_REG, 020);
psgWrite(1, CHANNEL_C_AMPLITUDE_REG, 020);
psgWrite(1, ENVELOPE_PERIOD_COARSE_REG, 070);
psgWrite(1, ENVELOPE_SHAPE_REG, 0);
}

9
sound-driver/effects.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef _EFFECTS_H_
#define _EFFECTS_H_
void effectsInit();
#endif // _EFFECTS_H_

View File

@ -9,6 +9,7 @@
#include "melody_tetris.h" #include "melody_tetris.h"
#include "melody_tusch1.h" #include "melody_tusch1.h"
#include "mute.h" #include "mute.h"
#include "effects.h"
int main() { int main() {
WDTCTL = WDTPW | WDTHOLD; WDTCTL = WDTPW | WDTHOLD;
@ -28,10 +29,10 @@ int main() {
psgInit(); psgInit();
muteInit(); muteInit();
sequencerInit(); sequencerInit();
effectsInit();
__enable_interrupt(); __enable_interrupt();
while (1) { while (1) {
schExec(); schExec();
} }

View File

@ -1,35 +0,0 @@
#include <stdbool.h>
#include <stddef.h>
#include "psg.h"
#include "sequencer.h"
#include "scheduler.h"
#include "config.h"
const t_tone plingVoice1[] = {
{ .octave = e_O_5, .note = e_C, .length = e_L_1_16, .legato = false, .staccato = false },
{ .octave = e_O_5, .note = e_Cis, .length = e_L_1_16, .legato = false, .staccato = false },
{ .octave = e_O_5, .note = e_D, .length = e_L_1_16, .legato = false, .staccato = false },
{ .octave = e_O_5, .note = e_Dis, .length = e_L_1_16, .legato = false, .staccato = false },
{ .octave = e_O_5, .note = e_E, .length = e_L_1_8, .legato = false, .staccato = false },
{ .octave = e_O_Null, .note = e_Null, .length = e_L_SyncMark,.legato = false, .staccato = false },
{ .octave = e_O_Null, .note = e_Null, .length = e_L_StopMark,.legato = false, .staccato = false },
{ .octave = e_O_Null, .note = e_Null, .length = e_L_EndMark, .legato = false, .staccato = false },
};
t_melodies pling = {
.melodies = { { .tones = plingVoice1 } },
.numOfMelodies = 1,
.pace = 200,
.chip = 1
};
void playPling() {
pling.p_amplitude = configGetEffectsAmplitudePtr();
sequencerPlayMelodies(&pling);
}

View File

@ -1,8 +0,0 @@
#ifndef _MELODY_PLING_H_
#define _MELODY_PLING_H_
void playPling();
#endif // _MELODY_PLING_H_

View File

@ -3,7 +3,6 @@
#include "psg.h" #include "psg.h"
#include "sequencer.h" #include "sequencer.h"
#include "scheduler.h" #include "scheduler.h"
#include "config.h"
/* /*
@ -923,28 +922,19 @@ const t_tone voice3[] = {
{ .octave = e_O_Null, .note = e_Null, .length = e_L_EndMark, .legato = false, .staccato = false }, { .octave = e_O_Null, .note = e_Null, .length = e_L_EndMark, .legato = false, .staccato = false },
}; };
#define INITIAL_PACE 160
t_melodies tetrisTheme = { t_melodies tetrisTheme = {
.melodies = { { .tones = voice1 }, { .tones = voice2 }, { .tones = voice3 } }, .melodies = { { .chip = 0, .amplitude = 8, .tones = voice1 }, { .chip = 0, .amplitude = 8, .tones = voice2 }, { .chip = 0, .amplitude = 8, .tones = voice3 } },
.numOfMelodies = 3, .numOfMelodies = 3,
.pace = INITIAL_PACE, .pace = 160,
.chip = 0 .slotMask = 0x01
}; };
void playMelodyTetris() { void playMelodyTetris() {
tetrisTheme.pace = INITIAL_PACE; // reset to start value each time
tetrisTheme.p_amplitude = configGetMelodyAmplitudePtr();
sequencerPlayMelodies(&tetrisTheme); sequencerPlayMelodies(&tetrisTheme);
} }
void playMelodyTetrisFaster() {
tetrisTheme.pace += 15;
sequencerChangePace(&tetrisTheme);
}
void stopMelodyTetris() { void stopMelodyTetris() {
sequencerStopMelodies(&tetrisTheme); sequencerStopMelodies(&tetrisTheme);
} }

View File

@ -4,7 +4,6 @@
void playMelodyTetris(); void playMelodyTetris();
void stopMelodyTetris(); void stopMelodyTetris();
void playMelodyTetrisFaster();
void playMelodyTetrisAmplitude(uint8_t a);
#endif // _MELODY_TETRIS_H_ #endif // _MELODY_TETRIS_H_

View File

@ -3,7 +3,6 @@
#include "psg.h" #include "psg.h"
#include "sequencer.h" #include "sequencer.h"
#include "scheduler.h" #include "scheduler.h"
#include "config.h"
const t_tone tusch1voice1[] = { const t_tone tusch1voice1[] = {
{ .octave = e_O_5, .note = e_C, .length = e_L_1_4, .legato = false, .staccato = true }, { .octave = e_O_5, .note = e_C, .length = e_L_1_4, .legato = false, .staccato = true },
@ -72,14 +71,13 @@ const t_tone tusch1voice3[] = {
}; };
t_melodies tusch1 = { t_melodies tusch1 = {
.melodies = { { .tones = tusch1voice1 }, { .tones = tusch1voice2 }, { .tones = tusch1voice3 } }, .melodies = { { .chip = 1, .amplitude = 12, .tones = tusch1voice1 }, { .chip = 1, .amplitude = 12, .tones = tusch1voice2 }, { .chip = 1, .amplitude = 12, .tones = tusch1voice3 } },
.numOfMelodies = 3, .numOfMelodies = 3,
.pace = 200, .pace = 200,
.chip = 1 .slotMask = 0x02
}; };
void playTusch1() { void playTusch1() {
tusch1.p_amplitude = configGetEffectsAmplitudePtr();
sequencerPlayMelodies(&tusch1); sequencerPlayMelodies(&tusch1);
} }

View File

@ -6,17 +6,15 @@
void muteInit() { void muteInit() {
// BIT6: MuteCtrl // BIT6: MuteCtrl
P1DIR |= BIT6; P1DIR |= BIT6;
// initially, mute
P1OUT &= ~BIT6; P1OUT &= ~BIT6;
} }
void mute() { void mute() {
P1OUT &= ~BIT6;
}
void unMute() {
P1OUT |= BIT6; P1OUT |= BIT6;
} }
void unMute() {
P1OUT &= ~BIT6;
}

View File

@ -36,11 +36,41 @@ typedef enum {
#define e_As e_Gis #define e_As e_Gis
#define e_B e_Ais #define e_B e_Ais
#define R0 0
#define CHANNEL_A_TONE_PERIOD_FINE_REG R0
#define R1 1
#define CHANNEL_A_TONE_PERIOD_COARSE_REG R1
#define R2 2
#define CHANNEL_B_TONE_PERIOD_FINE_REG R2
#define R3 3
#define CHANNEL_B_TONE_PERIOD_COARSE_REG R3
#define R4 4
#define CHANNEL_C_TONE_PERIOD_FINE_REG R4
#define R5 5
#define CHANNEL_C_TONE_PERIOD_COARSE_REG R5
#define R6 6
#define NOISE_PERIOD_REG R6
#define R7 7
#define _ENABLE_REG R7
#define R10 010
#define CHANNEL_A_AMPLITUDE_REG R10
#define R11 011
#define CHANNEL_B_AMPLITUDE_REG R11
#define R12 012
#define CHANNEL_C_AMPLITUDE_REG R12
#define R13 013
#define ENVELOPE_PERIOD_FINE_REG R13
#define R14 014
#define ENVELOPE_PERIOD_COARSE_REG R13
#define R15 015
#define ENVELOPE_SHAPE_REG R15
void psgInit(); void psgInit();
void psgPlayTone(uint8_t chip, uint8_t channel, uint8_t volume, t_octave octave, t_note note); void psgPlayTone(uint8_t chip, uint8_t channel, uint8_t volume, t_octave octave, t_note note);
void psgAmplitude(uint8_t chip, uint8_t channel, uint8_t volume); void psgAmplitude(uint8_t chip, uint8_t channel, uint8_t volume);
uint8_t psgReadShadow(uint8_t chip, uint8_t address);
void psgWrite(uint8_t chip, uint8_t address, uint8_t data);
#endif // _PSG_H_ #endif // _PSG_H_

View File

@ -52,6 +52,16 @@ uint16_t schAdd(void (*exec)(void *), void *handle, uint32_t delay, uint32_t per
return taskId; return taskId;
} }
/*
void schDel(void (*exec)(void *), void *handle) {
for (uint16_t i = 0; i < MAX_NUM_OF_TASKS; i++) {
if ((tasks[i].exec == exec) && (tasks[i].handle == handle)) {
tasks[i].exec = NULL;
break;
}
}
}
*/
void schDel(uint16_t taskId) { void schDel(uint16_t taskId) {
tasks[taskId].exec = NULL; tasks[taskId].exec = NULL;
} }

View File

@ -59,7 +59,7 @@ void sequencerExec(void *handle) {
if (melody->tones[melody->idx].length == e_L_EndMark) { if (melody->tones[melody->idx].length == e_L_EndMark) {
melody->idx = 0; melody->idx = 0;
} }
psgPlayTone(melodies->chip, channel, *(melodies->p_amplitude), melody->tones[melody->idx].octave, melody->tones[melody->idx].note); psgPlayTone(melody->chip, channel, melody->amplitude, melody->tones[melody->idx].octave, melody->tones[melody->idx].note);
melody->lengthCnt = (melody->tones[melody->idx].staccato) ? melody->lengthCnt = (melody->tones[melody->idx].staccato) ?
(calcLength(melodies, melody->tones[melody->idx].length) / 2) : (calcLength(melodies, melody->tones[melody->idx].length) / 2) :
calcLength(melodies, melody->tones[melody->idx].length); calcLength(melodies, melody->tones[melody->idx].length);
@ -78,7 +78,7 @@ void sequencerExec(void *handle) {
} }
break; break;
case e_StaccatoBreak: case e_StaccatoBreak:
psgPlayTone(melodies->chip, channel, 0, e_O_Null, e_Pause); psgPlayTone(melody->chip, channel, 0, e_O_Null, e_Pause);
melody->lengthCnt = calcLength(melodies, melody->tones[melody->idx].length) / 2; melody->lengthCnt = calcLength(melodies, melody->tones[melody->idx].length) / 2;
melody->state = e_HoldStaccatoBreak; melody->state = e_HoldStaccatoBreak;
break; break;
@ -90,17 +90,15 @@ void sequencerExec(void *handle) {
break; break;
case e_SeparateTone: case e_SeparateTone:
if (! (melody->tones[melody->idx].legato)) { if (! (melody->tones[melody->idx].legato)) {
psgPlayTone(melodies->chip, channel, 0, e_O_Null, e_Pause); psgPlayTone(melody->chip, channel, 0, e_O_Null, e_Pause);
} }
melody->idx += 1; melody->idx += 1;
melody->state = e_PlayTone; melody->state = e_PlayTone;
break; break;
case e_Hold: case e_Hold:
psgPlayTone(melodies->chip, channel, 0, e_O_Null, e_Pause);
break; break;
case e_Terminate: case e_Terminate:
schDel(melodies->taskId); schDel(melodies->taskId);
psgPlayTone(melodies->chip, channel, 0, e_O_Null, e_Pause);
slots &= ~(melodies->slotMask); slots &= ~(melodies->slotMask);
break; break;
} }
@ -108,8 +106,6 @@ void sequencerExec(void *handle) {
} }
void sequencerPlayMelodies(t_melodies *melodies) { void sequencerPlayMelodies(t_melodies *melodies) {
melodies->slotMask = (1 << melodies->chip);
if ((slots & melodies->slotMask) != 0) { if ((slots & melodies->slotMask) != 0) {
return; return;
} }
@ -127,14 +123,7 @@ void sequencerPlayMelodies(t_melodies *melodies) {
} }
void sequencerStopMelodies(t_melodies *melodies) { void sequencerStopMelodies(t_melodies *melodies) {
schDel(melodies->taskId);
slots &= ~(melodies->slotMask); slots &= ~(melodies->slotMask);
for (uint8_t channel = 0; channel < melodies->numOfMelodies; channel++) { schDel(melodies->taskId);
psgPlayTone(melodies->chip, channel, 0, e_O_Null, e_Pause);
}
}
void sequencerChangePace(t_melodies *melodies) {
melodies->quarterLength = 60000 / melodies->pace / SEQUENCER_PERIOD; // duration of a 1/4 tone in ms
} }

View File

@ -42,8 +42,10 @@ typedef enum {
typedef struct { typedef struct {
uint16_t idx; uint16_t idx;
uint8_t chip;
uint16_t lengthCnt; uint16_t lengthCnt;
t_sequencerState state; t_sequencerState state;
uint8_t amplitude;
const t_tone *tones; const t_tone *tones;
} t_melody; } t_melody;
@ -51,12 +53,10 @@ typedef struct {
#define NUM_OF_CHANNELS 3 #define NUM_OF_CHANNELS 3
typedef struct { typedef struct {
uint8_t slotMask; uint8_t slotMask;
uint8_t chip;
uint8_t *p_amplitude;
uint8_t taskId; uint8_t taskId;
uint16_t quarterLength; uint16_t quarterLength;
uint8_t numOfMelodies; uint8_t numOfMelodies;
uint16_t pace; // quarter notes per minute uint8_t pace; // quarter notes per minute
uint8_t sync; uint8_t sync;
t_melody melodies[NUM_OF_CHANNELS]; t_melody melodies[NUM_OF_CHANNELS];
} t_melodies; } t_melodies;
@ -64,6 +64,5 @@ typedef struct {
void sequencerInit(); void sequencerInit();
void sequencerPlayMelodies(t_melodies *melodies); void sequencerPlayMelodies(t_melodies *melodies);
void sequencerStopMelodies(t_melodies *melodies); void sequencerStopMelodies(t_melodies *melodies);
void sequencerChangePace(t_melodies *melodies);
#endif // _SEQUENCER_H_ #endif // _SEQUENCER_H_

171
sound-driver/sn76489an.c Normal file
View File

@ -0,0 +1,171 @@
#include <msp430g2553.h>
#include <stdint.h>
#include <stdlib.h>
#include "psg.h"
#include "scheduler.h"
// generated using utils/calc-76489an.py
const uint16_t frequencyCodes[8][12] = {
{ 3420, 3229, 3047, 2876, 2715, 2562, 2419, 2283, 2155, 2034, 1920, 1812 },
{ 1710, 1614, 1524, 1438, 1357, 1281, 1209, 1141, 1077, 1017, 960, 906 },
{ 855, 807, 762, 719, 679, 641, 605, 571, 539, 508, 480, 453 },
{ 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226 },
{ 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113 },
{ 107, 101, 95, 90, 85, 80, 76, 71, 67, 64, 60, 57 },
{ 53, 50, 48, 45, 42, 40, 38, 36, 34, 32, 30, 28 },
{ 27, 25, 24, 22, 21, 20, 19, 18, 17, 16, 15, 14 }
};
#define ADDR_DATA_REG P2OUT
#define BUS_CTRL_REG P1OUT
#define BUS_CTRL_IN_REG P1IN
#define _CS0 BIT0
#define _CS1 BIT1
#define _WE BIT2
#define READY BIT3
#define CHANNEL_A_PERIOD_ADDR 0
#define CHANNEL_A_ATTEN_ADDR 1
#define CHANNEL_B_PERIOD_ADDR 2
#define CHANNEL_B_ATTEN_ADDR 3
#define CHANNEL_C_PERIOD_ADDR 4
#define CHANNEL_C_ATTEN_ADDR 5
#define IGNORE_OCTET 0xff
uint8_t psgAmplitudeShadowValue[3];
static void delay() {
asm volatile (
"push r12\n"
"mov.w #5, r12\n"
"loop:\n"
"dec.w r12\n"
"jnz loop\n"
"pop r12\n"
);
}
inline static void WRITE_CYCLE(uint8_t chipNo) {
if (chipNo == 0) {
BUS_CTRL_REG &= ~_CS0;
} else {
BUS_CTRL_REG &= ~_CS1;
}
BUS_CTRL_REG &= ~_WE;
delay();
while ((BUS_CTRL_IN_REG & READY) == 0);
BUS_CTRL_REG |= _WE;
if (chipNo == 0) {
BUS_CTRL_REG |= _CS0;
} else {
BUS_CTRL_REG |= _CS1;
}
delay();
}
static void psgWrite(uint8_t chipNo, uint8_t value) {
ADDR_DATA_REG = value;
WRITE_CYCLE(chipNo);
}
static void psgWriteFrequency(uint8_t channel, uint16_t frequencyCode) {
uint8_t chipNo = channel / 3;
uint8_t regAddr = (channel % 3) * 2;
// bit order in frequncyCode and order in octet on data bus are reversed
// see datacheat cp. 1 and cp. 6
uint8_t firstOctet = 0x01;
firstOctet |= ((regAddr & 0x04) > 1);
firstOctet |= ((regAddr & 0x02) < 1);
firstOctet |= ((regAddr & 0x01) < 3);
uint8_t lowerPart = frequencyCode & 0x0f;
firstOctet |= ((lowerPart & 0x08) << 1);
firstOctet |= ((lowerPart & 0x04) << 3);
firstOctet |= ((lowerPart & 0x02) << 5);
firstOctet |= ((lowerPart & 0x01) << 7);
uint8_t secondOctet = 0;
uint8_t upperPart = (frequencyCode & 0x03f0) >> 4;
secondOctet |= ((upperPart & 0x20) >> 3);
secondOctet |= ((upperPart & 0x10) >> 1);
secondOctet |= ((upperPart & 0x08) << 1);
secondOctet |= ((upperPart & 0x04) << 3);
secondOctet |= ((upperPart & 0x02) << 5);
secondOctet |= ((upperPart & 0x01) << 7);
ADDR_DATA_REG = firstOctet;
WRITE_CYCLE(chipNo);
ADDR_DATA_REG = secondOctet;
WRITE_CYCLE(chipNo);
}
void psgAmplitude(uint8_t channel, uint8_t volume) {
psgAmplitudeShadowValue[channel] = volume;
uint8_t chipNo = channel / 3;
uint8_t regAddr = ((channel % 3) * 2) + 1;
uint8_t attenuation = 15 - volume;
uint8_t firstOctet = 0x01;
firstOctet |= ((regAddr & 0x04) >> 1);
firstOctet |= ((regAddr & 0x02) << 1);
firstOctet |= ((regAddr & 0x01) << 3);
firstOctet |= ((attenuation & 0x01) << 7);
firstOctet |= ((attenuation & 0x02) << 5);
firstOctet |= ((attenuation & 0x04) << 3);
firstOctet |= ((attenuation & 0x08) << 1);
ADDR_DATA_REG = firstOctet;
WRITE_CYCLE(chipNo);
}
void psgPlayTone(uint8_t channel, uint8_t volume, t_octave octave, t_note note) {
if (note == e_Pause) {
psgAmplitude(channel, 0);
} else {
// if (psgAmplitudeShadowValue[channel] == 0) {
psgAmplitude(channel, volume);
// }
psgWriteFrequency(channel, frequencyCodes[octave][note]);
}
}
void psgInit() {
// address/data bus
P2DIR = 0xff;
P2SEL = 0;
P2SEL2 = 0;
// bus control lines
// output:
// BIT0: /CS chip 0
// BIT1: /CS chip 1
// BIT2: /WE
// input:
// BIT3: READY
P1DIR |= BIT0 | BIT1 | BIT2;
P1DIR &= ~BIT3;
// immediately disable all outputs, all are active low
P1OUT |= BIT0 | BIT1 | BIT2;
// shutdown all channels including noise
psgWrite(0, 0b11111001);
psgWrite(0, 0b11111101);
psgWrite(0, 0b11111011);
psgWrite(0, 0b11111111);
// psgPlayTone(0, 5, e_O_3, e_A);
psgAmplitude(0, 3);
}

View File

@ -8,9 +8,7 @@
#define SOUND_GAMEOVER 0x08 #define SOUND_GAMEOVER 0x08
#define SOUND_FANFARE 0x10 #define SOUND_FANFARE 0x10
#define SOUND_LOCK 0x20 #define SOUND_LOCK 0x20
#define SOUND_PLING 0x40 #define SOUND_MOTION 0x40
#define SOUND_COMMAND 0x80 #define SOUND_SPEED_UP 0x80
#define SOUND_SUBCMD_AMPLITUDE 0x40
#endif // _SOUND_CODES_H_ #endif // _SOUND_CODES_H_

View File

@ -1,26 +1,21 @@
#include <msp430g2553.h> #include <msp430g2553.h>
#include "soundCodes.h" #include "soundCodes.h"
.section ".data"
.global cmd
cmd:
.byte
.section ".text","ax",@progbits .section ".text","ax",@progbits
receive_isr: receive_isr:
bit #UCB0RXIFG, &UC0IFG bit #UCB0RXIFG, &UC0IFG
jz receive_isr_no_data jz receive_isr_no_data
bit #SOUND_COMMAND, &cmd
jnz receive_isr_no_data
bis UCB0RXBUF, &cmd bis UCB0RXBUF, &cmd
receive_isr_no_data: receive_isr_no_data:
reti reti
.global spiCmdHandler .global spiCmdHandler
spiCmdHandler: spiCmdHandler:
spiCmdHandler_0:
bit #SOUND_COMMAND, &cmd
jz spiCmdHandler_1
;; insert a call here
call #spiCommandDispatcher
mov.b #0, &cmd
ret
spiCmdHandler_1: spiCmdHandler_1:
bit #SOUND_MUTE, &cmd bit #SOUND_MUTE, &cmd
jz spiCmdHandler_2 jz spiCmdHandler_2
@ -39,28 +34,36 @@ spiCmdHandler_3:
spiCmdHandler_4: spiCmdHandler_4:
bit #SOUND_GAMEOVER, &cmd bit #SOUND_GAMEOVER, &cmd
jz spiCmdHandler_5 jz spiCmdHandler_5
call #stopMelodyTetris ;; insert a call here
bic #SOUND_GAMEOVER, &cmd bic #SOUND_GAMEOVER, &cmd
spiCmdHandler_5: spiCmdHandler_5:
bit #SOUND_FANFARE, &cmd bit #SOUND_FANFARE, &cmd
jz spiCmdHandler_6 jz spiCmdHandler_6
call #playMelodyTetrisFaster
call #playTusch1 call #playTusch1
bic #SOUND_FANFARE, &cmd bic #SOUND_FANFARE, &cmd
spiCmdHandler_6: spiCmdHandler_6:
bit #SOUND_LOCK, &cmd bit #SOUND_LOCK, &cmd
jz spiCmdHandler_7 jz spiCmdHandler_7
call #playEffect1
;; insert a call here ;; insert a call here
bic #SOUND_LOCK, &cmd bic #SOUND_LOCK, &cmd
spiCmdHandler_7: spiCmdHandler_7:
bit #SOUND_PLING, &cmd bit #SOUND_MOTION, &cmd
jz spiCmdHandler_8
;; insert a call here
bic #SOUND_MOTION, &cmd
spiCmdHandler_8:
bit #SOUND_SPEED_UP, &cmd
jz spiCmdHandler_end jz spiCmdHandler_end
call #playPling ;; insert a call here
bic #SOUND_PLING, &cmd bic #SOUND_SPEED_UP, &cmd
spiCmdHandler_end: spiCmdHandler_end:
ret ret
.section "__interrupt_vector_8","ax",@progbits .section "__interrupt_vector_8","ax",@progbits
.word receive_isr .word receive_isr

View File

@ -4,10 +4,9 @@
#include "scheduler.h" #include "scheduler.h"
#include "spi.h" #include "spi.h"
#include "soundCodes.h" #include "soundCodes.h"
#include "config.h"
volatile uint8_t cmd; extern uint8_t cmd;
void spiInit() { void spiInit() {
// SPI slave // SPI slave
@ -19,21 +18,14 @@ void spiInit() {
P1SEL2 |= BIT4 | BIT5 | BIT7; P1SEL2 |= BIT4 | BIT5 | BIT7;
// most significant bit first, enable STE // most significant bit first, enable STE
UCB0CTL0 = UCCKPH | UCSYNC | UCMSB | UCMODE_2; UCB0CTL0 = UCSYNC | UCMSB | UCMODE_2;
UCB0CTL1 = 0x00; UCB0CTL1 = 0x00;
// enable RX interrupt // enable RX interrupt
UC0IE |= UCB0RXIE; UC0IE |= UCB0RXIE;
cmd = SOUND_IDLE; cmd = SOUND_IDLE;
schAdd(spiCmdHandler, NULL, 0, 5); schAdd(spiCmdHandler, NULL, 0, 100);
} }
void spiCommandDispatcher() {
cmd &= ~SOUND_COMMAND;
if (cmd & SOUND_SUBCMD_AMPLITUDE) {
cmd &= ~SOUND_SUBCMD_AMPLITUDE;
configSetAmplitude(cmd);
}
}