?login_element?

Subversion Repositories NedoOS

Rev

Blame | Last modification | View Log | Download

  1.         module APULTRA
  2. ;
  3. ;  Size-optimized ApLib decompressor by spke & uniabis (ver.04 01-07/06/2020, 139 bytes)
  4. ;
  5. ;  The original Z80 decompressor for ApLib was written by Dan Weiss (Dwedit),
  6. ;  then tweaked by Francisco Javier Pena Pareja (utopian),
  7. ;  and optimized by Jaime Tejedor Gomez (Metalbrain).
  8. ;
  9. ;  This version was heavily re-optimized for size by spke.
  10. ;  (It is 17 bytes shorter and 22% faster than the 156b version by Metalbrain.)
  11. ;
  12. ;  ver.00 by spke (21/08/2018-01/09/2018, 141 bytes);
  13. ;  ver.01 by spke (spring 2019, 140(-1) bytes, slightly faster);
  14. ;  ver.02 by spke (05-07/01/2020, added full revision history, support for long offsets
  15. ;                  and an option to use self-modifying code instead of IY)
  16. ;  ver.03 by spke (18-29/05/2020, +0.5% speed, added support for backward compression)
  17. ;  ver.04 by uniabis (01-07/06/2020, 139(-1) bytes, +1% speed, added support for HD64180)
  18. ;
  19. ;  The data must be compressed using any compressor for ApLib capable of generating raw data.
  20. ;  At present, two best available compressors are:
  21. ;
  22. ;  "APC" by Sven-Ake Dahl: https://github.com/svendahl/cap or
  23. ;  "apultra" by Emmanuel Marty: https://github.com/emmanuel-marty/apultra
  24. ;
  25. ;  The compression can be done as follows:
  26. ;
  27. ;  apc.exe e <sourcefile> <outfile>
  28. ;  or
  29. ;  apultra.exe <sourcefile> <outfile>
  30. ;
  31. ;  A decent compressor was written by r57shell (although it is worse than compressors above):
  32. ;  http://gendev.spritesmind.net/forum/viewtopic.php?p=32548#p32548
  33. ;  The use of the official ApLib compressor by Joergen Ibsen is not recommended.
  34. ;
  35. ;  The decompression is done in the standard way:
  36. ;
  37. ;  ld hl,FirstByteOfCompressedData
  38. ;  ld de,FirstByteOfMemoryForDecompressedData
  39. ;  call DecompressApLib
  40. ;
  41. ;  Backward decompression is also supported; you can compress files backward using:
  42. ;
  43. ;  apultra.exe -b <sourcefile> <outfile>
  44. ;
  45. ;  uncomment option "DEFINE BackwardDecompression" and decompress the resulting files using:
  46. ;
  47. ;  ld hl,LastByteOfCompressedData
  48. ;  ld de,LastByteOfMemoryForDecompressedData
  49. ;  call DecompressApLib
  50. ;
  51. ;  The decompressor modifies AF, AF', BC, DE, HL, IX.
  52. ;
  53. ;  Of course, ApLib compression algorithms are (c) 1998-2014 Joergen Ibsen,
  54. ;  see http://www.ibsensoftware.com/ for more information
  55. ;
  56. ;  Drop me an email if you have any comments/ideas/suggestions: zxintrospec@gmail.com
  57. ;
  58. ;  This software is provided 'as-is', without any express or implied
  59. ;  warranty.  In no event will the authors be held liable for any damages
  60. ;  arising from the use of this software.
  61. ;
  62. ;  Permission is granted to anyone to use this software for any purpose,
  63. ;  including commercial applications, and to alter it and redistribute it
  64. ;  freely, subject to the following restrictions:
  65. ;
  66. ;  1. The origin of this software must not be misrepresented; you must not
  67. ;     claim that you wrote the original software. If you use this software
  68. ;     in a product, an acknowledgment in the product documentation would be
  69. ;     appreciated but is not required.
  70. ;  2. Altered source versions must be plainly marked as such, and must not be
  71. ;     misrepresented as being the original software.
  72. ;  3. This notice may not be removed or altered from any source distribution.
  73.  
  74. ;       DEFINE FasterGetBit                                     ; 16% speed-up at the cost of extra 4 bytes
  75. ;       DEFINE SupportLongOffsets                               ; +4 bytes for long offset support. slows decompression down by 1%, but may be needed to decompress files >=32K
  76. ;       DEFINE BackwardDecompression                            ; decompress data compressed backwards, -5 bytes, speeds decompression up by 3%
  77.  
  78.  
  79.         IFDEF FasterGetBit
  80.                 MACRO   GET_BIT
  81.                         add a : call z,ReloadByte
  82.                 ENDM
  83.         ELSE
  84.                 MACRO   GET_BIT
  85.                         call GetOneBit
  86.                 ENDM
  87.         ENDIF
  88.  
  89.         IFNDEF BackwardDecompression
  90.  
  91.                 MACRO NEXT_HL
  92.                 inc hl
  93.                 ENDM
  94.  
  95.                 MACRO COPY_1
  96.                 ldi
  97.                 ENDM
  98.  
  99.                 MACRO COPY_BC
  100.                 ldir
  101.                 ENDM
  102.  
  103.         ELSE
  104.  
  105.                 MACRO NEXT_HL
  106.                 dec hl
  107.                 ENDM
  108.  
  109.                 MACRO COPY_1
  110.                 ldd
  111.                 ENDM
  112.  
  113.                 MACRO COPY_BC
  114.                 lddr
  115.                 ENDM
  116.  
  117.         ENDIF
  118.  
  119. @DecompressApLib:       ld a,128
  120.  
  121. ;
  122. ;  case "0"+BYTE: copy a single literal
  123.  
  124. CASE0:                  COPY_1                                  ; first byte is always copied as literal
  125. ResetLWM:               ld b,-1                                 ; LWM = 0 (LWM stands for "Last Was Match"; a flag that we did not have a match)
  126.  
  127. ;
  128. ;  main decompressor loop
  129.  
  130. MainLoop:               GET_BIT : jr nc,CASE0                   ; "0"+BYTE = copy literal
  131.                         GET_BIT : jr nc,CASE10                  ; "10"+gamma(offset/256)+BYTE+gamma(length) = the main matching mechanism
  132.  
  133.                         ld bc,%11100000
  134.                         GET_BIT : jr nc,CASE110                 ; "110"+[oooooool] = matched 2-3 bytes with a small offset
  135.  
  136. ;
  137. ;  case "111"+"oooo": copy a byte with offset -1..-15, or write zero to dest
  138.  
  139. CASE111:
  140. ReadFourBits            GET_BIT                                 ; read short offset (4 bits)
  141.                         rl c : jr c,ReadFourBits
  142.                         ex de,hl : jr z,WriteZero               ; zero offset means "write zero" (NB: B is zero here)
  143.  
  144.                         ; "write a previous byte (1-15 away from dest)"
  145.                         push hl                                 ; BC = offset, DE = src, HL = dest
  146.         IFNDEF BackwardDecompression
  147.                         sbc hl,bc                               ; HL = dest-offset (SBC works because branching above ensured NC)
  148.         ELSE
  149.                         add hl,bc                               ; HL = dest-offset (SBC works because branching above ensured NC)
  150.         ENDIF
  151.                         ld c,(hl) : pop hl
  152.  
  153. WriteZero               ld (hl),c : NEXT_HL
  154.                         ex de,hl : jr ResetLWM                  ; write one byte, reset LWM
  155.  
  156. ;
  157. ;  branch "110"+[oooooool]: copy two or three bytes (bit "l") with the offset -1..-127 (bits "ooooooo"), or stop
  158.  
  159. CASE110:                ; "use 7 bit offset, length = 2 or 3"
  160.                         ; "if a zero is found here, it's EOF"
  161.                         ld c,(hl) : rr c : ret z                ; process EOF
  162.                         NEXT_HL
  163.  
  164.                         push hl                                 ; save src
  165.                         ld h,b : ld l,c                         ; HL = offset
  166.  
  167.                         ; flag NC means len=2, flag C means len=3
  168.                         ld c,1 : rl c : jr SaveLWMOffset
  169.                        
  170. ;
  171. ;  branch "10"+gamma(offset/256)+BYTE+gamma(length): the main matching mechanism
  172.  
  173. CASE10:                 ; save state of LWM into A'
  174.                         exa : ld a,b : exa
  175.  
  176.                         ; "use a gamma code * 256 for offset, another gamma code for length"
  177.                         call GetGammaCoded
  178.  
  179.                         ; the original decompressor contains
  180.                         ;
  181.                         ; if ((LWM == 0) && (offs == 2)) { ... }
  182.                         ; else {
  183.                         ;       if (LWM == 0) { offs -= 3; }
  184.                         ;       else { offs -= 2; }
  185.                         ; }
  186.                         ;
  187.                         ; so, the idea here is to use the fact that GetGammaCoded returns (offset/256)+2,
  188.                         ; and to split the first condition by noticing that C-1 can never be zero
  189.                         exa : add c : ld c,a : exa
  190.  
  191.                         ; "if gamma code is 2, use old r0 offset"
  192.                         dec c : jr z,KickInLWM
  193.                         dec c
  194.                         ld b,c : ld c,(hl) : NEXT_HL            ; BC = offset
  195.  
  196.                         push bc                                 ; (SP) = offset
  197.                         call GetGammaCoded                      ; BC = len*
  198.                         ex (sp),hl                              ; HL = offset, (SP) = src
  199.  
  200.                         ; interpretation of length value is offset-dependent
  201.                         exa : ld a,h
  202.         IFDEF   SupportLongOffsets
  203.                         ; NB offsets over 32000 require an additional check, which is skipped in most
  204.                         ; Z80 decompressors (seemingly as a performance optimization)
  205.                         cp 32000/256 : jr nc,.Add2
  206.         ENDIF
  207.                         cp 5 : jr nc,.Add1
  208.                         or a : jr nz,.Add0
  209.                         bit 7,l : jr nz,.Add0
  210. .Add2                   inc bc
  211. .Add1                   inc bc
  212. .Add0                   exa
  213.  
  214. SaveLWMOffset:
  215.                         push hl : pop ix                        ; save offset for future LWMs
  216.  
  217. CopyMatch:              ; this assumes that BC = len, DE = dest, HL = offset
  218.                         ; and also that (SP) = src, while having NC
  219.         IFNDEF BackwardDecompression
  220.                         push de
  221.                         ex de,hl : sbc hl,de                    ; HL = dest-offset
  222.                         pop de                                  ; DE = dest
  223.         ELSE
  224.                         add hl,de                               ; HL = dest+offset
  225.         ENDIF
  226.  
  227.                         COPY_BC
  228.                         pop hl                                  ; recover src
  229.                         jr MainLoop
  230.  
  231. ;
  232. ;  the re-use of the previous offset (LWM magic)
  233.  
  234. KickInLWM:              ; "and a new gamma code for length"
  235.                         call GetGammaCoded                      ; BC = len
  236.                         push ix : ex (sp),hl                    ; DE = dest, HL = prev offset
  237.                         jr CopyMatch
  238.  
  239. ;
  240. ;  interlaced gamma code reader
  241. ;  x0 -> 1x
  242. ;  x1y0 -> 1xy
  243. ;  x1y1z0 -> 1xyz etc
  244. ;  (technically, this is a 2-based variation of Exp-Golomb-1)
  245.  
  246. GetGammaCoded:          ld bc,1
  247. ReadGamma               GET_BIT : rl c : rl b
  248.                         GET_BIT : ret nc
  249.                         jr ReadGamma
  250.  
  251. ;
  252. ;  pretty usual getbit for mixed datastreams
  253.  
  254.         IFNDEF FasterGetBit
  255. GetOneBit:              add a : ret nz
  256.         ENDIF
  257. ReloadByte:             ld a,(hl) : NEXT_HL
  258.                         rla : ret
  259.  
  260.  
  261.         endmodule
  262.