; "stripes" - KeyJ's first 256b ZX Spectrum intro
; requires a 48K machine, but can easily be modified for 16K

        org $8000
colors  equ $8100
stripes equ $8200

main
    ; copy the sprites template over to the stripes area
    ld a,8          ; init the outer count
    ld (outer_cnt),a
    ld de,stripes   ; destination addres (incremented permanently)
outer_cl:           ; OUTER LOOP: copy a "fine" block
    ld a,8          ; set loop count to 8
inner_cl:           ; INNER LOOP: create repetitions inside a line
    ld hl,stmpl
    ld bc,8
    ldir
    dec a           ; decrement inner_cnt (A) and loop if nonzero
    jr nz,inner_cl
    ; now shift the template 1 bit right
    scf             ; force carry flag
    ld a,(stmpl+7)  ; rotate last byte of the template 1 bit left
    rla             ; -> since carry is set, insert a 1 at the LSB
    ld (stmpl+7),a  ; -> side effect: carry is cleared, as the MSB is always 0
    ld a,(stmpl+3)  ; rotate middle byte of the template 1 bit left
    rla             ; -> since carry is cleared, insert a 0 at the LSB
    ld (stmpl+3),a
    ; tail of the outer loop
    ld hl,outer_cnt
    dec (hl)
    jr nz,outer_cl

    ; create the color array (two copies of it)
    ld de,colors
    ld hl,coltmpl
    ld bc,32
    ldir
    ld hl,coltmpl
    ld bc,32
    ldir

    ; clear the border
    xor a
    out ($FE),a

frameloop:

    ; apply color (done first to make the startup nicer to look at)
    ld hl,(23672)   ; load FRAMES into HL
    ld a,h          ; load high part
    rra             ; shift rightmost bit into carry
    ld a,l          ; load low part
    rra             ; shift right through carry 4 times
    rra             ; -> this is effectively a 4-bit shift of a 9-bit word
    rra
    rra
    inc a           ; add a small correction factor to match the color offset
    ld h,high colors; source: stripe color buffer
    ld de,$5800     ; destination: attribute memory
    ld b,12         ; counter: 12 double rows
colorloop:
    inc a           ; increase the color offset with each double row
    and $1F         ; discard everything but the first 5 bits
    push bc         ; preserve the coordinates and counters
    ld l,a          ; copy 32 bytes (1 row) [setting up L is sufficient, as
    ld bc,32        ;                        H never changes]
    ldir
    ld l,a          ; copy 32 bytes again (second row)
    ld bc,32
    ldir
    pop bc          ; restore the state
    djnz colorloop  ; loop over

    ; render EVEN scan lines
    ld hl,23672     ; load FRAMES into B
    ld b,(hl)
    ld c,0          ; clear line counter
evenloop:
    ld a,c          ; compute line address
    call lineadr
    ld a,c          ; compute stripe address (but add the time offset to Y)
    add a,b
    call stripeadr
    push bc         ; preserve Y coordinate and time
    ld bc,32        ; copy 32 stripe bytes to the framebuffer
    ldir
    pop bc          ; restore Y and time
    inc c           ; advance to the next line
    inc c
    ld a,c          ; load a copy of the Y coordinate
    cp 192          ; already at 192?
    jr nz,evenloop  ; if not, loop

    ; render ODD scan lines
    ld hl,23672     ; load FRAMES into B
    ld b,(hl)
    ld c,1          ; clear line counter
oddloop:
    ld a,c          ; compute line address
    call lineadr
    ld a,c          ; compute stripe address (but add the time offset to Y)
    add a,b
    call stripeadr
    push bc         ; preserve Y coordinate and time
    ld bc,32        ; copy 32 stripe bytes to the framebuffer
    ldir
    pop bc          ; restore Y and time
    inc c           ; advance to the next line
    inc c
    ld a,c          ; load a copy of the Y coordinate
    cp 193          ; already at 193?
    jr nz,oddloop   ; if not, loop

    ; exit
    ld c,$FE        ; load port address
    ld b,0          ; upper 8 bits = zero -> check all keys
    in a,(c)        ; read the keyboard
    cpl             ; it's inverted, so invert it back
    and $1F         ; mask out irrelevant bits
    jr z,frameloop  ; if no key is pressed, loop

    rst 0
main_end


; load the address of a SCREEN$ bitmap line into DE
; input:  A  - the line number (Y coordinate)
; output: DE - the address
lineadr
    ld e,a          ; save the initial value
    and $07         ; mask the fine scale bits (2:0 = 10:8 in the 16-bit word)
    or $40          ; while we're at it, add the 16k offset
    ld d,a          ; store to the high-order destination word

    ld a,e          ; load the init value again
    and $C0         ; mask the bank bits (7:6)
    rrca            ; shift right 3 bits (7:6 -> 4:3 = 12:11)
    rrca
    rrca
    or d            ; add the assembled low-order word to it
    ld d,a          ; store the low-owrd word

    ld a,e          ; load the initial value another time
    and $38         ; mask the coarse bits (5:3)
    rlca            ; shift left 2 bits (5:3 -> 7:5)
    rlca
    ld e,a          ; store the final low-order value
    ret             ; done.


; load the address of a stripe map line into HL
; input:  A  - the line number (xCCCFFFx; C = coarse; F = fine)
; output: HL - the address
stripeadr
    rrca            ; xxCCCFFF
    rrca            ; FxxCCCFF
    rrca            ; FFxxCCCF
    ld h,a          ;          -> H
    and $C0         ; FF000000
    ld l,a          ;          -> L
    ld a,h          ; FFxxCCCF <- H
    rrca            ; FFFxxCCC
    and $07         ; 00000CCC
    or l            ; FF000CCC
    ld l,a          ;          -> L
    ld a,h          ; FFxxCCCF <- H
    and $01         ; 0000000F
    or high stripes ; 1000000F
    ld h,a          ;          -> H
    ret             ; done. *phew*

code_end

; the template variables used in initialization
coltmpl
    defb $42,$42,$42,$42
    defb $72,$72,$72,$72
    defb $74,$74,$74,$74
    defb $6C,$6C,$6C,$6C
    defb $68,$68,$68,$68
    defb $00,$00,$00,$00
    defb $00,$00,$00,$00
    defb $00,$00,$00,$00
stmpl
    defb $FF,$FF,$FF,$FF
    defb $00,$00,$00,$00
outer_cnt
    defb 8
data_end

end main
