Inflate: Read decompressed data in buffer sized blocks.

This makes it much easier to process the data as it is being decompressed.

Subsume neonlib’s Writer in the process.
M .hgsubstate +1 -1
@@ 1,1 1,1 @@ 
-d30b04366fbd338ea986834315653980be450151 lib/neonlib
+8a43f418c682b8f87d70d518ce5484905361dad0 lib/neonlib

          
M src/Adler32Checker.asm +17 -55
@@ 2,15 2,6 @@ 
 ; Adler32 summer
 ;
 Adler32Checker: MACRO
-	this:
-	Process:
-		push ix
-		ld ix,this
-	Process_this: equ $ - 2
-		call Adler32Checker_UpdateAdler32
-		pop ix
-		jp 0
-	Process_oldHook: equ $ - 2
 	adler32:
 		dd 1
 	_size:

          
@@ 18,23 9,10 @@ Adler32Checker: MACRO
 
 	IF ZLIB_ADLER32
 
-; de = writer
 ; ix = this
 ; ix <- this
 ; Modifies: a
 Adler32Checker_Construct:
-	ld iyl,e
-	ld iyh,d
-	ld e,(iy + Writer.flusher)
-	ld d,(iy + Writer.flusher + 1)
-	ld (ix + Adler32Checker.Process_oldHook),e
-	ld (ix + Adler32Checker.Process_oldHook + 1),d
-	ld e,ixl
-	ld d,ixh
-	ld (iy + Writer.flusher),e
-	ld (iy + Writer.flusher + 1),d
-	ld (ix + Adler32Checker.Process_this),e
-	ld (ix + Adler32Checker.Process_this + 1),d
 	xor a
 	ld (ix + Adler32Checker.adler32),1
 	ld (ix + Adler32Checker.adler32 + 1),a

          
@@ 42,37 20,26 @@ Adler32Checker_Construct:
 	ld (ix + Adler32Checker.adler32 + 3),a
 	ret
 
-; bc = byte count
-; de = buffer start
+; de = read address
+; hl = byte count
 ; ix = this
-; Modifies: none
 Adler32Checker_UpdateAdler32:
-	push af
-	push bc
-	push de
-	push hl
-	ex de,hl
+	ld a,l
+	or h
+	ret z
 	exx
-	push bc
-	push de
-	push hl
 	ld e,(ix + Adler32Checker.adler32)
 	ld d,(ix + Adler32Checker.adler32 + 1)
 	ld c,(ix + Adler32Checker.adler32 + 2)
 	ld b,(ix + Adler32Checker.adler32 + 3)
+	exx
 	call Adler32Checker_CalculateAdler32
+	exx
 	ld (ix + Adler32Checker.adler32),e
 	ld (ix + Adler32Checker.adler32 + 1),d
 	ld (ix + Adler32Checker.adler32 + 2),c
 	ld (ix + Adler32Checker.adler32 + 3),b
-	pop hl
-	pop de
-	pop bc
 	exx
-	pop hl
-	pop de
-	pop bc
-	pop af
 	ret
 
 ; bcde = expected adler32

          
@@ 92,22 59,18 @@ Adler32Checker_VerifyAdler32:
 	sub (ix + Adler32Checker.adler32 + 3)
 	ret
 
-; bc' = byte count
-; hl' = read address
-; bcde = current adler
+; de = read address
+; hl = byte count
+; bcde' = current adler32
 ; ix = this
-; bcde <- updated adler
-; Modifies: af, bc, de, hl, bc', hl'
+; bcde' <- updated adler32
 Adler32Checker_CalculateAdler32: PROC
-	exx
-	ld a,c  ; convert 16-bit counter bc to two 8-bit counters in b and c
-	dec bc
-	inc b
-	ld c,b
-	ld b,a
+	ld b,l  ; convert 16-bit counter bc to two 8-bit counters in b and c
+	dec hl
+	inc h
 Loop:
-	ld a,(hl)
-	inc hl
+	ld a,(de)
+	inc de
 	exx
 	ld l,a
 	ld h,0

          
@@ 119,9 82,8 @@ Loop:
 	ld b,h
 	exx
 	djnz Loop
-	dec c
+	dec h
 	jp nz,Loop
-	exx
 	ret
 	ENDP
 

          
M src/Application.asm +49 -77
@@ 2,6 2,8 @@ 
 ; Top-level application program class
 ;
 Application: MACRO
+	outputFileHandle:
+		db 0FFH
 	outputSize:
 		dd 0
 	archiveFIB:

          
@@ 12,10 14,6 @@ Application: MACRO
 		CLI
 	fileReader:
 		FileReader
-	fileWriter:
-		FileWriter
-	nullWriter:
-		NullWriter
 	gzipArchive:
 		GzipArchive
 	_size:

          
@@ 25,7 23,6 @@ Application: MACRO
 Application_Main:
 	call Application_CheckStack
 
-	call WriterTest_Test
 	call CRC32CheckerTest_Test
 	call AlphabetTest_Test
 

          
@@ 74,26 71,6 @@ Application_GetFileReader:
 	ret
 
 ; ix = this
-; ix <- file writer
-; Modifies: f
-Application_GetFileWriter:
-	push de
-	ld de,Application.fileWriter
-	add ix,de
-	pop de
-	ret
-
-; ix = this
-; ix <- null writer
-; Modifies: f
-Application_GetNullWriter:
-	push de
-	ld de,Application.nullWriter
-	add ix,de
-	pop de
-	ret
-
-; ix = this
 ; ix <- gzip archive
 ; Modifies: f
 Application_GetGzipArchive:

          
@@ 131,56 108,75 @@ Application_Inflate:
 	jp Application_InflateToFile
 
 ; ix = this
-Application_InflateToFile:
+Application_InflateToFile: PROC
 	call Application_FindNextFile
 	ret c
 	call Application_PrintInflating
-	push ix
 	call Application_CreateFileReader
 	push de
 	call Application_ReadOutputSize
 	call Application_IsFast
 	cpl
+	pop hl
 	push ix
 	call Application_GetGzipArchive
+	ld bc,Application_decoders
+	ld de,WRITEBUFFER
 	call GzipArchive_Construct
-	ex (sp),ix
+	pop ix
 	call Application_FindNewOutput
 	call Application_CreateFileWriter
 	call Application_PreAllocateOutput
+InflateLoop:
+	push ix
+	call Application_GetGzipArchive
+	call GzipArchive_Inflate
 	pop ix
-	push de
-	ld bc,Application_decoders
-	call GzipArchive_Extract
-	pop ix
-	call FileWriter_Destruct
-	pop ix
+	push af
+	push hl
+	call DOS_ConsoleStatus  ; allow ctrl-c
+	pop hl
+	ld b,(ix + Application.outputFileHandle)
+	call DOS_WriteToFileHandle
+	call DOS_TerminateIfError
+	call DOS_ConsoleStatus  ; allow ctrl-c
+	pop af
+	jr nz,InflateLoop
+	push ix
+	call Application_GetFileReader
 	call FileReader_Destruct
 	pop ix
+	ld b,(ix + Application.outputFileHandle)
+	call DOS_CloseFileHandle
+	call DOS_TerminateIfError
 	jr Application_InflateToFile
+	ENDP
 
 ; ix = this
-Application_InflateTest:
+Application_InflateTest: PROC
 	call Application_FindNextFile
 	ret c
 	call Application_PrintTesting
-	push ix
 	call Application_CreateFileReader
 	push de
 	call Application_IsFast
 	cpl
+	pop hl
 	push ix
 	call Application_GetGzipArchive
+	ld bc,Application_decoders
+	ld de,WRITEBUFFER
 	call GzipArchive_Construct
-	ex (sp),ix
-	call Application_CreateNullWriter
+InflateLoop:
+	call GzipArchive_Inflate
+	jr nz,InflateLoop
 	pop ix
-	ld bc,Application_decoders
-	call GzipArchive_Extract
-	pop ix
+	push ix
+	call Application_GetFileReader
 	call FileReader_Destruct
 	pop ix
 	jr Application_InflateTest
+	ENDP
 
 ; ix = this
 Application_PrintWelcome:

          
@@ 333,36 329,19 @@ Application_CreateFileWriter:
 	ld hl,Application.outputFIB
 	add hl,de
 	ex de,hl
-	ld hl,WRITEBUFFER
-	ld bc,WRITEBUFFER_SIZE
-	push ix
-	call Application_GetFileWriter
-	call FileWriter_Construct
-	ld e,ixl
-	ld d,ixh
-	pop ix
-	ret
-
-; ix = this
-; de <- null writer
-Application_CreateNullWriter:
-	ld hl,WRITEBUFFER
-	ld bc,WRITEBUFFER_SIZE
-	push ix
-	call Application_GetNullWriter
-	call NullWriter_Construct
-	ld e,ixl
-	ld d,ixh
-	pop ix
+	ld a,00000010B  ; write only
+	ld b,0
+	call DOS_OpenFileHandle
+	call DOS_TerminateIfError
+	ld (ix + Application.outputFileHandle),b
 	ret
 
 ; de = file reader
 ; ix = this
-; Modifies: af, bc, hl, iy
+; Modifies: af, bc, de, hl, iy
 Application_ReadOutputSize:
 	ld iyl,e
 	ld iyh,d
-	push de
 	ld b,(iy + FileReader.fileHandle)
 	call DOS_GetFileHandlePointer
 	call DOS_TerminateIfError

          
@@ 387,21 366,15 @@ Application_ReadOutputSize:
 	pop de
 	ld b,(iy + FileReader.fileHandle)
 	call DOS_SetFileHandlePointer
-	call DOS_TerminateIfError
-	pop de
-	ret
+	jp DOS_TerminateIfError
 
-; de = file writer
 ; ix = this
-; Modifies: af, bc, hl, iy
+; Modifies: af, bc, de, hl
 Application_PreAllocateOutput:
-	ld iyl,e
-	ld iyh,d
 	ld a,(ix + Application.outputSize + 2)
 	or (ix + Application.outputSize + 3)
 	ret z  ; don’t pre-allocate if < 64K
-	push de
-	ld b,(iy + FileWriter.fileHandle)
+	ld b,(ix + Application.outputFileHandle)
 	call DOS_GetFileHandlePointer
 	call DOS_TerminateIfError
 	push de

          
@@ 416,20 389,19 @@ Application_PreAllocateOutput:
 	ex de,hl
 	sbc hl,bc
 	ex de,hl
-	ld b,(iy + FileWriter.fileHandle)
+	ld b,(ix + Application.outputFileHandle)
 	call DOS_SetFileHandlePointer
 	call DOS_TerminateIfError
 	ld de,0  ; don’t care which value we write
 	ld hl,1
-	ld b,(iy + FileWriter.fileHandle)
+	ld b,(ix + Application.outputFileHandle)
 	call DOS_WriteToFileHandle
 	call DOS_TerminateIfError
 	pop hl
 	pop de
-	ld b,(iy + FileWriter.fileHandle)
+	ld b,(ix + Application.outputFileHandle)
 	call DOS_SetFileHandlePointer
 	call DOS_TerminateIfError
-	pop de
 	ret
 
 ; Check if the stack is well above the heap

          
M src/COM.asm +0 -4
@@ 39,11 39,7 @@ TPA_TOP:
 	INCLUDE "deflate/Branch.asm"
 	INCLUDE "deflate/HuffmanCodes.asm"
 	INCLUDE "Reader.asm"
-	INCLUDE "Writer.asm"
-	INCLUDE "WriterTest.asm"
 	INCLUDE "FileReader.asm"
-	INCLUDE "FileWriter.asm"
-	INCLUDE "NullWriter.asm"
 	INCLUDE "CRC32Checker.asm"
 	INCLUDE "CRC32CheckerTest.asm"
 

          
M src/CRC32Checker.asm +17 -55
@@ 2,15 2,6 @@ 
 ; CRC32 summer
 ;
 CRC32Checker: MACRO
-	this:
-	Process:
-		push ix
-		ld ix,this
-	Process_this: equ $ - 2
-		call CRC32Checker_UpdateCRC32
-		pop ix
-		jp 0
-	Process_oldHook: equ $ - 2
 	crc32:
 		dd 0FFFFFFFFH
 	_size:

          
@@ 18,22 9,9 @@ CRC32Checker: MACRO
 
 	IF GZIP_CRC32
 
-; de = writer
 ; ix = this
 ; ix <- this
 CRC32Checker_Construct:
-	ld iyl,e
-	ld iyh,d
-	ld e,(iy + Writer.flusher)
-	ld d,(iy + Writer.flusher + 1)
-	ld (ix + CRC32Checker.Process_oldHook),e
-	ld (ix + CRC32Checker.Process_oldHook + 1),d
-	ld e,ixl
-	ld d,ixh
-	ld (iy + Writer.flusher),e
-	ld (iy + Writer.flusher + 1),d
-	ld (ix + CRC32Checker.Process_this),e
-	ld (ix + CRC32Checker.Process_this + 1),d
 	ld a,0FFH
 	ld (ix + CRC32Checker.crc32),a
 	ld (ix + CRC32Checker.crc32 + 1),a

          
@@ 41,37 19,26 @@ CRC32Checker_Construct:
 	ld (ix + CRC32Checker.crc32 + 3),a
 	ret
 
-; bc = byte count
-; de = buffer start
+; de = read address
+; hl = byte count
 ; ix = this
-; Modifies: none
 CRC32Checker_UpdateCRC32:
-	push af
-	push bc
-	push de
-	push hl
-	ex de,hl
+	ld a,l
+	or h
+	ret z
 	exx
-	push bc
-	push de
-	push hl
 	ld e,(ix + CRC32Checker.crc32)
 	ld d,(ix + CRC32Checker.crc32 + 1)
 	ld c,(ix + CRC32Checker.crc32 + 2)
 	ld b,(ix + CRC32Checker.crc32 + 3)
+	exx
 	call CRC32Checker_CalculateCRC32
+	exx
 	ld (ix + CRC32Checker.crc32),e
 	ld (ix + CRC32Checker.crc32 + 1),d
 	ld (ix + CRC32Checker.crc32 + 2),c
 	ld (ix + CRC32Checker.crc32 + 3),b
-	pop hl
-	pop de
-	pop bc
 	exx
-	pop hl
-	pop de
-	pop bc
-	pop af
 	ret
 
 ; bcde = expected crc32

          
@@ 93,22 60,18 @@ CRC32Checker_VerifyCRC32:
 	adc a,(ix + CRC32Checker.crc32 + 3)
 	ret
 
-; bc' = byte count
-; hl' = read address
-; bcde = current crc
+; de = read address
+; hl = byte count
+; bcde' = current crc32
 ; ix = this
-; bcde <- updated crc
-; Modifies: af, bc, de, hl, bc', hl'
+; bcde' <- updated crc32
 CRC32Checker_CalculateCRC32: PROC
-	exx
-	ld a,c  ; convert 16-bit counter bc to two 8-bit counters in b and c
-	dec bc
-	inc b
-	ld c,b
-	ld b,a
+	ld b,l  ; convert 16-bit counter bc to two 8-bit counters in b and c
+	dec hl
+	inc h
 Loop:
-	ld a,(hl)
-	inc hl
+	ld a,(de)
+	inc de
 	exx
 	xor e
 	ld l,a

          
@@ 128,9 91,8 @@ Loop:
 	ld b,(hl)
 	exx
 	djnz Loop
-	dec c
+	dec h
 	jp nz,Loop
-	exx
 	ret
 	ENDP
 

          
M src/CRC32CheckerTest.asm +12 -18
@@ 10,13 10,6 @@ CRC32CheckerTest_Test:
 	IF GZIP_CRC32
 
 CRC32CheckerTest_TestCRC32: PROC
-	ld hl,WRITEBUFFER
-	ld bc,256
-	ld ix,CRC32CheckerTest_nullWriter
-	call NullWriter_Construct
-	ld e,ixl
-	ld d,ixh
-
 	ld ix,CRC32CheckerTest_crc32Checker
 	call CRC32Checker_Construct
 

          
@@ 27,12 20,14 @@ CRC32CheckerTest_TestCRC32: PROC
 	call nz,System_ThrowException
 
 	ld b,58
-	ld ix,CRC32CheckerTest_nullWriter
+	ld hl,WRITEBUFFER
 Loop1:
-	ld a,b
-	call Writer_Write
+	ld (hl),b
+	inc hl
 	djnz Loop1
-	call Writer_FinishBlock
+	ld hl,58
+	ld de,WRITEBUFFER
+	call CRC32Checker_UpdateCRC32
 	ld de,07364H
 	ld bc,0DAA8H
 	ld ix,CRC32CheckerTest_crc32Checker

          
@@ 40,12 35,14 @@ Loop1:
 	call nz,System_ThrowException
 
 	ld b,200
-	ld ix,CRC32CheckerTest_nullWriter
+	ld hl,WRITEBUFFER
 Loop2:
-	ld a,b
-	call Writer_Write
+	ld (hl),b
+	inc hl
 	djnz Loop2
-	call Writer_FinishBlock
+	ld hl,200
+	ld de,WRITEBUFFER
+	call CRC32Checker_UpdateCRC32
 	ld de,04E96H
 	ld bc,02DA9H
 	ld ix,CRC32CheckerTest_crc32Checker

          
@@ 58,7 55,4 @@ Loop2:
 CRC32CheckerTest_crc32Checker:
 	CRC32Checker
 
-CRC32CheckerTest_nullWriter:
-	NullWriter
-
 	ENDIF

          
M src/GzipArchive.asm +87 -53
@@ 2,6 2,7 @@ 
 ; Gzip archive
 ;
 GzipArchive_STATE_CHECKSUM_BIT: equ 0
+GzipArchive_STATE_FOOTER_BIT: equ 1
 GzipArchive_SIGNATURE_1: equ 1FH
 GzipArchive_SIGNATURE_2: equ 8BH
 GzipArchive_DEFLATE_ID: equ 8

          
@@ 17,6 18,8 @@ GzipArchive: MACRO
 		db 0
 	reader:
 		dw 0
+	count:
+		dd 0
 	flags:
 		db 0
 	mtime:

          
@@ 41,7 44,9 @@ GzipArchive: MACRO
 	ENDM
 
 ; a = -1: CRC32 check enabled, 0: disabled
-; de = reader
+; bc = decoders buffer
+; de = write buffer (32K, 256-byte aligned)
+; hl = reader
 ; ix = this
 ; ix <- this
 GzipArchive_Construct:

          
@@ 51,8 56,27 @@ GzipArchive_Construct:
 	xor a
 	ENDIF
 	ld (ix + GzipArchive.state),a
-	ld (ix + GzipArchive.reader),e
-	ld (ix + GzipArchive.reader + 1),d
+	ld (ix + GzipArchive.reader),l
+	ld (ix + GzipArchive.reader + 1),h
+	xor a
+	ld (ix + GzipArchive.count),a
+	ld (ix + GzipArchive.count + 1),a
+	ld (ix + GzipArchive.count + 2),a
+	ld (ix + GzipArchive.count + 3),a
+	push ix
+	push de
+	ld de,GzipArchive.inflate
+	add ix,de
+	pop de
+	call Inflate_Construct
+	pop ix
+	IF GZIP_CRC32
+	push ix
+	ld bc,GzipArchive.crc32Checker
+	add ix,bc
+	call CRC32Checker_Construct
+	pop ix
+	ENDIF
 	jp GzipArchive_ReadHeader
 
 ; ix = this

          
@@ 66,16 90,6 @@ GzipArchive_GetReaderIY:
 	ret
 
 ; ix = this
-; ix <- Inflate implementation
-; Modifies: f
-GzipArchive_GetInflate:
-	push de
-	ld de,GzipArchive.inflate
-	add ix,de
-	pop de
-	ret
-
-; ix = this
 GzipArchive_ReadHeader: PROC
 	call GzipArchive_GetReaderIY
 	call Reader_Read_IY

          
@@ 147,59 161,84 @@ GzipArchive_ReadFooter:
 	ld (ix + GzipArchive.isize + 3),d
 	ret
 
-; bc = decoders buffer
-; de = writer (min 32K)
 ; ix = this
-GzipArchive_Extract:
-	IF GZIP_CRC32
-	push bc
-	bit GzipArchive_STATE_CHECKSUM_BIT,(ix + GzipArchive.state)
-	call nz,GzipArchive_CreateCRC32Checker
-	pop bc
-	ENDIF
-	call GzipArchive_CreateInflate
-	call GzipArchive_Inflate
+; a <- >0: more data, 0: final data
+; f <- nz: more data, z: final data
+; de <- buffer address
+; hl <- byte count
+GzipArchive_Inflate: PROC
+	push ix
+	ld de,GzipArchive.inflate
+	add ix,de
+	call Inflate_Inflate
+	pop ix
+	call GzipArchive_Update
+	ret nz
+	bit GzipArchive_STATE_FOOTER_BIT,(ix + GzipArchive.state)
+	call z,Footer
+	and a
+	ret
+Footer:
+	set GzipArchive_STATE_FOOTER_BIT,(ix + GzipArchive.state)
+	ex af,af'
+	exx
 	call GzipArchive_ReadFooter
 	call GzipArchive_Verify
+	exx
+	ex af,af'
+	ret
+	ENDP
+
+; de = buffer address
+; hl = byte count
+; ix = this
+GzipArchive_Update:
+	push af
+	call GzipArchive_UpdateCount
+	IF GZIP_CRC32
+	bit GzipArchive_STATE_CHECKSUM_BIT,(ix + GzipArchive.state)
+	call nz,GzipArchive_UpdateCRC32
+	ENDIF
+	pop af
 	ret
 
-; bc = decoders buffer
-; de = writer (min 32K)
+; de = buffer address
+; hl = byte count
 ; ix = this
-GzipArchive_CreateInflate:
-	ld l,(ix + GzipArchive.reader)
-	ld h,(ix + GzipArchive.reader + 1)
-	push ix
-	call GzipArchive_GetInflate
-	call Inflate_Construct
-	pop ix
+GzipArchive_UpdateCount:
+	push hl
+	ld c,(ix + GzipArchive.count)
+	ld b,(ix + GzipArchive.count + 1)
+	add hl,bc
+	ld (ix + GzipArchive.count),l
+	ld (ix + GzipArchive.count + 1),h
+	pop hl
+	ret nc
+	inc (ix + GzipArchive.count + 2)
+	ret nz
+	inc (ix + GzipArchive.count + 3)
 	ret
 
 	IF GZIP_CRC32
 
-; de = writer (min 32K)
+; hl = byte count
+; de = buffer address
 ; ix = this
-GzipArchive_CreateCRC32Checker:
+GzipArchive_UpdateCRC32:
 	push de
+	push hl
 	push ix
 	ld bc,GzipArchive.crc32Checker
 	add ix,bc
-	call CRC32Checker_Construct
+	call CRC32Checker_UpdateCRC32
 	pop ix
+	pop hl
 	pop de
 	ret
 
 	ENDIF
 
 ; ix = this
-GzipArchive_Inflate:
-	push ix
-	call GzipArchive_GetInflate
-	call Inflate_Inflate
-	pop ix
-	ret
-
-; ix = this
 GzipArchive_Verify:
 	call GzipArchive_VerifyISIZE
 	ld hl,GzipArchive_isizeMismatchError

          
@@ 214,21 253,16 @@ GzipArchive_Verify:
 ; ix = this
 ; f <- nz: mismatch
 GzipArchive_VerifyISIZE:
-	push ix
-	call GzipArchive_GetInflate
-	call Inflate_GetWriter
-	call Writer_GetCount
-	pop ix
-	ld a,e
+	ld a,(ix + GzipArchive.count)
 	cp (ix + GzipArchive.isize)
 	ret nz
-	ld a,d
+	ld a,(ix + GzipArchive.count + 1)
 	cp (ix + GzipArchive.isize + 1)
 	ret nz
-	ld a,c
+	ld a,(ix + GzipArchive.count + 2)
 	cp (ix + GzipArchive.isize + 2)
 	ret nz
-	ld a,b
+	ld a,(ix + GzipArchive.count + 3)
 	cp (ix + GzipArchive.isize + 3)
 	ret
 

          
M src/ZlibArchive.asm +58 -44
@@ 2,6 2,7 @@ 
 ; Zlib archive
 ;
 ZlibArchive_STATE_CHECKSUM_BIT: equ 0
+ZlibArchive_STATE_FOOTER_BIT: equ 1
 ZlibArchive_DEFLATE_ID: equ 8
 ZlibArchive_MAX_WINDOW: equ 7
 

          
@@ 30,7 31,9 @@ ZlibArchive: MACRO
 	ENDM
 
 ; a = -1: Adler32 check enabled, 0: disabled
-; de = reader
+; bc = decoders buffer
+; de = write buffer (32K, 256-byte aligned)
+; hl = reader
 ; ix = this
 ; ix <- this
 ZlibArchive_Construct:

          
@@ 40,8 43,22 @@ ZlibArchive_Construct:
 	xor a
 	ENDIF
 	ld (ix + ZlibArchive.state),a
-	ld (ix + ZlibArchive.reader),e
-	ld (ix + ZlibArchive.reader + 1),d
+	ld (ix + ZlibArchive.reader),l
+	ld (ix + ZlibArchive.reader + 1),h
+	push ix
+	push de
+	ld de,ZlibArchive.inflate
+	add ix,de
+	pop de
+	call Inflate_Construct
+	pop ix
+	IF ZLIB_ADLER32
+	push ix
+	ld bc,ZlibArchive.adler32Checker
+	add ix,bc
+	call Adler32Checker_Construct
+	pop ix
+	ENDIF
 	jp ZlibArchive_ReadHeader
 
 ; ix = this

          
@@ 55,16 72,6 @@ ZlibArchive_GetReaderIY:
 	ret
 
 ; ix = this
-; ix <- Inflate implementation
-; Modifies: f
-ZlibArchive_GetInflate:
-	push de
-	ld de,ZlibArchive.inflate
-	add ix,de
-	pop de
-	ret
-
-; ix = this
 ZlibArchive_ReadHeader:
 	call ZlibArchive_GetReaderIY
 	call Reader_ReadWordBE_IY

          
@@ 130,59 137,66 @@ ZlibArchive_ReadFooter:
 	ENDIF
 	ret
 
-; bc = decoders buffer
-; de = writer (min 32K)
 ; ix = this
-ZlibArchive_Extract:
-	IF ZLIB_ADLER32
-	push bc
-	bit ZlibArchive_STATE_CHECKSUM_BIT,(ix + ZlibArchive.state)
-	call nz,ZlibArchive_CreateAdler32Checker
-	pop bc
-	ENDIF
-	call ZlibArchive_CreateInflate
-	call ZlibArchive_Inflate
+; a <- >0: more data, 0: final data
+; f <- nz: more data, z: final data
+; de <- buffer address
+; hl <- byte count
+ZlibArchive_Inflate: PROC
+	push ix
+	ld de,ZlibArchive.inflate
+	add ix,de
+	call Inflate_Inflate
+	pop ix
+	call ZlibArchive_Update
+	ret nz
+	bit ZlibArchive_STATE_FOOTER_BIT,(ix + ZlibArchive.state)
+	call z,Footer
+	and a
+	ret
+Footer:
+	set ZlibArchive_STATE_FOOTER_BIT,(ix + ZlibArchive.state)
+	ex af,af'
+	exx
 	call ZlibArchive_ReadFooter
 	call ZlibArchive_Verify
+	exx
+	ex af,af'
 	ret
+	ENDP
 
-; bc = decoders buffer
-; de = writer (min 32K)
+; de = buffer address
+; hl = byte count
 ; ix = this
-ZlibArchive_CreateInflate:
-	ld l,(ix + ZlibArchive.reader)
-	ld h,(ix + ZlibArchive.reader + 1)
-	push ix
-	call ZlibArchive_GetInflate
-	call Inflate_Construct
-	pop ix
+ZlibArchive_Update:
+	IF ZLIB_ADLER32
+	push af
+	bit ZlibArchive_STATE_CHECKSUM_BIT,(ix + ZlibArchive.state)
+	call nz,ZlibArchive_UpdateAdler32
+	pop af
+	ENDIF
 	ret
 
 	IF ZLIB_ADLER32
 
-; de = writer (min 32K)
+; de = buffer address
+; hl = byte count
 ; ix = this
-ZlibArchive_CreateAdler32Checker:
+ZlibArchive_UpdateAdler32:
 	push de
+	push hl
 	push ix
 	ld bc,ZlibArchive.adler32Checker
 	add ix,bc
-	call Adler32Checker_Construct
+	call Adler32Checker_UpdateAdler32
 	pop ix
+	pop hl
 	pop de
 	ret
 
 	ENDIF
 
 ; ix = this
-ZlibArchive_Inflate:
-	push ix
-	call ZlibArchive_GetInflate
-	call Inflate_Inflate
-	pop ix
-	ret
-
-; ix = this
 ZlibArchive_Verify:
 	IF ZLIB_ADLER32
 	call ZlibArchive_VerifyAdler32

          
M src/deflate/Inflate.asm +268 -63
@@ 1,14 1,36 @@ 
 ;
 ; Inflate implementation
 ;
-Inflate_FINAL: equ 0
+Inflate_STATE_FINAL_BIT: equ 0
+Inflate_STATE_COPY_BIT: equ 1
+Inflate_STATE_COMPRESSED_BIT: equ 2
+Inflate_BUFFER_SIZE: equ 8000H
 
 Inflate: MACRO
+	; a = value
+	; hl = literal/length decoder
+	; ix = this
+	WriteLiteral:
+		ld (0),a
+	bufferPosition: equ $ - 2
+		inc (ix + Inflate.bufferPosition)
+		jr z,WriteLiteralCarry  ; unlikely
+		jp hl
+	WriteLiteralCarry:
+		jp Inflate_WriteLiteralCarry
+	bufferStart:
+		db 0
+	bufferEnd:
+		db 0
+	bufferEndCopyMargin:
+		db 0
 	state:
 		db 0
-	reader:
+	copyLength:
 		dw 0
-	writer:
+	copySource:
+		dw 0
+	reader:
 		dw 0
 	decoders:
 		dw 0

          
@@ 18,21 40,25 @@ Inflate: MACRO
 	ENDM
 
 ; bc = decoders buffer
+; de = write buffer (32K, 256-byte aligned)
 ; hl = reader
-; de = writer (min 32K)
 ; ix = this
 ; ix <- this
 Inflate_Construct:
-	ld iyl,e  ; check if write buffer is at least 32K
-	ld iyh,d
-	ld a,(iy + Writer.bufferSize + 1)
-	cp 80H
-	call c,System_ThrowException
+	ld a,e  ; check if write buffer is 256-byte aligned
+	and a
+	call nz,System_ThrowException
+	ld (ix + Inflate.bufferStart),d
+	ld (ix + Inflate.bufferPosition),0
+	ld (ix + Inflate.bufferPosition + 1),d
+	ld a,d
+	add a,Inflate_BUFFER_SIZE >> 8
+	ld (ix + Inflate.bufferEnd),a
+	sub 3
+	ld (ix + Inflate.bufferEndCopyMargin),a
 	ld (ix + Inflate.state),0
 	ld (ix + Inflate.reader),l
 	ld (ix + Inflate.reader + 1),h
-	ld (ix + Inflate.writer),e
-	ld (ix + Inflate.writer + 1),d
 	ld (ix + Inflate.decoders),c
 	ld (ix + Inflate.decoders + 1),b
 	push ix

          
@@ 52,16 78,6 @@ Inflate_GetReaderIY:
 	ret
 
 ; ix = this
-; ix <- writer
-; Modifies: de
-Inflate_GetWriter:
-	ld e,(ix + Inflate.writer)
-	ld d,(ix + Inflate.writer + 1)
-	ld ixl,e
-	ld ixh,d
-	ret
-
-; ix = this
 ; ix <- decoders buffer
 ; Modifies: de
 Inflate_GetDecoders:

          
@@ 80,22 96,41 @@ Inflate_GetHuffmanCodes:
 	ret
 
 ; ix = this
+; a <- >0: more data, 0: final data
+; f <- nz: more data, z: final data
+; de <- buffer address
+; hl <- byte count
 Inflate_Inflate:
 	call Inflate_GetReaderIY
-	call Inflate_InflateLoop
-	push ix
-	call Inflate_GetWriter
-	call Writer_FinishBlock
-	pop ix
+	call Inflate_InflateResume
+	ld l,(ix + Inflate.bufferPosition)
+	ld h,(ix + Inflate.bufferPosition + 1)
+	ld e,0
+	ld d,(ix + Inflate.bufferStart)
+	ld (ix + Inflate.bufferPosition),e
+	ld (ix + Inflate.bufferPosition + 1),d
+	and a
+	sbc hl,de
+	ld a,(ix + Inflate.state)
+	xor 1 << Inflate_STATE_FINAL_BIT
 	ret
 
 ; ix = this
+Inflate_InflateResume:
+	bit Inflate_STATE_COMPRESSED_BIT,(ix + Inflate.state)
+	jp nz,Inflate_InflateCompressed
+	bit Inflate_STATE_COPY_BIT,(ix + Inflate.state)
+	jp nz,Inflate_InflateUncompressed.Resume
+	jp Inflate_InflateLoop
+
+; ix = this
 Inflate_InflateLoop:
 	ld a,(ix + Inflate.state)
-	and 1 << Inflate_FINAL
+	and 1 << Inflate_STATE_FINAL_BIT
+	ld (ix + Inflate.state),a
 	ret nz
 	call Reader_ReadBit_IY
-	rla  ; carry to Inflate_FINAL
+	rla  ; carry to Inflate_STATE_FINAL_BIT
 	ld (ix + Inflate.state),a
 	ld b,2
 	call Reader_ReadBits_IY

          
@@ 108,7 143,8 @@ Inflate_InflateLoop:
 
 ; ix = this
 ; iy = reader
-Inflate_InflateUncompressed:
+Inflate_InflateUncompressed: PROC
+	set Inflate_STATE_COPY_BIT,(ix + Inflate.state)
 	call Reader_Align_IY
 	call Reader_ReadWord_IY
 	ld c,e

          
@@ 118,14 154,46 @@ Inflate_InflateUncompressed:
 	scf
 	adc hl,bc
 	jp nz,System_ThrowException  ; invalid length
+ResumeLoop:
+	ld e,(ix + Inflate.bufferPosition)
+	ld d,(ix + Inflate.bufferPosition + 1)
+Loop:
 	ld a,c
 	or b
-	ret z
-	push ix
-	call Inflate_GetWriter
-	call Writer_CopyFromReader
-	pop ix
-	jp Inflate_InflateLoop
+	jp z,Inflate_InflateLoop
+	ld l,0
+	ld h,(ix + Inflate.bufferEnd)
+	sbc hl,de
+	jr c,Suspend
+	jr z,Suspend
+	push bc
+	sbc hl,bc
+	jr nc,NoSplit  ; bc <= hl
+	add hl,bc
+	ld c,l
+	ld b,h
+NoSplit:
+	call Reader_ReadBlockDirect_IY
+	push bc
+	call System_FastLDIR
+	pop bc
+	pop hl
+	and a
+	sbc hl,bc
+	ld c,l
+	ld b,h
+	ld (ix + Inflate.bufferPosition),e
+	ld (ix + Inflate.bufferPosition + 1),d
+	jr Loop
+Suspend:
+	ld (ix + Inflate.copyLength),c
+	ld (ix + Inflate.copyLength + 1),b
+	ret
+Resume:
+	ld c,(ix + Inflate.copyLength)
+	ld b,(ix + Inflate.copyLength + 1)
+	jr ResumeLoop
+	ENDP
 
 ; ix = this
 ; iy = reader

          
@@ 152,54 220,67 @@ Inflate_InflateCompressed:
 	call Inflate_GetDecoders
 	call Decoders_GetDecoders
 	pop ix
-	push ix
-	ld c,(ix + Inflate.writer)
-	ld b,(ix + Inflate.writer + 1)
-	ld ixl,c
-	ld ixh,b
 	call Reader_PrepareReadBitInline_IY
-	call Inflate_DecodeLiteralLength
+	call Inflate_Decode
 	call Reader_FinishReadBitInline_IY
-	pop ix
+	bit Inflate_STATE_COMPRESSED_BIT,(ix + Inflate.state)
+	ret nz
 	jp Inflate_InflateLoop
 
+Inflate_Decode: PROC
+	set Inflate_STATE_COMPRESSED_BIT,(ix + Inflate.state)
+	bit Inflate_STATE_COPY_BIT,(ix + Inflate.state)
+	jp nz,Inflate_CopySplitDestination.Resume
+	Inflate_DecodeLiteralLengthInline
+	ENDP
+
 ; c = inline bit reader state
 ; hl = literal/length decoder
 ; de = distance decoder
-; ix = writer
+; ix = this
 ; iy = reader
 Inflate_DecodeLiteralLengthInline: MACRO
 	jp hl
 	ENDM
 
-Inflate_DecodeLiteralLength:
-	Inflate_DecodeLiteralLengthInline
-
 ; Literal/length alphabet symbols 0-255
 ; c = inline bit reader state
 ; hl = literal/length decoder
 ; de = distance decoder
-; ix = writer
+; ix = this
 ; iy = reader
 Inflate_WriteLiteral: REPT 256, ?value
 	ld a,?value
-	Writer_WriteInline_JumpHL
+	jp ix
 	ENDM
 
+; c = inline bit reader state
+; hl = literal/length decoder
+; de = distance decoder
+; ix = this
+; iy = reader
+Inflate_WriteLiteralCarry:
+	inc (ix + Inflate.bufferPosition + 1)
+	ld a,(ix + Inflate.bufferPosition + 1)
+	cp (ix + Inflate.bufferEnd)
+	ret nc
+	jp hl
+
 ; Literal/length alphabet symbol 256
 ; c = inline bit reader state
 ; hl = literal/length decoder
 ; de = distance decoder
-; ix = writer
+; ix = this
 ; iy = reader
 Inflate_EndBlock:
+	res Inflate_STATE_COMPRESSED_BIT,(ix + Inflate.state)
 	ret
 
 ; Literal/length alphabet symbols 257-285
 ; c = inline bit reader state
 ; hl = literal/length decoder
 ; de = distance decoder
-; ix = writer
+; ix = this
 ; iy = reader
 Inflate_CopyLength.0:
 	exx

          
@@ 326,7 407,7 @@ Inflate_CopyLength.28:
 ; c' = inline bit reader state
 ; hl' = literal/length decoder
 ; de' = distance decoder
-; ix = writer
+; ix = this
 ; iy = reader
 Inflate_DecodeDistance_SetLength:
 	exx

          
@@ 338,7 419,7 @@ Inflate_DecodeDistance_SetLength:
 ; c' = inline bit reader state
 ; hl' = literal/length decoder
 ; de' = distance decoder
-; ix = writer
+; ix = this
 ; iy = reader
 Inflate_DecodeDistanceInline: MACRO
 	exx

          
@@ 351,7 432,7 @@ Inflate_DecodeDistanceInline: MACRO
 ; bc = length
 ; de = literal/length decoder
 ; hl = distance decoder
-; ix = writer
+; ix = this
 ; iy = reader
 Inflate_CopyDistance.0:
 	exx

          
@@ 511,13 592,13 @@ Inflate_CopyDistance.29:
 ; c' = inline bit reader state
 ; de' = literal/length decoder
 ; hl' = distance decoder
-; ix = writer
+; ix = this
 ; iy = reader
 Inflate_CopySmallDistance:
 	exx
 	ld l,a
 	ld h,-1
-	Inflate_CopyInline
+	jp Inflate_Copy
 
 ; a = -distance MSB
 ; a' = ~-distance LSB

          
@@ 525,7 606,7 @@ Inflate_CopySmallDistance:
 ; c' = inline bit reader state
 ; de' = literal/length decoder
 ; hl' = distance decoder
-; ix = writer
+; ix = this
 ; iy = reader
 Inflate_CopyLargeDistance:
 	exx

          
@@ 533,24 614,148 @@ Inflate_CopyLargeDistance:
 	ex af,af'
 	cpl
 	ld l,a
-	Inflate_CopyInline
+	jp Inflate_Copy
 
 ; bc = length
 ; hl = -distance
 ; c' = inline bit reader state
 ; de' = literal/length decoder
 ; hl' = distance decoder
-; ix = writer
+; ix = this
 ; iy = reader
-Inflate_CopyInline: MACRO
-	call Writer_Copy
+Inflate_Copy: PROC
+	ld e,(ix + Inflate.bufferPosition)
+	ld d,(ix + Inflate.bufferPosition + 1)
+	add hl,de
+	ld a,h
+	jr nc,WrapSource
+	cp (ix + Inflate.bufferStart)
+	jr c,WrapSource
+	ld a,(ix + Inflate.bufferEndCopyMargin)
+	cp d  ; does the destination have a 512 byte margin without wrapping?
+	jr c,Inflate_CopySplitDestination
+Copy:
+	ldi
+	ldi
+	ldir
+Next:
+	ld (ix + Inflate.bufferPosition),e
+	ld (ix + Inflate.bufferPosition + 1),d
 	exx
 	ex de,hl
 	Inflate_DecodeLiteralLengthInline
-	ENDM
+WrapSource:
+	add a,Inflate_BUFFER_SIZE >> 8
+	ld h,a
+	ld a,(ix + Inflate.bufferEndCopyMargin)
+	cp d  ; does the destination have a 512 byte margin without wrapping?
+	jr c,Inflate_CopySplitDestination
+	cp h  ; does the source have a 512 byte margin without wrapping?
+	jp nc,Copy
+	call Inflate_CopySplitSource
+	jp Next
+	ENDP
 
-Inflate_Copy:
-	Inflate_CopyInline
+; bc = byte count
+; de = destination
+; hl = source
+; ix = this
+; bc <- 0
+; de <- updated destination
+; hl <- updated source
+; Modifies: af
+Inflate_CopySplitDestination: PROC
+	ex de,hl
+	add hl,bc
+	jr c,Split
+	ld a,h
+	cp (ix + Inflate.bufferEnd)
+	jr nc,Split
+	and a
+	sbc hl,bc
+	ex de,hl
+	call Inflate_CopySplitSource
+	jp Inflate_Copy.Next
+Split:
+	push hl
+	xor a
+	sbc hl,bc
+	sub l
+	ld c,a
+	ld a,(ix + Inflate.bufferEnd)
+	sbc a,h
+	ld b,a  ; bc = buffer end - source start
+	ex de,hl
+	call Inflate_CopySplitSource
+	pop bc
+	ld a,b
+	sub (ix + Inflate.bufferEnd)
+	ld b,a  ; bc = source end - buffer end
+	or c
+	jp z,SuspendLiteral
+Suspend:
+	set Inflate_STATE_COPY_BIT,(ix + Inflate.state)
+	ld (ix + Inflate.copyLength),c
+	ld (ix + Inflate.copyLength + 1),b
+	ld (ix + Inflate.copySource),l
+	ld (ix + Inflate.copySource + 1),h
+SuspendLiteral:
+	ld (ix + Inflate.bufferPosition),e
+	ld (ix + Inflate.bufferPosition + 1),d
+	exx
+	ex de,hl
+	ret
+Resume:
+	res Inflate_STATE_COPY_BIT,(ix + Inflate.state)
+	ex de,hl
+	exx
+	ld c,(ix + Inflate.copyLength)
+	ld b,(ix + Inflate.copyLength + 1)
+	ld e,(ix + Inflate.bufferPosition)
+	ld d,(ix + Inflate.bufferPosition + 1)
+	ld l,(ix + Inflate.copySource)
+	ld h,(ix + Inflate.copySource + 1)
+	jp Inflate_CopySplitDestination
+	ENDP
+
+; bc = byte count
+; de = destination
+; hl = source
+; ix = this
+; bc <- 0
+; de <- updated destination
+; hl <- updated source
+; Modifies: af
+Inflate_CopySplitSource: PROC
+	add hl,bc
+	jr c,Split
+	ld a,h
+	cp (ix + Inflate.bufferEnd)
+	jr nc,Split
+	and a
+	sbc hl,bc
+	ldir
+	ret
+Split:
+	push hl
+	xor a
+	sbc hl,bc
+	sub l
+	ld c,a
+	ld a,(ix + Inflate.bufferEnd)
+	sbc a,h
+	ld b,a  ; bc = buffer end - source start
+	ldir
+	pop bc
+	ld h,(ix + Inflate.bufferStart)
+	ld a,b
+	sub (ix + Inflate.bufferEnd)
+	ld b,a  ; bc = source end - buffer end
+	or c
+	ret z
+	ldir
+	ret
+	ENDP
 
 ;
 Inflate_literalLengthSymbols: