Compare commits
1 Commits
fix_wipe_l
...
sound_tuni
Author | SHA1 | Date | |
---|---|---|---|
68816a06fa |
Binary file not shown.
Before Width: | Height: | Size: 603 KiB |
Binary file not shown.
Before Width: | Height: | Size: 262 KiB |
Binary file not shown.
BIN
docs/sound-driver-2.jpg
Normal file
BIN
docs/sound-driver-2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
Binary file not shown.
Before Width: | Height: | Size: 183 KiB |
@ -7,7 +7,7 @@ MCU=msp430g2553
|
||||
CFLAGS=-Wall -mmcu=$(MCU) -std=gnu99 -I $(TOOLCHAIN_PREFIX)/include -O1 -g0
|
||||
|
||||
# for debugging
|
||||
#CFLAGS+= -g3 -ggdb -gdwarf-2
|
||||
CFLAGS+= -g3 -ggdb -gdwarf-2
|
||||
|
||||
LDFLAGS=-mmcu=$(MCU) -L $(TOOLCHAIN_PREFIX)/include
|
||||
|
||||
|
@ -21,12 +21,17 @@ static uint8_t delayFactor(uint8_t level) {
|
||||
return MAX_LEVEL + 1 - level;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
e_Phase_Game, e_Phase_GameOver
|
||||
} phase_t;
|
||||
|
||||
typedef enum {
|
||||
e_Start, e_NewStone, e_Down, e_DownDelay, e_ClearRows,
|
||||
e_GameOver, e_GameOverFill, e_GameOverWipe, e_GameOverDelay
|
||||
} state_t;
|
||||
|
||||
void gameExec(void *handle) {
|
||||
static phase_t phase;
|
||||
static state_t state = e_Start;
|
||||
static uint8_t gameOverDelay;
|
||||
static uint8_t rowIndex;
|
||||
@ -36,8 +41,6 @@ void gameExec(void *handle) {
|
||||
static uint16_t score;
|
||||
static bool newHighScoreAchieved;
|
||||
|
||||
bool wipedLines = false;
|
||||
|
||||
// --- engine begin -------------------------------------------------------
|
||||
switch (state) {
|
||||
// --- phase: game --------------------------------------------------------
|
||||
@ -48,6 +51,7 @@ void gameExec(void *handle) {
|
||||
filledLines = 0;
|
||||
score = 0;
|
||||
newHighScoreAchieved = false;
|
||||
phase = e_Phase_Game;
|
||||
state = e_NewStone;
|
||||
break;
|
||||
|
||||
@ -65,56 +69,29 @@ void gameExec(void *handle) {
|
||||
proceedDelay--;
|
||||
if (proceedDelay == 0) {
|
||||
rowIndex = 0;
|
||||
state = e_Down;
|
||||
state = e_ClearRows;
|
||||
}
|
||||
break;
|
||||
|
||||
case e_ClearRows:
|
||||
state = e_Down;
|
||||
break;
|
||||
|
||||
case e_Down:
|
||||
if (! stoneMoveDown()) {
|
||||
soundCtrl(SOUND_LOCK);
|
||||
stoneLock();
|
||||
state = e_ClearRows;
|
||||
state = e_NewStone;
|
||||
} else {
|
||||
proceedDelay = delayFactor(level);
|
||||
state = e_DownDelay;
|
||||
}
|
||||
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 ---------------------------------------------------
|
||||
case e_GameOver:
|
||||
soundCtrl(SOUND_GAMEOVER);
|
||||
rowIndex = CANVAS_HEIGHT;
|
||||
phase = e_Phase_GameOver;
|
||||
state = e_GameOverFill;
|
||||
break;
|
||||
|
||||
@ -144,7 +121,36 @@ void gameExec(void *handle) {
|
||||
}
|
||||
// --- engine end ---------------------------------------------------------
|
||||
|
||||
bool wipedLines = false;
|
||||
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()) {
|
||||
displaySetValue(score);
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "shapes.h"
|
||||
#include "myrand.h"
|
||||
@ -20,7 +19,6 @@ typedef struct {
|
||||
orientation_t orientation;
|
||||
uint8_t x; // column
|
||||
uint8_t y; // row
|
||||
bool locked;
|
||||
} stone_t;
|
||||
|
||||
typedef struct {
|
||||
@ -365,11 +363,6 @@ void stoneCreate() {
|
||||
stone.orientation = e_0;
|
||||
stone.x = 4;
|
||||
stone.y = 0;
|
||||
stone.locked = false;
|
||||
}
|
||||
|
||||
void stoneLock() {
|
||||
stone.locked = true;
|
||||
}
|
||||
|
||||
uint8_t stoneIsValid() {
|
||||
@ -383,12 +376,6 @@ static uint8_t move(direction_t direction) {
|
||||
if (motions[stone.shape].nullRotation && (direction == e_RotateLeft || direction == e_RotateRight)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// if the stone is already locked, do nothing
|
||||
if (stone.locked) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check whether the pixels to move to are free
|
||||
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) &&
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
void shapesInit();
|
||||
void stoneCreate();
|
||||
void stoneLock();
|
||||
uint8_t stoneIsValid();
|
||||
uint8_t stoneDraw();
|
||||
uint8_t stoneMoveDown();
|
||||
|
10
readme.md
10
readme.md
@ -2,14 +2,6 @@
|
||||
|
||||

|
||||
|
||||
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):
|
||||
|
||||

|
||||
|
||||
Update of the power switch of the amplifier (at appears, that the small transistor couldn't deliver enough current):
|
||||
|
||||

|
||||
|
||||
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.
|
||||
@ -62,7 +54,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.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
|
@ -4,20 +4,22 @@ OBJDUMP=$(TOOLCHAIN_PREFIX)/bin/msp430-elf-objdump
|
||||
|
||||
ARTIFACT=firmware
|
||||
MCU=msp430g2553
|
||||
COMMONFLAGS=-Wall -mmcu=$(MCU) -I $(TOOLCHAIN_PREFIX)/include -g0 -fdata-sections -ffunction-sections
|
||||
|
||||
DEBUGFLAGS=
|
||||
# DEBUGFLAGS+= -g3 -ggdb -gdwarf-2
|
||||
COMMONFLAGS=-Wall -mmcu=$(MCU) -I $(TOOLCHAIN_PREFIX)/include -O0 -g0 $(DEBUGFLAGS)
|
||||
CFLAGS=$(COMMONFLAGS) -std=gnu99
|
||||
ASFLAGS=$(COMMONFLAGS) -D__ASSEMBLER__
|
||||
|
||||
LDFLAGS=-mmcu=$(MCU) -L $(TOOLCHAIN_PREFIX)/include
|
||||
CFLAGS=$(COMMONFLAGS) -std=gnu99 -O0 $(DEBUGFLAGS)
|
||||
ASFLAGS=$(COMMONFLAGS) -D__ASSEMBLER__ -Os $(DEBUGFLAGS)
|
||||
|
||||
LDFLAGS=-mmcu=$(MCU) -L $(TOOLCHAIN_PREFIX)/include -Wl,-Map,firmware.map
|
||||
|
||||
$(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
|
||||
$(CC) -o $@ $(LDFLAGS) $^
|
||||
$(OBJDUMP) -D $(ARTIFACT).elf > $(ARTIFACT).txt
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) -c $<
|
||||
$(CC) $(CFLAGS) -c $<
|
||||
|
||||
.S.o:
|
||||
$(CC) $(ASFLAGS) -c $<
|
||||
|
@ -6,8 +6,6 @@
|
||||
#include "psg.h"
|
||||
#include "scheduler.h"
|
||||
#include "sequencer.h"
|
||||
#include "melody_tetris.h"
|
||||
#include "melody_tusch1.h"
|
||||
#include "mute.h"
|
||||
|
||||
int main() {
|
||||
@ -31,7 +29,6 @@ int main() {
|
||||
|
||||
__enable_interrupt();
|
||||
|
||||
|
||||
while (1) {
|
||||
schExec();
|
||||
}
|
||||
|
@ -5,11 +5,8 @@
|
||||
#include "scheduler.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_5, .note = e_C, .length = e_L_1_4, .legato = false, .staccato = true },
|
||||
{ .octave = e_O_5, .note = e_G, .length = e_L_1_4, .legato = false, .staccato = true },
|
||||
|
||||
{ .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 },
|
||||
@ -19,9 +16,10 @@ const t_tone plingVoice1[] = {
|
||||
|
||||
t_melodies pling = {
|
||||
.melodies = { { .amplitude = 12, .tones = plingVoice1 } },
|
||||
.chip = 1,
|
||||
.numOfMelodies = 1,
|
||||
.pace = 200,
|
||||
.chip = 1
|
||||
.slotMask = 0x02
|
||||
};
|
||||
|
||||
void playPling() {
|
||||
|
@ -922,21 +922,21 @@ const t_tone voice3[] = {
|
||||
{ .octave = e_O_Null, .note = e_Null, .length = e_L_EndMark, .legato = false, .staccato = false },
|
||||
};
|
||||
|
||||
#define INITIAL_PACE 160
|
||||
t_melodies tetrisTheme = {
|
||||
.melodies = { { .amplitude = 8, .tones = voice1 }, { .amplitude = 8, .tones = voice2 }, { .amplitude = 8, .tones = voice3 } },
|
||||
.chip = 0,
|
||||
.numOfMelodies = 3,
|
||||
.pace = INITIAL_PACE,
|
||||
.chip = 0
|
||||
.pace = 160,
|
||||
.slotMask = 0x01
|
||||
};
|
||||
|
||||
void playMelodyTetris() {
|
||||
tetrisTheme.pace = INITIAL_PACE; // reset to start value each time
|
||||
tetrisTheme.pace = 160; // reset to start value each time
|
||||
sequencerPlayMelodies(&tetrisTheme);
|
||||
}
|
||||
|
||||
void playMelodyTetrisFaster() {
|
||||
tetrisTheme.pace += 15;
|
||||
tetrisTheme.pace += 10;
|
||||
sequencerChangePace(&tetrisTheme);
|
||||
}
|
||||
|
||||
|
@ -73,14 +73,14 @@ const t_tone tusch1voice3[] = {
|
||||
|
||||
t_melodies tusch1 = {
|
||||
.melodies = { { .amplitude = 12, .tones = tusch1voice1 }, { .amplitude = 12, .tones = tusch1voice2 }, { .amplitude = 12, .tones = tusch1voice3 } },
|
||||
.chip = 1,
|
||||
.numOfMelodies = 3,
|
||||
.pace = 200,
|
||||
.chip = 1
|
||||
.slotMask = 0x02
|
||||
};
|
||||
|
||||
void playTusch1() {
|
||||
sequencerPlayMelodies(&tusch1);
|
||||
// playMelodyTetrisFaster();
|
||||
}
|
||||
|
||||
|
||||
|
@ -8,15 +8,15 @@ void muteInit() {
|
||||
P1DIR |= BIT6;
|
||||
|
||||
// initially, mute
|
||||
P1OUT &= ~BIT6;
|
||||
}
|
||||
|
||||
void mute() {
|
||||
P1OUT &= ~BIT6;
|
||||
}
|
||||
|
||||
void unMute() {
|
||||
P1OUT |= BIT6;
|
||||
}
|
||||
|
||||
void mute() {
|
||||
P1OUT |= BIT6;
|
||||
}
|
||||
|
||||
void unMute() {
|
||||
P1OUT &= ~BIT6;
|
||||
}
|
||||
|
||||
|
||||
|
@ -81,6 +81,19 @@ inline static void BUS_OP_CS1_DISABLE() {
|
||||
BUS_CTRL_REG |= _CS1;
|
||||
}
|
||||
|
||||
#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
|
||||
|
||||
static uint8_t psgReadShadow(uint8_t chip, uint8_t address) {
|
||||
return psgShadowRegisters[chip][address];
|
||||
}
|
||||
@ -162,5 +175,21 @@ void psgInit() {
|
||||
// disable everything
|
||||
psgWrite(0, _ENABLE_REG, 0xff);
|
||||
psgWrite(1, _ENABLE_REG, 0xff);
|
||||
|
||||
// volume to 0 on all channels
|
||||
psgWrite(0, CHANNEL_A_AMPLITUDE_REG, 0);
|
||||
psgWrite(0, CHANNEL_B_AMPLITUDE_REG, 0);
|
||||
psgWrite(0, CHANNEL_C_AMPLITUDE_REG, 0);
|
||||
psgWrite(1, CHANNEL_A_AMPLITUDE_REG, 0);
|
||||
psgWrite(1, CHANNEL_B_AMPLITUDE_REG, 0);
|
||||
psgWrite(1, CHANNEL_C_AMPLITUDE_REG, 0);
|
||||
|
||||
// frequency preset
|
||||
psgWriteFrequency(0, 0, 0);
|
||||
psgWriteFrequency(0, 1, 0);
|
||||
psgWriteFrequency(0, 2, 0);
|
||||
psgWriteFrequency(1, 0, 0);
|
||||
psgWriteFrequency(1, 1, 0);
|
||||
psgWriteFrequency(1, 2, 0);
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,16 @@ uint16_t schAdd(void (*exec)(void *), void *handle, uint32_t delay, uint32_t per
|
||||
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) {
|
||||
tasks[taskId].exec = NULL;
|
||||
}
|
||||
|
@ -96,11 +96,9 @@ void sequencerExec(void *handle) {
|
||||
melody->state = e_PlayTone;
|
||||
break;
|
||||
case e_Hold:
|
||||
psgPlayTone(melodies->chip, channel, 0, e_O_Null, e_Pause);
|
||||
break;
|
||||
case e_Terminate:
|
||||
schDel(melodies->taskId);
|
||||
psgPlayTone(melodies->chip, channel, 0, e_O_Null, e_Pause);
|
||||
slots &= ~(melodies->slotMask);
|
||||
break;
|
||||
}
|
||||
@ -108,8 +106,6 @@ void sequencerExec(void *handle) {
|
||||
}
|
||||
|
||||
void sequencerPlayMelodies(t_melodies *melodies) {
|
||||
melodies->slotMask = (1 << melodies->chip);
|
||||
|
||||
if ((slots & melodies->slotMask) != 0) {
|
||||
return;
|
||||
}
|
||||
@ -127,11 +123,8 @@ void sequencerPlayMelodies(t_melodies *melodies) {
|
||||
}
|
||||
|
||||
void sequencerStopMelodies(t_melodies *melodies) {
|
||||
schDel(melodies->taskId);
|
||||
slots &= ~(melodies->slotMask);
|
||||
for (uint8_t channel = 0; channel < melodies->numOfMelodies; channel++) {
|
||||
psgPlayTone(melodies->chip, channel, 0, e_O_Null, e_Pause);
|
||||
}
|
||||
schDel(melodies->taskId);
|
||||
}
|
||||
|
||||
void sequencerChangePace(t_melodies *melodies) {
|
||||
|
@ -51,8 +51,8 @@ typedef struct {
|
||||
#define SEQUENCER_PERIOD 4 // ms
|
||||
#define NUM_OF_CHANNELS 3
|
||||
typedef struct {
|
||||
uint8_t slotMask;
|
||||
uint8_t chip;
|
||||
uint8_t slotMask;
|
||||
uint8_t taskId;
|
||||
uint16_t quarterLength;
|
||||
uint8_t numOfMelodies;
|
||||
|
@ -1,6 +1,10 @@
|
||||
#include <msp430g2553.h>
|
||||
#include "soundCodes.h"
|
||||
|
||||
;; .global cmd
|
||||
;; .section .data.spi,"aw"
|
||||
;;cmd:
|
||||
;; .byte
|
||||
|
||||
.section ".text","ax",@progbits
|
||||
receive_isr:
|
||||
@ -25,18 +29,19 @@ spiCmdHandler_2:
|
||||
spiCmdHandler_3:
|
||||
bit #SOUND_START, &cmd
|
||||
jz spiCmdHandler_4
|
||||
call #playMelodyTetris
|
||||
;;call #playMelodyTetris
|
||||
bic #SOUND_START, &cmd
|
||||
spiCmdHandler_4:
|
||||
bit #SOUND_GAMEOVER, &cmd
|
||||
jz spiCmdHandler_5
|
||||
call #stopMelodyTetris
|
||||
;;call #stopMelodyTetris
|
||||
;; insert a call here
|
||||
bic #SOUND_GAMEOVER, &cmd
|
||||
spiCmdHandler_5:
|
||||
bit #SOUND_FANFARE, &cmd
|
||||
jz spiCmdHandler_6
|
||||
call #playMelodyTetrisFaster
|
||||
call #playTusch1
|
||||
;;call #playMelodyTetrisFaster
|
||||
;;call #playTusch1
|
||||
bic #SOUND_FANFARE, &cmd
|
||||
spiCmdHandler_6:
|
||||
bit #SOUND_LOCK, &cmd
|
||||
@ -51,14 +56,12 @@ spiCmdHandler_7:
|
||||
spiCmdHandler_8:
|
||||
bit #SOUND_PLING, &cmd
|
||||
jz spiCmdHandler_end
|
||||
call #playPling
|
||||
;;call #playPling
|
||||
bic #SOUND_PLING, &cmd
|
||||
spiCmdHandler_end:
|
||||
ret
|
||||
|
||||
|
||||
.section "__interrupt_vector_8","ax",@progbits
|
||||
.word receive_isr
|
||||
|
||||
|
||||
.end
|
||||
|
Reference in New Issue
Block a user