From d0967e683b286e01fce486ddfb53d6013ed67c03 Mon Sep 17 00:00:00 2001 From: David Given Date: Mon, 10 Jun 2019 23:54:23 +0200 Subject: [PATCH] Extend the CP/M libsys with a full set of (hopefully correct) 2.2 BDOS calls. --- plat/cpm/boot.s | 47 +++++++++-- plat/cpm/include/cpm.h | 168 ++++++++++++++++++++++++-------------- plat/cpm/libsys/_bdos.s | 62 ++++---------- plat/cpm/libsys/_trap.s | 24 ++---- plat/cpm/libsys/brk.c | 13 +-- plat/cpm/libsys/build.lua | 63 +++++++++++++- plat/cpm/libsys/read.c | 8 +- plat/cpm/libsys/write.c | 19 +---- 8 files changed, 240 insertions(+), 164 deletions(-) diff --git a/plat/cpm/boot.s b/plat/cpm/boot.s index 40ab0d775..a1e99ed00 100644 --- a/plat/cpm/boot.s +++ b/plat/cpm/boot.s @@ -27,7 +27,7 @@ begtext: mov c, a ! c = high byte of BDOS address mov a, b ! a = high byte of _end cmp c - jnc __exit ! emergency exit if a >= c + rnc ! emergency exit if a >= c ! We have to clear the bss. (argify requires it.) @@ -46,6 +46,9 @@ begtext: ! Set up the stack (now it's been cleared, since it's in the BSS). + lxi h, 0 + dad sp + shld saved_sp ! save old stack pointer lxi sp, stack + STACKSIZE ! Initialise the rsts (if desired). @@ -54,8 +57,13 @@ begtext: call .rst_init #endif + ! Now the 'heap'. + + lxi h, __end + shld _cpm_ram + ! C-ify the command line at 0x0080. - + lxi h, 0x0080 mov a, m ! a = length of command line cpi 0x7F ! 127-byte command lines... @@ -119,16 +127,39 @@ end_of_argify: lhld argc ! slightly evil mvi h, 0 push h - call __m_a_i_n - ! FALLTHROUGH + push h ! return address is 0 + jmp __m_a_i_n + +.define _cpm_fastexit +_cpm_fastexit: +saved_sp = _cpm_fastexit + 1 + lxi sp, 0 ! patched on startup + ret ! Emergency exit routine. -.define EXIT, __exit -EXIT: -__exit: - rst 0 +.define EXIT, __exit, _cpm_exit +EXIT = 0 +__exit = 0 +_cpm_exit = 0 +! Special CP/M stuff. + +.define _cpm_fcb +_cpm_fcb = 0x005c + +.define _cpm_ram +.comm _cpm_ram, 2 +.define _cpm_ramtop +_cpm_ramtop = 0x0001 + +.define _cpm_default_dma +_cpm_default_dma = 0x0080 + +.define _cpm_cmdlinelen, _cpm_cmdline +_cpm_cmdlinelen = 0x0080 +_cpm_cmdline = 0x0081 + ! Define symbols at the beginning of our various segments, so that we can find ! them. (Except .text, which has already been done.) diff --git a/plat/cpm/include/cpm.h b/plat/cpm/include/cpm.h index bdda5ed23..9a354eb03 100644 --- a/plat/cpm/include/cpm.h +++ b/plat/cpm/include/cpm.h @@ -1,66 +1,114 @@ -/* - * unistd.h - standard system calls - */ -/* $Id$ */ - -#ifndef _CPM_H -#define _CPM_H +#ifndef CPM_H +#define CPM_H #include -/* These interface provides a very bare-bones interface to the CP/M BDOS. Set - * the following four variables as you wish, call cpm_bdos(), and the contents - * of the variables will have been updated accordingly. */ - -extern uint8_t cpm_a_register; -extern uint16_t cpm_bc_register; -extern uint16_t cpm_de_register; -extern uint16_t cpm_hl_register; - -extern void cpm_bdos(void); - -/* Describes the available CP/M BDOS calls. They're a fairly conservative set - * taken from the CP/M 2.0 manual. */ - -enum +typedef struct { - CPM_BDOS_SYSTEM_RESET, - CPM_BDOS_CONSOLE_INPUT, - CPM_BDOS_CONSOLE_OUTPUT, - CPM_BDOS_READER_INPUT, - CPM_BDOS_PUNCH_OUTPUT, - CPM_BDOS_LIST_OUTPUT, - CPM_BDOS_CONSOLE_IO, - CPM_BDOS_GET_IO_BYTE, - CPM_BDOS_SET_IO_BYTE, - CPM_BDOS_PRINT_STRING, - CPM_BDOS_READ_CONSOLE_BUFFER, - CPM_BDOS_GET_CONSOLE_STATUS, - CPM_BDOS_GET_VERSION_NUMBER, - CPM_BDOS_RESET_DISK_SYSTEM, - CPM_BDOS_SELECT_DISK, - CPM_BDOS_OPEN_FILE, - CPM_BDOS_CLOSE_FILE, - CPM_BDOS_SEARCHFIRST, - CPM_BDOS_SEARCHNEXT, - CPM_BDOS_DELETE_FILE, - CPM_BDOS_READ_SEQ, - CPM_BDOS_WRITE_SEQ, - CPM_BDOS_MAKE_FILE, - CPM_BDOS_RENAME_FILE, - CPM_BDOS_GET_LOGIN_VECTOR, - CPM_BDOS_GET_CURRENT_DISK, - CPM_BDOS_SET_DMA_ADDRESS, - CPM_BDOS_GET_ALLOC_VECTOR, - CPM_BDOS_WRITE_PROTECT, - CPM_BDOS_GET_RO_VECTOR, - CPM_BDOS_SET_FILE_ATTR, - CPM_BDOS_GET_DISK_PARMS, - CPM_BDOS_SETGET_USER, - CPM_BDOS_READ_RANDOM, - CPM_BDOS_WRITE_RANDOM, - CPM_BDOS_GET_FILE_SIZE, - CPM_BDOS_SET_RANDOM -}; + uint8_t dr; + uint8_t f[11]; + uint8_t ex; + uint8_t s1; + uint8_t s2; + uint8_t rc; + uint8_t d[16]; + uint8_t cr; + uint16_t r; + uint8_t r2; +} +FCB; + +typedef struct +{ + uint8_t dr; + uint8_t src[11]; + uint8_t _padding[5]; + uint8_t dest[11]; +} +RCB; + +typedef struct +{ + uint8_t us; + uint8_t f[11]; + uint8_t ex; + uint8_t s[2]; + uint8_t rc; + union + { + uint8_t al8[16]; + uint16_t al16[8]; + } + al; +} +DIRE; + +typedef struct +{ + uint16_t spt; /* number of 128-byte sectors per track */ + uint8_t bsh; /* block shift; 3=1kB, 4=2kB, 5=4kB etc */ + uint8_t blm; /* block mask; 0x07=1kB, 0x0f=2kB, 0x1f=4k etc */ + uint8_t exm; /* extent mask */ + uint16_t dsm; /* maximum block number */ + uint16_t drm; /* maximum directory entry number */ + uint16_t al; /* directory allocation bitmap */ + uint16_t cks; /* checksum vector size */ + uint16_t off; /* number of reserved tracks */ +} +DPB; + +extern FCB cpm_fcb; /* primary FCB */ +extern FCB cpm_fcb2; /* secondary FCB (special purpose) */ +extern uint8_t cpm_iobyte; + +extern uint8_t cpm_default_dma[128]; /* also contains the parsed command line */ +extern uint8_t* cpm_ram; +extern uint8_t* cpm_ramtop; +extern uint8_t cpm_cmdlinelen; +extern char cpm_cmdline[0x7f]; + +/* Special: longjmps out of the program. Don't use if you've overwritten the + * CCP. */ +extern void cpm_fastexit(void); + +/* 0 */ extern void cpm_exit(void); +/* 1 */ extern uint8_t cpm_conin(void); +/* 2 */ extern void cpm_conout(uint8_t b); +/* 3 */ extern uint8_t cpm_auxin(void); +/* 4 */ extern void cpm_auxout(uint8_t b); +/* 5 */ extern void cpm_lstout(uint8_t b); +/* 6 */ extern uint8_t cpm_conio(uint8_t b); +/* 7 */ extern uint8_t cpm_get_iobyte(void); +/* 8 */ extern void cpm_set_iobyte(uint8_t iob); +/* 9 */ extern void cpm_printstring(const char* s); /* $-terminated */ +/* 10 */ extern uint8_t cpm_readline(uint8_t* buffer); +/* 11 */ extern uint8_t cpm_const(void); +/* 12 */ extern uint16_t cpm_get_version(void); +/* 13 */ extern void cpm_reset_disk_system(void); +/* 14 */ extern void cpm_select_disk(uint8_t disk); +/* 15 */ extern uint8_t cpm_open_file(FCB* fcb); +/* 16 */ extern uint8_t cpm_close_file(FCB* fcb); +/* 17 */ extern uint8_t cpm_findfirst(FCB* fcb); +/* 18 */ extern uint8_t cpm_findnext(FCB* fcb); +/* 19 */ extern uint8_t cpm_delete_file(FCB* fcb); +/* 20 */ extern uint8_t cpm_read_sequential(FCB* fcb); +/* 21 */ extern uint8_t cpm_write_sequential(FCB* fcb); +/* 22 */ extern uint8_t cpm_make_file(FCB* fcb); +/* 23 */ extern uint8_t cpm_rename_file(RCB* rcb); +/* 24 */ extern uint16_t cpm_get_login_vector(void); +/* 25 */ extern uint8_t cpm_get_current_drive(void); +/* 26 */ extern void cpm_set_dma(void* ptr); +/* 27 */ extern uint8_t* cpm_get_allocation_vector(void); +/* 28 */ extern void cpm_write_protect_drive(void); +/* 29 */ extern uint16_t cpm_get_readonly_vector(void); +/* 30 */ extern uint8_t cpm_set_file_attributes(FCB* fcb); +/* 31 */ extern DPB* cpm_get_dpb(void); +/* 32 */ extern uint8_t cpm_get_set_user(uint8_t user); +/* 33 */ extern uint8_t cpm_read_random(FCB* fcb); +/* 34 */ extern uint8_t cpm_write_random(FCB* fcb); +/* 35 */ extern void cpm_seek_to_end(FCB* fcb); +/* 36 */ extern void cpm_seek_to_seq_pos(FCB* fcb); +/* 37 */ extern uint8_t cpm_reset_drives(uint16_t drive_bitmap); +/* 40 */ extern uint8_t cpm_write_random_filled(FCB* fcb); #endif diff --git a/plat/cpm/libsys/_bdos.s b/plat/cpm/libsys/_bdos.s index 4a05dfcdf..9e136925e 100644 --- a/plat/cpm/libsys/_bdos.s +++ b/plat/cpm/libsys/_bdos.s @@ -1,55 +1,21 @@ # -! $Source$ -! $State$ -! $Revision$ +#include "asm.h" -! Declare segments (the order is important). +! Calls a BDOS routine and returns the result. +! a = BDOS opcode -.sect .text -.sect .rom -.sect .data -.sect .bss +.define call_bdos +call_bdos: + pop h ! pop return address + pop d ! pop parameter (possibly junk) + push d + push h -.sect .text - -! Calls a BDOS routine. - -.define _cpm_bdos -_cpm_bdos: - push b + push b ! save FP as the BDOS will corrupt it + mov c, a ! move opcode to C + call 0x0005 + pop b ! restore FP - lda _cpm_a_register - - lhld _cpm_bc_register - mov b, h - mov c, l - - lhld _cpm_de_register mov d, h mov e, l - - lhld _cpm_hl_register - - call 5 - - shld _cpm_hl_register - - mov h, d - mov l, e - shld _cpm_de_register - - mov h, b - mov l, c - shld _cpm_bc_register - - sta _cpm_a_register - - pop b - ret - -.sect .bss -.define _cpm_a_register, _cpm_bc_register, _cpm_de_register, _cpm_hl_register -.comm _cpm_a_register, 1 -.comm _cpm_bc_register, 2 -.comm _cpm_de_register, 2 -.comm _cpm_hl_register, 2 + ret \ No newline at end of file diff --git a/plat/cpm/libsys/_trap.s b/plat/cpm/libsys/_trap.s index a3836c998..678b9cc7f 100644 --- a/plat/cpm/libsys/_trap.s +++ b/plat/cpm/libsys/_trap.s @@ -3,12 +3,8 @@ ! $State$ ! $Revision$ -! Declare segments (the order is important). - -.sect .text -.sect .rom -.sect .data -.sect .bss +# +#include "asm.h" .define .trp .define earray, erange, eset, eiovfl, efovfl, efunfl, eidivz, eidivz @@ -16,8 +12,6 @@ .define ecase, ememflt, ebadptr, ebadpc, ebadlae, ebadmon, ebadlin, ebadgto .define eunimpl -.sect .text - ! Trap routine ! Expects trap number on stack. ! Just returns if trap has to be ignored. @@ -206,15 +200,11 @@ eunimpl:lxi h,EUNIMPL pop d ret 1: - lxi h, 6 - push h - lxi h, text - push h - lxi h, 1 - push h - call _write - jmp EXIT + lxi d, text + mvi c, 9 ! write $-terminated string + call 0x0005 + rst 0 ! abend .sect .rom -text: .ascii "TRAP!\n" +text: .ascii "TRAP!\r\n$" diff --git a/plat/cpm/libsys/brk.c b/plat/cpm/libsys/brk.c index 6c139dd2a..e9f13c1e6 100644 --- a/plat/cpm/libsys/brk.c +++ b/plat/cpm/libsys/brk.c @@ -1,8 +1,4 @@ -/* $Source$ - * $State$ - * $Revision$ - */ - +#include #include #include #include @@ -11,7 +7,6 @@ #define STACK_BUFFER 128 /* number of bytes to leave for stack */ extern char _end[1]; -static char* current = _end; int brk(void* newend) { @@ -24,7 +19,7 @@ int brk(void* newend) (p < _end)) return -1; - current = p; + cpm_ram = p; return 0; } @@ -34,9 +29,9 @@ void* sbrk(int increment) char* new; if (increment == 0) - return current; + return cpm_ram; - old = current; + old = cpm_ram; new = old + increment; if ((increment > 0) && (new <= old)) diff --git a/plat/cpm/libsys/build.lua b/plat/cpm/libsys/build.lua index c2a75a4c3..780dea626 100644 --- a/plat/cpm/libsys/build.lua +++ b/plat/cpm/libsys/build.lua @@ -1,12 +1,73 @@ +acklibrary { + name = "internal", + hdrs = { "./*.h" } +} + +local bdos_calls = { + [ 0] = "cpm_exit", + [ 1] = "cpm_conin", + [ 2] = "cpm_conout", + [ 3] = "cpm_auxin", + [ 4] = "cpm_auxout", + [ 5] = "cpm_lstout", + [ 6] = "cpm_conio", + [ 7] = "cpm_get_iobyte", + [ 8] = "cpm_set_iobyte", + [ 9] = "cpm_printstring", + [10] = "cpm_readline", + [11] = "cpm_const", + [12] = "cpm_get_version", + [13] = "cpm_reset_disk_system", + [14] = "cpm_select_disk", + [15] = "cpm_open_file", + [16] = "cpm_close_file", + [17] = "cpm_findfirst", + [18] = "cpm_findnext", + [19] = "cpm_delete_file", + [20] = "cpm_read_sequential", + [21] = "cpm_write_sequential", + [22] = "cpm_make_file", + [23] = "cpm_rename_file", + [24] = "cpm_get_login_vector", + [25] = "cpm_get_current_drive", + [26] = "cpm_set_dma", + [27] = "cpm_get_allocation_vector", + [28] = "cpm_write_protect_drive", + [29] = "cpm_get_readonly_vector", + [30] = "cpm_set_file_attributes", + [31] = "cpm_get_dpb", + [32] = "cpm_get_set_user", + [33] = "cpm_read_random", + [34] = "cpm_write_random", + [35] = "cpm_seek_to_end", + [36] = "cpm_seek_to_seq_pos", + [37] = "cpm_reset_drives", + [40] = "cpm_write_random_filled", +} + +local generated = {} +for n, name in pairs(bdos_calls) do + generated[#generated+1] = normalrule { + name = name, + ins = { "./make_bdos_call.sh" }, + outleaves = { name..".s" }, + commands = { + "%{ins[1]} "..n.." "..name.." > %{outs}" + } + } +end + acklibrary { name = "lib", srcs = { "./*.c", "./*.s", + generated }, deps = { "lang/cem/libcc.ansi/headers+headers", - "plat/cpm/include+headers", + "plat/cpm/include+headers", + "+internal", }, vars = { plat = "cpm" diff --git a/plat/cpm/libsys/read.c b/plat/cpm/libsys/read.c index df4a3ef3b..b04a13ee5 100644 --- a/plat/cpm/libsys/read.c +++ b/plat/cpm/libsys/read.c @@ -36,18 +36,14 @@ ssize_t read(int fd, void* buffer, size_t count) /* Read one line from the console. */ ((unsigned char*)buffer)[-2] = before_n; - cpm_bc_register = CPM_BDOS_READ_CONSOLE_BUFFER; - cpm_de_register = (uint16_t)(char*)buffer - 2; - cpm_bdos(); + cpm_readline((uint8_t*)buffer - 2); before_n = ((unsigned char*)buffer)[-1]; ((char*)buffer)[before_n] = '\n'; /* Append '\n'. */ ((short*)buffer)[-1] = save; /* Give back borrowed bytes. */ /* Echo '\n' to console. */ - cpm_bc_register = CPM_BDOS_PRINT_STRING; - cpm_de_register = (uint16_t)"\r\n$"; - cpm_bdos(); + cpm_printstring("\r\n$"); return (int)before_n + 1; } diff --git a/plat/cpm/libsys/write.c b/plat/cpm/libsys/write.c index 21dbc90c0..768e9b2eb 100644 --- a/plat/cpm/libsys/write.c +++ b/plat/cpm/libsys/write.c @@ -10,16 +10,9 @@ void _sys_write_tty(char c) { - cpm_bc_register = CPM_BDOS_CONSOLE_OUTPUT; - cpm_de_register = c; - cpm_bdos(); - + cpm_conout(c); if (c == '\n') - { - cpm_bc_register = CPM_BDOS_CONSOLE_OUTPUT; - cpm_de_register = '\r'; - cpm_bdos(); - } + cpm_conout(c); } ssize_t write(int fd, void* buffer, size_t count) @@ -37,13 +30,9 @@ ssize_t write(int fd, void* buffer, size_t count) /* Write all data. */ - i = 0; - while (i < count) - { + i = count; + while (i--) _sys_write_tty(*p++); - - i++; - } /* No failures. */