; minion.asm — steer a Minion around the screen.
; From the article at https://thespeccy.com/articles/minion-sprite
;
; Keys: Q = up, A = down, O = left, P = right, SPACE = exit to BASIC.
;
; Build:  pasmo --tapbas minion.asm minion.tap
;
; The sprite is 16x16 pixels = a 2x2 block of attribute cells, moved in
; whole-cell steps. Top cells are bright yellow ink, bottom cells bright
; cyan (the dungarees) — the sprite is drawn to respect the attribute
; grid, the Spectrum way of dodging colour clash.

        org 32768

start:
        ; black out the whole screen: bitmap, attributes, border
        ld hl,16384
        ld de,16385
        ld bc,6143
        ld (hl),0
        ldir
        ld hl,22528
        ld de,22529
        ld bc,767
        ld (hl),0
        ldir
        xor a
        out (254),a

main:
        halt                    ; wait for the 50Hz frame interrupt

        ; move only every 4th frame — a sane walking pace
        ld a,(tick)
        inc a
        ld (tick),a
        and 3
        jr nz,nomove

        call erase              ; wipe the sprite at its old position

        ; P = right (port 0xDFFE, bit 0, active low)
        ld a,0xDF
        in a,(0xFE)
        rra
        jr c,notp
        ld a,(xpos)
        cp 30                   ; sprite is 2 cells wide, so max x = 30
        jr nc,notp
        inc a
        ld (xpos),a
notp:
        ; O = left (port 0xDFFE, bit 1)
        ld a,0xDF
        in a,(0xFE)
        rra
        rra
        jr c,noto
        ld a,(xpos)
        or a
        jr z,noto
        dec a
        ld (xpos),a
noto:
        ; Q = up (port 0xFBFE, bit 0)
        ld a,0xFB
        in a,(0xFE)
        rra
        jr c,notq
        ld a,(ypos)
        or a
        jr z,notq
        dec a
        ld (ypos),a
notq:
        ; A = down (port 0xFDFE, bit 0)
        ld a,0xFD
        in a,(0xFE)
        rra
        jr c,nota
        ld a,(ypos)
        cp 22                   ; 2 cells tall, so max y = 22
        jr nc,nota
        inc a
        ld (ypos),a
nota:
nomove:
        call draw

        ; SPACE = exit (port 0x7FFE, bit 0)
        ld a,0x7F
        in a,(0xFE)
        rra
        jr c,main

        call 0x0DAF             ; ROM CLS tidies the screen up for BASIC
        ret

; ----------------------------------------------------------------------

erase:
        ld de,blank
        call blit
        call attraddr           ; both attribute cells to black
        xor a
        ld (hl),a
        inc hl
        ld (hl),a
        dec hl
        ld bc,32
        add hl,bc
        xor a
        ld (hl),a
        inc hl
        ld (hl),a
        ret

draw:
        ld de,sprite
        call blit
        call attraddr
        ld a,0x46               ; bright yellow ink on black
        ld (hl),a
        inc hl
        ld (hl),a
        dec hl
        ld bc,32
        add hl,bc
        ld a,0x45               ; bright cyan ink — the dungarees
        ld (hl),a
        inc hl
        ld (hl),a
        ret

; copy 16 rows x 2 bytes from DE to the sprite position (two cell rows)
blit:
        call scraddr
        call cell8
        ld a,(ypos)
        inc a
        call scraddr_a
        jr cell8

cell8:
        ld b,8
cell8l:
        push hl
        ld a,(de)
        ld (hl),a
        inc de
        inc l
        ld a,(de)
        ld (hl),a
        inc de
        pop hl
        inc h                   ; next pixel line within the cell
        djnz cell8l
        ret

; HL = bitmap address of cell (xpos, A=cell row): the classic
; 0x4000 + ((y & 0x18) << 8) + ((y & 7) << 5) + x
scraddr:
        ld a,(ypos)
scraddr_a:
        ld c,a
        and 0x18
        or 0x40
        ld h,a
        ld a,c
        and 7
        rrca
        rrca
        rrca                    ; (y & 7) << 5
        ld c,a
        ld a,(xpos)
        or c
        ld l,a
        ret

; HL = attribute address of cell (xpos, ypos)
attraddr:
        ld a,(ypos)
        ld c,a
        rrca
        rrca
        rrca
        and 3                   ; y >> 3
        add a,0x58
        ld h,a
        ld a,c
        and 7
        rrca
        rrca
        rrca                    ; (y & 7) << 5
        ld c,a
        ld a,(xpos)
        or c
        ld l,a
        ret

; ----------------------------------------------------------------------

tick:   defb 0
xpos:   defb 15                 ; start mid-screen
ypos:   defb 11

blank:  defs 32,0

; the Minion, 16x16, one byte pair per row
sprite:
        defb 0x0F,0xF0          ; ....XXXXXXXX....   head
        defb 0x3F,0xFC          ; ..XXXXXXXXXXXX..
        defb 0x7F,0xFE          ; .XXXXXXXXXXXXXX.
        defb 0x7F,0xFE          ; .XXXXXXXXXXXXXX.
        defb 0xF8,0x1F          ; XXXXX......XXXXX   goggle
        defb 0xF9,0x9F          ; XXXXX..XX..XXXXX   the eye
        defb 0xF8,0x1F          ; XXXXX......XXXXX
        defb 0xF0,0x0F          ; XXXX........XXXX   grin
        defb 0xFF,0xFF          ; XXXXXXXXXXXXXXXX   dungarees from here
        defb 0xBF,0xFD          ; X.XXXXXXXXXXXX.X   strap buttons
        defb 0xFF,0xFF          ; XXXXXXXXXXXXXXXX
        defb 0xFF,0xFF          ; XXXXXXXXXXXXXXXX
        defb 0x7F,0xFE          ; .XXXXXXXXXXXXXX.
        defb 0x3F,0xFC          ; ..XXXXXXXXXXXX..
        defb 0x38,0x1C          ; ..XXX......XXX..   legs
        defb 0x38,0x1C          ; ..XXX......XXX..

        end start
