
; if non-zero, there are no border effects, and code size is smaller by 6 bytes
NO_BORDER_FX            equ     0

; total table size is 153 bytes, and should not cross a 256-byte page boundary
decodeTablesBegin       equ     0ff00h

offs1DecodeTable        equ     decodeTablesBegin
lengthDecodeTable       equ     offs1DecodeTable + (4 * 3)
offs2DecodeTable        equ     lengthDecodeTable + (8 * 3)
offs3DecodeTable        equ     offs2DecodeTable + (8 * 3)
decodeTablesEnd         equ     offs3DecodeTable + (31 * 3)

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

decompressData:
        ld      a, 80h                  ; initialize shift register
    if NO_BORDER_FX != 0
        defb    0feh                    ; = CP n
.l7:    ret     nc
    endif
.l1:    push    de                      ; decompress data block
        ld      bc, 4002h
        add     a, a
        call    readBitsB.l2            ; get prefix size for >= 3 byte matches
        inc     b
        push    hl
        ld      d, 0f0h                 ; prefix size codes: 40h, 20h, 10h, 08h
.l2:    sla     c                       ; with -20h offset
        srl     d
        djnz    .l2
        ld      ixh, d
        dec     c
        ld      hl, decodeTablesBegin   ; initialize decode tables
.l3:    ld      de, 1
.l4:    ld      b, 10h
        ex      (sp), hl                ; Carry must be 0 here
        call    readBitsB
        inc     b
        ex      (sp), hl
        ld      (hl), b                 ; store the number of bits to read + 1
        inc     l
        ld      (hl), e                 ; store base value LSB
        inc     l
        ld      (hl), d                 ; store base value MSB
        inc     l
        push    hl
        ld      hl, 1                   ; calculate 2 ^ nBits
        defb    0feh                    ; = CP n
.l5:    add     hl, hl
        djnz    .l5
        add     hl, de                  ; calculate new base value
        ex      de, hl
        pop     hl
        ld      b, a
        ld      a, l
        sub     low offs3DecodeTable
        jr      nc, .l6
        and     07h
        ld      a, b
        jr      nz, .l4
.l6:    ld      a, b
        jr      z, .l3                  ; at the beginning of the next table?
        dec     c
        jr      nz, .l4                 ; continue until all tables are read
        pop     hl
        pop     de                      ; DE = decompressed data write address
        jr      .l10                    ; jump to main decompress loop
    if NO_BORDER_FX == 0
.l7:    jr      c, .l1                  ; more blocks remaining?
        xor     a                       ; reset border color
        out     (81h), a
        ret
    endif
.l8:    ld      c, (hl)                 ; read length - 16
        srl     c
        ld      c, (hl)
        inc     hl
        jr      z, .l7                  ; end of block?
        ldir
        ld      c, 15
        ldir
.l9:    ldi                             ; copy literal byte
.l10:   add     a, a                    ; read flag bit
        jr      nz, .l11
        ld      a, (hl)
        inc     hl
        rla
.l11:   jr      nc, .l9                 ; literal byte?
        ld      bc, 0f700h + ((10000h - offs2DecodeTable) & 00ffh)
.l12:   inc     b
        jr      z, .l8                  ; literal sequence?
        add     a, a                    ; read length prefix bits
        jr      nz, .l13
        ld      a, (hl)
        inc     hl
        rla
.l13:   jr      c, .l12

copyLZMatch:
        push    de
        call    decodeLZMatchParam      ; decode length
    if NO_BORDER_FX == 0
        out     (81h), a
    endif
        push    de                      ; Carry = 0 from decodeLZMatchParam
        inc     d
        dec     d
        jr      nz, .l1                 ; length >= 256 bytes?
        dec     e
        jr      z, .l3                  ; length == 1?
        ld      bc, 3f00h + ((10000h - offs3DecodeTable) & 00ffh)
        dec     e
        jr      z, .l4                  ; length == 2?
.l1:    ld      c, low (10000h - (decodeTablesEnd + 3)) ; length >= 3 bytes,
        ld      b, ixh                                  ; variable prefix size
        call    readBitsB
        dec     b                       ; Carry = 1
        bit     5, b
        jr      nz, .l5                 ; no delta value?
        ld      b, 07h
        call    readBitsB.l3            ; read 2's complement of offset
        ld      e, b
        ld      d, 0ffh                 ; DE = -offset
        pop     bc                      ; BC = length
        ld      ixl, a
        ld      a, (hl)                 ; read delta value
        inc     hl
        ex      (sp), hl
        push    hl
        add     hl, de
        pop     de
.l2:    add     a, (hl)
        ld      (de), a
        sub     (hl)
        inc     de
        cpi
        jp      pe, .l2
        ld      a, ixl
        jr      .l6
.l3:    ld      bc, 7f00h + ((10000h - lengthDecodeTable) & 00ffh)
.l4:    call    readBitsB               ; read offset prefix and decode offset
.l5:    call    decodeLZMatchParam
        pop     bc                      ; BC = length
        ex      (sp), hl
        push    hl
        sbc     hl, de                  ; Carry = 0
        pop     de
        ldir                            ; expand match
.l6:    pop     hl
        jr      decompressData.l10      ; return to main decompress loop
.l7:    ld      a, (hl)
        inc     hl

readBitsB:
.l1:    adc     a, a
.l2:    jr      z, copyLZMatch.l7
.l3:    rl      b
        jr      nc, .l1
        ret

decodeLZMatchParam:
        ex      de, hl
        ld      h, a
        ld      a, b                    ; calculate table address L (3 * B - C)
        add     a, a
        add     a, b
        sub     c
        ld      l, a                    ; Carry = 0
        ld      a, h
        ld      h, high decodeTablesBegin
        ld      b, (hl)                 ; B = number of extra bits + 1
        inc     l
        ld      c, (hl)
        inc     l
        ld      h, (hl)
        ld      l, c                    ; HL = base value
        dec     b
        jr      z, .l3
        push    hl
        ld      hl, 0
.l1:    add     a, a
        jp      nz, .l2
        ld      a, (de)
        inc     de
        rla
.l2:    adc     hl, hl
        djnz    .l1
        pop     bc
        add     hl, bc
.l3:    ex      de, hl
        ret

