├── .gitignore ├── ROMDISK └── s │ └── startup-sequence ├── src ├── boot.h ├── worker.h ├── unpacker │ ├── rnc.s │ ├── lz4.s │ ├── rnc_1.s │ └── inflate.s ├── debug.h ├── end.s ├── compiler.h ├── unpack.h ├── debug.c ├── diag.s ├── unpack.c ├── mydev.h ├── disk.h ├── Makefile ├── mydev.c ├── boot.c ├── worker.c ├── device.c └── disk.c ├── version.mk ├── doc ├── benchmarks.md ├── mkromdisk.md └── examples.md ├── Makefile ├── README.md └── mkromdisk /.gitignore: -------------------------------------------------------------------------------- 1 | BUILD 2 | 3 | -------------------------------------------------------------------------------- /ROMDISK/s/startup-sequence: -------------------------------------------------------------------------------- 1 | echo "Hello, romboot!" 2 | 3 | -------------------------------------------------------------------------------- /src/boot.h: -------------------------------------------------------------------------------- 1 | extern BOOL boot_init(struct DevBase *base); 2 | -------------------------------------------------------------------------------- /version.mk: -------------------------------------------------------------------------------- 1 | PROJECT_MAJOR=0 2 | PROJECT_MINOR=1 3 | PROJECT_DATE=21.07.2016 4 | -------------------------------------------------------------------------------- /src/worker.h: -------------------------------------------------------------------------------- 1 | extern BOOL worker_start(struct DevBase *db); 2 | extern void worker_stop(struct DevBase *db); 3 | -------------------------------------------------------------------------------- /src/unpacker/rnc.s: -------------------------------------------------------------------------------- 1 | xdef _unpack_rnc1 2 | _unpack_rnc1: 3 | moveq #0,d0 ; set no key 4 | include "rnc_1.s" 5 | 6 | -------------------------------------------------------------------------------- /src/debug.h: -------------------------------------------------------------------------------- 1 | #ifdef DEBUG 2 | extern void KPrintF(char *, ...); 3 | #define D(x) KPrintF x ; 4 | #else 5 | #define D(x) 6 | #endif 7 | -------------------------------------------------------------------------------- /src/end.s: -------------------------------------------------------------------------------- 1 | ; hack for amiga.lib's reference to _SysBase 2 | xdef _SysBase 3 | _SysBase equ 4 4 | 5 | ; theEnd marks the end of this device and the begin of the disk image 6 | cnop 0,4 7 | xdef _theEnd 8 | _theEnd: 9 | dc.l $deadbeef 10 | -------------------------------------------------------------------------------- /src/compiler.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPILER_H 2 | #define COMPILER_H 3 | 4 | /* compiler specific switches */ 5 | #ifdef __VBCC__ 6 | #define REG(r,t) __reg( #r ) t 7 | #define SAVEDS __saveds 8 | #define ASM 9 | #else 10 | #error unsupported compiler 11 | #endif /* VBCC */ 12 | 13 | #endif /* COMPILER_H */ 14 | -------------------------------------------------------------------------------- /src/unpack.h: -------------------------------------------------------------------------------- 1 | extern UBYTE *unpack_nop(UBYTE *packed_data, UBYTE *out_buffer, ULONG out_size); 2 | extern UBYTE *unpack_rnc(UBYTE *packed_data, UBYTE *out_buffer, ULONG out_size); 3 | extern UBYTE *unpack_dflt(UBYTE *packed_data, UBYTE *out_buffer, ULONG out_size); 4 | extern UBYTE *unpack_lz4(UBYTE *packed_data, UBYTE *out_buffer, ULONG out_size); 5 | -------------------------------------------------------------------------------- /src/debug.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "compiler.h" 5 | 6 | /* VBCC */ 7 | void __KPutCh(__reg("a6") void *, __reg("d0") UBYTE ch) = "\tjsr\t-516(a6)"; 8 | #define KPutCh(ch) __KPutCh(SysBase, ch) 9 | 10 | #define SIZE 80 11 | 12 | struct buffer { 13 | UBYTE buf[SIZE]; 14 | int pos; 15 | }; 16 | 17 | #define SysBase (*(struct Library **)4) 18 | 19 | ASM static void my_putch(REG(d0, char ch), 20 | REG(a3, struct buffer *buf)) 21 | { 22 | if(buf->pos < SIZE) { 23 | buf->buf[buf->pos] = ch; 24 | buf->pos++; 25 | } 26 | } 27 | 28 | void KPrintF(char *fmt, ...) 29 | { 30 | struct buffer buf; 31 | int i; 32 | va_list ap; 33 | 34 | buf.pos = 0; 35 | va_start(ap, fmt); 36 | RawDoFmt(fmt, ap, my_putch, &buf); 37 | va_end(ap); 38 | 39 | for(i=0;i 2 | 3 | #include "compiler.h" 4 | #include "debug.h" 5 | 6 | UBYTE * unpack_nop(UBYTE *packed_data, UBYTE *out_buffer, ULONG out_size) 7 | { 8 | /* packed data is not packed at all */ 9 | return packed_data; 10 | } 11 | 12 | /* asm function */ 13 | extern ASM ULONG unpack_rnc1(REG(a0, UBYTE *packed_data), 14 | REG(a1, UBYTE *out_data)); 15 | 16 | UBYTE * unpack_rnc(UBYTE *packed_data, UBYTE *out_buffer, ULONG out_size) 17 | { 18 | ULONG *tag = (ULONG *)packed_data; 19 | /* is really packed with RNC1 */ 20 | if(*tag == 0x524e4301) { 21 | unpack_rnc1(packed_data, out_buffer); 22 | return out_buffer; 23 | } 24 | /* raw track */ 25 | else { 26 | return packed_data; 27 | } 28 | } 29 | 30 | extern ASM ULONG inflate(REG(a5, UBYTE *packed_data), 31 | REG(a4, UBYTE *out_data)); 32 | 33 | UBYTE * unpack_dflt(UBYTE *packed_data, UBYTE *out_buffer, ULONG out_size) 34 | { 35 | inflate(packed_data, out_buffer); 36 | return out_buffer; 37 | } 38 | 39 | 40 | extern ASM void lz4_unpack(REG(a0, UBYTE *packed_data), 41 | REG(a1, UBYTE *out_data)); 42 | 43 | UBYTE * unpack_lz4(UBYTE *packed_data, UBYTE *out_buffer, ULONG out_size) 44 | { 45 | lz4_unpack(packed_data, out_buffer); 46 | return out_buffer; 47 | } 48 | -------------------------------------------------------------------------------- /src/mydev.h: -------------------------------------------------------------------------------- 1 | struct DiskHeader; 2 | struct PackHeader; 3 | struct BufferMap; 4 | struct DevBase; 5 | 6 | typedef void (*read_func_t)(struct IOStdReq *ior, struct DevBase *base); 7 | typedef UBYTE * (*unpack_func_t)(UBYTE *packed_data, UBYTE *out_buffer, ULONG out_size); 8 | 9 | struct DevBase 10 | { 11 | /* common */ 12 | struct Library libBase; 13 | struct Library *sysBase; 14 | ULONG segList; 15 | /* worker */ 16 | struct MsgPort *workerPort; 17 | /* romdisk */ 18 | struct DiskHeader *diskHeader; 19 | UBYTE *diskData; 20 | read_func_t readFunc; 21 | /* pack only */ 22 | struct PackHeader *packHeader; 23 | unpack_func_t unpackFunc; 24 | /* open state */ 25 | UBYTE *unpackBuffer; 26 | UBYTE *curBuffer; /* unpacked data or NULL if empty buffer */ 27 | ULONG curPackId; 28 | }; 29 | 30 | #ifndef NO_SYSBASE 31 | #define SysBase base->sysBase 32 | #endif 33 | 34 | extern struct DevBase *mydev_init(struct DevBase *base); 35 | extern void mydev_expunge(struct DevBase *base); 36 | extern struct DevBase * mydev_open(struct IOStdReq *ior, ULONG unit, ULONG flags, struct DevBase *base); 37 | extern void mydev_close(struct IOStdReq *ior, struct DevBase *base); 38 | extern void mydev_begin_io(struct IOStdReq *ior, struct DevBase *base); 39 | extern LONG mydev_abort_io(struct IOStdReq *ior, struct DevBase *base); 40 | 41 | extern BOOL mydev_worker_init(struct DevBase *base); 42 | extern void mydev_worker_cmd(struct DevBase *base, struct IOStdReq *ior); 43 | extern void mydev_worker_exit(struct DevBase *base); 44 | 45 | -------------------------------------------------------------------------------- /src/disk.h: -------------------------------------------------------------------------------- 1 | 2 | /* disk header created by mkromdisk and searched in ROM */ 3 | struct DiskHeader { 4 | ULONG tag; /* RODI tag */ 5 | UWORD version; /* version of disk header */ 6 | UWORD format; /* format of disk image */ 7 | UBYTE name[4]; /* dos name (3 chars + zero!) */ 8 | /* layout of disk */ 9 | ULONG cylinders; 10 | ULONG heads; 11 | ULONG sectors; 12 | ULONG boot_prio; 13 | ULONG dos_type; 14 | /* params */ 15 | ULONG num_buffers; /* number of RAM buffers for caching */ 16 | ULONG disk_size; /* total bytes in disk */ 17 | }; 18 | 19 | /* placed in ROM after DiskHeader if format is packed */ 20 | struct PackHeader { 21 | ULONG tag; /* PACK tag */ 22 | ULONG packer; /* tag for packer */ 23 | ULONG num_packs; /* number of packs (per track/per cylinder) */ 24 | ULONG pack_size; /* size of each pack in bytes */ 25 | ULONG offsets[1]; /* num_packs offsets following */ 26 | }; 27 | 28 | #define ROMDISK_TAG 0x524f4449 /* RODI */ 29 | #define ROMDISK_VERSION 1 30 | 31 | #define ROMDISK_FORMAT_RAW 0 32 | #define ROMDISK_FORMAT_PACK 1 33 | 34 | #define ROMDISK_PACK_TAG 0x5041434b /* PACK */ 35 | #define ROMDISK_PACK_RNC 0x524e4300 /* RNC */ 36 | #define ROMDISK_PACK_NOP 0x4e4f5000 /* NOP */ 37 | #define ROMDISK_PACK_DFLT 0x44464c54 /* DFLT */ 38 | #define ROMDISK_PACK_LZ4 0x4c5a3400 39 | 40 | extern BOOL disk_setup(struct DevBase *base); 41 | extern BOOL disk_open(struct DevBase *base); 42 | extern void disk_close(struct DevBase *base); 43 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | FORMAT?=raw 2 | FLAVOR?=_dbg 3 | BUILD_DIR=BUILD 4 | ROM_NAME=$(BUILD_DIR)/ext_$(FORMAT)$(FLAVOR).rom 5 | DISK_NAME=$(BUILD_DIR)/disk_$(FORMAT).rodi 6 | 7 | DEV_NAME=src/BUILD/romdisk.device$(FLAVOR) 8 | 9 | FLAVORS=_rel _dbg 10 | FORMATS=raw nop rnc dflt lz4 11 | 12 | include version.mk 13 | 14 | DIST_NAME=romdisk_$(PROJECT_MAJOR).$(PROJECT_MINOR) 15 | 16 | all: $(ROM_NAME) 17 | 18 | force: 19 | rm -f $(DEV_NAME) 20 | $(MAKE) 21 | 22 | formats: 23 | @for f in $(FORMATS) ; do \ 24 | $(MAKE) FORMAT=$$f || exit 1 ; \ 25 | done 26 | @echo "--- formats $(FORMATS) ---" 27 | 28 | flavors: 29 | @for f in $(FLAVORS) ; do \ 30 | $(MAKE) FLAVOR=$$f formats || exit 1 ; \ 31 | done 32 | @echo "--- flavors $(FLAVORS) ---" 33 | 34 | $(BUILD_DIR): 35 | mkdir -p $(BUILD_DIR) 36 | 37 | $(DEV_NAME): 38 | $(MAKE) -C src FLAVOR=$(FLAVOR) 39 | 40 | $(DISK_NAME): $(BUILD_DIR) ROMDISK 41 | ./mkromdisk $@ -d ROMDISK -p 15 -f $(FORMAT) 42 | 43 | $(ROM_NAME): $(DEV_NAME) $(DISK_NAME) 44 | romtool -v build -o $@ -t ext $^ 45 | 46 | clean_all: clean clean_dist 47 | $(MAKE) -C src clean 48 | rm -rf DIST 49 | 50 | clean: 51 | rm -rf BUILD 52 | 53 | dist_dirs: 54 | @mkdir -p $(DIST_NAME)/devs $(DIST_NAME)/roms $(DIST_NAME)/doc 55 | 56 | dist_flavors: 57 | @for f in $(FLAVORS) ; do \ 58 | $(MAKE) FLAVOR=$$f dist_formats || exit 1 ; \ 59 | done 60 | 61 | dist_formats: 62 | @for f in $(FORMATS) ; do \ 63 | $(MAKE) FORMAT=$$f dist_local || exit 1 ; \ 64 | done 65 | 66 | dist_local: $(ROM_NAME) 67 | cp $(ROM_NAME) $(DIST_NAME)/roms 68 | 69 | dist: dist_dirs dist_flavors 70 | @$(MAKE) -C src dist DIST_DIR=../$(DIST_NAME) 71 | @cp README.md $(DIST_NAME)/ 72 | @cp mkromdisk $(DIST_NAME)/ 73 | @cp doc/*.md $(DIST_NAME)/doc/ 74 | @echo "--- dist: $(DIST_NAME) ---" 75 | @ls -laR $(DIST_NAME) 76 | 77 | dist_zip: dist 78 | @zip -r -9 $(DIST_NAME).zip $(DIST_NAME) 79 | 80 | clean_dist: 81 | rm -rf $(DIST_NAME) 82 | rm -f $(DIST_NAME).zip 83 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile to cross-compile romdisk.device 2 | 3 | FLAVOR?=_dbg 4 | BUILD_DIR=BUILD 5 | OBJ_DIR=$(BUILD_DIR)/obj$(FLAVOR) 6 | 7 | # NDK directory 8 | NDK_DIR?=$(HOME)/projects/amidev/ndk_3.9 9 | 10 | # what flavors to build 11 | FLAVORS=_rel _dbg _rel_td _dbg_td 12 | 13 | # parse flavors 14 | ifeq "_dbg" "$(findstring _dbg,$(FLAVOR))" 15 | DEBUG=1 16 | DEFINES+=DEBUG 17 | else 18 | OPTFLAGS=-O2 19 | endif 20 | 21 | ifeq "_td" "$(findstring _td,$(FLAVOR))" 22 | FAKE_TD=1 23 | DEFINES+=FAKE_TD 24 | DEV_NAME=trackdisk.device 25 | else 26 | AUTOBOOT=1 27 | DEFINES+=AUTOBOOT 28 | DEV_NAME=romdisk.device 29 | endif 30 | 31 | # setup defines 32 | CDEFINES=$(patsubst %,-D%=1,$(DEFINES)) 33 | include ../version.mk 34 | QUOTE=\\\" 35 | CDEFINES+=-DMYDEV_VERSION=$(PROJECT_MAJOR) 36 | CDEFINES+=-DMYDEV_REVISION=$(PROJECT_MINOR) 37 | CDEFINES+=-DMYDEV_VERSION_STR=$(QUOTE)$(PROJECT_MAJOR).$(PROJECT_MINOR)$(QUOTE) 38 | CDEFINES+=-DMYDEV_DATE=$(QUOTE)$(PROJECT_DATE)$(QUOTE) 39 | CDEFINES+=-DMYDEV_NAME=$(QUOTE)$(DEV_NAME)$(QUOTE) 40 | CDEFINES+=-DMYDEV_WORKER=$(QUOTE)romdisk$(QUOTE) 41 | 42 | # cross compiler/assembler setup 43 | CC=vc +aos68k 44 | # make sure to prepend VBCC includes before NDK so VBCC's 'proto' is used 45 | CFLAGS=-c99 -g -sc -I$(VBCC)/targets/m68k-amigaos/include/ 46 | CFLAGS+=-I$(NDK_DIR)/include/include_h $(CDEFINES) $(OPTFLAGS) 47 | LD=vc +aos68k 48 | LDFLAGS=-g -sc -nostdlib -lamiga -lvcs 49 | AS=vasmm68k_mot 50 | ASFLAGS=-quiet -Fhunk -I$(NDK_DIR)/include/include_i -m68000 51 | 52 | SRCS=device.c disk.c mydev.c worker.c unpack.c 53 | ASRCS=rnc.s inflate.s lz4.s 54 | HDRS=boot.h disk.h debug.h mydev.h worker.h unpack.h 55 | 56 | # fetch sources from subdirs, too 57 | VPATH=unpacker 58 | 59 | # parse flags 60 | ifeq "$(DEBUG)" "1" 61 | SRCS+=debug.c 62 | endif 63 | ifeq "$(AUTOBOOT)" "1" 64 | SRCS+=boot.c 65 | ASRCS+=diag.s 66 | endif 67 | 68 | # end must be last 69 | ASRCS+=end.s 70 | 71 | OBJS=$(patsubst %.c,$(OBJ_DIR)/%.o,$(SRCS)) 72 | OBJS+=$(patsubst %.s,$(OBJ_DIR)/%.o,$(ASRCS)) 73 | 74 | DEVICE=$(BUILD_DIR)/$(DEV_NAME)$(FLAVOR) 75 | 76 | # ----- rules ----- 77 | 78 | all: $(BUILD_DIR) $(OBJ_DIR) $(DEVICE) 79 | 80 | flavors: 81 | @for f in $(FLAVORS) ; do \ 82 | $(MAKE) FLAVOR=$$f || exit 1 ; \ 83 | done 84 | @echo "---- done flavors: $(FLAVORS) -----" 85 | 86 | $(DEVICE): $(OBJS) 87 | $(LD) $^ -o $@ $(LDFLAGS) 88 | 89 | $(BUILD_DIR): 90 | mkdir -p $(BUILD_DIR) 91 | 92 | $(OBJ_DIR): 93 | mkdir -p $(OBJ_DIR) 94 | 95 | $(OBJS): $(SRCS) $(HDRS) $(ASRCS) 96 | 97 | $(OBJ_DIR)/%.o: %.c 98 | $(CC) $(CFLAGS) -c -o $@ $< 99 | 100 | $(OBJ_DIR)/%.o: %.s 101 | $(AS) $(ASFLAGS) -o $@ $< 102 | 103 | clean: 104 | rm -rf $(BUILD_DIR) 105 | 106 | dist: 107 | @for f in $(FLAVORS) ; do \ 108 | $(MAKE) dist_flavor FLAVOR=$$f || exit 1 ; \ 109 | done 110 | 111 | dist_flavor: all 112 | cp $(DEVICE) $(DIST_DIR)/devs 113 | -------------------------------------------------------------------------------- /src/mydev.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "compiler.h" 9 | #include "debug.h" 10 | #include "mydev.h" 11 | #include "boot.h" 12 | #include "disk.h" 13 | #include "worker.h" 14 | 15 | struct DevBase *mydev_init(struct DevBase *base) 16 | { 17 | /* try to find disk in ROM */ 18 | if(!disk_setup(base)) { 19 | return NULL; 20 | } 21 | 22 | #ifdef AUTOBOOT 23 | boot_init(base); 24 | #endif 25 | 26 | return base; 27 | } 28 | 29 | void mydev_expunge(struct DevBase *base) 30 | { 31 | 32 | } 33 | 34 | struct DevBase * mydev_open(struct IOStdReq *ior, ULONG unit, ULONG flags, struct DevBase *base) 35 | { 36 | /* only unit 0 allowed */ 37 | if(unit != 0) { 38 | D(("only unit 0 allowed!\n")); 39 | return NULL; 40 | } 41 | 42 | if(base->libBase.lib_OpenCnt == 1) { 43 | D(("disk_open\n")); 44 | if(!disk_open(base)) { 45 | D(("disk_open failed!\n")); 46 | return NULL; 47 | } 48 | 49 | /* start worker process */ 50 | if(!worker_start(base)) { 51 | return NULL; 52 | } 53 | } 54 | 55 | return base; 56 | } 57 | 58 | void mydev_close(struct IOStdReq *ior, struct DevBase *base) 59 | { 60 | if(base->libBase.lib_OpenCnt == 1) { 61 | D(("disk_close\n")); 62 | disk_close(base); 63 | 64 | /* stop worker process */ 65 | worker_stop(base); 66 | } 67 | } 68 | 69 | void mydev_begin_io(struct IOStdReq *ior, struct DevBase *base) 70 | { 71 | /* mark message as active */ 72 | ior->io_Message.mn_Node.ln_Type = NT_MESSAGE; 73 | 74 | /* assume no error */ 75 | ior->io_Error = 0; 76 | 77 | UWORD cmd = ior->io_Command; 78 | switch(cmd) { 79 | case CMD_READ: 80 | /* clear quick */ 81 | ior->io_Flags &= ~IOF_QUICK; 82 | /* forward to worker */ 83 | PutMsg(base->workerPort, &ior->io_Message); 84 | return; 85 | case TD_CHANGENUM: 86 | D(("NUM\n")); 87 | ior->io_Actual = 0; 88 | break; 89 | case TD_CHANGESTATE: 90 | D(("STATE\n")); 91 | ior->io_Actual = 0; 92 | break; 93 | case TD_PROTSTATUS: 94 | D(("WPROT\n")); 95 | ior->io_Actual = 1; /* is write protected */ 96 | break; 97 | /* ignore the following commands */ 98 | case CMD_UPDATE: 99 | case CMD_CLEAR: 100 | case TD_MOTOR: 101 | case TD_SEEK: 102 | case TD_REMOVE: 103 | D(("NOP\n")); 104 | break; 105 | /* report invalid write */ 106 | case CMD_WRITE: 107 | case TD_FORMAT: 108 | D(("Write!?\n")); 109 | ior->io_Error = TDERR_WriteProt; 110 | break; 111 | /* report invalid command */ 112 | default: 113 | D(("??\n")); 114 | ior->io_Error = IOERR_NOCMD; 115 | break; 116 | } 117 | 118 | /* reply message */ 119 | if (!(ior->io_Flags & IOF_QUICK)) { 120 | ReplyMsg(&ior->io_Message); 121 | } else { 122 | /* mark as done */ 123 | ior->io_Message.mn_Node.ln_Type = NT_REPLYMSG; 124 | } 125 | } 126 | 127 | LONG mydev_abort_io(struct IOStdReq *ior, struct DevBase *base) 128 | { 129 | return 1; 130 | } 131 | 132 | BOOL mydev_worker_init(struct DevBase *base) 133 | { 134 | return TRUE; 135 | } 136 | 137 | void mydev_worker_cmd(struct DevBase *base, struct IOStdReq *ior) 138 | { 139 | UWORD cmd = ior->io_Command; 140 | switch(cmd) { 141 | case CMD_READ: 142 | D(("READ: off=%08lx len=%08lx buf=%08lx\n", 143 | ior->io_Offset, ior->io_Length, ior->io_Data)); 144 | base->readFunc(ior, base); 145 | break; 146 | default: 147 | D(("?? cmd=%08lx\n", (ULONG)cmd)); 148 | break; 149 | } 150 | } 151 | 152 | void mydev_worker_exit(struct DevBase *base) 153 | { 154 | } 155 | -------------------------------------------------------------------------------- /doc/mkromdisk.md: -------------------------------------------------------------------------------- 1 | # The mkromdisk Manual 2 | 3 | ## Introduction 4 | 5 | The mkromdisk is the host-based tool of the romdisk project that allows you 6 | to create disk images that are suitable to be placed into a ROM and used 7 | as a romdisk with the romdisk.device of this project. 8 | 9 | You can either create a romdisk image directly **from a directory tree** (in this 10 | case amitool's xdftool is used to create a Amiga OFS/FFS image first) or you 11 | supply a **pre-mastered disk image** in a .adf or .hdf file. 12 | 13 | Both floppy disk images (usually stored in .adf files) and hard disk files of 14 | arbitrary disk geometry (stored in .hdf files) are supported. Since ROMs have 15 | a size of 512 KiB floppy disk images can only be stored in a compressed 16 | format. Hard disk images with a very small geometry (e.g. 40 cylinders, 2 17 | heads, 11 sectors = half a disk = 440 KiB) also fit in a ROM uncompressed. 18 | 19 | The mkromdisk tool lets you decide if and what type of compression you want 20 | to apply. Furthermore, you can select parameters of your drive, like boot 21 | priority or number of file system buffers used. 22 | 23 | Depending on the type of compression you selected, you will need to install 24 | an external compression tool on your command line first. 25 | 26 | The tool is written in **Python 2.7** (not 3.x!), so make sure you have it 27 | installed on your machine. 28 | 29 | ## Typical Usages 30 | 31 | ### From Directory 32 | 33 | * Create a romdisk directly from a directory tree (Assuming `ROMDISK` is a 34 | directory that contains all the files and directories for your romdisk): 35 | 36 | ``` 37 | mkromdisk -d ROMDISK disk.rodi 38 | ``` 39 | 40 | * Note: The romdisk disks use a `.rodi` suffix and the tool always requires 41 | the name of a output disk image as argument. 42 | 43 | ### From Disk Image 44 | 45 | * Create a romdisk from a pre-mastered disk image: 46 | 47 | ``` 48 | mkromdisk -i wb31.adf -f dflt disk.rodi 49 | ``` 50 | 51 | * Note: since a floppy disk image does not fit uncompressed, we enable 52 | compression with the -f option 53 | 54 | ## Options 55 | 56 | The tool allows you to specify lots of additional options to alter its 57 | behavior. 58 | 59 | * `-h` shows you detailed help on all switches 60 | * `-d ` create a romdisk image from a given directory. 61 | Make sure to install `xdftool` command from amitools, first. 62 | * `-i ` create a romdisk image from a given disk image. 63 | * `-t ` during image creation some temporary files are created. This 64 | option sets the name prefix used for all files. Default is `temp`. 65 | * `-g ,,` specify a disk image geometry. Specify cylinders, 66 | heads, and sectors as numbers, e.g. `-g 80,2,11` specifies a floppy disk 67 | geometry. Use `-g adf` as a short cut for floppies. 68 | * `-D ` when creating an image from a directory (`-i`), you can specify 69 | the created file system with this option: Use `ofs` for old and `ffs` for 70 | fast file system images. Default is `ffs`. 71 | * `-p ` set the boot priority of the romdisk. Default is `5`. 72 | * `-b ` set the number of buffers that the filesys uses. Default is `5`. 73 | * `-f ` sets the compression format (Default is `raw`): 74 | * `raw`: no compression applied at all. disk is stored unmodified. even empty 75 | tracks are kept. 76 | * `nop`: no compression, but omit empty (zero) tracks. 77 | * `dflt`: compress with zlib's deflate compressor (level 9). no external 78 | tool is required. 79 | * `rnc`: compress with Rob Northern Computing's ProPack. You need to install 80 | `vamos` from amitools first and supply the packer binary `ppami.exe` in 81 | current dir. 82 | * `lz4`: compress with lz4 algorithm. You need to install the `lz4` tool 83 | first. 84 | * `-v` be more verbose 85 | * `-e ` select the compression granularity: either pack whole `tracks` 86 | or `cyls` (= track * heads). Cylinder packing needs to decompress more for 87 | a single block but allows you to usually get a better compression ratio. 88 | Default is `tracks`. 89 | -------------------------------------------------------------------------------- /src/boot.c: -------------------------------------------------------------------------------- 1 | #define __NOLIBBASE__ 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "compiler.h" 10 | #include "debug.h" 11 | #include "mydev.h" 12 | #include "disk.h" 13 | 14 | static const char execName[] = "romdisk.device"; 15 | 16 | extern struct DiagArea myDiagArea; 17 | 18 | static ULONG *create_param_pkt(struct DevBase *base, ULONG *size) 19 | { 20 | *size = 21 * 4; 21 | ULONG *paramPkt = (ULONG *)AllocMem(*size, 0); 22 | if(paramPkt == NULL) { 23 | return NULL; 24 | } 25 | 26 | struct DiskHeader *hdr = base->diskHeader; 27 | 28 | paramPkt[0] = (ULONG)hdr->name; 29 | paramPkt[1] = (ULONG)execName; 30 | paramPkt[2] = 0; /* unit */ 31 | paramPkt[3] = 0; /* open flags */ 32 | /* env block */ 33 | paramPkt[4] = 16; /* size of table */ 34 | paramPkt[5] = 512>>2; /* 0 # longwords in a block */ 35 | paramPkt[6] = 0; /* 1 sector origin -- unused */ 36 | paramPkt[7] = hdr->heads; /* 2 number of surfaces */ 37 | paramPkt[8] = 1; /* 3 secs per logical block -- leave as 1 */ 38 | paramPkt[9] = hdr->sectors; /* 4 blocks per track */ 39 | paramPkt[10] = 2; /* 5 reserved blocks -- 2 boot blocks */ 40 | paramPkt[11] = 0; /* 6 ?? -- unused */ 41 | paramPkt[12] = 0; /* 7 interleave */ 42 | paramPkt[13] = 0; /* 8 lower cylinder */ 43 | paramPkt[14] = hdr->cylinders-1; /* 9 upper cylinder */ 44 | paramPkt[15] = hdr->num_buffers; /* a number of buffers */ 45 | paramPkt[16] = MEMF_PUBLIC; /* b type of memory for buffers */ 46 | paramPkt[17] = 0x7fffffff; /* c largest transfer size (largest signed #) */ 47 | paramPkt[18] = ~1; /* d addmask */ 48 | paramPkt[19] = hdr->boot_prio; /* e boot priority */ 49 | paramPkt[20] = hdr->dos_type; /* f dostype: 'DOS\0' */ 50 | 51 | return paramPkt; 52 | } 53 | 54 | BOOL boot_init(struct DevBase *base) 55 | { 56 | BOOL ok = FALSE; 57 | struct Library *ExpansionBase; 58 | 59 | ExpansionBase = (struct Library *)OpenLibrary("expansion.library", 36); 60 | if(ExpansionBase != NULL) { 61 | struct ConfigDev *cd = AllocConfigDev(); 62 | D(("got expansion. config dev=%08lx\n", cd)); 63 | if(cd != NULL) { 64 | 65 | /* get diag address */ 66 | ULONG diag_addr = (ULONG)&myDiagArea; 67 | ULONG diag_base = diag_addr & ~0xffff; 68 | ULONG diag_off = diag_addr & 0xffff; 69 | D(("diag_addr: base=%08lx offset=%04lx\n", diag_base, diag_off)); 70 | 71 | /* fill faked config dev */ 72 | cd->cd_Flags = 0; 73 | cd->cd_BoardAddr = (APTR)diag_base; 74 | cd->cd_BoardSize = 0x010000; 75 | cd->cd_Driver = (APTR)base; 76 | struct ExpansionRom *rom = &cd->cd_Rom; 77 | rom->er_Type = ERT_ZORROII | ERTF_DIAGVALID | 1; /* size=64 KiB */ 78 | rom->er_Flags = ERFF_NOSHUTUP; 79 | rom->er_Product = 42; 80 | rom->er_Manufacturer = 2011; /* hack id */ 81 | rom->er_SerialNumber = 1; 82 | rom->er_InitDiagVec = (UWORD)diag_off; 83 | 84 | /* fake copy of diag area. the pointer is stored in er_Reserved0c..0f */ 85 | ULONG *ptr = (ULONG *)&rom->er_Reserved0c; 86 | *ptr = diag_addr; 87 | 88 | AddConfigDev(cd); 89 | 90 | ULONG paramSize = 0; 91 | ULONG *paramPkt = create_param_pkt(base, ¶mSize); 92 | D(("got param pkt=%08lx\n", paramPkt)); 93 | if(paramPkt != NULL) { 94 | struct DeviceNode *dn = MakeDosNode(paramPkt); 95 | D(("got dos node=%08lx\n", dn)); 96 | if(dn != NULL) { 97 | /* now add boot node */ 98 | struct DiskHeader *hdr = base->diskHeader; 99 | BYTE boot_prio = (BYTE)hdr->boot_prio; 100 | 101 | ok = AddBootNode( boot_prio, ADNF_STARTPROC, dn, cd ); 102 | D(("add boot node=%d\n", ok)); 103 | } 104 | FreeMem(paramPkt, paramSize); 105 | } 106 | } 107 | CloseLibrary(ExpansionBase); 108 | } 109 | return ok; 110 | } 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Project romdisk - Boot your Amiga from ROM 2 | 3 | Written by Christian Vogelgsang under the GNU Public License V2 4 | 5 | This projects provides a romdisk.device you can place in an Amiga Kickstart 6 | or external ROM. It will autoboot a disk image that is stored next to the 7 | device directly in the ROM. You can either store a raw OFS /FFS disk image 8 | (ca. 500 KiB size) or a compressed image that holds up to a whole Amiga 9 | disk (ca. 880 KiB size). While the raw storage is ultra fast, the compressed 10 | images allow you to deploy whole disks into ROM. 11 | 12 | Additionally, a tool for modern systems is available that allows you to 13 | create those romdisk image easily from existing .adf disk images or from 14 | a directory of files. 15 | 16 | ## Why? 17 | 18 | Create boot ROMs for 19 | 20 | * Diagnosis 21 | * Network Boot 22 | * Quick Startup 23 | * Device-less Amiga Setups 24 | * stripped down Amiga OS ROMs that do not use any external devices 25 | during boot (simplifies emulation) 26 | 27 | and 28 | 29 | * use them in your favorite Amiga emulator 30 | * soft-kick'em in your accelerator card 31 | * flash or burn them to ROMs 32 | 33 | ## Features 34 | 35 | * Store raw or compressed disk images 36 | * Compressions supported: 37 | * Deflate Compression from ZLib 38 | * RNC Compressor (Rob Nothern Computing) 39 | * LZ4 Fast Compression 40 | * Autoboot on OS 2.x and 3.x 41 | * Configurable image geometry, boot priority 42 | * Python tool `mkromdisk` to create image file from disk images or directories 43 | * Amiga device with source that can be cross-compiled on modern systems 44 | * A drop-in replacement variant for `trackdisk.device` to have a very 45 | compatible DF0 device 46 | 47 | ## Prerequisites 48 | 49 | * Amiga OS 2.x or 3.x and real/emulated classic Amiga m68k system 50 | * Python 2.7 for host tool 51 | * [amitools][1] installed for `xdftool`, `romtool`, and `vamos` 52 | * `ppami.exe` Amiga tool from [RNC ProPack][2] to use RNC Compression 53 | * `lz4` tool from [lz4.org][4] to use LZ4 Compression 54 | * Cross compilers from [amigaos-cross-toolchain][3] to build device from sources 55 | 56 | [1]: https://github.com/cnvogelg/amitools 57 | [2]: http://aminet.net/package/util/pack/RNC_ProPack 58 | [3]: https://github.com/cahirwpz/amigaos-cross-toolchain 59 | [4]: https://lz4.org/ 60 | 61 | ## Tutorial 62 | 63 | Here is a quick wrap-up on how to create and run a ROM with a romdisk: 64 | 65 | * First create a Kickstart ROM that supports an external ROM with additional 66 | 512 KiB storage. We use amitool's `romtool` to convert a A1200 Cloanto OS 3.1 67 | to support 1 MiB ROMs: 68 | 69 | ``` 70 | romtool patch -o kick.rom miga-os-310-a1200.rom 1mb_rom 71 | ``` 72 | 73 | * Now run romdisk's `mkromdisk` tool to create a romdisk image from a disk 74 | image. We use the Workbench 3.1 disk from Cloanto's Amiga Forever distribution 75 | here (its called `amiga-os-310-workbench.adf`): 76 | 77 | ``` 78 | ./mkromdisk -i amiga-os-310-workbench.adf -f dflt wb31.rodi 79 | ``` 80 | 81 | * Finally, create an external ROM image with `romtool` that contains the 82 | `romdisk.device` (release version) and your romdisk image `wb31.rodi`: 83 | 84 | ``` 85 | romtool -v build -o ext.rom -t ext romdisk.device_rel wb31.rodi 86 | ``` 87 | 88 | * Voila! Your ROM set is ready to be run. You can either use the two files 89 | directly (e.g. in your emulator) or combine them to a single 1 MiB ROM file 90 | if your soft-kicker needs that: 91 | 92 | ``` 93 | romtool combine -o 1mb.rom kick.rom ext.rom 94 | ``` 95 | 96 | * Run the ROM set in the [FS-UAE][1] emulator with a configuration like this 97 | one (e.g. in a `test.fs-uae` file): 98 | 99 | ``` 100 | [config] 101 | amiga_model = A1200 102 | kickstart_file = kick.rom 103 | kickstart_ext_file = ext.rom 104 | ``` 105 | 106 | If you run the emulator it will boot the Workbench directly from ROM... 107 | 108 | [1]: https://fs-uae.net 109 | 110 | ## More Documents 111 | 112 | * [romdisk Examples](doc/examples.md) 113 | * [romdisk Benchmarks](doc/benchmarks.md) 114 | * [mkromdisk Manual](doc/mkromdisk.md) 115 | 116 | ## Thanks 117 | 118 | * Krystian Bacławski for the great [amigaos-cross-toolchain][1] 119 | * Keir Fraser for releasing the [deflate m68k source][2] 120 | * Frank Wille for [vasm][3] and porting the deflate source to vasm 121 | * Rob Northern Computing for releasing its [Pro Pack][4] including source 122 | 123 | [1]: https://github.com/cahirwpz/amigaos-cross-toolchain 124 | [2]: https://github.com/keirf/Amiga-Stuff/blob/master/inflate.asm 125 | [3]: http://sun.hasenbraten.de/vasm/ 126 | [4]: http://aminet.net/package/util/pack/RNC_ProPack 127 | -------------------------------------------------------------------------------- /src/unpacker/lz4.s: -------------------------------------------------------------------------------- 1 | * lz4 decompressor 2 | * from: http://www.atari-forum.com/viewtopic.php?t=26825 3 | * author: Anima 4 | * patched by cnvogelg to be rommable 5 | * assumes pre-processed lz4 streams without frame header 6 | 7 | * a0 = packed data 8 | * a1 = output data 9 | 10 | xref _lz4_unpack 11 | 12 | _lz4_unpack: 13 | movem.l d0-d4/a0-a4,-(sp) 14 | 15 | addq.l #7,a0 ; skip lz4 frame header 16 | moveq #0,d2 17 | move.w #$f,d4 18 | 19 | move.l sp,a2 ; temp storage for numbers 20 | 21 | next_block: 22 | ; read block size 23 | move.b (a0)+,-(a2) 24 | move.b (a0)+,-(a2) 25 | move.b (a0)+,-(a2) 26 | move.b (a0)+,-(a2) 27 | move.l (a2)+,d3 28 | ; neg is direct copy 29 | bmi copy_uncompressed_block 30 | ; null block is end 31 | beq end_of_compressed_data 32 | 33 | ; calc end address of compressed block -> a4 34 | lea (a0,d3.l),a4 35 | 36 | next_token: 37 | moveq #0,d0 38 | move.b (a0)+,d0 39 | 40 | ; literal length 41 | move.w d0,d1 42 | lsr.w #4,d1 43 | beq.s match_data_only 44 | 45 | ; 15? more length bytes follow 46 | cmp.w d4,d1 47 | beq.s additional_literal_length 48 | 49 | ; copy up to 14 direct 50 | subq.w #1,d1 51 | 52 | short_literal_copy_loop: 53 | move.b (a0)+,(a1)+ 54 | 55 | dbra d1,short_literal_copy_loop 56 | 57 | bra.s process_match_data 58 | 59 | additional_literal_length: 60 | move.b (a0)+,d2 61 | add.w d2,d1 62 | ; last is != 0xff 63 | not.b d2 64 | beq.s additional_literal_length 65 | 66 | ; d1=lit length 67 | move.w d1,d3 68 | lsr.w #4,d1 69 | and.w d4,d3 70 | add.w d3,d3 71 | neg.w d3 72 | jmp literal_copy_start(pc,d3.w) 73 | 74 | long_literal_copy_loop: 75 | move.b (a0)+,(a1)+ 76 | move.b (a0)+,(a1)+ 77 | move.b (a0)+,(a1)+ 78 | move.b (a0)+,(a1)+ 79 | move.b (a0)+,(a1)+ 80 | move.b (a0)+,(a1)+ 81 | move.b (a0)+,(a1)+ 82 | move.b (a0)+,(a1)+ 83 | move.b (a0)+,(a1)+ 84 | move.b (a0)+,(a1)+ 85 | move.b (a0)+,(a1)+ 86 | move.b (a0)+,(a1)+ 87 | move.b (a0)+,(a1)+ 88 | move.b (a0)+,(a1)+ 89 | move.b (a0)+,(a1)+ 90 | move.b (a0)+,(a1)+ 91 | literal_copy_start: 92 | dbra d1,long_literal_copy_loop 93 | 94 | process_match_data: 95 | ; end of block reached? 96 | cmpa.l a4,a0 97 | beq.s next_block 98 | 99 | match_data_only: 100 | ; match length 101 | moveq #0,d3 102 | move.b (a0)+,-(a2) 103 | move.b (a0)+,-(a2) 104 | move.w (a2)+,d3 105 | 106 | ; calc source ptr in output buffer -> a3 107 | neg.l d3 108 | lea (a1,d3.l),a3 109 | 110 | and.w d4,d0 111 | cmp.w d4,d0 112 | beq.s additional_match_length 113 | 114 | ; 4+short len copy 115 | move.b (a3)+,(a1)+ 116 | move.b (a3)+,(a1)+ 117 | move.b (a3)+,(a1)+ 118 | short_match_copy_loop: 119 | move.b (a3)+,(a1)+ 120 | 121 | dbra d0,short_match_copy_loop 122 | 123 | bra.s next_token 124 | 125 | additional_match_length: 126 | move.b (a0)+,d2 127 | add.w d2,d0 128 | not.b d2 129 | beq.s additional_match_length 130 | 131 | move.w d0,d3 132 | lsr.w #4,d0 133 | and.w d4,d3 134 | add.w d3,d3 135 | neg.w d3 136 | jmp match_copy_start(pc,d3.w) 137 | 138 | long_match_copy_loop: 139 | move.b (a3)+,(a1)+ 140 | move.b (a3)+,(a1)+ 141 | move.b (a3)+,(a1)+ 142 | move.b (a3)+,(a1)+ 143 | move.b (a3)+,(a1)+ 144 | move.b (a3)+,(a1)+ 145 | move.b (a3)+,(a1)+ 146 | move.b (a3)+,(a1)+ 147 | move.b (a3)+,(a1)+ 148 | move.b (a3)+,(a1)+ 149 | move.b (a3)+,(a1)+ 150 | move.b (a3)+,(a1)+ 151 | move.b (a3)+,(a1)+ 152 | move.b (a3)+,(a1)+ 153 | move.b (a3)+,(a1)+ 154 | move.b (a3)+,(a1)+ 155 | match_copy_start: 156 | dbra d0,long_match_copy_loop 157 | 158 | ; additional 4 ops 159 | move.b (a3)+,(a1)+ 160 | move.b (a3)+,(a1)+ 161 | move.b (a3)+,(a1)+ 162 | move.b (a3)+,(a1)+ 163 | 164 | bra next_token 165 | 166 | copy_uncompressed_block: 167 | andi.l #$7fffffff,d3 168 | block_copy_loop: 169 | move.b (a0)+,(a1)+ 170 | 171 | subq.l #1,d3 172 | bne.s block_copy_loop 173 | 174 | bra next_block 175 | 176 | end_of_compressed_data: 177 | movem.l (sp)+,d0-d4/a0-a4 178 | rts 179 | -------------------------------------------------------------------------------- /src/worker.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define NO_SYSBASE 11 | #include "compiler.h" 12 | #include "mydev.h" 13 | #include "debug.h" 14 | #include "worker.h" 15 | 16 | #define CMD_TERM 0x7ff0 17 | 18 | struct InitData 19 | { 20 | ULONG initSigMask; 21 | struct Task *initTask; 22 | struct DevBase *base; 23 | }; 24 | 25 | static const char WorkerTaskName[] = MYDEV_WORKER ".task"; 26 | 27 | static struct InitData * worker_startup(void) 28 | { 29 | /* retrieve global sys base */ 30 | struct Library *SysBase = *((struct Library **)4); 31 | 32 | struct Task *task = FindTask(NULL); 33 | 34 | return (struct InitData *)task->tc_UserData; 35 | } 36 | 37 | #define SysBase base->sysBase 38 | 39 | static SAVEDS ASM void worker_main(void) 40 | { 41 | struct IOStdReq *ior; 42 | struct MsgPort *port; 43 | 44 | /* retrieve dev base stored in user data of task */ 45 | struct InitData *id = worker_startup(); 46 | struct DevBase *base = id->base; 47 | D(("Task: id=%08lx base=%08lx\n", id, base)); 48 | 49 | /* create worker port */ 50 | port = CreateMsgPort(); 51 | 52 | /* call user init */ 53 | if(port != NULL) { 54 | if(!mydev_worker_init(base)) { 55 | /* user aborted worker */ 56 | DeleteMsgPort(port); 57 | port = NULL; 58 | } 59 | } 60 | 61 | /* setup port or NULL and trigger signal to caller task */ 62 | base->workerPort = port; 63 | D(("Task: signal task=%08lx mask=%08lx\n", id->initTask, id->initSigMask)); 64 | Signal(id->initTask, id->initSigMask); 65 | 66 | /* only if port is available then enter work loop. otherwise quit task */ 67 | if(port != NULL) 68 | { 69 | /* worker loop */ 70 | D(("Task: enter\n")); 71 | BOOL stay = TRUE; 72 | while (stay) { 73 | WaitPort(port); 74 | while (1) { 75 | ior = (struct IOStdReq *)GetMsg(port); 76 | if(ior == NULL) { 77 | break; 78 | } 79 | /* terminate? */ 80 | if(ior->io_Command == CMD_TERM) { 81 | stay = FALSE; 82 | ReplyMsg(&ior->io_Message); 83 | break; 84 | } 85 | /* regular command */ 86 | else { 87 | mydev_worker_cmd(base, ior); 88 | ReplyMsg(&ior->io_Message); 89 | } 90 | } 91 | } 92 | 93 | /* call shutdown only if worker was entered */ 94 | D(("Task: exit\n")); 95 | /* shutdown worker */ 96 | mydev_worker_exit(base); 97 | } 98 | 99 | D(("Task: delete port\n")); 100 | DeleteMsgPort(port); 101 | base->workerPort = NULL; 102 | 103 | /* kill myself */ 104 | D(("Task: die\n")); 105 | struct Task *me = FindTask(NULL); 106 | DeleteTask(me); 107 | Wait(0); 108 | D(("Task: NEVER!\n")); 109 | } 110 | 111 | BOOL worker_start(struct DevBase *base) 112 | { 113 | D(("Worker: start\n")); 114 | base->workerPort = NULL; 115 | 116 | /* alloc a signal */ 117 | BYTE signal = AllocSignal(-1); 118 | if(signal == -1) { 119 | D(("Worker: NO SIGNAL!\n")); 120 | return FALSE; 121 | } 122 | 123 | /* setup init data */ 124 | struct InitData id; 125 | id.initSigMask = 1 << signal; 126 | id.initTask = FindTask(NULL); 127 | id.base = base; 128 | D(("Worker: init data %08lx\n", &id)); 129 | 130 | /* now launch worker task and inject dev base 131 | make sure worker_main() does not run before base is set. 132 | */ 133 | Forbid(); 134 | struct Task *myTask = CreateTask(WorkerTaskName, 0, (CONST APTR)worker_main, 4096); 135 | if(myTask != NULL) { 136 | myTask->tc_UserData = (APTR)&id; 137 | } 138 | Permit(); 139 | if(myTask == NULL) { 140 | D(("Worker: NO TASK!\n")); 141 | FreeSignal(signal); 142 | return FALSE; 143 | } 144 | 145 | /* wait for start signal of new task */ 146 | D(("Worker: wait for task startup. sigmask=%08lx\n", id.initSigMask)); 147 | Wait(id.initSigMask); 148 | 149 | FreeSignal(signal); 150 | 151 | /* ok everything is fine. worker is ready to receive commands */ 152 | D(("Worker: started: port=%08lx\n", base->workerPort)); 153 | return (base->workerPort != NULL) ? TRUE : FALSE; 154 | } 155 | 156 | void worker_stop(struct DevBase *base) 157 | { 158 | struct IORequest newior; 159 | 160 | D(("Worker: stop\n")); 161 | 162 | if(base->workerPort != NULL) { 163 | /* send a message to the child process to shut down. */ 164 | newior.io_Message.mn_ReplyPort = CreateMsgPort(); 165 | newior.io_Command = CMD_TERM; 166 | 167 | /* send term message and wait for reply */ 168 | PutMsg(base->workerPort, &newior.io_Message); 169 | WaitPort(newior.io_Message.mn_ReplyPort); 170 | DeleteMsgPort(newior.io_Message.mn_ReplyPort); 171 | } 172 | 173 | D(("Worker: stopped\n")); 174 | } 175 | -------------------------------------------------------------------------------- /src/device.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "compiler.h" 10 | #include "debug.h" 11 | #include "mydev.h" 12 | 13 | #define VERS MYDEV_NAME " " MYDEV_VERSION_STR 14 | #define VSTRING MYDEV_NAME " " MYDEV_VERSION_STR " (" MYDEV_DATE ")\r\n" 15 | #define VERSTAG "\0$VER: " MYDEV_NAME " " MYDEV_VERSION_STR " (" MYDEV_DATE ")" 16 | 17 | int Main(void) 18 | { 19 | return RETURN_FAIL; 20 | } 21 | 22 | static const char UserLibName[] = MYDEV_NAME; 23 | static const char UserLibVer[] = VSTRING; 24 | static const char UserLibID[] = VERSTAG; 25 | 26 | #define LIBFUNC SAVEDS ASM 27 | 28 | LIBFUNC static struct DevBase * DevInit (REG(a0, BPTR Segment), 29 | REG(d0, struct DevBase *lh), 30 | REG(a6, struct ExecBase *sb)); 31 | LIBFUNC static BPTR DevExpunge (REG(a6, struct DevBase *base)); 32 | LIBFUNC static struct DevBase * DevOpen (REG(a1, struct IOStdReq *ior), 33 | REG(d0, ULONG unit), 34 | REG(d1, ULONG flags), 35 | REG(a6, struct DevBase *base)); 36 | LIBFUNC static BPTR DevClose (REG(a1, struct IOStdReq *ior), 37 | REG(a6, struct DevBase *base)); 38 | LIBFUNC static LONG DevNull (void); 39 | LIBFUNC static void DevBeginIO(REG(a1, struct IOStdReq *ior), 40 | REG(a6, struct DevBase *base)); 41 | LIBFUNC static LONG DevAbortIO(REG(a1, struct IOStdReq *ior), 42 | REG(a6, struct DevBase *base)); 43 | 44 | static const APTR LibVectors[] = 45 | { 46 | (APTR)DevOpen, 47 | (APTR)DevClose, 48 | (APTR)DevExpunge, 49 | (APTR)DevNull, 50 | (APTR)DevBeginIO, 51 | (APTR)DevAbortIO, 52 | (APTR)-1 53 | }; 54 | 55 | static const ULONG LibInitTab[] = 56 | { 57 | sizeof(struct DevBase), 58 | (ULONG)LibVectors, 59 | (ULONG)NULL, 60 | (ULONG)DevInit 61 | }; 62 | 63 | /* ---- RomTag ---- */ 64 | static const struct Resident ROMTag = 65 | { 66 | RTC_MATCHWORD, 67 | (struct Resident *)&ROMTag, 68 | (struct Resident *)&ROMTag + 1, 69 | RTF_AUTOINIT | RTF_COLDSTART, 70 | MYDEV_VERSION, 71 | NT_DEVICE, 72 | 0, /* prio */ 73 | (APTR)UserLibName, 74 | (APTR)UserLibVer, 75 | (APTR)LibInitTab 76 | }; 77 | 78 | #define DeleteLibrary(LIB) \ 79 | FreeMem((STRPTR)(LIB)-(LIB)->lib_NegSize, (ULONG)((LIB)->lib_NegSize+(LIB)->lib_PosSize)) 80 | 81 | /* ----- Functions ----- */ 82 | 83 | LIBFUNC static struct DevBase * DevInit(REG(a0, BPTR Segment), 84 | REG(d0, struct DevBase *base), 85 | REG(a6, struct ExecBase *sb)) 86 | { 87 | base->libBase.lib_Node.ln_Type = NT_LIBRARY; 88 | base->libBase.lib_Node.ln_Pri = 0; 89 | base->libBase.lib_Node.ln_Name = (char *)UserLibName; 90 | base->libBase.lib_Flags = LIBF_CHANGED | LIBF_SUMUSED; 91 | base->libBase.lib_Version = MYDEV_VERSION; 92 | base->libBase.lib_Revision = MYDEV_REVISION; 93 | base->libBase.lib_IdString = (char *)UserLibVer; 94 | 95 | base->segList = Segment; 96 | base->sysBase = (APTR)sb; 97 | 98 | D(("+DevInit(%08lx, %08lx, %08lx)\n", Segment, base, sb)); 99 | struct DevBase *result = mydev_init(base); 100 | D(("-DevInit: result=%08lx\n", result)); 101 | return result; 102 | } 103 | 104 | LIBFUNC static BPTR DevExpunge(REG(a6, struct DevBase *base)) 105 | { 106 | BPTR rc; 107 | 108 | if(base->libBase.lib_OpenCnt > 0) 109 | { 110 | base->libBase.lib_Flags |= LIBF_DELEXP; 111 | return 0; 112 | } 113 | 114 | mydev_expunge(base); 115 | 116 | rc = base->segList; 117 | 118 | Remove((struct Node *)base); 119 | DeleteLibrary(&base->libBase); 120 | 121 | return rc; 122 | } 123 | 124 | LIBFUNC static struct DevBase * DevOpen(REG(a1, struct IOStdReq *ior), 125 | REG(d0, ULONG unit), 126 | REG(d1, ULONG flags), 127 | REG(a6, struct DevBase *base)) 128 | { 129 | D(("+DevOpen(%lx,%ld,%ld)\n", ior, unit, flags)); 130 | if(base == NULL) { 131 | return NULL; 132 | } 133 | 134 | base->libBase.lib_OpenCnt++; 135 | 136 | struct DevBase *result = mydev_open(ior, unit, flags, base); 137 | 138 | if(result == NULL) { 139 | base->libBase.lib_OpenCnt--; 140 | } else { 141 | base->libBase.lib_Flags &= ~LIBF_DELEXP; 142 | } 143 | 144 | D(("-DevOpen: result=%08lx\n", result)); 145 | return result; 146 | } 147 | 148 | LIBFUNC static BPTR DevClose(REG(a1, struct IOStdReq *ior), 149 | REG(a6, struct DevBase *base)) 150 | { 151 | BPTR result = 0; 152 | D(("+DevClose(%lx)\n", ior)); 153 | 154 | mydev_close(ior, base); 155 | 156 | base->libBase.lib_OpenCnt--; 157 | if(base->libBase.lib_OpenCnt == 0) 158 | { 159 | if(base->libBase.lib_Flags & LIBF_DELEXP) 160 | { 161 | result = DevExpunge(base); 162 | } 163 | } 164 | 165 | D(("-DevClose: result=%08lx\n", result)); 166 | return 0; 167 | } 168 | 169 | LIBFUNC static LONG DevNull(void) 170 | { 171 | return 0; 172 | } 173 | 174 | LIBFUNC static void DevBeginIO(REG(a1, struct IOStdReq *ior), 175 | REG(a6, struct DevBase *base)) 176 | { 177 | D(("+DevBeginIO(%lx) cmd=%lx\n", ior, ior->io_Command)); 178 | mydev_begin_io(ior, base); 179 | D(("-DevBeginIO(%lx)\n", ior)); 180 | } 181 | 182 | LIBFUNC static LONG DevAbortIO(REG(a1, struct IOStdReq *ior), 183 | REG(a6, struct DevBase *base)) 184 | { 185 | D(("DevAbortIO(%lx)\n", ior)); 186 | return mydev_abort_io(ior, base); 187 | } 188 | -------------------------------------------------------------------------------- /doc/examples.md: -------------------------------------------------------------------------------- 1 | # romdisk Examples 2 | 3 | ## 1. A small device-less Kickstart with serial console 4 | 5 | ### Purpose 6 | 7 | We will create a stripped-down OS 3.1 Kickstart for A1200 or A500 that 8 | contains only the essential things that are required to boot from DF0 into 9 | a console. 10 | 11 | The fake-trackdisk variant of romdisk is used as a drop-in replacement for 12 | the original trackdisk device. A small disk image is created that mounts 13 | AUX: handler and starts a shell for it. 14 | 15 | ### Tutorial 16 | 17 | #### Split ROM 18 | 19 | * First, split an existing ROM using amitool's `romtool into its modules. We use the 20 | Cloanto Amiga Forever ROM for OS 3.1 on A1200: 21 | 22 | ``` 23 | romtool split -o . amiga-os-310-a1200.rom 24 | ``` 25 | 26 | * A directory called `40.68(A1200)` will be created with all the contained 27 | modules and an `index.txt` file that contains a list of the modules in the 28 | order found in the ROM. 29 | 30 | * Create a new file `mini.txt` by copying `index.txt` that only contains the 31 | minimal set of modules: 32 | 33 | ``` 34 | exec_40.10(A1200) 35 | expansion_40.2(A1200) 36 | romboot_40.1 37 | graphics.lib_40.24(AGA) 38 | dos.library_40.3 39 | filesystem_40.1 40 | console.device_40.2 41 | layers.library_40.1 42 | con-handler_40.2 43 | input_40.1 44 | utility.library_40.1(020) 45 | ramlib_40.2 46 | cia.resource_39.1 47 | misc.resource_37.1 48 | potgo.resource_37.4 49 | filesystem.resource_40.1 50 | disk.resource_37.2 51 | timer.device_39.4 52 | keymap.library_40.4 53 | bootmenu_40.5 54 | ram-handler_39.4 55 | shell_40.2 56 | intuition.library_40.85 57 | ``` 58 | 59 | * Note: we also removed `trackdisk.device` since we will replace it with 60 | romdisk's variant. 61 | 62 | #### Setup Disk Image 63 | 64 | * Next a ROMDISK disk image will be created: Create a directory called `ROMDISK` 65 | and build the following disk structure: 66 | 67 | ``` 68 | ROMDISK 69 | +-- C (Dir) 70 | | +-- Mount 71 | +-- L (Dir) 72 | | +-- aux-handler 73 | +-- Devs (Dir) 74 | | +-- serial.device 75 | | +-- MountList 76 | +-- S (Dir) 77 | +-- Startup-Sequence 78 | ``` 79 | 80 | * Copy the files `C/Mount`, `L/aux-handler`, and `Devs/serial.device` from 81 | a Workbench 3.1 installation 82 | 83 | * `MountList` contains the AUX: mountpoint: 84 | 85 | ``` 86 | AUX: 87 | Handler = L:Aux-Handler 88 | StackSize = 1000 89 | Priority = 5 90 | # 91 | ``` 92 | 93 | * `Startup-Sequence` looks like this: 94 | 95 | ``` 96 | echo "Hello, romboot!" 97 | mount aux: 98 | newshell aux: 99 | ``` 100 | 101 | #### Build Romdisk Image 102 | 103 | * Use `mkromdisk` tool of this release to pack the directory into a compressed 104 | romdisk image. Note: make sure amitool's `xdftool` is installed! We select 105 | the `deflate` compression method with `-f dflt`. 106 | 107 | ``` 108 | mkromdisk -f dflt -d ROMDISK disk.rodi 109 | ``` 110 | 111 | #### Build ROM Image 112 | 113 | * Finally, we combine the modules of the `mini.txt` list with the romdisk 114 | device and the created romdisk image to create our new Kickstart ROM. 115 | Note: use the trackdisk.device variant of romdisk! 116 | 117 | ``` 118 | romtool -v build -o kick.rom 40.68(A1200)/mini.txt devs/trackdisk.device_rel_td disk.rodi 119 | ``` 120 | 121 | * Here is the contents of the new ROM: 122 | 123 | ``` 124 | 2016-07-25 19:32:44,290 INFO root Welcom to romtool 125 | 2016-07-25 19:32:44,290 INFO root building 512 KiB Kick ROM @00f80000 126 | 2016-07-25 19:32:44,291 INFO root @00000000: adding module '40.68(A1200)/exec_40.10(A1200)' 127 | 2016-07-25 19:32:44,291 INFO root @000037b8: adding module '40.68(A1200)/expansion_40.2(A1200)' 128 | 2016-07-25 19:32:44,291 INFO root @00004290: adding module '40.68(A1200)/romboot_40.1' 129 | 2016-07-25 19:32:44,294 INFO root @000051a8: adding module '40.68(A1200)/graphics.lib_40.24(AGA)' 130 | 2016-07-25 19:32:44,294 INFO root @0001e62c: adding module '40.68(A1200)/dos.library_40.3' 131 | 2016-07-25 19:32:44,295 INFO root @00028238: adding module '40.68(A1200)/filesystem_40.1' 132 | 2016-07-25 19:32:44,295 INFO root @0002e1d8: adding module '40.68(A1200)/console.device_40.2' 133 | 2016-07-25 19:32:44,295 INFO root @00031e5c: adding module '40.68(A1200)/layers.library_40.1' 134 | 2016-07-25 19:32:44,296 INFO root @00035010: adding module '40.68(A1200)/con-handler_40.2' 135 | 2016-07-25 19:32:44,296 INFO root @000377d4: adding module '40.68(A1200)/input_40.1' 136 | 2016-07-25 19:32:44,296 INFO root @00038e08: adding module '40.68(A1200)/utility.library_40.1(020)' 137 | 2016-07-25 19:32:44,297 INFO root @000397c4: adding module '40.68(A1200)/ramlib_40.2' 138 | 2016-07-25 19:32:44,297 INFO root @00039be8: adding module '40.68(A1200)/cia.resource_39.1' 139 | 2016-07-25 19:32:44,297 INFO root @00039fd8: adding module '40.68(A1200)/misc.resource_37.1' 140 | 2016-07-25 19:32:44,298 INFO root @0003a088: adding module '40.68(A1200)/potgo.resource_37.4' 141 | 2016-07-25 19:32:44,298 INFO root @0003a1c0: adding module '40.68(A1200)/filesystem.resource_40.1' 142 | 2016-07-25 19:32:44,298 INFO root @0003a360: adding module '40.68(A1200)/disk.resource_37.2' 143 | 2016-07-25 19:32:44,298 INFO root @0003a6a8: adding module '40.68(A1200)/timer.device_39.4' 144 | 2016-07-25 19:32:44,299 INFO root @0003b494: adding module '40.68(A1200)/keymap.library_40.4' 145 | 2016-07-25 19:32:44,299 INFO root @0003c114: adding module '40.68(A1200)/bootmenu_40.5' 146 | 2016-07-25 19:32:44,299 INFO root @0003d724: adding module '40.68(A1200)/ram-handler_39.4' 147 | 2016-07-25 19:32:44,300 INFO root @0003fb9c: adding module '40.68(A1200)/shell_40.2' 148 | 2016-07-25 19:32:44,307 INFO root @0004407c: adding module '40.68(A1200)/intuition.library_40.85' 149 | 2016-07-25 19:32:44,312 INFO root @0005da1c: adding module 'devs/trackdisk.device_rel_td' 150 | 2016-07-25 19:32:44,312 INFO root @0005eb50: adding raw data 'disk.rodi' 151 | 2016-07-25 19:32:44,312 INFO root @00061e98: padding 123216 bytes with ff 152 | 2016-07-25 19:32:44,407 INFO root saving ROM to 'kick.rom' 153 | ``` 154 | 155 | * Now use your favorite Amiga emulator to test the ROM or soft kick it on a 156 | real machine! You should see a CLI on both the boot console and on serial. 157 | -------------------------------------------------------------------------------- /src/disk.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "compiler.h" 7 | #include "debug.h" 8 | #include "mydev.h" 9 | #include "disk.h" 10 | #include "unpack.h" 11 | 12 | #define EMPTY_PACK 0xffffffff 13 | 14 | extern ULONG theEnd; 15 | 16 | static struct DiskHeader *disk_find_header(struct DevBase *base) 17 | { 18 | /* HACK: point after device in ROM */ 19 | ULONG *ptr = &theEnd + 1; 20 | D(("disk_find: start=%08lx\n", ptr)); 21 | 22 | /* search range: 1 KiB */ 23 | int i; 24 | for(i=0;i<256;i++) { 25 | if(*ptr == ROMDISK_TAG) { 26 | struct DiskHeader *hdr = (struct DiskHeader *)ptr; 27 | if(hdr->version == ROMDISK_VERSION) { 28 | D(("disk_find: found=%08lx\n", ptr)); 29 | return hdr; 30 | } 31 | } 32 | ptr++; 33 | } 34 | 35 | D(("disk_find: NOT FOUND!\n")); 36 | return NULL; 37 | } 38 | 39 | static BOOL disk_get_unpacked_data(struct DevBase *base, ULONG pack_id) 40 | { 41 | /* already unpacked? */ 42 | if(pack_id == base->curPackId) { 43 | D((" pack: #%ld re-used\n", pack_id)); 44 | return TRUE; 45 | } 46 | 47 | /* retrieve offset for pack from pack header */ 48 | struct PackHeader *ph = base->packHeader; 49 | ULONG offset = ph->offsets[pack_id]; 50 | 51 | /* empty pack? */ 52 | if(offset == EMPTY_PACK) { 53 | D((" pack: #%ld empty\n", pack_id)); 54 | base->curBuffer = NULL; /* NULL marks empty pack */ 55 | base->curPackId = pack_id; 56 | } 57 | /* packed data */ 58 | else { 59 | UBYTE *pack_data = base->diskData + offset; 60 | base->curBuffer = base->unpackFunc(pack_data, base->unpackBuffer, ph->pack_size); 61 | D((" pack: #%ld -> @%08lx -> buf=%08lx\n", pack_id, pack_data, base->curBuffer)); 62 | if(base->curBuffer != NULL) { 63 | base->curPackId = pack_id; 64 | return TRUE; 65 | } else { 66 | base->curPackId = -1; 67 | return FALSE; 68 | } 69 | } 70 | } 71 | 72 | static void disk_raw_read(struct IOStdReq *ior, struct DevBase *base) 73 | { 74 | ULONG offset = ior->io_Offset; 75 | ULONG length = ior->io_Length; 76 | APTR buffer = ior->io_Data; 77 | 78 | if((offset + length) > base->diskHeader->disk_size) { 79 | ior->io_Actual = 0; 80 | ior->io_Error = TDERR_NotSpecified; 81 | D(("read out of range off=%08lx len=%08lx\n", offset, length)); 82 | } else { 83 | ior->io_Actual = length; 84 | UBYTE *data = base->diskData + offset; 85 | D(("read data @%08lx\n", data)); 86 | CopyMemQuick(data, buffer, length); 87 | } 88 | } 89 | 90 | static void disk_pack_read(struct IOStdReq *ior, struct DevBase *base) 91 | { 92 | ULONG offset = ior->io_Offset; 93 | ULONG length = ior->io_Length; 94 | UBYTE *buffer = (UBYTE *)ior->io_Data; 95 | 96 | if((offset + length) > base->diskHeader->disk_size) { 97 | ior->io_Actual = 0; 98 | ior->io_Error = TDERR_NotSpecified; 99 | D(("read out of range off=%08lx len=%08lx\n", offset, length)); 100 | } else { 101 | struct PackHeader *packHeader = base->packHeader; 102 | ULONG pack_size = packHeader->pack_size; 103 | ULONG pack_id = offset / pack_size; 104 | ULONG pack_off = offset % pack_size; 105 | /* read until length reached */ 106 | ULONG done = 0; 107 | BOOL last = FALSE; 108 | while(1) { 109 | ULONG l; 110 | /* read command crosses pack boundaries -> multiple unpacks required */ 111 | if((pack_off + length) > pack_size) { 112 | l = pack_size - pack_off; 113 | } 114 | /* read fits in this pack */ 115 | else { 116 | l = length; 117 | last = TRUE; 118 | } 119 | /* get unpacked buffer for pack_id */ 120 | BOOL ok = disk_get_unpacked_data(base, pack_id); 121 | if(!ok) { 122 | ior->io_Error = TDERR_NotSpecified; 123 | D(("READ ERROR: no buffer!\n")); 124 | break; 125 | } 126 | /* copy/clear fragment of this pack */ 127 | UBYTE *pack_buffer = base->curBuffer; 128 | if(pack_buffer != NULL) { 129 | UBYTE *data = pack_buffer + pack_off; 130 | D((" read @%08lx: data @%08lx + %08lx with %08lx bytes\n", 131 | buffer, pack_buffer, pack_off, l)); 132 | CopyMemQuick(data, buffer, l); 133 | } else { 134 | /* clear */ 135 | memset(buffer, 0, l); 136 | } 137 | /* last pack? */ 138 | done += l; 139 | if(last) { 140 | break; 141 | } 142 | /* next pack iteration */ 143 | pack_id++; 144 | pack_off=0; 145 | length -= l; 146 | buffer += l; 147 | } 148 | /* done */ 149 | ior->io_Actual = done; 150 | D((" done %08lx\n", done)); 151 | } 152 | } 153 | 154 | BOOL disk_setup(struct DevBase *base) 155 | { 156 | base->diskHeader = disk_find_header(base); 157 | if(base->diskHeader == NULL) { 158 | return FALSE; 159 | } 160 | 161 | /* setup format */ 162 | UWORD format = base->diskHeader->format; 163 | if(format == ROMDISK_FORMAT_RAW) { 164 | D(("raw format\n")); 165 | base->diskData = (UBYTE *)(base->diskHeader + 1); 166 | base->readFunc = disk_raw_read; 167 | base->packHeader = NULL; 168 | } 169 | else if(format == ROMDISK_FORMAT_PACK) { 170 | /* expect pack header after disk header */ 171 | struct PackHeader *packHeader = (struct PackHeader *)(base->diskHeader + 1); 172 | base->packHeader = packHeader; 173 | base->readFunc = disk_pack_read; 174 | 175 | /* check tag */ 176 | ULONG pack_tag = packHeader->tag; 177 | if(pack_tag != ROMDISK_PACK_TAG) { 178 | D(("no pack header tag: %08lx\n", pack_tag)); 179 | return FALSE; 180 | } 181 | /* check packer */ 182 | ULONG packer = packHeader->packer; 183 | if(packer == ROMDISK_PACK_NOP) { 184 | base->unpackFunc = unpack_nop; 185 | D(("no packer\n")); 186 | } 187 | else if(packer == ROMDISK_PACK_RNC) { 188 | base->unpackFunc = unpack_rnc; 189 | D(("RNC format\n")); 190 | } 191 | else if(packer == ROMDISK_PACK_DFLT) { 192 | base->unpackFunc = unpack_dflt; 193 | D(("DFLT format\n")); 194 | } 195 | else if(packer == ROMDISK_PACK_LZ4) { 196 | base->unpackFunc = unpack_lz4; 197 | D(("LZ4 format\n")); 198 | } 199 | else { 200 | D(("unknown packer: %08lx\n", packer)); 201 | return FALSE; 202 | } 203 | 204 | /* calc disk base */ 205 | UBYTE *base_ptr = (UBYTE *)(packHeader + 1); 206 | base_ptr += (packHeader->num_packs - 1) * sizeof(ULONG); 207 | base->diskData = base_ptr; 208 | D(("pack disk data: %08lx\n", base_ptr)); 209 | } 210 | else { 211 | D(("unknown format: %08x\n", (ULONG)format)); 212 | return FALSE; 213 | } 214 | return TRUE; 215 | } 216 | 217 | BOOL disk_open(struct DevBase *base) 218 | { 219 | base->curPackId = -1; 220 | 221 | /* alloc pack buffer */ 222 | struct PackHeader *ph = base->packHeader; 223 | if((ph != NULL) && (ph->tag != ROMDISK_PACK_NOP)) { 224 | base->unpackBuffer = (BYTE *)AllocMem(ph->pack_size, MEMF_PUBLIC); 225 | D(("unpackBuffer=%08lx size=%08lx\n", base->unpackBuffer, ph->pack_size)); 226 | if(base->unpackBuffer == NULL) { 227 | return FALSE; 228 | } 229 | } else { 230 | base->unpackBuffer = NULL; 231 | } 232 | 233 | return TRUE; 234 | } 235 | 236 | void disk_close(struct DevBase *base) 237 | { 238 | /* free pack buffer */ 239 | struct PackHeader *ph = base->packHeader; 240 | if(base->unpackBuffer != NULL) { 241 | FreeMem(base->unpackBuffer, ph->pack_size); 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /src/unpacker/rnc_1.s: -------------------------------------------------------------------------------- 1 | *------------------------------------------------------------------------------ 2 | * PRO-PACK Unpack Source Code - MC68000, Method 1 3 | * 4 | * Copyright (c) 1991,92 Rob Northen Computing, U.K. All Rights Reserved. 5 | * 6 | * File: RNC_1.S 7 | * 8 | * Date: 24.3.92 9 | *------------------------------------------------------------------------------ 10 | 11 | *------------------------------------------------------------------------------ 12 | * Conditional Assembly Flags 13 | *------------------------------------------------------------------------------ 14 | 15 | CHECKSUMS EQU 0 ; set this flag to 1 if you require 16 | ; the data to be validated 17 | 18 | PROTECTED EQU 0 ; set this flag to 1 if you are unpacking 19 | ; a file packed with option "-K" 20 | 21 | *------------------------------------------------------------------------------ 22 | * Return Codes 23 | *------------------------------------------------------------------------------ 24 | 25 | NOT_PACKED EQU 0 26 | PACKED_CRC EQU -1 27 | UNPACKED_CRC EQU -2 28 | 29 | *------------------------------------------------------------------------------ 30 | * Other Equates 31 | *------------------------------------------------------------------------------ 32 | 33 | PACK_TYPE EQU 1 34 | PACK_ID EQU "R"<<24+"N"<<16+"C"<<8+PACK_TYPE 35 | HEADER_LEN EQU 18 36 | MIN_LENGTH EQU 2 37 | CRC_POLY EQU $A001 38 | RAW_TABLE EQU 0 39 | POS_TABLE EQU RAW_TABLE+16*8 40 | LEN_TABLE EQU POS_TABLE+16*8 41 | 42 | 43 | IFEQ CHECKSUMS 44 | BUFSIZE EQU 16*8*3 45 | ELSEIF 46 | BUFSIZE EQU 512 47 | ENDC 48 | 49 | counts EQUR d4 50 | key EQUR d5 51 | bit_buffer EQUR d6 52 | bit_count EQUR d7 53 | 54 | input EQUR a3 55 | input_hi EQUR a4 56 | output EQUR a5 57 | output_hi EQUR a6 58 | 59 | *------------------------------------------------------------------------------ 60 | * Macros 61 | *------------------------------------------------------------------------------ 62 | 63 | getrawREP MACRO 64 | getrawREP2\@ 65 | move.b (input)+,(output)+ 66 | IFNE PROTECTED 67 | eor.b key,-1(output) 68 | ENDC 69 | dbra d0,getrawREP2\@ 70 | IFNE PROTECTED 71 | ror.w #1,key 72 | ENDC 73 | ENDM 74 | 75 | *------------------------------------------------------------------------------ 76 | * PRO-PACK Unpack Routine - MC68000, Method 1 77 | * 78 | * on entry, 79 | * d0.l = packed data key, or 0 if file was not packed with a key 80 | * a0.l = start address of packed file 81 | * a1.l = start address to write unpacked file 82 | * on exit, 83 | * d0.l = length of unpacked file in bytes OR error code 84 | * 0 = not a packed file 85 | * -1 = packed data CRC error 86 | * -2 = unpacked data CRC error 87 | * 88 | * all other registers are preserved 89 | *------------------------------------------------------------------------------ 90 | Unpack 91 | movem.l d0-d7/a0-a6,-(sp) 92 | lea -BUFSIZE(sp),sp 93 | move.l sp,a2 94 | 95 | IFNE PROTECTED 96 | move.w d0,key 97 | ENDC 98 | 99 | bsr read_long 100 | moveq.l #NOT_PACKED,d1 101 | cmp.l #PACK_ID,d0 102 | bne unpack16 103 | bsr read_long 104 | move.l d0,BUFSIZE(sp) 105 | lea HEADER_LEN-8(a0),input 106 | move.l a1,output 107 | lea (output,d0.l),output_hi 108 | bsr read_long 109 | lea (input,d0.l),input_hi 110 | 111 | IFNE CHECKSUMS 112 | move.l input,a1 113 | bsr crc_block 114 | lea -6(input),a0 115 | bsr read_long 116 | moveq.l #PACKED_CRC,d1 117 | cmp.w d2,d0 118 | bne unpack16 119 | swap d0 120 | move.w d0,-(sp) 121 | ENDC 122 | 123 | clr.w -(sp) 124 | cmp.l input_hi,output 125 | bcc.s unpack7 126 | moveq.l #0,d0 127 | move.b -2(input),d0 128 | lea (output_hi,d0.l),a0 129 | cmp.l input_hi,a0 130 | bls.s unpack7 131 | addq.w #2,sp 132 | 133 | move.l input_hi,d0 134 | btst #0,d0 135 | beq.s unpack2 136 | addq.w #1,input_hi 137 | addq.w #1,a0 138 | unpack2 139 | move.l a0,d0 140 | btst #0,d0 141 | beq.s unpack3 142 | addq.w #1,a0 143 | unpack3 144 | moveq.l #0,d0 145 | unpack4 146 | cmp.l a0,output_hi 147 | beq.s unpack5 148 | move.b -(a0),d1 149 | move.w d1,-(sp) 150 | addq.b #1,d0 151 | bra.s unpack4 152 | unpack5 153 | move.w d0,-(sp) 154 | add.l d0,a0 155 | IFNE PROTECTED 156 | move.w key,-(sp) 157 | ENDC 158 | unpack6 159 | lea -8*4(input_hi),input_hi 160 | movem.l (input_hi),d0-d7 161 | movem.l d0-d7,-(a0) 162 | cmp.l input,input_hi 163 | bhi.s unpack6 164 | sub.l input_hi,input 165 | add.l a0,input 166 | IFNE PROTECTED 167 | move.w (sp)+,key 168 | ENDC 169 | 170 | unpack7 171 | moveq.l #0,bit_count 172 | move.b 1(input),bit_buffer 173 | rol.w #8,bit_buffer 174 | move.b (input),bit_buffer 175 | moveq.l #2,d0 176 | moveq.l #2,d1 177 | bsr input_bits 178 | unpack8 179 | move.l a2,a0 180 | bsr make_huftable 181 | lea POS_TABLE(a2),a0 182 | bsr make_huftable 183 | lea LEN_TABLE(a2),a0 184 | bsr make_huftable 185 | unpack9 186 | moveq.l #-1,d0 187 | moveq.l #16,d1 188 | bsr input_bits 189 | move.w d0,counts 190 | subq.w #1,counts 191 | bra.s unpack12 192 | unpack10 193 | lea POS_TABLE(a2),a0 194 | moveq.l #0,d0 195 | bsr.s input_value 196 | neg.l d0 197 | lea -1(output,d0.l),a1 198 | lea LEN_TABLE(a2),a0 199 | bsr.s input_value 200 | move.b (a1)+,(output)+ 201 | unpack11 202 | move.b (a1)+,(output)+ 203 | dbra d0,unpack11 204 | unpack12 205 | move.l a2,a0 206 | bsr.s input_value 207 | subq.w #1,d0 208 | bmi.s unpack13 209 | getrawREP 210 | move.b 1(input),d0 211 | rol.w #8,d0 212 | move.b (input),d0 213 | lsl.l bit_count,d0 214 | moveq.l #1,d1 215 | lsl.w bit_count,d1 216 | subq.w #1,d1 217 | and.l d1,bit_buffer 218 | or.l d0,bit_buffer 219 | unpack13 220 | dbra counts,unpack10 221 | cmp.l output_hi,output 222 | bcs.s unpack8 223 | 224 | move.w (sp)+,d0 225 | beq.s unpack15 226 | IFNE CHECKSUMS 227 | move.l output,a0 228 | ENDC 229 | unpack14 230 | move.w (sp)+,d1 231 | IFNE CHECKSUMS 232 | move.b d1,(a0)+ 233 | ELSEIF 234 | move.b d1,(output)+ 235 | ENDC 236 | subq.b #1,d0 237 | bne.s unpack14 238 | unpack15 239 | 240 | IFNE CHECKSUMS 241 | move.l BUFSIZE+2(sp),d0 242 | sub.l d0,output 243 | move.l output,a1 244 | bsr crc_block 245 | moveq.l #UNPACKED_CRC,d1 246 | cmp.w (sp)+,d2 247 | beq.s unpack17 248 | ELSEIF 249 | bra.s unpack17 250 | ENDC 251 | unpack16 252 | move.l d1,BUFSIZE(sp) 253 | unpack17 254 | lea BUFSIZE(sp),sp 255 | movem.l (sp)+,d0-d7/a0-a6 256 | rts 257 | 258 | input_value 259 | move.w (a0)+,d0 260 | and.w bit_buffer,d0 261 | sub.w (a0)+,d0 262 | bne.s input_value 263 | move.b 16*4-4(a0),d1 264 | sub.b d1,bit_count 265 | bge.s input_value2 266 | bsr.s input_bits3 267 | input_value2 268 | lsr.l d1,bit_buffer 269 | move.b 16*4-3(a0),d0 270 | cmp.b #2,d0 271 | blt.s input_value4 272 | subq.b #1,d0 273 | move.b d0,d1 274 | move.b d0,d2 275 | move.w 16*4-2(a0),d0 276 | and.w bit_buffer,d0 277 | sub.b d1,bit_count 278 | bge.s input_value3 279 | bsr.s input_bits3 280 | input_value3 281 | lsr.l d1,bit_buffer 282 | bset d2,d0 283 | input_value4 284 | rts 285 | 286 | input_bits 287 | and.w bit_buffer,d0 288 | sub.b d1,bit_count 289 | bge.s input_bits2 290 | bsr.s input_bits3 291 | input_bits2 292 | lsr.l d1,bit_buffer 293 | rts 294 | 295 | input_bits3 296 | add.b d1,bit_count 297 | lsr.l bit_count,bit_buffer 298 | swap bit_buffer 299 | addq.w #4,input 300 | move.b -(input),bit_buffer 301 | rol.w #8,bit_buffer 302 | move.b -(input),bit_buffer 303 | swap bit_buffer 304 | sub.b bit_count,d1 305 | moveq.l #16,bit_count 306 | sub.b d1,bit_count 307 | rts 308 | 309 | read_long 310 | moveq.l #3,d1 311 | read_long2 312 | lsl.l #8,d0 313 | move.b (a0)+,d0 314 | dbra d1,read_long2 315 | rts 316 | 317 | make_huftable 318 | moveq.l #$1f,d0 319 | moveq.l #5,d1 320 | bsr.s input_bits 321 | subq.w #1,d0 322 | bmi.s make_huftable8 323 | move.w d0,d2 324 | move.w d0,d3 325 | lea -16(sp),sp 326 | move.l sp,a1 327 | make_huftable3 328 | moveq.l #$f,d0 329 | moveq.l #4,d1 330 | bsr.s input_bits 331 | move.b d0,(a1)+ 332 | dbra d2,make_huftable3 333 | moveq.l #1,d0 334 | ror.l #1,d0 335 | moveq.l #1,d1 336 | moveq.l #0,d2 337 | movem.l d5-d7,-(sp) 338 | make_huftable4 339 | move.w d3,d4 340 | lea 12(sp),a1 341 | make_huftable5 342 | cmp.b (a1)+,d1 343 | bne.s make_huftable7 344 | moveq.l #1,d5 345 | lsl.w d1,d5 346 | subq.w #1,d5 347 | move.w d5,(a0)+ 348 | move.l d2,d5 349 | swap d5 350 | move.w d1,d7 351 | subq.w #1,d7 352 | make_huftable6 353 | roxl.w #1,d5 354 | roxr.w #1,d6 355 | dbra d7,make_huftable6 356 | moveq.l #16,d5 357 | sub.b d1,d5 358 | lsr.w d5,d6 359 | move.w d6,(a0)+ 360 | move.b d1,16*4-4(a0) 361 | move.b d3,d5 362 | sub.b d4,d5 363 | move.b d5,16*4-3(a0) 364 | moveq.l #1,d6 365 | subq.b #1,d5 366 | lsl.w d5,d6 367 | subq.w #1,d6 368 | move.w d6,16*4-2(a0) 369 | add.l d0,d2 370 | make_huftable7 371 | dbra d4,make_huftable5 372 | lsr.l #1,d0 373 | addq.b #1,d1 374 | cmp.b #17,d1 375 | bne.s make_huftable4 376 | movem.l (sp)+,d5-d7 377 | lea 16(sp),sp 378 | make_huftable8 379 | rts 380 | 381 | IFNE CHECKSUMS 382 | crc_block 383 | move.l a2,a0 384 | moveq.l #0,d3 385 | crc_block2 386 | move.l d3,d1 387 | moveq.l #7,d2 388 | crc_block3 389 | lsr.w #1,d1 390 | bcc.s crc_block4 391 | eor.w #CRC_POLY,d1 392 | crc_block4 393 | dbra d2,crc_block3 394 | move.w d1,(a0)+ 395 | addq.b #1,d3 396 | bne.s crc_block2 397 | moveq.l #0,d2 398 | crc_block5 399 | move.b (a1)+,d1 400 | eor.b d1,d2 401 | move.w d2,d1 402 | and.w #$ff,d2 403 | add.w d2,d2 404 | move.w (a2,d2.w),d2 405 | lsr.w #8,d1 406 | eor.b d1,d2 407 | subq.l #1,d0 408 | bne.s crc_block5 409 | rts 410 | ENDC 411 |  -------------------------------------------------------------------------------- /mkromdisk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # mkromdisk - create romboot'able disk image 4 | # 5 | 6 | import os 7 | import sys 8 | import argparse 9 | import subprocess 10 | import logging 11 | import struct 12 | from io import BytesIO 13 | import zlib 14 | 15 | 16 | LOG_FORMAT = "%(asctime)-15s %(levelname)-10s %(message)s" 17 | DESC = "create romboot'able disk image suitable for romdisk.device" 18 | 19 | 20 | def create_disk_image(temp_prefix, disk_dir, geo, dostype): 21 | temp_hdf = temp_prefix + ".hdf" 22 | geo_str = "chs=%d,%d,%d" % tuple(geo) 23 | cmd = ["xdftool", "-f", temp_hdf, "pack", disk_dir, dostype, geo_str] 24 | # add boot block 25 | cmd += ["+", "boot", "install"] 26 | logging.info("mastering image with: %s", " ".join(cmd)) 27 | res = subprocess.call(cmd) 28 | if res != 0: 29 | logging.error("Error mastering image: return code=%d", res) 30 | return None 31 | else: 32 | return temp_hdf 33 | 34 | 35 | def gen_disk_header(geo, dostype, disk_format=0, boot_prio=5, num_buffers=5): 36 | io = BytesIO() 37 | # tag 38 | io.write(b"RODI") 39 | # version, format 40 | io.write(struct.pack(">HH", 1, disk_format)) 41 | # dos name 42 | io.write(b"rom\0") 43 | # geometry: cyls, heads, secs 44 | io.write(struct.pack(">III", geo[0], geo[1], geo[2])) 45 | # boot_prio, dostype 46 | io.write(struct.pack(">iI", boot_prio, dostype)) 47 | # num_buffers, disk_size 48 | disk_size = geo[0] * geo[1] * geo[2] * 512 49 | io.write(struct.pack(">II", num_buffers, disk_size)) 50 | return io.getvalue() 51 | 52 | 53 | def pack_rnc(raw_packs, temp_prefix): 54 | # first write all raw packs as files to disk 55 | n = len(raw_packs) 56 | for i in range(n): 57 | data = raw_packs[i] 58 | if data is not None: 59 | out_file = "%s_%03d.pak" % (temp_prefix, i) 60 | # write track to temp file 61 | with open(out_file, "wb") as fo: 62 | fo.write(data) 63 | logging.debug("wrote raw data to '%s'", out_file) 64 | 65 | # call ppami.exe via vamos to pack all files in one run 66 | file_pat = "%s_#?.pak" % temp_prefix 67 | cmd = ["vamos", "ppami.exe", "p", "d", file_pat] 68 | logging.debug("calling RNC packer: %s", " ".join(cmd)) 69 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE) 70 | p.communicate() 71 | res = p.returncode 72 | if res != 0: 73 | logging.error("RNC packer failed: return code=%d", res) 74 | return None 75 | 76 | # collect generated packed files 77 | out_packs = [] 78 | for i in range(n): 79 | if raw_packs[i] is None: 80 | out_packs.append(None) 81 | else: 82 | out_file = "%s_%03d.pak" % (temp_prefix, i) 83 | pack_file = "%s_%03d.RNC" % (temp_prefix, i) 84 | if os.path.exists(pack_file): 85 | # read result 86 | with open(pack_file, "rb") as fi: 87 | data = fi.read() 88 | out_packs.append(data) 89 | logging.debug("read packed data from '%s'", pack_file) 90 | # clean files 91 | os.remove(pack_file) 92 | else: 93 | # keep raw pack 94 | out_packs.append(raw_packs[i]) 95 | logging.debug("keep raw packet for '%s'", out_file) 96 | os.remove(out_file) 97 | return out_packs 98 | 99 | 100 | def pack_dflt(raw_packs): 101 | out_packs = [] 102 | for p in raw_packs: 103 | if p is None: 104 | out_packs.append(None) 105 | else: 106 | zdata = zlib.compress(p, 9) 107 | # strip zlib header to get deflate stream (see RFC 1950) 108 | skip = 2 109 | cmf = zdata[0] 110 | flg = zdata[1] 111 | cm = cmf & 0xF 112 | cinfo = cmf >> 4 113 | fdict = flg & 0x20 == 0x20 114 | flevel = flg >> 6 115 | if fdict: 116 | skip += 4 117 | data = zdata[skip:-4] 118 | out_packs.append(data) 119 | return out_packs 120 | 121 | 122 | def pack_with_files(raw_packs, temp_prefix, cmd_cb): 123 | # loop over packs write them to disk and read back packed data 124 | out_packs = [] 125 | n = len(raw_packs) 126 | for i in range(n): 127 | data = raw_packs[i] 128 | if data is None: 129 | out_packs.append(None) 130 | else: 131 | in_file = "%s_%03d.raw" % (temp_prefix, i) 132 | out_file = "%s_%03d.pak" % (temp_prefix, i) 133 | # write track to temp file 134 | with open(in_file, "wb") as fi: 135 | fi.write(data) 136 | logging.debug("wrote raw data to '%s'", in_file) 137 | 138 | # now call external tool 139 | ok = cmd_cb(in_file, out_file) 140 | if ok: 141 | # read result 142 | with open(out_file, "rb") as fo: 143 | data = fo.read() 144 | out_packs.append(data) 145 | # clean files 146 | os.remove(in_file) 147 | os.remove(out_file) 148 | if not ok: 149 | break 150 | return out_packs 151 | 152 | 153 | def pack_lz4(raw_packs, temp_prefix): 154 | cmd = ["lz4", "-9", "-q", "-f"] 155 | 156 | def lz4_cmd(in_file, out_file): 157 | my_cmd = cmd + [in_file, out_file] 158 | ret = subprocess.call(my_cmd) 159 | return ret == 0 160 | 161 | return pack_with_files(raw_packs, temp_prefix, lz4_cmd) 162 | 163 | 164 | def is_pack_empty(pack): 165 | for b in pack: 166 | if b != 0: 167 | return False 168 | return True 169 | 170 | 171 | def pack_image(geo, pack_entity, img_data, packer, temp_prefix="temp"): 172 | # get packs either based on cyls or tracks 173 | cyls, heads, secs = geo 174 | if pack_entity == "cyls": 175 | num_packs = cyls 176 | pack_size = heads * secs * 512 177 | else: 178 | num_packs = cyls * heads 179 | pack_size = secs * 512 180 | 181 | logging.info( 182 | "packer=%s pack_entity=%s -> num_packs=%d, pack_size=%d", 183 | packer, 184 | pack_entity, 185 | num_packs, 186 | pack_size, 187 | ) 188 | 189 | # generate raw data to be packed 190 | raw_packs = [] 191 | off = 0 192 | num_filled = 0 193 | for p in range(num_packs): 194 | # extract pack data from image 195 | pack_data = img_data[off : off + pack_size] 196 | if is_pack_empty(pack_data): 197 | raw_packs.append(None) 198 | else: 199 | raw_packs.append(pack_data) 200 | num_filled += 1 201 | off += pack_size 202 | 203 | # call packer 204 | if num_filled > 0: 205 | if packer == "rnc": 206 | out_packs = pack_rnc(raw_packs, temp_prefix) 207 | elif packer == "dflt": 208 | out_packs = pack_dflt(raw_packs) 209 | elif packer == "lz4": 210 | out_packs = pack_lz4(raw_packs, temp_prefix) 211 | elif packer == "nop": 212 | # no packer 213 | out_packs = raw_packs 214 | else: 215 | return None 216 | if out_packs is None: 217 | return None 218 | else: 219 | out_packs = [None] * num_packs 220 | 221 | # post process packs 222 | total_size = 0 223 | for p in range(num_packs): 224 | data = out_packs[p] 225 | if data is not None: 226 | # pad pack data to long 227 | size = len(data) 228 | mo = size % 4 229 | if mo > 0: 230 | data += b"\0" * (4 - mo) 231 | size += 4 - mo 232 | out_packs[p] = data 233 | # stats 234 | ratio = size * 100 / pack_size 235 | total_size += size 236 | logging.info("pack #%d: size=%d ratio=%.2f", p, size, ratio) 237 | 238 | # total ratio 239 | img_size = len(img_data) 240 | ratio = total_size * 100 / img_size 241 | pack_kib = int((total_size + 1023) / 1024) 242 | logging.info("disk: size=%d (%d KiB) ratio=%.2f", total_size, pack_kib, ratio) 243 | 244 | # generate output 245 | return gen_pack_data(packer, pack_size, out_packs) 246 | 247 | 248 | def get_packer_tag(packer): 249 | p = packer.upper().encode("ASCII") 250 | while len(p) < 4: 251 | p = p + b"\0" 252 | return p 253 | 254 | 255 | def gen_pack_data(packer, pack_size, pack_datas): 256 | # gen header 257 | io = BytesIO() 258 | # tag 259 | io.write(b"PACK") 260 | # packer 261 | io.write(get_packer_tag(packer)) 262 | # num packs, pack_Size 263 | io.write(struct.pack(">II", len(pack_datas), pack_size)) 264 | # offsets for num_packs 265 | off = 0 266 | empty = 0xFFFFFFFF 267 | for data in pack_datas: 268 | if data is None: 269 | val = empty 270 | else: 271 | val = off 272 | off += len(data) 273 | io.write(struct.pack(">I", val)) 274 | # data packs 275 | for data in pack_datas: 276 | if data is not None: 277 | io.write(data) 278 | return io.getvalue() 279 | 280 | 281 | def mkromdisk( 282 | out_disk, 283 | geometry, 284 | from_dir=None, 285 | from_image=None, 286 | temp_prefix="temp", 287 | dostype="DOS0", 288 | boot_prio=5, 289 | image_format="raw", 290 | pack_entity="cyls", 291 | num_buffers=5, 292 | ): 293 | logging.info("image geometry: cylinders=%d, heads=%d, sectors=%d" % geometry) 294 | # create from dir 295 | if from_dir is not None: 296 | logging.info("create image from dir: %s", from_dir) 297 | img = create_disk_image(temp_prefix, from_dir, geometry, dostype) 298 | is_temp = True 299 | if img is None: 300 | return 1 301 | elif from_image is not None: 302 | img = from_image 303 | is_temp = False 304 | logging.info("using given image: %s", img) 305 | else: 306 | logging.error("Neither image not dir given as input!") 307 | return 2 308 | 309 | # check input image 310 | if not os.path.isfile(img): 311 | logging.error("can't find image file: %s", img) 312 | # read image 313 | with open(img, "rb") as fh: 314 | img_data = fh.read() 315 | # remove temp image 316 | if is_temp: 317 | logging.info("removing temp image: %s", img) 318 | os.remove(img) 319 | 320 | # check size 321 | cyls, heads, secs = geometry 322 | image_size = cyls * heads * secs * 512 323 | file_size = len(img_data) 324 | if file_size != image_size: 325 | logging.error( 326 | "disk image has wrong size: expect=%d but got=%d", image_size, file_size 327 | ) 328 | return 3 329 | 330 | # extract dostype 331 | img_dostype = struct.unpack_from(">I", img_data, 0)[0] 332 | logging.info("dostype found: %08x %s", img_dostype, img_data[0:4]) 333 | 334 | # compress tracks or cyls? 335 | if image_format == "raw": 336 | out_data = img_data 337 | disk_format = 0 338 | elif image_format in ("rnc", "nop", "dflt", "lz4"): 339 | out_data = pack_image( 340 | geometry, pack_entity, img_data, image_format, temp_prefix 341 | ) 342 | if not out_data: 343 | logging.error("Packing image failed!") 344 | return 6 345 | disk_format = 1 346 | else: 347 | logging.error("Unknown storage format: %s", image_format) 348 | return 4 349 | if out_data is None: 350 | return 5 351 | 352 | # generate romdisk file 353 | logging.info("generating romdisk file: %s", out_disk) 354 | with open(out_disk, "wb") as fh: 355 | hdr = gen_disk_header( 356 | geometry, 357 | img_dostype, 358 | disk_format=disk_format, 359 | boot_prio=boot_prio, 360 | num_buffers=num_buffers, 361 | ) 362 | fh.write(hdr) 363 | fh.write(out_data) 364 | # pad to long 365 | n = len(hdr) + len(out_data) 366 | mo = n % 4 367 | if mo > 0: 368 | pad = b"\0" * (4 - mo) 369 | fh.write(pad) 370 | 371 | # done 372 | size = os.path.getsize(out_disk) 373 | kib = int((size + 1023) / 1024) 374 | logging.info("done. created romdisk image with %d bytes/%d KiB", size, kib) 375 | return 0 376 | 377 | 378 | def parse_geo(geo, fmt): 379 | if geo is None: 380 | # default for raw is half-disk 381 | if fmt == "raw": 382 | return (40, 2, 11) 383 | # for all packed formats the default is full DD disk 384 | else: 385 | return (80, 2, 11) 386 | elif geo == "adf": 387 | return (80, 2, 11) 388 | elif geo == "mini": # mini disk image 389 | return (40, 2, 11) 390 | else: 391 | disk_geo = list(map(int, geo.split(","))) 392 | assert len(disk_geo) == 3 393 | return disk_geo 394 | 395 | 396 | def parse_args(): 397 | """parse args and return (args, opts)""" 398 | parser = argparse.ArgumentParser(description=DESC) 399 | 400 | # global options 401 | parser.add_argument("out_disk", help="disk image to be created") 402 | parser.add_argument( 403 | "-d", "--dir", default=None, help="create romdisk image from given directory" 404 | ) 405 | parser.add_argument( 406 | "-i", "--image", default=None, help="create romdisk image from given disk image" 407 | ) 408 | parser.add_argument( 409 | "-t", "--temp-prefix", default="temp", help="prefix for intermediate files" 410 | ) 411 | parser.add_argument( 412 | "-g", 413 | "--geometry", 414 | default=None, 415 | help="disk image geometry (cylinders, heads, sectors)", 416 | ) 417 | parser.add_argument( 418 | "-D", 419 | "--dostype", 420 | default="ffs", 421 | help="dostype used for image mastering with xdftool", 422 | ) 423 | parser.add_argument( 424 | "-p", "--boot-prio", default=5, type=int, help="boot priority for romdisk" 425 | ) 426 | parser.add_argument( 427 | "-b", 428 | "--num-buffers", 429 | default=5, 430 | type=int, 431 | help="number of buffers allocated for fs of this device", 432 | ) 433 | parser.add_argument( 434 | "-f", 435 | "--format", 436 | default="raw", 437 | help="disk image storage format: raw, nop, dflt, rnc, lz4", 438 | ) 439 | parser.add_argument( 440 | "-v", "--verbose", default=False, action="store_true", help="be more verbose" 441 | ) 442 | parser.add_argument( 443 | "-e", "--pack-entity", default="tracks", help="what to pack: 'cyls' or 'tracks'" 444 | ) 445 | 446 | return parser.parse_args() 447 | 448 | 449 | def main(): 450 | # parse args and init logging 451 | args = parse_args() 452 | # setup logging 453 | level = logging.DEBUG if args.verbose else logging.INFO 454 | logging.basicConfig(format=LOG_FORMAT, level=level) 455 | # extract disk geo 456 | try: 457 | geo = parse_geo(args.geometry, args.format) 458 | return mkromdisk( 459 | args.out_disk, 460 | from_dir=args.dir, 461 | from_image=args.image, 462 | temp_prefix=args.temp_prefix, 463 | geometry=geo, 464 | dostype=args.dostype, 465 | boot_prio=args.boot_prio, 466 | image_format=args.format, 467 | pack_entity=args.pack_entity, 468 | num_buffers=args.num_buffers, 469 | ) 470 | except IOError as e: 471 | logging.error("FAILED with %s", e) 472 | return 1 473 | 474 | 475 | # ----- entry point ----- 476 | if __name__ == "__main__": 477 | sys.exit(main()) 478 | -------------------------------------------------------------------------------- /src/unpacker/inflate.s: -------------------------------------------------------------------------------- 1 | * 2 | * inflate.asm 3 | * 4 | * Fixed for Amiga native asm syntax by phx / English Amiga Board. 5 | * 6 | * Decompression of DEFLATE streams, as produced by zip/gzip/pkzip and 7 | * specified in RFC 1951 "DEFLATE Compressed Data Format Specification". 8 | * 9 | * Usage: Optionally configure the OPT_xxx options below at build time; 10 | * at run time 'bsr inflate' with arguments: 11 | * a4 = output buffer, a5 = input stream 12 | * a6 = *end* of temporary storage area (only if OPT_STORAGE_OFFSTACK) 13 | * All register values (including arguments) are preserved. 14 | * 15 | * Space requirements: 638-930 bytes code; 2044-2940 bytes stack. 16 | * (NB1. Above ranges are [No Optimisations]-[All Optimisations]) 17 | * (NB2. Stack space can be relocated to a separately-specified storage 18 | * area, see OPT_STORAGE_OFFSTACK below) 19 | * 20 | * Timings: With all Optimisation Options enabled (see below) this routine 21 | * will decompress on a basic 7MHz 68000 at ~25kB/s. An AmigaDOS track of 22 | * data (5.5kB) is processed in ~220ms. This is only fractionally slower than 23 | * the track can be fetched from disk, hence there is scope for a 24 | * decompressing loader to keep CPU and disk both at near 100% utilisation. 25 | * 26 | * Written & released by Keir Fraser 27 | * 28 | * This is free and unencumbered software released into the public domain. 29 | * See the file COPYING for more details, or visit . 30 | * 31 | 32 | * Optimisation Option #1: 33 | * Avoid long Huffman-tree walks by indexing the first 8 bits of each codeword 34 | * in a 256-entry lookup table. This shortens all walks by 8 steps and since 35 | * the most common codes are less than 8 bits, most tree walks are avoided. 36 | * Also pre-shifts selected symbols in the code->symbol table, ready to be used 37 | * as indexes into further lookup tables. 38 | * SPEEDUP: 41% (c.w. no Options); COST: 122 bytes code, 896 bytes stack */ 39 | ifnd OPT_TABLE_LOOKUP 40 | OPT_TABLE_LOOKUP = 1 41 | endc 42 | 43 | * Optimisation Option #2: 44 | * Inline functions in the main decode loop to avoid all BSR/RTS pairs. 45 | * SPEEDUP: 15% (on top of Option #1); COST: 164 bytes code 46 | ifnd OPT_INLINE_FUNCTIONS 47 | OPT_INLINE_FUNCTIONS = 1 48 | endc 49 | 50 | * Optimisation Option #3: 51 | * Unroll the copy loop for tuples by one iteration 52 | * (so two bytes are copied per iteration). 53 | * SPEEDUP: ~1% (on top of Options #1 and #2); COST: 6 bytes code 54 | ifnd OPT_UNROLL_COPY_LOOP 55 | OPT_UNROLL_COPY_LOOP = 1 56 | endc 57 | 58 | * Storage Option: 59 | * All but 12 bytes of this routine's space requirement can be allocated 60 | * off stack, in a data area specified in register a6. 61 | * If this option is set then inflate must be called with a6 pointing at 62 | * the *end* of the reserved storage area (+2032 or +2928 bytes, depending 63 | * on whether OPT_TABLE_LOOKUP is enabled). 64 | * SPEEDUP: none; COST: -2 bytes code (makes code slightly smaller) 65 | ifnd OPT_STORAGE_OFFSTACK 66 | OPT_STORAGE_OFFSTACK = 0 67 | endc 68 | 69 | * By default all lookup/conversion tables are generated on-the-fly on every 70 | * call to inflate. In some cases this can be very inefficient. 71 | * If this option is enabled then two new routines are generated: At start-of- 72 | * day call 'inflate_gentables' with a6 pointing to the *end* of a 6000-byte 73 | * block of memory. Then call 'inflate_fromtables' instead of 'inflate', with 74 | * a6 still pointing to the end of the pre-generated memory block. 75 | * SPEEDUP: variable; COST: 116 bytes code 76 | ifnd OPT_PREGENERATE_TABLES 77 | OPT_PREGENERATE_TABLES = 0 78 | endc 79 | 80 | ifne OPT_STORAGE_OFFSTACK 81 | aS equr a6 82 | else 83 | aS equr sp 84 | endc 85 | 86 | ; Longest possible code. 87 | MAX_CODE_LEN = 16 88 | 89 | ; (Maximum) alphabet sizes. 90 | nr_codelen_symbols = 19 91 | nr_litlen_symbols = 288 92 | nr_distance_symbols = 32 93 | 94 | ; Alphabet-description stream for a static Huffman block (BTYPE=01b). 95 | static_huffman_prefix: 96 | dc.b $ff,$5b,$00,$6c,$03,$36,$db 97 | dc.b $b6,$6d,$db,$b6,$6d,$db,$b6 98 | dc.b $cd,$db,$b6,$6d,$db,$b6,$6d 99 | dc.b $db,$a8,$6d,$ce,$8b,$6d,$3b 100 | 101 | ifne OPT_TABLE_LOOKUP 102 | 103 | * Number of bytes required for code-lookup table/tree: 104 | * - 256 2-byte entries for the 8-bit lookup table 105 | * - Worst-case only 8 symbols decode directly in the table and all the rest 106 | * are in a tree hanging off one table entry. This tree requires 107 | * (nr_symbols-8)-1 internal 4-byte nodes. 108 | LOOKUP_BYTES_CODELEN = 256*2+(nr_codelen_symbols-9)*4 109 | LOOKUP_BYTES_LITLEN = 256*2+(nr_litlen_symbols-9)*4 110 | LOOKUP_BYTES_DISTANCE = 256*2+(nr_distance_symbols-9)*4 111 | 112 | ; a0 = len[], a1 = nodes[], d0 = nr_symbols 113 | ; d1 = symbol beyond which all symbols get <<2 114 | ; a2-a3 are scratched 115 | build_code: 116 | movem.l d0-d7,-(aS) 117 | 118 | ; Allocate space for bl_count[]/next_code[] array on stack. 119 | moveq #(MAX_CODE_LEN+1)/2,d1 120 | moveq #0,d2 121 | .1: move.l d2,-(aS) 122 | dbf d1,.1 123 | 124 | ; Count occurrences of each code length into bl_count[] array. 125 | subq.w #1,d0 126 | move.w d0,d1 127 | move.l a0,a2 ; a2 = &len[0] 128 | .2: move.b (a2)+,d2 ; d2 = len[i] 129 | ifd MC68020 130 | addq.w #1,(aS,d2.w*2) 131 | else 132 | add.b d2,d2 133 | addq.w #1,(aS,d2.w) ; bl_count[len[i]]++ 134 | endif 135 | dbf d1,.2 136 | 137 | ; Calculate next_code[] start values for each code length. 138 | move.l aS,a2 ; a2 = bl_count[] / next_code[] 139 | moveq #MAX_CODE_LEN-1,d1 140 | moveq #0,d2 ; d2 = code 141 | move.w d2,(aS) ; bl_count[0] = 0, ignore zero-length codes 142 | .3: add.w (a2),d2 143 | add.w d2,d2 ; code = (code + bl_count[i-1]) << 1 144 | move.w d2,(a2)+ ; next_code[i] = code 145 | dbf d1,.3 146 | 147 | ; Create the Huffman-code lookup tree 148 | move.w d0,d1 149 | moveq #127,d4 ; d4 = next_node = 127 150 | move.l a0,a2 ; a2 = &len[0] 151 | build_code_loop: 152 | moveq #0,d5 153 | move.b (a2)+,d5 ; d5 = len[i] / *len++ 154 | beq.b build_code_next 155 | subq.w #1,d5 156 | move.w d5,d6 157 | ifd MC68020 158 | move.w (aS,d6.w*2),d3 159 | addq.w #1,(aS,d6.w*2) 160 | else 161 | add.w d6,d6 162 | move.w (aS,d6.w),d3 ; d3 = code = next_code[len[i]]++ 163 | addq.w #1,(aS,d6.w) 164 | move.w d5,d6 165 | endif 166 | 167 | moveq #0,d2 168 | .1: lsr.w #1,d3 169 | roxl.w #1,d2 170 | dbf d6,.1 ; d5 = codelen-1; d2 = reversed code 171 | move.b d2,d3 172 | add.w d3,d3 ; d3 = table offset 173 | move.w d0,d6 174 | sub.w d1,d6 ; d6 = symbol 175 | cmp.w (((MAX_CODE_LEN+1)/2)+1)*4+6(aS),d6 ; symbol > saved d1.w? 176 | bls .2 177 | lsl.w #2,d6 ; symbol <<= 2 if so 178 | .2: cmp.b #9-1,d5 179 | bpl codelen_gt_8 180 | 181 | codelen_le_8: ; codelen <= 8: leaf in table entry(s) 182 | lsl.w #3,d6 183 | or.b d5,d6 ; d6 = (symbol<<3) | (codelen-1) 184 | moveq #0,d2 185 | addq.b #2,d5 186 | bset d5,d2 ; d2 = 1<<(codelen+1) [table step] 187 | move.w d2,d7 188 | neg.w d7 189 | and.w #511,d7 190 | or.w d7,d3 ; d3 = last table offset 191 | .1: move.w d6,(a1,d3.w) 192 | sub.w d2,d3 193 | bpl .1 194 | bra build_code_next 195 | 196 | codelen_gt_8: ; codelen > 8: requires a tree walk 197 | lsr.w #8,d2 198 | subq.b #8,d5 ; Skip the first 8 bits of code 199 | lea (a1,d3.w),a3 ; pnode = table entry 200 | 201 | .1: ; Walk through *pnode. 202 | move.w (a3),d7 ; d3 = *pnode 203 | bne .2 204 | ; Link missing: Create a new internal node 205 | addq.w #1,d4 206 | move.w d4,d7 207 | bset #15,d7 208 | move.w d7,(a3) ; *pnode = ++next_node | INTERNAL 209 | .2: ; Take left or right branch depending on next code bit 210 | lsr.b #1,d2 211 | addx.w d7,d7 212 | ifd MC68020 213 | lea (a1,d7.w*2),a3 214 | else 215 | add.w d7,d7 216 | lea (a1,d7.w),a3 ; pnode = next_bit ? &node->r : &node->l 217 | endif 218 | dbf d5,.1 219 | 220 | ; Insert the current symbol as a new leaf node 221 | move.w d6,(a3) ; *pnode = sym 222 | build_code_next: 223 | dbf d1,build_code_loop 224 | 225 | lea (((MAX_CODE_LEN+1)/2)+1)*4(aS),aS 226 | movem.l (aS)+,d0-d7 227 | rts 228 | 229 | ; d5-d6/a5 = stream, a0 = tree 230 | ; d0.w = result, d1.l = scratch 231 | STREAM_NEXTSYMBOL macro 232 | moveq #0,d0 ; 4 233 | moveq #7,d1 ; 4 234 | cmp.b d1,d6 ; 4 235 | bhi .1\@ ; 10 236 | ; Less than 8 bits cached; grab another byte from the stream 237 | move.b (a5)+,d0 ; [8] 238 | lsl.w d6,d0 ; [~14] 239 | or.w d0,d5 ; [4] s->cur |= *p++ << s->nr 240 | addq.b #8,d6 ; [4] s->nr += 8 241 | moveq #0,d0 ; [4] 242 | .1\@: ; Use next input byte as index into code lookup table 243 | move.b d5,d0 ; 4 244 | ifd MC68020 245 | move.w (a0,d0.w*2),d0 246 | else 247 | add.w d0,d0 ; 4 248 | move.w (a0,d0.w),d0 ; 14 249 | endif 250 | bpl .4\@ ; 10 (taken) 251 | ; Code is longer than 8 bits: do the remainder via a tree walk 252 | lsr.w #8,d5 253 | subq.b #8,d6 ; consume 8 bits from the stream 254 | .2\@: ; stream_next_bits(1), inlined & optimised 255 | subq.b #1,d6 ; 4 cy 256 | bpl .3\@ ; 10 cy (taken) 257 | move.b (a5)+,d5 ; [8 cy] 258 | moveq #7,d6 ; [4 cy] 259 | .3\@: lsr.w #1,d5 ; 8 cy 260 | addx.w d0,d0 ; 4 cy 261 | ifd MC68020 262 | move.w (a0,d0.w*2),d0 263 | else 264 | add.w d0,d0 ; 4 cy 265 | move.w (a0,d0.w),d0 ; 14 cy 266 | endif 267 | bmi .2\@ ; 10 cy (taken); loop on INTERNAL flag 268 | bra .5\@ ; TOTAL LOOP CYCLES ~= 54 269 | .4\@: ; Symbol found directly: consume bits and return symbol 270 | and.b d0,d1 ; 4 271 | addq.b #1,d1 ; 4 272 | lsr.w d1,d5 ; ~16 consume bits from the stream 273 | sub.b d1,d6 ; 4 274 | lsr.w #3,d0 ; 12 d0 = symbol 275 | .5\@: ; ~94 CYCLES TOTAL [+ 34] 276 | endm 277 | 278 | else ; !OPT_TABLE_LOOKUP 279 | 280 | * Number of bytes required for code-lookup tree: 281 | * - Every binary tree with N leaves has N-1 internal nodes. 282 | * - Internal nodes require 4 bytes each. Leaves are free. 283 | LOOKUP_BYTES_CODELEN = ((nr_codelen_symbols)-1)*4 284 | LOOKUP_BYTES_LITLEN = ((nr_litlen_symbols)-1)*4 285 | LOOKUP_BYTES_DISTANCE = ((nr_distance_symbols)-1)*4 286 | 287 | ; a0 = len[], a1 = nodes[], d0 = nr_symbols 288 | ; a2-a3 are scratched 289 | build_code: 290 | movem.l d0-d5,-(aS) 291 | 292 | ; Allocate space for bl_count[]/next_code[] array on stack. 293 | moveq #(MAX_CODE_LEN+1)/2,d1 294 | moveq #0,d2 295 | .1: move.l d2,-(aS) 296 | dbf d1,.1 297 | 298 | ; Count occurrences of each code length into bl_count[] array. 299 | subq.w #1,d0 300 | move.w d0,d1 301 | move.l a0,a2 ; a2 = &len[0] 302 | .2: move.b (a2)+,d2 ; d2 = len[i] 303 | add.b d2,d2 304 | addq.w #1,(aS,d2.w) ; bl_count[len[i]]++ 305 | dbf d1,.2 306 | 307 | ; Calculate next_code[] start values for each code length. 308 | move.l aS,a2 ; a2 = bl_count[] / next_code[] 309 | moveq #MAX_CODE_LEN-1,d1 310 | moveq #0,d2 ; d2 = code 311 | move.w d2,(aS) ; bl_count[0] = 0, ignore zero-length codes 312 | .3: add.w (a2),d2 313 | add.w d2,d2 ; code = (code + bl_count[i-1]) << 1 314 | move.w d2,(a2)+ ; next_code[i] = code 315 | dbf d1,.3 316 | 317 | ; Create the Huffman-code lookup tree 318 | move.w d0,d1 319 | moveq #0,d4 ; d4 = next_node 320 | move.l a0,a2 ; a2 = &len[0] 321 | build_code_loop: 322 | moveq #0,d5 323 | move.b (a2)+,d5 ; d5 = len[i] / *len++ 324 | beq build_code_next 325 | subq.w #1,d5 326 | add.w d5,d5 327 | move.w (aS,d5.w),d3 ; d3 = code = next_code[len[i]]++ 328 | addq.w #1,(aS,d5.w) 329 | lsr.w #1,d5 330 | ; Walk down the tree, creating nodes as necessary 331 | moveq #0,d2 ; d2 = 0 (root node) 332 | bra .2 333 | 334 | .1: ; Walk through *pnode. 335 | move.w (a3),d2 ; d2 = *pnode 336 | bne .2 337 | ; Link missing: Create a new internal node 338 | addq.w #1,d4 339 | move.w d4,d2 340 | bset #15,d2 341 | move.w d2,(a3) ; *pnode = ++next_node | INTERNAL 342 | .2: ; Take left or right branch depending on next code bit 343 | lsl.w #2,d2 344 | btst d5,d3 345 | beq .3 346 | addq.w #2,d2 347 | .3: lea (a1,d2.w),a3 ; pnode = next_bit ? &node->r : &node->l 348 | dbf d5,.1 349 | 350 | ; Insert the current symbol as a new leaf node 351 | move.w d0,d2 352 | sub.w d1,d2 353 | move.w d2,(a3) ; *pnode = sym 354 | build_code_next: 355 | dbf d1,build_code_loop 356 | 357 | lea (((MAX_CODE_LEN+1)/2)+1)*4(aS),aS 358 | movem.l (aS)+,d0-d5 359 | rts 360 | 361 | ; d5-d6/a5 = stream, a0 = tree 362 | ; d0.w = result 363 | STREAM_NEXTSYMBOL macro 364 | moveq #0,d0 365 | .1\@: ; stream_next_bits(1), inlined & optimised 366 | subq.b #1,d6 ; 4 cy 367 | bpl .2\@ ; 10 cy (taken) 368 | move.b (a5)+,d5 ; [8 cy] 369 | moveq #7,d6 ; [4 cy] 370 | .2\@: lsr.w #1,d5 ; 8 cy 371 | addx.w d0,d0 ; 4 cy 372 | add.w d0,d0 ; 4 cy 373 | move.w (a0,d0.w),d0 ; 14 cy 374 | bmi .1\@ ; 10 cy (taken) loop on INTERNAL flag set 375 | ; TOTAL LOOP CYCLES ~= 54 376 | endm 377 | 378 | endc 379 | 380 | ; d1.b = nr, d5-d6/a5 = stream [fetched_bits/nr_fetched_bits/inp] 381 | ; d0.w = result 382 | STREAM_NEXTBITS macro 383 | .1\@: moveq #0,d0 384 | cmp.b d1,d6 385 | bpl .2\@ ; while (s->nr < nr) 386 | move.b (a5)+,d0 387 | lsl.l d6,d0 388 | or.l d0,d5 ; s->cur |= *p++ << s->nr 389 | addq.b #8,d6 ; s->nr += 8 390 | bra .1\@ 391 | .2\@: bset d1,d0 392 | subq.w #1,d0 ; d0 = (1<cur & ((1<cur >>= nr 395 | sub.b d1,d6 ; s->nr -= nr 396 | endm 397 | 398 | ifne OPT_INLINE_FUNCTIONS 399 | INLINE_stream_next_bits macro 400 | STREAM_NEXTBITS 401 | endm 402 | INLINE_stream_next_symbol macro 403 | STREAM_NEXTSYMBOL 404 | endm 405 | else 406 | INLINE_stream_next_bits macro 407 | bsr stream_next_bits 408 | endm 409 | INLINE_stream_next_symbol macro 410 | bsr stream_next_symbol 411 | endm 412 | endc 413 | 414 | stream_next_bits: 415 | STREAM_NEXTBITS 416 | rts 417 | 418 | ; d5-d6/a5 = stream, a4 = output 419 | ; d0-d1 are scratched 420 | uncompressed_block: 421 | ifne OPT_TABLE_LOOKUP 422 | ; Push whole bytes back into input stream. 423 | lsr.w #3,d6 424 | sub.w d6,a5 425 | else 426 | ; No need to push bytes back into input stream because stream_next_ 427 | ; {bits,symbol} will never leave more than 7 bits cached. 428 | endc 429 | ; Snap input stream up to byte boundary. 430 | moveq #0,d5 431 | moveq #0,d6 432 | ; Read block header and copy LEN bytes. 433 | moveq #16,d1 434 | bsr stream_next_bits ; LEN 435 | addq.w #2,a5 ; skip NLEN 436 | subq.w #1,d0 ; d0.w = len-1 (for dbf) 437 | .1: move.b (a5)+,(a4)+ 438 | dbf d0,.1 439 | rts 440 | 441 | o_hdist = 0 442 | o_hlit = 2 443 | o_lens = o_hlit+2 444 | o_codelen_tree = o_lens+nr_litlen_symbols+nr_distance_symbols 445 | ifne OPT_TABLE_LOOKUP 446 | ; Lit/len and codelen lookup structures share space. 447 | o_litlen_tree = o_codelen_tree 448 | else 449 | o_litlen_tree = o_codelen_tree+LOOKUP_BYTES_CODELEN 450 | endc 451 | o_dist_tree = o_litlen_tree+LOOKUP_BYTES_LITLEN 452 | o_stream = o_dist_tree+LOOKUP_BYTES_DISTANCE 453 | o_frame = o_stream+3*4 454 | ifne OPT_STORAGE_OFFSTACK 455 | o_mode = o_frame 456 | else 457 | ; Allow for BSR return address from decoder 458 | o_mode = o_frame+4 459 | endc 460 | o_dist_extra = o_mode+4 461 | o_length_extra = o_dist_extra+30*4 462 | 463 | ; d5-d6/a5 = stream, a4 = output 464 | ; d0-d4,a0-a3 are scratched 465 | static_huffman: 466 | movem.l d5-d6/a5,-(aS) 467 | moveq #0,d5 468 | moveq #0,d6 469 | lea static_huffman_prefix(pc),a5 470 | move.w #o_stream/4-2,d0 471 | bra huffman 472 | 473 | ; d5-d6/a5 = stream, a4 = output 474 | ; d0-d4,a0-a3 are scratched 475 | dynamic_huffman: 476 | ; Allocate stack space for len[] and node[] arrays 477 | move.w #o_frame/4-2,d0 478 | huffman: 479 | moveq #0,d1 480 | .1: move.l d1,-(aS) 481 | dbf d0,.1 482 | ; HLIT = stream_next_bits(5) + 257 483 | moveq #5,d1 484 | bsr stream_next_bits 485 | add.w #257,d0 486 | move.w d0,-(aS) 487 | ; HDIST = stream_next_bits(5) + 1 488 | moveq #5,d1 489 | bsr stream_next_bits 490 | addq.w #1,d0 491 | move.w d0,-(aS) 492 | ; HCLEN = stream_next_bits(4) + 4 493 | moveq #4,d1 494 | bsr stream_next_bits 495 | addq.w #4-1,d0 ; -1 for dbf 496 | ; Initialise len[] array with code-length symbol code lengths 497 | lea codelen_order(pc),a1 498 | lea o_lens(aS),a0 ; a0 = len[] 499 | moveq #0,d2 500 | move.w d0,d3 501 | .2: moveq #3,d1 502 | bsr stream_next_bits 503 | move.b (a1)+,d2 504 | move.b d0,(a0,d2.w) ; len[codelen_order[i++]] = next_bits(3) 505 | dbf d3,.2 506 | ; Build the codelen_tree 507 | lea o_codelen_tree(aS),a1 508 | moveq #nr_codelen_symbols,d0 509 | ifne OPT_TABLE_LOOKUP 510 | moveq #127,d1 ; don't left-shift any symbols 511 | endc 512 | bsr build_code ; build_code(codelen_tree) 513 | ; Read the literal/length & distance code lengths 514 | move.w o_hlit(aS),d2 515 | add.w o_hdist(aS),d2 516 | subq.w #1,d2 ; d2 = hlit+hdist-1 517 | move.l a0,a2 ; a2 = len[] 518 | move.l a1,a0 ; a0 = a1 = codelen_tree 519 | c_loop: 520 | INLINE_stream_next_symbol 521 | cmp.b #16,d0 522 | bmi .c_lit 523 | beq .c_16 524 | cmp.b #17,d0 525 | beq .c_17 526 | .c_18: ; 18: Repeat zero N times 527 | moveq #7,d1 528 | bsr stream_next_bits 529 | addq.w #11-3,d0 530 | bra .1 531 | .c_17: ; 17: repeat zero N times 532 | moveq #3,d1 533 | bsr stream_next_bits 534 | .1: moveq #0,d1 535 | bra .2 536 | .c_16: ; 16: repeat previous N times 537 | moveq #2,d1 538 | bsr stream_next_bits 539 | move.b -1(a2),d1 540 | .2: addq.w #3-1,d0 541 | sub.w d0,d2 542 | .3: move.b d1,(a2)+ 543 | dbf d0,.3 544 | bra .4 545 | .c_lit: ; 0-16: Literal symbol 546 | move.b d0,(a2)+ 547 | .4: dbf d2,c_loop 548 | ; Build the lit/len and distance trees 549 | ifne OPT_TABLE_LOOKUP 550 | ; Clear the codelen tree (shared space with lit/len tree). 551 | ; NB. a0 = a1 = codelen_tree = litlen_tree 552 | moveq #0,d0 553 | move.w #LOOKUP_BYTES_CODELEN/4-1,d1 554 | .5: move.l d0,(a0)+ 555 | dbf d1,.5 556 | ; litlen_tree (= codelen_tree) is already in a1, and now zeroed. 557 | else 558 | lea o_litlen_tree(aS),a1 559 | endc 560 | lea o_lens(aS),a0 561 | move.w o_hlit(aS),d0 562 | ifne OPT_TABLE_LOOKUP 563 | move.w #256,d1 564 | move.w d1,d4 ; left-shift symbols >127 (i.e., lengths) 565 | endc 566 | bsr build_code ; build_code(litlen_tree) 567 | add.w d0,a0 568 | lea o_dist_tree(aS),a1 569 | move.w o_hdist(aS),d0 570 | ifne OPT_TABLE_LOOKUP 571 | moveq #0,d1 ; left-shift all symbols (i.e., distances) 572 | endc 573 | bsr build_code ; build_code(dist_tree) 574 | ; Reinstate the main stream if we used the static prefix 575 | tst.l o_stream+8(aS) 576 | beq decode_loop 577 | movem.l o_stream(aS),d5-d6/a5 578 | ; Now decode the compressed data stream up to EOB 579 | decode_loop: 580 | lea o_litlen_tree(aS),a0 581 | ; START OF HOT LOOP 582 | .1: INLINE_stream_next_symbol ; litlen_sym 583 | ifne OPT_TABLE_LOOKUP 584 | cmp.w d4,d0 ; 4 cy (d4.w = 256) 585 | else 586 | cmp.w #256,d0 ; 8 cy 587 | endc 588 | bpl .2 ; 8 cy 589 | ; 0-255: Byte literal 590 | move.b d0,(a4)+ ; 8 cy 591 | bra .1 ; 10 cy 592 | ; END OF HOT LOOP -- 30 + ~108 + [34] = ~160 CYCLES 593 | .done: ; 256: End-of-block: we're done 594 | lea o_frame(aS),aS 595 | rts 596 | .2: beq .done 597 | ; 257+: pair 598 | ifeq OPT_TABLE_LOOKUP 599 | lsl.w #2,d0 600 | endc 601 | lea o_length_extra-257*4(aS),a2 602 | add.w d0,a2 603 | move.w (a2)+,d1 604 | INLINE_stream_next_bits 605 | add.w (a2),d0 606 | move.w d0,d3 ; d3 = cplen 607 | lea o_dist_tree(aS),a0 608 | INLINE_stream_next_symbol ; dist_sym 609 | ifeq OPT_TABLE_LOOKUP 610 | lsl.w #2,d0 611 | endc 612 | lea o_dist_extra(aS),a2 613 | add.w d0,a2 614 | move.w (a2)+,d1 615 | INLINE_stream_next_bits 616 | add.w (a2),d0 ; d0 = cpdst 617 | move.l a4,a0 618 | sub.w d0,a0 ; a0 = outp - cpdst 619 | ifne OPT_UNROLL_COPY_LOOP 620 | lsr.w #1,d3 621 | bcs .4 622 | subq.w #1,d3 623 | .3: move.b (a0)+,(a4)+ 624 | .4: move.b (a0)+,(a4)+ 625 | else 626 | subq.w #1,d3 627 | .3: move.b (a0)+,(a4)+ 628 | endc 629 | dbf d3,.3 630 | bra decode_loop 631 | 632 | ifeq OPT_INLINE_FUNCTIONS 633 | stream_next_symbol: 634 | STREAM_NEXTSYMBOL 635 | rts 636 | endc 637 | 638 | ; Build a base/extra-bits table on the stack 639 | build_base_extrabits: 640 | ifeq OPT_STORAGE_OFFSTACK 641 | move.l (sp)+,a0 642 | endc 643 | .1: move.w d0,d3 644 | lsr.w d4,d3 645 | subq.w #1,d3 646 | bpl .2 647 | moveq #0,d3 648 | .2: moveq #0,d1 649 | bset d3,d1 650 | sub.w d1,d2 651 | move.w d2,-(aS) 652 | move.w d3,-(aS) 653 | dbf d0,.1 654 | ifeq OPT_STORAGE_OFFSTACK 655 | jmp (a0) 656 | else 657 | rts 658 | endc 659 | 660 | dispatch: ; Decoder dispatch table. 661 | dc.b uncompressed_block-uncompressed_block 662 | dc.b static_huffman-uncompressed_block 663 | dc.b dynamic_huffman-uncompressed_block 664 | 665 | codelen_order: ; Order of code lengths for the code length alphabet. 666 | dc.b 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 667 | 668 | ; a4 = output, a5 = input, all regs preserved 669 | ; a6 = *end* of storage area (only if OPT_STORAGE_OFFSTACK) 670 | xdef _inflate 671 | _inflate: 672 | movem.l d0-d6/a0-a5,-(aS) 673 | 674 | ; Build the base/extra-bits table 675 | move.l #258,d2 676 | move.l d2,-(aS) 677 | addq.w #1,d2 678 | moveq #27,d0 679 | moveq #2,d4 680 | bsr build_base_extrabits 681 | 682 | ; Build the base/extra-bits table 683 | move.w #32769,d2 684 | moveq #29,d0 685 | moveq #1,d4 686 | bsr build_base_extrabits 687 | 688 | ; Initialise the stream 689 | moveq #0,d5 ; d5 = stream: fetched data 690 | moveq #0,d6 ; d6 = stream: nr fetched bits 691 | 692 | .1: ; Process a block: Grab the BTYPE|BFINAL 3-bit code 693 | moveq #3,d1 694 | bsr stream_next_bits 695 | move.l d0,-(aS) 696 | ; Dispatch to the correct decoder for this block 697 | lsr.b #1,d0 698 | move.b dispatch(pc,d0.w),d0 699 | lea uncompressed_block(pc),a0 700 | jsr (a0,d0.w) 701 | ; Keep going until we see BFINAL=1 702 | move.l (aS)+,d0 703 | lsr.b #1,d0 704 | bcc .1 705 | 706 | ; Pop the base/extra-bits lookup tables 707 | lea (30+29)*4(aS),aS 708 | 709 | movem.l (aS)+,d0-d6/a0-a5 710 | rts 711 | 712 | ifne OPT_PREGENERATE_TABLES 713 | pregen_static_huffman: 714 | lea -o_frame(aS),aS ; frame pre-generated; skip over it 715 | move.w #256,d4 716 | bra decode_loop 717 | pregen_dynamic_huffman: 718 | move.l (aS),d0 719 | lea -3000(aS),aS ; move to dynamic-huffman frame 720 | move.l d0,(aS) ; copy o_mode into it 721 | bsr dynamic_huffman 722 | lea 3000(aS),aS 723 | rts 724 | 725 | ; Pre-generate conversion tables for Inflate. 726 | ; a6 = Pointer to end of 6000-byte block of memory to contain 727 | ; pre-generated tables. All registers preserved. 728 | inflate_gentables: 729 | movem.l a5-a6,-(sp) 730 | lea pregen_dummy_block(pc),a5 731 | bsr inflate ; static block 732 | lea -3000(aS),aS 733 | lea pregen_dummy_block(pc),a5 734 | bsr inflate ; dynamic block 735 | movem.l (sp)+,a5-a6 736 | rts 737 | 738 | ; Inflate, using pre-generated tables. 739 | ; a4 = output, a5 = input, all regs preserved 740 | ; a6 = *end* of 6000-byte pre-generated storage area 741 | inflate_fromtables: 742 | movem.l d0-d6/a0-a5,-(aS) 743 | 744 | ; Skip the pre-generated base/extra-bits lookup tables 745 | lea -(30+29)*4(aS),aS 746 | 747 | ; Initialise the stream 748 | moveq #0,d5 ; d5 = stream: fetched data 749 | moveq #0,d6 ; d6 = stream: nr fetched bits 750 | 751 | .1: ; Process a block: Grab the BTYPE|BFINAL 3-bit code 752 | moveq #3,d1 753 | bsr stream_next_bits 754 | move.l d0,-(aS) 755 | ; Dispatch to the correct decoder for this block 756 | and.b #$fe,d0 757 | move.w pregen_dispatch(pc,d0.w),d0 758 | lea uncompressed_block(pc),a0 759 | jsr (a0,d0.w) 760 | ; Keep going until we see BFINAL=1 761 | move.l (aS)+,d0 762 | lsr.b #1,d0 763 | bcc .1 764 | 765 | ; Pop the base/extra-bits lookup tables 766 | lea (30+29)*4(aS),aS 767 | 768 | movem.l (aS)+,d0-d6/a0-a5 769 | rts 770 | 771 | pregen_dispatch: 772 | dc.w uncompressed_block - uncompressed_block 773 | dc.w pregen_static_huffman - uncompressed_block 774 | dc.w pregen_dynamic_huffman - uncompressed_block 775 | pregen_dummy_block: ; A single static block containing EOB symbol only 776 | dc.b $03,$00 777 | endc ; OPT_PREGENERATE_TABLES 778 | 779 | end 780 | --------------------------------------------------------------------------------