;
;; Version CPC par T&J/GPA * 07/2007 !
;
;; Petites optimisations diverses, sans toucher le corps de la routine.
;; Voir la routine non modifiee pour essayer de comprendre son fonctionnement !
;
;; Liste des modifications :
;
;; - Remplacement de boucles de copies par des LDIR / LDI
;
;; - Integration dans le code des variables MaxGamma et consors, gain en
;;   place et en temps machine !
;
;; Utilisation du registre IX pour pointer sur TablePu (faible gain en place
;; et en temps machine
;
;; Remplacement de quelques JP par des JR
;
;
;;* PUCRUNCH unpacker for GB
;;*   Modeled after Pasi Ojala's C64 code.
;;*
;;*   Written in RGBDS
;;*
;;*  V1.0 - Ported to GB by Jeff Frohwein, started 22-Jul-99
;;*  V1.1 - Various optimizations, 23-Jul-99
;;*  V1.2 - Even more optimizations, 23-Jul-99
;;*  V1.3 - Fixed a bug in the code. 256 byte copy didn't work. 24-Feb-00
;;*

ORG &A000
NOLIST
WRITE "BASIC.BIN"

; Read CALL datas transmitted from Basic

booter CP &2
       RET NZ
       LD E,(IX+&0)
       LD D,(IX+&1)
       LD L,(IX+&2)
       LD H,(IX+&3)

; ****** Unpack pucrunch data ******
; Entry HL = Source packed data
;       DE = Destination for unpacked data

Unpack  LD (OutPtr+1),DE

; Read the file header & setup variables

        LD BC,6
        ADD HL,BC

        LD A,(HL)
        INC HL
        LD (escPu +1),A

        INC HL
        INC HL

        LD A,(HL)
        INC HL

        LD (main +1),A  ; EscBits
        LD B,A

        LD A,8
        SUB B
        LD (noesc +1),A ; Esc8Bits

        LD A,(HL)
        INC HL
        LD (MaxGamma +1),A

        DEC A
        LD B,A
        LD A,8
        SUB B
        LD (Max8Gamma +1),A

        LD A,(HL)
        INC HL
        LD (Max1Gamma +1),A
        ADD A,A
        DEC A
        LD (Max2Gamma +1),A

        LD A,(HL)
        INC HL
        LD (ExtraBits +1),A

        INC HL
        INC HL

        LD A,(HL)
        INC HL

        LD C,A

        LD DE,tablePu-1

        PUSH DE
        POP IX
        INC DE    ; DE = TablePu, IX=TablePu-1 (useful for chrcode optimization)


; Copy the RLE table (maximum of 31 bytes) to RAM

        LD B,&0
        LDIR

        LD D,&80

        JR main

tablepu ds 31,&0

newesc
        LD B,A

        LD A,(escPu +1)
        LD (regy +1),A

        LD A,(main +1)

        LD E,A
        LD A,B
        INC E
        CALL getchk

        LD (escPu +1),A
        LD A,(regy +1)

; Fall through and get the rest of the bits.

noesc   LD E,&00

        INC E
        CALL getchk

; Write out the escaped/normal byte

OutPtr  LD BC,&0000
        LD (BC),A
        INC BC
        LD (OutPtr+1),BC

; Fall through and check the escape bits again

main    LD A,&00  ; changed LD A,(EscBits)
        LD E,A

        XOR A               ; A = 0
        LD (regy +1),A

        INC E

        CALL getchk         ; X=2 -> X=0

        LD B,A
escPu   LD A,&00
        CP B
        LD A,B

        JR NZ,noesc       ; Not the escape code -> get the rest of the byte

; Fall through to packed code

        CALL getval       ; X=0 -> X=0

        LD (lzpos +1),A   ; xstore - save the length for a later time

        SRL A             ; cmp #1        ; LEN == 2 ? (A is never 0)
        JR NZ,lz77        ; LEN != 2      -> LZ77

        CALL get1bit      ; X=0 -> X=0

        SRL A             ; bit -> C, A = 0

        JR NC,lz77_2      ; A=0 -> LZPOS+1        LZ77, len=2

 ; e..e01

        CALL get1bit      ; X=0 -> X=0
        SRL A             ; bit -> C, A = 0
        JR NC,newesc      ; e..e010               New Escape

 ; e..e011    Short/Long RLE

regy    LD A,&00          ; Y is 1 bigger than MSB loops
        INC A
        LD (regy +1),a

        CALL getval       ; Y is 1, get len,  X=0 -> X=0
        LD (lzpos +1),A   ; xstore - Save length LSB


Max1Gamma LD B,&00

        CP B              ; ** PARAMETER 63-64 -> C set, 64-64 -> C clear..

        JR C,chrcode      ; short RLE, get bytecode

 ; Otherwise it's long RLE

longrle LD B,A

Max8Gamma LD A,&00

        LD E,A            ; ** PARAMETER  111111xxxxxx
        LD A,B

        CALL getbits      ; get 3/2/1 more bits to get a full byte,  X=2 -> X=0
        LD (lzpos +1),A      ; xstore - Save length LSB

        CALL getval       ; length MSB, X=0 -> X=0

        LD (regy +1),A       ; Y is 1 bigger than MSB loops

chrcode CALL getval         ; Byte Code,  X=0 -> X=0

        LD E,A

        LD (cpc1+2),A
        CP 32             ; 31-32 -> C set, 32-32 -> C clear..
cpc1    LD A,(IX + &00)

        JR C,less32       ; 1..31

 ; Not ranks 1..31, -> 111110xxxxx (32..64), get byte..

        LD A,E        ; get back the value (5 valid bits)

        LD E,3

        CALL getbits        ; get 3 more bits to get a full byte, X=3 -> X=0

less32  PUSH HL
        PUSH AF

        LD A,(lzpos +1)
        LD E,A         ; xstore - get length LSB

        LD B,E
        INC B          ; adjust for cpx#$ff;bne -> bne

        LD A,(regy +1)
        LD C,A

        LD HL,(OutPtr +1)

        POP AF

dorle   LD (HL),A
        INC HL

        DEC B
        JR NZ,dorle       ; xstore 0..255 -> 1..256

        DEC C
        JR NZ,dorle       ; Y was 1 bigger than wanted originally

        LD (OutPtr +1),HL

        POP HL
        JP main

lz77    CALL getval  ; X=0 -> X=0

        LD B,A


Max2Gamma LD A,&00

        CP B         ; end of file ?
        RET Z        ; yes, exit

ExtraBits LD A,&00   ; ** PARAMETER (more bits to get)
        LD E,A

        LD A,B

        DEC A        ; subtract 1  (1..126 -> 0..125)

        INC E

        CALL getchk ;f     ; clears Carry, X=0 -> X=0


lz77_2  LD (lzpos +2),A     ; offset MSB

        LD E,8

        CALL getbits       ; clears Carry, X=8 -> X=0

                        ; Note Already eored in the compressor..
        LD B,A

        LD A,(lzpos +1)
        LD E,A          ; xstore - LZLEN (read before it's overwritten)

        LD A,(OutPtr +1)
        ADD A,B         ; -offset -1 + curpos (C is clear)
        LD (lzpos +1),A

        LD A,(lzpos +2)
        LD B,A

        LD A,(OutPtr+2)
        CCF
        SBC B
        LD (lzpos +2),A   ; copy X+1 number of chars from LZPOS to OUTPOS

        INC E            ; adjust for cpx#$ff;bne -> bne

; Write decompressed bytes out to RAM

        LD B,E

        PUSH DE
        PUSH HL

lzpos   LD HL,&0000
        LD DE,(OutPtr +1)

; Modification GPA pour utilisation de LDI
; du coup, on libere le registre A

        LD A,B
        OR A              ; Is it zero?
        JR Z,zero         ; yes

        INC A

        SRL A
        JR NC,olzloop

; optimisable en LDI INC BC

lzloop  LDI         ; Note Must be copied forward

olzloop LDI         ; Note Must be copied forward

        DEC A
        JR NZ,lzloop      ; X loops, (256,1..255)

        LD (OutPtr+1),DE

        POP HL
        POP DE
        JP main

zero    LD A,128
        JR lzloop

; getval  Gets a 'static huffman coded' value
; ** Scratches X, returns the value in A **

getval  LD A,1           ; X must be 0 when called!
        LD E,A

loop0   SLA D

        JR NZ,loop1

        LD D,(HL)
        INC HL

        RL D           ; Shift in C=1 (last bit marker)
                       ; bitstr initial value = $80 == empty

loop1   JR NC,getchk   ; got 0-bit

        INC E

        LD B,A         ; save a

MaxGamma LD A,&00
        CP E

        LD A,B         ; restore a

        JR NZ,loop0

        JR getchk


; getbits Gets X bits from the stream
; ** Scratches X, returns the value in A **

get1bit INC E

getbits SLA D

        JR NZ,loop3

        LD D,(HL)
        INC HL

        RL D          ; Shift in C=1 (last bit marker)
                      ; bitstr initial value = &80 == empty
loop3   RLA

getchk  DEC E

        JR NZ,getbits
        OR A          ; clear carry flag
        RET