Appendix A

Schematic

8051 Schematic

Space Invaders Schematic

Appendix B

Include Files

notes.h

The following code is for reference to the note.h file.

#define C0 84550
#define Cs0 79815
#define Db0 79815
#define D0 75335
#define Ds0 71075
#define Eb0 71075
#define E0 67107
#define F0 63326
#define Fs0 59792
#define Gb0 59792
#define G0 56424
#define Gs0 53251
#define Ab0 53251
#define A0 50269
#define As0 47440
#define Bb0 47440
#define B0 44781
#define C1 42275
#define Cs1 39896
#define Db1 39896
#define D1 37657
#define Ds1 35546
#define Eb1 35546
#define E1 33553
#define F1 31670
#define Fs1 29890
#define Gb1 29890
#define G1 28212
#define Gs1 26631
#define Ab1 26631
#define A1 25135
#define As1 23724
#define Bb1 23724
#define B1 22391
#define C2 21134
#define Cs2 19948
#define Db2 19948
#define D2 18829
#define Ds2 17773
#define Eb2 17773
#define E2 16775
#define F2 15833
#define Fs2 14945
#define Gb2 14945
#define G2 14106
#define Gs2 13314
#define Ab2 13314
#define A2 12567
#define As2 11862
#define Bb2 11862
#define B2 11196
#define C3 10568
#define Cs3 9975
#define Db3 9975
#define D3 9415
#define Ds3 8887
#define Eb3 8887
#define E3 8388
#define F3 7917
#define Fs3 7472
#define Gb3 7472
#define G3 7053
#define Gs3 6657
#define Ab3 6657
#define A3 6284
#define As3 5931
#define Bb3 5931
#define B3 5598
#define C4 5284
#define Cs4 4987
#define Db4 4987
#define D4 4707
#define Ds4 4443
#define Eb4 4443
#define E4 4194
#define F4 3958
#define Fs4 3736
#define Gb4 3736
#define G4 3527
#define Gs4 3329
#define Ab4 3329
#define A4 3142
#define As4 2966
#define Bb4 2966
#define B4 2799
#define C5 2642
#define Cs5 2494
#define Db5 2494
#define D5 2354
#define Ds5 2222
#define Eb5 2222
#define E5 2097
#define F5 1979
#define Fs5 1868
#define Gb5 1868
#define G5 1763
#define Gs5 1664
#define Ab5 1664
#define A5 1571
#define As5 1483
#define Bb5 1483
#define B5 1400
#define C6 1321
#define Cs6 1247
#define Db6 1247
#define D6 1177
#define Ds6 1111
#define Eb6 1111
#define E6 1048
#define F6 990
#define Fs6 934
#define Gb6 934
#define G6 882
#define Gs6 832
#define Ab6 832
#define A6 785
#define As6 741
#define Bb6 741
#define B6 700
#define C7 660
#define Cs7 623
#define Db7 623
#define D7 588
#define Ds7 555
#define Eb7 555
#define E7 524
#define F7 495
#define Fs7 467
#define Gb7 467
#define G7 441
#define Gs7 416
#define Ab7 416
#define A7 393
#define As7 371
#define Bb7 371
#define B7 350
#define C8 330
#define Cs8 312
#define Db8 312
#define D8 294
#define Ds8 278
#define Eb8 278

lcd.h

The following code is for reference to the lcd.h file.

//
// LCD Interface
//
// This module initializes the 64x128 LCD module, declares a shadow memory
// in external memory, and provides subroutines to blank the shadow memory
// and/or copy that memory to the LCD.
//
//
// initialize LCD - Call this once at the beginning of time.
// It sets up LCD hardware, blanks the shadow memory then displays it on
// the screen.
//
void init_lcd(void);

//
// Copy shadow memory to LCD screen.
//
void refresh_screen(void);

//
// Clear the shadow memory.
//
void blank_screen(void);

//
// Shadow memory. 1024 bytes. Eight 128-byte pages. Each page corresponds
// to 8 rows of pixels. screen[0] is upper left, screen[127] is upper right,
// screen[1023] is lower right. Least significant bit of each byte is on the
// top pixel row of its page.
//
extern xdata char screen[];

//
// Handy 5x7 font that will come in handy in later labs. Always put at least
// a one pixel space between characters.
//
extern code char font5x8[];

C8051F020_defs.h

The following code is for reference to the C8051F020_defs.h file.

//-----------------------------------------------------------------------------
// C8051F020_defs.h
//-----------------------------------------------------------------------------
// Copyright 2007, Silicon Laboratories, Inc.
// http://www.silabs.com
//
// Program Description:
//
// Register/bit definitions for the C8051F02x family.
// ^^Important Note^^: The si_toolchain.h header file should be included
// before including this header file.
//
// Target:         C8051F020, 'F021, 'F022, 'F023
// Tool chain:     Generic
// Command Line:   None
//
// Release 1.4 - 20 AUG 2012 (TP)
//    -Added #define for _XPAGE to provide support for SDCC memory paging
//     (pdata)
// Release 1.3 - 07 AUG 2007 (PKC)
//    -Removed #include "si_toolchain.h". The C source file should include it.
// Release 1.2 - 09 JUL 2007 (PKC)
//    -Reformatted header file to enable portable SFR definitions

//-----------------------------------------------------------------------------
// Header File Preprocessor Directive
//-----------------------------------------------------------------------------

#ifndef C8051F020_DEFS_H
#define C8051F020_DEFS_H

//-----------------------------------------------------------------------------
// Byte Registers
//-----------------------------------------------------------------------------

SI_SFR(P0, 0x80);                        // Port 0 Latch
SI_SFR(SP, 0x81);                        // Stack Pointer
SI_SFR(DPL, 0x82);                       // Data Pointer Low
SI_SFR(DPH, 0x83);                       // Data Pointer High
SI_SFR(P4, 0x84);                        // Port 4 Latch
SI_SFR(P5, 0x85);                        // Port 5 Latch
SI_SFR(P6, 0x86);                        // Port 6 Latch
SI_SFR(PCON, 0x87);                      // Power Control
SI_SFR(TCON, 0x88);                      // Timer/Counter Control
SI_SFR(TMOD, 0x89);                      // Timer/Counter Mode
SI_SFR(TL0, 0x8A);                       // Timer/Counter 0 Low
SI_SFR(TL1, 0x8B);                       // Timer/Counter 1 Low
SI_SFR(TH0, 0x8C);                       // Timer/Counter 0 High
SI_SFR(TH1, 0x8D);                       // Timer/Counter 1 High
SI_SFR(CKCON, 0x8E);                     // Clock Control
SI_SFR(PSCTL, 0x8F);                     // Program Store R/W Control
SI_SFR(P1, 0x90);                        // Port 1 Latch
SI_SFR(TMR3CN, 0x91);                    // Timer/Counter 3 Control
SI_SFR(TMR3RLL, 0x92);                   // Timer/Counter 3 Reload Low
SI_SFR(TMR3RLH, 0x93);                   // Timer/Counter 3 Reload High
SI_SFR(TMR3L, 0x94);                     // Timer/Counter 3 Low
SI_SFR(TMR3H, 0x95);                     // Timer/Counter 3 High
SI_SFR(P7, 0x96);                        // Port 7 Latch
SI_SFR(SCON0, 0x98);                     // Serial Port UART0 Control
SI_SFR(SBUF0, 0x99);                     // Serial Port UART0 Data Buffer
SI_SFR(SPI0CFG, 0x9A);                   // SPI0 Configuration
SI_SFR(SPI0DAT, 0x9B);                   // SPI0 Data
SI_SFR(ADC1, 0x9C);                      // ADC1 Data
SI_SFR(SPI0CKR, 0x9D);                   // SPI0 Clock Rate Control
SI_SFR(CPT0CN, 0x9E);                    // Comparator 0 Control
SI_SFR(CPT1CN, 0x9F);                    // Comparator 1 Control
SI_SFR(P2, 0xA0);                        // Port 2 Latch
SI_SFR(EMI0TC, 0xA1);                    // EMIF Timing Control
SI_SFR(EMI0CF, 0xA3);                    // EMIF Configuration
SI_SFR(P0MDOUT, 0xA4);                   // Port 0 Output Mode Configuration
SI_SFR(P1MDOUT, 0xA5);                   // Port 1 Output Mode Configuration
SI_SFR(P2MDOUT, 0xA6);                   // Port 2 Output Mode Configuration
SI_SFR(P3MDOUT, 0xA7);                   // Port 3 Output Mode Configuration
SI_SFR(IE, 0xA8);                        // Interrupt Enable
SI_SFR(SADDR0, 0xA9);                    // Serial Port UART0 Slave Address
SI_SFR(ADC1CN, 0xAA);                    // ADC1 Control
SI_SFR(ADC1CF, 0xAB);                    // ADC1 Analog Mux Configuration
SI_SFR(AMX1SL, 0xAC);                    // ADC1 Analog Mux Channel Select
SI_SFR(P3IF, 0xAD);                      // Port 3 External Interrupt Flags
SI_SFR(SADEN1, 0xAE);                    // Serial Port UART1 Slave Address Mask
SI_SFR(EMI0CN, 0xAF);                    // EMIF Control
SI_SFR(P3, 0xB0);                        // Port 3 Latch
SI_SFR(OSCXCN, 0xB1);                    // External Oscillator Control
SI_SFR(OSCICN, 0xB2);                    // Internal Oscillator Control
SI_SFR(P74OUT, 0xB5);                    // Ports 4 - 7 Output Mode
SI_SFR(FLSCL, 0xB6);                     // Flash Memory Timing Prescaler
SI_SFR(FLACL, 0xB7);                     // Flash Acess Limit
SI_SFR(IP, 0xB8);                        // Interrupt Priority
SI_SFR(SADEN0, 0xB9);                    // Serial Port UART0 Slave Address Mask
SI_SFR(AMX0CF, 0xBA);                    // ADC0 Mux Configuration
SI_SFR(AMX0SL, 0xBB);                    // ADC0 Mux Channel Selection
SI_SFR(ADC0CF, 0xBC);                    // ADC0 Configuration
SI_SFR(P1MDIN, 0xBD);                    // Port 1 Input Mode
SI_SFR(ADC0L, 0xBE);                     // ADC0 Data Low
SI_SFR(ADC0H, 0xBF);                     // ADC0 Data High
SI_SFR(SMB0CN, 0xC0);                    // SMBus0 Control
SI_SFR(SMB0STA, 0xC1);                   // SMBus0 Status
SI_SFR(SMB0DAT, 0xC2);                   // SMBus0 Data
SI_SFR(SMB0ADR, 0xC3);                   // SMBus0 Slave Address
SI_SFR(ADC0GTL, 0xC4);                   // ADC0 Greater-Than Register Low
SI_SFR(ADC0GTH, 0xC5);                   // ADC0 Greater-Than Register High
SI_SFR(ADC0LTL, 0xC6);                   // ADC0 Less-Than Register Low
SI_SFR(ADC0LTH, 0xC7);                   // ADC0 Less-Than Register High
SI_SFR(T2CON, 0xC8);                     // Timer/Counter 2 Control
SI_SFR(T4CON, 0xC9);                     // Timer/Counter 4 Control
SI_SFR(RCAP2L, 0xCA);                    // Timer/Counter 2 Capture Low
SI_SFR(RCAP2H, 0xCB);                    // Timer/Counter 2 Capture High
SI_SFR(TL2, 0xCC);                       // Timer/Counter 2 Low
SI_SFR(TH2, 0xCD);                       // Timer/Counter 2 High
SI_SFR(SMB0CR, 0xCF);                    // SMBus0 Clock Rate
SI_SFR(PSW, 0xD0);                       // Program Status Word
SI_SFR(REF0CN, 0xD1);                    // Voltage Reference 0 Control
SI_SFR(DAC0L, 0xD2);                     // DAC0 Register Low
SI_SFR(DAC0H, 0xD3);                     // DAC0 Register High
SI_SFR(DAC0CN, 0xD4);                    // DAC0 Control
SI_SFR(DAC1L, 0xD5);                     // DAC1 Register Low
SI_SFR(DAC1H, 0xD6);                     // DAC1 Register High
SI_SFR(DAC1CN, 0xD7);                    // DAC1 Control
SI_SFR(PCA0CN, 0xD8);                    // PCA0 Control
SI_SFR(PCA0MD, 0xD9);                    // PCA0 Mode
SI_SFR(PCA0CPM0, 0xDA);                  // PCA0 Module 0 Mode Register
SI_SFR(PCA0CPM1, 0xDB);                  // PCA0 Module 1 Mode Register
SI_SFR(PCA0CPM2, 0xDC);                  // PCA0 Module 2 Mode Register
SI_SFR(PCA0CPM3, 0xDD);                  // PCA0 Module 3 Mode Register
SI_SFR(PCA0CPM4, 0xDE);                  // PCA0 Module 4 Mode Register
SI_SFR(ACC, 0xE0);                       // Accumulator
SI_SFR(XBR0, 0xE1);                      // Port I/O Crossbar Control 0
SI_SFR(XBR1, 0xE2);                      // Port I/O Crossbar Control 1
SI_SFR(XBR2, 0xE3);                      // Port I/O Crossbar Control 2
SI_SFR(RCAP4L, 0xE4);                    // Timer 4 Capture Register Low
SI_SFR(RCAP4H, 0xE5);                    // Timer 4 Capture Register High
SI_SFR(EIE1, 0xE6);                      // External Interrupt Enable 1
SI_SFR(EIE2, 0xE7);                      // External Interrupt Enable 2
SI_SFR(ADC0CN, 0xE8);                    // ADC0 Control
SI_SFR(PCA0L, 0xE9);                     // PCA0 Counter Low
SI_SFR(PCA0CPL0, 0xEA);                  // PCA0 Capture 0 Low
SI_SFR(PCA0CPL1, 0xEB);                  // PCA0 Capture 1 Low
SI_SFR(PCA0CPL2, 0xEC);                  // PCA0 Capture 2 Low
SI_SFR(PCA0CPL3, 0xED);                  // PCA0 Capture 3 Low
SI_SFR(PCA0CPL4, 0xEE);                  // PCA0 Capture 4 Low
SI_SFR(RSTSRC, 0xEF);                    // Reset Source Configuration/Status
SI_SFR(B, 0xF0);                         // B Register
SI_SFR(SCON1, 0xF1);                     // Serial Port UART1 Control
SI_SFR(SBUF1, 0xF2);                     // Serail Port UART1 Data
SI_SFR(SADDR1, 0xF3);                    // Serail Port UART1 Slave Address
SI_SFR(TL4, 0xF4);                       // Timer/Counter 4 Low
SI_SFR(TH4, 0xF5);                       // Timer/Counter 4 High
SI_SFR(EIP1, 0xF6);                      // External Interrupt Priority 1
SI_SFR(EIP2, 0xF7);                      // External Interrupt Priority 2
SI_SFR(SPI0CN, 0xF8);                    // SPI0 Control
SI_SFR(PCA0H, 0xF9);                     // PCA0 Counter High
SI_SFR(PCA0CPH0, 0xFA);                  // PCA0 Capture 0 High
SI_SFR(PCA0CPH1, 0xFB);                  // PCA0 Capture 1 High
SI_SFR(PCA0CPH2, 0xFC);                  // PCA0 Capture 2 High
SI_SFR(PCA0CPH3, 0xFD);                  // PCA0 Capture 3 High
SI_SFR(PCA0CPH4, 0xFE);                  // PCA0 Capture 4 High
SI_SFR(WDTCN, 0xFF);                     // Watchdog Timer Control

//-----------------------------------------------------------------------------
// 16-bit Register Definitions (might not be supported by all compilers)
//-----------------------------------------------------------------------------

SI_SFR16(DP, 0x82);                      // Data Pointer
SI_SFR16(TMR3RL, 0x92);                  // Timer3 Reload Value
SI_SFR16(TMR3, 0x94);                    // Timer3 Counter
SI_SFR16(ADC0, 0xBE);                    // ADC0 Data
SI_SFR16(ADC0GT, 0xC4);                  // ADC0 Greater Than Window
SI_SFR16(ADC0LT, 0xC6);                  // ADC0 Less Than Window
SI_SFR16(RCAP2, 0xCA);                   // Timer2 Capture/Reload
SI_SFR16(T2, 0xCC);                      // Timer2 Counter
SI_SFR16(TMR2RL, 0xCA);                  // Timer2 Capture/Reload
SI_SFR16(TMR2, 0xCC);                    // Timer2 Counter
SI_SFR16(RCAP4, 0xE4);                   // Timer4 Capture/Reload
SI_SFR16(T4, 0xF4);                      // Timer4 Counter
SI_SFR16(TMR4RL, 0xE4);                  // Timer4 Capture/Reload
SI_SFR16(TMR4, 0xF4);                    // Timer4 Counter
SI_SFR16(DAC0, 0xD2);                    // DAC0 Data
SI_SFR16(DAC1, 0xD5);                    // DAC1 Data

//-----------------------------------------------------------------------------
// Address Definitions for bit-addressable SFRs
//-----------------------------------------------------------------------------

#define SFR_P0       0x80
#define SFR_TCON     0x88
#define SFR_P1       0x90
#define SFR_SCON0    0x98
#define SFR_P2       0xA0
#define SFR_IE       0xA8
#define SFR_P3       0xB0
#define SFR_IP       0xB8
#define SFR_SMB0CN   0xC0
#define SFR_T2CON    0xC8
#define SFR_PSW      0xD0
#define SFR_PCA0CN   0xD8
#define SFR_ACC      0xE0
#define SFR_ADC0CN   0xE8
#define SFR_B        0xF0
#define SFR_SPI0CN   0xF8

//-----------------------------------------------------------------------------
// Bit Definitions
//-----------------------------------------------------------------------------

// TCON 0x88
SI_SBIT(TF1, SFR_TCON, 7);               // Timer 1 Overflow Flag
SI_SBIT(TR1, SFR_TCON, 6);               // Timer 1 On/Off Control
SI_SBIT(TF0, SFR_TCON, 5);               // Timer 0 Overflow Flag
SI_SBIT(TR0, SFR_TCON, 4);               // Timer 0 On/Off Control
SI_SBIT(IE1, SFR_TCON, 3);               // Ext. Interrupt 1 Edge Flag
SI_SBIT(IT1, SFR_TCON, 2);               // Ext. Interrupt 1 Type
SI_SBIT(IE0, SFR_TCON, 1);               // Ext. Interrupt 0 Edge Flag
SI_SBIT(IT0, SFR_TCON, 0);               // Ext. Interrupt 0 Type

// SCON0 0x98
SI_SBIT(SM00, SFR_SCON0, 7);             // Serial Mode Control Bit 0
SI_SBIT(SM10, SFR_SCON0, 6);             // Serial Mode Control Bit 1
SI_SBIT(SM20, SFR_SCON0, 5);             // Multiprocessor Communication Enable
SI_SBIT(REN0, SFR_SCON0, 4);             // Receive Enable
SI_SBIT(TB80, SFR_SCON0, 3);             // Transmit Bit 8
SI_SBIT(RB80, SFR_SCON0, 2);             // Receive Bit 8
SI_SBIT(TI0, SFR_SCON0, 1);              // Transmit Interrupt Flag
SI_SBIT(RI0, SFR_SCON0, 0);              // Receive Interrupt Flag

// IE 0xA8
SI_SBIT(EA, SFR_IE, 7);                  // Global Interrupt Enable
SI_SBIT(IEGF0, SFR_IE, 6);               // General Purpose Flag 0
SI_SBIT(ET2, SFR_IE, 5);                 // Timer 2 Interrupt Enable
SI_SBIT(ES0, SFR_IE, 4);                 // Uart0 Interrupt Enable
SI_SBIT(ET1, SFR_IE, 3);                 // Timer 1 Interrupt Enable
SI_SBIT(EX1, SFR_IE, 2);                 // External Interrupt 1 Enable
SI_SBIT(ET0, SFR_IE, 1);                 // Timer 0 Interrupt Enable
SI_SBIT(EX0, SFR_IE, 0);                 // External Interrupt 0 Enable

// IP 0xB8
                                       // Bit7 UNUSED
                                       // Bit6 UNUSED
SI_SBIT(PT2, SFR_IP, 5);                 // Timer 2 Priority
SI_SBIT(PS, SFR_IP, 4);                  // Serial Port Priority
SI_SBIT(PT1, SFR_IP, 3);                 // Timer 1 Priority
SI_SBIT(PX1, SFR_IP, 2);                 // External Interrupt 1 Priority
SI_SBIT(PT0, SFR_IP, 1);                 // Timer 0 Priority
SI_SBIT(PX0, SFR_IP, 0);                 // External Interrupt 0 Priority

// SMB0CN 0xC0
SI_SBIT(BUSY, SFR_SMB0CN, 7);            // SMBus 0 Busy
SI_SBIT(ENSMB, SFR_SMB0CN, 6);           // SMBus 0 Enable
SI_SBIT(STA, SFR_SMB0CN, 5);             // SMBus 0 Start Flag
SI_SBIT(STO, SFR_SMB0CN, 4);             // SMBus 0 Stop Flag
SI_SBIT(SI, SFR_SMB0CN, 3);              // SMBus 0 Interrupt Pending Flag
SI_SBIT(AA, SFR_SMB0CN, 2);              // SMBus 0 Assert/Acknowledge Flag
SI_SBIT(SMBFTE, SFR_SMB0CN, 1);          // SMBus 0 Free Timer Enable
SI_SBIT(SMBTOE, SFR_SMB0CN, 0);          // SMBus 0 Timeout Enable

// T2CON 0xC8
SI_SBIT(TF2, SFR_T2CON, 7);              // Timer 2 Overflow Flag
SI_SBIT(EXF2, SFR_T2CON, 6);             // External Flag
SI_SBIT(RCLK0, SFR_T2CON, 5);            // Uart0 Rx Clock Source
SI_SBIT(TCLK0, SFR_T2CON, 4);            // Uart0 Tx Clock Source
SI_SBIT(EXEN2, SFR_T2CON, 3);            // Timer 2 External Enable Flag
SI_SBIT(TR2, SFR_T2CON, 2);              // Timer 2 On/Off Control
SI_SBIT(CT2, SFR_T2CON, 1);              // Timer Or Counter Select
SI_SBIT(CPRL2, SFR_T2CON, 0);            // Capture Or Reload Select

//  PSW 0xD0
SI_SBIT(CY, SFR_PSW, 7);                 // Carry Flag
SI_SBIT(AC, SFR_PSW, 6);                 // Auxiliary Carry Flag
SI_SBIT(F0, SFR_PSW, 5);                 // User Flag 0
SI_SBIT(RS1, SFR_PSW, 4);                // Register Bank Select 1
SI_SBIT(RS0, SFR_PSW, 3);                // Register Bank Select 0
SI_SBIT(OV, SFR_PSW, 2);                 // Overflow Flag
SI_SBIT(F1, SFR_PSW, 1);                 // User Flag 1
SI_SBIT(P, SFR_PSW, 0);                  // Accumulator Parity Flag

// PCA0CN 0xD8
SI_SBIT(CF, SFR_PCA0CN, 7);              // PCA 0 Counter Overflow Flag
SI_SBIT(CR, SFR_PCA0CN, 6);              // PCA 0 Counter Run Control Bit
                                       // Bit5 UNUSED
SI_SBIT(CCF4, SFR_PCA0CN, 4);            // PCA 0 Module 4 Interrupt Flag
SI_SBIT(CCF3, SFR_PCA0CN, 3);            // PCA 0 Module 3 Interrupt Flag
SI_SBIT(CCF2, SFR_PCA0CN, 2);            // PCA 0 Module 2 Interrupt Flag
SI_SBIT(CCF1, SFR_PCA0CN, 1);            // PCA 0 Module 1 Interrupt Flag
SI_SBIT(CCF0, SFR_PCA0CN, 0);            // PCA 0 Module 0 Interrupt Flag

// ADC0CN 0xE8
SI_SBIT(AD0EN, SFR_ADC0CN, 7);           // ADC 0 Enable
SI_SBIT(AD0TM, SFR_ADC0CN, 6);           // ADC 0 Track Mode
SI_SBIT(AD0INT, SFR_ADC0CN, 5);          // ADC 0 Converision Complete Interrupt Flag
SI_SBIT(AD0BUSY, SFR_ADC0CN, 4);         // ADC 0 Busy Flag
SI_SBIT(AD0CM1, SFR_ADC0CN, 3);          // ADC 0 Start Of Conversion Mode Bit 1
SI_SBIT(AD0CM0, SFR_ADC0CN, 2);          // ADC 0 Start Of Conversion Mode Bit 0
SI_SBIT(AD0WINT, SFR_ADC0CN, 1);         // ADC 0 Window Compare Interrupt Flag
SI_SBIT(AD0LJST, SFR_ADC0CN, 0);         // ADC 0 Right Justify Data Bit

// SPI0CN 0xF8
SI_SBIT(SPIF, SFR_SPI0CN, 7);            // SPI 0 Interrupt Flag
SI_SBIT(WCOL, SFR_SPI0CN, 6);            // SPI 0 Write Collision Flag
SI_SBIT(MODF, SFR_SPI0CN, 5);            // SPI 0 Mode Fault Flag
SI_SBIT(RXOVRN, SFR_SPI0CN, 4);          // SPI 0 Rx Overrun Flag
SI_SBIT(TXBSY, SFR_SPI0CN, 3);           // SPI 0 Tx Busy Flag
SI_SBIT(SLVSEL, SFR_SPI0CN, 2);          // SPI 0 Slave Select
SI_SBIT(MSTEN, SFR_SPI0CN, 1);           // SPI 0 Master Enable
SI_SBIT(SPIEN, SFR_SPI0CN, 0);           // SPI 0 SPI Enable

//-----------------------------------------------------------------------------
// Interrupt Priorities
//-----------------------------------------------------------------------------

#define INTERRUPT_INT0           0     // External Interrupt 0
#define INTERRUPT_TIMER0         1     // Timer0 Overflow
#define INTERRUPT_INT1           2     // External Interrupt 1
#define INTERRUPT_TIMER1         3     // Timer1 Overflow
#define INTERRUPT_UART0          4     // Serial Port UART0
#define INTERRUPT_TIMER2         5     // Timer2 Overflow
#define INTERRUPT_SPI0           6     // SPI0 Interface
#define INTERRUPT_SMBUS0         7     // SMBus0 Interface
#define INTERRUPT_ADC0_WINDOW    8     // ADC0 Window Comparison
#define INTERRUPT_PCA0           9     // PCA0 Peripheral
#define INTERRUPT_COMPARATOR0F   10    // Comparator0 Falling Edge
#define INTERRUPT_COMPARATOR0R   11    // Comparator0 Rising Edge
#define INTERRUPT_COMPARATOR1F   12    // Comparator1 Falling Edge
#define INTERRUPT_COMPARATOR1R   13    // Comparator1 Rising Edge
#define INTERRUPT_TIMER3         14    // Timer3 Overflow
#define INTERRUPT_ADC0_EOC       15    // ADC0 End Of Conversion
#define INTERRUPT_TIMER4         16    // Timer4 Overflow
#define INTERRUPT_ADC1_EOC       17    // ADC1 End Of Conversion
#define INTERRUPT_INT6           18    // External Interrupt 6
#define INTERRUPT_INT7           19    // External Interrupt 7
#define INTERRUPT_UART1          20    // Serial Port UART1
#define INTERRUPT_XTAL_READY     21    // External Crystal Oscillator Ready

//-----------------------------------------------------------------------------
// SDCC PDATA External Memory Paging Support
//-----------------------------------------------------------------------------

#if defined SDCC

SI_SFR(_XPAGE, 0xAF); // Point to the EMI0CN register

#endif

//-----------------------------------------------------------------------------
// Header File PreProcessor Directive
//-----------------------------------------------------------------------------

#endif                                 // #define C8051F020_DEFS_H

//-----------------------------------------------------------------------------
// End Of File
//-----------------------------------------------------------------------------

debug.h

#ifndef DEBUG_H
#define DEBUG_H

extern unsigned char debug_line_pos;

extern void debug_pot_position(unsigned char x);
extern void debug_army_position(signed char x);
extern void debug_draw_vertical_line(void);

#endif

init.h

#ifndef INIT_H
#define INIT_H

extern long sum;
extern unsigned int avg;

extern void init(void);

#endif

interrupt.h

#ifndef INTERRUPTS_H
#define INTERRUPTS_H

//----------------------ADC Variables-------------------------
extern bit pot_flag;
extern unsigned int adc_value;
extern unsigned int count; //used to determine when to update the adc value

//------------------- Sound Variables ------------------------
extern unsigned long duration;               // number of cycles left to output
extern signed long envelope;
extern code unsigned char sine[];
extern unsigned char phase;  // current point in sine to output

//----------------------Timer0 Variables------------------------
extern unsigned int timer0;
extern bit timer0_flag;

#endif

invaders.h

#ifndef INVADERS_H
#define INVADERS_H
//------------------------ Define ----------------------------
#define MAX_LASER 10 //active lasers on the screen
#define INVADERS_PAGE_START 1
#define INVADERS_COL_START 15 //IMPORTANT: if you change this value you will need to adjust the invader_right_limit_array and invader_left_limit_array
#define MAX_INVADERS 16
#define LASER_ANIMATION_TIME_DELAY 2 //used to determine speed of laser
#define INVADER_ANIMATION_TIME_DELAY 3 //used to determine speed of invader animation
#define DEBOUNCE_BUTTON_TIME_DELAY 3// used for debounce stuff

typedef struct{
   unsigned char active;
   unsigned long col;
   unsigned long page;
} laser;

typedef struct{
   unsigned char active;
   int col;
   int page;
} invader_laser_list;

extern xdata laser lasers[MAX_LASER];
extern xdata invader_laser_list invader_lasers[MAX_LASER];
// Fart deluxe variables (aka the bounding edges of the army)
extern xdata unsigned long army_box_right;
extern xdata unsigned long   army_box_left;
extern int invaders_alive;
extern int army_page_offset;
extern int army_col_offset;
extern int invader_left_dead;
extern int invader_right_dead;
extern int set_tank_pos;
extern int player_lives;
extern int player_score;

//master array that holds the state of each invader
extern xdata unsigned char invader_array[16];
#endif

Source Code

debug.c

#include <C8051F020.h>
#include <init.h>
#include <lcd.h>
#include <interrupts.h>

unsigned char debug_line_pos;

/** ----------- Debug Write Char ------------
*  This funciton is used to write a character to the screen.
*/
void debug_write_char(unsigned char page, unsigned char col, char ch)
{
   int i = page * 128 + col;
   int j = (ch - ' ') * 5;
   unsigned char k;

   for(k=0; k<5;++k)
   {
      screen[i + k] = font5x8[j + k];    // OR operator paints in character rather than deleting pixels and refilling. Allows for smoother transistions?
   }
}

/*   ---------- Debug Pot Position ----------
*    This function is used to debug the potentiometer. It will
*    take the value of the pot and display it on the LCD.
*/
void debug_pot_position(unsigned char x)
{
   debug_write_char(0,0, (x/100)%10 + 0x30);
   debug_write_char(0, 6, (x/10)%10 + 0x30);
   debug_write_char(0, 12, x%10 + 0x30);
}

/*  ---------- Debug Army Position ----------
*    This function is used to debug the army position. It will
*    take the value of the army and display it on the LCD.
*/
void debug_army_position(signed char x)
{
   //if x is negative, display a negative sign
   if(x < 0)
   {
      debug_write_char(0,20, '-');
      x = -x; //convert negative x to positive for correct display
   }
   debug_write_char(0,26, (x/100)%10 + 0x30);
   debug_write_char(0,32, (x/10)%10 + 0x30);
   debug_write_char(0,38, x%10 + 0x30);
}

/* ---------- Debug Draw Vertical Line ----------
* Used for determining the actual column position of the screen
*/
void debug_draw_vertical_line(void)
{
   unsigned char i;
   if(pot_flag ==1)
   {
      pot_flag = 0; //reset flag
      debug_line_pos = ((avg * 128L) >> 12); //convert avg to be between 00-128
      debug_pot_position(debug_line_pos); //debug for pot
      for(i=0; i<8; ++i)
      {
            screen[128 * i + debug_line_pos] = 0xFF; //draw vertical line
      }

   }

}

init.c

#include <C8051F020.h>
#include <lcd.h>
#include <init.h>

long sum;
unsigned int avg;

void init(void)
{
//---------------- Initialization Code -------------------
   WDTCN = 0xde;   // disable watchdog
   WDTCN = 0xad;
   XBR2 = 0x40;    // enable port output

   OSCXCN = 0x67;            // turn on external crystal
   TMOD = 0x21;            // wait 1ms using T1 mode 2
   TH1 = -167;                // 2MHz clock, 167 counts - 1ms
   TR1 = 1;
   while(TF1 == 0){ }          // wait 1ms
   while(!(OSCXCN & 0x80)){ }  // wait till oscillator stable
   OSCICN = 8;                    // switch over to 22.1184MHz

   //--------------------- Registers ------------------------
   REF0CN = 0x03; // enable ADC
   ADC0CN = 0x8C; // ADC0 Control Register
   ADC0CF = 0x40; // ADC0 Configuration Register gain 1
   AMX0SL = 0x06; // AMUX0 Channel Select Register
   IE = 0x82;   // interupt enable
   EIE2 = 0x06; // Enable timer4 and ADC
   EIP2 = 0x04; // Highest Priority for timer4

   //---------------------- Timer 0 -------------------------
   /*
      Description of timer0 here. See function in invaders.c
   */
   TL0 = -18432 >> 8; //get high byte
   TH0 = -18432; //get low byte
   //turn on timer0
   TR0 = 1;

   //---------------------- Timer 2 -------------------------
   /*
      Used for the ADC
   */
   T2CON = 0x04;   // timer 2
   RCAP2H = -1844 >> 8; //get high byte
   RCAP2L = -1844; //get low byte

   //-------------- Initialization for ADC ------------------
   sum = 0;
   avg = 0;

   //---------------------- Timer 4 -------------------------
   /*
      Used for the DAC
   */
   DAC0CN = 0x94; //used for the DAC set to timer4 overflow left most
   T4CON = 0x04;
   RCAP4H = 0;
   RCAP4L = 0;

   CKCON = 0x60; // Remove divide by 12 for timer4 and timer2

   init_lcd(); // initialize the LCD screen
}

interrupts.c

#include <C8051F020.h>
#include <init.h>

#define TIMER0_VALUE 1

//----------------------ADC Variables-------------------------
bit pot_flag;
unsigned int adc_value;
unsigned int count; //used to determine when to update the adc value

//------------------- Sound Variables ------------------------
unsigned long duration = 0;          // number of cycles left to output
signed long envelope = 512;
code unsigned char sine[] = { 176, 217, 244, 254, 244, 217, 176, 128, 80, 39, 12, 2, 12, 39, 80, 128 };
unsigned char phase = sizeof(sine)-1;        // current point in sine to output

//----------------------Timer0 Variables------------------------
unsigned int timer0 = TIMER0_VALUE;
bit timer0_flag;

/*   ---------- DAC Interrupt Serivce Routine ----------
   This function is used for the DAC which is used to play the
   sounds.
*/
void interrupt_dac(void) interrupt 16
{
   T4CON &= 0x7F; //clear the flag
   DAC0H = ((sine[phase] - 128) * envelope >> 10) + 128;
   if(phase<sizeof(sine)-1){phase++;}
   else if (duration>0){
      phase = 0;
      duration--;
      if(envelope>0){envelope--;}
      if(duration == 0){RCAP4H = RCAP4L = 0;} //reset timer4 H and L to zero
   }
}

/*   ---------- ADC Interrupt Serivce Routine ----------
   This function is used for the ADC. If the ADC0 interrupt flag
   is one (1) the program will be directed here. This service routine
   will happen roughly every 1 ms (If the clock is divide by 12).
   An average is taken to avoid any noisey signal coming from the
   pot on the dev board. This code we are taking 64 samples. Then
   dividing by 64 by shifting right six (6).
*/
void interrupt_adc(void)interrupt 15
{
   AD0INT = 0; //clear ADC0 interrupt flag
   adc_value = (ADC0H << 8) | ADC0L; //OR the two High and Low bits together
   sum += adc_value; //continually sum the pot
   count++; //add to count

   if(count >= 64)
   {
      avg = 0; //clear average
      avg = (sum >> 6);
      count = 0; //reset count
      sum = 0; //reset sum
      pot_flag = 1; //set pot flag}
   }
}

/* ----------- Timer 0 Interrupt Service Routine --------
   Refer to page 117/272 in the data sheet for where the #1 comes from for timer0.
   timer0 overflows about every 10ms

   TODO: figure out how fast the timer 0 is running with an oscilloscope. Add
   information to READTHEDOCS
*/
void interrupt_timer0(void)interrupt 1
{
   TL0 = -18432 >> 8; //get high byte
   TH0 = -18432; //get low byte

   //if the timer is not zero, decrement it
   if(timer0 != 0)
   {
      timer0--;
   }
   else
   {
      timer0 = TIMER0_VALUE;
      timer0_flag = 1;
   }
}

invaders.c

#include <C8051F020.h>
#include <lcd.h>
#include <notes.h>
#include <init.h>
#include <interrupts.h>
#include <debug.h>
#include "invaders.h"
#include "utils.h"

//-------------------- BEGIN VARIABLES -----------------------

//------------------- Global Game Variables ------------------
int invaders_alive = 16;

int army_page_offset = 0;
int army_col_offset = 13;

int player_score = 0;

int set_tank_pos;

int player_lives = 3;
//------------------- Button Variables -----------------------
sbit fire = P3^6;
sbit start = P3^7;
bit last_start = 0;
bit last_fire = 0;

//------------------- Laser Variables ------------------------
unsigned char laser_time = LASER_ANIMATION_TIME_DELAY; //used to determine speed of laser
unsigned int current_laser_pos = 0;

xdata unsigned char invader_array[16] = {1,1,1,1,1,1,1,1,
                                 1,1,1,1,1,1,1,1};
xdata laser lasers[MAX_LASER];
xdata invader_laser_list invader_lasers[MAX_LASER];

// bounding edges of the army
xdata unsigned long army_box_right = 100;
xdata unsigned long army_box_left = 20;

//--------------------- Invader Array ------------------------
int army_dir_toggle = 0;
int army_width = MAX_INVADERS/2;
int invader_left_dead;
int invader_right_dead;
int not_dead = 1;

unsigned char invader_animation_time = INVADER_ANIMATION_TIME_DELAY; //used to determine speed of invader animatio

//------------------- Debounce Button ------------------------
unsigned char debounce_time = DEBOUNCE_BUTTON_TIME_DELAY;


//-------------------- END VARIABLES -------------------------

//----------------------- Functions --------------------------

/*
* Funciton: Write a character to the screen
* ----------------------------
* Given a passed in character, write it to the screen
* at the specified page and column
* Arguments:
*   unsigned char: page
*   unsigned char: col
*   char ch: character to write
*/
void write_char(unsigned char page, unsigned char col, char ch)
{
   int i = page * 128 + col;
   int j = (ch - ' ') * 5;
   unsigned char k;

   for(k=0; k<5;++k)
   {
      screen[i + k] = font5x8[j + k];    // OR operator paints in character rather than deleting pixels and refilling. Allows for smoother transistions?
   }
}

/*
* Funciton: Write a string to the screen
* ----------------------------
* Given a passed in string, write it to the screen
* at the specified page and column
* Arguments:
*   unsigned char: page
*   unsigned char: col
*   *char array: String to write
*/
void write_string(unsigned char page, unsigned char col, char *temp)
{
   while(*temp){
      write_char(page, col, *temp);
      temp++;
      col+=6;
   }
}

/*
* Funciton: Write a specific byte to the screen
* ----------------------------
* Given a passed byte, write it to the screen
* at the specified page and column
* Arguments:
*   unsigned char: page
*   unsigned char: col
*   unsigned char: val
*/
void write_byte(unsigned char page, unsigned char col, unsigned char val)
{
   if(col < 0 || col > 127){
      return;
   }
   screen[128 * page + col] = val;
}

/*
* Funciton: Draw Sprite
* ----------------------------
* Given the desired sprite texture, (1 for up, 0 for down),
* write it to the screen.
* at the specified page and column
* Arguments:
*   unsigned char: page
*   unsigned char: col
*   unsigned char: figure (desired sprite texture)
*/
void draw_sprite(unsigned char page, unsigned char col, unsigned char figure)
{
   static unsigned int code sprite_texture_tb[] = {
      0x70, 0x18, 0x7D, 0xB6, 0x3C, 0x3C, 0xB6, 0x7D, 0x18, 0x70, //first sprite
      0x0E, 0x98, 0x7D, 0x36, 0x3C, 0x3C, 0x36, 0x7D, 0x98, 0x0E};//second sprite
   unsigned char frame = figure * 10; //if figure is 0 then frame = 0, if figure is 1 then frame = 10

   unsigned char i = 0;
   for(i=0; i<10; i++)
   {
      write_byte(page, col+i, sprite_texture_tb[frame+i]);
   }
}

/*
* Funciton: Add Cannon Laser
* ----------------------------
* When the player shoots a laser, add it to
* the laser tracking array where the cannon is at
* when the laser is shot.
* Arguments:
*   unsigned char: col
* Global Variables:
*    laser lasers: array of lasers structs
*/
void add_laser(int col)
{
   int ii;
   for(ii = 0; ii < MAX_LASER; ii++){
      if(lasers[ii].active == 0){
         lasers[ii].active = 1;
         lasers[ii].page = 7;
         lasers[ii].col = col;
      }
   }
}

/*
* Funciton: Update Cannon Lasers
* ----------------------------
* Iterates over the cannon laser array and updates
* the laser position on the screen by decrementing
* the laser page by 1.
* Global Variables:
*    laser lasers: array of lasers structs
*/
void update_lasers()
{
   int ii;
   for(ii = 0; ii < MAX_LASER; ii++){
      if(lasers[ii].active == 1){
         screen[(lasers[ii].page * 128) + lasers[ii].col] = 0x00;
         lasers[ii].page--;
         screen[(lasers[ii].page * 128) + lasers[ii].col] = 0xFF;
         if(lasers[ii].page < 1){
            lasers[ii].active = 0;
         }
      }
   }
}

/*
* Funciton: Draw Invader Army
* ----------------------------
* Given the offset for page and column, draw the invader army
* using the invader array to determine which invaders are alive.
* Arguments:
*   unsigned char: page
*   unsigned char: col
*   unsigned char: figure (desired sprite texture)
* Global Variables:
*    inavder array: array of invaders
*/
void draw_army(unsigned char page, unsigned char col, unsigned char figure)
{
   unsigned char i;
   unsigned char j;
   for(i = 0; i < 2; i++){
      for(j = 0; j < 8; j++){
         if(invader_array[i*8+j] == 1)//invader_array is a 16 element array
         {
            draw_sprite(page+i, col+j*13, figure);
         }
         else
         {
            continue; //if invader value is 0 then skip it
         }
      }
   }
}

/*
* Funciton: Draw Cannon
* ----------------------------
* Draw the cannon at the position mapped to
* the potentiameter position
* Arguments:
*   unsigned char: x - Potentiometer position
*   unsigned char: size - Width of cannon
* Global Variables:
*    inavder array: array of invaders
* Functions Called:
*    write_byte
*/
void write_cannon(int x, unsigned char size)
{
   int i = 0;
   write_byte(7, x, 0xFF);
   write_byte(7,x+1, 0xFE);
   write_byte(7,x-1, 0xFE);
   write_byte(7,x+2, 0xFC);
   write_byte(7,x-2, 0xFC);

   for(i = 0; i < size; i++){
      write_byte(7,x+3+i, 0xF8);
      write_byte(7,x-3-i, 0xF8);
   }
   for(i = size; i < 20; i++){
      write_byte(7,x+4+i, 0x00);
      write_byte(7,x-4-i, 0x00);
   }
}

/*
* Funciton: Play Note
* ----------------------------
* Play a note for a given duration
* Arguments:
*   int note:  note to play
*   int dur:  duration of note
*/
void play_note(int note, int dur)
{
   RCAP4H = -note >> 8;
   RCAP4L = -note;
   duration = (dur*1382L)/note;
   envelope = 512;
}

//---------------------- END FUNCTIONS -----------------------

//----------------------- BEGIN MAIN -------------------------
void main()
{
   // Initilization for variables used in the main loop
   unsigned char ii;
   int margin;
   int padding_left;
   int padding_right;
   invader_left_dead = 0;
   invader_right_dead = 0;
   lasers[0].active = 0;
   lasers[1].active = 0;
   lasers[2].active = 0;
   invader_lasers[0].active = 0;
   invader_lasers[1].active = 0;
   invader_lasers[2].active = 0;

   //--------------------Initialize Code---------------------
   //use init.c to configure the initilization
   //refer to init.c & init.h for more information
   init();

   // Set all
   for(ii = 0; ii < MAX_LASER; ii++){
      lasers[ii].page = 0;
      lasers[ii].col = 0;
      lasers[ii].active = 0;
   }

   //--------------------- START SCREEN ---------------------
   while(start == 1)
   {
      write_string(1,46,"READY?");
      write_string(6,22,"PRESS START TO");
      write_string(7,49,"BEGIN");
      draw_army(3,13,0);
      refresh_screen();
      //debug_draw_vertical_line(); //draws vertical line TODO: fix so only single line
   }
   blank_screen(); //clear screen after start

   //--------------------- MAIN LOOP ---------------------
   while(not_dead)
   {
      /**`
      * The follow if loops are for checking interrupts on timers
      * Flags:
      *              pot_flag: 1 if potentiometer has changed
      *              timer0_flag: 1 if timer0 has reached 0
      *              laser_time: 1 if laser_time has reached 0
      *              invader_animation_time: 1 if invader_animation_time has reached 0
      *
      */
      if(pot_flag == 1) //If pot_flag == 1 update the position of the cannon
      {
         pot_flag = 0; //reset flag
         set_tank_pos = ((avg * 128L) >> 12); //convert avg to be between 00-128
         write_cannon(set_tank_pos, 3); //write cannon
         // debug_pot_position(set_tank_pos); //debug for pot
         refresh_screen();
      }
      if(timer0_flag == 1)
      {
         timer0_flag = 0;//reset flag from interrupts.c file
         laser_time--;
         invader_animation_time--;
         debounce_time--;
      }
      if(laser_time == 0)
      {
         laser_time = LASER_ANIMATION_TIME_DELAY; //reset laser_time
         update_lasers();
         collision_detect();
      }
      if(invader_animation_time == 0)
      {
         invader_animation_time = INVADER_ANIMATION_TIME_DELAY; //reset animation time
         blank_screen();
         update_lasers();
         draw_army(army_page_offset, army_col_offset, 0);

         army_width = invaders_alive / 2;



         if(invader_array[invader_left_dead] == 0){
            if(invader_array[invader_left_dead + 8] == 0){
               invader_left_dead++;
            }
         }
         if(invader_array[7 - invader_right_dead] == 0){
            if(invader_array[7 - invader_right_dead + 8] == 0){
               invader_right_dead++;
            }
         }

         /**
         * The following code is for the movement of the army
         * and their direction
         */
         margin = 1;
         padding_right = (invader_right_dead * 13) + 13;
         padding_left = (invader_left_dead * 13) + 13;
         if(army_dir_toggle == 0){
            army_col_offset++;
            if(army_col_offset > padding_right + (13-margin)){
               army_page_offset++;
               army_dir_toggle = !army_dir_toggle;
            }
         }else{
            army_col_offset--;
            if(army_col_offset < margin - padding_left + 13){
               army_page_offset++;
               army_dir_toggle = !army_dir_toggle;
            }
         }

         collision_detect();
         debug_army_position((char)player_score);
         debug_pot_position((char)player_lives);

         update_invader_lasers();

         refresh_screen();
         P1^=1; //debug led light for animation
      }
      if(fire == 0){
         add_laser(set_tank_pos);
         //play_note(A4, 10);
         invader_laser();
      }
      if(player_lives <= 0){
         not_dead = 0;
      }
   }
   while(!not_dead){
      blank_screen();
      write_string(6,22,"DEAD");
      refresh_screen();
   }
   //--------------------- END MAIN LOOP ---------------------
}
//------------------------ END MAIN -------------------------

lcd.asm

public init_lcd, refresh_screen, blank_screen, screen, font5x8;
?PR?lcd segment code
   rseg ?PR?lcd

$include (c8051f020.inc)
LCD_CMD   equ 8000h ; Set this to the address of the command register
LCD_DAT   equ 8100h ; Set this to the address of the data register
LCD_RESET equ 10h ; Mask that selects the reset line on P4 (e.g. for P4.4 use 10H)

;
; subroutines wcom and w_com_a
;   Writes a byte to the LCD command register after checking the busy flag first
;   Assumes the external memory interface is configured for split mode with bank
;   select.
; inputs:
;   wcom:   r0  = byte to write to command register
;   wcom_a: acc = byte to write to command register
; outputs: none
; destroys:
;   wcom:   EMI0CN
;   wcom_a: EMI0CN, r0
wcom_a:      mov     r0,a            ; save acc in R0 while we check BUSY
wcom:        mov     EMI0CN,#HIGH LCD_CMD ; command/status register
wcom1:       movx    a,@r0           ; r0 has no relevance here
   jb        acc.7,wcom1     ; wait for not BUSY
   mov       a,r0            ; get the actual data to write
   movx      @r0,a           ; write the command, r0 is irrelevant here
   ret
;
; subroutines wdat and w_dat_a and w_datc
;   Writes a byte to the LCD data register after checking the busy flag first
;   Assumes the external memory interface is configured for split mode with bank
;   select.
; inputs:
;   wdat:   r0  = byte to write to data register
;   wdat_a: acc = byte to write to data register
;   wdat_c: acc = dptr-relative index (dptr[acc]) of byte to write to data register
; outputs: none
; destroys:
;   wdat:   EMI0CN
;   wdat_a: EMI0CN, r0
;   wdat_c: EMI0CN, r0
wdat_c:      movc    a,@a+dptr       ; lookup byte to write (handy for fonts)
wdat_a:      mov     r0,a            ; save it in R0 while we check BUSY
wdat:        mov     EMI0CN,#HIGH LCD_CMD ; command/status register
wdat1:       movx    a,@r0           ; r0 has no relevance here
   jb        acc.7,wdat1     ; wait for not BUSY
   mov       EMI0CN,#HIGH LCD_DAT    ; data register
   mov       a,r0            ; actual data to write
   movx      @r0,a           ; write the data, r0 is irrelevant here
   ret
;
; Initialize controller for S64128N LCD module
;   inputs: none
;   outputs: none
;   destroys: r0, r2, r3, dptr
;
init_lcd:
   mov       p4,#not LCD_RESET
   mov       emi0cf,#28h     ; B5: P4-7, B4: multiplexed, B3-2: split bank
   mov       emi0tc,#4Dh     ; pulse width 4 sysclock cycles
   mov       p74out,#0FFH    ; push-pull
   orl p4,#LCD_RESET   ; assert then deassert reset

   mov       R0,#02FH        ; Boost on, voltage Reg and follower on
   call      wcom
   mov       R0,#0A2H;       ; 1/9bias selected
   call      wcom
   mov       R0,#0A1H        ; reverse segment driver output seg131-seg0
   call      wcom
   mov       R0,#0C0H        ; common output mode com0 to com63
   call      wcom
   mov       R0,#024H        ; Ra/Rb ratio
   call      wcom
   mov       R0,#081H        ; electronic vloume mode set
   call      wcom
   mov       R0,#026H        ; contrast
   call      wcom
   mov       R0,#040H        ; display line address = 0
   call      wcom
   mov       R0,#0A6H        ; normal video
   call      wcom
   mov       R0,#0AFH        ; display on
   call      wcom
   call      blank_screen    ; fall through to refresh_screen
;
; subroutine refresh_screen
;   Copies 1k bytes of data from external address 0 to the LCD. Bytes 0-7F go
;   into page 0, bytes 80-FF go to page 1 etc.
; inputs: none
; outputs: none
; destroys: r0, r2, r3, dptr, EMI0CN
;
refresh_screen:
   mov       dptr,#0         ; start of 1k block of memory
   mov       r2,#0B0H        ; command to set page number to 0
page_loop:
   mov       a,r2            ; set page number n, n = 0, 1, 2...7
   call      wcom_a
   mov       a,#04H          ; set column number to 4. If LCD is not
   call      wcom_a          ; inverted, you will want to set column
   mov       a,#10H          ; number to 0.
   call      wcom_a
   mov       r3,#128         ; copy 128 bytes
byte_loop:
   movx      a,@dptr         ; get byte from memory
   call      wdat_a          ; and write it to the LCD
   inc       dptr
   djnz      r3,byte_loop
   inc       r2              ; advance to next page, but bail if it is 8 (B8)
   cjne      r2,#0B8H,page_loop
   ret

blank_screen:
   mov       dptr,#0
   mov       a,#00h

blank_loop:
   movx @dptr,a
   inc       dptr
   mov       b,dph
   jnb       b.2,blank_loop
   ret

font5x8:
db  000H, 000H, 000H, 000H, 000H ;
db  000H, 006H, 05FH, 006H, 000H ; !
db  007H, 003H, 000H, 007H, 003H ; "
db  024H, 07EH, 024H, 07EH, 024H ; #
db  024H, 02BH, 06AH, 012H, 000H ; $
db  063H, 013H, 008H, 064H, 063H ; %
db  036H, 049H, 056H, 020H, 050H ; &
db  000H, 007H, 003H, 000H, 000H ; '
db  000H, 03EH, 041H, 000H, 000H ; (
db  000H, 041H, 03EH, 000H, 000H ; )
db  008H, 03EH, 01CH, 03EH, 008H ; *
db  008H, 008H, 03EH, 008H, 008H ; +
db  000H, 0E0H, 060H, 000H, 000H ; ,
db  008H, 008H, 008H, 008H, 008H ; -
db  000H, 060H, 060H, 000H, 000H ; .
db  020H, 010H, 008H, 004H, 002H ; /
db  03EH, 051H, 049H, 045H, 03EH ; 0
db  000H, 042H, 07FH, 040H, 000H ; 1
db  062H, 051H, 049H, 049H, 046H ; 2
db  022H, 049H, 049H, 049H, 036H ; 3
db  018H, 014H, 012H, 07FH, 010H ; 4
db  02FH, 049H, 049H, 049H, 031H ; 5
db  03CH, 04AH, 049H, 049H, 030H ; 6
db  001H, 071H, 009H, 005H, 003H ; 7
db  036H, 049H, 049H, 049H, 036H ; 8
db  006H, 049H, 049H, 029H, 01EH ; 9
db  000H, 06CH, 06CH, 000H, 000H ; :
db  000H, 0ECH, 06CH, 000H, 000H ; ;
db  008H, 014H, 022H, 041H, 000H ; <
db  024H, 024H, 024H, 024H, 024H ; =
db  000H, 041H, 022H, 014H, 008H ; >
db  002H, 001H, 059H, 009H, 006H ; ?
db  03EH, 041H, 05DH, 055H, 01EH ; @
db  07EH, 011H, 011H, 011H, 07EH ; A
db  07FH, 049H, 049H, 049H, 036H ; B
db  03EH, 041H, 041H, 041H, 022H ; C
db  07FH, 041H, 041H, 041H, 03EH ; D
db  07FH, 049H, 049H, 049H, 041H ; E
db  07FH, 009H, 009H, 009H, 001H ; F
db  03EH, 041H, 049H, 049H, 07AH ; G
db  07FH, 008H, 008H, 008H, 07FH ; H
db  000H, 041H, 07FH, 041H, 000H ; I
db  030H, 040H, 040H, 040H, 03FH ; J
db  07FH, 008H, 014H, 022H, 041H ; K
db  07FH, 040H, 040H, 040H, 040H ; L
db  07FH, 002H, 004H, 002H, 07FH ; M
db  07FH, 002H, 004H, 008H, 07FH ; N
db  03EH, 041H, 041H, 041H, 03EH ; O
db  07FH, 009H, 009H, 009H, 006H ; P
db  03EH, 041H, 051H, 021H, 05EH ; Q
db  07FH, 009H, 009H, 019H, 066H ; R
db  026H, 049H, 049H, 049H, 032H ; S
db  001H, 001H, 07FH, 001H, 001H ; T
db  03FH, 040H, 040H, 040H, 03FH ; U
db  01FH, 020H, 040H, 020H, 01FH ; V
db  03FH, 040H, 03CH, 040H, 03FH ; W
db  063H, 014H, 008H, 014H, 063H ; X
db  007H, 008H, 070H, 008H, 007H ; Y
db  071H, 049H, 045H, 043H, 000H ; Z
db  000H, 07FH, 041H, 041H, 000H ; [
db  002H, 004H, 008H, 010H, 020H ; \
db  000H, 041H, 041H, 07FH, 000H ; ]
db  004H, 002H, 001H, 002H, 004H ; ^
db  080H, 080H, 080H, 080H, 080H ; _
db  000H, 003H, 007H, 000H, 000H ; `
db  020H, 054H, 054H, 054H, 078H ; a
db  07FH, 044H, 044H, 044H, 038H ; b
db  038H, 044H, 044H, 044H, 028H ; c
db  038H, 044H, 044H, 044H, 07FH ; d
db  038H, 054H, 054H, 054H, 008H ; e
db  008H, 07EH, 009H, 009H, 000H ; f
db  018H, 0A4H, 0A4H, 0A4H, 07CH ; g
db  07FH, 004H, 004H, 078H, 000H ; h
db  000H, 000H, 07DH, 040H, 000H ; i
db  040H, 080H, 084H, 07DH, 000H ; j
db  07FH, 010H, 028H, 044H, 000H ; k
db  000H, 000H, 07FH, 040H, 000H ; l
db  07CH, 004H, 018H, 004H, 078H ; m
db  07CH, 004H, 004H, 078H, 000H ; n
db  038H, 044H, 044H, 044H, 038H ; o
db  0FCH, 044H, 044H, 044H, 038H ; p
db  038H, 044H, 044H, 044H, 0FCH ; q
db  044H, 078H, 044H, 004H, 008H ; r
db  008H, 054H, 054H, 054H, 020H ; s
db  004H, 03EH, 044H, 024H, 000H ; t
db  03CH, 040H, 020H, 07CH, 000H ; u
db  01CH, 020H, 040H, 020H, 01CH ; v
db  03CH, 060H, 030H, 060H, 03CH ; w
db  06CH, 010H, 010H, 06CH, 000H ; x
db  09CH, 0A0H, 060H, 03CH, 000H ; y
db  064H, 054H, 054H, 04CH, 000H ; z
db  008H, 03EH, 041H, 041H, 000H ; {
db  000H, 000H, 077H, 000H, 000H ; |
db  000H, 041H, 041H, 03EH, 008H ; }
db  002H, 001H, 002H, 001H, 000H ; ~
db  006H, 009H, 009H, 006H, 000H ; ?

   xseg
screen:      ds 1024

   end

utils.c

#include <C8051F020.h>
#include <init.h>
#include <lcd.h>
#include <interrupts.h>
#include "invaders.h"

int invader_laser_delay = 8; //8 is the default speed
int baddy_laeser_counter = 0; //counter for the delay

/*
* Function: Detect a collision between a laser and the tank
* ----------------------------
* Globals:
*   unsigned int: left boundry of army block
*   unsigned int: right boundry of army block
*   unsigned char array: army block
*   laser lasers: array of lasers
*/
void collision_tank_detect(){
   int ii;
   int col_temp;
   int page_temp;

   for(ii = 0; ii < MAX_LASER; ii++){
      col_temp = invader_lasers[ii].col;
      page_temp = invader_lasers[ii].page;

      if(page_temp > 6){
         if((col_temp < set_tank_pos + 5) && (col_temp > set_tank_pos - 5) && invader_lasers[ii].active == 1){
            player_lives--;
            death_note_flag = 1;
            invader_lasers[ii].active = 0;
            break;
         }
      }
   }
}


/*
* Function: Detect collision
* between a laser and a sprite
* ----------------------------
* Globals:
*   unsigned int: left boundry of army block
*   unsigned int: right boundry of army block
*   unsigned char array: army block
*   laser lasers: array of lasers
*/
void collision_detect(){
int SPRITE_RATIO = 5042;
int ii;
int col_override;
unsigned long diff;
unsigned long TEMP;
     TEMP = 100;
     collision_tank_detect();
 for(ii = 0; ii < MAX_LASER; ii++){
     if(lasers[ii].active){
         if(lasers[ii].page <= 0){
             lasers[ii].active = 0;
         }
         else if(lasers[ii].page <= army_page_offset+1){
                             if (army_col_offset < 0){
                                     col_override = 1;
                             }else{
                                     col_override = 0;
                             }
             if(((lasers[ii].col > army_col_offset) || col_override == 1) && lasers[ii].col < army_col_offset + (13 * (MAX_INVADERS/2))){
                 //if(lasers[ii].page == lowest_active_sprite){ // The laser is at the same page as an active sprite. Next check for col.
                     unsigned long col_temp = lasers[ii].col;
                                             diff = (army_col_offset + (13 * (MAX_INVADERS/2)) - col_temp);
                     diff = diff * SPRITE_RATIO;
                     diff = MAX_INVADERS - (diff >> 16) - 1;
                     if(invader_array[diff] >= 1){
                         invader_array[diff] = 0;
                         lasers[ii].active = 0;
                                                     player_score = player_score + 50;
                                                     invaders_alive--;
                                                     invader_death_flag = 1; //set flag for sound

                     }
             }

             if(lasers[ii].col > army_col_offset && lasers[ii].col < army_col_offset + (13 * (MAX_INVADERS/2)) && lasers[ii].active == 1){
                     //if(lasers[ii].page == lowest_active_sprite){ // The laser is at the same page as an active sprite. Next check for col.
                         unsigned long col_temp = lasers[ii].col;
                         diff = (army_col_offset + (13 * (MAX_INVADERS/2)) - col_temp);
                         diff = diff * SPRITE_RATIO;
                         diff = MAX_INVADERS/2 - (diff >> 16) - 1;
                         if(invader_array[diff] >= 1){
                             invader_array[diff] = 0;
                             lasers[ii].active = 0;
                                                             player_score = player_score + 50;
                                                             invaders_alive--;
                                                             invader_death_flag = 1; //set flag for sound

                         }

               }
            }
      }
   }
}

/*
* Function: add_invdaer_laser
* ----------------------------
* Adds a laser to the invader laser array
* given the page and col
* Globals:
*   laser lasers: array of lasers
*/
void add_invader_laser(int page, int col)
{
   int ii;

   if(baddy_laeser_counter == invader_laser_delay){
      baddy_laeser_counter = 0;
      for(ii = 0; ii < MAX_LASER; ii++){
         if(invader_lasers[ii].active == 0){
            invader_lasers[ii].active = 1;
            invader_lasers[ii].page = page;
            invader_lasers[ii].col = col;
            invaders_laser_flag = 1; //set bit for fire sound from invaders
            break;
         }

      }
   }else{
      baddy_laeser_counter++;
   }

}

/*
* Function: Update invader lasers position
* ----------------------------
* Updates the position of the invader lasers by incrementing
* the page by 1 and leaving the col the same
* Globals:
*   laser lasers: array of lasers
*/
void update_invader_lasers()
{
   int ii;
   for(ii = 0; ii < MAX_LASER; ii++){
      if(invader_lasers[ii].active == 1){
         screen[(invader_lasers[ii].page * 128) + invader_lasers[ii].col] = 0x00;
         invader_lasers[ii].page++;
         screen[(invader_lasers[ii].page * 128) + invader_lasers[ii].col] = 0xFF;
         if(invader_lasers[ii].page > 7){
            invader_lasers[ii].active = 0;
         }
      }
   }
}

/*
* Function: A function for the code to call to
* run and functions associated with the invaders
* lasers
* ----------------------------
* Updates the position of the invader lasers by incrementing
* the page by 1 and leaving the col the same
* Globals:
*   laser lasers: array of lasers
*   army_page_offset: the page offset of the army
*   army_col_offset: the col offset of the army
*/
void invader_laser(){
   int new_laser_col = (int)army_col_offset + (invader_left_dead * 13) + ((random_pos)*13);
   add_invader_laser((int)army_page_offset+1, new_laser_col);

}