/* Made by Ben Duncan (C) */
// Included header files
#include <8051.h>
#include <string.h>
#include <stdio.h> /* for printf, printf_fast, etc */
// Subroutine decleration
extern void delay_ms(unsigned char ms);
extern void restart(void);
extern char key_char(void);
extern void lcd_putchar(char c);
extern void lcd_command(unsigned char cmd);
extern void lcd_set_xy_raw(unsigned int x_and_y);
extern char a2d(void);
void delay(int s); // Integer seconds only delay
void display_rules(void);
void display_countdown(char count); // Displays a countdown from 'count' to 0
void generate_player(void);
void generate_enemies(void);
// Memory map locations for TAD board
#define DIL_SWITCH 0x0000 // DIL Switch at address 0000h
#define DAC 0x0600 // DAC at address 0600h
#define LCD_COMMAND_WR 0x0800 // Write LCD Command at address 0800h
#define LCD_STATUS_RD 0x0801 // Read LCD Status at address 0801h
#define LCD_DATA_WR 0x0802 // Write LCD Data at address 0802h
#define LCD_DATA_RD 0x0803 // Read LCD Data at address 0803h
#define COMPARATOR P3_5 // Comparator is set to bit P3.5
// External data locations
volatile xdata at DAC unsigned char dac;
volatile xdata at LCD_COMMAND_WR unsigned char lcd_command_wr;
volatile xdata at LCD_STATUS_RD unsigned char lcd_status_rd;
volatile xdata at LCD_DATA_WR unsigned char lcd_data_wr;
volatile xdata at LCD_DATA_RD unsigned char lcd_data_rd;
// LCD commands
#define LCD_CLEAR_CMD 0x01
#define LCD_HOME_CMD 0x02
#define LCD_ON_CMD 0x0C
#define LCD_SHIFT_CMD 0x10
#define LCD_CONFIG_CMD 0x38
// Macro to merge two parameters into a single 16-bit parameter
#define lcd_set_xy(x, y) (lcd_set_xy_raw((x) + ((y) << 8)))
// LCD screen parameters
#define LCD_HORIZ_SIZE 16
#define LCD_VERT_SIZE 2
#define LCD_BYTES_PER_LINE (128 / LCD_VERT_SIZE)
// Send serial data to LCD or Serial Port
bit print_to_lcd=0;
// Interrupt declaration
void ex0_isr(void) interrupt 0;
void ex1_isr(void) interrupt 2;
// Set the side of the 'track' the player is on
bit player_side = 0; // Defaults to the right side '0'
// Soft-enable/disables interrupts
bit enable_interrupts;
/* Player and characters */
#define PLAYER "X"
#define MINE "*"
#define PLAYER_X 16 // Sets player to right of screen
#define ENEMY_SPACES 12 // # spaces enemies can exist in
#define ENEMY_START 0 // Start position of enemies
#define ENEMY_END_ 11 // End position of enemies
#define NUM_ENEMY_STR 1 // Number of enemy strings
/* Enemy sprites */
char enemy_track1[20] = {1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0}; // Sprites of enemies on track 1
char enemy_track2[20] = {1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0}; // Sprites of enemies on track 2
/* Keyboard character codes */
#define BS_KEY 8
#define ESC_KEY 27
#define SPACE_KEY 32
#define DEL_KEY 127
/* Main code section */
void main(){
/* Initialise devices */
enable_interrupts = 0; // Disallow interrupt functions to change 'player_side' state
EA=1; // Configure interrupt 0 for falling edge on /INT0 (P3.2)
EX0 = 1; // Enable EX0 Interrupt
EX1 = 1; // Enable EX1 Interrupt
lcd_command(LCD_HOME_CMD); // Initialise LCD
lcd_command(LCD_CLEAR_CMD); // Clear LCD screen
print_to_lcd=1; // Select LCD
lcd_set_xy(0,0); // Select LCD character
printf_fast("- Mine Dodger! -"); // Display first line of LCD
lcd_set_xy(0,1); // Select LCD character
printf_fast(" By Ben Duncan "); // Display second line of LCD
delay(3); // Wait for 3 seconds
lcd_command(LCD_CLEAR_CMD); // Clear LCD screen
/* Main control loop that exits when 'esc' is pressed on the keyboard */
do{
enable_interrupts = 1; // Allow interrupt functions to change 'player_side' state
generate_player(); // Print player to screen
generate_enemies(); // Display enemies on LCD screen
} while(key_char() != ESC_KEY); // When 'esc' is pressed escape loop
}
/* Subroutines for Display */
void generate_enemies(void){
int j,i;
for(i=0; i<ENEMY_SPACES; i++){
for(j=0; j<ENEMY_SPACES; j++){
lcd_set_xy(i, 0); // Set position of line 1 of enemies
printf_fast(enemy_track1[j]);
lcd_set_xy(i, 1); // Set position of line 2 of enemies
printf_fast(enemy_track2[j]);
}
speed_delay(1); // Delay for enemy speed
i++ // Move enemies up one place on the screen
if(i=>ENEMY_SPACES){ // If the next enemy moves past the end position
i=0; // Start at 0 again
}
}
}
void generate_player(void){
lcd_set_xy(PLAYER_X, player_side); // Place player on right of screen on 'player_side'
printf_fast(PLAYER); // Print player (X)
delay_ms(10); // Wait for LCD to refresh
}
void display_countdown(char count){ // Passes value of countdown
int n; // Counting variable
lcd_command(LCD_CLEAR_CMD); // Clear LCD screen
for(n=count; n>0; n--){ // Count 5-0
lcd_set_xy(0,0); // Select LCD character
printf_fast("Start in: %d ", n); // Display first line of LCD
delay(1); // Wait for 1 second
}
}
void display_rules(void){
lcd_command(LCD_CLEAR_CMD); // Clear LCD screen
lcd_set_xy(0,0); // Select LCD character
printf_fast("* are the mines "); // Display first line of LCD
lcd_set_xy(0,1); // Select LCD character
printf_fast(" & are dangerous"); // Display second line of LCD
delay(5); // Wait for 5 seconds
lcd_set_xy(0,0); // Select LCD character
printf_fast("X is the vehicle"); // Display first line of LCD
lcd_set_xy(0,1); // Select LCD character
printf_fast("Avoid the mines!"); // Display second line of LCD
delay(5); // Wait for 5 seconds
}
/* Subroutines for Interrupts */
void ex0_isr(void) interrupt 0{ // 'Left' button
if(enable_interrupts==1){ // If enabled
if(player_side==1){ // If player on right
player_side = 0; // Set player to left side
lcd_set_xy(PLAYER_X,player_side); // Select location of player
printf_fast(PLAYER); // Display player on screen
}
}
}
void ex1_isr(void) interrupt 2{ // 'Right' button
if(enable_interrupts==1){ // If enabled
if(player_side==0){ // If player on left
player_side = 1; // Set player to right side
lcd_set_xy(PLAYER_X,player_side); // Select location of player
printf_fast(PLAYER); // Display player on screen
}
}
}
/* Subroutines to control and communicate with the LCD */
void putchar(char c) // Write character to lcd or serial port
{
if (print_to_lcd) // If print_to_lcd is set then write to lcd
{
lcd_putchar(c);
}
else // Else write to the serial port
{
while(!TI); /* Wait for ready */
TI=0;
SBUF = c;
}
}
char getchar(void) // Get character from serial port
{
char c;
while(!RI);
RI =0;
c = SBUF;
return c;
}
void restart(void) // Soft reset, jumps to address 0000h
{
_asm
ljmp 0
_endasm;
}
char key_char(void)
{
char c;
if (RI)
{
RI = 0;
c = SBUF;
}
return c;
}
char a2d(void)
{
unsigned char val=0;
dac = 0;
delay_ms(1);
while (COMPARATOR == 1)
{
val = val + 1;
dac = val;
delay_ms(1); // Allow time for DAC to settle
}
return (val);
}
/* This is a simple delay based on a busy loop */
/* with an 11.0592 MHz crystal, the delay lasts */
/* for approximately 1 ms times the number passed */
/* Of course, it will take longer if there are */
/* interrupts occuring... */
void delay_ms(unsigned char ms)
{
ms;
_asm
mov r0, dpl
00001$: mov r1, #250
00002$: nop
djnz r1, 00002$
djnz r0, 00001$
ret
_endasm;
}
void delay(int s){ // Wait for 1 second
int i,j;
for(j=0; j<4; j++){
for(i=0; i<s; i++){ // Delays multiple times
delay_ms(250); // Quater of a second
}
}
}
/* This C-only version is easier to read and understand, but it is
* not as fast and efficient as the assembly version below. You
* should normally use the optimized assembly version, but read
* this one to understand the code.
*/
volatile xdata at LCD_COMMAND_WR unsigned char lcd_command_wr;
volatile xdata at LCD_STATUS_RD unsigned char lcd_status_rd;
volatile xdata at LCD_DATA_WR unsigned char lcd_data_wr;
volatile xdata at LCD_DATA_RD unsigned char lcd_data_rd;
void lcd_busy(void)
{
while (lcd_status_rd & 0x80) ;
}
void lcd_command(unsigned char cmd)
{
lcd_busy();
lcd_command_wr = cmd;
}
void lcd_putchar(char c)
{
lcd_busy();
if (c=='\n') lcd_command_wr = LCD_CLEAR_CMD;
else lcd_data_wr = c;
}
void lcd_set_xy_raw(unsigned int x_and_y)
{
unsigned char x, y;
if ((x = x_and_y & 255) >= LCD_HORIZ_SIZE) x = LCD_HORIZ_SIZE - 1;
if ((y = x_and_y >> 8) >= LCD_VERT_SIZE) y = LCD_VERT_SIZE - 1;
lcd_busy();
lcd_command_wr = (y * LCD_BYTES_PER_LINE + x) | 0x80;
}
// End