summaryrefslogtreecommitdiff
path: root/src/home/decompress.asm
blob: b8952cceabec394ad83ac4a4d67732464fadec0c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
; initializes variables used to decompress data in DecompressData
; de = source of compressed data
; b = HIGH byte of secondary buffer ($100 bytes of buffer space)
; also clears this $100 byte space
InitDataDecompression:
	ld hl, wDecompSourcePosPtr
	ld [hl], e
	inc hl
	ld [hl], d
	ld hl, wDecompNumCommandBitsLeft
	ld [hl], 1
	inc hl
	xor a
	ld [hli], a ; wDecompCommandByte
	ld [hli], a ; wDecompRepeatModeToggle
	ld [hli], a ; wDecompRepeatLengths
	ld [hli], a ; wDecompNumBytesToRepeat
	ld [hl], b  ; wDecompSecondaryBufferPtrHigh
	inc hl
	ld [hli], a ; wDecompRepeatSeqOffset
	ld [hl], LOW(wDecompressionSecondaryBufferStart) ; wDecompSecondaryBufferPtrLow

; clear buffer
	ld h, b
	ld l, LOW(wDecompressionSecondaryBuffer)
	xor a
.loop
	ld [hl], a
	inc l
	jr nz, .loop
	ret

; decompresses data
; uses values initialized by InitDataDecompression
; wDecompSourcePosPtr holds the pointer for compressed source
; input:
; bc = row width
; de = buffer to place decompressed data
DecompressData:
	push hl
	push de
.loop
	push bc
	call .Decompress
	ld [de], a
	inc de
	pop bc
	dec bc
	ld a, c
	or b
	jr nz, .loop
	pop de
	pop hl
	ret

; decompression works as follows:
; first a command byte is read that will dictate how the
; following bytes will be copied
; the position will then move to the next byte (0xXY), and
; the command byte's bits are read from higher to lower bit
; - if command bit is set, then copy 0xXY to buffer;
; - if command bit is not set, then decompression enters "repeat mode,"
; which means it stores 0xXY in memory as number of bytes to repeat
; from a given offset. This offset is in the next byte in the data,
; 0xZZ, which tells the offset to start repeating. A toggle is switched
; each time the algorithm hits "repeat mode":
;  - if off -> on it reads 0xXY and stores it,
;  then repeats (0x0X + 2) bytes from the offset starting at 0xZZ;
;  - if on -> off, then the data only provides the offset,
;  and the previous byte read for number of bytes to repeat, 0xXY, is reused
;  in which case (0x0Y + 2) bytes are repeated starting from the offset.
.Decompress:
	ld hl, wDecompNumBytesToRepeat
	ld a, [hl]
	or a
	jr z, .read_command

; still repeating sequence
	dec [hl]
	inc hl
.repeat_byte
	ld b, [hl] ; wDecompSecondaryBufferPtrHigh
	inc hl
	ld c, [hl] ; wDecompRepeatSeqOffset
	inc [hl]
	inc hl
	ld a, [bc]
	ld c, [hl] ; wDecompSecondaryBufferPtrLow
	inc [hl]
	ld [bc], a
	ret

.read_command
	ld hl, wDecompSourcePosPtr
	ld c, [hl]
	inc hl
	ld b, [hl]
	inc hl ; wDecompNumCommandBitsLeft
	dec [hl]
	inc hl ; wDecompCommandByte
	jr nz, .read_command_bit
	dec hl ; wDecompNumCommandBitsLeft
	ld [hl], 8 ; number of bits
	inc hl ; wDecompCommandByte
	ld a, [bc]
	inc bc
	ld [hl], a
.read_command_bit
	rl [hl]
	ld a, [bc]
	inc bc
	jr nc, .repeat_command

; copy 1 byte literally
	ld hl, wDecompSourcePosPtr
	ld [hl], c
	inc hl
	ld [hl], b
	ld hl, wDecompSecondaryBufferPtrHigh
	ld b, [hl]
	inc hl
	inc hl
	ld c, [hl] ; wDecompSecondaryBufferPtrLow
	inc [hl]
	ld [bc], a
	ret

.repeat_command
	ld [wDecompRepeatSeqOffset], a ; save the offset to repeat from
	ld hl, wDecompRepeatModeToggle
	bit 0, [hl]
	jr nz, .repeat_mode_toggle_on
	set 0, [hl]
	inc hl
; read byte for num of bytes to read
; and use its higher nybble
	ld a, [bc]
	inc bc
	ld [hli], a ; wDecompRepeatLengths
	swap a
.get_sequence_len
	and $f
	inc a ; number of times to repeat
	ld [hli], a ; wDecompNumBytesToRepeat
	push hl
	ld hl, wDecompSourcePosPtr
	ld [hl], c
	inc hl
	ld [hl], b
	pop hl
	jr .repeat_byte

.repeat_mode_toggle_on
; get the previous byte (num of bytes to repeat)
; and use its lower nybble
	res 0, [hl]
	inc hl
	ld a, [hli] ; wDecompRepeatLengths
	jr .get_sequence_len