Ping Pong Scoreboard

From pauljmac.com Projects
Jump to: navigation, search

Updates

<section begin=updates />*New! Ping Pong Scoreboard! --Paul 13:16, 10 January 2008 (EST)<section end=updates /> <section begin=oldnews /> <section end=oldnews />

Abstract

This device is intended to make score keeping easier and more spectator friendly for the game of ping pong. It consists of 2 double digit 7-0segment displays, 1 display for each player. Each display has 5 LEDs that are used to indicate the player’s current serve. The device can operate in the old 21 point style game or the new 11 point style game. The game mode is selected using the Game Mode switch. After selecting the desired game mode the user should reset the board to register the change. The device has a cord that links 3 buttons to the main score board. The 3 buttons are intended to be taped to the underside of one of the player’s court. This allows 1 player to control the score without interfering with the playing field. The 3 buttons are P1 score, P2 Score and Undo. P1 and P2 score are used to give the respective player a point for winning a rally. The Undo button is to correct a miss used button. The undo is only 1 level deep, meaning it can only undo your last action. The scoreboard will automatically calculate whose serve it is and display it on the LEDs. The scoreboard even works when the game becomes Duce and a player must win by 2 and serve alternates every round. When a player reaches a winning condition the scoreboard will flash indicating the game is over. After a game is over the scoreboard should be reset to continue with a new game.

The PIC I used may seem like a strange selection but honestly the only reason I chose it was because I had extras lying around. Normally I wouldn't use an IC as expensive as the MAX7221 ($10!) but again I had one lying around.

Parts

Part QTY
NO momentary buttons 4
SPST switch 5
10K resistors 6
20MHZ Crystal 1
27pF ceramic caps 2
10uF cap 1
1uF cap 1
double digit 7-segment displays 2
LEDs 10
PIC16F872 1
MAX7221 1
6 pos male ICSP header 1
signal diode 1N4148 1
2n3904 npn 2
current limiting resistor dependent on your 7-segment displays 1

Schematic

PingPongScoreboard.png

EAGLE schem

Code

Full MPLAB project here: [1]

Spelling not guaranteed in code. :-)

;**********************************************************************
;                                                                     *
;    Filename:	    main.asm                                          *
;    Date: 01/08/08                                                   *
;    File Version: 1.0                                                *
;                                                                     *
;    Author: Paul Mac                                                 *
;            me@pauljmac.com                                          * 
;            http://pauljmac.com                                      *
;            Copyright Paul Mac 2008. Some Rights Reserved.           *
;**********************************************************************
;                                                                     *
;    Files required:  P16872.INC                                      *
;                                                                     *
;**********************************************************************
;                                                                     *
;    Notes: This is a program used to keep score of a Ping-Pong game. *
;   It uses a 16F872 and a MAX7221 7-segment driver with 4 displays.  *
;   The tracker also keeps score of whose serve it is and can even    *
;   commutate serve/score while in Duce. The Mode Switch is used to   *
;   tell the program weather to run an old style 21 point game or a   *
;   new 11 point style game. The game mode switch is read after a     *
;   reset. The "main controls" consists of 3 buttons: P1, P2 and Undo *
;   P1 and P2 is used to increment the scores while undo is, well,    * 
;   UNDO! The undo button is for if you just hit the wrong player     *
;   button and need to undo it. The undo is only 1 level deep.        *
;   Meaning you can only undo your last	incorrect score increment.    *
;   There’s no 3 or 4 undo’s here, it’s not MS word.                  *
;   When a player reaches the winning point the scoreboard            *	
;   will flash indicating the games over. Score can continue after    *
;   a winning game condition, but the score board may do some funky   *
;   things, as its not intended to still be used after a winning game *
;   condition. The Scoreboard should be reset after a winning game.   *
;                                                                     *
;**********************************************************************
; This work is licensed under the Creative Commons                    *
; Attribution-Noncommercial-Share Alike 3.0 United States License.    *
; To view a copy of this license, visit                               *
; http://creativecommons.org/licenses/by-nc-sa/3.0/us/ or send a      *
; letter to Creative Commons, 171 Second Street, Suite 300,           *
; San Francisco, California, 94105, USA.                              *
;**********************************************************************



	list      p=16f872            ; list directive to define processor
	#include <p16f872.inc>        ; processor specific variable definitions
	
	__CONFIG _CP_OFF & _WDT_OFF & _BODEN_OFF & _PWRTE_ON & _HS_OSC & _WRT_ENABLE_ON & _LVP_OFF & _CPD_OFF


;***** VARIABLE DEFINITIONS
	cblock 0x20
	p1s, p2s, serve, p1score, p2score, u1,u2, displaydelay, displaydelay1, toggle
	bin, hundreds, tens_and_ones
	p1button, p2button, undobutton, two, temp, gameover, gameovertoggle, gameovercounter
	d1,d2,d3
	endc
	
#define	CS	PORTC,7
#define	SL1	PORTC,2
#define	SL2	PORTA,5
#define	P1B	PORTA,0
#define	P2B	PORTA,1
#define	UndoB	PORTA,2
#define	Mode	PORTA,3


;**********************************************************************
	ORG     0x000             ; processor reset vector
	clrf    PCLATH            ; ensure page bits are cleared
 	goto    main              ; go to beginning of program

	ORG 	0x020
	ORG 	0x020
main
	BANKSEL	PORTA
	clrf	PORTA
	clrf	PORTB
	clrf	PORTC
	BANKSEL	TRISA
	clrf	TRISA
	clrf	TRISB
	clrf	TRISC
	movlw	b'00000110'		;turnning off analog inputs
	BANKSEL ADCON1
	movwf	ADCON1
	movlw	b'00001111'		;setting buttons to input
	BANKSEL	TRISA
	movwf	TRISA
	movlw	b'11001000'		;the server indicator LEDs
	movwf	TRISB
	movlw	b'00010000'
	movwf	TRISC
	BANKSEL	p1score
	clrf	p1score
	clrf	p2score
	clrf	p1s
	clrf	p2s
	clrf	serve
	clrf	p1button
	clrf	p2button
	clrf	undobutton
	clrf	gameover
	banksel	displaydelay1
	movlw	0xFF
	movwf	displaydelay1
	movwf	displaydelay
	movlw	0x02
	movwf	two
	movlw	0x19	;25
	movwf	gameovercounter
	banksel	PORTC
	bsf		CS			;the CS pin for the 7221
	movlw	0xC0
	banksel	SSPSTAT
	movwf	SSPSTAT
	movlw	0x20		;setup SPI
	banksel	SSPCON
	movwf	SSPCON
	movlw	0x0F		;put the MAX7221 into normal mode
	BANKSEL	PORTC
	bcf		CS		;Chip Select the MAX
	call	SendSPI
	movlw	0x00		;normal mode
	call 	SendSPI
	BANKSEL	PORTC
	bsf		CS		;unchip select the MAX
	movlw	0x0C		;Shutdown register
	BANKSEL	PORTC
	bcf		CS		;
	call	SendSPI
	movlw	0x01		;normal operation
	call 	SendSPI
	BANKSEL	PORTC
	bsf		CS		;
	movlw	0x0A		;Intensity register
	BANKSEL	PORTC
	bcf		CS		;
	call	SendSPI
	movlw	0x0F		;Max on
	call 	SendSPI
	BANKSEL	PORTC
	bsf		CS		;
	movlw	0x0B		;Scan-Limit register
	BANKSEL	PORTC
	bcf		CS		;
	call	SendSPI
	movlw	0x03		;4 digits
	call 	SendSPI
	BANKSEL	PORTC
	bsf		CS		;
	movlw	0x09		;decode mode register
	BANKSEL	PORTC
	bcf		CS		;
	call	SendSPI
	movlw	0xFF		;all decode
	call 	SendSPI
	BANKSEL	PORTC
	bsf		CS		;
	call	ClearSegs
	btfss	Mode			;test the mode switch. if its low its 21 point game, if high 11 point game
	goto	new_type
	goto	old_type
old_type				;begin the 21 point style play. Waiting for the user to select who has first serve.
	movlw	1
	BANKSEL	serve
	movwf	serve
	call	PollButtons
	btfsc	p1button,0			;poll the player 1 button
	goto	loadp1
	btfsc	p2button,0			;poll the player 2 button
	goto	loadp2
	goto	old_type		;loop, lol forgot this the first time	
PollButtons
	BANKSEL	PORTA
	btfss	P1B			;poll the player 1 button
	call	Player1Button
	btfss	P2B			;poll the player 2 button
	call	Player2Button
	btfss	UndoB
	call	UndoButton
	return
Player1Button
	btfss	P1B
	bsf		p1button,0
Player1Button_Test
	btfsc	P1B
	goto	Player1Button_Return
	goto	Player1Button_Test
Player1Button_Return
	call	Twenty_mS_Delay
	return
Player2Button
	btfss	P2B
	bsf		p2button,0
Player2Button_Test
	btfsc	P2B
	goto	Player2Button_Return
	goto	Player2Button_Test
Player2Button_Return
	call	Twenty_mS_Delay
	return
UndoButton
	btfss	UndoB
	bsf		undobutton,0
UndoButton_Test
	btfsc	UndoB
	goto	UndoButton_Return
	goto	UndoButton_Test
UndoButton_Return
	call	Twenty_mS_Delay
	return	
loadp1
	bsf		p1s,0			;set p1s bit 0 indicateing they are current server
	goto	Startold
loadp2
	bsf		p2s,0			;set p2s bit 0 indicateing they are current server
	goto	Startold
ClearButtons
	bcf		p1button,0
	bcf		p2button,0
	bcf		undobutton,0
	return
Startold
	call 	ClearButtons
	call	DisplayRoutine
	call    PollButtons
	movlw	0x15
	subwf	p1score,w
	btfsc	STATUS,Z		;test the Zero flag. It will be set if the score is 21	
	bsf		gameover,0		;set game over mode
	movlw	0x15
	subwf	p2score,w
	btfsc	STATUS,Z		;test the Zero flag. It will be set if the score is 21		
	bsf		gameover,0		;set game over mode
	btfsc	p1button,0			;test p1 button for a p1score++
	call	Addp1
	btfsc	p2button,0			;test p2 button for a p2score++
	call	Addp2
	btfsc	undobutton,0		;test undo button
	call 	Undo
	movlw	0x14				;setup to see if the serve # is more than 20
	subwf	p1score,w		;subtract 20
	btfss	STATUS,Z		;test the Zero flag. It will be clear if the serve is less than 20	
	goto	Startold
	movlw	0x14				;setup to see if the serve # is more than 20
	subwf	p2score,w		;subtract 20
	btfss	STATUS,Z		;test the carry flag. It will be clear if the serve is less than 20	
	goto	Startold
	goto	Duce
Duce
	call	ClearButtons
	call    PollButtons
	btfsc	p1button,0			;test p1 button for a p1score++
	call	Addp1Duce
	btfsc	p2button,0			;test p2 button for a p2score++
	call	Addp2Duce
	btfsc	undobutton,0		;test undo button
	call 	UndoDuce
	call	DisplayRoutine
	call	TestDuceGameOver
	goto	Duce	
UndoDuce
	call	GameOverOn
	movfw	p1score			;add p1 and p2 score to see if its 20
	addwf	p2score,w
	banksel	temp
	movwf	temp
	movlw	0x28
	subwf	temp,w
	btfss	STATUS,Z		;test the carry flag. It will be clear if the serve is less than 20	
	goto	UndoDuce1
	movlw	0x06
	movwf	serve
	banksel	p1s
	comf	p1s,f
	comf	p2s,f
	goto	UndoReturn
UndoReturn
	call	ResetToggler
	bcf		toggle,0
	btfsc	u1,0			;see if the last point awarded was to p1
	call	UndoP1Return			;if it was do this 
	btfsc	u2,0			;see if the last point awarded was to p2
	call	UndoP2Return
	goto	Startold
UndoP1Return
	movlw	b'11001000'
	banksel PORTB
	andwf	PORTB,f
	banksel p1score
	decf	p1score,f		;--p1score
	decf	serve,f			;--serve
	btfsc	STATUS,Z		;if Z flag is set that means that serve was at 1 and needs to be reset to 5
	call	UndoServe
	bcf		u1,0			;clear the p1 undo register to prevent a loop
	bcf		SL1
	bcf		SL2
	return
UndoP2Return
	movlw	b'11001000'
	banksel PORTB
	andwf	PORTB,f
	banksel p1score
	decf	p2score,f		;--p1score
	decf	serve,f			;--serve
	btfsc	STATUS,Z		;if Z flag is set that means that serve was at 1 and needs to be reset to 5
	call	UndoServe
	bcf		u2,0			;clear the p1 undo register to prevent a loop
	bcf		SL1
	bcf		SL2
	return
UndoDuce1
	call	ResetToggler
	bcf		toggle,0
	btfsc	u1,0			;see if the last point awarded was to p1
	call	UndoP1Duce		;if it was do this 
	btfsc	u2,0			;see if the last point awarded was to p2
	call	UndoP2Duce
	return
UndoP1Duce
	movlw	b'11001000'
	banksel PORTB
	andwf	PORTB,f
	banksel p1score
	decf	p1score,f		;--p1score
	call	UndoServeDuce
	bcf		u1,0			;clear the p1 undo register to prevent a loop
	bcf		SL1
	bcf		SL2
	return
UndoP2Duce
	movlw	b'11001000'
	banksel PORTB
	andwf	PORTB,f
	banksel p2score
	decf	p2score,f		;--p2score
	call	UndoServeDuce
	bcf		u2,0			;clear the p1 undo register to prevent a loop
	bcf		SL1
	bcf		SL2
	return
UndoServeDuce
	comf	p1s,f
	comf	p2s,f
	return	
TestDuceGameOver
	banksel	p1score
	movfw	p1score
	subwf	p2score,w
	call	TestTwo
	banksel	p1score	
	movfw	p2score
	subwf	p1score,w
	call	TestTwo
	return
TestTwo
	banksel	two
	subwf	two,w			;subtract 2 and test X flag
	btfsc	STATUS,Z		;test the carry flag. It will be clear if the serve is less than 20	
	bsf		gameover,0
	return
Addp1Duce
	call	ResetToggler
	bcf		toggle,0
	bsf		u1,0			;set u1 indicateing that p1 was the last person to reviece a point (for undo button)
	bcf		u2,0			;clear u2 undo register sence u1 is being set now
	incf	p1score,f		;incrament p1score
	comf	p1s,f
	comf	p2s,f
	bcf		SL1
	bcf		SL2	
	return					;return to the main game loop
Addp2Duce
	call	ResetToggler
	bcf		toggle,0
	bsf		u2,0			;set u2 indicateing that p2 was the last person to reviece a point (for undo button)
	bcf		u1,0			;clear u1 undo register sence u2 is being set now
	incf	p2score,f		;incrament p1score
	comf	p1s,f
	comf	p2s,f
	bcf		SL1
	bcf		SL2
	return	
Addp1
	call	ResetToggler
	bcf		toggle,0
	bsf		u1,0			;set u1 indicateing that p1 was the last person to reviece a point (for undo button)
	bcf		u2,0			;clear u2 undo register sence u1 is being set now
	incf	p1score,f		;incrament p1score
	incf	serve,f			;++ serve
	movlw	0x06			;setup to see if the serve # is more than 5
	subwf	serve,w			;subtract 6 from Serve
	btfsc	STATUS,C		;test the carry flag. It will be clear if Serve is less than 6
	call 	SwitchServe
	return					;return to the main game loop.	
Addp2
	call	ResetToggler
	bcf		toggle,0
	bsf		u2,0			;set u2 indicateing that p2 was the last person to reviece a point (for undo button)
	bcf		u1,0			;clear u1 undo register sence u2 is being set now
	incf	p2score,f		;incrament p1score
	incf	serve,f			
	movlw	0x06			;setup to see if the serve # is more than 5
	subwf	serve,w			;subtract 6 from Serve
	btfsc	STATUS,C		;test the carry flag. It will be clear if Serve is less than 6
	call 	SwitchServe
	return					;return to the main game loop.
Undo
	call	GameOverOn
	call	ResetToggler
	bcf		toggle,0
	btfsc	u1,0			;see if the last point awarded was to p1
	call	UndoP1			;if it was do this 
	btfsc	u2,0			;see if the last point awarded was to p2
	call	UndoP2
	return
UndoP1
	movlw	b'11001000'
	banksel PORTB
	andwf	PORTB,f
	banksel p1score
	decf	p1score,f		;--p1score
	decf	serve,f			;--serve
	btfsc	STATUS,Z		;if Z flag is set that means that serve was at 1 and needs to be reset to 5
	call	UndoServe
	bcf		u1,0			;clear the p1 undo register to prevent a loop
	bcf		SL1
	bcf		SL2
	return
UndoP2
	movlw	b'11001000'
	banksel PORTB
	andwf	PORTB,f
	banksel p1score
	decf	p2score,f		;--p1score
	decf	serve,f			;--serve
	btfsc	STATUS,Z		;if Z flag is set that means that serve was at 1 and needs to be reset to 5
	call	UndoServe
	bcf		u2,0			;clear the p1 undo register to prevent a loop
	bcf		SL1
	bcf		SL2
	return
UndoServe
	movlw	0x05
	movwf	serve
	comf	p1s,f
	comf	p2s,f
	return	
SwitchServe
	comf	p1s,f			;compliment p1s
	comf	p2s,f			;compliment p2s
	bcf		SL1			;turn OFF p1 server leds
	bcf		SL2			;turn OFF p2 server leds
	movlw	0x01			;reset the serve register 
	banksel	serve
	movwf	serve	
	movlw	b'11001000'
	banksel PORTB
	andwf	PORTB,f	
	return	
DisplayRoutine
		;counter
	call	Two_mS_Delay
	banksel	displaydelay
	decfsz	displaydelay,f	;dec the display delay counter
	goto	Skip	
DisplayDelay1
	decfsz	displaydelay1,f
	goto	DisplayDelay1
	comf	toggle,f
	goto	Skip
Skip
	btfss	toggle,0
	goto	Normal_LED_Display
	goto	Toggle_LED_Display	
ResetToggler
	banksel	displaydelay1
	movlw	0xFF
	movwf	displaydelay1
	movwf	displaydelay
	bcf		gameover,0
	return
Normal_LED_Display
	btfsc	p1s,0			;test the p1 serve register, if its clear skip next
	bsf		SL1			;turn on p1 server leds
	btfsc	p2s,0			;test p2 serve register, if its clear (it shouldnet be at this point) skip.
	bsf		SL2			;turn on p2 server leds
	movlw	HIGH LookupServe	;got to preload the high bites into PCLATH for this lookup to work
	movwf	PCLATH
	banksel	serve
	movfw	serve			;move the current serve # to W
	call	LookupServe		;lookup the bit pattern
	banksel PORTB
	iorwf	PORTB,f			;OR the bit pattern with PORTB to display the correct LEDS
	goto	DisplayScores
Toggle_LED_Display
	movlw	b'11001000'		;clear portb before we start this
	banksel PORTB			;
	andwf	PORTB,f			;
	btfsc	p1s,0			;test the p1 serve register, if its clear skip next
	bsf		SL1			;turn on p1 server leds
	btfsc	p2s,0			;test p2 serve register, if its clear (it shouldnet be at this point) skip.
	bsf		SL2			;turn on p2 server leds
	movlw	HIGH LookupServe	;got to preload the high bites into PCLATH for this lookup to work
	movwf	PCLATH
	banksel	serve
	decf	serve,w			;subtract 1 and put the result in W
	call	LookupServe		;lookup the bit pattern
	banksel PORTB
	iorwf	PORTB,f			;OR the bit pattern with PORTB to display the correct LEDS
	goto	DisplayScores
DisplayScores
		;player 1 digits
	movf	p1score,w		;move p1score to w
	movwf	bin				;move the p1score to bin for BCD computation
	call	binary_to_bcd
	movlw	0x01			;select the first digit to display
	bcf		CS
	call	SendSPI
	movf	tens_and_ones,w	;move tens_and_ones to W
	andlw	b'00001111'		;mask the crap and now ones is in W
	call	SendSPI
	bsf		CS
	movlw	0x02			;select the 2nd digit to display
	bcf		CS
	call	SendSPI
	swapf	tens_and_ones,w	;swap the nibbles in tens_and_ones and put it in W
	andlw	b'00001111'		;mask the crap and now tens is in W
	call	SendSPI
	bsf		CS
		;Do it all again for player 2 digits
	movf	p2score,w		;move p1score to w
	movwf	bin				;move the p1score to bin for BCD computation
	call	binary_to_bcd
	movlw	0x03			;select the first digit to display
	bcf		CS
	call	SendSPI
	movf	tens_and_ones,w	;move tens_and_ones to W
	andlw	b'00001111'		;mask the crap and now ones is in W
	call	SendSPI
	bsf		CS
	movlw	0x04			;select the 2nd digit to display
	bcf		CS
	call	SendSPI
	swapf	tens_and_ones,w	;swap the nibbles in tens_and_ones and put it in W
	andlw	b'00001111'		;mask the crap and now tens is in W
	call	SendSPI
	bsf		CS
	banksel gameover
	btfss	gameover,0
	return	;return the call if its not gameover mode
	call	Twenty_mS_Delay
	banksel	gameover
	decfsz	gameovercounter,f
	return	;return the call after the dec above
	banksel	gameovertoggle
	btfsc	gameovertoggle,0
	goto	GameOverOn
	goto	GameOverOff
GameOverOff
	bsf		gameovertoggle,0	
	movlw	0x0C		;Shutdown register
	BANKSEL	PORTC
	bcf		CS		;
	call	SendSPI
	movlw	0x00		;shutdown mode
	call 	SendSPI
	BANKSEL	PORTC
	bsf		CS		;
	movlw	0x19	;25
	movwf	gameovercounter
	return
GameOverOn	
	bcf		gameovertoggle,0	
	movlw	0x0C		;Shutdown register
	BANKSEL	PORTC
	bcf		CS		;
	call	SendSPI
	movlw	0x01		;normal mode
	call 	SendSPI
	BANKSEL	PORTC
	bsf		CS		;
	movlw	0x19	;25
	movwf	gameovercounter
	return	
ClearSegs
	bcf		CS
	movlw	0x01			;initilize the 7segs by clearing all the digits
	call	SendSPI
	movlw	0x00
	call 	SendSPI
	bsf		CS
	bcf		CS
	movlw 	0x02
	call 	SendSPI
	movlw	0x00
	call	SendSPI
	bsf		CS
	bcf		CS
	movlw	0x03
	call 	SendSPI
	movlw 	0x00
	call 	SendSPI
	bsf		CS
	bcf		CS
	movlw	0x04
	call	SendSPI
	movlw	0x00
	call	SendSPI
	bsf		CS
	return
SendSPI						;This rutine will hold the MCU untill transmission is over. 
	BANKSEL	SSPBUF			;If you want to load up the SSPBUF and GTFO dont use this.
	movwf	SSPBUF
wait1
	BANKSEL	SSPSTAT
	btfss	SSPSTAT,BF		;test to make sure its set indicateing the transmission is over
	goto	wait1			;when its over continue
	bcf		SSPSTAT,BF		;and clear the intrupt
	banksel	PORTA			; just get back to bank 0
	return

;==================================================================
; 11 point style game

		
new_type				;begin the 11 point style play. Waiting for the user to select who has first serve.
	movlw	1
	BANKSEL	serve
	movwf	serve
	call	PollButtons
	btfsc	p1button,0			;poll the player 1 button
	goto	loadp1_new
	btfsc	p2button,0			;poll the player 2 button
	goto	loadp2_new
	goto	new_type		;loop, lol forgot this the first time
loadp1_new
	bsf		p1s,0			;set p1s bit 0 indicateing they are current server
	goto	Startnew
loadp2_new
	bsf		p2s,0			;set p2s bit 0 indicateing they are current server
	goto	Startnew		
Startnew
	call 	ClearButtons
	call	DisplayRoutine
	movlw	0x0B			;11
	subwf	p1score,w
	btfsc	STATUS,Z		;test the Zero flag. It will be set if the score is 11	
	bsf		gameover,0
	movlw	0x0B			;11
	subwf	p2score,w
	btfsc	STATUS,Z		;test the Zero flag. It will be set if the score is 11		
	bsf		gameover,0	
	call    PollButtons
	btfsc	p1button,0			;test p1 button for a p1score++
	call	Addp1_new
	btfsc	p2button,0			;test p2 button for a p2score++
	call	Addp2_new
	btfsc	undobutton,0			;test undo button
	call 	Undo_new
	movlw	0x0A				;setup to see if the serve # is more than 10
	subwf	p1score,w		;subtract 10
	btfss	STATUS,Z		;test the Zero flag. It will be clear if the serve is less than 10	
	goto	Startnew
	movlw	0x0A				;setup to see if the serve # is more than 10
	subwf	p2score,w		;subtract 10
	btfss	STATUS,Z		;test the carry flag. It will be clear if the serve is less than 10	
	goto	Startnew
	goto	Duce_new
Duce_new
	call	ClearButtons
	call    PollButtons
	btfsc	p1button,0			;test p1 button for a p1score++
	call	Addp1Duce_new
	btfsc	p2button,0			;test p2 button for a p2score++
	call	Addp2Duce_new
	btfsc	undobutton,0		;test undo button
	call 	UndoDuce_new
	call	DisplayRoutine
	call	TestDuceGameOver_new
	goto	Duce_new
UndoDuce_new
	call	GameOverOn
	movfw	p1score			;add p1 and p2 score to see if its 20
	addwf	p2score,w
	banksel	temp
	movwf	temp
	movlw	0x14			;20
	subwf	temp,w
	btfss	STATUS,Z		;test the carry flag. It will be clear if the serve is less than 20	
	goto	UndoDuce1_new
	movlw	0x03
	movwf	serve
	banksel	p1s
	comf	p1s,f
	comf	p2s,f
	goto	UndoReturn_new
UndoReturn_new
	call	ResetToggler
	bcf		toggle,0
	btfsc	u1,0			;see if the last point awarded was to p1
	call	UndoP1Return_new			;if it was do this 
	btfsc	u2,0			;see if the last point awarded was to p2
	call	UndoP2Return_new
	goto	Startnew
UndoP1Return_new
	movlw	b'11001000'
	banksel PORTB
	andwf	PORTB,f
	banksel p1score
	decf	p1score,f		;--p1score
	decf	serve,f			;--serve
	btfsc	STATUS,Z		;if Z flag is set that means that serve was at 1 and needs to be reset to 2 
	call	UndoServe_new
	bcf		u1,0			;clear the p1 undo register to prevent a loop
	bcf		SL1
	bcf		SL2
	return
UndoP2Return_new
	movlw	b'11001000'
	banksel PORTB
	andwf	PORTB,f
	banksel p1score
	decf	p2score,f		;--p1score
	decf	serve,f			;--serve
	btfsc	STATUS,Z		;if Z flag is set that means that serve was at 1 and needs to be reset to 5
	call	UndoServe_new
	bcf		u2,0			;clear the p1 undo register to prevent a loop
	bcf		SL1
	bcf		SL2
	return
UndoDuce1_new
	call	ResetToggler
	bcf		toggle,0
	btfsc	u1,0			;see if the last point awarded was to p1
	call	UndoP1Duce_new		;if it was do this 
	btfsc	u2,0			;see if the last point awarded was to p2
	call	UndoP2Duce_new
	return
UndoP1Duce_new
	movlw	b'11001000'
	banksel PORTB
	andwf	PORTB,f
	banksel p1score
	decf	p1score,f		;--p1score
	call	UndoServeDuce_new
	bcf		u1,0			;clear the p1 undo register to prevent a loop
	bcf		SL1
	bcf		SL2
	return
UndoP2Duce_new
	movlw	b'11001000'
	banksel PORTB
	andwf	PORTB,f
	banksel p2score
	decf	p2score,f		;--p2score
	call	UndoServeDuce_new
	bcf		u2,0			;clear the p1 undo register to prevent a loop
	bcf		SL1
	bcf		SL2
	return
UndoServeDuce_new
	comf	p1s,f
	comf	p2s,f
	return
TestDuceGameOver_new
	banksel	p1score
	movfw	p1score
	subwf	p2score,w
	call	TestTwo_new
	banksel	p1score	
	movfw	p2score
	subwf	p1score,w
	call	TestTwo_new
	return
TestTwo_new
	banksel	two
	subwf	two,w			;subtract 2 and test X flag
	btfsc	STATUS,Z		;test the carry flag. It will be clear if the serve is less than 20	
	bsf		gameover,0
	return
Addp1Duce_new
	call	ResetToggler
	bcf		toggle,0
	bsf		u1,0			;set u1 indicateing that p1 was the last person to reviece a point (for undo button)
	bcf		u2,0			;clear u2 undo register sence u1 is being set now
	incf	p1score,f		;incrament p1score
	comf	p1s,f
	comf	p2s,f
	bcf		SL1
	bcf		SL2	
	return					;return to the main game loop
Addp2Duce_new
	call	ResetToggler
	bcf		toggle,0
	bsf		u2,0			;set u2 indicateing that p2 was the last person to reviece a point (for undo button)
	bcf		u1,0			;clear u1 undo register sence u2 is being set now
	incf	p2score,f		;incrament p1score
	comf	p1s,f
	comf	p2s,f
	bcf		SL1
	bcf		SL2
	return	
Addp1_new
	call	ResetToggler
	bcf		toggle,0
	bsf		u1,0			;set u1 indicateing that p1 was the last person to reviece a point (for undo button)
	bcf		u2,0			;clear u2 undo register sence u1 is being set now
	incf	p1score,f		;incrament p1score
	incf	serve,f			;++ serve
	movlw	0x03			;setup to see if the serve # is more than 2
	subwf	serve,w			;subtract 3 from Serve
	btfsc	STATUS,C		;test the carry flag. It will be clear if Serve is less than 3
	call 	SwitchServe_new
	return					;return to the main game loop.
Addp2_new
	call	ResetToggler
	bcf		toggle,0
	bsf		u2,0			;set u2 indicateing that p2 was the last person to reviece a point (for undo button)
	bcf		u1,0			;clear u1 undo register sence u2 is being set now
	incf	p2score,f		;incrament p1score
	incf	serve,f			
	movlw	0x03			;setup to see if the serve # is more than 2
	subwf	serve,w			;subtract 3 from Serve
	btfsc	STATUS,C		;test the carry flag. It will be clear if Serve is less than 3
	call 	SwitchServe_new
	return					;return to the main game loop.
Undo_new
	call	GameOverOn
	call	ResetToggler
	bcf		toggle,0
	btfsc	u1,0			;see if the last point awarded was to p1
	call	UndoP1_new			;if it was do this 
	btfsc	u2,0			;see if the last point awarded was to p2
	call	UndoP2_new
	return
UndoP1_new
	movlw	b'11001000'
	banksel PORTB
	andwf	PORTB,f
	banksel p1score
	decf	p1score,f		;--p1score
	decf	serve,f			;--serve
	btfsc	STATUS,Z		;if Z flag is set that means that serve was at 1 and needs to be reset to 2
	call	UndoServe_new
	bcf		u1,0			;clear the p1 undo register to prevent a loop
	bcf		SL1
	bcf		SL2
	return
UndoP2_new
	movlw	b'11001000'
	banksel PORTB
	andwf	PORTB,f
	banksel p1score
	decf	p2score,f		;--p1score
	decf	serve,f			;--serve
	btfsc	STATUS,Z		;if Z flag is set that means that serve was at 1 and needs to be reset to 2
	call	UndoServe_new
	bcf		u2,0			;clear the p1 undo register to prevent a loop
	bcf		SL1
	bcf		SL2
	return
UndoServe_new
	movlw	0x02
	movwf	serve
	comf	p1s,f
	comf	p2s,f
	return
SwitchServe_new
	comf	p1s,f			;compliment p1s
	comf	p2s,f			;compliment p2s
	bcf		SL1			;turn OFF p1 server leds
	bcf		SL2			;turn OFF p2 server leds
	movlw	0x01			;reset the serve register 
	banksel	serve
	movwf	serve	
	movlw	b'11001000'
	banksel PORTB
	andwf	PORTB,f	
	return	
	
;----------------------------------------------------------------
;end of the good stuff. everything else is routines

binary_to_bcd
	CLRF	hundreds 
	SWAPF	bin,w 			;w  = A0*16+A1
	ADDWF	bin,w 			;w  = A0+A1
	ANDLW	b'00001111' 	;w  = A0+A1 % 16
	SKPNDC 					;if A0+A1 > 16
	ADDLW	0x16 			;w  += 16
	SKPNDC 					;if w % 16 > 10
	ADDLW	0x06 			;w  += 6
	ADDLW 	0x06 			;w  += 6
	SKPDC 					;if w < 10
	ADDLW	-0x06 			;w  -= 6

	BTFSC	bin,4 
	ADDLW	0x16 - 1 + 0x6 
	SKPDC 
	ADDLW	-0x06 

	BTFSC	bin,5 
	ADDLW	0x30 
	
	BTFSC	bin,6 
	ADDLW	0x60 

	BTFSC	bin,7 
	ADDLW	0x20 

	ADDLW	0x60 
	RLF		hundreds,f 
	BTFSS	hundreds, 0 
	ADDLW 	-0x60 

	MOVWF	tens_and_ones 
	BTFSC	bin,7 
	INCF	hundreds,f 
	
	return
Half_Second_Delay
			;2499992 cycles
	movlw	0x15
	movwf	d1
	movlw	0x74
	movwf	d2
	movlw	0x06
	movwf	d3
Half_Second_Delay_0
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	$+2
	decfsz	d3, f
	goto	Half_Second_Delay_0

			;4 cycles
	goto	$+1
	goto	$+1

			;4 cycles (including call)
	return	
Twenty_mS_Delay
			;99993 cycles
	movlw	0x1E
	movwf	d1
	movlw	0x4F
	movwf	d2
Twenty_mS_Delay_0
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	Twenty_mS_Delay_0

			;3 cycles
	goto	$+1
	nop

			;4 cycles (including call)
	return

Two_mS_Delay
			;9993 cycles
	movlw	0xCE
	movwf	d1
	movlw	0x08
	movwf	d2
Two_mS_Delay_0
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	Two_mS_Delay_0

			;3 cycles
	goto	$+1
	nop

			;4 cycles (including call)
	return

LookupServe:
	addwf	PCL,f				;add the offset in W to the PC
	retlw	b'00000000'		;0
	retlw	b'00000001'		;1
	retlw	b'00000011'		;2
	retlw	b'00000111'		;3
	retlw	b'00010111'		;4
	retlw	b'00110111'		;5

	END                       ; directive 'end of program'

Pictures