tixyboot

tixyboot.asm

a 512 bytes bootsector version of tixy.land for the Sanyo MBC-555.

https://github.com/companje/tixy.boot

; tixyboot.asm by Rick Companje, 2021-2022, MIT licence
; a tribute to Martin Kleppe's beautiful https://tixy.land
; as well as a tribute to the Sanyo MBC-550/555 PC (1984)
; which forced me to be creative with code since 1994.
;
; The Sanyo MBC-55x has a very limited ROM BIOS. After some 
; hardware setup by the ROM BIOS a RAM BIOS loaded from
; floppy takes over. This means that we don't have any BIOS
; functions when running our own code from the bootsector. 
;
; The Sanyo has no display mode 13 (not even with the original
; RAM BIOS). It uses a 6845 video chip with three bitmapped 
; graphics planes and is organized as 50 rows by 72 (or 80) columns.
; One column consists of 4 bytes. Then the next column starts.
; After 72 columns a new row starts. A bitmap of 16x8 pixels 
; is made up of 2 columns on row 1 and 2 columns on row 2...
;
; To run this code write the compiled code to the bootsector of a
; Sanyo MBC-55x floppy or use an emulator like MAME.
;
; Add your own visuals by adding your own functions to the fx_table.
;
; t = time  0..255
; i = index 0..255
; x = x-pos 0..15
; y = y-pos 0..15
;
; result: al -15..15 (size and color)
;         al<0 red, al>0 white

org 0
cpu 8086

COLS  equ 72
TOP   equ 9*4*COLS+20*4    ; row=9,col=20
RED   equ 0xf0
GREEN equ 0x08
BLUE  equ 0xf4

effect_timeout equ 30      ; every 30 frames another effect
isqrt_table    equ 1000    ; available location in code segment

; using dx and bx registers as t,i,x,y variables
%define t dh
%define i dl
%define x bh
%define y bl

jmp setup

sin_table: ;31 bytes, (input -15..15 index=0..31)
    db 0,-3,-6,-9,-11,-13,-15,-15,-15,-15,-13,-11,-9,-6,-3,
    db 0, 3, 6, 9, 11, 13, 15, 15, 15, 15, 13, 11, 9, 6, 3,0  
    ; tried to mirror the second line of the sine table with code 
    ; but would take a same of amount of bytes

fx_table:      ; the 'effects' table: 8 bytes, overwriting the 'Sanyo1.2' tag
    db fx0,fx1,fx2,fx3,fx4,fx5

fx1: ; y+t
    mov al,y
    add al,t
    ret

fx0: ; xor
    mov al,x
    xor al,y
    or al,t
    sub al,7
    ret

fx2: ; sin(x+y+t)
    mov al,x
    add al,y
    add al,t
    call sin
    ret

fx3: ; bitmap_data[i+t]
    push bx
    mov al,i
    add al,t
    mov bx,bitmap_data
    xlat
    pop bx
    ret

fx4: ; ((y-x)*-8)+t
    mov al,y
    sub al,x
    mov cl,-8
    mul cl
    call limit
    add al,t
    ret

fx5: ; sin(sqrt(x^2+y^2))-t)
    mov al,i   ; isqrt_table[i] = sqrt(x^2+y^2)
    push bx
    mov bx,isqrt_table
    xlat
    pop bx
    sub al,t
    call sin
    ret

sin: ; sine function
    call wrap
    push bx
    add al,15 ; sin(-15) = sin_table[0]
    mov bx,sin_table
    xlat 
    pop bx
    ret

wrap: ; while (al>15) al-=15; while (al<-15) al+=15
    cmp al,15
    jg .sub16
    cmp al,-15
    jl .add16
    ret
  .sub16:
    sub al,31
    jmp wrap
  .add16:
    add al,31
    jmp wrap

limit: ; if (al>15) al=15; else if (al<-15) al=-15;
    cmp al,15
    jg .pos16
    cmp al,-15
    jnl .ret
    mov al,-15
    ret
  .pos16:
    mov al,15
  .ret:
    ret

calc_isqrt_xx_yy: ; isqrt_table[i] = sqrt(x^2+y^2)
    push dx
    push di
    mov di,isqrt_table      ; di=isqrt_table[0]
    add di,dx               ; di+=i
    mov al,x
    inc al
    mul al                  ; x*x
    xchg ax,cx
    mov al,y
    inc al
    mul al                  ; y*y
    add ax,cx               ; + 
  .isqrt:  ; while((L+1)^2<=y) L++; return L
    xchg cx,ax              ; cx=y
    xor ax,ax               ; ax=L=0
  .loop:
    inc ax
    push ax
    mul ax
    cmp ax,cx
    pop ax
    jl .loop
    dec ax
  .end_isqrt:
    mov [di],al             ; store al
    pop di
    pop dx
    ret

setup:                      ; starting point of code

    ;no need to clear the screen. ROM BIOS does this already.

    ;set ds and es segments to cs
    push cs
    pop ds                  ; ds:si in code segment
    push cs
    pop es                  ; es:di in code segment

    ; generate 16x8 bitmap data for 16 sizes of dots.
    ; Because the dots are symmetric we can save at least
    ; 97 bytes by mirroring the left-top corner 3 times

    call generate_chars

    xor bp,bp               ; start with effect 0
    xor dx,dx               ; t=i=0 (clear time and index)

draw:
    mov di,TOP              ; left top corner to center tixy
dot:
    push dx
    mov al,i                ; al=index
    xor ah,ah               ; ah=0
    mov cl,16
    div cl                  ; calculate x and y from i
    xchg ax,bx              ; bh=x, bl=y
    pop dx

    ;on the first frame calc sqrt table for every i
    ;reusing the i,x,y loop here. this saves some bytes.
    or t,t
    jnz .cont
    call calc_isqrt_xx_yy
  .cont:

    push bp
    push bx
    xchg bx,bp
    mov bp,[bx+fx_table]
    and bp,0xff             ; effect function needs to fit in one byte to save 8 bytes
    pop bx
    call bp                 ; call the effect function
    pop bp

draw_char_color:
    cmp al,0
    pushf
    jge .red
    neg al
  .red:
    mov cx,RED << 8              ; ch=0xf0, cl=0
    call draw_char
    popf
    jge .green_blue
    xor al,al               ; if negative then just red so clear (al=0) green and blue
  .green_blue:
    mov ch,GREEN
    call draw_char
    mov ch,BLUE
    call draw_char
  .next:  
    inc i                   ; i++
    add di,8         
    cmp x,15
    jl dot                  ; next col
    add di,4*COLS       
    add di,160
    cmp y,15
    jl dot                  ; next line
    inc t
    cmp t,effect_timeout
    jb draw                 ; next frame
    inc bp                  ; inc effect
    xor t,t                 ; reset time
    cmp bp,6
    jl draw                 ; next effect
    xor bp,bp               ; reset effect
    jmp draw

draw_char:                  ; es:di=vram (not increasing), al=char 0..15, destroys cx
    push ax
    push di

    push cx
    pop es                  ; es=bp (color channel now cx)
    push cs
    pop ds                  ; ds=cs

    mov cx,4
    push cx
    push cx

    and al,15               ; limit al to 15
    cbw                     ; ah=0

    shl al,cl               ; al*=16
    add ax,bitmap_data
    xchg si,ax              ; si = source address of rendered bitmap char

    pop cx                  ;cx=4
    rep movsw
    add di,4*COLS-8
    pop cx                  ;cx=4
    rep movsw

    pop di                    
    pop ax
    ret

generate_chars:
    mov di,bitmap_data      ; dest address of render data
    xor bh,bh
  .render_char:
    xor ah,ah
    mov al,bh
    mov cx,4                ; cl is also used below
    mul cl
    mov si,ax
    add si,img
  .render_char_part:        ; input requirement at first time cl=4
    lodsb                   ; use lodsb instead of movsb to keep a copy in al
    stosb                   ; draw in left top nibble
    push bx                 ; save cur x and y
    push cx                 ; cur loop counter (4,3,2,1)
    push cx
    pop bx                  ; bx = counter
    shl bx,1                ; bx *= 2
    push bx                 ; save counter*2 for right bottom
    cmp bx,2                ; skip top line of left bottom nibble
    je .flip_bits
    mov [di+bx+1],al        ; draw in left bottom starting at line 3 instead of 4

  .flip_bits:                 ; flips all bits dropping highest bit
    mov cl,8                ; 8 bits to flip
    xor ah,ah
  .flip_bit:
    mov bx,0x8001           ; bl=1, bh=128  bl doubles, bh halves
    shl bl,cl
    test al,bl
    jz .next_bit
    dec cx
    shr bh,cl
    or ah,bh
    inc cx
  .next_bit:
    loop .flip_bit          ; loop 8 bits for flipping
    mov [di+3],ah           ; draw in right top nibble
    pop bx                  ; bx = counter*2
    cmp bx,2                ; skip top line of right bottom nibble
    je .flip_done
    mov [di+bx+5],ah        ; draw in right bottom starting at line 3 instead of 4
  .flip_done:
    pop cx                  ; restore loop counter
    pop bx                  ; restore x and y
    loop .render_char_part
  .clear_bottom_line:
    add di,7
    xor al,al
    stosb                   ; right bottom
    add di,3
    stosb                   ; left bottom
  .next_char:
    inc bh                  ; next char
    cmp bh,16
    jl .render_char
    ret

img:
    db 0,0,0,0
    db 0,0,0,1
    db 0,0,0,3
    db 0,0,1,3
    db 0,0,3,7
    db 0,0,7,15
    db 0,3,15,31
    db 0,7,31,63
    db 1,15,63,63
    db 3,31,63,63
    db 7,31,63,127
    db 7,31,127,127
    db 7,63,127,127
    db 15,63,127,127
    db 15,63,127,255
    db 31,127,255,255

%assign num $-$$
%warning total num

bitmap_data:                          ; destination for 128 bytes rendered bitmap data

times 368640-num db  0                 ; fill up with zeros until file size=360k