// Air Hockey 3D // Henry Yip (cy68), Zhengpeng Chen (zc35) //video gen //D.5 is sync:1000 ohm + diode to 75 ohm resistor //D.6 is video:330 ohm + diode to 75 ohm resistor #pragma regalloc- //I allocate the registers myself #pragma optsize- //optimize for speed #include #include #include #include #include //cycles = 63.625 * 16 Note NTSC is 63.55 //but this line duration makes each frame exactly 1/60 sec //which is nice for keeping a realtime clock #define lineTime 1018 #define begin { #define end } #define ScreenTop 30 #define ScreenBot 130 // Macros for fixed point manipulation #define int2fix(a) (((int)(a))<<8) //Convert char to fix #define fix2int(a) ((signed char)((a)>>8)) //Convert fix to char #define float2fix(a) ((int)((a)*256.0)) //Convert float to fix #define fix2float(a) ((float)(a)/256.0) //Convert fix to float #define multfix(a,b) ((int)((((long)(a))*((long)(b)))>>8)) //multiply two fixed # #define divfix(a,b) ((int)((((long)(a))<<8)/((long)(b)))) //divide two fixed # #define absolute(a) ((a)>0?(a):((~a)+1)) #define invert(a) ((~a) + 1) //Multiply a by -1 #define MaxScore 7 #define MaxGames 3 //NOTE that v1 to v8 and i must be in registers! register char v1 @4; register char v2 @5; register char v3 @6; register char v4 @7; register char v5 @8; register char v6 @9; register char v7 @10; register char v8 @11; register int i @12; #pragma regalloc+ char syncON, syncOFF; int LineCount; char sec; char mins; char j; char holdt, holdsec, holdlimit; //for hold state char screen[800], t, ts[10]; char screenB[800]; char LineOdd; // =1 if line is Odd, =0 if even char FRAMEseq; // =1 for TV-A, =0 for TV-B char cu1[]="P1-"; char cu2[]="P2-"; //************************************************************ //Paddle positions from Hall Sensor #define POS1 0b01111111 #define POS2 0b00111111 #define POS3 0b10111111 #define POS4 0b10011111 #define POS5 0b11011111 #define POS6 0b11001111 #define POS7 0b11101111 #define POS8 0b11100111 #define POS9 0b11110111 #define POS10 0b11110011 #define POS11 0b11111011 #define POS12 0b11111001 #define POS13 0b11111101 #define POS14 0b11111100 #define POS15 0b11111110 unsigned char P1POS; // for storing player 1 Hall sensor output unsigned char paddle1Lastx; // for storing last location of player 1 paddle unsigned char P2POS; // for storing player 2 Hall sensor output unsigned char paddle2Lastx; // for storing last location of player 2 paddle unsigned char paddle1x, paddle2x; // x-positions of each player's paddle unsigned char paddle1far, paddle2far; // x-position of opponent's paddle unsigned char paddle1farL, paddle2farL; unsigned char p1x,p2x, p1f, p2f; char videoPuck1x, videoPuck1y, videoPuck2x, videoPuck2y; // pixel positions of puck int tempx, tempy, tempMinx; //animation //int drag; //drag on balls int Paddle1V, Paddle2V; //paddle velocity, x-direction unsigned int Ballx, Bally; // x and y coordinates of ball int Ballvx, Ballvy; // x and y velocity components of ball int BallAge; unsigned char score1, score2; // player scores unsigned char Games1, Games2; // number of sets won // moving average paddle velocity unsigned char index; //index into moving average arrays #define MaxIndex 9 //maximum index int velocities1[MaxIndex], velocities2[MaxIndex]; // moving average arrays for both paddles int Paddle1VSum, Paddle2VSum, Paddle1VAvg, Paddle2VAvg; // total and average velocities for each paddle char k; char p1flag; // player who scores //State machine state names #define Init 1 #define OnePlayer 2 #define TwoPlayer 3 #define Hold 4 #define Victory 5 unsigned char MachineState; //state machine unsigned char NextState; //next state for state machine char OneP[] = "1XPLAYER"; // In-game text displays char TwoP[] = "2XPLAYER"; // In-game text displays char title[] = "AIRXHOCKEY"; // Game title char victory[] = "VICTORY"; // victory char spaces10[] = "XXXXXXXXXX"; //spaces to clear text char spaces8[] = "XXXXXXXX"; //spaces to clear text char p1goal[] = "P1XGOAL"; char p2goal[] = "P2XGOAL"; char P1win[] = "ZXP1XWINXZ"; char P2win[] = "ZXP2XWINXZ"; char spaces4[] = "XXXX"; //************************************************************ //Point plot lookup table //One bit masks flash char pos[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01}; //================================ //3x5 font numbers, then letters //packed two per definition for fast //copy to the screen at x-position divisible by 4 flash char smallbitmap[39][5]={ //0 0b11101110, 0b10101010, 0b10101010, 0b10101010, 0b11101110, //1 0b01000100, 0b11001100, 0b01000100, 0b01000100, 0b11101110, //2 0b11101110, 0b00100010, 0b11101110, 0b10001000, 0b11101110, //3 0b11101110, 0b00100010, 0b11101110, 0b00100010, 0b11101110, //4 0b10101010, 0b10101010, 0b11101110, 0b00100010, 0b00100010, //5 0b11101110, 0b10001000, 0b11101110, 0b00100010, 0b11101110, //6 0b11001100, 0b10001000, 0b11101110, 0b10101010, 0b11101110, //7 0b11101110, 0b00100010, 0b01000100, 0b10001000, 0b10001000, //8 0b11101110, 0b10101010, 0b11101110, 0b10101010, 0b11101110, //9 0b11101110, 0b10101010, 0b11101110, 0b00100010, 0b01100110, //: (10) 0b00000000, 0b01000100, 0b00000000, 0b01000100, 0b00000000, //- (11) 0b00000000, 0b00000000, 0b11101110, 0b00000000, 0b00000000, //blank 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, //A 0b11101110, 0b10101010, 0b11101110, 0b10101010, 0b10101010, //B 0b11001100, 0b10101010, 0b11101110, 0b10101010, 0b11001100, //C 0b11101110, 0b10001000, 0b10001000, 0b10001000, 0b11101110, //D 0b11001100, 0b10101010, 0b10101010, 0b10101010, 0b11001100, //E 0b11101110, 0b10001000, 0b11101110, 0b10001000, 0b11101110, //F 0b11101110, 0b10001000, 0b11101110, 0b10001000, 0b10001000, //G 0b11101110, 0b10001000, 0b10001000, 0b10101010, 0b11101110, //H 0b10101010, 0b10101010, 0b11101110, 0b10101010, 0b10101010, //I 0b11101110, 0b01000100, 0b01000100, 0b01000100, 0b11101110, //J 0b00100010, 0b00100010, 0b00100010, 0b10101010, 0b11101110, //K 0b10001000, 0b10101010, 0b11001100, 0b11001100, 0b10101010, //L 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b11101110, //M 0b10101010, 0b11101110, 0b11101110, 0b10101010, 0b10101010, //N 0b00000000, 0b11001100, 0b10101010, 0b10101010, 0b10101010, //O 0b01000100, 0b10101010, 0b10101010, 0b10101010, 0b01000100, //P 0b11101110, 0b10101010, 0b11101110, 0b10001000, 0b10001000, //Q 0b01000100, 0b10101010, 0b10101010, 0b11101110, 0b01100110, //R 0b11101110, 0b10101010, 0b11001100, 0b11101110, 0b10101010, //S 0b11101110, 0b10001000, 0b11101110, 0b00100010, 0b11101110, //T 0b11101110, 0b01000100, 0b01000100, 0b01000100, 0b01000100, //U 0b10101010, 0b10101010, 0b10101010, 0b10101010, 0b11101110, //V 0b10101010, 0b10101010, 0b10101010, 0b10101010, 0b01000100, //W 0b10101010, 0b10101010, 0b11101110, 0b11101110, 0b10101010, //X 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, //Y 0b10101010, 0b10101010, 0b01000100, 0b01000100, 0b01000100, //Z 0b11101110, 0b11101110, 0b11101110, 0b01000100, 0b11101110 }; //================================== //This is the sync generator and raster generator. It MUST be entered from //sleep mode to get accurate timing of the sync pulses #pragma warn- interrupt [TIM1_COMPA] void t1_cmpA(void) { //start the Horizontal sync pulse PORTD = syncON; //update the curent scanline number LineCount ++ ; LineOdd = (~LineOdd) & 0x01; //invert last bit of LineOdd //begin inverted (Vertical) synch after line 247 if (LineCount==248) begin syncON = 0b00100001; syncOFF = 0; end //back to regular sync after line 250 if (LineCount==251) begin syncON = 0; syncOFF = 0b00100001; end //start new frame after line 262 if (LineCount==263) begin LineCount = 1; FRAMEseq = (~FRAMEseq) & 0x01; // toggle FRAMEseq end delay_us(2); //adjust to make 5 us pulses //end sync pulse PORTD = syncOFF; if (LineCount=ScreenTop) { //compute byte index for beginning of the next line //left-shift 4 would be individual lines // <<3 means line-double the pixels //The 0xfff8 truncates the odd line bit //i=(LineCount-ScreenTop)<<3 & 0xfff8; // #asm push r16 lds r12, _LineCount lds r13, _Linecount+1 ldi r16, 30 sub r12, r16 ldi r16,0 sbc r13, r16 lsl r12 rol r13 lsl r12 rol r13 lsl r12 rol r13 mov r16,r12 andi r16,0xf0 mov r12,r16 pop r16 #endasm if(LineOdd == FRAMEseq){ // Print to TV A if line is odd and FRAMEseq is 1 // of if line is even and FRAMEseq is 0 //load 16 registers with screen info #asm push r14 push r15 push r16 push r17 push r18 push r19 push r26 push r27 ldi r26,low(_screen) ;base address of screen ldi r27,high(_screen) add r26,r12 ;offset into screen (add i) adc r27,r13 ld r4,x+ ;load 16 registers and inc pointer ld r5,x+ ld r6,x+ ld r7,x+ ld r8,x+ ld r9,x+ ld r10,x+ ld r11,x+ ld r12,x+ ld r13,x+ ld r14,x+ ld r15,x+ ld r16,x+ ld r17,x+ ld r18,x+ ld r19,x pop r27 pop r26 #endasm delay_us(4); //adjust to center image on screen //blast 16 bytes to the screen #asm ;but first a macro to make the code shorter ;the macro takes a register number as a parameter ;and dumps its bits serially to portD.6 ;the nop can be eliminated to make the display narrower .macro videobits ;regnum BST @0,7 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,6 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,5 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,4 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,3 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,2 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,1 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,0 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 .endm videobits r4 ;video line -- byte 1 videobits r5 ;byte 2 videobits r6 ;byte 3 videobits r7 ;byte 4 videobits r8 ;byte 5 videobits r9 ;byte 6 videobits r10 ;byte 7 videobits r11 ;byte 8 videobits r12 ;byte 9 videobits r13 ;byte 10 videobits r14 ;byte 11 videobits r15 ;byte 12 videobits r16 ;byte 13 videobits r17 ;byte 14 videobits r18 ;byte 15 videobits r19 ;byte 16 clt ;clear video after the last pixel on the line IN R30,0x12 BLD R30,6 OUT 0x12,R30 pop r19 pop r18 pop r17 pop r16 pop r15 pop r14 #endasm }else{ //load 16 registers with screen info #asm push r14 push r15 push r16 push r17 push r18 push r19 push r26 push r27 ldi r26,low(_screenB) ;base address of screenB ldi r27,high(_screenB) add r26,r12 ;offset into screen (add i) adc r27,r13 ld r4,x+ ;load 16 registers and inc pointer ld r5,x+ ld r6,x+ ld r7,x+ ld r8,x+ ld r9,x+ ld r10,x+ ld r11,x+ ld r12,x+ ld r13,x+ ld r14,x+ ld r15,x+ ld r16,x+ ld r17,x+ ld r18,x+ ld r19,x pop r27 pop r26 #endasm delay_us(4); //adjust to center image on screen //blast 16 bytes to the screen #asm ;but first a macro to make the code shorter ;the macro takes a register number as a parameter ;and dumps its bits serially to portD.1 ;the nop can be eliminated to make the display narrower .macro videobits2 ;regnum BST @0,7 IN R30,0x12 BLD R30,1 nop OUT 0x12,R30 BST @0,6 IN R30,0x12 BLD R30,1 nop OUT 0x12,R30 BST @0,5 IN R30,0x12 BLD R30,1 nop OUT 0x12,R30 BST @0,4 IN R30,0x12 BLD R30,1 nop OUT 0x12,R30 BST @0,3 IN R30,0x12 BLD R30,1 nop OUT 0x12,R30 BST @0,2 IN R30,0x12 BLD R30,1 nop OUT 0x12,R30 BST @0,1 IN R30,0x12 BLD R30,1 nop OUT 0x12,R30 BST @0,0 IN R30,0x12 BLD R30,1 nop OUT 0x12,R30 .endm videobits2 r4 ;video line -- byte 1 videobits2 r5 ;byte 2 videobits2 r6 ;byte 3 videobits2 r7 ;byte 4 videobits2 r8 ;byte 5 videobits2 r9 ;byte 6 videobits2 r10 ;byte 7 videobits2 r11 ;byte 8 videobits2 r12 ;byte 9 videobits2 r13 ;byte 10 videobits2 r14 ;byte 11 videobits2 r15 ;byte 12 videobits2 r16 ;byte 13 videobits2 r17 ;byte 14 videobits2 r18 ;byte 15 videobits2 r19 ;byte 16 clt ;clear video after the last pixel on the line IN R30,0x12 BLD R30,1 OUT 0x12,R30 pop r19 pop r18 pop r17 pop r16 pop r15 pop r14 #endasm } } } #pragma warn+ //================================== //plot one point to Screen A //at x,y with color 1=white 0=black 2=invert #pragma warn- void video_pt(char x, char y, char c) begin #asm ; i=(x>>3) + ((int)y<<4) ; the byte with the pixel in it push r16 ldd r30,y+2 ;get x lsr r30 lsr r30 lsr r30 ;divide x by 8 ldd r12,y+1 ;get y lsl r12 ;mult y by 16 clr r13 lsl r12 rol r13 lsl r12 rol r13 lsl r12 rol r13 add r12, r30 ;add in x/8 ;v2 = screen[i]; r5 ;v3 = pos[x & 7]; r6 ;v4 = c r7 ldi r30,low(_screen) ldi r31,high(_screen) add r30, r12 adc r31, r13 ld r5,Z ;get screen byte ldd r26,y+2 ;get x ldi r27,0 andi r26,0x07 ;form x & 7 ldi r30,low(_pos*2) ldi r31,high(_pos*2) add r30,r26 adc r31,r27 lpm r6,Z ld r16,y ;get c ;if (v4==1) screen[i] = v2 | v3 ; ;if (v4==0) screen[i] = v2 & ~v3; ;if (v4==2) screen[i] = v2 ^ v3 ; cpi r16,1 brne tst0 or r5,r6 tst0: cpi r16,0 brne tst2 com r6 and r5,r6 tst2: cpi r16,2 brne writescrn eor r5,r6 writescrn: ldi r30,low(_screen) ldi r31,high(_screen) add r30, r12 adc r31, r13 st Z, r5 ;write the byte back to the screen pop r16 #endasm end #pragma warn+ //================================== //plot one point to Screen B //at x,y with color 1=white 0=black 2=invert #pragma warn- void video_pt2(char x, char y, char c) begin #asm ; i=(x>>3) + ((int)y<<4) ; the byte with the pixel in it push r16 ldd r30,y+2 ;get x lsr r30 lsr r30 lsr r30 ;divide x by 8 ldd r12,y+1 ;get y lsl r12 ;mult y by 16 clr r13 lsl r12 rol r13 lsl r12 rol r13 lsl r12 rol r13 add r12, r30 ;add in x/8 ;v2 = screen[i]; r5 ;v3 = pos[x & 7]; r6 ;v4 = c r7 ldi r30,low(_screenB) ldi r31,high(_screenB) add r30, r12 adc r31, r13 ld r5,Z ;get screen byte ldd r26,y+2 ;get x ldi r27,0 andi r26,0x07 ;form x & 7 ldi r30,low(_pos*2) ldi r31,high(_pos*2) add r30,r26 adc r31,r27 lpm r6,Z ld r16,y ;get c ;if (v4==1) screen[i] = v2 | v3 ; ;if (v4==0) screen[i] = v2 & ~v3; ;if (v4==2) screen[i] = v2 ^ v3 ; cpi r16,1 brne tst1 or r5,r6 tst1: cpi r16,0 brne tst3 com r6 and r5,r6 tst3: cpi r16,2 brne writescrn2 eor r5,r6 writescrn2: ldi r30,low(_screenB) ldi r31,high(_screenB) add r30, r12 adc r31, r13 st Z, r5 ;write the byte back to the screen pop r16 #endasm end #pragma warn+ //================================== //plot a line //at x1,y1 to x2,y2 with color 1=white 0=black 2=invert // screen 0 = TV1, screen 1 = TV2 //NOTE: this function requires signed chars //Code is from David Rodgers, //"Procedural Elements of Computer Graphics",1985 void video_line(char x1, char y1, char x2, char y2, char c) begin int e; signed char dx,dy,j, temp; signed char s1,s2, xchange; signed char x,y; x = x1; y = y1; dx = cabs(x2-x1); dy = cabs(y2-y1); s1 = csign(x2-x1); s2 = csign(y2-y1); xchange = 0; if (dy>dx) begin temp = dx; dx = dy; dy = temp; xchange = 1; end e = ((int)dy<<1) - dx; for (j=0; j<=dx; j++) begin video_pt(x,y,c) ; video_pt2(x,y,c); if (e>=0) begin if (xchange==1) x = x + s1; else y = y + s2; e = e - ((int)dx<<1); end if (xchange==1) y = y + s2; else x = x + s1; e = e + ((int)dy<<1); end end //================================== // put a small character on Screen A // x-cood must be on divisible by 4 // c is index into bitmap void video_smallchar(char x, char y, char c) begin char mask; i=((int)x>>3) + ((int)y<<4) ; if (x == (x & 0xf8)) mask = 0x0f; //f8 else mask = 0xf0; screen[i] = (screen[i] & mask) | (smallbitmap[c][0] & ~mask); screen[i+16] = (screen[i+16] & mask) | (smallbitmap[c][1] & ~mask); screen[i+32] = (screen[i+32] & mask) | (smallbitmap[c][2] & ~mask); screen[i+48] = (screen[i+48] & mask) | (smallbitmap[c][3] & ~mask); screen[i+64] = (screen[i+64] & mask) | (smallbitmap[c][4] & ~mask); end //================================== // put a small character on the Screen B // x-cood must be on divisible by 4 // c is index into bitmap void video_smallchar2(char x, char y, char c) begin char mask; i=((int)x>>3) + ((int)y<<4) ; if (x == (x & 0xf8)) mask = 0x0f; //f8 else mask = 0xf0; screenB[i] = (screenB[i] & mask) | (smallbitmap[c][0] & ~mask); screenB[i+16] = (screenB[i+16] & mask) | (smallbitmap[c][1] & ~mask); screenB[i+32] = (screenB[i+32] & mask) | (smallbitmap[c][2] & ~mask); screenB[i+48] = (screenB[i+48] & mask) | (smallbitmap[c][3] & ~mask); screenB[i+64] = (screenB[i+64] & mask) | (smallbitmap[c][4] & ~mask); end //================================== // put a string of small characters on screen A // x-cood must be on divisible by 4 void video_putsmalls(char x, char y, char *str) begin char i ; for (i=0; str[i]!=0; i++) begin if (str[i]>=0x30 && str[i]<=0x3a) video_smallchar(x,y,str[i]-0x30); else if(str[i]==0x2D) video_smallchar(x,y,0x0B); else video_smallchar(x,y,str[i]-0x40+12); x = x+4; end end //================================== // put a string of small characters on screen B // x-cood must be on divisible by 4 void video_putsmalls2(char x, char y, char *str) begin char i ; for (i=0; str[i]!=0; i++) begin if (str[i]>=0x30 && str[i]<=0x3a) video_smallchar2(x,y,str[i]-0x30); else if(str[i]==0x2D) video_smallchar2(x,y,0x0B); else video_smallchar2(x,y,str[i]-0x40+12); x = x+4; end end //================================== //plot a horiztonal line on Screen A (left to right) //at x1,y1 to x2 (x2 > x1) with color 1=white 0=black 2=invert void video_lineH(char x1, char y1, char x2, char c) begin for (j=x1; j<=x2; j++){ video_pt(j,y1,c); } end //================================== //plot a horiztonal line on Screen B(left to right) //at x1,y1 to x2 (x2 > x1) with color 1=white 0=black 2=invert void video_lineH2(char x1, char y1, char x2, char c) begin for (j=x1; j<=x2; j++){ video_pt2(j,y1,c); } end //================================== //plot a vertical line on Screen A(top to bottom) //at x1,y1 to y2 (y2 > y1) with color 1=white 0=black 2=invert void video_lineV(char x1, char y1, char y2, char c) begin for (j=y1; j<=y2; j++){ video_pt(x1,j,c); } end //================================== //plot a vertical line on Screen B(top to bottom) //at x1,y1 to y2 (y2 > y1) with color 1=white 0=black 2=invert void video_lineV2(char x1, char y1, char y2, char c) begin for (j=y1; j<=y2; j++){ video_pt2(x1,j,c); } end //================================== //draw Big Paddle to TV1 // at x1, with color 1=white, 0-black void drawBig(char x1, char c) begin video_lineH(x1-14, 46, x1+13, c); //bottom line video_lineH(x1-14, 37, x1+13, c); //top line video_lineV(x1-14, 38, 46, c); //left line video_lineV(x1+13, 38, 46, c); //right line end //================================== //draw Big Paddle to TV2 // at x1, with color 1=white, 0-black void drawBig2(char x1, char c) begin video_lineH2(x1-14, 46, x1+13, c); //bottom line video_lineH2(x1-14, 37, x1+13, c); //top line video_lineV2(x1-14, 38, 46, c); //left line video_lineV2(x1+13, 38, 46, c); //right line end //================================== //draw Small Paddle to TV1 // at x1, with color 1=white, 0-black void drawSmall(char x1, char c) begin video_lineH(x1-6, 12, x1+7, c); //bottom line video_lineH(x1-6, 9, x1+7, c); //top line video_lineV(x1-6, 10, 11, c); //left line video_lineV(x1+7, 10, 11, c); //right line end //================================== //draw Small Paddle to TV2 // at x1, with color 1=white, 0-black void drawSmall2(char x1, char c) begin video_lineH2(x1-6, 12, x1+7, c); //bottom line video_lineH2(x1-6, 9, x1+7, c); //top line video_lineV2(x1-6, 10, 11, c); //left line video_lineV2(x1+7, 10, 11, c); //right line end //================================== //draw big puck to TV1 // at x1, y1 with color 1=white, 0-black void drawBigPuck(char x1, char y1, char c) begin video_lineH(x1-3, y1+1, x1+2, c); //bottom line video_lineH(x1-3, y1-2, x1+2, c); //top line video_pt(x1-4,y1,c); //left line video_pt(x1-4,y1-1,c); video_pt(x1+3,y1,c); //right line video_pt(x1+3,y1-1,c); end //================================== //draw big puck to TV2 // at x1, y1 with color 1=white, 0-black void drawBigPuck2(char x1, char y1, char c) begin video_lineH2(x1-3, y1+1, x1+2, c); //bottom line video_lineH2(x1-3, y1-2, x1+2, c); //top line video_pt2(x1-4,y1,c); //left line video_pt2(x1-4,y1-1,c); video_pt2(x1+3,y1,c); //right line video_pt2(x1+3,y1-1,c); end //================================== //draw small puck to TV1 // at x1, y1 with color 1=white, 0-black void drawSmallPuck(char x1, char y1, char c) begin video_pt(x1-1,y1-1,c); //top video_pt(x1,y1-1,c); video_pt(x1+1,y1-1,c); video_pt(x1-1,y1+1,c); // bottom video_pt(x1,y1+1,c); video_pt(x1+1,y1+1,c); video_pt(x1-2, y1,c); //left video_pt(x1+2,y1,c); end //================================== //draw small puck to TV2 // at x1, y1 with color 1=white, 0-black void drawSmallPuck2(char x1, char y1, char c) begin video_pt2(x1-1,y1-1,c); //top video_pt2(x1,y1-1,c); video_pt2(x1+1,y1-1,c); video_pt2(x1-1,y1+1,c); // bottom video_pt2(x1,y1+1,c); video_pt2(x1+1,y1+1,c); video_pt2(x1-2, y1,c); //left video_pt2(x1+2,y1,c); end //================================== // set up the ports and timers void main(void) { //init timer 1 to generate sync OCR1A = lineTime; //One NTSC line TCCR1B = 9; //full speed; clear-on-match TCCR1A = 0x00; //turn off pwm and oc lines TIMSK = 0x10; //enable interrupt T1 cmp //init ports DDRD = 0xff; //video out and switches //D.5 is sync:1000 ohm + diode to 75 ohm resistor (TV A) //D.6 is video:330 ohm + diode to 75 ohm resistor (TV A) //D.0 is sync:1000 ohm + diode to 75 ohm resistor (TV B) //D.1 is video:330 ohm + diode to 75 ohm resistor (TV B) DDRA = 0x00; // Port A = Player 1 input DDRC = 0x00; // Port C = Player 2 input PIND.7 = 1; //initialize synch constants LineCount = 1; LineOdd=1; FRAMEseq = 1; syncON = 0b00000000; syncOFF = 0b00100000; //init animation (initial positions) Ballx = 0x4000; // x = 64 Bally = 0x6e00; //y = 110 Ballvx = 0x003f; Ballvy = 0xff10; BallAge = 1; //drag = 0x0001; paddle1x = 64; paddle2x = 64; paddle1far = 63; paddle2far = 63; paddle1Lastx = paddle1x; paddle2Lastx = paddle2x; videoPuck1x = 63; videoPuck1y = 21; videoPuck2x = 63; videoPuck2y = 21; MachineState = Init; tempMinx = 50; // draw 2 slanting lines video_line(1, 46, 31, 13, 1); video_line(96,13, 126, 46, 1); //*********** Print to TV A*********** //vertical side lines #define width 126 video_lineV(0,0,49,1); video_lineV(width,0,49,1); //horizontal lines video_lineH(0,0, width,1); //frame top video_lineH(0,49, width,1); // frame bottom video_lineH(0,47, width, 1); // table bottom video_lineH(32,13,95 , 1); //table top video_lineH(23,23,104,1); // center line //SCORE HEADER video_putsmalls(4,4,cu1); //"P1-" video_putsmalls(108,4,cu2); //"P2-" sprintf(ts,"%d:%02d",mins,sec); //M:SS format video_putsmalls(56,2,ts); // ******** Print to TV B *********** //vertical side lines video_lineV2(0,0,49,1); video_lineV2(width,0,49,1); //horizontal lines video_lineH2(0,0, width,1); //frame top video_lineH2(0,49, width,1); // frame bottom video_lineH2(0,47, width, 1); // table bottom video_lineH2(32,13,95 , 1); //table top video_lineH2(23,23,104,1); // center line //SCORE HEADER video_putsmalls2(4,4,cu2); //"P2-" video_putsmalls2(108,4,cu1); //"P1-" video_putsmalls2(56,2,ts); //init software timer t=0; sec=0; mins=0; //init hold state timer holdt = 0; holdsec = 0; holdlimit = 0; //enable sleep mode MCUCR = 0b10000000; #asm ("sei"); //The following loop executes once/video line during lines //1-230, then does all of the frame-end processing while(1) { //stall here until next line starts //sleep enable; mode=idle //use sleep to make entry into sync ISR uniform time #asm ("sleep"); //The following code executes during the vertical blanking //Code here can be as long as //a total of 60 lines x 63.5 uSec/line x 8 cycles/uSec if (LineCount==131) { //store previous paddle positions before updating them paddle1Lastx = paddle1x; paddle2Lastx = paddle2x; paddle1farL = paddle1far; paddle2farL = paddle2far; P1POS = PINA; //get current player 1 Hall sensor output if (P1POS != 0b11111111){ // signal detected from Hall sensors switch (P1POS) { case POS1: paddle1x = 15; paddle1far = 88; break; case POS2: paddle1x = 22; paddle1far = 84; break; case POS3: paddle1x = 29; paddle1far = 81; break; case POS4: paddle1x = 36; paddle1far = 77; break; case POS5: paddle1x = 43; paddle1far = 74; break; case POS6: paddle1x = 50; paddle1far = 70; break; case POS7: paddle1x = 57; paddle1far = 67; break; case POS8: paddle1x = 64; paddle1far = 63; break; case POS9: paddle1x = 71; paddle1far = 60; break; case POS10: paddle1x = 78; paddle1far = 56; break; case POS11: paddle1x = 85; paddle1far = 53; break; case POS12: paddle1x = 92; paddle1far = 49; break; case POS13: paddle1x = 99; paddle1far = 46; break; case POS14: paddle1x = 106; paddle1far = 42; break; case POS15: paddle1x = 112; paddle1far = 39; break; } } P2POS = PINC; //get current player 2 Hall sensor output if (P2POS != 0b11111111) { // signal detected from Hall sensors switch (P2POS) { case POS1: paddle2x = 15; paddle2far = 88; break; case POS2: paddle2x = 22; paddle2far = 84; break; case POS3: paddle2x = 29; paddle2far = 81; break; case POS4: paddle2x = 36; paddle2far = 77; break; case POS5: paddle2x = 43; paddle2far = 74; break; case POS6: paddle2x = 50; paddle2far = 70; break; case POS7: paddle2x = 57; paddle2far = 67; break; case POS8: paddle2x = 64; paddle2far = 63; break; case POS9: paddle2x = 71; paddle2far = 60; break; case POS10: paddle2x = 78; paddle2far = 56; break; case POS11: paddle2x = 85; paddle2far = 53; break; case POS12: paddle2x = 92; paddle2far = 49; break; case POS13: paddle2x = 99; paddle2far = 46; break; case POS14: paddle2x = 106; paddle2far = 42; break; case POS15: paddle2x = 112; paddle2far = 39; break; } } //State Machine switch (MachineState) { case Init: //Erase Old Paddle //TV1 drawBig(paddle1Lastx,0); //Draw New Paddle //TV1 drawBig(paddle1x,1); Games1=0; Games2=0; score1=0; score2=0; t=0; sec=0; mins=0; //display title and game play modes video_putsmalls(44,27,title); video_putsmalls(4,30,OneP); video_putsmalls(92, 30, TwoP); //clear trophies video_putsmalls(4,11,spaces4); video_putsmalls(108,11,spaces4); video_putsmalls2(4,11,spaces4); video_putsmalls2(108,11,spaces4); // reset games scores if (P1POS == POS1) { MachineState=OnePlayer; video_putsmalls(44,27,spaces10); video_putsmalls(4,30,spaces8); video_putsmalls(92, 30, spaces8); } else if (P1POS == POS15) { MachineState = TwoPlayer; video_putsmalls(44,27,spaces10); video_putsmalls(4,30,spaces8); video_putsmalls(92, 30, spaces8); } else MachineState=Init; break; case OnePlayer: // Game enters one-player mode video_lineH(32,13,95 , 1); //table top video_lineH(23,23,104,1); // center line video_lineH2(0,47, width, 1); // table bottom video_lineH2(32,13,95 , 1); //table top video_lineH2(23,23,104,1); // center line // draw 2 slanting lines video_line(1, 46, 31, 13, 1); video_line(96,13, 126, 46, 1); //update the system clock every seconds if (++t>59) { t=0; sec = sec + 1; if (sec>59){ sec=0; mins = mins + 1; } sprintf(ts,"%d:%02d",mins,sec); //M:SS format video_putsmalls(56,2,ts); video_putsmalls2(56,2,ts); } //Erase Old Puck // TV1 if(videoPuck1y < 24){ drawSmallPuck(videoPuck1x, videoPuck1y,0); }else{ drawBigPuck(videoPuck1x, videoPuck1y,0); } // TV2 //if(videoPuck2y < 24){ //drawSmallPuck2(videoPuck2x, videoPuck2y,0); //}else{ //drawBigPuck2(videoPuck2x, videoPuck2y,0); //} //Erase Old Paddle //TV1 drawBig(paddle1Lastx,0); drawSmall(paddle2farL,0); //TV2 //drawBig2(paddle2Lastx,0); //drawSmall2(paddle1farL,0); // Detect Paddle position //******************************* // CHANGED BY MICHAEL //initialize ball after scoring if (BallAge == 0) { //initialize new ball Ballx = 0x4000; // x = 64 Bally = 0x6e00; //y = 110 Ballvx = 0x0100; //0xfe00; BallAge = 1; /* if (Ballvy > 0) { //player 2 scored against, so new ball comes from player 2 Ballvy = 0xff0f; } */ //else { //Ballvy < 0 //player 1 scored against Ballvy = 0x0100; //} } // Update array to find moving average paddle veocities // moving average has number of elements specified by MaxIndex Paddle1V = int2fix(paddle1x) - int2fix(paddle1Lastx); //Paddle 1 velocity, x-direction //Paddle2V = int2fix(127 - paddle2x) - int2fix(127 - paddle2Lastx); //Paddle 2 velocity, x-direction //update array for paddle 1 velocities1[index] = Paddle1V; //update array for paddle 2 //velocities2[index] = Paddle2V; index = index + 1; if (index > MaxIndex) { index = 0; // reset index when all array elements have been updated } //**** Ball dynamics // Paddle-ball/ball-wall interactions //compute new Vj //Ballvx = Ballvx - multfix(Ballvx, drag); //Ballvy = Ballvy - multfix(Ballvy, drag); //compute new Rj Ballx = Ballx + Ballvx; Bally = Bally + Ballvy; //ball hits left side of area //0x08 = 0d08 if (Ballx < 0x0800) { //bounce off left side Ballvx = invert(Ballvx); Ballx=0x0800; } //ball hits right side of area //0x77 = 0d119 if (Ballx > 0x7700) {Ballvx = invert(Ballvx); Ballx=0x7700;} //ball hits top of area (player 2) //0xda = 0d218 //0xd5 = 0d213 if (Bally > 0xd500) { //if (Ballx > int2fix(((127 - paddle2x) - 14)) && Ballx < int2fix(((127 - paddle2x) + 14))) { // player 2 bounces ball back to player 1 Ballvy = invert(Ballvy); /* for (k = 0; k <= MaxIndex; k++) { Paddle2VSum = Paddle2VSum + velocities2[k]; } Paddle2VAvg = Paddle2VSum / (MaxIndex + 1); // find average paddle velocity Paddle2V = int2fix(Paddle2VAvg); //Paddle 2 velocity, x-direction Ballvx = Ballvx + (Paddle2V >> 6); */ Bally = 0xd500; //Paddle2VSum = 0; // reset paddle velocity sum //} /* else { // player 2 misses ball // player 1 score increases score1 = score1 + 1; //ball disappears BallAge=0; } */ } //ball hits bottom of area (player 1) // 0x16 = 0d22 // 0x1b = 0d27 if (Bally < 0x1b00 ) { if (Ballx > int2fix((paddle1x - 14)) && Ballx < int2fix((paddle1x + 14))) { // player 1 bounces ball back to player 2 Ballvy = invert(Ballvy); for (k = 0; k <= MaxIndex; k++) { Paddle1VSum = Paddle1VSum + velocities1[k]; } Paddle1VAvg = Paddle1VSum / (MaxIndex + 1); // find average paddle velocity Paddle1V = int2fix(Paddle1VAvg); //Paddle 2 velocity, x-direction ////Paddle1V = int2fix(paddle1x) - int2fix(paddle1Lastx); //Paddle 1 velocity, x-direction Ballvx = Ballvx + (Paddle1V >> 6); //if (Ballvx > Bally = 0x1b00; Paddle1VSum = 0; // reset paddle velocity sum } else { // player 1 misses ball // player 2 score increases score2 = score2 + 1; //ball disappears BallAge=0; } } // Projection: // int videoPuck1x, videoPuck1y, videoPuck2x, videoPuck2y; // Ballx, Bally // Calculate Projection on TV1 tempx = Ballx >> 8; // removes all decimal points tempy = Bally >> 8; videoPuck1x = (char)((tempx - 63)*200/(tempy+180) + 63); videoPuck1y = (char) (13600/(tempy+180) - 21); //videoPuck2x = (char)((64 - tempx)*200/(420-tempy) + 63); //videoPuck2y = (char) (13600/(420-tempy) -21); //Draw New Puck // TV1 if(videoPuck1y < 24){ drawSmallPuck(videoPuck1x, videoPuck1y,1); }else{ drawBigPuck(videoPuck1x, videoPuck1y,1); } // TV2 /* if(videoPuck2y < 24){ drawSmallPuck2(videoPuck2x, videoPuck2y,1); }else{ drawBigPuck2(videoPuck2x, videoPuck2y,1); } */ //Draw New Paddle //TV1 drawBig(paddle1x,1); drawSmall(paddle2far,1); //TV2 //drawBig2(paddle2x,1); //drawSmall2(paddle1far,1); // **** SCORE ****** sprintf(ts,"%1d",score1); video_putsmalls(16,4,ts); video_putsmalls2(120,4,ts); sprintf(ts,"%1d",score2); video_putsmalls2(16,4,ts); video_putsmalls(120,4,ts); break; //*************************************************************************** // Two Player case TwoPlayer: //if (either player wins a set) MachineState=Hold; //else MachineState=TwoPlayers; video_lineH(0,47, width, 1); // table bottom video_lineH(32,13,95 , 1); //table top video_lineH(23,23,104,1); // center line video_lineH2(0,47, width, 1); // table bottom video_lineH2(32,13,95 , 1); //table top video_lineH2(23,23,104,1); // center line // draw 2 slanting lines video_line(1, 46, 31, 13, 1); video_line(96,13, 126, 46, 1); //update the system clock every seconds if (++t>59) { t=0; sec = sec + 1; if (sec>59){ sec=0; mins = mins + 1; } sprintf(ts,"%d:%02d",mins,sec); //M:SS format video_putsmalls(56,2,ts); video_putsmalls2(56,2,ts); } //Erase Old Puck // TV1 if(videoPuck1y < 24){ drawSmallPuck(videoPuck1x, videoPuck1y,0); }else{ drawBigPuck(videoPuck1x, videoPuck1y,0); } // TV2 if(videoPuck2y < 24){ drawSmallPuck2(videoPuck2x, videoPuck2y,0); }else{ drawBigPuck2(videoPuck2x, videoPuck2y,0); } //Erase Old Paddle //TV1 drawBig(paddle1Lastx,0); drawSmall(paddle2farL,0); //TV2 drawBig2(paddle2Lastx,0); drawSmall2(paddle1farL,0); // Detect Paddle position //******************************* // CHANGED BY MICHAEL //initialize ball after scoring if (BallAge == 0) { //initialize new ball Ballx = 0x4000; // x = 64 Bally = 0x6e00; //y = 110 Ballvx = 0x0100; //0xfe00; BallAge = 1; if (Ballvy > 0) { //player 2 scored against, so new ball comes from player 2 Ballvy = 0xff0f; } else { //Ballvy < 0 //player 1 scored against Ballvy = 0x0100; } } // Update array to find moving average paddle veocities // moving average has number of elements specified by MaxIndex Paddle1V = int2fix(paddle1x) - int2fix(paddle1Lastx); //Paddle 1 velocity, x-direction Paddle2V = int2fix(127 - paddle2x) - int2fix(127 - paddle2Lastx); //Paddle 2 velocity, x-direction //update array for paddle 1 velocities1[index] = Paddle1V; //update array for paddle 2 velocities2[index] = Paddle2V; index = index + 1; if (index > MaxIndex) { index = 0; // reset index when all array elements have been updated } //**** Ball dynamics // Paddle-ball/ball-wall interactions //compute new Rj Ballx = Ballx + Ballvx; Bally = Bally + Ballvy; //ball hits left side of area //0x08 = 0d08 if (Ballx < 0x0800) { //bounce off left side Ballvx = invert(Ballvx); Ballx=0x0800; } //ball hits right side of area //0x77 = 0d119 if (Ballx > 0x7700) {Ballvx = invert(Ballvx); Ballx=0x7700;} //ball hits top of area (player 2) //0xda = 0d218 //0xd5 = 0d213 if (Bally > 0xd500) { if (Ballx > int2fix(((127 - paddle2x) - 14)) && Ballx < int2fix(((127 - paddle2x) + 14))) { // player 2 bounces ball back to player 1 Ballvy = invert(Ballvy); for (k = 0; k <= MaxIndex; k++) { Paddle2VSum = Paddle2VSum + velocities2[k]; } Paddle2VAvg = Paddle2VSum / (MaxIndex + 1); // find average paddle velocity Paddle2V = int2fix(Paddle2VAvg); //Paddle 2 velocity, x-direction Ballvx = Ballvx + (Paddle2V >> 6); Bally = 0xd500; Paddle2VSum = 0; // reset paddle velocity sum } else { // player 2 misses ball // player 1 score increases score1 = score1 + 1; //ball disappears BallAge=0; //pause gameplay for a short while, then resume play MachineState = Hold; NextState = TwoPlayer; holdlimit = 1; //hold for 2 sec p1x = paddle1x; p2x = paddle2x; p1f = paddle1far; p2f = paddle2far; p1flag = 1; //video_putsmalls(44,29,p1goal); //video_putsmalls2(44,29,p1goal); } } //ball hits bottom of area (player 1) // 0x16 = 0d22 // 0x1b = 0d27 if (Bally < 0x1b00 ) { if (Ballx > int2fix((paddle1x - 14)) && Ballx < int2fix((paddle1x + 14))) { // player 1 bounces ball back to player 2 Ballvy = invert(Ballvy); for (k = 0; k <= MaxIndex; k++) { Paddle1VSum = Paddle1VSum + velocities1[k]; } Paddle1VAvg = Paddle1VSum / (MaxIndex + 1); // find average paddle velocity Paddle1V = int2fix(Paddle1VAvg); //Paddle 2 velocity, x-direction ////Paddle1V = int2fix(paddle1x) - int2fix(paddle1Lastx); //Paddle 1 velocity, x-direction Ballvx = Ballvx + (Paddle1V >> 6); //if (Ballvx > Bally = 0x1b00; Paddle1VSum = 0; // reset paddle velocity sum } else { // player 1 misses ball // player 2 score increases score2 = score2 + 1; //ball disappears BallAge=0; //pause gameplay for a short while, then resume play MachineState = Hold; NextState = TwoPlayer; holdlimit = 1; //hold for 2 sec p1flag = 2; //video_putsmalls2(44,29,p2goal); //video_putsmalls(44,29,p2goal); p1x = paddle1x; p2x = paddle2x; p1f = paddle1far; p2f = paddle2far; } } // Projection: // int videoPuck1x, videoPuck1y, videoPuck2x, videoPuck2y; // Ballx, Bally // Calculate Projection on TV1 tempx = Ballx >> 8; // removes all decimal points tempy = Bally >> 8; videoPuck1x = (char)((tempx - 63)*200/(tempy+180) + 63); videoPuck1y = (char) (13600/(tempy+180) - 21); videoPuck2x = (char)((64 - tempx)*200/(420-tempy) + 63); videoPuck2y = (char) (13600/(420-tempy) -21); // debugging // if (tempx < tempMinx) { // // find min // tempMinx = tempx; // } // sprintf(ts,"%03d",Ballx >> 8); // video_putsmalls(4,11,ts); // // // sprintf(ts,"%03d",paddle2x); // video_putsmalls(112,11,ts); // // sprintf(ts,"%03d",paddle2x); // video_putsmalls2(4,11,ts); // // // sprintf(ts,"%03d",videoPuck1y); // video_putsmalls2(112,11,ts); //Draw New Puck // TV1 if(videoPuck1y < 24){ drawSmallPuck(videoPuck1x, videoPuck1y,1); }else{ drawBigPuck(videoPuck1x, videoPuck1y,1); } // TV2 if(videoPuck2y < 24){ drawSmallPuck2(videoPuck2x, videoPuck2y,1); }else{ drawBigPuck2(videoPuck2x, videoPuck2y,1); } //Draw New Paddle //TV1 drawBig(paddle1x,1); drawSmall(paddle2far,1); //TV2 drawBig2(paddle2x,1); drawSmall2(paddle1far,1); // **** SCORE ****** sprintf(ts,"%1d",score1); video_putsmalls(16,4,ts); video_putsmalls2(120,4,ts); sprintf(ts,"%1d",score2); video_putsmalls2(16,4,ts); video_putsmalls(120,4,ts); // **** GAME UPDATE **** NEW // Check if any player has won a set or won a game if(score1==MaxScore){ Games1++; if(Games1==MaxGames){ // Player 1 won the game sprintf(ts,"%sZ"); video_putsmalls((Games1<<2),11,ts); video_putsmalls2((124 - (Games1<<2)),11,ts); MachineState = Victory; holdlimit = 10; }else{ // won a set but didn't win a game yet sprintf(ts,"%sZ"); video_putsmalls((Games1<<2),11,ts); video_putsmalls2((124 - (Games1<<2)),11,ts); MachineState = Hold; NextState = TwoPlayer; holdlimit = 3; } //Erase Old Puck // TV1 if(videoPuck1y < 24){ drawSmallPuck(videoPuck1x, videoPuck1y,0); }else{ drawBigPuck(videoPuck1x, videoPuck1y,0); } //Erase Old Paddle //TV1 drawBig(paddle1x,0); drawSmall(paddle2far,0); //TV2 drawBig2(paddle2x,0); drawSmall2(paddle1far,0); } // score1==7 if(score2==MaxScore){ Games2++; if(Games2==MaxGames){ // Playe 2 won the game sprintf(ts,"%sZ"); video_putsmalls2((Games2<<2),11,ts); video_putsmalls((124 - (Games2<<2)),11,ts); MachineState = Victory; holdlimit = 10; }else{ // won a set but didn't win a game yet sprintf(ts,"%sZ"); video_putsmalls2((Games2<<2),11,ts); video_putsmalls((124 - (Games2<<2)),11,ts); MachineState = Hold; NextState = TwoPlayer; holdlimit = 3; } //Erase Old Puck // TV1 if(videoPuck1y < 24){ drawSmallPuck(videoPuck1x, videoPuck1y,0); }else{ drawBigPuck(videoPuck1x, videoPuck1y,0); } //Erase Old Paddle //TV1 drawBig(paddle1x,0); drawSmall(paddle2far,0); //TV2 drawBig2(paddle2x,0); drawSmall2(paddle1far,0); } // score2==7 break; case Hold: // Freeze display for specified time and show who scored or won a set // display who scores goal if (p1flag == 1) { video_putsmalls(44,29,p1goal); video_putsmalls2(44,29,p1goal); } else if (p1flag == 2) { video_putsmalls(44,29,p2goal); video_putsmalls2(44,29,p2goal); } p1flag = 0; // display who won set if(score1==MaxScore){ sprintf(ts,"%sSETXWON"); video_putsmalls(44,29,ts); sprintf(ts,"%sSETXLOST"); video_putsmalls2(44,29,ts); }else if(score2==MaxScore){ sprintf(ts,"%sSETXWON"); video_putsmalls2(44,29,ts); sprintf(ts,"%sSETXLOST"); video_putsmalls(44,29,ts); } //update the hold timer every second if (++holdt>59) { holdt=0; holdsec = holdsec + 1; if (holdsec > holdlimit){ holdsec = 0; MachineState = NextState; //Erase Old Paddle //TV1 drawBig(p1x,0); drawSmall(p2f,0); //TV2 drawBig2(p2x,0); drawSmall2(p1f,0); //video_putsmalls(44,27,spaces10); if(score1==MaxScore || score2==MaxScore){ score1=0; score2=0; } video_putsmalls(44,29,spaces10); video_putsmalls2(44,29,spaces10); } } break; case Victory: //show victory screen if(Games1==MaxGames){ // Player 1 won the game video_putsmalls(44,29,P1win); video_putsmalls2(44,29,P1win); } if(Games2==MaxGames){ // Playe 2 won the game video_putsmalls(44,29,P2win); video_putsmalls2(44,29,P2win); } //update the hold timer every second if (++holdt>59) { holdt=0; holdsec = holdsec + 1; if (holdsec > holdlimit){ holdsec = 0; MachineState = Init; video_putsmalls(44,29,spaces10); video_putsmalls2(44,29,spaces10); } } break; } } //line131 } //while } //main