Merge pull request #195 from davidgiven/dtrg-cpm

Various CP/M improvement
This commit is contained in:
David Given 2019-06-11 23:38:29 +02:00 committed by GitHub
commit 3d877ae3f8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 395 additions and 330 deletions

View file

@ -1,3 +1,5 @@
.obj
.sass-cache
_site
.vscode

View file

@ -27,17 +27,8 @@ vars.plats_with_tests = {
"pc86",
}
local plat_packages = {}
local test_packages = {}
for _, p in ipairs(vars.plats) do
plat_packages[#plat_packages+1] = "plat/"..p.."+pkg"
end
for _, p in ipairs(vars.plats_with_tests) do
test_packages[#test_packages+1] = "plat/"..p.."/tests+tests"
end
installable {
name = "ack",
name = "ack-common",
map = {
"lang/basic/src+pkg",
"lang/cem/cemcom.ansi+pkg",
@ -53,6 +44,31 @@ installable {
"util/led+pkg",
"util/misc+pkg",
"util/opt+pkg",
},
}
local plat_packages = {}
local test_packages = {}
for _, p in ipairs(vars.plats) do
local pkg = "plat/"..p.."+pkg"
plat_packages[#plat_packages+1] = pkg
installable {
name = "ack-"..p,
map = {
"+ack-common",
pkg,
},
}
end
for _, p in ipairs(vars.plats_with_tests) do
test_packages[#test_packages+1] = "plat/"..p.."/tests+tests"
end
installable {
name = "ack",
map = {
"+ack-common",
"examples+pkg",
plat_packages
},

View file

@ -7,6 +7,7 @@
-- is = { set of rule types which made the target }
-- }
local posix = require("posix")
local emitter = {}
local rules = {}
local targets = {}
@ -544,7 +545,7 @@ local function definerule(rulename, types, cb)
local args = {}
for propname, typespec in pairs(types) do
if not e[propname] then
if e[propname] == nil then
if not typespec.optional and (typespec.default == nil) then
error(string.format("missing mandatory property '%s'", propname))
end

View file

@ -2475,5 +2475,6 @@ with hlreg
gen shld {label,".reghp"}
pat trp
kills ALL
gen Call {label,".trp"}
with areg
kills ALL
gen Call {label,".trp"}

View file

@ -22,12 +22,13 @@ begtext:
! BDOS and crash CP/M. We cheat by comparing only high bytes
! of each address.
lxi b, __end
lda 0x0007
mov c, a ! c = high byte of BDOS address
mov a, b ! a = high byte of _end
mov c, a ! c = high byte of BDOS address
lda _cpm_ram+1 ! a = high byte of top of BSS, a.k.a. _end
cmp c
jnc __exit ! emergency exit if a >= c
lxi d, noroom
mvi c, 9
jnc 0x0005 ! print error and exit if a >= c
! We have to clear the bss. (argify requires it.)
@ -46,6 +47,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 +58,15 @@ begtext:
call .rst_init
#endif
! Now the 'heap'.
lhld 0x0006 ! BDOS entry point
lxi d, -0x806 ! offset to start of CCP
dad d
shld _cpm_ramtop
! C-ify the command line at 0x0080.
lxi h, 0x0080
mov a, m ! a = length of command line
cpi 0x7F ! 127-byte command lines...
@ -120,15 +131,40 @@ end_of_argify:
mvi h, 0
push h
call __m_a_i_n
! FALLTHROUGH
.define _cpm_exit, EXIT, __exit
_cpm_exit:
EXIT:
__exit:
stc ! set carry bit
jnc _cpm_warmboot ! warm boot if not set
saved_sp = . + 1
lxi sp, 0 ! patched on startup
ret
! Emergency exit routine.
.define EXIT, __exit
EXIT:
__exit:
rst 0
.define _cpm_warmboot
_cpm_warmboot = 0
! Special CP/M stuff.
.define _cpm_fcb, _cpm_fcb2
_cpm_fcb = 0x005c
_cpm_fcb2 = 0x006c
.define _cpm_ramtop
.comm _cpm_ramtop, 2
.define _cpm_default_dma
_cpm_default_dma = 0x0080
.define _cpm_iobyte
_cpm_iobyte = 3
.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.)
@ -139,8 +175,7 @@ __exit:
! Some magic data. All EM systems need these.
.define .trppc, .ignmask, _errno
.comm .trppc, 2
.define .ignmask, _errno
.comm .ignmask, 2
.comm _errno, 2
@ -153,11 +188,10 @@ envp: .space 2 ! envp array (always empty, must be after argv)
! These are used specifically by the i80 code generator.
.define .trapproc, .retadr, .retadr1
.define .retadr, .retadr1
.define .bcreg, .areg
.define .tmp1, .fra, block1, block2, block3
.comm .trapproc, 2
.comm .retadr, 2 ! used to save return address
.comm .retadr1, 2 ! reserve
.comm .bcreg, 2
@ -168,5 +202,10 @@ block1: .space 4 ! used by 32 bits divide and
block2: .space 4 ! multiply routines
block3: .space 4 ! must be contiguous (.comm doesn't guarantee this)
.sect .data
.define _cpm_ram
_cpm_ram: .data2 __end
.sect .rom
progname: .asciz 'ACKCPM'
noroom: .ascii 'No room$'

View file

@ -3,19 +3,19 @@
# $Revision$
var w=2
var wa=1
var wa=2
var p=2
var pa=1
var pa=2
var s=2
var sa=1
var sa=2
var l=4
var la=1
var la=2
var f=4
var fa=1
var fa=2
var d=8
var da=1
var da=2
var x=8
var xa=1
var xa=2
var ARCH=i80
var PLATFORM=cpm
var PLATFORMDIR={EM}/share/ack/{PLATFORM}

View file

@ -1,66 +1,117 @@
/*
* 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;
/* EM requires 2-byte alignment, even on the i80, so we can't declare these
* structures to contain uint16_ts. Use U16() to access them. */
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;
uint8_t r[3];
}
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;
uint8_t al[16];
}
DIRE;
typedef struct
{
uint8_t spt[2]; /* 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 */
uint8_t dsm[2]; /* maximum block number */
uint8_t drm[2]; /* maximum directory entry number */
uint8_t al[2]; /* directory allocation bitmap */
uint8_t cks[2]; /* checksum vector size */
uint8_t off[2]; /* number of reserved tracks */
}
DPB;
/* Access an unaligned field (see above). */
#define U16(ptr) (*(uint16_t*)(ptr))
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: if the CCP hasn't been overwritten, returns to it; otherwise does
* a warmboot. */
extern void cpm_exit(void);
/* Extends cpm_ramtop over the CCP, for a little extra space. */
extern void cpm_overwrite_ccp(void);
/* 0 */ extern void cpm_warmboot(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_drive(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,20 @@
#
! $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
xchg ! DE = HL
ret

View file

@ -3,23 +3,18 @@
! $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
.define efdivz, eiund, efund, econv, estack, eheap, eillins, eoddz
.define ecase, ememflt, ebadptr, ebadpc, ebadlae, ebadmon, ebadlin, ebadgto
.define eunimpl
.sect .text
.define EARRAY, ERANGE, ESET, EIOVFL, EFOVFL, EFUNFL, EIDIVZ, EIDIVZ
.define EFDIVZ, EIUND, EFUND, ECONV, ESTACK, EHEAP, EILLINS, EODDZ
.define ECASE, EMEMFLT, EBADPTR, EBADPC, EBADLAE, EBADMON, EBADLIN, EBADGTO
.define EUNIMPL
! Trap routine
! Expects trap number on stack.
! Expects trap number in A, and must be called directly from the code
! where execution should resume (for those traps which support it).
! Just returns if trap has to be ignored.
! Otherwise it calls a user-defined trap handler if provided.
! When no user-defined trap handler is provided or when the user-defined
@ -51,130 +46,7 @@
EBADGTO = 27
EUNIMPL = 63 ! unimplemented em-instruction called
earray: lxi h,EARRAY
push h
call .trp
ret
erange: lxi h,ERANGE
push h
call .trp
ret
eset: lxi h,ESET
push h
call .trp
ret
eiovfl: lxi h,EIOVFL
push h
call .trp
ret
efovfl: lxi h,EFOVFL
push h
call .trp
ret
efunfl: lxi h,EFUNFL
push h
call .trp
ret
eidivz: lxi h,EIDIVZ
push h
call .trp
ret
efdivz: lxi h,EFDIVZ
push h
call .trp
ret
eiund: lxi h,EIUND
push h
call .trp
ret
efund: lxi h,EFUND
push h
call .trp
ret
econv: lxi h,ECONV
push h
call .trp
ret
estack: lxi h,ESTACK
push h
call .trp
ret
eheap: lxi h,EHEAP
push h
call .trp
ret
eillins:lxi h,EILLINS
push h
call .trp
ret
eoddz: lxi h,EODDZ
push h
call .trp
ret
ecase: lxi h,ECASE
push h
call .trp
ret
ememflt:lxi h,EMEMFLT
push h
call .trp
ret
ebadptr:lxi h,EBADPTR
push h
call .trp
ret
ebadpc: lxi h,EBADPC
push h
call .trp
ret
ebadlae:lxi h,EBADLAE
push h
call .trp
ret
ebadmon:lxi h,EBADMON
push h
call .trp
ret
ebadlin:lxi h,EBADLIN
push h
call .trp
ret
ebadgto:lxi h,EBADGTO
push h
call .trp
ret
eunimpl:lxi h,EUNIMPL
push h
call .trp
ret
.trp:
pop h
xthl
push h ! trap number and return address exchanged
mov a,l
cpi 16
jnc 3f ! jump if trap cannot be ignored
@ -192,10 +64,12 @@ eunimpl:lxi h,EUNIMPL
ret ! OGEN DICHT EN ... SPRING!!!
3:
lhld .trapproc ! user defined trap handler?
.define .trapproc
.trapproc = . + 1
lxi h, 0 ! user defined trap handler held inline here
mov a,l
ora h
jz 1f ! jump if there was not
jz 1f ! jump if there was not
xra a
sta .trapproc ! .trapproc := 0
sta .trapproc+1
@ -206,15 +80,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$"

13
plat/cpm/libsys/asm.h Normal file
View file

@ -0,0 +1,13 @@
#ifndef ASM_H
#define ASM_H
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
#endif

View file

@ -1,42 +1,34 @@
/* $Source$
* $State$
* $Revision$
*/
#include <cpm.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#define OUT_OF_MEMORY (void*)(-1) /* sbrk returns this on failure */
#define STACK_BUFFER 128 /* number of bytes to leave for stack */
extern char _end[1];
static char* current = _end;
extern uint8_t _end[1];
int brk(void* newend)
{
/* We determine the amount of free memory by looking at the address of the
* BDOS vector at 0x0006. */
char* memtop = (char*) ((*(unsigned char*)0x0007)<<8);
char* p = newend;
uint8_t* p = newend;
if ((p >= memtop) ||
if ((p >= cpm_ramtop) ||
(p < _end))
return -1;
current = p;
cpm_ram = (uint8_t*)p;
return 0;
}
void* sbrk(int increment)
{
char* old;
char* new;
uint8_t* old;
uint8_t* 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,110 @@
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_drive",
[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 trap_calls = {
"EARRAY",
"ERANGE",
"ESET",
"EIOVFL",
"EFOVFL",
"EFUNFL",
"EIDIVZ",
"EFDIVZ",
"EIUND",
"EFUND",
"ECONV",
"ESTACK",
"EHEAP",
"EILLINS",
"EODDZ",
"ECASE",
"EMEMFLT",
"EBADPTR",
"EBADPC",
"EBADLAE",
"EBADMON",
"EBADLIN",
"EBADGTO",
"EUNIMPL",
}
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
for _, name in pairs(trap_calls) do
generated[#generated+1] = normalrule {
name = name,
ins = { "./make_trap.sh" },
outleaves = { name..".s" },
commands = {
"%{ins[1]} "..name:lower().." "..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

@ -0,0 +1,12 @@
#
#include "asm.h"
.define _cpm_overwrite_ccp
_cpm_overwrite_ccp:
mvi a, 0xaf ! 0xaf = xor a = clear carry bit
sta _cpm_exit
lhld _cpm_ramtop
lxi d, 0x800
dad d
shld _cpm_ramtop
ret

View file

@ -0,0 +1,10 @@
#!/bin/sh
cat <<EOF
#
#include "asm.h"
.define _$2
_$2:
mvi a, $1
jmp call_bdos
EOF

10
plat/cpm/libsys/make_trap.sh Executable file
View file

@ -0,0 +1,10 @@
#!/bin/sh
cat <<EOF
#
#include "asm.h"
.define $1
$1:
mvi a, $2
jmp .trp
EOF

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