sanyo-mbc-555-cellular-automata
Wolfram Cellular Automata (189 bytes)
GREEN_SEG equ 1c00h ; 0800h in mame
ROWS equ 50
COLS equ 72
LINES equ 4*ROWS
ROW_BYTES equ 4*COLS
PLANE_BYTES equ COLS*LINES
setup:
mask equ setup ; Reuse eight setup bytes after they have executed.
; DS addresses code/data; ES addresses the green VRAM plane.
; 14,400 bytes are visible (576x200 monochrome); 16 KB are addressable.
push cs
pop ds
mov ax,GREEN_SEG
mov es,ax
; Select the RAM page used as the green video plane.
mov al, 5
out 10h, al
; Start with one live pixel in the centre of the first scanline.
seed:
; mov byte [es:4*(COLS/2)],80h
xor si,si
; Fill the 200 visible lines plus one hidden four-line VRAM row
; before performing the first software scroll.
mov bp,LINES+3
rebuild_rule:
mov al,4
; Treat the rule byte directly as eight algebraic rule masks.
; Incrementing it still covers every CA rule, but not in Wolfram order.
xor di,di
mov cx,8
.expand_rule:
; Expand each coefficient bit to a full-byte mask: 0 -> 00h, 1 -> FFh.
shr al,1
sbb ah,ah
mov [di],ah
inc di
loop .expand_rule
draw:
; In Sanyo VRAM four scanlines are interleaved. Adjacent scanlines
; are one byte apart; after the fourth, advance to the next row.
mov di,si
inc di
mov ax,bp
and al,3
jnz .address_step_ready
add di,ROW_BYTES-4
.address_step_ready:
cwd ; AX is 0..3, therefore this clears DX.
; Every path back here leaves CH clear.
mov cl,COLS
.pixel_bytes:
; Read one byte from the current line and the byte to its right.
; SI advances by four because horizontally adjacent bytes are
; separated by the four interleaved scanlines.
mov al,[es:si]
add si,4
mov bl,[es:si]
dec cx
jnz .right_ready
xor bl,bl
.right_ready:
; Shift neighbouring bytes through the carries to construct three
; bit lanes containing the left, centre and right CA neighbours.
mov ah,al
shr dl,1
rcr al,1
mov dl,ah
shl bl,1
rcl ah,1
; Combine the eight neighbourhood masks selected by the active rule.
; The masks contain either 00h or FFh, avoiding a per-pixel rule lookup.
; Evaluate the algebraic normal form in nested (Horner) form.
mov bl,[mask+7]
and bl,ah
xor bl,[mask+6]
and bl,dl
mov dh,[mask+5]
and dh,ah
xor dh,[mask+4]
xor bl,dh
and bl,al
mov dh,[mask+3]
and dh,ah
xor dh,[mask+2]
and dh,dl
xor bl,dh
mov dh,[mask+1]
and dh,ah
xor dh,[mask+0]
xor bl,dh
mov [es:di],bl
add di,4
or cx,cx
jnz .pixel_bytes
; DI now points one row beyond the generated line. Move it back to
; the start of that line so it becomes the source for the next one.
sub di,ROW_BYTES
mov si,di
dec bp
jz .scroll
.line_done:
; Keep every rule for exactly 200 CA lines.
dec byte [line_count]
jnz draw
; Store the next coefficient byte in rebuild_rule's immediate operand.
inc byte [rebuild_rule+1]
mov byte [line_count],200
jmp rebuild_rule
.scroll:
; Scroll four physical scanlines. The newly generated hidden row is
; included at the end of this overlapping copy and lands at the bottom.
mov bp,4
xor di,di
mov si,ROW_BYTES
mov cx,PLANE_BYTES/2
; The boot ROM leaves DF clear, so MOVSW advances through VRAM.
es rep movsw
mov si,PLANE_BYTES-ROW_BYTES+3
jmp .line_done
line_count db 200