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 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.)

View file

@ -1,66 +1,114 @@
/*
* unistd.h - standard system calls
*/
/* $Id$ */
#ifndef _CPM_H
#define _CPM_H
#ifndef CPM_H
#define CPM_H
#include <stdint.h>
/* 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

View file

@ -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

View file

@ -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$"

View file

@ -1,8 +1,4 @@
/* $Source$
* $State$
* $Revision$
*/
#include <cpm.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
@ -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))

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 {
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"

View file

@ -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;
}

View file

@ -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. */