
codeBegin:

extMain:
main:
        ld a, 2
        cp c
        jr z, lm__02
        inc a
        cp c
        jr z, helpString
        ld a, 5
        cp c
        jp z, errorString
lm__01: xor a
        ret
lm__02: call checkCommandName
        jr nz, lm__01
        jp parseCommand

printString:
        push bc
        push de
        ld d, h
        ld e, l
        jr lhs_01

helpString:
        xor a
        cp b
        jr nz, lhs_03
        push bc
        push de
        ld de, versionMsg
lhs_01: ld a, (de)
        or a
        jr z, lhs_02
        ld b, a
        ld a, 0ffh
        push de
        rst 030h
        defb 7
        pop de
        inc de
        jr lhs_01
lhs_02: pop de
        pop bc
        xor a
        ret
lhs_03: call checkCommandName
        jr z, lhs_04
        xor a
        ret
lhs_04: ld c, a
        push bc
        push de
        ld de, usageMsg
        jr lhs_01

checkCommandName:
        push bc
        push de
        push hl
        ld a, cmdNameLength
        cp b
        jr nz, lccn03
        ld hl, cmdName
lccn01: inc de
        ld a, (de)
        cp (hl)
        jr nz, lccn02
        inc hl
        djnz lccn01
lccn02: ld a, b
lccn03: or a
        pop hl
        pop de
        pop bc
        ret

errorString:
        ld hl, errorTable
les_01: ld a, (hl)
        or a
        ret z
        inc hl
        inc hl
        inc hl
        cp b
        jr nz, les_01
        inc hl
        ld e, (hl)
        inc hl
        ld d, (hl)
        in a, (0b3h)
        ld b, a
        xor a
        ld c, a
        ret

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

; IX+0:   file name / I/O buffer address
; IX+2:   input file bytes remaining
; IX+4:   input file channel
; IX+5:   compressed data checksum
; IX+6:   output file channel
; IX+7:   non-zero if the compressed data includes start addresses
; IX+8:   non-zero: load flag (/L)
; IX+9:   non-zero: test flag (/T)
; IX+10:  IVIEW data block uncompressed size
; IX+12:  decompressor input buffer position (HL)
; IX+14:  decompressor output buffer position (DE)
; IX+16:  decompressor segment table pointer (IY)
; IX+18:  input file name address
; IX+20:  input file name length
; IX+21:  output file name length
; IX+22:  output file name address
; IX+24:  saved IY register
; IX+26:  saved stack pointer
; IX+84:  segment table

parseCommand:
        xor a
        ld h, a
        ld l, a
        add hl, sp
        push hl
        pop ix
        defb 0ddh
        ld l, a
        defb 0ddh
        dec h
        defb 0ddh
        dec h
        ld sp, ix
        defb 0ddh
        ld l, 0a4h
        defb 0ddh
        inc h
        ld (ix+26), l                   ; save stack pointer
        ld (ix+27), h
        push iy
        pop hl
        ld (ix+24), l                   ; save IY register
        ld (ix+25), h
        push ix
        pop hl
        dec h
        ld (ix+0), a                    ; store buffer start address
        ld (ix+1), h
        ld (ix+4), a                    ; input file channel
        ld (ix+6), a                    ; output file channel
        ld (ix+8), a                    ; load flag
        ld (ix+9), a                    ; test flag
        inc h
        ld l, 0f8h                      ; create empty segment table
lpc_01: ld (hl), a
        inc l
        jr nz, lpc_01
        push bc
        push de
        rst 030h                        ; allocate first segment
        defb 24
        or a
        ld a, 0f7h
        jp nz, endCmd
        ld (ix+84), c                   ; store segment number
        pop de
        pop bc
        ld a, (de)
        ld b, a
        inc de
        call getNextFileName            ; skip command name
        push bc
        push de
        call getNextFileName            ; check for /L or /T flag
        cp 2
        jr nz, lpc_04
        inc hl
        ld a, (hl)
        cp 02fh
        jr nz, lpc_04
        inc hl
        ld a, (hl)
        cp 054h
        jr z, lpc_03
        cp 074h
        jr z, lpc_03
        cp 04ch
        jr z, lpc_02
        cp 06ch
        jp nz, cmdError
lpc_02: inc (ix+8)                      ; set load flag
        pop hl
        pop hl
        jr lpc_05
lpc_03: inc (ix+9)                      ; set test flag
        pop hl
        pop hl
        jr lpc_05
lpc_04: pop de
        pop bc
lpc_05: call checkCmdArgs               ; get first file name (required)
        jp z, cmdError
        ld (ix+18), e
        ld (ix+19), d
        call getNextFileName
        ld (ix+20), a
        bit 0, (ix+9)
        jr z, lpc_08
lpc_06: push bc                         ; test mode:
        push de
        ld l, (ix+0)
        ld h, (ix+1)
        ld b, (hl)
lpc_07: inc hl
        ld a, (hl)
        push bc
        ld b, a
        ld a, 0ffh
        rst 030h
        defb 7
        pop bc
        djnz lpc_07
        ld hl, testMsg1
        call printString
        call openFileR
        call decompressFile
        call closeFiles
        call freeMemory
        ld hl, testMsg2
        call printString
        pop de
        pop bc
        call checkCmdArgs
        jp z, endCmd
        call getNextFileName
        jr lpc_06
lpc_08: push bc                         ; extract mode:
        push de
        call openFileR
        pop de
        pop bc
        call checkCmdArgs
        jr nz, lpc_09                   ; is the output file name specified ?
        bit 0, (ix+8)                   ; load the extracted file ?
        jp z, cmdError                  ; no, error: output name is required
        ld hl, tmpFileName              ; yes, use temporary file name
        ld e, (ix+0)                    ; copy the file name to the buffer
        ld d, (ix+1)
        ld c, (hl)
        ld b, 0
        inc hl
        ld (ix+22), l
        ld (ix+23), h
        ld (ix+21), c
        dec hl
        inc bc
        ldir
        jr lpc_10
lpc_09: ld (ix+22), e                   ; use the specified output file name
        ld (ix+23), d
        call getNextFileName
        ld (ix+21), a
lpc_10: ld a, (ix+20)                   ; input and output file name
        cp (ix+21)                      ; must not be the same
        jr nz, lpc_11
        ld b, a
        ld l, (ix+18)
        ld h, (ix+19)
        ld e, (ix+22)
        ld d, (ix+23)
lpc_14: ld a, (de)
        cp (hl)
        jr nz, lpc_11
        inc de
        inc hl
        djnz lpc_14
        jp cmdError
lpc_11: call openFileW
        call decompressFile
        call writeDecompressedData      ; write output file
        call closeFiles
        call freeAllMemory
        xor a
        bit 0, (ix+8)                   ; if load not requested, then done
        jr z, endCmd
        ld l, (ix+22)
        ld h, (ix+23)
        ld e, (ix+0)
        ld d, (ix+1)
        ld a, (ix+21)
        ld (de), a
        inc de
        ld c, a
        ld b, 0
        ldir
        call openFileR                  ; load module:
        ld a, (ix+4)
        ld l, (ix+24)                   ; clean up first
        ld h, (ix+25)                   ; restore IY
        push hl
        pop iy
        ld l, (ix+26)                   ; restore stack pointer
        ld h, (ix+27)
        ld bc, 16                       ; allocate 16 bytes for the header
        or a
        sbc hl, bc
        ld sp, hl
        ld b, a
lpc_12: ld d, h                         ; read next module
        ld e, l
        push bc
        rst 030h
        defb 29
        pop bc
        or a
        jr z, lpc_12
        ld hl, 16                       ; done loading module, clean up
        add hl, sp
        ld sp, hl
        push af
        ld a, b
        rst 030h
        defb 3
        pop af
        ld c, 0
        cp 0ech                         ; check status: .NOMOD ?
        jr nz, lpc_13
        xor a
        ret
lpc_13: or a
        ret

endCmd:
        defb 0ddh
        ld l, 0a4h
        ld l, (ix+24)                   ; restore IY register
        ld h, (ix+25)
        push hl
        pop iy
        ld (ix+23), a
        call freeAllMemory              ; free all allocated segments
        call closeFiles                 ; close all files
        ld l, (ix+26)                   ; restore stack pointer
        ld h, (ix+27)
        ld sp, hl
        ld c, 0
        ld a, (ix+23)
        or a
        ret

checkCmdArgs:
        ld a, b
        or a
        ret z
lcca01: ld a, (de)
        or a
        ret z
        cp 021h
        jr nc, lcca02
        inc de
        djnz lcca01
        xor a
        ret
lcca02: xor a
        inc a
        ret

getNextFileName:
lgnfn1: ld a, (de)                      ; skip leading white space
        or a
        jr z, cmdError
        cp 021h
        jr nc, lgnfn2
        inc de
        djnz lgnfn1
        jr cmdError
lgnfn2: ld l, (ix+0)
        ld h, (ix+1)
lgnfn3: ld a, (de)
        cp 021h
        jr c, lgnfn4
        inc hl
        ld (hl), a
        inc de
        djnz lgnfn3
lgnfn4: ld a, l
        inc hl
        ld (hl), 0
        ld l, 0
        ld (hl), a
        ret

cmdError:
        ld a, 0f0h
        jp endCmd

decompressFile:
        call checkFileHeader
        cp 5
        jp z, decompressProgram
        cp 049h
        jp z, decompressImageFile
        call initializeDecompressor     ; raw compressed data:
        ld (ix+2), 0ffh                 ; "infinite" input data size
        ld (ix+3), 0ffh
        ld (ix+7), 0                    ; no start addresses
        inc (ix+12)
        jp decompressFileData_

writeDecompressedData:
        push ix
        pop hl
        ld l, (ix+16)
        ld a, (hl)
        cp 2
        jp c, closeFiles
        ld de, 04000h
        ld c, (ix+14)
        ld b, (ix+15)
        dec bc
        res 7, b
        res 6, b
        inc bc
        in a, (0b1h)
        push af
        ld a, (hl)
        out (0b1h), a
        ld a, (ix+6)
        rst 030h
        defb 8
        ld c, a
        pop af
        out (0b1h), a
        ld a, c
        or a
        jp z, closeFiles
        jp endCmd

checkFileHeader:
        ld a, (ix+4)                    ; read header
        ld bc, 16
        ld l, (ix+0)
        ld h, (ix+1)
        ld e, l
        ld d, h
        rst 030h
        defb 6
        cp 0e4h
        jr z, lcfh01                    ; EOF ?
        or a
        jp nz, endCmd
        ld a, e
        cp 16
        jr c, lcfh01
        ld a, (hl)                      ; first byte:
        or a
        jr nz, lcfh01                   ; if non-zero, then raw file
        inc hl
        ld a, (hl)                      ; second byte:
        cp 5
        jr z, lcfh06                    ; program file ?
        cp 049h
        jr z, lcfh05                    ; IVIEW image ?
lcfh01: ld a, e                         ; raw compressed data:
        cp 5
        ld a, 0e4h
        jp c, endCmd                    ; length must be at least 5 bytes
        ld a, e
        cp 16
        jr c, lcfh03
lcfh02: ld a, (ix+4)                    ; fill input buffer
        ld bc, 239
        rst 030h
        defb 6
        cp 0e4h
        jr z, lcfh03
        or a
        jp nz, endCmd
lcfh03: ld b, e                         ; initialize checksum
        ld l, (ix+0)
        ld h, (ix+1)
        ld a, 080h
lcfh04: sub 0ach
        rrca
        xor (hl)
        inc l
        djnz lcfh04
        ld (ix+5), a
        ld l, 0feh                      ; move buffer data
        ld e, 0ffh
        ld bc, 255
        lddr
        xor a                           ; return file type 0 (raw data)
        ret
lcfh05: ld l, 10                        ; IVIEW image (header type 049h)
        ld a, (hl)
        cp 1
        jr nz, lcfh02                   ; uncompressed or unknown compression
lcfh06: ld l, 15                        ; program file (header type 5)
        ld a, (hl)
        or a
        jr nz, lcfh02                   ; last byte of header must be zero
        ld l, 1
        ld a, (hl)                      ; return header type (5 or 049h)
        or a
        ret

getDataSize:
lgds01: ld a, (ix+4)
        push bc
        rst 030h
        defb 5
        or a
        jp nz, endCmd
        ld a, b
        pop bc
        ld e, d
        ld d, l
        ld l, h
        ld h, a
        djnz lgds01
        ret

writeByte:
        push bc
        push de
        ld e, (ix+14)
        ld d, (ix+15)
        res 7, d
        set 6, d
        push af
        in a, (0b1h)
        ld b, a
        ld a, (ix+84)
        out (0b1h), a
        pop af
        ld (de), a
        inc de
        push af
        ld a, b
        out (0b1h), a
        pop af
        ld (ix+14), e
        ld (ix+15), d
        pop de
        pop bc
        ret

decompressProgram:
        ld b, 110
        call getDataSize
        dec d
        ld (ix+7), 1                    ; have start addresses
        ld (ix+2), l                    ; compressed size LSB
        ld (ix+3), h                    ; compressed size MSB
        ld (ix+10), e                   ; uncompressed size LSB
        ld (ix+11), d                   ; uncompressed size MSB
        or l
        jp z, fileCRCError              ; zero size is invalid
        ld a, d
        or e
        jp z, fileCRCError
        push de
        call initializeDecompressor
        pop de
        xor a                           ; write file header
        call writeByte
        ld a, 5
        call writeByte
        ld a, e
        call writeByte
        ld a, d
        call writeByte
        xor a
        ld b, 12
ldp_01: call writeByte
        djnz ldp_01
        jp decompressFileData

decompressImageFile:
        call initializeDecompressor
        ld l, 10                        ; write file header
        ld h, (ix+1)
        ld (hl), 0
        ld l, 0
        ld b, 16
ldif01: ld a, (hl)
        inc hl
        call writeByte
        djnz ldif01
        call decompressImageData        ; decompress first block (palette data)
        ld (ix+12), 0                   ; reset read pointer

decompressImageData:
        ld b, 4
        call getDataSize
        ld (ix+7), 0                    ; no start addresses
        ld (ix+2), l                    ; compressed size LSB
        ld (ix+3), h                    ; compressed size MSB
        ld (ix+10), e                   ; uncompressed size LSB
        ld (ix+11), d                   ; uncompressed size MSB
        or l
        jp z, fileCRCError              ; zero size is invalid
        ld a, d
        or e
        jp z, fileCRCError
        jp decompressFileData

initializeDecompressor:
        call freeMemory                 ; free all previously allocated memory
        xor a
        ld (ix+14), a                   ; output buffer position
        ld (ix+15), a
        ld a, (ix+0)                    ; input buffer position
        ld (ix+12), a
        ld a, (ix+1)
        ld (ix+13), a
        push ix
        pop bc
        ld (ix+16), 0f8h                ; segment table pointer
        ld (ix+17), b
        ret

decompressFileData:
        ld (ix+5), 080h                 ; initialize checksum
decompressFileData_:
        ld l, (ix+16)
        ld h, (ix+17)
        push hl
        pop iy                          ; IY = segment table pointer
        ld l, (ix+12)
        ld h, (ix+13)                   ; HL = read buffer position
        ld e, (ix+14)
        ld d, (ix+15)                   ; DE = write buffer position
        call decompressData
        defb 0ddh
        ld l, 0a4h
        jp c, endCmd                    ; if error decompressing data
        ld (ix+12), l                   ; save read buffer position
        ld (ix+13), h
        ld (ix+14), e                   ; save write buffer position
        ld (ix+15), d
        ld a, d
        and 03fh
        or e
        jr nz, ldfd02
        inc iy
ldfd02: push iy
        pop hl
        ld (ix+16), l                   ; save segment table pointer
        ld (ix+17), h
        ld l, (ix+24)
        ld h, (ix+25)
        push hl
        pop iy
        ld a, (ix+5)                    ; verify checksum: must be 0FFh
        sub 0ffh
        jp nz, fileCRCError             ; checksum error
        ret

freeAllMemory:
        ld l, 0f8h
        defb 03ah                       ; = LD a, (nnnn)

freeMemory:
        ld l, 0f9h
        defb 0ddh
        ld a, h
        ld h, a
lfm_01: ld a, (hl)
        or a
        jr z, lfm_03
        ld c, a
        ld a, l
        cp 0fch                         ; only the first 4 segments
        jr nc, lfm_02                   ; are actually allocated
        rst 030h
        defb 25
lfm_02: xor a
        ld (hl), a
lfm_03: inc l
        jr nz, lfm_01
        ret

closeFiles:
        ld a, (ix+4)
        or a
        jr z, lcf_01
        rst 030h
        defb 3
        ld (ix+4), 0
lcf_01: ld a, (ix+6)
        or a
        ret z
        rst 030h
        defb 4
        xor a
        ld (ix+6), a
        ret

openFileR:
        ld a, 1
lofr01: push af
        ld e, (ix+0)
        ld d, (ix+1)
        rst 030h
        defb 1
        or a
        jr z, lofr02
        cp 0f9h
        jp nz, endCmd                   ; error opening file ?
        pop af
        inc a
        cp 0ffh
        jr c, lofr01
        ld a, 0f9h                      ; could not find a free channel
        jp endCmd
lofr02: pop af                          ; file is successfully opened
        ld (ix+4), a
        ret

openFileW:
        ld a, 1
lofw01: push af
        ld e, (ix+0)
        ld d, (ix+1)
        rst 030h
        defb 2
        or a
        jr z, lofw02
        cp 0f9h
        jp nz, endCmd                   ; error opening file ?
        pop af
        inc a
        cp 0ffh
        jr c, lofw01
        ld a, 0f9h                      ; could not find a free channel
        jp endCmd
lofw02: pop af                          ; file is successfully opened
        ld (ix+6), a
        ret

readBlock:
        call setEXOSPaging
        push af
        push bc
        push de
        push hl
        ld d, h
        ld e, l
        push ix
        pop hl
        ld l, 0a4h+2                    ; check the number of bytes remaining:
        ld c, (hl)
        inc l
        ld b, (hl)
        inc l
        ld a, b
        or a
        jr nz, lrbl01                   ; >= 256 bytes ?
        or c
        jr z, fileReadError             ; no data is left to read: error
        jr lrbl02
lrbl01: and c
        inc a
        jr z, lrbl05                    ; if length = 0FFFFh: read all data
        ld bc, 00100h                   ; read at most 256 bytes
lrbl02: ld a, (hl)
        push bc
        rst 030h
        defb 6
        cp 0fbh
        jr z, fileReadError             ; channel does not exist: error
        ld a, b
        or c
        jr nz, fileReadError            ; not enough data was read: error
        pop bc
        ld l, 0a4h+2                    ; update bytes remaining
        ld a, (hl)
        sub c
        ld (hl), a
        inc l
        ld a, (hl)
        sbc a, b
        ld (hl), a
lrbl03: ld l, 0a4h+5                    ; update checksum
        ld a, (hl)
        pop hl
        push hl
        ld b, c
        ld c, 0ach
lrbl04: sub c
        rrca
        xor (hl)
        inc l
        djnz lrbl04
        push ix
        pop hl
        ld l, 0a4h+5
        ld (hl), a
        pop hl
        pop de
        pop bc
        pop af
        call setDecompressPaging
        ret
lrbl05: ld a, (hl)                      ; "infinite" input data length:
        ld bc, 00100h
        rst 030h
        defb 6
        cp 0fbh
        jr z, fileReadError             ; channel does not exist: error
        xor a                           ; calculate the number of bytes read
        sub c
        ld c, a
        ld a, 1
        sbc a, b
        ld b, a                         ; non-zero: update checksum and return,
        or c
        jr nz, lrbl03                   ; otherwise read error

fileReadError:
        ld a, 0e4h
        jp endCmd

fileCRCError:
        ld a, 0d0h
        jp endCmd

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

cmdNameLength   equ     10              ; length of "UNCOMPRES2"
cmdName:
versionMsg:
        defm "UNCOMPRES2 version 1.01"
        defb 00dh, 00ah
        defb 000h
usageMsg:
        defm "UNCOMPRES2 version 1.01"
        defb 00dh, 00ah
        defm "Usage:"
        defb 00dh, 00ah
        defm "  UNCOMPRES2 <infile> <outfile>"
        defb 00dh, 00ah
        defm "    uncompress 'infile' to 'outfile'"
        defb 00dh, 00ah
        defm "  UNCOMPRES2 /L <infile>"
        defb 00dh, 00ah
        defm "    uncompress to 'UNCOMP__.TMP', and"
        defb 00dh, 00ah
        defm "    load the resulting temporary file"
        defb 00dh, 00ah
        defm "  UNCOMPRES2 /L <infile> <outfile>"
        defb 00dh, 00ah
        defm "    uncompress 'infile' to 'outfile',"
        defb 00dh, 00ah
        defm "    and then load 'outfile'"
        defb 00dh, 00ah
        defm "  UNCOMPRES2 /T <infile> [infile2...]"
        defb 00dh, 00ah
        defm "    test all input files"
        defb 00dh, 00ah
        defb 000h

testMsg1:
        defm ": "
        defb 000h

testMsg2:
        defm "OK"
        defb 00dh, 00ah, 000h

tmpFileName:
        defb 12
        defm "UNCOMP__.TMP"

errorTable:
        defb 000h

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

; border effects are disabled if this is set to any non-zero value
NO_BORDER_FX            equ     0

decompressData:
        push hl
        exx
        pop hl                          ; HL' = compressed data read address
        dec l
        exx
        ld hl, 00000h
        add hl, sp
        defb 0ddh
        ld l, decodeTableEnd+6
        ld sp, ix
        in a, (0b2h)                    ; save memory paging,
        push af
        in a, (0b1h)
        push af
        push hl                         ; and stack pointer
        ld sp, hl
        call setDecompressPaging
        call allocateSegment            ; get first output segment
        dec de
        exx
        ld e, 080h                      ; initialize shift register
        exx
        call read8Bits                  ; skip checksum byte
ldd_01: call decompressDataBlock        ; decompress all blocks
        jr z, ldd_01
        call setEXOSPaging
        inc de
        xor a
        jr decompressDone

memoryError:
        ld a, 0f7h                      ; .NORAM

decompressDone:
        ld c, a                         ; save error code
        defb 0ddh
        ld l, decodeTableEnd
        ld sp, ix
        pop hl                          ; restore stack pointer,
        pop af                          ; memory paging,
        out (0b1h), a
        pop af
        out (0b2h), a
  if NO_BORDER_FX == 0
        xor a                           ; and border color
        out (081h), a
  endif
        ld sp, hl
        exx
        inc l
        push hl
        exx
        pop hl
        ld a, c                         ; on success: return A=0, Z=1, C=0
        add a, 0ffh                     ; on error: return A=error, Z=0, C=1
        inc a
        ret

writeBlock:
        inc d
        bit 6, d
        ret z
        defb 0fdh
        inc l
        jr nz, allocateSegment
        defb 0fdh
        ld l, 0fch

allocateSegment:
        set 7, d                        ; write decompressed data to page 2
        res 6, d
        push af
        push bc
        push de
        defb 0ddh
        ld l, 0a4h
        ld a, (ix+6)
        or a
        call nz, writeSegment           ; writing output file ?
        defb 0fdh
        ld a, l
        cp 0fch
        jr nc, las_03                   ; have 4 segments already ?
        ld a, (iy+0)
        or a
        jr nz, las_01                   ; use pre-allocated segment ?
        call setEXOSPaging
        rst 030h                        ; no, allocate new segment
        defb 24
        or a
        jr nz, las_02
        call setDecompressPaging
        ld a, c
        ld (iy+0), a                    ; save segment number
las_01: out (0b2h), a
        pop de
        pop bc
        pop af
        ret
las_02: cp 0f5h                         ; memory allocation error:
        jr z, memoryError               ; no segment ?
        rst 030h                        ; shared segment, free it first
        defb 25
        jr memoryError
las_03: ld a, (iy-4)                    ; use a 64K ring buffer
        ld (iy+0), a
        jr las_01

writeSegment:
        defb 0fdh
        ld a, l
        cp 0f8h                         ; if allocating first segment,
        ret z                           ; there is no data to write yet
        call setEXOSPaging
        in a, (0b1h)
        push af
        ld a, (iy-1)
        out (0b1h), a                   ; write data from page 1
        ld de, 04000h
        ld b, d
        ld c, e
        ld a, (ix+6)
        rst 030h
        defb 8
        ld c, a
        pop af
        out (0b1h), a
        ld a, c
        call setDecompressPaging
        or a
        ret z
        jp decompressDone               ; if there is an error writing the file

setEXOSPaging:
        di
        push af
        set 7, h
        exx
        set 7, h
        exx
        push hl
        ld a, 0ffh
        out (0b2h), a
        ld hl, 00000h
        add hl, sp
        set 7, h
        ld sp, hl
        ld a, (0bffch)
        out (0b0h), a
        defb 0ddh
        ld a, h
        or 080h
        defb 0ddh
        ld h, a
        defb 0fdh
        ld a, h
        or 080h
        defb 0fdh
        ld h, a
        pop hl
        pop af
        ei
        ret

setDecompressPaging:
        di
        push af
        push hl
        ld a, 0ffh
        out (0b0h), a
        ld hl, 00000h
        add hl, sp
        res 7, h
        ld sp, hl
        defb 0ddh
        ld a, h
        and 07fh
        defb 0ddh
        ld h, a
        defb 0fdh
        ld a, h
        and 07fh
        defb 0fdh
        ld h, a
        ld a, (iy+0)
        out (0b2h), a
        pop hl
        res 7, h
        exx
        res 7, h
        exx
        pop af
        ret

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

; A':  temp. register
; BC': symbols (literal byte or match code) remaining
; D':  prefix size for LZ77 matches with length >= 3 bytes
; E':  shift register
; HL': compressed data read address
; A:   temp. register
; BC:  temp. register (number of literal/LZ77 bytes to copy)
; DE:  decompressed data write address
; HL:  temp. register (literal/LZ77 data source address)
; IXH: decode table upper byte
; IY:  segment table pointer

nLengthSlots            equ 8
nOffs1Slots             equ 4
nOffs2Slots             equ 8
maxOffs3Slots           equ 32
totalSlots              equ nLengthSlots+nOffs1Slots+nOffs2Slots+maxOffs3Slots
; NOTE: the upper byte of the address of all table entries must be the same
slotBitsTable           equ 00000h
;slotBaseLowTable       equ slotBitsTable+totalSlots
;slotBaseHighTable      equ slotBaseLowTable+totalSlots
slotBitsTableL          equ slotBitsTable
;slotBaseLowTableL      equ slotBaseLowTable
;slotBaseHighTableL     equ slotBaseHighTable
slotBitsTableO1         equ slotBitsTableL+nLengthSlots
;slotBaseLowTableO1     equ slotBaseLowTableL+nLengthSlots
;slotBaseHighTableO1    equ slotBaseHighTableL+nLengthSlots
slotBitsTableO2         equ slotBitsTableO1+nOffs1Slots
;slotBaseLowTableO2     equ slotBaseLowTableO1+nOffs1Slots
;slotBaseHighTableO2    equ slotBaseHighTableO1+nOffs1Slots
slotBitsTableO3         equ slotBitsTableO2+nOffs2Slots
;slotBaseLowTableO3     equ slotBaseLowTableO2+nOffs2Slots
;slotBaseHighTableO3    equ slotBaseHighTableO2+nOffs2Slots
decodeTableEnd          equ slotBitsTable+(totalSlots*3)

decompressDataBlock:
        defb 0ddh
        ld l, 0a4h
        ld a, (ix+7)
        or a
        jr z, lddb15
        call read8Bits                  ; ignore start address
        call read8Bits
lddb15: call read8Bits                  ; read number of symbols - 1 (BC)
        ld c, a                         ; NOTE: MSB is in C, and LSB is in B
        call read8Bits
        ld b, a
        inc b
        inc c
        ld a, 040h
        call readBits                   ; read flag bits
        srl a
        push af                         ; save last block flag (A=1,Z=0)
        jr c, lddb01                    ; is compression enabled ?
        exx                             ; no, copy uncompressed literal data
        ld bc, 00101h
        jr lddb12
lddb01: push bc                         ; compression enabled:
        ld a, 040h
        call readBits                   ; get prefix size for length >= 3 bytes
        exx
        ld b, a
        inc b
        ld a, 002h
        ld d, 080h
lddb02: add a, a
        srl d                           ; D' = prefix size code for readBits
        djnz lddb02
        pop bc                          ; store the number of symbols in BC'
        exx
        add a, nLengthSlots+nOffs1Slots+nOffs2Slots-3
        ld c, a                         ; store total table size - 3 in C
        push de                         ; save decompressed data write address
        defb 0ddh                       ; initialize decode tables
        ld l, slotBitsTable&0ffh
lddb03: sbc hl, hl                      ; set initial base value (carry is 0)
lddb04: ld (ix+totalSlots), l           ; store base value LSB
        ld (ix+(totalSlots*2)), h       ; store base value MSB
        ld a, 010h
        call readBits
        ld (ix+0), a                    ; store the number of bits to read
        ex de, hl
        ld hl, 00001h                   ; calculate 1 << nBits
        jr z, lddb06
        ld b, a
lddb05: add hl, hl
        djnz lddb05
lddb06: add hl, de                      ; calculate new base value
        defb 0ddh
        inc l
        defb 0ddh
        ld a, l
        cp slotBitsTableO1&0ffh
        jr z, lddb03                    ; end of length decode table ?
        cp slotBitsTableO2&0ffh
        jr z, lddb03                    ; end of offset table for length=1 ?
        cp slotBitsTableO3&0ffh
        jr z, lddb03                    ; end of offset table for length=2 ?
        dec c
        jr nz, lddb04                   ; continue until all tables are read
        pop de                          ; DE = decompressed data write address
        exx
lddb07: sla e
        jp nz, lddb08
        inc l
        call z, readBlock
        ld e, (hl)
        rl e
lddb08: jr nc, lddb13                   ; literal byte ?
        ld a, 0f8h
lddb09: sla e
        jp nz, lddb10
        inc l
        call z, readBlock
        ld e, (hl)
        rl e
lddb10: jr nc, copyLZMatch              ; LZ77 match ?
        inc a
        jr nz, lddb09
        exx
        ld c, a
        call read8Bits                  ; get literal sequence length - 17
        add a, 16
        ld b, a
        rl c
        inc b                           ; B: (length - 1) LSB + 1
        inc c                           ; C: (length - 1) MSB + 1
lddb11: exx                             ; copy literal sequence
lddb12: inc l
        call z, readBlock
        ld a, (hl)
        exx
        inc e
        call z, writeBlock
        ld (de), a
        djnz lddb11
        dec c
        jr nz, lddb11
        jr lddb14
lddb13: inc l                           ; copy literal byte
        call z, readBlock
        ld a, (hl)
        exx
        inc e
        call z, writeBlock
        ld (de), a
lddb14: exx
        djnz lddb07
        dec c
        jr nz, lddb07
        exx
        pop af                          ; return with last block flag
        ret                             ; (A=1,Z=0 if last block)

copyLZMatch:
        exx
        add a, (slotBitsTableL+8)&0ffh
        defb 0ddh                       ; decode match length - 1
        ld l, a
        xor a
        ld h, a
        ld b, (ix+0)
        cp b
        jr z, lrev03
lrev01: exx
        sla e
        jp nz, lrev02
        inc l
        call z, readBlock
        ld e, (hl)
        rl e
lrev02: exx
        adc a, a
        rl h
        djnz lrev01
lrev03: add a, (ix+totalSlots)
        ld l, a                         ; L = (length - 1) LSB
        ld a, h
        adc a, (ix+(totalSlots*2))
        ld c, a                         ; C = (length - 1) MSB
        or l
        jr z, lclm02                    ; length == 1 byte ?
        dec a
        or c
        jr nz, lclm01                   ; length >= 3 bytes ?
        ld a, 020h                      ; length == 2 bytes, read 3 prefix bits
        ld b, slotBitsTableO2&0ffh
        jr lclm03
lclm01: exx                             ; length >= 3 bytes,
        ld a, d                         ; variable prefix size
        exx
        ld b, slotBitsTableO3&0ffh
        jr lclm03
lclm02: ld a, 040h                      ; length == 1 byte, read 2 prefix bits
        ld b, slotBitsTableO1&0ffh
lclm03: call readBits                   ; read offset prefix bits
        add a, b
        defb 0ddh                       ; decode match offset
        ld l, a
        xor a
        ld h, a
        ld b, (ix+0)
        cp b
        jr z, lrev13
lrev11: exx
        sla e
        jr z, lrev14
lrev12: exx
        adc a, a
        rl h
        djnz lrev11
lrev13: add a, (ix+totalSlots)
        ld b, a
        ld a, h
        adc a, (ix+(totalSlots*2))
        ld h, a
        ld a, e                         ; calculate LZ77 match read address
  if NO_BORDER_FX == 0
        out (081h), a
  endif
        sub b
        ld b, l                         ; length LSB
        ld l, a
        ld a, d
        sbc a, h
        ld h, a
        jr c, lclm15                    ; set up memory paging
        jp p, lclm13
        in a, (0b2h)
lclm04: res 7, h                        ; read from page 1
lclm05: set 6, h
lclm06: out (0b1h), a
        inc b                           ; B: (length - 1) LSB + 1
        inc c                           ; C: (length - 1) MSB + 1
lclm07: inc e                           ; copy match data
        jr z, lclm10
lclm08: ld a, (hl)
        ld (de), a
        inc l
        jr z, lclm11
lclm09: djnz lclm07
        dec c
        jp z, lddb14                    ; return to main decompress loop
        jr lclm07
lclm10: call writeBlock
        jr lclm08
lclm11: inc h
        jp p, lclm09
        ld h, 040h
        push iy
        in a, (0b1h)
lclm12: cp (iy+0)
        dec iy
        jr nz, lclm12
        ld a, (iy+2)
        out (0b1h), a                   ; read next segment
        pop iy
        jr lclm09
lclm13: add a, a
        jp p, lclm14
        ld a, (iy-1)
        jr lclm06
lclm14: ld a, (iy-2)
        jr lclm05
lclm15: add a, a
        jp p, lclm16
        ld a, (iy-3)
        jr lclm04
lclm16: ld a, (iy-4)
        jr lclm04
lrev14: inc l
        call z, readBlock
        ld e, (hl)
        rl e
        jp lrev12

read8Bits:
        ld a, 001h

readBits:
        exx
lrb_01: sla e
        jr z, lrb_03
lrb_02: adc a, a
        jp nc, lrb_01
        exx
        ret
lrb_03: inc l
        call z, readBlock
        ld e, (hl)
        rl e
        jp lrb_02

codeEnd:

