Compare commits

..

16 Commits
1.0 ... master

11 changed files with 636 additions and 94 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
release
*.hex
*.elf
GNUmakefile

17
ISSUES.txt Normal file
View File

@ -0,0 +1,17 @@
Outstanding
-----------
Fixed
-----
1. Scores should not go negative
2. Konami Code position doesn't reset on non-sequence button
3. It is too easy to mess up scores when adjusting period clock time
4. Period clock adjustments take forever
5. Make setup mode less confusing
6. Allow jam clock adjustments in setup
7. Unified build with multiple firmware targets
8. Debounce buttons
9. Pull-up NES data to prevent going apeshit when you unplug the controller

19
LICENSE.txt Normal file
View File

@ -0,0 +1,19 @@
Copyright © 2014 Neale Pickett <neale@woozle.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The software is provided "as is", without warranty of any kind, express or
implied, including but not limited to the warranties of merchantability,
fitness for a particular purpose and noninfringement. In no event shall the
authors or copyright holders be liable for any claim, damages or other
liability, whether in an action of contract, tort or otherwise, arising from,
out of or in connection with the software or the use or other dealings in
the software.

View File

@ -1,4 +1,7 @@
PROG = main TARGETS += scoreboard-neale.hex
TARGETS += scoreboard-susan1.hex
TARGETS += scoreboard-susan2.hex
TARGETS += scoreboard-std.hex
MCU = attiny84 MCU = attiny84
@ -9,34 +12,34 @@ CFLAGS += -w
LDFLAGS += -mmcu=$(MCU) LDFLAGS += -mmcu=$(MCU)
AVDFLAGS += -p $(MCU) all: $(TARGETS)
AVDFLAGS += -c usbtiny
FUSES += -U lfuse:w:0x7f:m scoreboard-%.elf: main.c avr.c config.h avr.h
FUSES += -U hfuse:w:0xdd:m $(CC) $(CFLAGS) -DVARIANT=$* $(LDFLAGS) -o $@ avr.c main.c
FUSES += -U efuse:w:0xff:m
upload: .upload %.hex: %.elf
.upload: $(PROG).hex
avrdude $(AVDFLAGS) -U flash:w:$<
touch $@
fuses:
avrdude $(AVDFLAGS) $(FUSES)
main: main.o avr.o
blink: blink.o avr.o
avr.o: avr.c config.h
%.hex: %
avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature $< $@ avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature $< $@
clean: clean:
rm -f $(PROG) *.o *.hex .upload rm -f *.elf *.o *.hex .upload
##
## Helpful targets for development
##
VARIANT = neale
AVDFLAGS += -p $(MCU)
AVDFLAGS += -c usbtiny
upload: scoreboard-$(VARIANT).hex
avrdude $(AVDFLAGS) -U flash:w:$<
fuses: FUSES += -U lfuse:w:0x7f:m
fuses: FUSES += -U hfuse:w:0xdd:m
fuses: FUSES += -U efuse:w:0xff:m
fuses:
avrdude $(AVDFLAGS) $(FUSES)

50
README.md Normal file
View File

@ -0,0 +1,50 @@
LED Roller Derby Scoreboard
===========================
A do-it-yourself LED Roller Derby scoreboard.
It's visible in full sunlight,
doesn't use a computer at all,
and can be easily operated by an 8-year-old.
If you're looking for a software scoreboard (using a projector)
this [list of free derby scoreboard software](http://woozle.org/scoreboard/others.html) will help.
This scoreboard is operated with a Nintendo controller,
and runs on an XBox 360 power supply.
The rest of the parts need to be ordered from various sources online.
The total should come out to around $200,
with plenty of options for upgrades.
It will take about 6 weeks of work,
and requires basic electronics understanding and
elementary soldering skills.
I provide updated firmware,
so unless you feel like modifying the MIT-licensed source code,
you don't need to do any programming.
![Original Build](https://lh3.googleusercontent.com/-xKiVBp0bv70/UuafY7lZxeI/AAAAAAAAd1g/6TlMNSDp9xc/s1600/IMG_20140125_094136.jpg)
![First duplicate](https://lh5.googleusercontent.com/-Mm23bx2F-wQ/Uip4SGmmzWI/AAAAAAAAMKI/48zmiHN6v6U/s1600/13%2B-%2B1)
![Second duplicate](https://lh6.googleusercontent.com/-hOKmDB5JRYY/UtXPLuY2bDI/AAAAAAAAdpE/PYxeqHQGxTA/s1600/2014-01-14)
Resources
---------
* [Discussion Group](https://groups.google.com/forum/#!forum/scoreboard-builders) for help from others who've built or are building one.
* [Google Drive Folder](https://drive.google.com/folderview?id=0BzovkpI6mzWQNFc4eGN0aGoyTGM&usp=sharing) with
schematics, documentation, and firmware releases.
* [Firmware Source Code](https://woozle.org/neale/g.cgi/derby/hw-scoreboard)
About Me
--------
I'm Neale Pickett,
also known as "Q" in the Derby world.
Feel free to [email me](mailto:neale@woozle.org),
but the group would be more appropriate for email relating to this scoreboard.
I've done a lot of [other derby stuff](/derby/)
that you may be interested in.

8
avr.c
View File

@ -23,7 +23,7 @@ ISR(TIM1_COMPA_vect)
} }
void void
init(void) avr_init(void)
{ {
int i; int i;
@ -39,7 +39,13 @@ init(void)
TCCR1B |= _BV(CS11) | _BV(CS10); // prescale: clk_io / 64 TCCR1B |= _BV(CS11) | _BV(CS10); // prescale: clk_io / 64
TIMSK1 |= _BV(OCIE1A); TIMSK1 |= _BV(OCIE1A);
// Pull-up NES data line
bit(PORTA, _BV(NESOUT), true);
// Turn on Power Supply
bit(PORTA, _BV(7), true); bit(PORTA, _BV(7), true);
PORTB = 0xff;
sei(); sei();
} }

2
avr.h
View File

@ -15,6 +15,6 @@
#define nesltch(on) bit(PORTA, _BV(NESLTCH), on) #define nesltch(on) bit(PORTA, _BV(NESLTCH), on)
#define nesout() ((PINA & _BV(NESOUT)) << NESOUT) #define nesout() ((PINA & _BV(NESOUT)) << NESOUT)
void init(void); void avr_init(void);
#endif #endif

View File

@ -4,14 +4,13 @@
#define CLOCK_MHZ 16 #define CLOCK_MHZ 16
#define CLOCK_HZ (CLOCK_MHZ * 1000000) #define CLOCK_HZ (CLOCK_MHZ * 1000000)
#define SCORE_DIGITS 2 #define LINEUP_DEFAULT (-30 * 10)
#define JAM_DIGITS 3 #define JAM_DEFAULT (-2 * 60 * 10)
#define PERIOD_DEFAULT (-30 * 60 * 10)
// Show an indicator (J/L/t) to the left of the jam clock // TPIC uses weird pin names
//#define JAM_INDICATOR #define SRCK SCLK
#define RCK SLTCH
// The jam clock is split across the period clock (easier folding, harder wiring)
//#define JAM_SPLIT
// Set these to the PORTA pins you use // Set these to the PORTA pins you use
#define NESCLK 0 #define NESCLK 0
@ -21,4 +20,80 @@
#define SCLK 4 #define SCLK 4
#define SLTCH 5 #define SLTCH 5
#define JAM_DIGITS 3
#define std 0
#define neale 1
#define susan1 2
#define susan2 3
#define robbie 3
#if VARIANT == neale
//
// Neale variant
//
// With Jam indicator, split jam clock, and only 2 score digits.
// Also featuring goofy brain wiring.
//
#define SCORE_DIGITS 2
#define JAM_INDICATOR
#define JAM_SPLIT
// I wired mine differently and I'm too lazy to fix it.
#undef NESCLK
#undef NESLTCH
#undef NESOUT
#undef SIN
#undef SCLK
#undef SLTCH
#define SIN 0
#define SCLK 1
#define SLTCH 2
#define NESCLK 3
#define NESLTCH 4
#define NESOUT 5
#elif VARIANT == susan1
//
// Susan 1 variant
//
// Like the neale variant but with correct brain wiring
//
#define SCORE_DIGITS 2
#define JAM_INDICATOR
#define JAM_SPLIT
#elif VARIANT == susan2
//
// Susan 2 variant
//
#define SCORE_DIGITS 3
#else
//
// Default variant
//
// Nobody has built this yet!
// But it's the one described by the instructions.
//
#define SCORE_DIGITS 3
#define JAM_SPLIT
#endif
#endif #endif

136
controller-conn.svg Normal file
View File

@ -0,0 +1,136 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="129.57692"
height="209.82599"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="controller-conn.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.83718329"
inkscape:cx="49.790255"
inkscape:cy="119.40543"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1259"
inkscape:window-height="839"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-298.3437,-356.56045)">
<path
style="fill:none;stroke:#000000;stroke-width:20;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 308.43035,525.01817 c 0.0224,12.61936 10.90299,30.79285 29.01664,31.18674 18.11366,0.3939 46.59996,0.14703 57.65053,-1.01178 12.80286,-1.34256 22.45738,-21.53441 22.52553,-31.36944 0,0 0.64469,-87.18796 0,-91.56654 -0.64469,-4.37857 -13.29505,-15.39673 -28.53212,-31.75759 -15.23706,-16.36086 -25.30565,-33.68303 -48.51772,-33.94468 -17.74986,-0.20008 -31.03771,23.34467 -31.76769,28.18425 -0.72997,4.83959 -0.37517,130.27904 -0.37517,130.27904 z"
id="path3050"
inkscape:connector-curvature="0"
sodipodi:nodetypes="szsszsszs" />
<path
sodipodi:type="arc"
style="fill:none;stroke:#000000;stroke-width:10;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path3820"
sodipodi:cx="342.81619"
sodipodi:cy="402.56421"
sodipodi:rx="8.361371"
sodipodi:ry="8.6599913"
d="m 351.17756,402.56421 c 0,4.78278 -3.74351,8.65999 -8.36137,8.65999 -4.61786,0 -8.36137,-3.87721 -8.36137,-8.65999 0,-4.78278 3.74351,-8.65999 8.36137,-8.65999 4.61786,0 8.36137,3.87721 8.36137,8.65999 z"
transform="matrix(1.335243,0,0,1.335243,-114.92672,-134.95682)" />
<path
transform="matrix(1.335243,0,0,1.335243,-114.6281,-95.538929)"
d="m 351.17756,402.56421 c 0,4.78278 -3.74351,8.65999 -8.36137,8.65999 -4.61786,0 -8.36137,-3.87721 -8.36137,-8.65999 0,-4.78278 3.74351,-8.65999 8.36137,-8.65999 4.61786,0 8.36137,3.87721 8.36137,8.65999 z"
sodipodi:ry="8.6599913"
sodipodi:rx="8.361371"
sodipodi:cy="402.56421"
sodipodi:cx="342.81619"
id="path3822"
style="fill:none;stroke:#000000;stroke-width:10;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
sodipodi:type="arc" />
<path
sodipodi:type="arc"
style="fill:none;stroke:#000000;stroke-width:10;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path3824"
sodipodi:cx="342.81619"
sodipodi:cy="402.56421"
sodipodi:rx="8.361371"
sodipodi:ry="8.6599913"
d="m 351.17756,402.56421 c 0,4.78278 -3.74351,8.65999 -8.36137,8.65999 -4.61786,0 -8.36137,-3.87721 -8.36137,-8.65999 0,-4.78278 3.74351,-8.65999 8.36137,-8.65999 4.61786,0 8.36137,3.87721 8.36137,8.65999 z"
transform="matrix(1.335243,0,0,1.335243,-114.92672,-55.523797)" />
<path
transform="matrix(1.335243,0,0,1.335243,-114.6281,-15.508665)"
d="m 351.17756,402.56421 c 0,4.78278 -3.74351,8.65999 -8.36137,8.65999 -4.61786,0 -8.36137,-3.87721 -8.36137,-8.65999 0,-4.78278 3.74351,-8.65999 8.36137,-8.65999 4.61786,0 8.36137,3.87721 8.36137,8.65999 z"
sodipodi:ry="8.6599913"
sodipodi:rx="8.361371"
sodipodi:cy="402.56421"
sodipodi:cx="342.81619"
id="path3826"
style="fill:none;stroke:#000000;stroke-width:10;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
sodipodi:type="arc" />
<path
sodipodi:type="arc"
style="fill:none;stroke:#000000;stroke-width:10;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path3828"
sodipodi:cx="342.81619"
sodipodi:cy="402.56421"
sodipodi:rx="8.361371"
sodipodi:ry="8.6599913"
d="m 351.17756,402.56421 c 0,4.78278 -3.74351,8.65999 -8.36137,8.65999 -4.61786,0 -8.36137,-3.87721 -8.36137,-8.65999 0,-4.78278 3.74351,-8.65999 8.36137,-8.65999 4.61786,0 8.36137,3.87721 8.36137,8.65999 z"
transform="matrix(1.335243,0,0,1.335243,-74.314347,-94.344447)" />
<path
transform="matrix(1.335243,0,0,1.335243,-74.612967,-54.329315)"
d="m 351.17756,402.56421 c 0,4.78278 -3.74351,8.65999 -8.36137,8.65999 -4.61786,0 -8.36137,-3.87721 -8.36137,-8.65999 0,-4.78278 3.74351,-8.65999 8.36137,-8.65999 4.61786,0 8.36137,3.87721 8.36137,8.65999 z"
sodipodi:ry="8.6599913"
sodipodi:rx="8.361371"
sodipodi:cy="402.56421"
sodipodi:cx="342.81619"
id="path3830"
style="fill:none;stroke:#000000;stroke-width:10;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
sodipodi:type="arc" />
<path
sodipodi:type="arc"
style="fill:none;stroke:#000000;stroke-width:10;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path3832"
sodipodi:cx="342.81619"
sodipodi:cy="402.56421"
sodipodi:rx="8.361371"
sodipodi:ry="8.6599913"
d="m 351.17756,402.56421 c 0,4.78278 -3.74351,8.65999 -8.36137,8.65999 -4.61786,0 -8.36137,-3.87721 -8.36137,-8.65999 0,-4.78278 3.74351,-8.65999 8.36137,-8.65999 4.61786,0 8.36137,3.87721 8.36137,8.65999 z"
transform="matrix(1.335243,0,0,1.335243,-74.314347,-14.314183)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

217
controller.svg Normal file
View File

@ -0,0 +1,217 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="New document 1">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4"
inkscape:cx="418.97372"
inkscape:cy="520"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1436"
inkscape:window-height="882"
inkscape:window-x="0"
inkscape:window-y="14"
inkscape:window-maximized="0" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#e0cece;fill-opacity:1;fill-rule:nonzero;"
id="rect3050"
width="600"
height="259.28571"
x="67.14286"
y="400.93362"
ry="1.9140695" />
<rect
style="fill:#000000;fill-opacity:1;fill-rule:nonzero"
id="rect3052"
width="561.42859"
height="199.28572"
x="85.714287"
y="442.36218"
ry="1.9140695" />
<rect
style="fill:#e0cece;fill-opacity:1;fill-rule:nonzero;"
id="rect3084"
width="169.28572"
height="58.57143"
x="262.14285"
y="556.64789"
ry="1.9140695" />
<g
id="g3094">
<rect
ry="1.9140695"
y="497.36218"
x="150"
height="115"
width="45.714287"
id="rect3090"
style="fill:#e0cece;fill-opacity:1;fill-rule:nonzero" />
<rect
transform="matrix(0,1,-1,0,0,0)"
style="fill:#e0cece;fill-opacity:1;fill-rule:nonzero"
id="rect3092"
width="45.714287"
height="115"
x="532.00507"
y="-230.35715"
ry="1.9140695" />
</g>
<g
id="g3072">
<rect
ry="1.9140695"
y="548.79077"
x="457.85715"
height="65.714287"
width="65"
id="rect3054"
style="fill:#e0cece;fill-opacity:1;fill-rule:nonzero" />
<path
transform="translate(-75,0)"
d="m 592.14287,582.36218 a 26.428572,26.428572 0 1 1 -52.85715,0 26.428572,26.428572 0 1 1 52.85715,0 z"
sodipodi:ry="26.428572"
sodipodi:rx="26.428572"
sodipodi:cy="582.36218"
sodipodi:cx="565.71429"
id="path3060"
style="fill:#d40000;fill-opacity:1;fill-rule:nonzero"
sodipodi:type="arc" />
</g>
<g
id="g3076"
transform="translate(75,0)">
<rect
style="fill:#e0cece;fill-opacity:1;fill-rule:nonzero"
id="rect3078"
width="65"
height="65.714287"
x="457.85715"
y="548.79077"
ry="1.9140695" />
<path
sodipodi:type="arc"
style="fill:#d40000;fill-opacity:1;fill-rule:nonzero"
id="path3080"
sodipodi:cx="565.71429"
sodipodi:cy="582.36218"
sodipodi:rx="26.428572"
sodipodi:ry="26.428572"
d="m 592.14287,582.36218 c 0,14.5961 -11.83248,26.42857 -26.42858,26.42857 -14.59609,0 -26.42857,-11.83247 -26.42857,-26.42857 0,-14.59609 11.83248,-26.42857 26.42857,-26.42857 14.5961,0 26.42858,11.83248 26.42858,26.42857 z"
transform="translate(-75,0)" />
</g>
<rect
style="fill:#000000;fill-opacity:1;fill-rule:nonzero"
id="rect3086"
width="49.285713"
height="18.571428"
x="286.42856"
y="577.36218"
ry="1.9140695"
rx="7" />
<rect
rx="7"
ry="1.9140695"
y="577.36218"
x="358.57141"
height="18.571428"
width="49.285713"
id="rect3088"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero" />
<g
id="g3102"
transform="translate(-0.35713959,-1.0714188)">
<rect
ry="1.9140695"
y="501.64789"
x="152.85715"
height="108.57143"
width="40.714287"
id="rect3098"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero" />
<rect
transform="matrix(0,1,-1,0,0,0)"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero"
id="rect3100"
width="40.714287"
height="108.57143"
x="535.57648"
y="-227.5"
ry="1.9140695" />
</g>
<rect
style="fill:#76745b;fill-opacity:1;fill-rule:nonzero"
id="rect3106"
width="170.71428"
height="28.571428"
x="261.42856"
y="515.93365"
ry="1.9140695"
rx="2" />
<rect
rx="2"
ry="1.9140695"
y="442.36221"
x="261.42856"
height="28.571428"
width="170.71428"
id="rect3108"
style="fill:#76745b;fill-opacity:1;fill-rule:nonzero" />
<rect
style="fill:#76745b;fill-opacity:1;fill-rule:nonzero"
id="rect3110"
width="170.71428"
height="28.571428"
x="261.42856"
y="478.79077"
ry="1.9140695"
rx="2" />
<rect
rx="2"
ry="1.9140695"
y="623.79077"
x="261.42856"
height="17.857143"
width="170.71428"
id="rect3112"
style="fill:#76745b;fill-opacity:1;fill-rule:nonzero" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

133
main.c
View File

@ -21,21 +21,20 @@
volatile uint32_t jiffies = 0; // Elapsed time in deciseconds (tenths of a second) volatile uint32_t jiffies = 0; // Elapsed time in deciseconds (tenths of a second)
volatile bool tick = false; // Set high when jiffy clock ticks volatile bool tick = false; // Set high when jiffy clock ticks
// Clocks are in deciseconds // Clocks are in deciseconds
uint16_t score_a = 0; int16_t score_a = 0;
uint16_t score_b = 0; int16_t score_b = 0;
int16_t period_clock = -(30 * 60 * 10); int16_t period_clock = PERIOD_DEFAULT;
int16_t jam_duration = -(2 * 60 * 10); int16_t jam_duration = JAM_DEFAULT;
int16_t lineup_duration = (-30 * 10); int16_t lineup_duration = LINEUP_DEFAULT;
int16_t jam_clock = 0; int16_t jam_clock = JAM_DEFAULT;
enum { enum {
SETUP = 0, TIMEOUT = 0,
JAM, JAM,
LINEUP, LINEUP,
TIMEOUT,
KONAMI KONAMI
} state = SETUP; } state = TIMEOUT;
bool setup = true;
// NES Controller buttons // NES Controller buttons
@ -65,17 +64,17 @@ const uint8_t seven_segment_digits[] = {
0x7b, 0x60, 0x37, 0x76, 0x6c, 0x5e, 0x5f, 0x70, 0x7f, 0x7e 0x7b, 0x60, 0x37, 0x76, 0x6c, 0x5e, 0x5f, 0x70, 0x7f, 0x7e
}; };
const uint8_t setup_digits[] = {
// [ = ]
0x1b, 0x12, 0x72
};
// keyed by state // keyed by state
const uint8_t indicator[] = { const uint8_t indicator[] = {
// '' J L T - // t, J, L, -
0x00, 0x63, 0x0b, 0x0f, 0x04 0x0f, 0x63, 0x0b, 0x04
}; };
#define max(a, b) ((a)>(b)?(a):(b))
#define min(a, b) ((a)<(b)?(a):(b))
void void
latch() latch()
{ {
@ -154,11 +153,6 @@ write_pclock()
write(0); write(0);
write(0); write(0);
write(0); write(0);
} else if (state == SETUP) {
write(setup_digits[2]);
write(setup_digits[1]);
write(setup_digits[1]);
write(setup_digits[0]);
} else { } else {
write_num(pclk, 4); write_num(pclk, 4);
} }
@ -238,18 +232,38 @@ nesprobe()
void void
update_controller() update_controller()
{ {
static uint8_t last_val = 0; static uint8_t last_held = 0;
static uint32_t last_change = 0; static uint32_t last_change = 0;
static uint32_t last_typematic = 0; static uint32_t last_typematic = 0;
uint8_t cur; uint8_t held;
uint8_t pressed; uint8_t pressed;
int typematic = 0;
int inc = 1; int inc = 1;
cur = nesprobe(); held = nesprobe();
pressed = (last_val ^ cur) & cur; pressed = (last_held ^ held) & held;
if (last_val != cur) {
// Set up typematic acceleration
if (last_held != held) {
// Debounce
if (last_change == jiffies) {
return;
}
last_change = jiffies; last_change = jiffies;
last_val = cur; last_typematic = jiffies;
last_held = held;
typematic = 1;
} else if (jiffies > last_typematic) {
last_typematic = jiffies;
if (jiffies - last_change < 6) {
typematic = 0;
} else if (jiffies - last_change < 40) {
typematic = 1;
} else if (jiffies - last_change < 80) {
typematic = 10;
} else {
typematic = 20;
}
} }
if (pressed == konami_code[konami_pos]) { if (pressed == konami_code[konami_pos]) {
@ -262,12 +276,22 @@ update_controller()
} else if (konami_pos > 3) { } else if (konami_pos > 3) {
return; return;
} }
} else if (pressed) {
konami_pos = 0;
} }
// Select means subtract // Select means subtract
if (cur & BTN_SELECT) { if (held & BTN_SELECT) {
inc = -1; inc = -1;
} }
if (setup && (held & BTN_START) && (pressed & BTN_SELECT)) {
jam_duration += 30 * 10;
if (jam_duration > -60 * 10) {
jam_duration = -120 * 10;
}
jam_clock = jam_duration;
}
if ((pressed & BTN_A) && ((state != JAM) || (jam_clock == 0))) { if ((pressed & BTN_A) && ((state != JAM) || (jam_clock == 0))) {
state = JAM; state = JAM;
jam_clock = jam_duration; jam_clock = jam_duration;
@ -283,27 +307,31 @@ update_controller()
jam_clock = 1; jam_clock = 1;
} }
if ((state == TIMEOUT) || (state == SETUP)) { if ((held & BTN_START) && (state == TIMEOUT)) {
uint8_t v = pressed; if (held & BTN_UP) {
period_clock -= typematic * 10;
}
if (held & BTN_DOWN) {
period_clock += typematic * 10;
}
if ((jiffies - last_change > 10) && (last_typematic < jiffies)) { period_clock = min(period_clock, 0);
v = cur; period_clock = max(period_clock, -90 * 30 * 10);
last_typematic = jiffies; } else {
// Score adjustment and clock adjustment are mutually exclusive
if (held & BTN_LEFT) {
score_a += typematic * inc;
score_a = max(score_a, 0);
} }
if (v & BTN_UP) {
period_clock -= 10; if (held & BTN_RIGHT) {
} score_b += typematic * inc;
if (v & BTN_DOWN) { score_b = max(score_b, 0);
period_clock += 10;
} }
} }
if (pressed & BTN_LEFT) { if (state != TIMEOUT) {
score_a += inc; setup = false;
}
if (pressed & BTN_RIGHT) {
score_b += inc;
} }
} }
@ -314,16 +342,6 @@ update_controller()
*/ */
void
setup()
{
// The TLC5941 required some setup.
// The TPIC doesn't.
// Hooray.
PORTB = 0xff;
}
void void
loop() loop()
{ {
@ -337,8 +355,6 @@ loop()
} }
switch (state) { switch (state) {
case SETUP:
break;
case JAM: case JAM:
case LINEUP: case LINEUP:
if (period_clock) { if (period_clock) {
@ -346,7 +362,7 @@ loop()
} }
// fall through // fall through
case TIMEOUT: case TIMEOUT:
if (jam_clock) { if (jam_clock && !setup) {
jam_clock += 1; jam_clock += 1;
} }
} }
@ -358,8 +374,7 @@ loop()
int int
main(void) main(void)
{ {
init(); avr_init();
setup();
for (;;) { for (;;) {
loop(); loop();
} }