222 lines
6.7 KiB
C
222 lines
6.7 KiB
C
/*
|
|
// Function: Retro Handheld Tetris Game Console source code, including game GUI,
|
|
// Tetris, Snake, Classic Racing, and Obstacle Shooting games.
|
|
|
|
v1.0:
|
|
2014-7-26 00:00:00 : Completed the basic model for the Tetris game;
|
|
2014-7-28 02:12:03 : Changed the temporary delay-based keyboard scanning to external interrupt status detection;
|
|
2014-7-28 20:40:37 : Implemented Tetris scoring and score display functions;
|
|
2014-8-1 01:01:54 : Merged and integrated the previously completed Snake game code;
|
|
2014-8-2 17:04:25 : Organized temporary source code to standardize project files;
|
|
2014-8-3 23:50:49 : Built the basic model for the game selection screen and completed the screen transition effect;
|
|
2014-8-5 00:50:04 : Improved LED dot-matrix low-level driver, added software PWM to adjust screen brightness;
|
|
2014-8-7 00:53:35 : Completed the racing game; used PWM layered scanning for screen display to create contrast;
|
|
2014-8-8 02:16:44 : Completed the racing game preview screen and merged all three games;
|
|
2014-8-12 01:47:07 : Changed line-by-line scanning to point-by-point scanning for uniform display brightness; added brightness adjustment;
|
|
2014-9-9 00:32:40 : Modified and optimized several details;
|
|
|
|
v1.1:
|
|
2014-9-18 0:57:23 : Re-laid out the circuit, corrected unreasonable design parts, and prepared for PCB production;
|
|
2014-9-19 22:02:36 : Added game sound effects with software-controlled mute switch;
|
|
2014-9-21 14:59:39 : Added three mini digital tubes to display the game score in real-time, sharing the parallel data port with the dot-matrix;
|
|
~~~~~~~~~~~ Note forgotten ~~~~~~~~~~~
|
|
2014-10-20 21:30:15 : Added shooting game and modularized the GUI to simplify the workflow.
|
|
*/
|
|
|
|
#include "config.h"
|
|
volatile uint8 data keypad = K_NULL; // Keypad status
|
|
volatile uint8 data keycont = 0; // Keypad debounce timer
|
|
volatile uint16 data tms = 0; // 1ms T0 timer
|
|
bit key_state = 0; // Key state
|
|
|
|
uint16 data speeds = 0; // Game speed
|
|
uint16 data scores = 0; // Game score
|
|
bit Glife = 1; // Game life status
|
|
bit sound_ON = 1; // Sound effect switch
|
|
uint8 data duty = 80; // PWM, default brightness
|
|
uint8 data DispRAM[16]={0}; // Display RAM
|
|
uint8 code bitman[8]={1,2,4,8,16,32,64,128}; // Bitmask for locating bits 0-7
|
|
|
|
uint8 code num[10][5]= // Yang code
|
|
{
|
|
{0x7,0x5,0x5,0x5,0x7},
|
|
{0x2,0x6,0x2,0x2,0x7},
|
|
{0x7,0x1,0x7,0x4,0x7},
|
|
{0x7,0x1,0x7,0x1,0x7},
|
|
{0x5,0x5,0x7,0x1,0x1},
|
|
{0x7,0x4,0x7,0x1,0x7},
|
|
{0x7,0x4,0x7,0x5,0x7},
|
|
{0x7,0x1,0x1,0x1,0x1},
|
|
{0x7,0x5,0x7,0x5,0x7},
|
|
{0x7,0x5,0x7,0x1,0x7}
|
|
};
|
|
|
|
/*********************PORT_INIT********************/
|
|
void Port_Init()
|
|
{
|
|
P0M0 = 0Xff;
|
|
P0M1 = 0X00;
|
|
P1M0 = 0Xff;
|
|
P1M1 = 0X00;
|
|
P2M0 = 0Xff;
|
|
P2M1 = 0X00;
|
|
P3M0 = 0X00;
|
|
P3M1 = 0X00;
|
|
P4M0 = 0Xff;
|
|
P4M1 = 0X00;
|
|
P5M0 = 0Xff;
|
|
P5M1 = 0X00;
|
|
}
|
|
|
|
void Init_Timer()
|
|
{
|
|
/****************** Timer 0 Interrupt Setup **********************/
|
|
AUXR |= 0x80; // Set Timer 0 to 1T mode
|
|
TMOD = 0x00; // Set Timer 0 to Mode 0 (16-bit auto-reload)
|
|
TL0 = T1MS; // Initialize timer value
|
|
TH0 = T1MS >> 8;
|
|
TR0 = 1; // Start Timer 0
|
|
ET0 = 1; // Enable Timer 0 interrupt
|
|
|
|
/***************** External Interrupt Setup *************************/
|
|
IT0 = 1; // Set INT0 to falling edge trigger
|
|
EX0 = 1; // Enable external interrupt INT0
|
|
IT1 = 1; // Set INT1 to falling edge trigger
|
|
EX1 = 1; // Enable external interrupt INT1
|
|
INT_CLKO |= 0x70; // Enable INT2, INT3, INT4 (EX4 = 1, EX3 = 1, EX2 = 1)
|
|
|
|
EA = 1; // Enable global interrupts
|
|
}
|
|
|
|
void Array_CLR(uint8 *p)
|
|
{
|
|
uint8 i=0;
|
|
while(i!=16)
|
|
{
|
|
p[i] = 0x00;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
void ShowScore(uint score)
|
|
{
|
|
uint8 i;
|
|
uint8 qx,bx,sx,gx;
|
|
qx = score /1000;
|
|
bx = score %1000 /100;
|
|
sx = score %100/10;
|
|
gx = score %10;
|
|
|
|
Array_CLR(DispRAM);
|
|
|
|
for(i=0;i<5;i++)
|
|
DispRAM[8-i] = (num[sx][i]<<4) | (num[gx][i]);
|
|
|
|
for(i=0;i<5;i++)
|
|
DispRAM[14-i] = (num[qx][i]<<5) | (num[bx][i]<<1);
|
|
|
|
// MatxDisp(DispRAM,duty);
|
|
}
|
|
|
|
void main()
|
|
{
|
|
Port_Init();
|
|
Sound_Init();
|
|
Init_Timer();
|
|
Sound_Tone(1,14,5);// drop
|
|
|
|
srand(TL0);
|
|
delayms(100);
|
|
Play_Music(sound_game_start);
|
|
Flash_Screen_Clear();
|
|
while(1)
|
|
{
|
|
tms = 0;
|
|
keypad = K_NULL;// Clear key status
|
|
switch (GUI_Main()) // Enter the user game selection interface and return to the selected game.
|
|
{
|
|
case 1: Tetris_Game(); break;
|
|
case 2: Snake_Game(); break;
|
|
case 3: RAC_Game(); break;
|
|
case 4: Shot_Game(); break;
|
|
}
|
|
Play_Music(sound_game_over);
|
|
Flash_Screen_Clear();
|
|
ShowScore(scores); // Load the score into video memory
|
|
delayms(300);
|
|
|
|
keypad = K_NULL;
|
|
while(keypad != K_UP) // Keep the score displayed when the OK button is not pressed.
|
|
{
|
|
MatxDisp(DispRAM,duty);
|
|
SMG_Display(scores,duty);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Timer0 interrupt routine */
|
|
void T0_Timer_1ms_int() interrupt 1 using 1
|
|
{
|
|
if(tms<0xfffe) tms++; // Prevent overflow
|
|
if(key_state){ if(!--keycont) key_state=0;}
|
|
|
|
if(PIN_TR2)// If TR2 is enabled, sound effects will begin playing.
|
|
{
|
|
if(!sound_cnt_time--)
|
|
{
|
|
TR2_OFF;
|
|
beep = 1;
|
|
if(music_p[s_p][1])
|
|
{
|
|
Sound_Tone(sound_ON,music_p[s_p][0],music_p[s_p][1]);
|
|
s_p++; // Automatically load the next note to achieve simultaneous sound effects and gameplay.
|
|
}
|
|
}
|
|
}
|
|
if(KEY_DOWN==0 & KEY_UP==0) IAP_CONTR=0x60;// Software reset download program
|
|
}
|
|
|
|
void T2_Timer_Sound_freq() interrupt 12 // Interrupt Entry
|
|
{
|
|
beep = ~beep; // Buzzer frequency vibration
|
|
}
|
|
|
|
uint8 Get_Kvuale(uint8 key_delay)// Key debouncing processing, parameter is sensitivity adjustment
|
|
{
|
|
uint8 kvuale = K_NULL; // The returned initial value is null.
|
|
if(keypad != K_NULL) // When the key value is not NULL, it means that a key has been pressed.
|
|
{
|
|
if(!key_state) // When the button status bit (state) is 0, it indicates that the first trigger is valid; when it is 1, it indicates a repeated trigger.
|
|
{
|
|
key_state=1; // Set to 1 to prevent repeated triggering.
|
|
kvuale = keypad;// Get Read Key Value
|
|
keycont = key_delay;// Loading debouncing time
|
|
}
|
|
keypad = K_NULL; // Clear key value
|
|
}
|
|
return kvuale;
|
|
}
|
|
/********* External interrupt button area **********/
|
|
void exint0() interrupt 0 //INT0
|
|
{
|
|
keypad = 0;
|
|
}
|
|
void exint1() interrupt 2 //INT1
|
|
{
|
|
keypad = 1;
|
|
}
|
|
void exint2() interrupt 10 //INT2
|
|
{
|
|
keypad = 2;
|
|
}
|
|
void exint3() interrupt 11 //INT3
|
|
{
|
|
keypad = 3;
|
|
}
|
|
void exint4() interrupt 16 //INT4
|
|
{
|
|
keypad = 4;
|
|
}
|
|
|
|
|