Bit banger
Bit banger is my most constrained and minimalistic microcontroller-based demo yet. It won the Oldschool 4k compo at Revision 2011.
- bitbanger-src (Source code, 7.1 kB)
- Linus Akesson - Bit Banger (MP3, 2.2 MB)
What's all this then?
Bit banger is built around an ATtiny15 microcontroller, which runs at 1.6 MHz and has 1 kB of flash ROM and a claustrophobic 32 bytes of RAM. In fact, those 32 bytes are the CPU registers. Only the most basic AVR instructions are supported; they occupy at least two bytes each, and can obviously not be compressed since they are executing from ROM, so a maximum of 512 instructions will fit inside the chip (fewer if static data is needed).
The microcontroller supports interrupts, but they would have been too costly to use. Instead, the entire demo is cycle counted.
At a clock rate of 1.6 MHz, the visible part of each line of the VGA signal swooshes by in exactly 36 clock cycles. The entire line, including horizontal blanking, is 51 clock cycles wide. During this time, both graphics and sound must be generated.
I quickly arrived at the following overall design: Three registers make up a 24-bit frame buffer, organized as a 3x8 grid. Every 60 raster lines, these registers are rotated one bit, to prepare for the next row of the grid. At three different positions along the visible part of the line, the MSB of the corresponding frame buffer register is interpreted as an instruction to either keep or invert the current colour; the resulting colour is then transmitted onto an output pin. At the end of the visible line, black is selected.
In the gaps between these four positions and the two places where the horizontal sync signal is flipped, sound must be generated and emitted. The ATtiny15 luckily has a PWM output that runs on a separate peripheral clock at a staggering 25.6 MHz, which is high enough for 8-bit audio output. Writing a sample to the PWM output is a simple one-cycle instruction; the challenge is to calculate the value of the sample during the remaining clock cycles.
Here's an excerpt from the source code, so you get an idea of what I was up against:
; Lines 0-479 displine: add r6, r10 ; t0 out PORTB, r2 ; t1 adc r7, r11 ; t2 mov r17, r22 ; t3 sbrc r24, 7 ; t4 eor r17, r23 ; t5 out PORTB, r17 ; t6 add r4, r8 ; t7 adc r5, r9 ; t8 brcc 1f ; t9 neg r12 ; t10 1: lsl r20 ; t11 rol r21 ; t12 brvc 1f ; t13 subi r20, 0xfe ; t14 1: in r16, TCNT0 ; t15 sbrc r25, 7 ; t16 eor r17, r23 ; t17 out PORTB, r17 ; t18 and r16, r21 ; t19 sbrc r16, 0 ; t20 neg r14 ; t21 ldi r16, ARPVOL ; t22 cp r7, r3 ; t23 brcs 1f ; t24 neg r16 ; t25 1: add r16, r12 ; t26 add r16, r14 ; t27 sbrc r15, 7 ; t28 eor r17, r23 ; t29 out PORTB, r17 ; t30 subi r16, 256-102 ; t31 out OCR1A, r16 ; t32 subi r27, 1 ; t33 brcc noadvance ; t34 lsl r24 ; t35 adc r24, r31 ; t36 lsl r25 ; t37 adc r25, r31 ; t38 lsl r15 ; t39 adc r15, r31 ; t40 mov r27, r1 ; t41 displine_back: out PORTB, r2 ; t42 sbrc r26, GF_INJ_FLICKER ; t43 subi r23, COLOURLSB ; t44 ldi r16, HSYNC ; t45 out PORTB, r16 ; t46 subi r18, 1 ; t47 sbc r19, r31 ; t48 brcc displine ; t49 rjmp blanking ; t50 noadvance: DE4 ; t36 rjmp displine_back ; t40
The peculiar indentation signifies that there are two things going on: The graphics output in the left column and the sound generation in the right column. Pixels are emitted at times t6, t18 and t30. If we should advance to the next row in the grid, the frame buffer registers are rotated at t35 through t41; otherwise we branch down to noadvance, delay for 4 clock cycles, and then jump back. Cycle counting at its finest. In the sound column, we see two 16-bit oscillators being updated (phase is accumulated in r7:r6 and r5:r4, frequency is taken from r11:r10 and r9:r8. White noise is generated by means of a linear feedback shift register in r21:r20. One of the oscillators has a variable pulse width, which is kept in r3.
Vertical blanking
Outside the visible area, 45 raster lines make up the vertical blanking space. During these lines, horizontal (and vertical) sync pulses must still be generated, but since there are no visible pixels we can afford the luxury of generating the sound in a subroutine, rather than inline. Hence, out of 51 clocks, 25 are spent in the sound subroutine, and the rest can be used freely to prepare the next frame and to perform music playback. This work can thus be divided up into at most 45 small code snippets, of no more than 26 cycles each.
In practice, music playback and frame calculation is intermingled; state information from the music player is used as parameters for the visual effects. This saves space and has the added benefit of creating effects that are synced with the music. The music is based around a single 32-step pattern, one byte per step, expressing the bass part and the drums. There is also an arpeggio table with the four chords. The code maintains a current offset into the pattern, which is in fact the lower bits of a 16-bit frame counter for overall demo progress. Other bits in this counter turn glitches on and off.
Most of the glitches are implemented in a semi-controlled fashion, by trashing the registers that will be used during the upcoming frame. In other words, most of them weren't designed as much as discovered. I tried to ensure that the timing of the sync signals would remain perfectly correct no matter how wildly the other parameters would vary. I'm pretty sure I failed somehow, because an assortment of monitors refuse to display the resulting VGA signal.
Analogue features
All of the above creates a very primitive digital output: A stable (well...) VGA signal, 3x8 huge pixels and sound. The microcontroller has five user-controllable I/O pins. Two of them are needed for horizontal and vertical sync, and one is the sound output. The remaining two pins express pixel colour; this allows no more than four colours, one of which must be black.
To improve the situation, the sound PWM doubles as a pixel colour bit as well; however, the VGA signal must be black outside the visible area, even when the sound is playing. A transistor is connected as a poor man's NOR-gate, to mask this signal whenever one of the regular pixel outputs is high. Another transistor, a capacitor, and a couple of resistors are also employed to recombine the pixel colour signals into red, green and blue voltages, because I wished to avoid being stuck with the standard coder colours. The capacitor introduces a low-pass characteristic, which is responsible for the clearly visible horizontal gradient effect.
Reception
Bit banger was a success in the compo, which was somewhat unexpected for such an experimental production. Naturally I was very happy!
Some people claim that I abused the rules for the Oldschool 4k compo, since the ATtiny15 is a modern chip. The other option was the Wild compo, which is typically a catch-all category when a production doesn't fit any other compo. I insist, however, that both the aesthetics of the demo and the technical limitations faced by the programmer are definitely more oldschool than newschool in this case.
Posted Monday 13-Jun-2011 10:23
Discuss this page
Disclaimer: I am not responsible for what people (other than myself) write in the forums. Please report any abuse, such as insults, slander, spam and illegal material, and I will take appropriate actions. Don't feed the trolls.
Jag tar inget ansvar för det som skrivs i forumet, förutom mina egna inlägg. Vänligen rapportera alla inlägg som bryter mot reglerna, så ska jag se vad jag kan göra. Som regelbrott räknas till exempel förolämpningar, förtal, spam och olagligt material. Mata inte trålarna.
Mon 13-Jun-2011 12:58
/Henrik
Mon 13-Jun-2011 18:00
Tue 14-Jun-2011 15:38
Fri 17-Jun-2011 19:30
Fri 17-Jun-2011 21:59
Tue 21-Jun-2011 04:19
Wed 22-Jun-2011 16:08
yeah, i also would really appreciate a recording of this tune! it is excellent!
Mon 27-Jun-2011 21:55
Sun 8-Jan-2012 00:12
Sat 14-Apr-2012 00:04
Ralph Willekes
Thu 26-Apr-2012 22:02
Ralph Willekes
Thu 14-Jun-2012 14:36
Most people like it, even those who have never heard of the words scene, demo or the combination of those words. Thanks for the layout!
Thu 28-Jun-2012 01:03
Mon 10-Sep-2012 12:43
Fri 27-Mar-2015 22:16
Thu 30-Apr-2015 22:29
Surf around, you'll see he sometimes does. I think you'll see that his habit of doing awesome stuff instead of trawling forums that prevents him from replying :)
Joost Markerink
Mon 29-Jul-2019 21:37
Sun 18-Apr-2021 15:54
Best I can tell, the parts are as follows. Use the picture at the start of the video as a reference.
The 3-pin device on the right is a 78L05, with input at the top. The two on the left are generic NPN transistors with base in the middle, collector at the bottom. BC547 would work. The electrolytic capacitors should be fine with anything in the range 1uF to 10µF, and the white rectangular caps are 100nF. The resistor for the LED is around 220Ω, and the long resistors right next to the transistors are 100Ω. All other resistors are 1KΩ. The diode on the right is a 1N4148 or similar.
I haven't verified this in hardware but a quick simulation seems to produce the right colors. Note that the blue signal can exceed 0.7v by quite a bit with these values, this might be why some monitors reject the signal.
Sat 29-May-2021 17:28
Fri 31-Dec-2021 02:44
Those AVR Tiny's can sure do a lot (with a good programmer at the helm)!
Here is another...
https://www.youtube.com/watch?v=j8apFDjGUTQ