Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
452 lines (356 sloc) 8.08 KB
;;;; phosphene
;;;;
;;;; fractal video feedback in the MBR
;;;;
;;;; by: keegan
;;;; greets: dei, mrule
;;;;
;;;; TO RUN:
;;;; nasm -f bin -o phosphene.mbr phosphene.asm
;;;; qemu -hda phosphene.mbr # or boot it for real
;;;; CONSTANTS
; MBR code runs in 16-bit mode at address 0x7C00
bits 16
org 0x7C00
; start of scratch ram
ram_start equ 0x7E00
; address relative to ram_start where we
; store rendered text
rendered_text equ 0x2000
; address of first element of stack, in ss
stack_start equ 0x1000
; start of video RAM
vram_start equ 0xA0000
; VGA palette registers
vga_dac_addr equ 0x3C8
; VESA mode 101h: 640 x 480 x byte
vesa_mode equ 0x101
; Feedback buffers:
; width : 512 pixels
; height: 3 segments of 128 pixels each
;
; The three segments:
; buffer A: 10000 20000 30000
; buffer B: 40000 50000 60000
;
; Use bit ops to find these from fs = 1000 or 4000
width equ 512
height equ 384
; viewport size = 2 log_2 e
; scale by the reciprocal
height_scale equ height * 1000 / 2886
width_scale equ width * 1000 / 2886
; text rendered in mode 13h
text_width equ 64
text_height equ 16
text_num equ 2
text_y equ 60
text_x equ 192
; Color palette:
; Each channel is incremented at a different rate
; For stupid reasons, inc_red is rounded to a multiple of 4
inc_red equ 4 ; u8 red increment per palette entry
inc_green equ 5 ; u8 green "
inc_blue equ 6 ; u8 blue "
;;;; GLOBAL VARIABLES
; ds, ss = scratch RAM
; fs = initial read segment per frame
;
; bp = frame counter
; si = 0
;;;; ENTRY POINT
; set up segments
xor ax, ax
mov ds, ax
mov es, ax
; set up stack
mov ax, ram_start >> 4
mov ss, ax
mov sp, stack_start
; use mode 13h temporarily, for rendering text
mov ax, 0x13
int 0x10
mov bx, 0x0F
mov ah, 0x0E
mov si, text
load_text:
lodsb
int 0x10
cmp al, 0xAA ; stop at end-of-MBR marker
jne load_text
; save rendered text to RAM
push ds
push vram_start >> 4
pop ds
xor si, si
mov di, ram_start + rendered_text
mov cx, 320*text_num*text_height / 2
rep movsw
pop ds
; switch back to text mode
; going directly from 13h to 101h upsets
; some non-emulated hardware
xor ax, ax
int 0x10
; get info for VESA mode 101h
mov ax, 0x4F01
mov cx, vesa_mode
push cx
mov di, ram_start
int 0x10
; enter the VESA mode
mov ax, 0x4F02
pop bx
int 0x10
; compute 64 / window_granularity
; VESA call successful => ah = 0
mov al, 64
xor dx, dx
div word [di+4]
push ax ;; MUST BE BOTTOM PUSH
; set up a palette
; we assume the VESA mode has a VGA-compatible DAC
mov dx, vga_dac_addr
xor al, al
out dx, al
inc dx
; 6-bit RGB values in al, bh, ch
; cx used for termination too
xor cx, cx
palette:
; worth saving al due to short operands
push ax
out dx, al
mov al, bh
out dx, al
mov al, ch
out dx, al
pop ax
add al, inc_red >> 2
add bx, inc_green << 6
add cx, inc_blue << 6
jnz palette
; initialize the FPU with some constants
fninit
mov word [di], height_scale
fild word [di]
mov byte [di], width_scale
fild word [di]
; initialize frame counter and segments
xor bp, bp
; select first buffer for reading
push 0x1000
pop fs
;;;; MAIN LOOP
main_loop:
; restore clobbered ds
push ss
pop ds
;;;; TEXT BLIT
; draw text into the read buffer
mov ax, fs
add ah, 0x10
mov es, ax
mov si, rendered_text
test bp, 0x400
jz text_first_message
; draw the second message sometimes
add si, text_height * 320
text_first_message:
mov di, width * text_y + text_x
text_blit:
; cx cleared by previous loop
mov cl, text_width
text_blit_row:
lodsb
add [es:di], al
inc di
loop text_blit_row
add si, 320 - text_width
add di, width - text_width
cmp di, width * (text_y + text_height)
jb text_blit
;;;; FEEDBACK
xor si, si
xor di, di
; initialize write segment register
mov ax, fs
xor ah, 0x50
mov es, ax
; push frame count to the FPU stack and scale
; by width (arbitrary, convenient)
mov [si], bp
fild word [si]
fdiv st1
; FPU stack: t h w
fld st0
fcos
fxch
fldl2e ; rel. period of k control pt.
fmul
fcos
; Move control point in a polar flower:
; j = cos(t)
; k = cos(log_2(e) * t)
;
; stack: j k h w
; offset control point to an interesting region
; center at (-ln(2) + 0i)
; flower radius 0.5
fld1
fadd st0
fdiv st2, st0
fdivp st1, st0
fldln2
fsubp st1, st0
; loop over pixels in the write buffer
mov dx, height
compute_row:
mov cx, width
compute_pix:
pusha
fldl2e ; used to offset viewport center to origin
; stack: o j k h w
mov [si], dx
fild word [si]
fdiv st5
fsub st1
; stack: y o j k h w
mov [si], cx
fild word [si]
fdiv st5
fsub st2
; stack: x y o j k h w
fst st2
fmul st2, st0
fld st1
fmul st0
fsubp st3, st0
; stack: x y (x^2 - y^2) j k h w
fmul
fadd st0
; stack: 2xy (x^2 - y^2) j k h w
fadd st3
fld st2
faddp st2, st0
; stack: 2xy+k (x^2 - y^2)+j j k h w
fldl2e
fadd st2, st0
faddp st1, st0
; stack: (2xy + o) ((x^2 - y^2) + o) j k h w
fmul st5
fistp word [si]
mov dx, [si]
; dx <- scaled (2xy + o)
fmul st3
fistp word [si]
mov bx, [si]
; bx <- scaled ((x^2 - y^2) + o)
; wrap x coordinate
and bh, 0x01
; default color for out-of-bounds pixels is 0
; al is 0 from earlier segment register load
; check bounds for y coordinate
cmp dx, height
jae compute_write
; extract segment from top 2 bits of y
shl dx, 1
shl dh, 4
mov ax, fs
add ah, dh
mov gs, ax
; fetch at offset (y*width + x)
; width = 2**9, shifted 1 already
xor dh, dh
shl dx, 8
add bx, dx
mov al, [gs:bx]
; clamp color to avoid super blinky center regions
cmp al, 0xF0
jae compute_write
; color shift per map iteration, varying over time
mov bx, bp
and bh, 0x0F
add al, bh
compute_write:
mov [es:di], al
popa
; next column, output pixel
inc di
loop compute_pix
; advance write segment when di wraps
test di, di
jnz compute_no_seginc
mov ax, es
add ah, 0x10
mov es, ax
compute_no_seginc:
; next row
dec dx
jnz compute_row
; bump frame counter
inc bp
; discard j, k from FPU stack
fcompp
;;;; DRAW TO SCREEN
; swap feedback buffers
mov ax, fs
xor ah, 0x50
mov fs, ax
mov ds, ax
; access graphics memory through segment es
push vram_start >> 4
pop es
; reset our window into VRAM
; dx is 0 from earlier loop
call setwin
; copy beginning of feedback buffer to
; center of screen
xor si, si
mov di, 48*640 + 64
mov bx, height
draw_row:
mov cx, width
draw_pix:
movsb
; advance the graphics window by 64k when di wraps
test di, di
jnz draw_no_wininc
add dl, [ss:stack_start-2]
push bx
call setwin
pop bx
draw_no_wininc:
loop draw_pix
; end of row: 128 pixels of left/right border
add di, 128
dec bx
; advance read segment when !(row & 0x7F)
test bl, 0x7F
jnz draw_no_seginc
mov ax, ds
add ah, 0x10
mov ds, ax
xor si, si
draw_no_seginc:
test bx, bx
jnz draw_row
jmp main_loop
setwin:
; call the VESA BIOS to set the VRAM window
mov ax, 0x4F05
xor bx, bx
int 0x10
ret
text:
db "I", 3, 0x0D, 0x0A, "io", 0x0D, 0x0A
db "gr:", 0xEB, 0xEE, "i", 0x0D, 0x0A, "mrule"
;;;; PROGRAM END
; MBR required data
padding:
times 446 - ($-$$) db 0xff
partitiontable:
times 64 db 0xff
signature:
db 0x55, 0xaa
; vim: ft=tasm