Extend the CP/M libsys with a full set of (hopefully correct) 2.2 BDOS calls.

This commit is contained in:
David Given 2019-06-10 23:54:23 +02:00
parent 3f938d651b
commit d0967e683b
8 changed files with 240 additions and 164 deletions

View file

@ -27,7 +27,7 @@ begtext:
mov c, a ! c = high byte of BDOS address mov c, a ! c = high byte of BDOS address
mov a, b ! a = high byte of _end mov a, b ! a = high byte of _end
cmp c cmp c
jnc __exit ! emergency exit if a >= c rnc ! emergency exit if a >= c
! We have to clear the bss. (argify requires it.) ! 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). ! 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 lxi sp, stack + STACKSIZE
! Initialise the rsts (if desired). ! Initialise the rsts (if desired).
@ -54,6 +57,11 @@ begtext:
call .rst_init call .rst_init
#endif #endif
! Now the 'heap'.
lxi h, __end
shld _cpm_ram
! C-ify the command line at 0x0080. ! C-ify the command line at 0x0080.
lxi h, 0x0080 lxi h, 0x0080
@ -119,15 +127,38 @@ end_of_argify:
lhld argc ! slightly evil lhld argc ! slightly evil
mvi h, 0 mvi h, 0
push h push h
call __m_a_i_n push h ! return address is 0
! FALLTHROUGH 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. ! Emergency exit routine.
.define EXIT, __exit .define EXIT, __exit, _cpm_exit
EXIT: EXIT = 0
__exit: __exit = 0
rst 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 ! Define symbols at the beginning of our various segments, so that we can find
! them. (Except .text, which has already been done.) ! them. (Except .text, which has already been done.)

View file

@ -1,66 +1,114 @@
/* #ifndef CPM_H
* unistd.h - standard system calls #define CPM_H
*/
/* $Id$ */
#ifndef _CPM_H
#define _CPM_H
#include <stdint.h> #include <stdint.h>
/* These interface provides a very bare-bones interface to the CP/M BDOS. Set typedef struct
* 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
{ {
CPM_BDOS_SYSTEM_RESET, uint8_t dr;
CPM_BDOS_CONSOLE_INPUT, uint8_t f[11];
CPM_BDOS_CONSOLE_OUTPUT, uint8_t ex;
CPM_BDOS_READER_INPUT, uint8_t s1;
CPM_BDOS_PUNCH_OUTPUT, uint8_t s2;
CPM_BDOS_LIST_OUTPUT, uint8_t rc;
CPM_BDOS_CONSOLE_IO, uint8_t d[16];
CPM_BDOS_GET_IO_BYTE, uint8_t cr;
CPM_BDOS_SET_IO_BYTE, uint16_t r;
CPM_BDOS_PRINT_STRING, uint8_t r2;
CPM_BDOS_READ_CONSOLE_BUFFER, }
CPM_BDOS_GET_CONSOLE_STATUS, FCB;
CPM_BDOS_GET_VERSION_NUMBER,
CPM_BDOS_RESET_DISK_SYSTEM, typedef struct
CPM_BDOS_SELECT_DISK, {
CPM_BDOS_OPEN_FILE, uint8_t dr;
CPM_BDOS_CLOSE_FILE, uint8_t src[11];
CPM_BDOS_SEARCHFIRST, uint8_t _padding[5];
CPM_BDOS_SEARCHNEXT, uint8_t dest[11];
CPM_BDOS_DELETE_FILE, }
CPM_BDOS_READ_SEQ, RCB;
CPM_BDOS_WRITE_SEQ,
CPM_BDOS_MAKE_FILE, typedef struct
CPM_BDOS_RENAME_FILE, {
CPM_BDOS_GET_LOGIN_VECTOR, uint8_t us;
CPM_BDOS_GET_CURRENT_DISK, uint8_t f[11];
CPM_BDOS_SET_DMA_ADDRESS, uint8_t ex;
CPM_BDOS_GET_ALLOC_VECTOR, uint8_t s[2];
CPM_BDOS_WRITE_PROTECT, uint8_t rc;
CPM_BDOS_GET_RO_VECTOR, union
CPM_BDOS_SET_FILE_ATTR, {
CPM_BDOS_GET_DISK_PARMS, uint8_t al8[16];
CPM_BDOS_SETGET_USER, uint16_t al16[8];
CPM_BDOS_READ_RANDOM, }
CPM_BDOS_WRITE_RANDOM, al;
CPM_BDOS_GET_FILE_SIZE, }
CPM_BDOS_SET_RANDOM 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 #endif

View file

@ -1,55 +1,21 @@
# #
! $Source$ #include "asm.h"
! $State$
! $Revision$
! Declare segments (the order is important). ! Calls a BDOS routine and returns the result.
! a = BDOS opcode
.sect .text .define call_bdos
.sect .rom call_bdos:
.sect .data pop h ! pop return address
.sect .bss pop d ! pop parameter (possibly junk)
push d
push h
.sect .text push b ! save FP as the BDOS will corrupt it
mov c, a ! move opcode to C
call 0x0005
pop b ! restore FP
! Calls a BDOS routine.
.define _cpm_bdos
_cpm_bdos:
push b
lda _cpm_a_register
lhld _cpm_bc_register
mov b, h
mov c, l
lhld _cpm_de_register
mov d, h mov d, h
mov e, l 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 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

View file

@ -3,12 +3,8 @@
! $State$ ! $State$
! $Revision$ ! $Revision$
! Declare segments (the order is important). #
#include "asm.h"
.sect .text
.sect .rom
.sect .data
.sect .bss
.define .trp .define .trp
.define earray, erange, eset, eiovfl, efovfl, efunfl, eidivz, eidivz .define earray, erange, eset, eiovfl, efovfl, efunfl, eidivz, eidivz
@ -16,8 +12,6 @@
.define ecase, ememflt, ebadptr, ebadpc, ebadlae, ebadmon, ebadlin, ebadgto .define ecase, ememflt, ebadptr, ebadpc, ebadlae, ebadmon, ebadlin, ebadgto
.define eunimpl .define eunimpl
.sect .text
! Trap routine ! Trap routine
! Expects trap number on stack. ! Expects trap number on stack.
! Just returns if trap has to be ignored. ! Just returns if trap has to be ignored.
@ -206,15 +200,11 @@ eunimpl:lxi h,EUNIMPL
pop d pop d
ret ret
1: 1:
lxi h, 6 lxi d, text
push h mvi c, 9 ! write $-terminated string
lxi h, text call 0x0005
push h rst 0 ! abend
lxi h, 1
push h
call _write
jmp EXIT
.sect .rom .sect .rom
text: .ascii "TRAP!\n" text: .ascii "TRAP!\r\n$"

View file

@ -1,8 +1,4 @@
/* $Source$ #include <cpm.h>
* $State$
* $Revision$
*/
#include <stdlib.h> #include <stdlib.h>
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
@ -11,7 +7,6 @@
#define STACK_BUFFER 128 /* number of bytes to leave for stack */ #define STACK_BUFFER 128 /* number of bytes to leave for stack */
extern char _end[1]; extern char _end[1];
static char* current = _end;
int brk(void* newend) int brk(void* newend)
{ {
@ -24,7 +19,7 @@ int brk(void* newend)
(p < _end)) (p < _end))
return -1; return -1;
current = p; cpm_ram = p;
return 0; return 0;
} }
@ -34,9 +29,9 @@ void* sbrk(int increment)
char* new; char* new;
if (increment == 0) if (increment == 0)
return current; return cpm_ram;
old = current; old = cpm_ram;
new = old + increment; new = old + increment;
if ((increment > 0) && (new <= old)) if ((increment > 0) && (new <= old))

View file

@ -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 { acklibrary {
name = "lib", name = "lib",
srcs = { srcs = {
"./*.c", "./*.c",
"./*.s", "./*.s",
generated
}, },
deps = { deps = {
"lang/cem/libcc.ansi/headers+headers", "lang/cem/libcc.ansi/headers+headers",
"plat/cpm/include+headers", "plat/cpm/include+headers",
"+internal",
}, },
vars = { vars = {
plat = "cpm" plat = "cpm"

View file

@ -36,18 +36,14 @@ ssize_t read(int fd, void* buffer, size_t count)
/* Read one line from the console. */ /* Read one line from the console. */
((unsigned char*)buffer)[-2] = before_n; ((unsigned char*)buffer)[-2] = before_n;
cpm_bc_register = CPM_BDOS_READ_CONSOLE_BUFFER; cpm_readline((uint8_t*)buffer - 2);
cpm_de_register = (uint16_t)(char*)buffer - 2;
cpm_bdos();
before_n = ((unsigned char*)buffer)[-1]; before_n = ((unsigned char*)buffer)[-1];
((char*)buffer)[before_n] = '\n'; /* Append '\n'. */ ((char*)buffer)[before_n] = '\n'; /* Append '\n'. */
((short*)buffer)[-1] = save; /* Give back borrowed bytes. */ ((short*)buffer)[-1] = save; /* Give back borrowed bytes. */
/* Echo '\n' to console. */ /* Echo '\n' to console. */
cpm_bc_register = CPM_BDOS_PRINT_STRING; cpm_printstring("\r\n$");
cpm_de_register = (uint16_t)"\r\n$";
cpm_bdos();
return (int)before_n + 1; return (int)before_n + 1;
} }

View file

@ -10,16 +10,9 @@
void _sys_write_tty(char c) void _sys_write_tty(char c)
{ {
cpm_bc_register = CPM_BDOS_CONSOLE_OUTPUT; cpm_conout(c);
cpm_de_register = c;
cpm_bdos();
if (c == '\n') if (c == '\n')
{ cpm_conout(c);
cpm_bc_register = CPM_BDOS_CONSOLE_OUTPUT;
cpm_de_register = '\r';
cpm_bdos();
}
} }
ssize_t write(int fd, void* buffer, size_t count) ssize_t write(int fd, void* buffer, size_t count)
@ -37,14 +30,10 @@ ssize_t write(int fd, void* buffer, size_t count)
/* Write all data. */ /* Write all data. */
i = 0; i = count;
while (i < count) while (i--)
{
_sys_write_tty(*p++); _sys_write_tty(*p++);
i++;
}
/* No failures. */ /* No failures. */
return count; return count;