The winning bootsector of Outline 2005, featuring two images zooming with experimental music in a valid 480bytes Atari bootsector.
Zoom, bootsector ?
Ooma features two images of 16x16 in 4 colours, zooming at 50 frames per second in an area of 256x160 out of the 320x200 of the Atari STf' low resolution.
As for a bootsector, this is the very chunk of data on any storage unit like a floppy disk, memory card, hard disk drive, cd, .... It describes the capacity and name of storage unit. On Atari, each sectors on a storage unit are 512 bytes big and the description in a bootsector takes 32 bytes. That leaves 480 bytes to do something cool. That's not much on a 16bits system where each instruction takes at least 2 bytes.
Zooming, fast
With a rendering area of 256x160, the naive idea would be to compute the zoomed graphic for every pixel.
However, the initial graphics are 16x16. This means we only need to compute 16 zoomed scanlines. Similarly, we can compute 256 zoomed position once and per frame, and reuse these positions to zoom the 16 scanalines horizontally and pick the appropriate scanline to render on screen.
Source code
; ##
; # # #######
; # # ## #
; ###### ### #
; ### # # #### #
; # ### # ### ##
; #### ## ## # #
; # # # # #
; #### # ####
; ## # ## #
; ## ### ## ####
; # #### # # #
; # #### ## # ###
; # ### # # # # #
; # # # ## # ##
; ####### ######
;
; ooma bootsector
;
; Mathieu 'P01' HENRI
; http://www.p01.org/
;
; #1 at Outline 2oo5
; notices:
;
; the sprites are in 16x16x2bpl and
; both have their own palette, thus
; each sprites takes 16*4+4*2 = 72b
;
; the rendering area is 256x160 big
; put some usefull addresses in
; a6 = palette
; a5 = ym2149
; a1 = current sprite index + zoom LUT
move.w #$8240,a6
move.w #$8800,a5
move.w #$200,a1
; start with sprite #0
clr.l -2(a1)
; sets the colour 15 to white
not.w 30(a6)
; display the header
pea title(pc)
move.w #9,-(sp)
trap #1
addq.l #6,sp
; set the current level of zoom
clr.l d0
; 15 is often used since the sprites are 16x16
moveq #15,d2
main_loop:
; decrease the zoom ratio
sub.b #4,d0
bcc.s no_carry
; move the center of the zoom
add.b #$47,-1(a1)
; swap the current sprite ( 72 or 0 )
eori.w #72,(a1)
no_carry:
; pick the current sprite -> a2
lea sprite(pc),a2
adda.w (a1),a2
; set the colors of the sprite
move.l (a2)+,(a6)
move.l (a2)+,4(a6)
; pick the zoomed position of the center
move.l d0,d1
mulu.w -2(a1),d1
; precalculate 256 consecutive zoomed
; positions and store them in 2(a1)
move.w #255,d7
precalc_scale:
sub.w d0,d1
move.w d1,d5
lsr.w #8,d5
and.w d2,d5
move.b d5,2(a1,d7.w)
dbf d7,precalc_scale
; precalculate the zoom for the 16 scanlines
; of the current sprite and store them in $600
move.w #$600,a3
moveq #60,d6
precalc_scanline_y:
; pick the long ( aka the current scanline ) of the sprite
move.l (a2,d6.w),d1
; calculate the zoomed scanlines of 256 pixels ( = 16 longs )
move.w #255,d7
move.w d2,d5
precalc_scanline_loop_x:
; pick the current zoomed position in
; the LUT computed in "precalc_scale:"
move.b 2(a1,d7.w),d4
; test the corresponding bit on each
; of the 2bpl of the sprite and set it
; in our temporary long accordingly
btst d4,d1
beq.s firstBitplaneDone
bset d5,d3
firstBitplaneDone:
swap d1
swap d3
btst d4,d1
beq.s secondBitplaneDone
bset d5,d3
secondBitplaneDone:
swap d3
swap d1
; next pixel
subq #1,d5
bpl.s drawDone
; once 16 pixels are processed,
; we stack the long in $600
move.l d3,(a3)+
moveq #0,d3
drawDone:
and.w d2,d5
dbf d7,precalc_scanline_loop_x
; next scanline
subq #4,d6
bpl.s precalc_scanline_y
; pick the address of the screen, and shift to 32,20
; that is (160-256/2),(100-160/2)
move.l $44e.w,a0
adda.w #16080-64-80*160,a0
move.w #159,d6
loop_y:
; browse the LUT computed in "precalc_scale:"
; to pick the appropriate (pre)zoomed scanline
moveq #24,d1
add.b 50(a1,d6.w),d1
lsl.w #6,d1
move.w d1,a3
; display the (pre)zoomed scanline
move.w d2,d7
loop_x:
move.l (a3)+,(a0)
addq #8,a0
dbf d7,loop_x
adda.w #32,a0
dbf d6,loop_y
music_rout:
; channel A = tone+noise
; channel B = tone
; channel C = mute
move.w #$7f4,d5
movep d5,(a5)
; get two pseudo random numbers
move.w $468.w,d5
move.b d0,d5
lsr.w #2,d5
move.w d5,d6
lsr.w #1,d6
eori.b #149,d6
asr.w #2,d5
andi.b #63,d5
move.b (a2,d5.w),d5
eor.b d6,d5
; Channel B fine tone
move.b #2,(a5)
move.b d5,2(a5)
; Channel B volume
move.b #9,(a5)
eor.b d2,d5
move.b d5,2(a5)
; Channel A fine tone
movep d6,(a5)
; Channel A rough tone
move.w #$104,d6
movep.w d6,(a5)
; Channel A volume
move.w #$80b,d6
movep.w d6,(a5)
cmp.b #57,$fffffc02.w
bne main_loop
kill_music:
; channels A,B,C = mute
move.w #$7ff,d5
movep d5,(a5)
; reset the 4 first and the 15th colors and exit
move.l #$07770700,(a6)+
move.l #$03450770,(a6)
clr.w 26(a6)
rts
title:
dc.b 27,"Y",32+1,32+8,"ooma | p01 | Outline 2oo5",0
even
sprite:
; Charly's pretty skin tones
dc.l $01010420,$06410762
; Hello Charly!
dc.l %01001111010011001100000001100011
dc.l %01010111100010001101000000000111
dc.l %11000111110011001001000000010011
dc.l %01001111111001111100000000000000
dc.l %10011111110110000100000000000000
dc.l %10101111110010100110000000011000
dc.l %00001111110000100000000000000000
dc.l %11111111111111110000000000000000
dc.l %00110111111100100000100000000000
dc.l %11011111111101011100001000000100
dc.l %01101111111100100100000000000101
dc.l %00101111111110101110010000000001
dc.l %11110011111110011001000000000000
dc.l %10011011111000110000100000000000
dc.l %10100111110011010000000000011110
dc.l %00011111100100101100000000100001
sprite2:
; Buzzy Bee deep tones
dc.l $01010013,$02360677
; Bzzz
dc.l %00001111000111000000000001000000
dc.l %11101111010110111110000001000011
dc.l %00001111000001100010000000000110
dc.l %00001000100001000010000000110100
dc.l %01000010001000000100011000110100
dc.l %10000000010000001010000011001100
dc.l %00011110000000000100000011011011
dc.l %00111100000001000000000000110110
dc.l %10011101100010011100000111101101
dc.l %00001011000000100000001100011011
dc.l %11001010000001001110001000010110
dc.l %00001010001001011000001000100101
dc.l %01001010010000010110001001010101
dc.l %00001010000010000100001110100001
dc.l %00001000000111000000000000000000
dc.l %11111111100111110000000000000000
Feeback
Of course you can find Ooma on Pouet
Other recent projects
There are many experiments and projects like OOMA to discover other here.
- FRONTFEST MOSCOW It was an honour to be invited to Fronfest Moscow 2017 with the little family to give my first workshop; implementing a Twin-stick shooter using ES6 and Canvas, and to continue my CODE🎙ART series of talks + live coding aiming to inspire new web developer artists. on November 18th, 2017
- VOLTRA VOLTRA: Grinding the Universe, a gritty JavaScript demo, winner of the 1024 bytes demo competition at the Assembly 2017. on August 6th, 2017
- BREATHING EARTH Another take on Nadieh Bremer mesmerizing Breathing Earth visualisation, running at 60fps on a 2D Canvas without libraries or frameworks. on June 26th, 2017
- CODING⬢ART AT SCRIPT'17 Script'17 was all kinds of awesome: Great organization, line up, venue, catering, diverse and welcoming. It was a pleasure to talk to you all and LIVE code something useless before the party. Looking forward to see useless things from you all. on January 27th, 2017
- 10 PRINT THEREMIN AT WEB REBELS A lighting talk about labyrinth generation and theremin instrument using the Web Audio API in 219 bytes presented at Web Rebels 2016. on June 2nd, 2016
- WOLF1K The idea of this entry for the JS1K contest was to do the impossible: a 1K remake of the famous WOLF5K that rocked the final edition of the5K. It does not feature guns, evil grins and violence for in WOLF1K there is no room for guns or any form of violence. on September 10th, 2010
- NEJA My first JavaScript demo, presented at the Assembly 2005 where it ranked #4 on July 30th, 2005
- FIRE Warming up with a 32 bytes fire effect for MSDOS after a few years break from the demoscene for studies and work. on September 30th, 2004
Let's talk
Don't be shy; get in touch by mail, twitter, github, linkedin or pouet if you have any questions, feedback, speaking, workshop or performance opportunity.