
        header  5

    ifndef CHRSET_FIXED
        define  CHRSET_FIXED            60
    endif
    ifndef FRAMES_PER_BLOCK
        define  FRAMES_PER_BLOCK        7
    endif
    ifndef FRAME_RATE
        define  FRAME_RATE              5
    endif
    ifndef COLOR4_MODE
        define  COLOR4_MODE             0
    endif
    ifndef VIDEOFILE_NAME
        define  VIDEOFILE_NAME          "VIDEOCHR.M4"
    endif
    ifndef MUSICFILE_NAME
        define  MUSICFILE_NAME          "MUSICDAT.M4"
    endif
    ifndef ENABLE_DEBUG
        define  ENABLE_DEBUG            0
    endif

main:
        di
        ld      sp, 0100h
        ld      a, 0ffh
        out     (0b2h), a
        ld      hl, resetRoutine
        ld      (0bff8h), hl
        call    daveInit
        ei
        ld      bc, 011bh               ; BORD_VID
        ld      d, 00h
        exos    16
        halt
        halt
        call    allocateMemory
        call    createLPT
        ld      a, (ringBufSegment0)
        out     (0b1h), a
        ld      de, musicFileName
        xor     a
        exos    1
        jp      nz, resetRoutine
        ld      de, 4000h
        ld      bc, 4000h
        exos    6
        jp      z, resetRoutine
        xor     a
        exos    3
        di
        ld      a, (musicSegment)
        out     (0b3h), a
        in      a, (0b0h)
        out     (0b2h), a
        ld      hl, 4000h
        ld      de, musicData + 8000h
        call    decompressData
        ei
        ld      hl, 80ffh
        add     hl, de
        ld      a, h
        or      a
        jp      m, resetRoutine
        ld      (fileBufOffset + 1), a
        call    loadDataFile
        jr      z, .l1
        xor     a
        exos    3
.l1:    di
        xor     a
        ld      (framesBuffered), a
        inc     a
        ld      (frameTimer), a
        call    musicInit1
        call    setIRQRoutine
        ei
        call    loadBaseChrSet
.l2:    call    decompVideoBlock
        jr      .l2

setIRQRoutine:
        di
        ld      a, 0c3h                 ; = JP nn
        ld      hl, irqRoutine
        ld      (0038h), a
        ld      (0039h), hl
.l1:    ld      a, 0ch                  ; * bfPortValue
        out     (0bfh), a
        ld      a, 30h
        out     (0b4h), a
        call    daveReset
        ei
        ret

bfPortValue     equ     setIRQRoutine.l1 + 1

restoreIRQRoutine:
        di
        ld      hl, 0038h
        ld      (hl), 0f5h              ; = PUSH AF
        inc     l
        ld      (hl), 37h               ; = SCF
        inc     l
        ld      (hl), 18h               ; = JR +d
        ld      a, (bfPortValue)
        and     06h
        out     (0bfh), a
        ld      a, 3ch
        out     (0b4h), a

daveReset:
        ld      bc, 10b0h
        xor     a
.l1:    dec     c
        out     (c), a
        djnz    .l1
        ret

daveInit:
        call    testCPUFrequency        ; returns with B = 0
        ld      a, 25
        cp      l                       ; Carry = 1 if Z80 frequency > 5 MHz
        ld      a, 03h
        rla
        rla                             ; Z80 <= 5 MHz: 0Ch, > 5 MHz: 0Eh
        ld      (bfPortValue), a
        ret

; L = 1 kHz interrupts per video frame

testCPUFrequency:
        di
        ld      a, 04h
        out     (0bfh), a
        xor     a
        ld      b, a
        out     (0a7h), a
        ld      c, b
        call    .l1
        ld      l, b
.l1:    in      a, (0b4h)
        and     11h
        or      c
        rlca
        and     66h
        ld      c, a                    ; -ON--ON-
        rlca                            ; ON--ON--
        xor     c                       ; OXN-OXN-
        bit     2, a
        jr      z, .l2
        inc     l                       ; 1 kHz interrupt
.l2:    cp      0c0h
        jr      c, .l1                  ; not 50 Hz interrupt ?
        ret

irqRoutine:
        push    af
        ld      a, 30h
        out     (0b4h), a
        push    bc
        push    de
        push    hl
        in      a, (0b1h)
        ld      l, a
        in      a, (0b2h)
        ld      h, a
        push    hl
        ld      a, (musicSegment)
        out     (0b1h), a
        call    musicPlay
.l1:    ld      a, 1                    ; * frameTimer
        dec     a
        jp      nz, .l10
.l2:    ld      a, 0                    ; * framesBuffered
        dec     a
        jp      m, .l11
        ld      (framesBuffered), a
        ld      b, a
    if ENABLE_DEBUG != 0
        ld      a, 1
        out     (81h), a
    endif
        ld      a, (videoSegment)
        out     (0b1h), a
        ld      hl, ringBufSegment0
        ld      a, FRAMES_PER_BLOCK - 1
        sub     b
        jr      nc, .l3
        add     a, FRAMES_PER_BLOCK
        inc     l
.l3:    ld      c, 0b2h
        outi
        ld      hl, 8000h
        ld      de, 0800h - (CHRSET_FIXED * 8)
        ld      b, a
        add     a, a
        add     a, b
        jr      z, .l5
.l4:    add     hl, de
        djnz    .l4
.l5:    add     a, high (0c000h - (0300h * FRAMES_PER_BLOCK))
        ld      (.l7 + 1), sp
        ld      sp, hl
        ld      hl, 4000h + (CHRSET_ADDR & 3fffh) + CHRSET_FIXED
        pop     bc
.l6:
        assert  (CHRSET_FIXED & 3) == 0
    rept 2
        ld      (hl), c                 ; 0
        inc     h
        ld      (hl), b                 ; 1
        inc     h
        pop     de
        ld      (hl), e                 ; 2
        inc     h
        ld      (hl), d                 ; 3
        inc     h
        pop     de
        ld      (hl), e                 ; 4
        inc     h
        ld      (hl), d                 ; 5
        inc     h
        pop     de
        ld      (hl), e                 ; 6
        inc     h
        ld      (hl), d                 ; 7
        inc     l
        pop     de
        ld      (hl), e                 ; 7
        dec     h
        ld      (hl), d                 ; 6
        dec     h
        pop     de
        ld      (hl), e                 ; 5
        dec     h
        ld      (hl), d                 ; 4
        dec     h
        pop     de
        ld      (hl), e                 ; 3
        dec     h
        ld      (hl), d                 ; 2
        dec     h
        pop     de
        ld      (hl), e                 ; 1
        dec     h
        pop     bc
        ld      (hl), d                 ; 0
        inc     l
    endm
        jp      nz, .l6
.l7:    ld      sp, 0000h               ; *
        ld      h, a                    ; L = 0
    if ENABLE_DEBUG != 0
        ld      a, 2
        out     (81h), a
    endif
        ld      de, 4000h + (SCREEN_ADDR & 3fffh)
        ld      bc, 0300h
.l8:
    rept 32
        ldi
    endm
        jp      pe, .l8
    if ENABLE_DEBUG != 0
        xor     a
        out     (81h), a
    endif
.l9:    ld      a, FRAME_RATE           ; * frameRate
.l10:   ld      (frameTimer), a
.l11:   pop     hl
        ld      a, l
        out     (0b1h), a
        ld      a, h
        out     (0b2h), a
        pop     hl
        pop     de
        pop     bc
        pop     af
        ei
        ret

frameTimer      equ     irqRoutine.l1 + 1
framesBuffered  equ     irqRoutine.l2 + 1
frameRate       equ     irqRoutine.l9 + 1

loadBaseChrSet:
        ld      hl, fileBufSegments
        ld      (fileBufCurSegmentP), hl
        ld      a, (hl)
        ld      (fileBufCurSegment), a
        out     (0b1h), a
        ld      a, (videoSegment)
        out     (0b2h), a
        in      a, (0b0h)
        out     (0b3h), a
        ld      de, tmpBuf + 0c000h
        ld      hl, (fileBufOffset)
        ld      a, 80h
        call    decompressData
        ld      a, 80h
        ld      (fileBufPos), hl
        ld      (fileBufSR), a
        ld      hl, tmpBuf + 0c000h
        ld      de, 8000h + (CHRSET_ADDR & 3fffh)
        ld      a, CHRSET_FIXED
.l1:    ld      bc, 8
        ldi
.l2:    dec     e
        inc     d
        ldi
        jp      pe, .l2
        ld      bc, 10000h - 0700h
        ex      de, hl
        add     hl, bc
        ex      de, hl
        dec     a
        jr      nz, .l1
        ret

loadDataFile:
        ld      de, dataFileName
        xor     a
        exos    1
        jp      nz, resetRoutine
        ld      a, 80h
        ld      (fileBufSR), a

loadNextSegments:
        ld      hl, (fileBufOffset)
        ld      (fileBufPos), hl
        ld      e, l
        ld      d, h
        ld      c, l                    ; L = 0
        ld      a, high 8000h
        sub     h
        ld      b, a
        ld      hl, fileBufSegments
        ld      (fileBufCurSegmentP), hl
        ld      a, (hl)
        ld      (fileBufCurSegment), a
.l1:    out     (0b1h), a
        xor     a
        exos    6
        call    displayProgress
        jr      nz, .l2
        ld      de, 4000h
        ld      b, d                    ; C = 0
        inc     hl
        or      (hl)
        jr      nz, .l1
.l2:    ld      (fileLoaded), a

clearStatusLine:
        push    af
        ld      a, 0ffh
        out     (0b2h), a
        ld      hl, (0bff6h)
        ld      bc, 6
        add     hl, bc
        ld      e, l
        ld      d, h
        inc     de
        ld      c, 25
        ld      (hl), 20h
        ldir
        pop     af
        ret

displayProgress:
        push    af
        push    hl
        ld      a, (fileBufOffset + 1)
        neg
        add     a, a
        add     a, a
        ld      h, a
        ld      a, l
        sub     low (fileBufSegments + 2)
        ld      l, 0
        sra     a
        rr      h
        sra     a
        rr      h
        add     hl, de
        adc     a, 0
        call    convertAHLToBCD
        ex      de, hl
        ld      hl, loadMessage + 14
        ld      (hl), a
        ld      a, 33h
        rrd
        inc     hl
        ld      (hl), a
        inc     hl
        ld      (hl), d
        ld      a, 33h
        rrd
        inc     hl
        ld      (hl), a
        inc     hl
        ld      (hl), e
        ld      a, 33h
        rrd
        inc     hl
        ld      (hl), a
        ld      a, 0ffh
        out     (0b2h), a
        ld      hl, (0bff6h)
        ld      bc, 6
        add     hl, bc
        ex      de, hl
        ld      hl, loadMessage
        ld      c, 26
        ldir
        pop     hl
        pop     af
        ret

convertAHLToBCD:
        ld      de, 0
        ld      b, d
        ld      c, a
        ld      a, l
        cp      40h
        ld      a, h
        sbc     a, 42h
        ld      a, c
        sbc     a, 0fh
        jr      nc, .l3                 ; > 999999?
        adc     hl, hl
.l1:    rl      c
.l2:    ld      a, e
        adc     a, a
        daa
        ld      e, a
        ld      a, d
        adc     a, a
        daa
        ld      d, a
        ld      a, b
        adc     a, a
        daa
        ld      b, a
        adc     hl, hl
        jr      nz, .l1
        rl      c
        jr      nz, .l2
        ld      l, e
        ld      h, d
        ret
.l3:    ld      hl, 9999h
        ld      a, h
        ret

allocateMemory:
        assert  fileBufSegments == (ringBufSegment0 + 2)
        assert  fileBufSegments == musicSegment
        ld      hl, ringBufSegment0 + 1
.l1:    exos    24
        jr      nz, .l2
        ld      (hl), c
        inc     hl
        jr      .l1
.l2:    add     a, a
        jr      c, resetRoutine         ; .NOSEG?
        cp      (hl)
        jr      c, resetRoutine         ; < 80K?
        ld      a, c                    ; Carry = 0
        ld      (videoSegment), a
        dec     hl
        ld      a, (hl)
        ld      (hl), 00h
        ld      hl, (ringBufSegment0 + 1)
        ld      (ringBufSegment0), hl
        ld      (musicSegment), a
        ld      bc, LPT_END & 3fffh
        ex      de, hl
        sbc     hl, bc
        ex      de, hl
        jr      c, resetRoutine
        ld      e, c
        ld      d, b
        exos    23
        ret     z

resetRoutine:
        di
        ld      sp, 3800h
        ld      a, 0ffh
        out     (0b2h), a
        ld      hl, resetRoutine
        ld      (0bff8h), hl
        call    restoreIRQRoutine
        ld      c, 40h
        exos    0
        ld      a, 01h
        out     (0b3h), a
        ld      a, 6
        jp      0c00dh

; =============================================================================
; decompress_m4.s

; total table size is 256 bytes, and should not cross a 256-byte page boundary
;decodeTablesBegin      equ     0ff00h
        assert  (decodeTablesBegin & 00ffh) == 0

; input/output parameters (updated on return):
;   HL:               source address (forward decompression)
;   DE:               destination address
; registers used:
;   BC:               0 on return
;   A, IX:            undefined

decompressData:
        ld      a, 80h                  ; initialize shift register
.l1:    call    decompressDataBlock
        jr      c, .l1
        ret

decompressDataBlock:
        push    de
        ld      bc, 4002h
        call    readBitsB               ; get prefix size for >= 3 byte matches
        inc     b
        ld      d, a
        ld      a, low ((offs3DecodeTable >> 1) | 80h)
.l1:    sla     c                       ; len >= 3 offset slots: 4, 8, 16, 32
        rra
        djnz    .l1
        ld      (copyLZMatch.l1 + 1), a
        ld      a, d
        ld      ix, decodeTablesBegin + 0088h   ; initialize decode tables
        ld      de, 3
        jr      .l3
.l2:    ld      de, 1
.l3:    ld      b, 10h                  ; Carry must be 0 here
        call    readBitsB
        push    hl
        ld      l, b
        ld      h, high decodeTablesBegin
        ld      b, (hl)
        ld      (ix + 80), b            ; store jump table address LSB + 1
        ld      (ix), e                 ; store base value LSB
        ld      (ix + 40), d            ; store base value MSB
        inc     ixl
        set     4, l
        ld      b, (hl)
        res     4, l
        set     5, l
        ld      h, (hl)
        ld      l, b                    ; HL = 2 ^ nBits
        add     hl, de                  ; calculate new base value
        ex      de, hl
        pop     hl
        ld      b, a
        ld      a, ixl
        sub     low (offs3DecodeTable - 80)
        ld      a, b
        jr      c, .l3
        jr      z, .l2                  ; at the beginning of the next table?
        dec     c
        jr      nz, .l3                 ; continue until all tables are read
        pop     de                      ; DE = decompressed data write address
        jr      .l9                     ; jump to main decompress loop
.l6:    ldi                             ; copy literal byte
.l7:    bit     7, h
        jp      z, .l9
        call    readCompressedBlock.l1
        jr      .l9
.l8:    ldi                             ; expand match
        ldi
        ldir
        pop     hl
.l9:    add     a, a                    ; read flag bit
        call    z, readCompressedByte
        jr      nc, .l6                 ; literal byte?
        ld      bc, 0800h | (lengthDecodeTable & 00ffh)
.l10:   add     a, a                    ; read length prefix bits
        call    z, readCompressedByte
        jr      nc, copyLZMatch
        inc     c
        djnz    .l10
        ld      c, (hl)                 ; literal sequence, read length - 16
        inc     l                       ; C < 2: end of block
        call    z, readCompressedBlock
        srl     c
        ret     z                       ; Carry = 1 if more blocks remaining
        rl      c                       ; copy literal sequence
        push    hl                      ; C = length - 16
        ld      hl, 16
        add     hl, bc
        ld      c, l
        ld      b, h
        pop     hl
.l11:   ldi
        jp      po, .l7
        bit     7, h
        jr      z, .l11
        call    readCompressedBlock.l1
        jr      .l11

copyLZMatch:
        push    de
        ld      ixl, c
        call    decodeLZMatchParam      ; decode length
        push    de                      ; Carry = 0 from decodeLZMatchParam
.l1:                                    ; length >= 3 bytes,
                                        ; variable prefix size
        ld      b, low (((offs3DecodeTable & 00ffh) | 0100h) >> 4)      ; *
.l2:    call    readBitsB               ; read offset prefix and decode offset
        pop     bc                      ; BC = length
        ex      (sp), hl
        push    hl
        sbc     hl, de                  ; Carry = 0
        pop     de
        jp      m, decompressDataBlock.l8   ; return to main decompress loop
        ld      ixl, a
        in      a, (0b3h)
        out     (0b1h), a
        ldi
        ldi
        ldir
.l3:    ld      a, 0feh                 ; * fileBufCurSegment
        out     (0b1h), a
        ld      a, ixl
        pop     hl
        jp      decompressDataBlock.l9

fileBufCurSegment       equ     copyLZMatch.l3 + 1

readBitsB:
        add     a, a
        jr      z, .l2
        rl      b
        jr      c, .l4
        add     a, a
        jr      z, .l2
        rl      b
        jr      c, .l4
        add     a, a
        jr      z, .l2
        rl      b
        jr      c, .l4
        add     a, a
        jr      z, .l2
        rl      b
        jr      c, .l4
        add     a, a
        jr      z, .l2
        rl      b
        ret     p
        jp      .l5
.l1:    call    readCompressedBlock
        jr      .l3
.l2:    ld      a, (hl)
        rla
        inc     l
        jr      z, .l1
.l3:    rl      b
        jr      c, .l4
        add     a, a
        rl      b
        jr      c, .l4
        add     a, a
        rl      b
        jr      c, .l4
        add     a, a
        rl      b
        jr      c, .l4
        add     a, a
        rl      b
.l4:    ret     p
.l5:    ld      ixl, b

decodeLZMatchParam:
        ld      b, (ix)                 ; B = number of extra bits + 1
        ld      c, (ix - 80)
        djnz    .l1
        ld      e, c
        ld      d, (ix - 40)
        or      a                       ; Carry = 0 on return
        ret
.l1:    ex      de, hl
        ld      l, b
        ld      h, high decodeTablesBegin
        jp      (hl)
.l2:    add     a, a
        call    z, .l21
        rl      h
.l3:    add     a, a
        call    z, .l21
        rl      h
        ld      l, 01h
.l4:    add     a, a
        call    z, .l21
        rl      h
.l5:    add     a, a
        call    z, .l21
        rl      h
.l6:    add     a, a
        call    z, .l21
        rl      h
.l7:    add     a, a
        call    z, .l21
        rl      h
.l8:    add     a, a
        call    z, .l21
        rl      h
.l9:    add     a, a
        jr      z, .l19
        rl      l
.l10:   add     a, a
        jr      z, .l19
        rl      l
.l11:   add     a, a
        jr      z, .l19
        rl      l
.l12:   add     a, a
        jr      z, .l19
        rl      l
.l13:   add     a, a
        jr      z, .l19
        rl      l
.l14:   add     a, a
        jr      z, .l19
        rl      l
.l15:   add     a, a
        jr      z, .l19
        rl      l
.l16:   add     a, a
        jr      z, .l19
        rl      l
.l17:   ld      b, (ix - 40)            ; BC = base value
        add     hl, bc
        ex      de, hl
        ret
.l18:   inc     d
        jp      p, .l20
        ex      de, hl
        call    readCompressedBlock.l1
        ex      de, hl
        jr      .l20
.l19:   ld      a, (de)
        rla
        inc     e
        jr      z, .l18
.l20:   rl      l
        jr      c, .l17
        add     a, a
        rl      l
        jr      c, .l17
        add     a, a
        rl      l
        jr      c, .l17
        add     a, a
        rl      l
        jr      c, .l17
        add     a, a
        rl      l
        jr      c, .l17
        add     a, a
        rl      l
        jr      c, .l17
        add     a, a
        rl      l
        jr      c, .l17
        add     a, a
        rl      l
        jr      .l17
.l21:   ld      a, (de)
        rla
        inc     e
        ret     nz
        inc     d
        ret     p
        ex      de, hl
        call    readCompressedBlock.l1
        ex      de, hl
        ret

readCompressedByte:
        ld      a, (hl)
        rla
        inc     l
        ret     nz

; =============================================================================

readCompressedBlock:
        inc     h
        ret     p
.l1:    push    af
        ld      hl, (fileBufCurSegmentP)
        inc     hl
        ld      a, (hl)
        or      a
        jr      z, .l2
        ld      (fileBufCurSegmentP), hl
        ld      (fileBufCurSegment), a
        out     (0b1h), a
        ld      hl, 4000h
        pop     af
        ret
.l2:    push    bc
        push    de
        call    restoreIRQRoutine
        ei
        call    loadNextSegments
        call    setIRQRoutine
        xor     a
        ld      (fileLoaded), a
        ld      a, (ringBufSegment0)
        out     (0b2h), a
        ld      a, (fileBufSegments)
        out     (0b1h), a
        ld      hl, (fileBufPos)
        pop     de
        pop     bc
        pop     af
        ret

decompVideoBlock:
.l1:    ld      a, (framesBuffered)
        cp      FRAMES_PER_BLOCK + 1
        jr      nc, .l1
        ld      a, (fileBufCurSegment)
        out     (0b1h), a
        assert  ringBufSegment1 == (ringBufSegment0 + 1)
        ld      hl, (ringBufSegment0)
        ld      a, l
        out     (0b2h), a
        ld      a, h
        out     (0b3h), a
        ld      hl, (fileBufPos)
        ld      a, (fileBufSR)
        ld      de, 0c000h
        call    decompressDataBlock
        call    decompressDataBlock
        ld      (fileBufPos), hl        ; Carry = 1: not last block
        ld      (fileBufSR), a
        rl      b
        di
        ld      a, (framesBuffered)
        add     a, FRAMES_PER_BLOCK
        ld      (framesBuffered), a
        ld      hl, (ringBufSegment0)
        ld      a, l
        ld      l, h
        ld      h, a
        ld      (ringBufSegment0), hl
        ei
        dec     b
        ret     z
        ld      a, (fileLoaded)
        or      a
        jp      nz, loadBaseChrSet
        call    restoreIRQRoutine
        ei
        ld      de, chnParamBlock
        ld      c, 01h                  ; file position
        xor     a
        exos    10
        jp      nz, resetRoutine
        call    loadNextSegments
        call    setIRQRoutine
        jp      loadBaseChrSet

; =============================================================================
; xorplay8K.s

; daveconv file.xr8 file.dav:e4,n,s 0x0000

musicInit1:
        ld      hl, trackOffsets + 1
        ld      bc, 0400h + ((trackOffsets - 2800h) >> 8)
.l1:    ld      a, (hl)
        add     a, c
        ld      (hl), a
        inc     l
        inc     l
        djnz    .l1

musicInit:
        ld      hl, (trackOffsets)
        xor     a
        di
        out     (0a7h), a
        inc     a
        ld      (envTimer0), a
        ld      (envTimer1), a
        ld      (envTimer2), a
        ld      (envTimer3), a
        ld      (trackPtr0), hl
        ld      hl, (trackOffsets + 2)
        ld      (trackPtr1), hl
        ld      hl, (trackOffsets + 4)
        ld      (trackPtr2), hl
        ld      hl, (trackOffsets + 6)
        ld      (trackPtr3), hl
;       ei
        ret

    macro daveChnPlay fr, vr, lenvt, ltptr
.lenv:  ld      a, 1                    ; * envelope timer
        dec     a
        jr      nz, .l5
.ltp:   ld      hl, trackOffsets + 8    ; * data pointer
        or      (hl)
        inc     hl
        jr      nz, .l1
        or      (hl)
      if fr == 0a0h
        jr      z, musicRestart
      endif
        inc     hl
        ld      b, (hl)
        inc     hl
        ld      e, (hl)
        inc     hl
      if fr != 0a6h
        ld      d, (hl)
        inc     hl
      endif
        ld      (.ltp + 1), hl
        jp      .l4
.l1:    ld      d, high noteParamTable
        ld      b, a
        djnz    .l2
        ld      d, high (noteParamTable + 0400h)
        ld      a, (hl)
        inc     hl
.l2:    ld      (.ltp + 1), hl
        ld      l, a
        ld      h, d
        ld      a, (hl)
        inc     h
        ld      b, (hl)
        inc     h
        ld      e, (hl)
      if fr != 0a6h
        inc     h
        ld      d, (hl)
      endif
      if fr == 0a0h
        or      a
        jr      z, musicRestart
      endif
.l4:    ld      l, b
        ld      h, a
        and     0fh
        ld      (.lenv + 1), a          ; duration
        xor     h
        rra
        rra
        rra
        rra                             ; envelope H
        add     a, high envelopeData
        ld      h, a
      if fr != 0a6h
        ld      (.l6 - 2), de
      else
        ld      a, e
        ld      (.l6 - 1), a
      endif
        jp      .l6                     ; Carry = 0
.l5:    ld      (.lenv + 1), a
        ld      hl, envelopeData        ; * envelope position
        inc     hl
      if fr != 0a6h
        ld      de, 0000h               ; * frequency
      else
        ld      a, 00h                  ; * channel 3 control
      endif
.l6:                                    ; Z = 0
      if fr != 0a6h
        ld      a, e
      endif
        out     (fr), a
      if fr != 0a6h
        ld      a, d
        out     (fr + 1), a
      endif
        ld      a, (hl)
        out     (vr), a
        rla
        jr      c, .l7
        ld      (.l5 + 4), hl
.l7:    ld      a, h
        add     a, c                    ; C = high 1000h
        ld      h, a
        ld      a, (hl)
        out     (vr + 4), a

lenvt   equ     .lenv + 1
ltptr   equ     .ltp + 1
    endm

musicRestart:
        call    musicInit

musicPlay:
        ld      c, high 1000h
        daveChnPlay 0a0h, 0a8h, envTimer0, trackPtr0
        daveChnPlay 0a2h, 0a9h, envTimer1, trackPtr1
        daveChnPlay 0a4h, 0aah, envTimer2, trackPtr2
        daveChnPlay 0a6h, 0abh, envTimer3, trackPtr3
        ret

; =============================================================================

loadMessage:
        defm    "Loading file: 000000 bytes"

dataFileName:
        dbl     VIDEOFILE_NAME

fileBufOffset:
        defw    6000h
fileBufCurSegmentP:
        defw    fileBufSegments
fileBufPos:
        defw    6000h
fileBufSR:
        defb    80h

fileLoaded:
        defb    00h

        align   4
videoSegment:
        defb    0ffh
ringBufSegment0:
        defb    0ffh
ringBufSegment1:
        defb    0ffh
musicSegment:
fileBufSegments:
        defb    0ffh
        block   3, 0ffh
        block   249, 00h

chnParamBlock:
        block   16, 00h

        align   256
decodeTablesBegin:
        defb    1, low (.l1 + 1), low (.l2 + 1), low (.l3 + 1)
        defb    low (.l4 + 1), low (.l5 + 1), low (.l6 + 1), low (.l7 + 1)
        defb    low (.l8 + 1), low (.l9 + 1), low (.l10 + 1), low (.l11 + 1)
        defb    low (.l12 + 1), low (.l13 + 1), low (.l14 + 1), low (.l15 + 1)
        defb    low 0001h, low 0002h, low 0004h, low 0008h
        defb    low 0010h, low 0020h, low 0040h, low 0080h
        defb    low 0100h, low 0200h, low 0400h, low 0800h
        defb    low 1000h, low 2000h, low 4000h, low 8000h
        defb    high 0001h, high 0002h, high 0004h, high 0008h
        defb    high 0010h, high 0020h, high 0040h, high 0080h
        defb    high 0100h, high 0200h, high 0400h, high 0800h
        defb    high 1000h, high 2000h, high 4000h, high 8000h
.l1:    ld      hl, 0080h
        jp      decodeLZMatchParam.l16
.l2:    ld      hl, 0040h
        jp      decodeLZMatchParam.l15
.l3:    ld      hl, 0020h
        jp      decodeLZMatchParam.l14
.l4:    ld      hl, 0010h
        jp      decodeLZMatchParam.l13
.l5:    ld      hl, 0008h
        jp      decodeLZMatchParam.l12
.l6:    ld      hl, 0004h
        jp      decodeLZMatchParam.l11
.l7:    ld      hl, 0002h
        jp      decodeLZMatchParam.l10
.l8:    ld      hl, 0001h
        jp      decodeLZMatchParam.l9
.l9:    ld      hl, 0001h
        jp      decodeLZMatchParam.l8
.l10:   ld      hl, 0001h
        jp      decodeLZMatchParam.l7
.l11:   ld      hl, 0001h
        jp      decodeLZMatchParam.l6
.l12:   ld      hl, 0001h
        jp      decodeLZMatchParam.l5
.l13:   ld      hl, 0001h
        jp      decodeLZMatchParam.l4
.l14:   ld      h, 0
        jp      decodeLZMatchParam.l3
.l15:   ld      h, 0
        jp      decodeLZMatchParam.l2
        assert  ($ & 00ffh) == 0088h
        block   8 + 32, 00h
        block   8 + 32, 00h
lengthDecodeTable:
        block   8, 00h
offs3DecodeTable:
        block   32, 00h
decodeTablesEnd:

tmpBuf:
        assert  (tmpBuf & 00ffh) == 0

musicFileName:
        dbl     MUSICFILE_NAME

musicData       equ     tmpBuf + ((CHRSET_FIXED * 8 + 00ffh) & 0ff00h)
        assert  (musicData & 00ffh) == 0
envelopeData    equ     musicData
noteParamTable  equ     musicData + 2000h
trackOffsets    equ     musicData + 2800h

createLPT:
        ld      a, 0ffh
        out     (0b2h), a
        ld      a, (videoSegment)
        out     (0b3h), a
        ld      hl, SCREEN_ADDR & 3fffh
        rrca
        rrca
        and     high 0c000h
        or      h
        ld      h, a
        ld      (lpbData + 4), hl
        add     a, high (CHRSET_ADDR - SCREEN_ADDR)
        ld      (lpbData + 6), a
        ld      bc, LPT_ADDR - SCREEN_ADDR
        add     hl, bc
        push    hl
        ld      hl, (0bff4h)
        inc     hl
        ld      de, statusLineLPB + 1
        ld      bc, 15
        ldir
        ld      hl, SCREEN_ADDR | 0c000h
        ld      (hl), c
        ld      e, l
        ld      d, h
        inc     de
        ld      bc, LPT_ADDR - (SCREEN_ADDR + 1)
        ldir
        ld      a, 24
.l1:    ld      hl, lpbData
        ld      c, 16
        ldir
        ld      hl, (lpbData + 4)
        ld      c, 32
        add     hl, bc
        ld      (lpbData + 4), hl
        dec     a
        jr      nz, .l1
        ld      hl, 10001h - 16
        add     hl, de
        set     7, (hl)                 ; VINT
        ld      hl, lptBorderVBlank
        ld      bc, lptBorderVBlankSize
        ldir
        pop     hl
        ld      a, 1ch
.l2:    add     hl, hl
        rla
        jr      nc, .l2
        ld      c, 82h
        di
        out     (c), h
        out     (83h), a
        ei
        ret

lpbData:
    if COLOR4_MODE == 0
        defb    256 - 8, 06h, 15, 47
        defw    SCREEN_ADDR, CHRSET_ADDR >> 8
        defb    000h, 0ffh, 000h, 000h, 000h, 000h, 000h, 000h
    else
        defb    256 - 8, 26h, 15, 47
        defw    SCREEN_ADDR, CHRSET_ADDR >> 8
        defb    000h, 038h, 0c7h, 0ffh, 000h, 000h, 000h, 000h
    endif

lptBorderVBlank:
        defb    256 - 51, 02h, 63, 0,  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0
        defb    256 - 3, 00h, 63, 0,  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0
        defb    256 - 2, 00h, 6, 63,  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0
        defb    256 - 1, 00h, 63, 32,  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0
        defb    256 - 3, 00h, 63, 0,  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0
        defb    256 - 14, 02h, 6, 63,  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0
        defb    256 - 30, 02h, 63, 0,  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0
statusLineLPB:
        defb    256 - 9, 02h, 63, 0,  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0
        defb    256 - 7, 03h, 63, 0,  0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0
lptBorderVBlankSize     equ     $ - lptBorderVBlank

SCREEN_ADDR     equ     0000h
CHRSET_ADDR     equ     SCREEN_ADDR + 0300h
LPT_ADDR        equ     CHRSET_ADDR + 0800h
LPT_END         equ     LPT_ADDR + (24 * 16) + lptBorderVBlankSize

