Navigation
Home & news
Random page
All pages
Site search:
Databases
Fortune cookies
Haikus
SID themes
Page collections
Blag
Chip music
Chipophone
Games
Hardware projects
Music downloads
Obfuscated programming
Piano music
Sane programming
Scene productions
SID related pages
Software downloads
Video downloads
Featured pages
A Chipful of Love For You
Autosokoban
Beagleboard VGA
Binary Art
Brainfuck
Chipophone
Chopin romance
Craft
Elements of Chip Music
Fratres
GCR decoding on the fly
Hardsync
Kernighan's lever
Klämrisk Hero
Live at LCP 2011
Parallelogram
Phasor
Pipe Logic
Poems for bugs
Rasp64
Reverberations
Sidreloc
Specular Highlight
Spindle
TTY demystified
We learn the nibbles
Vim + ^Z
Zeugma
Don't miss

Spellbound
Forum
Register
Log in
Latest comments
Syndication
RSS feed
Feedback
  • Swedish content
  • Personal content
  • Offensive content

Bit banger

Bit banger is my most constrained and minimalistic microcontroller-based demo yet. It won the Oldschool 4k compo at Revision 2011.

Pouët link

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.

Anonymous
Mon 13-Jun-2011 12:58
Riktigt bra, som alltid! :)

/Henrik
Anonymous
Mon 13-Jun-2011 18:00
This almost sounds like a IDM track by Aphex Twin. And damn, only 32 Bytes ram? Awesome!
Anonymous
Tue 14-Jun-2011 15:38
I am working on a demo of my own based on the ATtiny2313. I almost stopped when i realized that it only goes to 16 MHz. Until I saw you using the ATtiny15 -- WTF?!!?! Okay.. Now I am inspired! :)
Anonymous
Fri 17-Jun-2011 19:30
Any chance you'd post annotated schematics?
Anonymous
Fri 17-Jun-2011 21:59
Awesome. If anyone wanted to complain about using a modern chip, I'd say the fact that you are running it at merely 1.6Mhz without any support chips at all should quell any disgruntlement. Very cool.
Anonymous
Tue 21-Jun-2011 04:19
Do you think you could upload a recording of this? I think it would make a great ringtone for my cell phone.
Anonymous
Wed 22-Jun-2011 16:08
Do you think you could upload a recording of this? I think it would make a great ringtone for my cell phone.

yeah, i also would really appreciate a recording of this tune! it is excellent!
Anonymous
Mon 27-Jun-2011 21:55
Release an album of your stuff :)
Anonymous
Sun 8-Jan-2012 00:12
Is there an emulator for this? I'd like to play these demos on my pc (including video+audio).
Anonymous
Sat 14-Apr-2012 00:04
Do you have a schematic?
benadski
Ralph Willekes
Thu 26-Apr-2012 22:02
Awesome! I'm so gonna build one myself! For the poster above, the PCB layout is in the package!
benadski
Ralph Willekes
Thu 14-Jun-2012 14:36
Done, works great! :)

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!
Anonymous
Thu 28-Jun-2012 01:03
I think you included a schematic but you forgot to include part values. I would be very much obliged to you if you could include some labeling so I can build this wonderful project.
Anonymous
Mon 10-Sep-2012 12:43
wow... thats all i can say..