80un 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 80un-0.2.0.dist-info/METADATA +517 -0
- 80un-0.2.0.dist-info/RECORD +14 -0
- 80un-0.2.0.dist-info/WHEEL +5 -0
- 80un-0.2.0.dist-info/entry_points.txt +2 -0
- 80un-0.2.0.dist-info/licenses/LICENSE +209 -0
- 80un-0.2.0.dist-info/top_level.txt +1 -0
- un80/__init__.py +30 -0
- un80/arc.py +553 -0
- un80/cli.py +350 -0
- un80/cpm.py +171 -0
- un80/crlzh.py +221 -0
- un80/crunch.py +300 -0
- un80/lbr.py +272 -0
- un80/squeeze.py +250 -0
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: 80un
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Unpacker for CP/M compression and packing formats
|
|
5
|
+
Author: avwohl
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/avwohl/80un
|
|
8
|
+
Project-URL: Repository, https://github.com/avwohl/80un
|
|
9
|
+
Keywords: cpm,cp/m,8080,unpack,squeeze,crunch,lbr,arc,retro
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: System :: Archiving :: Compression
|
|
22
|
+
Classifier: Topic :: System :: Archiving
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# 80un
|
|
32
|
+
|
|
33
|
+
Unpack and decompress archive and compression formats used on the CP/M operating system for Z80 computers.
|
|
34
|
+
|
|
35
|
+
Two implementations are provided:
|
|
36
|
+
|
|
37
|
+
| Version | Runs On | Use Case |
|
|
38
|
+
|---------|---------|----------|
|
|
39
|
+
| **80un.com** | CP/M 2.2+ | Extract archives on vintage hardware or emulators |
|
|
40
|
+
| **80un (Python)** | Python 3.8+ | Extract archives on modern systems |
|
|
41
|
+
|
|
42
|
+
Both support the same formats and produce identical output.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Python Version
|
|
47
|
+
|
|
48
|
+
### Installation
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install 80un
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Requires Python 3.8 or later. No external dependencies.
|
|
55
|
+
|
|
56
|
+
## Quick Start
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Extract an LBR archive
|
|
60
|
+
80un archive.lbr
|
|
61
|
+
|
|
62
|
+
# Extract an ARC archive to a specific directory
|
|
63
|
+
80un archive.arc -o extracted/
|
|
64
|
+
|
|
65
|
+
# List contents of an archive without extracting
|
|
66
|
+
80un archive.lbr -l
|
|
67
|
+
|
|
68
|
+
# Decompress a crunched file
|
|
69
|
+
80un document.tzt
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Supported Formats
|
|
73
|
+
|
|
74
|
+
### Archive Formats (contain multiple files)
|
|
75
|
+
|
|
76
|
+
| Format | Extensions | Description |
|
|
77
|
+
|--------|------------|-------------|
|
|
78
|
+
| **LBR** | `.lbr`, `.lqr`, `.lzr` | Library archive, similar to tar. Files inside may be compressed individually. |
|
|
79
|
+
| **ARC** | `.arc`, `.ark` | Compressed archive supporting multiple compression methods (stored, packed, squeezed, crunched, squashed). |
|
|
80
|
+
|
|
81
|
+
### Compression Formats (single file)
|
|
82
|
+
|
|
83
|
+
| Format | Extensions | Magic Bytes | Description |
|
|
84
|
+
|--------|------------|-------------|-------------|
|
|
85
|
+
| **Squeeze** | `.?q?` | `76 FF` | Huffman coding with run-length encoding. Devised by Richard Greenlaw, 1981. |
|
|
86
|
+
| **Crunch** | `.?z?` | `76 FE` | LZW compression similar to Unix compress. More efficient than squeeze. |
|
|
87
|
+
| **CrLZH** | `.?y?` | `76 FD` | LZH compression (Lempel-Ziv + Huffman). Most efficient CP/M compression. |
|
|
88
|
+
|
|
89
|
+
### CP/M File Naming Convention
|
|
90
|
+
|
|
91
|
+
CP/M used 8.3 filenames. Compressed files indicated their compression by replacing the **middle letter** of the extension:
|
|
92
|
+
|
|
93
|
+
| Original | Squeezed | Crunched | CrLZH |
|
|
94
|
+
|----------|----------|----------|-------|
|
|
95
|
+
| `FILE.TXT` | `FILE.TQT` | `FILE.TZT` | `FILE.TYT` |
|
|
96
|
+
| `FILE.COM` | `FILE.CQM` | `FILE.CZM` | `FILE.CYM` |
|
|
97
|
+
| `FILE.ASM` | `FILE.AQM` | `FILE.AZM` | `FILE.AYM` |
|
|
98
|
+
| `FILE.DOC` | `FILE.DQC` | `FILE.DZC` | `FILE.DYC` |
|
|
99
|
+
|
|
100
|
+
Files with no extension used `.QQQ`, `.ZZZ`, or `.YYY`.
|
|
101
|
+
|
|
102
|
+
## Command Line Usage
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
usage: 80un [-h] [--version] [-o DIR] [-l] [-t] [-f FORMAT] [-n] file
|
|
106
|
+
|
|
107
|
+
Unpacker for CP/M compression and packing formats
|
|
108
|
+
|
|
109
|
+
positional arguments:
|
|
110
|
+
file File to extract or decompress
|
|
111
|
+
|
|
112
|
+
options:
|
|
113
|
+
-h, --help Show this help message and exit
|
|
114
|
+
--version Show program's version number and exit
|
|
115
|
+
-o, --output DIR Output directory for extracted files
|
|
116
|
+
-l, --list List contents without extracting
|
|
117
|
+
-t, --text Convert text files (strip ^Z, CR/LF to LF)
|
|
118
|
+
-f, --format FORMAT Force file format: lbr, arc, squeeze, crunch, crlzh
|
|
119
|
+
-n, --no-clobber Do not overwrite existing files
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Examples
|
|
123
|
+
|
|
124
|
+
**List contents of an LBR archive:**
|
|
125
|
+
```bash
|
|
126
|
+
$ 80un myarchive.lbr -l
|
|
127
|
+
Filename Size Sectors
|
|
128
|
+
------------------------------------
|
|
129
|
+
README.TZT 512 4
|
|
130
|
+
PROGRAM.CZM 8192 64
|
|
131
|
+
DATA.DZT 1024 8
|
|
132
|
+
|
|
133
|
+
3 file(s)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**List contents of an ARC archive:**
|
|
137
|
+
```bash
|
|
138
|
+
$ 80un myarchive.arc -l
|
|
139
|
+
Filename Original Compressed Method
|
|
140
|
+
------------------------------------------------------
|
|
141
|
+
README.TXT 1024 512 crunched LZW
|
|
142
|
+
PROGRAM.COM 16384 8192 crunched LZW
|
|
143
|
+
DATA.DAT 2048 1024 squeezed
|
|
144
|
+
|
|
145
|
+
3 file(s)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Extract an archive:**
|
|
149
|
+
```bash
|
|
150
|
+
$ 80un myarchive.lbr
|
|
151
|
+
README.TXT
|
|
152
|
+
PROGRAM.COM
|
|
153
|
+
DATA.DAT
|
|
154
|
+
|
|
155
|
+
Extracted 3 file(s)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Extract to a specific directory:**
|
|
159
|
+
```bash
|
|
160
|
+
$ 80un myarchive.lbr -o output/
|
|
161
|
+
README.TXT
|
|
162
|
+
PROGRAM.COM
|
|
163
|
+
DATA.DAT
|
|
164
|
+
|
|
165
|
+
Extracted 3 file(s)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Extract and convert text files to Unix format:**
|
|
169
|
+
```bash
|
|
170
|
+
$ 80un myarchive.lbr -t -o output/
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
This strips the ^Z (Ctrl-Z) end-of-file padding and converts CR/LF line endings to Unix LF.
|
|
174
|
+
|
|
175
|
+
**Decompress a single crunched file:**
|
|
176
|
+
```bash
|
|
177
|
+
$ 80un document.tzt
|
|
178
|
+
document.txt (2048 bytes)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
The original filename is recovered from the compressed file header.
|
|
182
|
+
|
|
183
|
+
**Force a specific format:**
|
|
184
|
+
```bash
|
|
185
|
+
$ 80un unknown.dat -f crunch
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Extract without overwriting existing files:**
|
|
189
|
+
```bash
|
|
190
|
+
$ 80un myarchive.lbr -o output/ -n
|
|
191
|
+
README.TXT
|
|
192
|
+
PROGRAM.COM (skipped, already exists)
|
|
193
|
+
DATA.DAT
|
|
194
|
+
|
|
195
|
+
3 file(s): 2 extracted, 1 skipped
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The `-n` / `--no-clobber` option is useful when extracting multiple archives to the same directory, or when you want to preserve files you've already modified.
|
|
199
|
+
|
|
200
|
+
## Python API
|
|
201
|
+
|
|
202
|
+
### Extracting Archives
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
from un80 import extract_lbr, extract_arc
|
|
206
|
+
|
|
207
|
+
# Extract LBR archive
|
|
208
|
+
# Returns list of (filename, data) tuples
|
|
209
|
+
files = extract_lbr("archive.lbr", "output_dir/")
|
|
210
|
+
for filename, data in files:
|
|
211
|
+
print(f"Extracted {filename}: {len(data)} bytes")
|
|
212
|
+
|
|
213
|
+
# Extract without writing to disk
|
|
214
|
+
files = extract_lbr("archive.lbr") # No output_dir
|
|
215
|
+
for filename, data in files:
|
|
216
|
+
process(data)
|
|
217
|
+
|
|
218
|
+
# Extract with text conversion
|
|
219
|
+
files = extract_lbr("archive.lbr", "output/", convert_text=True)
|
|
220
|
+
|
|
221
|
+
# Extract ARC archive
|
|
222
|
+
files = extract_arc("archive.arc", "output_dir/")
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Decompressing Single Files
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
from un80 import unsqueeze, uncrunch, uncrlzh
|
|
229
|
+
|
|
230
|
+
# Read compressed file
|
|
231
|
+
with open("document.tqt", "rb") as f:
|
|
232
|
+
compressed = f.read()
|
|
233
|
+
|
|
234
|
+
# Decompress based on format
|
|
235
|
+
decompressed = unsqueeze(compressed) # For .?q? files
|
|
236
|
+
decompressed = uncrunch(compressed) # For .?z? files
|
|
237
|
+
decompressed = uncrlzh(compressed) # For .?y? files
|
|
238
|
+
|
|
239
|
+
# Write decompressed data
|
|
240
|
+
with open("document.txt", "wb") as f:
|
|
241
|
+
f.write(decompressed)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Listing Archive Contents
|
|
245
|
+
|
|
246
|
+
```python
|
|
247
|
+
from un80.lbr import list_lbr
|
|
248
|
+
from un80.arc import list_arc
|
|
249
|
+
|
|
250
|
+
# List LBR contents
|
|
251
|
+
for entry in list_lbr("archive.lbr"):
|
|
252
|
+
print(f"{entry.filename}: {entry.data_size} bytes")
|
|
253
|
+
|
|
254
|
+
# List ARC contents
|
|
255
|
+
for entry in list_arc("archive.arc"):
|
|
256
|
+
print(f"{entry.filename}: {entry.original_size} bytes ({entry.method_name})")
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### CP/M Text File Utilities
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
from un80 import strip_cpm_eof, crlf_to_lf, is_text_file
|
|
263
|
+
|
|
264
|
+
# Strip ^Z EOF padding from CP/M text file
|
|
265
|
+
data = strip_cpm_eof(data)
|
|
266
|
+
|
|
267
|
+
# Convert CR/LF to Unix LF
|
|
268
|
+
data = crlf_to_lf(data)
|
|
269
|
+
|
|
270
|
+
# Check if file is likely text based on extension
|
|
271
|
+
if is_text_file("readme.txt"):
|
|
272
|
+
data = strip_cpm_eof(data)
|
|
273
|
+
data = crlf_to_lf(data)
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Format Detection
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
from un80.cpm import detect_compression
|
|
280
|
+
|
|
281
|
+
with open("unknown.file", "rb") as f:
|
|
282
|
+
data = f.read()
|
|
283
|
+
|
|
284
|
+
format_type = detect_compression(data)
|
|
285
|
+
# Returns: 'squeeze', 'crunch', 'crlzh', 'arc', 'lbr', or None
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Getting Original Filenames
|
|
289
|
+
|
|
290
|
+
Compressed files store the original filename in their header:
|
|
291
|
+
|
|
292
|
+
```python
|
|
293
|
+
from un80.squeeze import get_squeezed_filename
|
|
294
|
+
from un80.crunch import get_crunched_filename
|
|
295
|
+
from un80.crlzh import get_crlzh_filename
|
|
296
|
+
|
|
297
|
+
with open("file.tzt", "rb") as f:
|
|
298
|
+
data = f.read()
|
|
299
|
+
|
|
300
|
+
original_name = get_crunched_filename(data)
|
|
301
|
+
print(f"Original filename: {original_name}") # e.g., "FILE.TXT"
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## CP/M File Handling
|
|
305
|
+
|
|
306
|
+
CP/M files have characteristics that differ from modern systems:
|
|
307
|
+
|
|
308
|
+
### 128-Byte Records
|
|
309
|
+
|
|
310
|
+
CP/M measured file sizes in 128-byte records (sectors), not bytes. A file's actual byte length wasn't stored; only the record count. This means:
|
|
311
|
+
|
|
312
|
+
- Files are always multiples of 128 bytes
|
|
313
|
+
- The last record may contain padding
|
|
314
|
+
|
|
315
|
+
### ^Z End-of-File Marker
|
|
316
|
+
|
|
317
|
+
Text files that didn't fill their last 128-byte record were padded. The convention was to mark the end of actual content with a Ctrl-Z character (0x1A), with the remainder filled with more ^Z characters or garbage.
|
|
318
|
+
|
|
319
|
+
Use `--text` or `strip_cpm_eof()` to remove this padding.
|
|
320
|
+
|
|
321
|
+
### CR/LF Line Endings
|
|
322
|
+
|
|
323
|
+
CP/M text files used CR/LF (carriage return + line feed, 0x0D 0x0A) line endings, like DOS/Windows. Use `--text` or `crlf_to_lf()` to convert to Unix-style LF endings.
|
|
324
|
+
|
|
325
|
+
## ARC Compression Methods
|
|
326
|
+
|
|
327
|
+
ARC archives can contain files compressed with different methods:
|
|
328
|
+
|
|
329
|
+
| Method | Name | Description |
|
|
330
|
+
|--------|------|-------------|
|
|
331
|
+
| 1 | Stored (old) | No compression (obsolete) |
|
|
332
|
+
| 2 | Stored | No compression |
|
|
333
|
+
| 3 | Packed | Run-length encoding only |
|
|
334
|
+
| 4 | Squeezed | Huffman coding after RLE |
|
|
335
|
+
| 5 | Crunched (old) | 12-bit LZW (obsolete) |
|
|
336
|
+
| 6 | Crunched+RLE | 12-bit LZW with RLE (obsolete) |
|
|
337
|
+
| 7 | Crunched | LZW with faster hash |
|
|
338
|
+
| 8 | Crunched | 9-12 bit LZW (most common) |
|
|
339
|
+
| 9 | Squashed | 13-bit LZW (Phil Katz) |
|
|
340
|
+
|
|
341
|
+
## Troubleshooting
|
|
342
|
+
|
|
343
|
+
### "Cannot determine format"
|
|
344
|
+
|
|
345
|
+
The file doesn't have a recognized magic number or extension. Try specifying the format manually:
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
80un mystery.dat -f crunch
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Garbled output from text files
|
|
352
|
+
|
|
353
|
+
The file may still have CP/M formatting. Use the `--text` option:
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
80un archive.lbr -t
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### "Invalid magic" or decompression errors
|
|
360
|
+
|
|
361
|
+
The file may be corrupted, truncated, or not actually in the detected format. Try:
|
|
362
|
+
|
|
363
|
+
1. Verify the file is complete
|
|
364
|
+
2. Try a different format with `-f`
|
|
365
|
+
3. Check if it's a different vintage format not yet supported
|
|
366
|
+
|
|
367
|
+
### Files extract with wrong names
|
|
368
|
+
|
|
369
|
+
Some very old archives don't store original filenames. The tool will use the archive member name with the compression indicator removed.
|
|
370
|
+
|
|
371
|
+
### Duplicate filenames in archive
|
|
372
|
+
|
|
373
|
+
Some archives contain multiple files with the same name (e.g., from different directories that CP/M flattened). When this happens, 80un automatically renames duplicates by appending `_1`, `_2`, etc.:
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
$ 80un archive_with_dupes.lbr
|
|
377
|
+
README.TXT
|
|
378
|
+
README.TXT -> README_1.TXT
|
|
379
|
+
DATA.DAT
|
|
380
|
+
|
|
381
|
+
3 file(s): 3 extracted
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
## History
|
|
385
|
+
|
|
386
|
+
These compression formats were developed in the early 1980s for CP/M systems:
|
|
387
|
+
|
|
388
|
+
- **1981**: Squeeze (SQ/USQ) by Richard Greenlaw - first widely-used CP/M compression
|
|
389
|
+
- **1984**: LBR format by Gary P. Novosielski - library/archive format
|
|
390
|
+
- **1985**: ARC by System Enhancement Associates - compressed archives
|
|
391
|
+
- **1985**: Crunch - LZW compression, more efficient than squeeze
|
|
392
|
+
- **1986**: Crunch v2.0 - improved with "metastatic code reassignment"
|
|
393
|
+
- **Late 1980s**: CrLZH - LZH compression, most efficient
|
|
394
|
+
|
|
395
|
+
## License
|
|
396
|
+
|
|
397
|
+
GPL v3 License
|
|
398
|
+
|
|
399
|
+
## Contributing
|
|
400
|
+
|
|
401
|
+
Bug reports and pull requests welcome at https://github.com/avwohl/80un
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## CP/M Version (80un.com)
|
|
406
|
+
|
|
407
|
+
A native CP/M program written in PL/M-80 that runs on real vintage hardware or emulators.
|
|
408
|
+
|
|
409
|
+
### Getting 80un.com
|
|
410
|
+
|
|
411
|
+
Download `80un.com` directly from this repository, or build from source (see below).
|
|
412
|
+
|
|
413
|
+
Transfer to your CP/M system via:
|
|
414
|
+
- XMODEM/YMODEM from a terminal program
|
|
415
|
+
- Write to a disk image and mount it
|
|
416
|
+
- Your emulator's file import feature
|
|
417
|
+
|
|
418
|
+
### Usage on CP/M
|
|
419
|
+
|
|
420
|
+
Extract an LBR archive:
|
|
421
|
+
```
|
|
422
|
+
A>80UN MYLIB.LBR
|
|
423
|
+
|
|
424
|
+
80UN - CP/M Archive Unpacker v2.1
|
|
425
|
+
|
|
426
|
+
Extracting:
|
|
427
|
+
README.TXT OK
|
|
428
|
+
PROGRAM.COM OK
|
|
429
|
+
SOURCE.ASM OK
|
|
430
|
+
|
|
431
|
+
3 file(s) extracted
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
Extract an ARC archive:
|
|
435
|
+
```
|
|
436
|
+
A>80UN SOFTWARE.ARC
|
|
437
|
+
|
|
438
|
+
80UN - CP/M Archive Unpacker v2.1
|
|
439
|
+
|
|
440
|
+
Extracting:
|
|
441
|
+
INSTALL.DOC OK
|
|
442
|
+
PROG.COM OK
|
|
443
|
+
CONFIG.DAT OK
|
|
444
|
+
|
|
445
|
+
3 file(s) extracted
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
Decompress a squeezed file:
|
|
449
|
+
```
|
|
450
|
+
A>80UN MANUAL.TQT
|
|
451
|
+
|
|
452
|
+
80UN - CP/M Archive Unpacker v2.1
|
|
453
|
+
|
|
454
|
+
Extracting:
|
|
455
|
+
Creating: MANUAL.TXT OK
|
|
456
|
+
|
|
457
|
+
1 file(s) extracted
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
Decompress a crunched file:
|
|
461
|
+
```
|
|
462
|
+
A>80UN SOURCE.AZM
|
|
463
|
+
|
|
464
|
+
80UN - CP/M Archive Unpacker v2.1
|
|
465
|
+
|
|
466
|
+
Extracting:
|
|
467
|
+
Creating: SOURCE.ASM OK
|
|
468
|
+
|
|
469
|
+
1 file(s) extracted
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### Notes
|
|
473
|
+
|
|
474
|
+
- Files extract to current drive/user area
|
|
475
|
+
- Existing files are overwritten without warning
|
|
476
|
+
- Original filenames are restored from compressed file headers
|
|
477
|
+
- Nested compression is handled (e.g., crunched files inside LBR)
|
|
478
|
+
|
|
479
|
+
### Building from Source
|
|
480
|
+
|
|
481
|
+
Requires the [uplm80](https://github.com/avwohl/uplm80) toolchain:
|
|
482
|
+
|
|
483
|
+
```bash
|
|
484
|
+
make # Build 80un.com
|
|
485
|
+
make test # Test with sample archives
|
|
486
|
+
make clean # Remove build artifacts
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### Source Files
|
|
490
|
+
|
|
491
|
+
PL/M-80 source is in `src/plm/`:
|
|
492
|
+
|
|
493
|
+
| File | Purpose |
|
|
494
|
+
|------|---------|
|
|
495
|
+
| `startup.plm` | Entry point |
|
|
496
|
+
| `common.plm` | BDOS interface, memory ops |
|
|
497
|
+
| `io.plm` | Buffered I/O, bit readers |
|
|
498
|
+
| `squeeze.plm` | Huffman decompressor |
|
|
499
|
+
| `crunch.plm` | LZW decompressor |
|
|
500
|
+
| `lzh.plm` | LZSS decompressor |
|
|
501
|
+
| `arc.plm` | ARC archive extractor |
|
|
502
|
+
| `lbr.plm` | LBR archive extractor |
|
|
503
|
+
| `main.plm` | Main program logic |
|
|
504
|
+
| `heap.asm` | Heap allocation bridge |
|
|
505
|
+
|
|
506
|
+
### Requirements
|
|
507
|
+
|
|
508
|
+
- CP/M 2.2 or compatible (MP/M, ZCPR, etc.)
|
|
509
|
+
- ~40KB TPA (Transient Program Area)
|
|
510
|
+
- Z80 processor
|
|
511
|
+
|
|
512
|
+
---
|
|
513
|
+
|
|
514
|
+
## See Also
|
|
515
|
+
|
|
516
|
+
- [CP/M information archive](https://www.seasip.info/Cpm/) - CP/M documentation
|
|
517
|
+
- [Walnut Creek CP/M CD-ROM](http://www.classiccmp.org/cpmarchives/) - Large CP/M software archive
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
80un-0.2.0.dist-info/licenses/LICENSE,sha256=sBYYnbXG62D6ry-endlqms9Htu8Iep8ToRSN0T77-rk,34939
|
|
2
|
+
un80/__init__.py,sha256=GhAjE0fmaFMph5cY5YfOPes6wRg2aX0bGMUaLM1h0W8,629
|
|
3
|
+
un80/arc.py,sha256=HxPSBTzrpUSGaCSCE-AgiabZMiLoAL-xws_CHw3E8a8,14814
|
|
4
|
+
un80/cli.py,sha256=sJIKrTqcWjoXToLnMJ8UJf4IKsWzA5pwtHj-nh52mwM,10491
|
|
5
|
+
un80/cpm.py,sha256=JhjujcFT6ucF9BLnxpS83IYOUfBDkzGUxPILcyz0co8,5080
|
|
6
|
+
un80/crlzh.py,sha256=eMrgPRb6Pl5IFNKfbbIpHa-xqryEJ4s9wAyG8WcT2YU,5746
|
|
7
|
+
un80/crunch.py,sha256=y-BnpJGsSWl2i7Eh4599iBDp43upAc9RuMaUnkZHV5I,7699
|
|
8
|
+
un80/lbr.py,sha256=PCaO-GPbUowYj9_psW9Nh9XRJANjeDx5Y-_lOK-mgCI,8084
|
|
9
|
+
un80/squeeze.py,sha256=nnbCV7Hhzcfwa1dE_mbQZrwumrfgJj9GAC1mD5n9isE,6533
|
|
10
|
+
80un-0.2.0.dist-info/METADATA,sha256=caOexXadgDhe1ahEs035L7AlE-IERqYSme3ytoRZCbk,13558
|
|
11
|
+
80un-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
+
80un-0.2.0.dist-info/entry_points.txt,sha256=BQwGYo9FS8ffH86XZXwMJUf3lN2nIarrIBK8r9ESq9w,39
|
|
13
|
+
80un-0.2.0.dist-info/top_level.txt,sha256=KlBvz54ZY7H_TT27VVi6_ux_i0vPNd4yNBkvOyodQ64,5
|
|
14
|
+
80un-0.2.0.dist-info/RECORD,,
|