From 9d0191c7ab55cc394f47a2fb5d7f82f0dd687ef0 Mon Sep 17 00:00:00 2001 From: Will Sowerbutts Date: Sat, 27 Dec 2014 01:12:09 +0000 Subject: [PATCH] cpm-loader: Tools to make a CP/M application which boots the Fuzix kernel --- Kernel/Makefile | 5 +- Kernel/cpm-loader/Makefile | 11 ++ Kernel/cpm-loader/cpmload.s | 38 +++++++ Kernel/cpm-loader/makecpmloader.c | 160 ++++++++++++++++++++++++++++++ 4 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 Kernel/cpm-loader/Makefile create mode 100644 Kernel/cpm-loader/cpmload.s create mode 100644 Kernel/cpm-loader/makecpmloader.c diff --git a/Kernel/Makefile b/Kernel/Makefile index 562aa1ef..a029b333 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -158,13 +158,16 @@ tools/decbdragon: tools/decbdragon.c tools/bintomdv: tools/bintomdv.c +cpm-loader/cpmload.bin: cpm-loader/cpmload.s cpm-loader/makecpmloader.c + +make -C cpm-loader + tools/makejv3: tools/makejv3.c ifneq (,$(filter $(CPU),z80 z180)) # matches CPU = z80 or z180 uzi.ihx: target $(OBJS) platform-$(TARGET)/uzi.lnk $(CROSS_LD) -n -k $(LIBZ80) -f platform-$(TARGET)/uzi.lnk -fuzix.bin: uzi.ihx tools/analysemap tools/memhogs tools/binman tools/bintomdv +fuzix.bin: uzi.ihx tools/analysemap tools/memhogs tools/binman tools/bintomdv cpm-loader/cpmload.bin tools/analysemap hogs.txt diff --git a/Kernel/cpm-loader/Makefile b/Kernel/cpm-loader/Makefile new file mode 100644 index 00000000..6138e478 --- /dev/null +++ b/Kernel/cpm-loader/Makefile @@ -0,0 +1,11 @@ +all: makecpmloader cpmload.bin + +makecpmloader: makecpmloader.c + +cpmload.bin: cpmload.s + $(CROSS_AS) $(ASOPTS) cpmload.s + sdldz80 -nmi cpmload.rel + makebin -p cpmload.ihx > cpmload.bin + +clean: + rm -f *~ *.rst *.lst *.asm *.bin *.sym *.rel *.map *.ihx makecpmloader diff --git a/Kernel/cpm-loader/cpmload.s b/Kernel/cpm-loader/cpmload.s new file mode 100644 index 00000000..807bde62 --- /dev/null +++ b/Kernel/cpm-loader/cpmload.s @@ -0,0 +1,38 @@ +; 2014-12-21 Will Sowerbutts + +.module cpmload +.area _LOADER (ABS) + + ; CP/M will load us at 0x0100 + ; We want to relocate our payload (the kernel) and jump into it. + ; We put a small stub at the very bottom of memory, copy the kernel into + ; place above us, then jump into it. + + .org 0x100 + di + ; copy ourselves to the very bottom of memory -- 0x00 upwards + ld de, #0 + ld hl, #(doload) ; start of loader code + ld bc, #(endloader-doload) ; length of our loader + ldir ; copy copy copy! + ld hl, (load_address) + ld sp, hl ; stash copy of entry vector in SP for now + ex de, hl ; load_address to de + ld hl, #payload_start + ld bc, (load_length) + jp 0 ; jump and perform copy in low memory + + ; this code gets copied to .org 0 +doload: + ldir ; copy image into correct place + ld hl, #0 + add hl, sp ; recover entry vector + jp (hl) ; run image +endloader: ; end of code to copy + + ; the data is in a trailer, with a 6-byte header: +load_address: + .ds 2 +load_length: + .ds 2 +payload_start: diff --git a/Kernel/cpm-loader/makecpmloader.c b/Kernel/cpm-loader/makecpmloader.c new file mode 100644 index 00000000..2f285401 --- /dev/null +++ b/Kernel/cpm-loader/makecpmloader.c @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 2014-12-21 William R Sowerbutts + * + * Create a CP/M program which boots Fuzix. + * + * The CP/M executable file contains a small loader program, followed by some + * data describing where the kernel should be loaded, and then the kernel + * itself. + * + * When run, the CP/M program copies a small (20 byte) program to the very + * start of memory, which then copies the kernel to the correct address and + * executes it. + * + * + * Syntax: makecpmloader
+ * + * loader -- normally cpmload.bin, assembled from cpmload.s + * kernel -- fuzix.bin from the Fuzix build process + * address -- the kernel's load address (normally 0x88) + * output -- output file name + * + */ + +#define LOADER_TRIM 0x100 /* CP/M loads us at 0x100 */ +#define MAX_LOADER_LENGTH 0x200 /* Loader should really be teeny tiny */ +#define MAX_KERNEL_LENGTH 0x10000 /* Kernel can't be larger than 64KB ... yet ... */ +#define BYTESWAP16(x) (((x>>8) & 0xFF) | ((x & 0xFF) << 8)) + +char loader_data[MAX_LOADER_LENGTH]; +char kernel_data[MAX_KERNEL_LENGTH]; + +struct loader_trailer { + unsigned short load_address; + unsigned short load_length; +}; + +int load_file(const char *filename, char *buffer, int buffer_len) +{ + int fd, length, r; + + fd = open(filename, O_RDONLY); + + if(fd < 0){ + fprintf(stderr, "Cannot open \"%s\": %s\n", filename, strerror(errno)); + return -1; + } + + length = 0; + while(1){ + r = read(fd, &buffer[length], buffer_len - length); + if(r == 0) /* EOF */ + break; + else if(r > 0) + length += r; + else{ + fprintf(stderr, "Cannot read \"%s\": %s\n", filename, strerror(errno)); + close(fd); + return -1; + } + if(length == buffer_len){ + fprintf(stderr, "Out of buffer space reading \"%s\"\n", filename); + close(fd); + return -1; + } + } + + close(fd); + return length; +} + +int parse_int(char *str) +{ + int base = 10; + long val; + char *end; + + end = str; + if(strncasecmp(end, "0x", 2) == 0){ + base = 16; + end = end + 2; + } + + val = strtol(end, &end, base); + + if(*end == 0 && *str != 0) + return val; + else{ + fprintf(stderr, "Cannot parse load address \"%s\"\n", str); + return -1; + } +} + +int main(int argc, char **argv) +{ + int loader_length; + int load_address; + int kernel_length; + int fd; + struct loader_trailer trailer; + + if(argc < 5){ + fprintf(stderr, "%s [loader] [kernel] [address] [output]\n", argv[0]); + return 1; + } + + loader_length = load_file(argv[1], loader_data, MAX_LOADER_LENGTH); + if(loader_length < 0) + return 1; + if(loader_length <= LOADER_TRIM){ + fprintf(stderr, "Loader image is too small\n"); + return 1; + } + + kernel_length = load_file(argv[2], kernel_data, MAX_KERNEL_LENGTH); + if(kernel_length < 0) + return 1; + + load_address = parse_int(argv[3]); + + if(load_address < 0 || load_address > 0xFFFF){ + fprintf(stderr, "Bad load address.\n"); + return 1; + } + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + trailer.load_length = kernel_length; + trailer.load_address = load_address; +#else + trailer.load_length = BYTESWAP16(kernel_length); + trailer.load_address = BYTESWAP16(load_address); +#endif + + fd = open(argv[4], O_WRONLY | O_CREAT | O_TRUNC, 0666); + if(fd < 0){ + fprintf(stderr, "Cannot open \"%s\": %s\n", argv[4], strerror(errno)); + return 1; + } + + if(write(fd, &loader_data[LOADER_TRIM], loader_length - LOADER_TRIM) != (loader_length - LOADER_TRIM) || + write(fd, &trailer, sizeof(trailer)) != sizeof(trailer) || + write(fd, kernel_data, kernel_length) != kernel_length){ + fprintf(stderr, "Write to \"%s\" failed: %s\n", argv[4], strerror(errno)); + close(fd); + unlink(argv[4]); + return 1; + } + + close(fd); + + return 0; +} -- 2.34.1