; Dots tunnel, version 5 : la v4 garde ses valeurs dans des registres, lit x
; dans deux tables et déroule les 16 points d'un anneau.
; Les tables viennent du fichier sauvé par le BASIC.
; rasm tunnel-v5-fr.asm -o tunnel-v5 écrit tunnel-v5.sna.

        buildsna
        bankset 0

YLO = #4000                     ; la carte mémoire de tables.bas
YHI = #4100
CENTER = #4200
SHAPE = #4A00
XMASK = #7A00                   ; construite par make_x_tables
COLX = #7B00
LIST_C0 = #7C00                 ; les octets allumés dans l'écran #C000
LIST_80 = #7E00                 ; et dans l'écran #8000
STACK = #1000                   ; la vraie pile, sous le code
SCREEN_C0 = #C0                 ; poids forts des deux écrans
SCREEN_80 = #80
RINGS = 16
DOTS = 16

; Couleurs du Gate Array (#40 + numéro de la couleur matérielle)
BLACK = #54
BLUE = #44
SKY_BLUE = #57
BRIGHT_WHITE = #4B
RED = #4C

        macro border,colour
        ld bc,#7F10             ; Gate Array : choisit la bordure
        out (c),c
        ld a,{colour}
        out (c),a
        mend

        org YLO
        incbin "TABLES.BIN",128         ; sans le header AMSDOS de 128 octets

        org #1000
        run start

start:  di
        ld sp,STACK
        call init
        call make_x_tables
        ld hl,LIST_C0
        ld a,SCREEN_C0
        call fill_list
        ld hl,LIST_80
        ld a,SCREEN_80
        call fill_list

main:   ld sp,STACK             ; le tracé a laissé SP dans une liste
        call wait_vbl
        ld a,(work)
        call show_screen        ; affiche l'écran qu'on vient de dessiner
        ld a,(work)
        xor SCREEN_C0 ^ SCREEN_80
        ld (work),a             ; et dessine dans l'autre
        ld iyh,a                ; IYH : l'écran de travail, pour chaque point
        ld a,(erase_list)
        xor #02                 ; #7C <-> #7E : la liste suit l'écran
        ld (erase_list),a

        border RED              ; début du travail

        ld a,(erase_list)       ; efface les octets allumés deux images plus tôt
        ld h,a
        ld l,0
        ld sp,hl                ; SP parcourt la liste
        ld c,0
        repeat 256              ; un pop par adresse, sans compteur
        pop hl
        ld (hl),c
        rend
        ; SP pointe maintenant sur la fin de la liste : le push de chaque point la
        ; remplit de nouveau, en descendant. Jusqu'à `jp main`, ni call ni autre push.

        ld hl,t
        inc (hl)
        ld a,(hl)
        neg
        and 63
        ld (depth),a            ; anneau 0 : d = -t and 63

        ld iyl,RINGS            ; IYL : anneaux restants
ring_loop:
        ld a,(depth)            ; IXL, IXH : le centre de l'anneau d
        ld c,a
        and #3C                 ; (d >> 2) * 4
        ld l,a
        ld h,0
        add hl,hl
        add hl,hl
        add hl,hl
        add hl,hl
        add hl,hl               ; (d >> 2) * 128
        ld a,(t)
        add a,c
        and #FC
        rrca                    ; phase * 2, phase = ((t + d) >> 2) and 63
        or l
        ld l,a
        ld a,h
        add a,CENTER>>8
        ld h,a
        ld a,(hl)
        ld ixl,a
        inc hl
        ld a,(hl)
        ld ixh,a

        ld a,c                  ; B = PENS(d >> 4)
        rlca
        rlca
        rlca
        rlca
        and 3
        ld e,a
        ld d,0
        ld hl,PENS
        add hl,de
        ld b,(hl)

        ld a,(t)                ; DE = SHAPE + d * 128 + rot * 32
        and #0C
        rlca
        rlca
        rlca                    ; rot * 32, rot = (t >> 2) and 3
        ld e,a
        ld a,c
        srl a                   ; A = d >> 1, retenue = d and 1
        jr nc,even_depth
        set 7,e                 ; + 128 si d est impair
even_depth:
        add a,SHAPE>>8
        ld d,a

        repeat DOTS             ; les 16 points, l'un après l'autre
        ld a,(de)               ; dx
        inc e                   ; les 32 octets d'un anneau restent dans une page
        add a,ixl               ; x = centre x de l'anneau + dx
        ld l,a
        ld h,XMASK>>8
        ld a,(hl)               ; bits du pixel dans son octet
        and b                   ; dans l'encre
        ex af,af'               ; mis de côté pour la fin
        inc h                   ; COLX = XMASK + 256
        ld c,(hl)               ; colonne d'octet : x >> 2
        ld a,(de)               ; dy
        inc e
        add a,ixh               ; y = centre y de l'anneau + dy
        ld l,a
        ld h,YLO>>8
        ld a,(hl)
        add a,c                 ; poids faible de la ligne + colonne
        ld c,a
        inc h                   ; YHI = YLO + 256
        ld a,iyh
        adc a,(hl)              ; + poids fort, retenue et début de l'écran
        ld h,a
        ld l,c
        ex af,af'               ; de nouveau les bits du pixel
        or (hl)                 ; garde les autres pixels de l'octet
        ld (hl),a
        push hl                 ; note l'adresse pour l'effacement
        rend

        ld a,(depth)            ; anneau suivant : d + 4
        add a,4
        and 63
        ld (depth),a
        dec iyl
        jp nz,ring_loop

        border BLACK            ; fin du travail
        jp main

; init : mode 1, ROM coupées, palette.
init:   ld bc,#7F8D
        out (c),c
        ld hl,palette
        ld e,0
next_ink:
        out (c),e               ; E < #10 : choisit l'encre E
        ld a,(hl)
        out (c),a               ; #40 à #5F : lui donne sa couleur
        inc hl
        inc e
        ld a,e
        cp 4
        jr nz,next_ink
        ld a,#10                ; choisit la bordure
        out (c),a
        ld a,BLACK
        out (c),a
        ret

; make_x_tables : pour chaque x de 0 à 255, XMASK(x) = les bits du pixel
; x and 3 dans son octet (#88 décalé x and 3 fois à droite), COLX(x) = x >> 2.
make_x_tables:
        ld hl,XMASK
next_x: ld a,l
        and 3
        ld b,a
        ld a,#88                ; bits du pixel de gauche
        jr z,mask_done
shift_mask:
        srl a
        djnz shift_mask
mask_done:
        ld (hl),a
        inc h                   ; COLX
        ld a,l
        srl a
        srl a
        ld (hl),a
        dec h
        inc l
        jr nz,next_x
        ret

; fill_list : remplit la liste HL de 256 fois l'adresse A * 256, le premier
; octet d'un écran encore noir : le premier effacement n'y change rien.
fill_list:
        ld b,0
next_entry:
        ld (hl),0
        inc hl
        ld (hl),a
        inc hl
        djnz next_entry
        ret

; show_screen : le CRTC affichera l'écran de poids fort A (#C0 ou #80)
; à partir de la prochaine image.
show_screen:
        ld bc,#BC0C             ; CRTC : choisit le registre 12
        out (c),c
        rrca
        rrca
        and #30                 ; #C0 -> #30, #80 -> #20
        inc b                   ; #BD : écrit dans le registre
        out (c),a
        ret

; wait_vbl : attend le début du retour de trame.
wait_vbl:
        ld b,#F5                ; PPI port B, bit 0 = VBL
vbl_end:
        in a,(c)
        rra
        jr c,vbl_end            ; déjà dans la VBL : attend sa fin
vbl_start:
        in a,(c)
        rra
        jr nc,vbl_start
        ret

palette:
        db BLACK,BLUE,SKY_BLUE,BRIGHT_WHITE
PENS:   db #FF,#0F,#F0,#F0      ; 4 pixels d'encre 3, 2, 1, 1 selon d >> 4

work:   db SCREEN_C0            ; poids fort de l'écran de travail
erase_list: db LIST_C0>>8       ; poids fort de sa liste
t:      db 0
depth:  db 0
