MAD TEAM ALGORITHM • ATARI 7800 MARIA 160B • 6502 / MADS
SIN_TABLE / COS_TABLE — 16-bit Fixed Point LUT [0..255]
sin_table (zx)cos_table (zy)zoom envelope
Code — Mad Team Algorithm
; ==========================================; Chess Zoomrotator - inner loop; Atari 7800 / MADS assembler; 160B bitmap mode, 16-bit fixed point; ==========================================; Zero page variables (16-bit)fx= $40 ; 2 bytes - texture Xfy= $42 ; 2 bytes - texture Yfx_= $44 ; 2 bytes - row start Xfy_= $46 ; 2 bytes - row start Yzx= $48 ; 2 bytes - sin_table valzy= $4A ; 2 bytes - cos_table valtmp= $4C ; work bytepixbuf= $4D ; 4-pixel accumulatorcol_x= $4E ; pixel counterrow_y= $4F ; scanline counter; ---- Load zx, zy from LUT ---- ldxanglelda sin_table_lo,x
stazxlda sin_table_hi,x
stazx+1
lda cos_table_lo,x
stazylda cos_table_hi,x
stazy+1
; ---- Centering: fx_ = $10000-(W/2)*(zx+zy); fy_ = $10000-(H/2)*(zx-zy); (precalc or unrolled multiply) ---- jsr calc_center
; ---- Outer loop: scanlines ----lda #0
starow_y@yloop; fx = fx_; fy = fy_ldafx_stafxldafx_+1
stafx+1
ldafy_stafyldafy_+1
stafy+1
lda #0
stacol_x; ---- Inner loop: pixels ----@xloop; THE KEY: checker = (fx XOR fy) hi byte ldafx+1 ; high byte of fxeorfy+1 ; XOR with high byte fyand #$10 ; test checker bitbeq @color_a
; Color B: pixel value %10 in 160B@color_bldapixbufora mask_b,x ; OR in %10 at positionstapixbufjmp @advance
; Color A: pixel value %01 in 160B@color_aldapixbufora mask_a,x ; OR in %01 at positionstapixbuf; ---- Advance fx += zx ----@advanceclcldafxadczxstafxldafx+1
adczx+1
stafx+1
; ---- Advance fy -= zy ----secldafysbczystafyldafy+1
sbczy+1
stafy+1
; ---- Every 4 pixels: write byte ----inccol_xldacol_xand #$03
bne @xloop
ldapixbuf; write 4 packed pixelssta (fb_ptr),y ; to framebufferinylda #0
stapixbuf; reset accumulatorldacol_xcmp #WIDTH
bne @xloop
; ---- Row done: fx_ += zy, fy_ += zx ---- clcldafx_adczystafx_ldafx_+1
adczy+1
stafx_+1
clcldafy_adczxstafy_ldafy_+1
adczx+1
stafy_+1
incrow_yldarow_ycmp #HEIGHT
bne @yloop
rts; ---- Pixel bit masks for 160B packing ----; 4 pixels per byte: bits 76,54,32,10; color A = %01, color B = %10mask_a.byte %01000000,%00010000,%00000100,%00000001
mask_b.byte %10000000,%00100000,%00001000,%00000010
; Mad Team pseudocode (from madteam.atari8.info)zx= sin_table[angle]
zy= cos_table[angle]
; Center on screen (symmetry point)fx_= 65536 - ((W/2) * (zx+zy))
fy_= 65536 - ((H/2) * (zx-zy))
for j = 0 to height-1
fx=fx_fy=fy_for i = 0 to width-1
if (fxxorfy) >> 8 and $10 = 0
Pixels[i,j] = $ff
else
Pixels[i,j] = $00
fx=fx+zxfy=fy-zynext i
fx_=fx_+zyfy_=fy_+zxnext j
; ==========================================; LUT Generation (run once at init); sin_table / cos_table: rotation + zoom; 256 entries, 16-bit signed each; ==========================================; Generated offline or at startup:angle= (6.284/128)*96
dzo= 0.05
zo= dzo*21
ml= 256 ; fixed-point scalefor a = 0 to 255
zo=zo-dzoif (a mod 64) = 0
dzo=-dzo; bounce zoom
sin_table[a] = round((-zo*sin(angle))*ml)
cos_table[a] = round((-zo*cos(angle))*ml)
angle=angle+ 6.284/128
next a
; ---- In ROM as split hi/lo tables: ----sin_table_lo.byte <val0, <val1, ... <val255
sin_table_hi.byte >val0, >val1, ... >val255
cos_table_lo.byte <val0, <val1, ... <val255
cos_table_hi.byte >val0, >val1, ... >val255
; 4 × 256 = 1024 bytes of ROM; Only need 64 unique entries; (90° then invert palettes)
Controls
2$105128
160B Palettes — 12 unique colors + background
16 Unique 2×2 Blocks per Frame (active = used this frame)
Mad Team insight: ANY chess frame = mosaic of max 16 block types. On 7800 Maria: precompute 16 tile patterns, select per DL entry.
Quarter Symmetry — compute 1/4, derive rest
Green = computed quarter • Purple = 90° rotate + XOR invert
Blue/Orange = vertical mirror of top • Only 1/4 CPU work needed!
On 7800: swap palette registers after 90° for free color inversion.