Shattering expectations: The development of Shattered

Introduction:

I've been wanting to create a 3D Rotating pixel object in 128 bytes on 68000 for a while now, but every attempt I tried in the last few years always fell short, until finally this year at Silly Venture 2025 SE I was able to complete this mission.

Visuals:

The basic idea for the visuals is to take a 2D grid of rotating pixels, then squeeze it down for perspective and extract the height of the object from here. The visuals in this case are similar to my Sharded intro from 2021, but with less dots. You can check out the writeup for Sharded for a more step by step explaination for the overall approach.

The trick to get this working in such a small sizebudget on 68000 is to not use any sin/cos for the rotation, but rather using only one simple 2-component oscillator to achieve the sin/cos approximations needed to do the rotation.

This oscillator in D6,D7 can then be used to do the rotation with each of the dots in the grid like so:

; d1 = Y' = (x*c + y*s)
move d5,d0
muls d6,d0
move d4,d1
muls d7,d1
add d0,d1

; d0 = X' = (x*s - y*c)
move d4,d0
muls d6,d0
move d5,d2
muls d7,d2
sub d2,d0

For here we only need scale back to rotated coordinates, subtract the height value and add the center position. Putting this all together will result in the following source for the intro:

; init supervisor (6)
moveq #$20,d7
move.w d7,-(sp)
trap #1

; Setup simple grayscale palette (12)
lea $ffff8240.w,a1
palloop:
move.w d5,(a1)+
add.w #$1111,d5
bcc.s palloop

; Init A000 routines
dc.w $A000 ; 2
movem.l (a0),a1-a4 ; 4 a3=INTIN, a4=PTSIN
sf -6(A0) ; 4 hide cursor

; oscillator startvalue
lsl #3,d7 ; d7 = 32<<3 = 256

; lets set up the screen addresses
move.w #$44e,a6
moveq #7,d3

updateloop:
; clear the screen + disable cursor (10+4)
move.l (a6),a0 ; get screen addr
move.w #200*40,d0 ; 8000 times
.cls
clr.l (a0)+
dbra d0,.cls

;d7=d7+d6/16
move d6,d0
asr.w #4,d0
add d0,d7

;d6=d6-d7/8
move d7,d0
asr.w #3,d0
sub d0,d6

; Grid loop
moveq #-8,d5
yloop:
moveq #-8,d4
xloop:
; d2 = (d4^d5) << 3
move d4,d2
eor d5,d2
add d2,(a3) ; set pixel color
lsl #3,d2

; d1 = sy = (x*c+y*s) >> 7 + d2 + 100
move d5,d0
muls d6,d0
move d4,d1
muls d7,d1
add d0,d1
asr #6,d1
add d2,d1
add #100,d1

; d0 = sx = (x*s-y*c) >> 5 + 160
move d4,d0
muls d6,d0
move d5,d2
muls d7,d2
sub d2,d0
asr #5,d0
add #160,d0

; plot point
movem.w d0/d1,(a4) ; (a4)=x,y
dc.w $A001 ; plot pixel

addq #1,d4
cmp d3,d4
blt xloop
addq #1,d5
cmp d3,d5
blt yloop

; swap screen for double buffering
move.w (a6),$ffff8200.w
eor d3,(a6)
bra.s updateloop

Once the basic visuals were up and running I was able to add a little movement by adding a slight offset to the oscillation start. The final component to make this all work was to add relatively cheap double-buffering by XOR-ing the highest word of the screen-adress register, offseting the screen adress in a multiple of 64k segments.

Amiga version:

Once the Atari ST version was out, I immediately started on the Amiga OCS port. Thanks to gigabates' awesome work trying to implement some of the Tiny Code Christmas challenges on Amiga, I was able to release both a slightly flickering 128 byte version (excl. Amiga header) as well as a version with proper backbuffering.

Conclusion:

The intro was finished and released at Silly Venture 2025 SE, where it won the 128 byte intro competition with highest score of the entire party. Sadly there were not many entries in the competition, but i'm very happy to get the intro finally released.

For more information, you can check out the intros at demozoo: Shattered (2025)

Return to blog overview