' -----------------------------------------------------------------------------
' BORIEL LIBRARY FOR ZX0 DECOMPRESSORS
'
' USAGE:
'
' 1. Include this library in your program:
'    #include <zx0.bas>
'
' 2. Choose a ZX0 decompressor to use in your program, for instance:
'    dzx0Turbo(51200, 16384)
'
' 3. Compile your program:
'    zxb.exe -T -B prog.bas
'
' Original version and further information is available at
' https://github.com/einar-saukas/ZX0
'
' Copyleft (k) Einar Saukas
' -----------------------------------------------------------------------------

#ifndef __LIBRARY_ZX0__
#define __LIBRARY_ZX0__
#pragma push(case_insensitive)
#pragma case_insensitive = true


' -----------------------------------------------------------------------------
' Decompress (from source to destination address) data that was previously
' compressed using ZX0. This is the smallest version of the ZX0 decompressor.
'
' Parameters:
'     src: source address (compressed data)
'     dst: destination address (decompressing)
' -----------------------------------------------------------------------------
sub FASTCALL dzx0Standard(src as UINTEGER, dst as UINTEGER)
    asm
        pop bc          ; RET address
        pop de          ; DE=dst
        push bc         ; restore RET address
; -----------------------------------------------------------------------------
; ZX0 decoder by Einar Saukas & Urusergi
; "Standard" version (68 bytes only)
; -----------------------------------------------------------------------------
; Parameters:
;   HL: source address (compressed data)
;   DE: destination address (decompressing)
; -----------------------------------------------------------------------------
dzx0_standard:
        ld      bc, $ffff               ; preserve default offset 1
        push    bc
        inc     bc
        ld      a, $80
dzx0s_literals:
        call    dzx0s_elias             ; obtain length
        ldir                            ; copy literals
        add     a, a                    ; copy from last offset or new offset?
        jr      c, dzx0s_new_offset
        call    dzx0s_elias             ; obtain length
dzx0s_copy:
        ex      (sp), hl                ; preserve source, restore offset
        push    hl                      ; preserve offset
        add     hl, de                  ; calculate destination - offset
        ldir                            ; copy from offset
        pop     hl                      ; restore offset
        ex      (sp), hl                ; preserve offset, restore source
        add     a, a                    ; copy from literals or new offset?
        jr      nc, dzx0s_literals
dzx0s_new_offset:
        pop     bc                      ; discard last offset
        ld      c, $fe                  ; prepare negative offset
        call    dzx0s_elias_loop        ; obtain offset MSB
        inc     c
        ret     z                       ; check end marker
        ld      b, c
        ld      c, (hl)                 ; obtain offset LSB
        inc     hl
        rr      b                       ; last offset bit becomes first length bit
        rr      c
        push    bc                      ; preserve new offset
        ld      bc, 1                   ; obtain length
        call    nc, dzx0s_elias_backtrack
        inc     bc
        jr      dzx0s_copy
dzx0s_elias:
        inc     c                       ; interlaced Elias gamma coding
dzx0s_elias_loop:
        add     a, a
        jr      nz, dzx0s_elias_skip
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        rla
dzx0s_elias_skip:
        ret     c
dzx0s_elias_backtrack:
        add     a, a
        rl      c
        rl      b
        jr      dzx0s_elias_loop
; -----------------------------------------------------------------------------
    end asm
end sub


' -----------------------------------------------------------------------------
' Decompress (from source to destination address) data that was previously
' compressed backwards using ZX0. This is the smallest version of the ZX0
' decompressor.
'
' Parameters:
'     src: last source address (compressed data)
'     dst: last destination address (decompressing)
' -----------------------------------------------------------------------------
sub FASTCALL dzx0StandardBack(src as UINTEGER, dst as UINTEGER)
    asm
        pop bc          ; RET address
        pop de          ; DE=dst
        push bc         ; restore RET address
; -----------------------------------------------------------------------------
; ZX0 decoder by Einar Saukas
; "Standard" version (69 bytes only) - BACKWARDS VARIANT
; -----------------------------------------------------------------------------
; Parameters:
;   HL: last source address (compressed data)
;   DE: last destination address (decompressing)
; -----------------------------------------------------------------------------
dzx0_standard_back:
        ld      bc, 1                   ; preserve default offset 1
        push    bc
        ld      a, $80
dzx0sb_literals:
        call    dzx0sb_elias            ; obtain length
        lddr                            ; copy literals
        inc     c
        add     a, a                    ; copy from last offset or new offset?
        jr      c, dzx0sb_new_offset
        call    dzx0sb_elias            ; obtain length
dzx0sb_copy:
        ex      (sp), hl                ; preserve source, restore offset
        push    hl                      ; preserve offset
        add     hl, de                  ; calculate destination - offset
        lddr                            ; copy from offset
        inc     c
        pop     hl                      ; restore offset
        ex      (sp), hl                ; preserve offset, restore source
        add     a, a                    ; copy from literals or new offset?
        jr      nc, dzx0sb_literals
dzx0sb_new_offset:
        inc     sp                      ; discard last offset
        inc     sp
        call    dzx0sb_elias            ; obtain offset MSB
        dec     b
        ret     z                       ; check end marker
        dec     c                       ; adjust for positive offset
        ld      b, c
        ld      c, (hl)                 ; obtain offset LSB
        dec     hl
        srl     b                       ; last offset bit becomes first length bit
        rr      c
        inc     bc
        push    bc                      ; preserve new offset
        ld      bc, 1                   ; obtain length
        call    c, dzx0sb_elias_backtrack
        inc     bc
        jr      dzx0sb_copy
dzx0sb_elias_backtrack:
        add     a, a
        rl      c
        rl      b
dzx0sb_elias:
        add     a, a                    ; inverted interlaced Elias gamma coding
        jr      nz, dzx0sb_elias_skip
        ld      a, (hl)                 ; load another group of 8 bits
        dec     hl
        rla
dzx0sb_elias_skip:
        jr      c, dzx0sb_elias_backtrack
        ret
; -----------------------------------------------------------------------------
    end asm
end sub


' -----------------------------------------------------------------------------
' Decompress (from source to destination address) data that was previously
' compressed using ZX0. This is the intermediate version of the ZX0
' decompressor, providing the best balance between speed and size.
'
' Parameters:
'     src: source address (compressed data)
'     dst: destination address (decompressing)
' -----------------------------------------------------------------------------
sub FASTCALL dzx0Turbo(src as UINTEGER, dst as UINTEGER)
    asm
        pop bc          ; RET address
        pop de          ; DE=dst
        push bc         ; restore RET address
; -----------------------------------------------------------------------------
; ZX0 decoder by Einar Saukas & introspec
; "Turbo" version (126 bytes, 21% faster)
; -----------------------------------------------------------------------------
; Parameters:
;   HL: source address (compressed data)
;   DE: destination address (decompressing)
; -----------------------------------------------------------------------------
dzx0_turbo:
        ld      bc, $ffff               ; preserve default offset 1
        ld      (dzx0t_last_offset+1), bc
        inc     bc
        ld      a, $80
        jr      dzx0t_literals
dzx0t_new_offset:
        ld      c, $fe                  ; prepare negative offset
        add     a, a
        jp      nz, dzx0t_new_offset_skip
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        rla
dzx0t_new_offset_skip:
        call    nc, dzx0t_elias         ; obtain offset MSB
        inc     c
        ret     z                       ; check end marker
        ld      b, c
        ld      c, (hl)                 ; obtain offset LSB
        inc     hl
        rr      b                       ; last offset bit becomes first length bit
        rr      c
        ld      (dzx0t_last_offset+1), bc ; preserve new offset
        ld      bc, 1                   ; obtain length
        call    nc, dzx0t_elias
        inc     bc
dzx0t_copy:
        push    hl                      ; preserve source
dzx0t_last_offset:
        ld      hl, 0                   ; restore offset
        add     hl, de                  ; calculate destination - offset
        ldir                            ; copy from offset
        pop     hl                      ; restore source
        add     a, a                    ; copy from literals or new offset?
        jr      c, dzx0t_new_offset
dzx0t_literals:
        inc     c                       ; obtain length
        add     a, a
        jp      nz, dzx0t_literals_skip
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        rla
dzx0t_literals_skip:
        call    nc, dzx0t_elias
        ldir                            ; copy literals
        add     a, a                    ; copy from last offset or new offset?
        jr      c, dzx0t_new_offset
        inc     c                       ; obtain length
        add     a, a
        jp      nz, dzx0t_last_offset_skip
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        rla
dzx0t_last_offset_skip:
        call    nc, dzx0t_elias
        jp      dzx0t_copy
dzx0t_elias:
        add     a, a                    ; interlaced Elias gamma coding
        rl      c
        add     a, a
        jr      nc, dzx0t_elias
        ret     nz
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        rla
        ret     c
        add     a, a
        rl      c
        add     a, a
        ret     c
        add     a, a
        rl      c
        add     a, a
        ret     c
        add     a, a
        rl      c
        add     a, a
        ret     c
dzx0t_elias_loop:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jr      nc, dzx0t_elias_loop
        ret     nz
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        rla
        jr      nc, dzx0t_elias_loop
        ret
; -----------------------------------------------------------------------------
    end asm
end sub


' -----------------------------------------------------------------------------
' Decompress (from source to destination address) data that was previously
' compressed backwards using ZX0. This is the intermediate version of the ZX0
' decompressor, providing the best balance between speed and size.
'
' Parameters:
'     src: last source address (compressed data)
'     dst: last destination address (decompressing)
' -----------------------------------------------------------------------------
sub FASTCALL dzx0TurboBack(src as UINTEGER, dst as UINTEGER)
    asm
        pop bc          ; RET address
        pop de          ; DE=dst
        push bc         ; restore RET address
; -----------------------------------------------------------------------------
; ZX0 decoder by Einar Saukas & introspec
; "Turbo" version (126 bytes, 21% faster) - BACKWARDS VARIANT
; -----------------------------------------------------------------------------
; Parameters:
;   HL: last source address (compressed data)
;   DE: last destination address (decompressing)
; -----------------------------------------------------------------------------
dzx0_turbo_back:
        ld      bc, 1                   ; preserve default offset 1
        ld      (dzx0tb_last_offset+1), bc
        ld      a, $80
        jr      dzx0tb_literals
dzx0tb_new_offset:
        add     a, a                    ; obtain offset MSB
        call    c, dzx0tb_elias
        dec     b
        ret     z                       ; check end marker
        dec     c                       ; adjust for positive offset
        ld      b, c
        ld      c, (hl)                 ; obtain offset LSB
        dec     hl
        srl     b                       ; last offset bit becomes first length bit
        rr      c
        inc     bc
        ld      (dzx0tb_last_offset+1), bc ; preserve new offset
        ld      bc, 1                   ; obtain length
        call    c, dzx0tb_elias_loop
        inc     bc
dzx0tb_copy:
        push    hl                      ; preserve source
dzx0tb_last_offset:
        ld      hl, 0                   ; restore offset
        add     hl, de                  ; calculate destination - offset
        lddr                            ; copy from offset
        inc     c
        pop     hl                      ; restore source
        add     a, a                    ; copy from literals or new offset?
        jr      c, dzx0tb_new_offset
dzx0tb_literals:
        add     a, a                    ; obtain length
        call    c, dzx0tb_elias
        lddr                            ; copy literals
        inc     c
        add     a, a                    ; copy from last offset or new offset?
        jr      c, dzx0tb_new_offset
        add     a, a                    ; obtain length
        call    c, dzx0tb_elias
        jp      dzx0tb_copy
dzx0tb_elias_loop:
        add     a, a
        rl      c
        add     a, a
        ret     nc
dzx0tb_elias:
        jp      nz, dzx0tb_elias_loop   ; inverted interlaced Elias gamma coding
        ld      a, (hl)                 ; load another group of 8 bits
        dec     hl
        rla
        ret     nc
        add     a, a
        rl      c
        add     a, a
        ret     nc
        add     a, a
        rl      c
        add     a, a
        ret     nc
        add     a, a
        rl      c
        add     a, a
        ret     nc
dzx0tb_elias_reload:
        add     a, a
        rl      c
        rl      b
        add     a, a
        ld      a, (hl)                 ; load another group of 8 bits
        dec     hl
        rla
        ret     nc
        add     a, a
        rl      c
        rl      b
        add     a, a
        ret     nc
        add     a, a
        rl      c
        rl      b
        add     a, a
        ret     nc
        add     a, a
        rl      c
        rl      b
        add     a, a
        jr      c, dzx0tb_elias_reload
        ret
; -----------------------------------------------------------------------------
    end asm
end sub


' -----------------------------------------------------------------------------
' Decompress (from source to destination address) data that was previously
' compressed using ZX0. This is the fastest version of the ZX0 decompressor.
'
' Parameters:
'     src: source address (compressed data)
'     dst: destination address (decompressing)
' -----------------------------------------------------------------------------
sub FASTCALL dzx0Mega(src as UINTEGER, dst as UINTEGER)
    asm
        pop bc          ; RET address
        pop de          ; DE=dst
        push bc         ; restore RET address
; -----------------------------------------------------------------------------
; ZX0 decoder by Einar Saukas
; "Mega" version (673 bytes, 28% faster)
; -----------------------------------------------------------------------------
; Parameters:
;   HL: source address (compressed data)
;   DE: destination address (decompressing)
; -----------------------------------------------------------------------------
dzx0_mega:
        ld      bc, $ffff               ; preserve default offset 1
        ld      (dzx0m_last_offset+1), bc
        inc     bc
        jr      dzx0m_literals0

dzx0m_new_offset6:
        ld      c, $fe                  ; prepare negative offset
        add     a, a                    ; obtain offset MSB
        jp      c, dzx0m_new_offset5
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_new_offset3
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_new_offset1
dzx0m_elias_offset1:
        add     a, a
        rl      c
        rl      b
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        add     a, a
        jp      nc, dzx0m_elias_offset7
dzx0m_new_offset7:
        inc     c
        ret     z                       ; check end marker
        ld      b, c
        ld      c, (hl)                 ; obtain offset LSB
        inc     hl
        rr      b                       ; last offset bit becomes first length bit
        rr      c
        ld      (dzx0m_last_offset+1), bc ; preserve new offset
        ld      bc, 1
        jp      c, dzx0m_length7        ; obtain length
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_length5
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_length3
dzx0m_elias_length3:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      nc, dzx0m_elias_length1
dzx0m_length1:
        push    hl                      ; preserve source
        ld      hl, (dzx0m_last_offset+1)
        add     hl, de                  ; calculate destination - offset
        ldir                            ; copy from offset
        inc     c
        ldi                             ; copy one more from offset
        pop     hl                      ; restore source
        add     a, a                    ; copy from literals or new offset?
        jr      c, dzx0m_new_offset0
dzx0m_literals0:
        inc     c
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        add     a, a                    ; obtain length
        jp      c, dzx0m_literals7
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_literals5
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_literals3
dzx0m_elias_literals3:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      nc, dzx0m_elias_literals1
dzx0m_literals1:
        ldir                            ; copy literals
        add     a, a                    ; copy from last offset or new offset?
        jr      c, dzx0m_new_offset0
        inc     c
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        add     a, a                    ; obtain length
        jp      c, dzx0m_reuse7
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_reuse5
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_reuse3
dzx0m_elias_reuse3:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      nc, dzx0m_elias_reuse1
dzx0m_reuse1:
        push    hl                      ; preserve source
        ld      hl, (dzx0m_last_offset+1)
        add     hl, de                  ; calculate destination - offset
        ldir                            ; copy from offset
        pop     hl                      ; restore source
        add     a, a                    ; copy from literals or new offset?
        jr      nc, dzx0m_literals0

dzx0m_new_offset0:
        ld      c, $fe                  ; prepare negative offset
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        add     a, a                    ; obtain offset MSB
        jp      c, dzx0m_new_offset7
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_new_offset5
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_new_offset3
dzx0m_elias_offset3:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      nc, dzx0m_elias_offset1
dzx0m_new_offset1:
        inc     c
        ret     z                       ; check end marker
        ld      b, c
        ld      c, (hl)                 ; obtain offset LSB
        inc     hl
        rr      b                       ; last offset bit becomes first length bit
        rr      c
        ld      (dzx0m_last_offset+1), bc ; preserve new offset
        ld      bc, 1
        jp      c, dzx0m_length1        ; obtain length
        add     a, a
        rl      c
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        add     a, a
        jp      c, dzx0m_length7
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_length5
dzx0m_elias_length5:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      nc, dzx0m_elias_length3
dzx0m_length3:
        push    hl                      ; preserve source
        ld      hl, (dzx0m_last_offset+1)
        add     hl, de                  ; calculate destination - offset
        ldir                            ; copy from offset
        inc     c
        ldi                             ; copy one more from offset
        pop     hl                      ; restore source
        add     a, a                    ; copy from literals or new offset?
        jr      c, dzx0m_new_offset2
dzx0m_literals2:
        inc     c
        add     a, a                    ; obtain length
        jp      c, dzx0m_literals1
        add     a, a
        rl      c
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        add     a, a
        jp      c, dzx0m_literals7
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_literals5
dzx0m_elias_literals5:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      nc, dzx0m_elias_literals3
dzx0m_literals3:
        ldir                            ; copy literals
        add     a, a                    ; copy from last offset or new offset?
        jr      c, dzx0m_new_offset2
        inc     c
        add     a, a                    ; obtain length
        jp      c, dzx0m_reuse1
        add     a, a
        rl      c
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        add     a, a
        jp      c, dzx0m_reuse7
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_reuse5
dzx0m_elias_reuse5:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      nc, dzx0m_elias_reuse3
dzx0m_reuse3:
        push    hl                      ; preserve source
        ld      hl, (dzx0m_last_offset+1)
        add     hl, de                  ; calculate destination - offset
        ldir                            ; copy from offset
        pop     hl                      ; restore source
        add     a, a                    ; copy from literals or new offset?
        jr      nc, dzx0m_literals2

dzx0m_new_offset2:
        ld      c, $fe                  ; prepare negative offset
        add     a, a                    ; obtain offset MSB
        jp      c, dzx0m_new_offset1
        add     a, a
        rl      c
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        add     a, a
        jp      c, dzx0m_new_offset7
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_new_offset5
dzx0m_elias_offset5:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      nc, dzx0m_elias_offset3
dzx0m_new_offset3:
        inc     c
        ret     z                       ; check end marker
        ld      b, c
        ld      c, (hl)                 ; obtain offset LSB
        inc     hl
        rr      b                       ; last offset bit becomes first length bit
        rr      c
        ld      (dzx0m_last_offset+1), bc ; preserve new offset
        ld      bc, 1
        jp      c, dzx0m_length3        ; obtain length
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_length1
        add     a, a
        rl      c
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        add     a, a
        jp      c, dzx0m_length7
dzx0m_elias_length7:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      nc, dzx0m_elias_length5
dzx0m_length5:
        push    hl                      ; preserve source
        ld      hl, (dzx0m_last_offset+1)
        add     hl, de                  ; calculate destination - offset
        ldir                            ; copy from offset
        inc     c
        ldi                             ; copy one more from offset
        pop     hl                      ; restore source
        add     a, a                    ; copy from literals or new offset?
        jr      c, dzx0m_new_offset4
dzx0m_literals4:
        inc     c
        add     a, a                    ; obtain length
        jp      c, dzx0m_literals3
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_literals1
        add     a, a
        rl      c
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        add     a, a
        jp      c, dzx0m_literals7
dzx0m_elias_literals7:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      nc, dzx0m_elias_literals5
dzx0m_literals5:
        ldir                            ; copy literals
        add     a, a                    ; copy from last offset or new offset?
        jr      c, dzx0m_new_offset4
        inc     c
        add     a, a                    ; obtain length
        jp      c, dzx0m_reuse3
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_reuse1
        add     a, a
        rl      c
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        add     a, a
        jp      c, dzx0m_reuse7
dzx0m_elias_reuse7:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      nc, dzx0m_elias_reuse5
dzx0m_reuse5:
        push    hl                      ; preserve source
        ld      hl, (dzx0m_last_offset+1)
        add     hl, de                  ; calculate destination - offset
        ldir                            ; copy from offset
        pop     hl                      ; restore source
        add     a, a                    ; copy from literals or new offset?
        jr      nc, dzx0m_literals4

dzx0m_new_offset4:
        ld      c, $fe                  ; prepare negative offset
        add     a, a                    ; obtain offset MSB
        jp      c, dzx0m_new_offset3
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_new_offset1
        add     a, a
        rl      c
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        add     a, a
        jp      c, dzx0m_new_offset7
dzx0m_elias_offset7:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      nc, dzx0m_elias_offset5
dzx0m_new_offset5:
        inc     c
        ret     z                       ; check end marker
        ld      b, c
        ld      c, (hl)                 ; obtain offset LSB
        inc     hl
        rr      b                       ; last offset bit becomes first length bit
        rr      c
        ld      (dzx0m_last_offset+1), bc ; preserve new offset
        ld      bc, 1
        jp      c, dzx0m_length5        ; obtain length
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_length3
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_length1
dzx0m_elias_length1:
        add     a, a
        rl      c
        rl      b
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        add     a, a
        jp      nc, dzx0m_elias_length7
dzx0m_length7:
        push    hl                      ; preserve source
        ld      hl, (dzx0m_last_offset+1)
        add     hl, de                  ; calculate destination - offset
        ldir                            ; copy from offset
        inc     c
        ldi                             ; copy one more from offset
        pop     hl                      ; restore source
        add     a, a                    ; copy from literals or new offset?
        jp      c, dzx0m_new_offset6
dzx0m_literals6:
        inc     c
        add     a, a                    ; obtain length
        jp      c, dzx0m_literals5
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_literals3
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_literals1
dzx0m_elias_literals1:
        add     a, a
        rl      c
        rl      b
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        add     a, a
        jp      nc, dzx0m_elias_literals7
dzx0m_literals7:
        ldir                            ; copy literals
        add     a, a                    ; copy from last offset or new offset?
        jp      c, dzx0m_new_offset6
        inc     c
        add     a, a                    ; obtain length
        jp      c, dzx0m_reuse5
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_reuse3
        add     a, a
        rl      c
        add     a, a
        jp      c, dzx0m_reuse1
dzx0m_elias_reuse1:
        add     a, a
        rl      c
        rl      b
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        add     a, a
        jp      nc, dzx0m_elias_reuse7
dzx0m_reuse7:
        push    hl                      ; preserve source
dzx0m_last_offset:
        ld      hl, 0
        add     hl, de                  ; calculate destination - offset
        ldir                            ; copy from offset
        pop     hl                      ; restore source
        add     a, a                    ; copy from literals or new offset?
        jr      nc, dzx0m_literals6

        jp      dzx0m_new_offset6
; -----------------------------------------------------------------------------
    end asm
end sub


' -----------------------------------------------------------------------------
' Decompress (from source to destination address) data that was previously
' compressed backwards using ZX0. This is the fastest version of the ZX0
' decompressor.
'
' Parameters:
'     src: last source address (compressed data)
'     dst: last destination address (decompressing)
' -----------------------------------------------------------------------------
sub FASTCALL dzx0MegaBack(src as UINTEGER, dst as UINTEGER)
    asm
        pop bc          ; RET address
        pop de          ; DE=dst
        push bc         ; restore RET address
; -----------------------------------------------------------------------------
; ZX0 decoder by Einar Saukas & introspec
; "Mega" version (676 bytes, 28% faster) - BACKWARDS VARIANT
; -----------------------------------------------------------------------------
; Parameters:
;   HL: last source address (compressed data)
;   DE: last destination address (decompressing)
; -----------------------------------------------------------------------------
dzx0_mega_back:
        ld      bc, 1                   ; preserve default offset 1
        ld      (dzx0mb_last_offset+1), bc
        jr      dzx0mb_literals0

dzx0mb_new_offset6:
        add     a, a                    ; obtain offset MSB
        jp      nc, dzx0mb_new_offset5
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_new_offset3
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_new_offset1
dzx0mb_elias_offset1:
        add     a, a
        rl      c
        rl      b
        ld      a, (hl)                 ; load another group of 8 bits
        dec     hl
        add     a, a
        jp      c, dzx0mb_elias_offset7
dzx0mb_new_offset7:
        dec     b
        ret     z                       ; check end marker
        dec     c                       ; adjust for positive offset
        ld      b, c
        ld      c, (hl)                 ; obtain offset LSB
        dec     hl
        srl     b                       ; last offset bit becomes first length bit
        rr      c
        inc     bc
        ld      (dzx0mb_last_offset+1), bc ; preserve new offset
        ld      bc, 1
        jp      nc, dzx0mb_length7      ; obtain length
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_length5
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_length3
dzx0mb_elias_length3:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      c, dzx0mb_elias_length1
dzx0mb_length1:
        push    hl                      ; preserve source
        ld      hl, (dzx0mb_last_offset+1)
        add     hl, de                  ; calculate destination - offset
        lddr                            ; copy from offset
        inc     c
        ldd                             ; copy one more from offset
        inc     c        
        pop     hl                      ; restore source
        add     a, a                    ; copy from literals or new offset?
        jr      c, dzx0mb_new_offset0
dzx0mb_literals0:
        ld      a, (hl)                 ; load another group of 8 bits
        dec     hl
        add     a, a                    ; obtain length
        jp      nc, dzx0mb_literals7
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_literals5
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_literals3
dzx0mb_elias_literals3:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      c, dzx0mb_elias_literals1
dzx0mb_literals1:
        lddr                            ; copy literals
        inc     c        
        add     a, a                    ; copy from last offset or new offset?
        jr      c, dzx0mb_new_offset0
        ld      a, (hl)                 ; load another group of 8 bits
        dec     hl
        add     a, a                    ; obtain length
        jp      nc, dzx0mb_reuse7
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_reuse5
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_reuse3
dzx0mb_elias_reuse3:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      c, dzx0mb_elias_reuse1
dzx0mb_reuse1:
        push    hl                      ; preserve source
        ld      hl, (dzx0mb_last_offset+1)
        add     hl, de                  ; calculate destination - offset
        lddr                            ; copy from offset
        inc     c        
        pop     hl                      ; restore source
        add     a, a                    ; copy from literals or new offset?
        jr      nc, dzx0mb_literals0

dzx0mb_new_offset0:
        ld      a, (hl)                 ; load another group of 8 bits
        dec     hl
        add     a, a                    ; obtain offset MSB
        jp      nc, dzx0mb_new_offset7
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_new_offset5
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_new_offset3
dzx0mb_elias_offset3:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      c, dzx0mb_elias_offset1
dzx0mb_new_offset1:
        dec     b
        ret     z                       ; check end marker
        dec     c                       ; adjust for positive offset
        ld      b, c
        ld      c, (hl)                 ; obtain offset LSB
        dec     hl
        srl     b                       ; last offset bit becomes first length bit
        rr      c
        inc     bc
        ld      (dzx0mb_last_offset+1), bc ; preserve new offset
        ld      bc, 1
        jp      nc, dzx0mb_length1      ; obtain length
        add     a, a
        rl      c
        ld      a, (hl)                 ; load another group of 8 bits
        dec     hl
        add     a, a
        jp      nc, dzx0mb_length7
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_length5
dzx0mb_elias_length5:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      c, dzx0mb_elias_length3
dzx0mb_length3:
        push    hl                      ; preserve source
        ld      hl, (dzx0mb_last_offset+1)
        add     hl, de                  ; calculate destination - offset
        lddr                            ; copy from offset
        inc     c
        ldd                             ; copy one more from offset
        inc     c        
        pop     hl                      ; restore source
        add     a, a                    ; copy from literals or new offset?
        jr      c, dzx0mb_new_offset2
dzx0mb_literals2:
        add     a, a                    ; obtain length
        jp      nc, dzx0mb_literals1
        add     a, a
        rl      c
        ld      a, (hl)                 ; load another group of 8 bits
        dec     hl
        add     a, a
        jp      nc, dzx0mb_literals7
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_literals5
dzx0mb_elias_literals5:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      c, dzx0mb_elias_literals3
dzx0mb_literals3:
        lddr                            ; copy literals
        inc     c        
        add     a, a                    ; copy from last offset or new offset?
        jr      c, dzx0mb_new_offset2
        add     a, a                    ; obtain length
        jp      nc, dzx0mb_reuse1
        add     a, a
        rl      c
        ld      a, (hl)                 ; load another group of 8 bits
        dec     hl
        add     a, a
        jp      nc, dzx0mb_reuse7
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_reuse5
dzx0mb_elias_reuse5:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      c, dzx0mb_elias_reuse3
dzx0mb_reuse3:
        push    hl                      ; preserve source
        ld      hl, (dzx0mb_last_offset+1)
        add     hl, de                  ; calculate destination - offset
        lddr                            ; copy from offset
        inc     c        
        pop     hl                      ; restore source
        add     a, a                    ; copy from literals or new offset?
        jr      nc, dzx0mb_literals2

dzx0mb_new_offset2:
        add     a, a                    ; obtain offset MSB
        jp      nc, dzx0mb_new_offset1
        add     a, a
        rl      c
        ld      a, (hl)                 ; load another group of 8 bits
        dec     hl
        add     a, a
        jp      nc, dzx0mb_new_offset7
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_new_offset5
dzx0mb_elias_offset5:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      c, dzx0mb_elias_offset3
dzx0mb_new_offset3:
        dec     b
        ret     z                       ; check end marker
        dec     c                       ; adjust for positive offset
        ld      b, c
        ld      c, (hl)                 ; obtain offset LSB
        dec     hl
        srl     b                       ; last offset bit becomes first length bit
        rr      c
        inc     bc
        ld      (dzx0mb_last_offset+1), bc ; preserve new offset
        ld      bc, 1
        jp      nc, dzx0mb_length3      ; obtain length
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_length1
        add     a, a
        rl      c
        ld      a, (hl)                 ; load another group of 8 bits
        dec     hl
        add     a, a
        jp      nc, dzx0mb_length7
dzx0mb_elias_length7:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      c, dzx0mb_elias_length5
dzx0mb_length5:
        push    hl                      ; preserve source
        ld      hl, (dzx0mb_last_offset+1)
        add     hl, de                  ; calculate destination - offset
        lddr                            ; copy from offset
        inc     c
        ldd                             ; copy one more from offset
        inc     c        
        pop     hl                      ; restore source
        add     a, a                    ; copy from literals or new offset?
        jr      c, dzx0mb_new_offset4
dzx0mb_literals4:
        add     a, a                    ; obtain length
        jp      nc, dzx0mb_literals3
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_literals1
        add     a, a
        rl      c
        ld      a, (hl)                 ; load another group of 8 bits
        dec     hl
        add     a, a
        jp      nc, dzx0mb_literals7
dzx0mb_elias_literals7:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      c, dzx0mb_elias_literals5
dzx0mb_literals5:
        lddr                            ; copy literals
        inc     c        
        add     a, a                    ; copy from last offset or new offset?
        jr      c, dzx0mb_new_offset4
        add     a, a                    ; obtain length
        jp      nc, dzx0mb_reuse3
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_reuse1
        add     a, a
        rl      c
        ld      a, (hl)                 ; load another group of 8 bits
        dec     hl
        add     a, a
        jp      nc, dzx0mb_reuse7
dzx0mb_elias_reuse7:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      c, dzx0mb_elias_reuse5
dzx0mb_reuse5:
        push    hl                      ; preserve source
        ld      hl, (dzx0mb_last_offset+1)
        add     hl, de                  ; calculate destination - offset
        lddr                            ; copy from offset
        inc     c        
        pop     hl                      ; restore source
        add     a, a                    ; copy from literals or new offset?
        jr      nc, dzx0mb_literals4

dzx0mb_new_offset4:
        add     a, a                    ; obtain offset MSB
        jp      nc, dzx0mb_new_offset3
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_new_offset1
        add     a, a
        rl      c
        ld      a, (hl)                 ; load another group of 8 bits
        dec     hl
        add     a, a
        jp      nc, dzx0mb_new_offset7
dzx0mb_elias_offset7:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jp      c, dzx0mb_elias_offset5
dzx0mb_new_offset5:
        dec     b
        ret     z                       ; check end marker
        dec     c                       ; adjust for positive offset
        ld      b, c
        ld      c, (hl)                 ; obtain offset LSB
        dec     hl
        srl     b                       ; last offset bit becomes first length bit
        rr      c
        inc     bc
        ld      (dzx0mb_last_offset+1), bc ; preserve new offset
        ld      bc, 1
        jp      nc, dzx0mb_length5      ; obtain length
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_length3
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_length1
dzx0mb_elias_length1:
        add     a, a
        rl      c
        rl      b
        ld      a, (hl)                 ; load another group of 8 bits
        dec     hl
        add     a, a
        jp      c, dzx0mb_elias_length7
dzx0mb_length7:
        push    hl                      ; preserve source
        ld      hl, (dzx0mb_last_offset+1)
        add     hl, de                  ; calculate destination - offset
        lddr                            ; copy from offset
        inc     c
        ldd                             ; copy one more from offset
        inc     c        
        pop     hl                      ; restore source
        add     a, a                    ; copy from literals or new offset?
        jp      c, dzx0mb_new_offset6
dzx0mb_literals6:
        add     a, a                    ; obtain length
        jp      nc, dzx0mb_literals5
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_literals3
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_literals1
dzx0mb_elias_literals1:
        add     a, a
        rl      c
        rl      b
        ld      a, (hl)                 ; load another group of 8 bits
        dec     hl
        add     a, a
        jp      c, dzx0mb_elias_literals7
dzx0mb_literals7:
        lddr                            ; copy literals
        inc     c        
        add     a, a                    ; copy from last offset or new offset?
        jp      c, dzx0mb_new_offset6
        add     a, a                    ; obtain length
        jp      nc, dzx0mb_reuse5
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_reuse3
        add     a, a
        rl      c
        add     a, a
        jp      nc, dzx0mb_reuse1
dzx0mb_elias_reuse1:
        add     a, a
        rl      c
        rl      b
        ld      a, (hl)                 ; load another group of 8 bits
        dec     hl
        add     a, a
        jp      c, dzx0mb_elias_reuse7
dzx0mb_reuse7:
        push    hl                      ; preserve source
dzx0mb_last_offset:
        ld      hl, 0
        add     hl, de                  ; calculate destination - offset
        lddr                            ; copy from offset
        inc     c
        pop     hl                      ; restore source
        add     a, a                    ; copy from literals or new offset?
        jr      nc, dzx0mb_literals6

        jp      dzx0mb_new_offset6
; -----------------------------------------------------------------------------
    end asm
end sub


' -----------------------------------------------------------------------------
' Decompress (from source to destination address) data that was previously
' compressed using ZX0. This is the smallest version of the integrated RCS+ZX0
' decompressor.
'
' IMPORTANT: Data decompressed directly to the ZX-Spectrum screen must be both
' RCS encoded and ZX0 compressed, everything else must be ZX0 compressed only.
'
' Parameters:
'     src: source address (compressed data)
'     dst: destination address (decompressing)
' -----------------------------------------------------------------------------
sub FASTCALL dzx0SmartRCS(src as UINTEGER, dst as UINTEGER)
    asm
        pop bc          ; RET address
        pop de          ; DE=dst
        push bc         ; restore RET address
; -----------------------------------------------------------------------------
; "Smart" integrated RCS+ZX0 decoder by Einar Saukas (112 bytes)
; -----------------------------------------------------------------------------
; Parameters:
;   HL: source address (compressed data)
;   DE: destination address (decompressing)
; -----------------------------------------------------------------------------
dzx0_smartrcs:
        ld      bc, $ffff               ; preserve default offset 1
        push    bc
        inc     bc
        ld      a, $80
dzx0r_literals:
        call    dzx0r_elias             ; obtain length
dzx0r_literals_loop:
        call    dzx0r_copy_byte         ; copy literals
        jp      pe, dzx0r_literals_loop
        add     a, a                    ; copy from last offset or new offset?
        jr      c, dzx0r_new_offset
        call    dzx0r_elias             ; obtain length
dzx0r_copy:
        ex      (sp), hl                ; preserve source, restore offset
        push    hl                      ; preserve offset
        add     hl, de                  ; calculate destination - offset
dzx0r_copy_loop:
        push    hl                      ; copy from offset
        ex      de, hl
        call    dzx0r_convert
        ex      de, hl
        call    dzx0r_copy_byte
        pop     hl
        inc     hl
        jp      pe, dzx0r_copy_loop
        pop     hl                      ; restore offset
        ex      (sp), hl                ; preserve offset, restore source
        add     a, a                    ; copy from literals or new offset?
        jr      nc, dzx0r_literals
dzx0r_new_offset:
        pop     bc                      ; discard last offset
        ld      c, $fe                  ; prepare negative offset
        call    dzx0r_elias_loop        ; obtain offset MSB
        inc     c
        ret     z                       ; check end marker
        ld      b, c
        ld      c, (hl)                 ; obtain offset LSB
        inc     hl
        rr      b                       ; last offset bit becomes first length bit
        rr      c
        push    bc                      ; preserve new offset
        ld      bc, 1                   ; obtain length
        call    nc, dzx0r_elias_backtrack
        inc     bc
        jr      dzx0r_copy
dzx0r_elias:
        inc     c                       ; interlaced Elias gamma coding
dzx0r_elias_loop:
        add     a, a
        jr      nz, dzx0r_elias_skip
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        rla
dzx0r_elias_skip:
        ret     c
dzx0r_elias_backtrack:
        add     a, a
        rl      c
        rl      b
        jr      dzx0r_elias_loop
dzx0r_copy_byte:
        push    de                      ; preserve destination
        call    dzx0r_convert           ; convert destination
        ldi                             ; copy byte
        pop     de                      ; restore destination
        inc     de                      ; update destination
        ret
; Convert an RCS address 010RRccc ccrrrppp to screen address 010RRppp rrrccccc
dzx0r_convert:
        ex      af, af'
        ld      a, d                    ; A = 010RRccc
        cp      $58
        jr      nc, dzx0r_skip
        xor     e
        and     $f8
        xor     e                       ; A = 010RRppp
        push    af
        xor     d
        xor     e                       ; A = ccrrrccc
        rlca
        rlca                            ; A = rrrccccc
        pop     de                      ; D = 010RRppp
        ld      e, a                    ; E = rrrccccc
dzx0r_skip:
        ex      af, af'
        ret
; -----------------------------------------------------------------------------
    end asm
end sub


' -----------------------------------------------------------------------------
' Decompress (from source to destination address) data that was previously
' compressed backwards using ZX0. This is the smallest version of the
' integrated RCS+ZX0 decompressor.
'
' IMPORTANT: Data decompressed directly to the ZX-Spectrum screen must be both
' RCS encoded and ZX0 compressed, everything else must be ZX0 compressed only.
'
' Parameters:
'     src: last source address (compressed data)
'     dst: last destination address (decompressing)
' -----------------------------------------------------------------------------
sub FASTCALL dzx0SmartRCSBack(src as UINTEGER, dst as UINTEGER)
    asm
        pop bc          ; RET address
        pop de          ; DE=dst
        push bc         ; restore RET address
; -----------------------------------------------------------------------------
; "Smart" integrated RCS+ZX0 decoder by Einar Saukas (113 bytes) - BACKWARDS
; -----------------------------------------------------------------------------
; Parameters:
;   HL: last source address (compressed data)
;   DE: last destination address (decompressing)
; -----------------------------------------------------------------------------
dzx0_smartrcs_back:
        ld      bc, 1                   ; preserve default offset 1
        push    bc
        ld      a, $80
dzx0rb_literals:
        call    dzx0rb_elias            ; obtain length
dzx0rb_literals_loop:
        call    dzx0rb_copy_byte        ; copy literals
        jp      pe, dzx0rb_literals_loop
        inc     c
        add     a, a                    ; copy from last offset or new offset?
        jr      c, dzx0rb_new_offset
        call    dzx0rb_elias            ; obtain length
dzx0rb_copy:
        ex      (sp), hl                ; preserve source, restore offset
        push    hl                      ; preserve offset
        add     hl, de                  ; calculate destination - offset
dzx0rb_copy_loop:
        push    hl                      ; copy from offset
        ex      de, hl
        call    dzx0rb_convert
        ex      de, hl
        call    dzx0rb_copy_byte
        pop     hl
        dec     hl
        jp      pe, dzx0rb_copy_loop
        inc     c
        pop     hl                      ; restore offset
        ex      (sp), hl                ; preserve offset, restore source
        add     a, a                    ; copy from literals or new offset?
        jr      nc, dzx0rb_literals
dzx0rb_new_offset:
        inc     sp                      ; discard last offset
        inc     sp
        call    dzx0rb_elias            ; obtain offset MSB
        dec     b
        ret     z                       ; check end marker
        dec     c                       ; adjust for positive offset
        ld      b, c
        ld      c, (hl)                 ; obtain offset LSB
        dec     hl
        srl     b                       ; last offset bit becomes first length bit
        rr      c
        inc     bc
        push    bc                      ; preserve new offset
        ld      bc, 1                   ; obtain length
        call    c, dzx0rb_elias_backtrack
        inc     bc
        jr      dzx0rb_copy
dzx0rb_elias_backtrack:
        add     a, a
        rl      c
        rl      b
dzx0rb_elias:
        add     a, a                    ; inverted interlaced Elias gamma coding
        jr      nz, dzx0rb_elias_skip
        ld      a, (hl)                 ; load another group of 8 bits
        dec     hl
        rla
dzx0rb_elias_skip:
        jr      c, dzx0rb_elias_backtrack
        ret
dzx0rb_copy_byte:
        push    de                      ; preserve destination
        call    dzx0rb_convert          ; convert destination
        ldd                             ; copy byte
        pop     de                      ; restore destination
        dec     de                      ; update destination
        ret
; Convert an RCS address 010RRccc ccrrrppp to screen address 010RRppp rrrccccc
dzx0rb_convert:
        ex      af, af'
        ld      a, d                    ; A = 010RRccc
        cp      $58
        jr      nc, dzx0rb_skip
        xor     e
        and     $f8
        xor     e                       ; A = 010RRppp
        push    af
        xor     d
        xor     e                       ; A = ccrrrccc
        rlca
        rlca                            ; A = rrrccccc
        pop     de                      ; D = 010RRppp
        ld      e, a                    ; E = rrrccccc
dzx0rb_skip:
        ex      af, af'
        ret
; -----------------------------------------------------------------------------
    end asm
end sub


' -----------------------------------------------------------------------------
' Decompress (from source to destination address) data that was previously
' compressed using ZX0. This is the fastest version of the integrated RCS+ZX0
' decompressor.
'
' IMPORTANT: Data decompressed directly to the ZX-Spectrum screen must be both
' RCS encoded and ZX0 compressed, everything else must be ZX0 compressed only.
'
' Parameters:
'     src: source address (compressed data)
'     dst: destination address (decompressing)
' -----------------------------------------------------------------------------
sub FASTCALL dzx0AgileRCS(src as UINTEGER, dst as UINTEGER)
    asm
        pop bc          ; RET address
        pop de          ; DE=dst
        push bc         ; restore RET address
; -----------------------------------------------------------------------------
; "Agile" integrated RCS+ZX0 decoder by Einar Saukas (187 bytes)
; -----------------------------------------------------------------------------
; Parameters:
;   HL: source address (compressed data)
;   DE: destination address (decompressing)
; -----------------------------------------------------------------------------
dzx0_agilercs:
        ld      bc, $ffff               ; preserve default offset 1
        ld      (dzx0a_last_offset+1), bc
        inc     bc
        ld      a, $80
        jr      dzx0a_literals
dzx0a_new_offset:
        ld      c, $fe                  ; prepare negative offset
        add     a, a
        jp      nz, dzx0a_new_offset_skip
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        rla
dzx0a_new_offset_skip:
        call    nc, dzx0a_elias         ; obtain offset MSB
        inc     c
        ret     z                       ; check end marker
        ld      b, c
        ld      c, (hl)                 ; obtain offset LSB
        inc     hl
        rr      b                       ; last offset bit becomes first length bit
        rr      c
        ld      (dzx0a_last_offset+1), bc ; preserve new offset
        ld      bc, 1                   ; obtain length
        call    nc, dzx0a_elias
        inc     bc
dzx0a_copy:
        push    hl                      ; preserve source
dzx0a_last_offset:
        ld      hl, 0                   ; restore offset
        add     hl, de                  ; calculate destination - offset
        ex      af, af'
dzx0a_copy_loop:
        ld      a, h                    ; copy from offset
        cp      $58
        jr      nc, dzx0a_copy_ldir
        push    hl
        ex      de, hl
        call    dzx0a_convert
        ex      de, hl
        push    de
        ld      a, d
        cp      $58
        call    c, dzx0a_convert
        ldi
        pop     de
        inc     de
        pop     hl
        inc     hl
        jp      pe, dzx0a_copy_loop
        db      $ea                     ; skip next instruction
dzx0a_copy_ldir:
        ldir
        ex      af, af'
        pop     hl                      ; restore source
        add     a, a                    ; copy from literals or new offset?
        jr      c, dzx0a_new_offset
dzx0a_literals:
        inc     c                       ; obtain length
        add     a, a
        jp      nz, dzx0a_literals_skip
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        rla
dzx0a_literals_skip:
        call    nc, dzx0a_elias
        ex      af, af'
dzx0a_literals_loop:
        ld      a, d                    ; copy literals
        cp      $58
        jr      nc, dzx0a_literals_ldir
        push    de
        call    dzx0a_convert
        ldi
        pop     de
        inc     de
        jp      pe, dzx0a_literals_loop
        db      $ea                     ; skip next instruction
dzx0a_literals_ldir:
        ldir
        ex      af, af'
        add     a, a                    ; copy from last offset or new offset?
        jr      c, dzx0a_new_offset
        inc     c                       ; obtain length
        add     a, a
        jp      nz, dzx0a_last_offset_skip
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        rla
dzx0a_last_offset_skip:
        call    nc, dzx0a_elias
        jp      dzx0a_copy
dzx0a_elias:
        add     a, a                    ; interlaced Elias gamma coding
        rl      c
        add     a, a
        jr      nc, dzx0a_elias
        ret     nz
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        rla
        ret     c
        add     a, a
        rl      c
        add     a, a
        ret     c
        add     a, a
        rl      c
        add     a, a
        ret     c
        add     a, a
        rl      c
        add     a, a
        ret     c
dzx0a_elias_loop:
        add     a, a
        rl      c
        rl      b
        add     a, a
        jr      nc, dzx0a_elias_loop
        ret     nz
        ld      a, (hl)                 ; load another group of 8 bits
        inc     hl
        rla
        jr      nc, dzx0a_elias_loop
        ret
; Convert an RCS address 010RRccc ccrrrppp to screen address 010RRppp rrrccccc
dzx0a_convert:                          ; A = 010RRccc
        xor     e
        and     $f8
        xor     e                       ; A = 010RRppp
        push    af
        xor     d
        xor     e                       ; A = ccrrrccc
        rlca
        rlca                            ; A = rrrccccc
        pop     de                      ; D = 010RRppp
        ld      e, a                    ; E = rrrccccc
        ret
; -----------------------------------------------------------------------------
    end asm
end sub


#pragma pop(case_insensitive)
#endif
