7 Commits

13 changed files with 73 additions and 79 deletions

BIN
docs/IMG_4941.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 KiB

BIN
docs/IMG_4958.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

BIN
docs/sound-driver-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

View File

@ -7,7 +7,7 @@ MCU=msp430g2553
CFLAGS=-Wall -mmcu=$(MCU) -std=gnu99 -I $(TOOLCHAIN_PREFIX)/include -O1 -g0 CFLAGS=-Wall -mmcu=$(MCU) -std=gnu99 -I $(TOOLCHAIN_PREFIX)/include -O1 -g0
# for debugging # for debugging
CFLAGS+= -g3 -ggdb -gdwarf-2 #CFLAGS+= -g3 -ggdb -gdwarf-2
LDFLAGS=-mmcu=$(MCU) -L $(TOOLCHAIN_PREFIX)/include LDFLAGS=-mmcu=$(MCU) -L $(TOOLCHAIN_PREFIX)/include

View File

@ -21,17 +21,12 @@ static uint8_t delayFactor(uint8_t level) {
return MAX_LEVEL + 1 - level; return MAX_LEVEL + 1 - level;
} }
typedef enum {
e_Phase_Game, e_Phase_GameOver
} phase_t;
typedef enum { typedef enum {
e_Start, e_NewStone, e_Down, e_DownDelay, e_ClearRows, 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 phase_t phase;
static state_t state = e_Start; static state_t state = e_Start;
static uint8_t gameOverDelay; static uint8_t gameOverDelay;
static uint8_t rowIndex; static uint8_t rowIndex;
@ -41,6 +36,8 @@ void gameExec(void *handle) {
static uint16_t score; static uint16_t score;
static bool newHighScoreAchieved; static bool newHighScoreAchieved;
bool wipedLines = false;
// --- engine begin ------------------------------------------------------- // --- engine begin -------------------------------------------------------
switch (state) { switch (state) {
// --- phase: game -------------------------------------------------------- // --- phase: game --------------------------------------------------------
@ -51,7 +48,6 @@ void gameExec(void *handle) {
filledLines = 0; filledLines = 0;
score = 0; score = 0;
newHighScoreAchieved = false; newHighScoreAchieved = false;
phase = e_Phase_Game;
state = e_NewStone; state = e_NewStone;
break; break;
@ -69,29 +65,56 @@ void gameExec(void *handle) {
proceedDelay--; proceedDelay--;
if (proceedDelay == 0) { if (proceedDelay == 0) {
rowIndex = 0; rowIndex = 0;
state = e_ClearRows;
}
break;
case e_ClearRows:
state = e_Down; state = e_Down;
}
break; break;
case e_Down: case e_Down:
if (! stoneMoveDown()) { if (! stoneMoveDown()) {
soundCtrl(SOUND_LOCK); soundCtrl(SOUND_LOCK);
state = e_NewStone; stoneLock();
state = e_ClearRows;
} else { } else {
proceedDelay = delayFactor(level); proceedDelay = delayFactor(level);
state = e_DownDelay; state = e_DownDelay;
} }
break; break;
case e_ClearRows:
// clear filled lines
for (uint8_t r = 0; r < CANVAS_HEIGHT; r++) {
if (canvasIsRowFilled(r)) {
score += level;
if (score > eepromReadHighScore()) {
newHighScoreAchieved = true;
eepromWriteHighScore(score);
}
displaySetValue(score);
canvasWipeRow(r);
canvasShow();
wipedLines = true;
filledLines += 1;
}
}
if (wipedLines) {
soundCtrl(SOUND_PLING);
}
if (wipedLines && (filledLines > 0) && ((filledLines % 10) == 0)) {
if (level < MAX_LEVEL) {
level += 1;
}
soundCtrl(SOUND_FANFARE);
}
state = e_NewStone;
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;
@ -121,36 +144,7 @@ void gameExec(void *handle) {
} }
// --- engine end --------------------------------------------------------- // --- engine end ---------------------------------------------------------
bool wipedLines = false;
canvasShow(); canvasShow();
if (phase == e_Phase_Game) {
for (uint8_t r = 0; r < CANVAS_HEIGHT; r++) {
if (canvasIsRowFilled(r)) {
score += level;
if (score > eepromReadHighScore()) {
newHighScoreAchieved = true;
eepromWriteHighScore(score);
}
displaySetValue(score);
canvasWipeRow(r);
canvasShow();
wipedLines = true;
filledLines += 1;
}
}
}
if (wipedLines) {
soundCtrl(SOUND_PLING);
}
if (wipedLines && (filledLines > 0) && ((filledLines % 10) == 0)) {
if (level < MAX_LEVEL) {
level += 1;
}
soundCtrl(SOUND_FANFARE);
}
if (isGameActive()) { if (isGameActive()) {
displaySetValue(score); displaySetValue(score);

View File

@ -1,6 +1,7 @@
#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"
@ -19,6 +20,7 @@ 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 {
@ -363,6 +365,11 @@ void stoneCreate() {
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() {
@ -376,6 +383,12 @@ 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) &&

View File

@ -5,6 +5,7 @@
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();

View File

@ -2,6 +2,14 @@
![](./docs/IMG_4936.jpg) ![](./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). 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. 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.
@ -54,7 +62,7 @@ An amplifier following the proposal of the AY-3-8913 datasheet is implemented us
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. 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-1.jpg)
![](./docs/sound-driver-2.jpg) ![](./docs/sound-driver-2.png)
![](./docs/sound-driver-3.jpg) ![](./docs/sound-driver-3.jpg)
![](./docs/sound-driver-4.jpg) ![](./docs/sound-driver-4.jpg)

View File

@ -9,41 +9,17 @@ const t_tone plingVoice1[] = {
{ .octave = e_O_5, .note = e_Cis, .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_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_Dis, .length = e_L_1_16, .legato = false, .staccato = false },
{ .octave = e_O_5, .note = e_E, .length = e_L_1_4, .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_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_StopMark,.legato = false, .staccato = false },
{ .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 },
}; };
const t_tone plingVoice2[] = {
{ .octave = e_O_Null, .note = e_Pause, .length = e_L_1_16, .legato = false, .staccato = false },
{ .octave = e_O_Null, .note = e_Pause, .length = e_L_1_16, .legato = false, .staccato = false },
{ .octave = e_O_Null, .note = e_Pause, .length = e_L_1_16, .legato = false, .staccato = false },
{ .octave = e_O_Null, .note = e_Pause, .length = e_L_1_16, .legato = false, .staccato = false },
{ .octave = e_O_5, .note = e_Gis, .length = e_L_1_4, .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_HoldMark,.legato = false, .staccato = false },
{ .octave = e_O_Null, .note = e_Null, .length = e_L_EndMark, .legato = false, .staccato = false },
};
const t_tone plingVoice3[] = {
{ .octave = e_O_Null, .note = e_Pause, .length = e_L_1_16, .legato = false, .staccato = false },
{ .octave = e_O_Null, .note = e_Pause, .length = e_L_1_16, .legato = false, .staccato = false },
{ .octave = e_O_Null, .note = e_Pause, .length = e_L_1_16, .legato = false, .staccato = false },
{ .octave = e_O_Null, .note = e_Pause, .length = e_L_1_16, .legato = false, .staccato = false },
{ .octave = e_O_5, .note = e_H, .length = e_L_1_4, .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_HoldMark,.legato = false, .staccato = false },
{ .octave = e_O_Null, .note = e_Null, .length = e_L_EndMark, .legato = false, .staccato = false },
};
t_melodies pling = { t_melodies pling = {
.melodies = { { .amplitude = 12, .tones = plingVoice1 }, { .amplitude = 12, .tones = plingVoice2 }, { .amplitude = 12, .tones = plingVoice3 } }, .melodies = { { .amplitude = 12, .tones = plingVoice1 } },
.numOfMelodies = 3, .numOfMelodies = 1,
.pace = 200, .pace = 200,
.chip = 1 .chip = 1
}; };

View File

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

View File

@ -96,9 +96,11 @@ void sequencerExec(void *handle) {
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;
} }