OOMA

1147 words ~ 5-10 mins

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.


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.