; -----------------------------------------------------------------------------
; 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
; -----------------------------------------------------------------------------
