Compare commits

..

47 commits

Author SHA1 Message Date
David Given db5a32c68c
Merge pull request #288 from tkchia/tkchia/20230708
plat/linuxppc/emu: fix fctiw & fctiwz emulation for AArch64 host
2023-07-08 22:17:27 +02:00
tkchia 56ebd1409b plat/linuxppc/emu: get dump_state( ) to also dump FP registers 2023-07-08 20:08:33 +00:00
tkchia 7dd8ea2ea5 plat/linuxppc/emu: fix fctiw & fctiwz emulation for AArch64 host
Fixes https://github.com/davidgiven/ack/issues/287
2023-07-08 19:54:29 +00:00
David Given 6aa3fef91f
Merge pull request #278 from davidgiven/dtrg-qemu
Remove the qemu tests.
2023-02-14 00:09:59 +01:00
dg 3badc8ece7 Remove the qemu tests --- they've never been reliable and cause problems for
people.
2023-02-13 22:21:42 +00:00
David Given 1fb1016349
Merge pull request #248 from davidgiven/llgen
Add HTML documentation to the LLgen build.
2023-01-08 21:32:29 +01:00
David Given 278ed3cf26
Merge pull request #267 from davidgiven/dtrg-fixes
More warning fixes.
2023-01-08 13:02:19 +01:00
dg f21d256f3f Update documentation. 2023-01-08 11:27:46 +00:00
dg 42b8c481e3 More warning fixes. 2023-01-08 11:27:36 +00:00
dg 0fe814b530 Merge from master. 2023-01-08 10:35:47 +00:00
David Given 9dfa4a88c5
Merge pull request #276 from davidgiven/dtrg-build
Fix some build issues when using the make build backend.
2022-12-30 20:27:54 +01:00
dg baff578256 Fix some build issues when using the make build backend. 2022-12-30 18:50:16 +00:00
David Given 5b554e8e6e
Merge pull request #274 from tkchia/tkchia/20220920
pc86: add degenerate "hard disk partition table" to boot sector
2022-09-21 00:41:40 +02:00
tkchia b59422a24b pc86: add degenerate "hard disk partition table" to boot sector
This is needed on some PCs to boot the pc86.img as a USB drive.
2022-09-20 16:14:38 +00:00
David Given 8619ce3ada
Merge pull request #272 from tkchia/tkchia/20220826
plat/msdos386: refactor, firm up, and optimize
2022-08-26 23:14:26 +02:00
tkchia 247a2bd6ae plat/msdos386: rename _psp[] to .psp, avoid namespace pollution 2022-08-26 17:32:50 +00:00
tkchia f8a6f92752 plat/msdos386: fix: RM int 0x21 calls was clobbering high halves
of esi and edi registers
2022-08-26 17:32:50 +00:00
tkchia cba54b205b plat/msdos386: refactor, firm up, and optimize
- refactor code for transfer buffer reads/writes, real mode
   int 0x21 calls, and assembler segment declarations
 - define transfer buffer size in one place
 - beef up error checking for transfer buffer operations
   (prevent buffer overflows)
 - also optimize such operations to transfer dword by dword
   where feasible
2022-08-26 17:31:28 +00:00
David Given 8891fae303
Merge pull request #270 from tkchia/tkchia/20220823
Some fixes for msdos386 target platform
2022-08-24 19:13:37 +02:00
tkchia 15955282f6 plat/msdos386: fix some issues in libsys code
- ensure es = ds = ss upon exit of each C runtime function
 - clear upper 16 bits of ebx before setting them to 0x0021,
   when invoking interrupt_ptr to simulate a RM int 0x21
 - make _sys_exists use the transfer buffer (which it needs)
 - make _sys_rawread properly handle an end-of-file read
   (zero bytes read)
 - make argument to _sys_seterrno short --- after a failed
   int 0x21 call, only the lower 16 bits of eax hold the MS-DOs
   error code
 - _sys_rawlseek accepts only 3 longword arguments, not 4
   (the offset is only 1 longword)
 - other minor fixes
2022-08-24 15:17:04 +00:00
tkchia 249e4b9069 plat/msdos386: fix a few more issues with startup and stub code
- correctly calculate size of BSS area to clear
 - make realloc routine set both ds & es to point to new
   data segment
 - correctly initialize real mode flags when simulating real
   mode interrupt via DPMI
 - other minor fixes
2022-08-24 10:17:15 +00:00
tkchia 95ed8ee484 plat/msdos386: fix startup code so that it works under dosemu2
- do not try to store data through code segment selectors
 - set each segment limit to segment size - 1, not segment size
 - get PSP segment while still in real/V86 mode (DPMI host
   might choose to return PM selector for int 0x21, ah = 0x62)
 - other minor fixes
2022-08-23 14:35:43 +00:00
David Given 0165c07c7b
Merge pull request #269 from davidgiven/dtrg-msdos386
Add a plat for 32-bit DOS executables.
2022-08-19 21:00:43 +02:00
David Given 887ec0656e Update documentation. 2022-08-19 20:08:39 +02:00
David Given d24c3f8722 Fix typo. 2022-08-19 20:03:48 +02:00
David Given c37c0e62cc Remember to actually update the brk position when calling brk. 2022-08-19 20:03:40 +02:00
David Given dc1f69be83 Most of read and write now works; filename-based operations partially work; but
there's a nasty memory corruption somewhere which needs investigating.
2022-08-19 00:08:57 +02:00
David Given 5329c98b81 Simplify and fix reading and writing so they seem to work. 2022-08-18 21:21:33 +02:00
David Given cc356b5c75 Programs now start up and can write to the console, but crash when reading. 2022-08-18 00:24:08 +02:00
David Given 48398b072a write() system calls work; start going through the libsys. 2022-08-17 22:34:06 +02:00
David Given 680b6b9857 Marshal the environment, command line and PSP into 32-bit memory. 2022-08-11 23:36:23 +02:00
David Given 1764c6baa2 Made realloc work; the 32-bit process can now be resized to include bss and
stack.
2022-08-10 16:14:12 +02:00
David Given 967b46e98e Hacky workaround to make lar work. 2022-08-10 00:15:40 +02:00
David Given b868c1ece9 Hacky workaround to make operatings on pointer differences work. 2022-08-10 00:15:28 +02:00
David Given d464606dd6 We can now load and run 32-bit protected-mode executables. We have not,
however, set up the data segment.
2022-08-09 23:49:18 +02:00
David Given b48b5b13ce Maybe have a working stub now. 2022-08-09 23:32:41 +02:00
David Given 21b30ccadb We can now switch to 32-bit mode. 2022-08-08 23:22:00 +02:00
David Given 1e0961c679 Do enough of the boot stub to produce an EXE header and enter DPMI protected
mode.
2022-08-08 22:03:12 +02:00
David Given 3716d49cd9 Allow arithmetic on variable values. 2022-08-08 22:02:51 +02:00
David Given f2a49ff3ab msdos86 actually emits .COM files. 2022-08-08 00:01:06 +02:00
David Given b81ac5e2c3 Cut-and-paste the msdos86 libsys so the msdos386 stuff now at least builds. 2022-08-07 22:10:08 +02:00
David Given 00c722d2ef Merge from trunk. 2022-08-07 22:01:08 +02:00
David Given a47bb5493d
Merge pull request #268 from tkchia/tkchia/self-host
plat/msdos86: add rename( )
2022-08-07 20:42:01 +02:00
tkchia 63f646de78 plat/msdos86: add rename( ) 2022-08-07 16:38:31 +00:00
David Given 6d9ac0b182 Add the nonfunctional boilerplate for the MSDOS 386 port. 2022-08-03 20:41:06 +02:00
David Given a45f1cdd33 Allow stosw, lodsw, scasw. 2022-08-03 20:32:58 +02:00
David Given 01d978cc22 Fix the groff build to put the images in the right place. 2022-02-05 22:48:14 +01:00
86 changed files with 2388 additions and 603 deletions

8
README
View file

@ -2,7 +2,7 @@
===================================
© 1987-2005 Vrije Universiteit, Amsterdam
2022-06-18
2022-08-19
INTRODUCTION
@ -38,6 +38,7 @@ cpm produces i80 CP/M .COM files
rpi produces Raspberry Pi GPU binaries
pdpv7 produces PDP/11 V7 Unix binaries
msdos86 produces i86 MS-DOS .COM files
msdos386 produces i386 MS-DOS 32-bit DPMI .EXE files
@ -62,11 +63,6 @@ Requirements:
- (optionally) ninja; if you've got this, this will be autodetected and give
you faster builds.
- (optionally) the qemu suite: if you have this installed, the build system
will detect it automatically and run the test suites for the supported
architectures. Get both the qemu-system-* platform emulators and the qemu-*
userland emulators (only works on Linux).
- about 115MB free in /tmp (or some other temporary directory).
- about 15MB in the target directory.

View file

@ -12,6 +12,7 @@ vars.plats = {
"linuxppc",
"linuxmips",
"msdos86",
"msdos386",
"osx386",
"osxppc",
"pc86",
@ -22,9 +23,9 @@ vars.plats = {
vars.plats_with_tests = {
"cpm",
"linux68k",
"linux386",
--"linux386",
"linuxppc",
"linuxmips",
--"linuxmips",
"pc86",
}

View file

@ -225,7 +225,7 @@ definerule("cprogram",
commands = {
type="strings",
default={
"$(CC) $LDFLAGS -o %{outs[1]} %{ins} %{ins}"
"$(CC) $(LDFLAGS) -o %{outs[1]} %{ins} %{ins}"
},
}
},

View file

@ -184,10 +184,13 @@
{0, NOOP_1, 0155, "ins"},
{0, NOOP_1, 0252, "stosb"},
{0, NOOP_1, 0253, "stos"},
{0, NOOP_1, 0253, "stosw"},
{0, NOOP_1, 0254, "lodsb"},
{0, NOOP_1, 0255, "lods"},
{0, NOOP_1, 0255, "lodsw"},
{0, NOOP_1, 0256, "scasb"},
{0, NOOP_1, 0257, "scas"},
{0, NOOP_1, 0257, "scasw"},
{0, NOOP_1, 0311, "leave"},
{0, NOOP_1, 0316, "into"},
{0, NOOP_1, 0317, "iret"},

View file

@ -10,8 +10,8 @@
void ea_1_16(int param)
{
reg_1 &= 0377;
if ((reg_1 & 070) || (param & ~070)) {
reg_1 &= 0377;
if (param & ~070) {
serror("bad operand");
}
emit1(reg_1 | param);
@ -39,542 +39,545 @@ void ea_1_16(int param)
}
void ea_1(int param) {
if (! address_long) {
ea_1_16(param);
return;
}
if (is_expr(reg_1)) {
serror("bad operand");
return;
}
if (is_reg(reg_1)) {
emit1(0300 | param | (reg_1&07));
return;
}
if (rm_1 == 04) {
/* sib field use here */
emit1(mod_1 << 6 | param | 04);
emit1(sib_1 | reg_1);
}
else emit1(mod_1<<6 | param | (reg_1&07));
if ((mod_1 == 0 && reg_1 == 5) || mod_1 == 2) {
/* ??? should this be protected by a call to "small" ??? */
if (! address_long) {
ea_1_16(param);
return;
}
if (is_expr(reg_1)) {
serror("bad operand");
return;
}
if (is_reg(reg_1)) {
emit1(0300 | param | (reg_1&07));
return;
}
if (rm_1 == 04) {
/* sib field use here */
emit1(mod_1 << 6 | param | 04);
emit1(sib_1 | reg_1);
}
else emit1(mod_1<<6 | param | (reg_1&07));
if ((mod_1 == 0 && reg_1 == 5) || mod_1 == 2) {
/* ??? should this be protected by a call to "small" ??? */
#ifdef RELOCATION
RELOMOVE(relonami, rel_1);
newrelo(exp_1.typ, RELO4);
RELOMOVE(relonami, rel_1);
newrelo(exp_1.typ, RELO4);
#endif
emit4((long)(exp_1.val));
}
else if (mod_1 == 1) {
emit1((int)(exp_1.val));
}
emit4((long)(exp_1.val));
}
else if (mod_1 == 1) {
emit1((int)(exp_1.val));
}
}
void ea_2(int param) {
op_1 = op_2;
RELOMOVE(rel_1, rel_2);
ea_1(param);
op_1 = op_2;
RELOMOVE(rel_1, rel_2);
ea_1(param);
}
int checkscale(valu_t val)
{
int v = val;
int v = val;
if (! address_long) {
serror("scaling not allowed in 16-bit mode");
return 0;
}
if (v != val) v = 0;
switch(v) {
case 1:
return 0;
case 2:
return 1 << 6;
case 4:
return 2 << 6;
case 8:
return 3 << 6;
default:
serror("bad scale");
return 0;
}
/*NOTREACHED*/
if (! address_long) {
serror("scaling not allowed in 16-bit mode");
return 0;
}
if (v != val) v = 0;
switch(v) {
case 1:
return 0;
case 2:
return 1 << 6;
case 4:
return 2 << 6;
case 8:
return 3 << 6;
default:
serror("bad scale");
return 0;
}
/*NOTREACHED*/
}
void reverse(void) {
struct operand op;
struct operand op;
#ifdef RELOCATION
int r = rel_1;
int r = rel_1;
rel_1 = rel_2; rel_2 = r;
rel_1 = rel_2; rel_2 = r;
#endif
op = op_1; op_1 = op_2; op_2 = op;
op = op_1; op_1 = op_2; op_2 = op;
}
void badsyntax(void)
{
serror("bad operands");
serror("bad operands");
}
void regsize(int sz)
{
register int bit;
register int bit;
bit = (sz&1) ? 0 : IS_R8;
if ((is_reg(reg_1) && (reg_1 & IS_R8) != bit) ||
(is_reg(reg_2) && (reg_2 & IS_R8) != bit))
serror("register error");
if (! address_long) {
reg_1 &= ~010;
reg_2 &= ~010;
}
bit = (sz&1) ? 0 : IS_R8;
if ((is_reg(reg_1) && (reg_1 & IS_R8) != bit) ||
(is_reg(reg_2) && (reg_2 & IS_R8) != bit))
serror("register error");
if (! address_long) {
reg_1 &= ~010;
reg_2 &= ~010;
}
}
void indexed(void) {
if (address_long) {
mod_2 = 0;
if (sib_2 == -1)
serror("register error");
if (rm_2 == 0 && reg_2 == 4) {
/* base register sp, no index register; use
indexed mode without index register
*/
rm_2 = 04;
sib_2 = 044;
}
if (reg_2 == 015) {
reg_2 = 05;
return;
}
if (small(exp_2.typ == S_ABS && fitb(exp_2.val), 3)) {
if (small(exp_2.val == 0 && reg_2 != 5, 1)) {
}
else mod_2 = 01;
}
else if (small(0, 1)) {
}
else mod_2 = 02;
}
else {
if (reg_2 & ~7)
serror("register error");
if (small(exp_2.typ == S_ABS && fitb(exp_2.val), 1)) {
if (small(exp_2.val == 0 && reg_2 != 6, 1)) {
}
else reg_2 |= 0100;
}
else if (small(0, 1)) {
}
else reg_2 |= 0200;
}
if (address_long) {
mod_2 = 0;
if (sib_2 == -1)
serror("register error");
if (rm_2 == 0 && reg_2 == 4) {
/* base register sp, no index register; use
indexed mode without index register
*/
rm_2 = 04;
sib_2 = 044;
}
if (reg_2 == 015) {
reg_2 = 05;
return;
}
if (small(exp_2.typ == S_ABS && fitb(exp_2.val), 3)) {
if (small(exp_2.val == 0 && reg_2 != 5, 1)) {
}
else mod_2 = 01;
}
else if (small(0, 1)) {
}
else mod_2 = 02;
}
else {
if (reg_2 & ~7)
serror("register error");
if (small(exp_2.typ == S_ABS && fitb(exp_2.val), 1)) {
if (small(exp_2.val == 0 && reg_2 != 6, 1)) {
}
else reg_2 |= 0100;
}
else if (small(0, 1)) {
}
else reg_2 |= 0200;
}
}
void ebranch(register int opc,expr_t exp)
{
/* Conditional branching; Full displacements are available
on the 80386, so the welknown trick with the reverse branch
over a jump is not needed here.
The only complication here is with the address size, which
can be set with a prefix. In this case, the user gets what
he asked for.
*/
register int sm;
register long dist;
int saving = address_long ? 4 : 2;
/* Conditional branching; Full displacements are available
on the 80386, so the welknown trick with the reverse branch
over a jump is not needed here.
The only complication here is with the address size, which
can be set with a prefix. In this case, the user gets what
he asked for.
*/
register int sm;
register long dist;
int saving = address_long ? 4 : 2;
if (opc == 0353) saving--;
dist = exp.val - (DOTVAL + 2);
if (pass == PASS_2 && dist > 0 && !(exp.typ & S_DOT))
dist -= DOTGAIN;
sm = dist > 0 ? fitb(dist-saving) : fitb(dist);
if ((exp.typ & ~S_DOT) != DOTTYP)
sm = 0;
if ((sm = small(sm,saving)) == 0) {
if (opc == 0353) {
emit1(0xe9);
}
else {
emit1(0xF);
emit1(opc | 0x80);
}
dist -= saving;
exp.val = dist;
adsize_exp(exp, RELPC);
}
else {
if (opc == 0353) {
emit1(opc);
}
else {
emit1(opc | 0x70);
}
emit1((int)dist);
}
if (opc == 0353) saving--;
dist = exp.val - (DOTVAL + 2);
if (pass == PASS_2 && dist > 0 && !(exp.typ & S_DOT))
dist -= DOTGAIN;
sm = dist > 0 ? fitb(dist-saving) : fitb(dist);
if ((exp.typ & ~S_DOT) != DOTTYP)
sm = 0;
if ((sm = small(sm,saving)) == 0) {
if (opc == 0353) {
emit1(0xe9);
}
else {
emit1(0xF);
emit1(opc | 0x80);
}
dist -= saving;
exp.val = dist;
adsize_exp(exp, RELPC);
}
else {
if (opc == 0353) {
emit1(opc);
}
else {
emit1(opc | 0x70);
}
emit1((int)dist);
}
}
void branch(register int opc,expr_t exp)
{
/* LOOP, JCXZ, etc. branch instructions.
Here, the offset just must fit in a byte.
*/
register long dist;
/* LOOP, JCXZ, etc. branch instructions.
Here, the offset just must fit in a byte.
*/
register long dist;
dist = exp.val - (DOTVAL + 2);
if (pass == PASS_2 && dist > 0 && !(exp.typ & S_DOT))
dist -= DOTGAIN;
fit((exp.typ & ~S_DOT) == DOTTYP && fitb(dist));
emit1(opc);
emit1((int)dist);
dist = exp.val - (DOTVAL + 2);
if (pass == PASS_2 && dist > 0 && !(exp.typ & S_DOT))
dist -= DOTGAIN;
fit((exp.typ & ~S_DOT) == DOTTYP && fitb(dist));
emit1(opc);
emit1((int)dist);
}
void pushop(register int opc)
{
regsize(1);
if (is_segreg(reg_1)) {
/* segment register */
if ((reg_1 & 07) <= 3)
emit1(6 | opc | (reg_1&7)<<3);
else {
emit1(0xF);
emit1(0200 | opc | ((reg_1&7)<<3));
}
} else if (is_reg(reg_1)) {
/* normal register */
emit1(0120 | opc<<3 | (reg_1&7));
} else if (opc == 0) {
if (is_expr(reg_1)) {
if (small(exp_1.typ == S_ABS && fitb(exp_1.val),
operand_long ? 3 : 1)) {
emit1(0152);
emit1((int)(exp_1.val));
}
else {
emit1(0150);
RELOMOVE(relonami, rel_1);
opsize_exp(exp_1, 1);
}
}
else {
emit1(0377); ea_1(6<<3);
}
} else {
emit1(0217); ea_1(0<<3);
}
regsize(1);
if (is_segreg(reg_1)) {
/* segment register */
if ((reg_1 & 07) <= 3)
emit1(6 | opc | (reg_1&7)<<3);
else {
emit1(0xF);
emit1(0200 | opc | ((reg_1&7)<<3));
}
} else if (is_reg(reg_1)) {
/* normal register */
emit1(0120 | opc<<3 | (reg_1&7));
} else if (opc == 0) {
if (is_expr(reg_1)) {
if (small(exp_1.typ == S_ABS && fitb(exp_1.val),
operand_long ? 3 : 1)) {
emit1(0152);
emit1((int)(exp_1.val));
}
else {
emit1(0150);
RELOMOVE(relonami, rel_1);
opsize_exp(exp_1, 1);
}
}
else {
emit1(0377); ea_1(6<<3);
}
} else {
emit1(0217); ea_1(0<<3);
}
}
void opsize_exp(expr_t exp, int nobyte)
{
if (! nobyte) {
if (! nobyte) {
#ifdef RELOCATION
newrelo(exp.typ, RELO1);
newrelo(exp.typ, RELO1);
#endif
emit1((int)(exp.val));
}
else if (operand_long) {
emit1((int)(exp.val));
}
else if (operand_long) {
#ifdef RELOCATION
newrelo(exp.typ, RELO4);
newrelo(exp.typ, RELO4);
#endif
emit4((long)(exp.val));
}
else {
emit4((long)(exp.val));
}
else {
#ifdef RELOCATION
newrelo(exp.typ, RELO2);
newrelo(exp.typ, RELO2);
#endif
emit2((int)(exp.val));
}
emit2((int)(exp.val));
}
}
void adsize_exp(expr_t exp, int relpc)
{
if (address_long) {
if (address_long) {
#ifdef RELOCATION
newrelo(exp.typ, RELO4 | relpc);
newrelo(exp.typ, RELO4 | relpc);
#endif
emit4((long)(exp.val));
}
else {
emit4((long)(exp.val));
}
else {
#ifdef RELOCATION
newrelo(exp.typ, RELO2 | relpc);
newrelo(exp.typ, RELO2 | relpc);
#endif
emit2((int)(exp.val));
}
emit2((int)(exp.val));
}
}
void addop(register int opc)
{
regsize(opc);
if (is_reg(reg_2)) {
/* Add register to register or memory */
emit1(opc); ea_1((reg_2&7)<<3);
} else if (is_acc(reg_1) && is_expr(reg_2)) {
/* Add immediate to accumulator */
emit1(opc | 4);
RELOMOVE(relonami, rel_2);
opsize_exp(exp_2, (opc&1));
} else if (is_expr(reg_2)) {
/* Add immediate to register or memory */
if ((opc&1) == 0) {
emit1(0200);
} else if (! small(exp_2.typ == S_ABS && fitb(exp_2.val),
operand_long ? 3 : 1)) {
emit1(0201);
} else {
emit1(0203); opc &= ~1;
}
ea_1(opc & 070);
RELOMOVE(relonami, rel_2);
opsize_exp(exp_2, (opc&1));
} else if (is_reg(reg_1)) {
/* Add register or memory to register */
emit1(opc | 2);
ea_2((reg_1&7)<<3);
} else
badsyntax();
regsize(opc);
if (is_reg(reg_2)) {
/* Add register to register or memory */
emit1(opc); ea_1((reg_2&7)<<3);
} else if (is_acc(reg_1) && is_expr(reg_2)) {
/* Add immediate to accumulator */
emit1(opc | 4);
RELOMOVE(relonami, rel_2);
opsize_exp(exp_2, (opc&1));
} else if (is_expr(reg_2)) {
/* Add immediate to register or memory */
if ((opc&1) == 0) {
emit1(0200);
} else if (! small(exp_2.typ == S_ABS && fitb(exp_2.val),
operand_long ? 3 : 1)) {
emit1(0201);
} else {
emit1(0203); opc &= ~1;
}
ea_1(opc & 070);
RELOMOVE(relonami, rel_2);
opsize_exp(exp_2, (opc&1));
} else if (is_reg(reg_1)) {
/* Add register or memory to register */
emit1(opc | 2);
ea_2((reg_1&7)<<3);
} else
badsyntax();
}
void rolop(register int opc)
{
register int oreg;
register int oreg;
oreg = reg_2;
reg_2 = reg_1;
regsize(opc);
if (oreg == (IS_R8 | 1 | (address_long ? 0 : 0300))) {
/* cl register */
emit1(0322 | (opc&1)); ea_1(opc&070);
} else if (is_expr(oreg)) {
if (small(exp_2.typ == S_ABS && exp_2.val == 1, 1)) {
/* shift by 1 */
emit1(0320 | (opc&1)); ea_1(opc&070);
} else {
/* shift by byte count */
emit1(0300 | (opc & 1));
ea_1(opc & 070);
oreg = reg_2;
reg_2 = reg_1;
regsize(opc);
if (oreg == (IS_R8 | 1 | (address_long ? 0 : 0300))) {
/* cl register */
emit1(0322 | (opc&1)); ea_1(opc&070);
} else if (is_expr(oreg)) {
if (small(exp_2.typ == S_ABS && exp_2.val == 1, 1)) {
/* shift by 1 */
emit1(0320 | (opc&1)); ea_1(opc&070);
} else {
/* shift by byte count */
emit1(0300 | (opc & 1));
ea_1(opc & 070);
#ifdef RELOCATION
RELOMOVE(relonami, rel_2);
newrelo(exp_2.typ, RELO1);
RELOMOVE(relonami, rel_2);
newrelo(exp_2.typ, RELO1);
#endif
emit1((int)(exp_2.val));
}
}
else
badsyntax();
emit1((int)(exp_2.val));
}
}
else
badsyntax();
}
void incop(register int opc)
{
regsize(opc);
if ((opc&1) && is_reg(reg_1)) {
/* word register */
emit1(0100 | (opc&010) | (reg_1&7));
} else {
emit1(0376 | (opc&1));
ea_1(opc & 010);
}
regsize(opc);
if ((opc&1) && is_reg(reg_1)) {
/* word register */
emit1(0100 | (opc&010) | (reg_1&7));
} else {
emit1(0376 | (opc&1));
ea_1(opc & 010);
}
}
void callop(register int opc)
{
regsize(1);
if (is_expr(reg_1)) {
if (opc == (040+(0351<<8))) {
RELOMOVE(relonami, rel_1);
ebranch(0353,exp_1);
} else {
exp_1.val -= (DOTVAL+3 + (address_long ? 2 : 0));
emit1(opc>>8);
RELOMOVE(relonami, rel_1);
adsize_exp(exp_1, RELPC);
}
} else {
emit1(0377); ea_1(opc&070);
}
regsize(1);
if (is_expr(reg_1)) {
if (opc == (040+(0351<<8))) {
RELOMOVE(relonami, rel_1);
ebranch(0353,exp_1);
} else {
exp_1.val -= (DOTVAL+3 + (address_long ? 2 : 0));
emit1(opc>>8);
RELOMOVE(relonami, rel_1);
adsize_exp(exp_1, RELPC);
}
} else {
emit1(0377); ea_1(opc&070);
}
}
void xchg(register int opc)
{
regsize(opc);
if (! is_reg(reg_1) || is_acc(reg_2)) {
reverse();
}
if (opc == 1 && is_acc(reg_1) && is_reg(reg_2)) {
emit1(0220 | (reg_2&7));
} else if (is_reg(reg_1)) {
emit1(0206 | opc); ea_2((reg_1&7)<<3);
} else
badsyntax();
regsize(opc);
if (! is_reg(reg_1) || is_acc(reg_2)) {
reverse();
}
if (opc == 1 && is_acc(reg_1) && is_reg(reg_2)) {
emit1(0220 | (reg_2&7));
} else if (is_reg(reg_1)) {
emit1(0206 | opc); ea_2((reg_1&7)<<3);
} else
badsyntax();
}
void test(register int opc)
{
regsize(opc);
if (is_reg(reg_2) || is_expr(reg_1))
reverse();
if (is_expr(reg_2)) {
if (is_acc(reg_1)) {
emit1(0250 | opc);
RELOMOVE(relonami, rel_2);
opsize_exp(exp_2, (opc&1));
}
else {
emit1(0366 | opc);
ea_1(0<<3);
RELOMOVE(relonami, rel_2);
opsize_exp(exp_2, (opc&1));
}
} else if (is_reg(reg_1)) {
emit1(0204 | opc); ea_2((reg_1&7)<<3);
} else
badsyntax();
regsize(opc);
if (is_reg(reg_2) || is_expr(reg_1))
reverse();
if (is_expr(reg_2)) {
if (is_acc(reg_1)) {
emit1(0250 | opc);
RELOMOVE(relonami, rel_2);
opsize_exp(exp_2, (opc&1));
}
else {
emit1(0366 | opc);
ea_1(0<<3);
RELOMOVE(relonami, rel_2);
opsize_exp(exp_2, (opc&1));
}
} else if (is_reg(reg_1)) {
emit1(0204 | opc); ea_2((reg_1&7)<<3);
} else
badsyntax();
}
void mov(register int opc)
{
regsize(opc);
if (is_segreg(reg_1)) {
/* to segment register */
emit1(0216); ea_2((reg_1&07)<<3);
} else if (is_segreg(reg_2)) {
/* from segment register */
emit1(0214); ea_1((reg_2&07)<<3);
} else if (is_expr(reg_2)) {
/* from immediate */
if (is_reg(reg_1)) {
/* to register */
emit1(0260 | opc<<3 | (reg_1&7));
} else {
/* to memory */
emit1(0306 | opc); ea_1(0<<3);
}
RELOMOVE(relonami, rel_2);
opsize_exp(exp_2, (opc&1));
} else if (rm_1 == 05 && is_acc(reg_2)) {
/* from accumulator to memory (displacement) */
emit1(0242 | opc);
RELOMOVE(relonami, rel_1);
adsize_exp(exp_1, 0);
} else if (rm_2 == 05 && is_acc(reg_1)) {
/* from memory (displacement) to accumulator */
emit1(0240 | opc);
RELOMOVE(relonami, rel_2);
adsize_exp(exp_2, 0);
} else if (is_reg(reg_2)) {
/* from register to memory or register */
emit1(0210 | opc); ea_1((reg_2&07)<<3);
} else if (is_reg(reg_1)) {
/* from memory or register to register */
emit1(0212 | opc); ea_2((reg_1&07)<<3);
} else
badsyntax();
regsize(opc);
if (is_segreg(reg_1)) {
/* to segment register */
emit1(0216); ea_2((reg_1&07)<<3);
} else if (is_segreg(reg_2)) {
/* from segment register */
emit1(0214); ea_1((reg_2&07)<<3);
} else if (is_expr(reg_2)) {
/* from immediate */
if (is_reg(reg_1)) {
/* to register */
emit1(0260 | opc<<3 | (reg_1&7));
} else {
/* to memory */
emit1(0306 | opc); ea_1(0<<3);
}
RELOMOVE(relonami, rel_2);
opsize_exp(exp_2, (opc&1));
} else if (rm_1 == 05 && is_acc(reg_2)) {
/* from accumulator to memory (displacement) */
emit1(0242 | opc);
RELOMOVE(relonami, rel_1);
adsize_exp(exp_1, 0);
} else if (rm_2 == 05 && is_acc(reg_1)) {
/* from memory (displacement) to accumulator */
emit1(0240 | opc);
RELOMOVE(relonami, rel_2);
adsize_exp(exp_2, 0);
} else if (is_reg(reg_2)) {
/* from register to memory or register */
emit1(0210 | opc); ea_1((reg_2&07)<<3);
} else if (is_reg(reg_1)) {
/* from memory or register to register */
emit1(0212 | opc); ea_2((reg_1&07)<<3);
} else
badsyntax();
}
void extshft(int opc, int reg)
{
int oreg2 = reg_2;
int oreg2 = reg_2;
reg_2 = reg_1;
regsize(1);
reg_2 = reg_1;
regsize(1);
emit1(0xF);
if (oreg2 == (IS_R8 | 1 | (address_long ? 0 : 0300))) {
/* cl register */
emit1(opc|1);
ea_1(reg << 3);
}
else if (is_expr(oreg2)) {
emit1(opc);
ea_1(reg << 3);
emit1(0xF);
if (oreg2 == (IS_R8 | 1 | (address_long ? 0 : 0300))) {
/* cl register */
emit1(opc|1);
ea_1(reg << 3);
}
else if (is_expr(oreg2)) {
emit1(opc);
ea_1(reg << 3);
#ifdef RELOCATION
RELOMOVE(relonami, rel_2);
newrelo(exp_2.typ, RELO1);
RELOMOVE(relonami, rel_2);
newrelo(exp_2.typ, RELO1);
#endif
emit1((int)(exp_2.val));
}
else badsyntax();
emit1((int)(exp_2.val));
}
else badsyntax();
}
void bittestop(int opc)
{
regsize(1);
emit1(0xF);
if (is_expr(reg_2)) {
emit1(0272);
ea_1(opc << 3);
regsize(1);
emit1(0xF);
if (is_expr(reg_2)) {
emit1(0272);
ea_1(opc << 3);
#ifdef RELOCATION
RELOMOVE(relonami, rel_2);
newrelo(exp_2.typ, RELO1);
RELOMOVE(relonami, rel_2);
newrelo(exp_2.typ, RELO1);
#endif
emit1((int)(exp_2.val));
}
else if (is_reg(reg_2)) {
emit1(0203 | (opc<<3));
ea_1((reg_2&7)<<3);
}
else badsyntax();
emit1((int)(exp_2.val));
}
else if (is_reg(reg_2)) {
emit1(0203 | (opc<<3));
ea_1((reg_2&7)<<3);
}
else badsyntax();
}
void imul(int reg)
{
/* This instruction is more elaborate on the 80386. Its most
general form is:
imul reg, reg_or_mem, immediate.
This is the form processed here.
*/
regsize(1);
if (is_expr(reg_1)) {
/* To also handle
imul reg, immediate, reg_or_mem
*/
reverse();
}
if (is_expr(reg_2)) {
/* The immediate form; two cases: */
if (small(exp_2.typ == S_ABS && fitb(exp_2.val),
operand_long ? 3 : 1)) {
/* case 1: 1 byte encoding of immediate */
emit1(0153);
ea_1((reg & 07) << 3);
emit1((int)(exp_2.val));
}
else {
/* case 2: WORD or DWORD encoding of immediate */
emit1(0151);
ea_1((reg & 07) << 3);
RELOMOVE(relonami, rel_2);
opsize_exp(exp_2, 1);
}
}
else if (is_reg(reg_1) && ((reg_1&7) == (reg & 07))) {
/* the "reg" field and the "reg_or_mem" field are the same,
and the 3rd operand is not an immediate ...
*/
if (reg == 0) {
/* how lucky we are, the target is the ax register */
/* However, we cannot make an optimization for f.i.
imul eax, blablabla
because the latter does not affect edx, whereas
imul blablabla
does! Therefore, "reg" is or-ed with 0x10 in the
former case, so that the test above fails.
*/
emit1(0367);
ea_2(050);
}
else {
/* another register ... */
emit1(0xF);
emit1(0257);
ea_2((reg & 07) << 3);
}
}
else badsyntax();
/* This instruction is more elaborate on the 80386. Its most
general form is:
imul reg, reg_or_mem, immediate.
This is the form processed here.
*/
regsize(1);
if (is_expr(reg_1)) {
/* To also handle
imul reg, immediate, reg_or_mem
*/
reverse();
}
if (is_expr(reg_2)) {
/* The immediate form; two cases: */
if (small(exp_2.typ == S_ABS && fitb(exp_2.val),
operand_long ? 3 : 1)) {
/* case 1: 1 byte encoding of immediate */
emit1(0153);
ea_1((reg & 07) << 3);
emit1((int)(exp_2.val));
}
else {
/* case 2: WORD or DWORD encoding of immediate */
emit1(0151);
ea_1((reg & 07) << 3);
RELOMOVE(relonami, rel_2);
opsize_exp(exp_2, 1);
}
}
else if (is_reg(reg_1) && ((reg_1&7) == (reg & 07))) {
/* the "reg" field and the "reg_or_mem" field are the same,
and the 3rd operand is not an immediate ...
*/
if (reg == 0) {
/* how lucky we are, the target is the ax register */
/* However, we cannot make an optimization for f.i.
imul eax, blablabla
because the latter does not affect edx, whereas
imul blablabla
does! Therefore, "reg" is or-ed with 0x10 in the
former case, so that the test above fails.
*/
emit1(0367);
ea_2(050);
}
else {
/* another register ... */
emit1(0xF);
emit1(0257);
ea_2((reg & 07) << 3);
}
}
else badsyntax();
}
// vim: ts=8 sw=8 et

View file

@ -86,6 +86,9 @@ char* remember(char* s)
int combine(int typ1, int typ2, int op)
{
typ1 &= ~S_VAR;
typ2 &= ~S_VAR;
switch (op)
{
case '+':

View file

@ -78,7 +78,7 @@ for _, plat in ipairs({"cpm"}) do
},
outleaves = { n..".s" },
commands = {
"$LUA %{ins[1]} <%{ins[2]} >%{outs}"
"$(LUA) %{ins[1]} <%{ins[2]} >%{outs}"
}
}
end

View file

@ -1,6 +1,9 @@
#ifndef PMAP_H
#define PMAP_H
/* A bidirectional multimap, storing (left, right) tuples.
*/
struct pmap_node
{
void* left;
@ -23,10 +26,33 @@ struct pmap
int max; \
}
/** Adds a new relation `(left -> right)` to the map
* if `left` is not present. Otherwise replaces the
* `right` associated with the first `left`.
*
* Use this in normal map mode.
*/
extern void pmap_put(void* map, void* left, void* right);
/** Adds a new relation to the map if that relation does
* not already exist.
*
* Use this in multimap mode.
*/
extern void pmap_add(void* map, void* left, void* right);
/** Removes a relation from the map.
*
* Use this in multimap mode.
*/
extern void pmap_remove(void* map, void* left, void* right);
/** Given a `left`, find the first matching `right`.
*/
extern void* pmap_findleft(void* map, void* left);
/** Given a `right`, find the first matching `left`.
*/
extern void* pmap_findright(void* map, void* right);
#endif

View file

@ -6,6 +6,8 @@
#ifndef SMAP_H
#define SMAP_H
/* A string multimap (each key can have multiple values). */
struct smap_node
{
const char* left;
@ -28,24 +30,33 @@ struct smap
int max; \
}
/** Initializes a string map and returns the initialized
* handle in `mapp`.
/** Initializes a string map. Optional; a zero-initialised map
* structure is a valid empty map.
*/
extern void smap_init(void *mapp);
/** Adds a new item with name `key` in the string map if
* it does not already exist, otherwise replaces the
* value `value` associated with the existing `key`.
/** Adds a new item with name `key` in the string map if it does not already
* exist. Otherwise replaces the value `value` associated with the existing
* `key`.
*
* Use this for normal map mode.
*/
extern void smap_put(void *mapp, const char* key, void* value);
/** Adds a new item in a string map only if `key` does
* not already exist in the string map.
/** Adds a new item with name `key` and value `value`. If that relation already
* exists, nothing happens.
*
* Use this for multimap mode.
*/
extern void smap_add(void *mapp, const char* key, void* value);
/** Returns the value associated with the specified `key`, returns
* NULL if `key` is not present in the string map.
/** Returns the first value associated with the specified `key`. Returns NULL
* if `key` is not present in the string map.
*
* Use this in normal map mode.
*/
extern void* smap_get(void* mapp, const char* key);
/** Frees the data structure associated with the string map.
* Also frees the memory associated with the key if
* `free_key` is non-zero, and free the memory associated

View file

@ -39,7 +39,7 @@ void stringlist_free(struct stringlist* list, int freedata)
struct stringfragment* next = f->next;
if (freedata)
{
free(f->data);
free((void*) f->data);
f->data = NULL;
}
free(f);
@ -54,7 +54,7 @@ void stringlist_init(struct stringlist* list)
}
char* stringlist_get(struct stringlist *list, int index)
const char* stringlist_get(struct stringlist *list, int index)
{
struct stringfragment* f = list->first;
int i = 0;

View file

@ -3,7 +3,7 @@
struct stringfragment
{
char* data;
const char* data;
struct stringfragment* next;
};
@ -18,7 +18,7 @@ extern void stringlist_addall(struct stringlist* list, struct stringlist* src);
extern void stringlist_free(struct stringlist* list, int freedata);
extern void stringlist_init(struct stringlist* list);
extern int stringlist_count(struct stringlist *list);
extern char* stringlist_get(struct stringlist *list, int index);
extern const char* stringlist_get(struct stringlist *list, int index);
#endif

View file

@ -8,7 +8,7 @@ normalrule {
},
outleaves = { "em_codeEK.h" },
commands = {
"$LUA %{ins[1]} < %{ins[3]} > %{outs}",
"$(LUA) %{ins[1]} < %{ins[3]} > %{outs}",
"cat %{ins[4]} >> %{outs}"
}
}

View file

@ -23,7 +23,7 @@ for _, f in ipairs(GENFILES) do
"h+emheaders"
},
commands = {
"$LUA %{ins[1]} < %{ins[3]} > %{outs}"
"$(LUA) %{ins[1]} < %{ins[3]} > %{outs}"
}
}
end

View file

@ -7,7 +7,7 @@ normalrule {
},
outleaves = "C_mnem_narg.h",
commands = {
"$LUA %{ins[1]} < %{ins[3]} > %{outs}"
"$(LUA) %{ins[1]} < %{ins[3]} > %{outs}"
}
}
@ -20,7 +20,7 @@ normalrule {
},
outleaves = "C_mnem.h",
commands = {
"$LUA %{ins[1]} < %{ins[3]} > %{outs}"
"$(LUA) %{ins[1]} < %{ins[3]} > %{outs}"
}
}

View file

@ -1,7 +0,0 @@
include("tests/plat/build.lua")
plat_testsuite {
name = "tests",
plat = "linux386",
method = "qemu-i386"
}

View file

@ -1,10 +0,0 @@
include("tests/plat/build.lua")
plat_testsuite {
name = "tests",
plat = "linuxmips",
method = "qemu-mipsel",
skipsets = {
"long-long",
},
}

View file

@ -325,6 +325,12 @@ void dump_state(FILE* stream)
fprintf(stream, "\n");
fprintf(stream, "gpr%02d=0x%08x ", i, cpu.gpr[i]);
}
for (i=0; i<32; i++)
{
if ((i % 3) == 0)
fprintf(stream, "\n");
fprintf(stream, "fpr%02d=0x%016lx ", i, cpu.fpr[i]);
}
fprintf(stderr, "\n");
/* This might fail and cause a reentrant trap if cia is invalid, so

View file

@ -207,5 +207,5 @@
# Floating point conversions
<63--><FRT>.....<FRB><14------>R setcr1(R, fpr(FRB)); cpu.fpr[FRT] = (uint32_t)fpr(FRB);
<63--><FRT>.....<FRB><15------>R setcr1(R, fpr(FRB)); cpu.fpr[FRT] = (uint32_t)fpr(FRB);
<63--><FRT>.....<FRB><14------>R setcr1(R, fpr(FRB)); cpu.fpr[FRT] = (uint32_t)(int32_t)fpr(FRB);
<63--><FRT>.....<FRB><15------>R setcr1(R, fpr(FRB)); cpu.fpr[FRT] = (uint32_t)(int32_t)fpr(FRB);

View file

@ -0,0 +1,11 @@
/* $Source$
* $State$
* $Revision$
*/
#ifndef _ACK_PLAT_H
#define _ACK_PLAT_H
#define ACKCONF_WANT_O_TEXT_O_BINARY 1
#endif

View file

@ -0,0 +1,24 @@
include("plat/build.lua")
headermap = {}
packagemap = {}
local function addheader(h)
headermap[h] = "./"..h
packagemap["$(PLATIND)/msdos86/include/"..h] = "./"..h
end
addheader("ack/plat.h")
addheader("sys/types.h")
acklibrary {
name = "headers",
hdrs = headermap
}
installable {
name = "pkg",
map = packagemap
}

View file

@ -0,0 +1,9 @@
#ifndef _SYS_TYPES_H
#define _SYS_TYPES_H
typedef long pid_t;
typedef int mode_t;
typedef long time_t;
typedef long suseconds_t;
#endif

View file

@ -0,0 +1,28 @@
bundle {
name = "srcs",
srcs = {
"./creat.c",
"./gettimeofday.c",
"./kill.c",
"./lseek.c",
"./open.c",
"./read.c",
"./setmode.c",
"./signal.c",
"./sys_fdmodes.c",
"./sys_getmode.c",
"./sys_initmain.c",
"./sys_iseof.c",
"./sys_seteof.c",
"./sys_seterrno.c",
"./sys_setmode.c",
"./write.c",
}
}
bundle {
name = "headers",
srcs = {
"./libsys.h"
}
}

View file

@ -5,6 +5,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
@ -16,9 +17,7 @@ ssize_t read(int fd, void* buffer, size_t count)
ssize_t r, tot;
size_t left;
int eof = 0;
if (!count || _sys_getmode(fd) == O_BINARY)
return _sys_rawread(fd, buffer, count);
bool text = _sys_getmode(fd) == O_TEXT;
if (_sys_iseof(fd))
return 0;
@ -47,33 +46,36 @@ ssize_t read(int fd, void* buffer, size_t count)
if (r <= 0)
return tot ? tot : r;
q = memchr(p, 0x1a, (size_t)r);
if (q)
if (text)
{
_sys_rawlseek(fd, (off_t)(q - p) - r, SEEK_CUR);
r = q - p;
eof = 1;
_sys_seteof(fd, 1);
}
q = memchr(p, '\r', (size_t)r);
if (!q)
return tot + r;
tot += q - p;
left = r - (q + 1 - p);
p = q;
++q;
while (left)
{
char c = *q++;
if (c != '\r')
q = memchr(p, 0x1a, (size_t)r);
if (q)
{
*p++ = c;
++tot;
_sys_rawlseek(fd, (off_t)(q - p) - r, SEEK_CUR);
r = q - p;
eof = 1;
_sys_seteof(fd, 1);
}
q = memchr(p, '\r', (size_t)r);
if (!q)
return tot + r;
tot += q - p;
left = r - (q + 1 - p);
p = q;
++q;
while (left)
{
char c = *q++;
if (c != '\r')
{
*p++ = c;
++tot;
}
--left;
}
--left;
}
} while (tot < count && !eof && _sys_isreadyr(fd));

View file

@ -35,7 +35,7 @@ static const signed char err_map[] =
* Map an MS-DOS 2+ system error code to an `errno' value and store that in
* `errno'. Return a longword -1.
*/
long _sys_seterrno(unsigned dos_err)
long _sys_seterrno(unsigned short dos_err)
{
if (dos_err < sizeof(err_map) / sizeof(err_map[0]))
errno = err_map[dos_err];

74
plat/msdos/libsys/write.c Normal file
View file

@ -0,0 +1,74 @@
/* $Source$
* $State$
* $Revision$
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include "libsys.h"
ssize_t write(int fd, void* buffer, size_t count)
{
static const char crlf[2] = "\r\n";
int i;
char *p, *q;
size_t left, n;
ssize_t r, tot;
if (!count)
return 0;
p = buffer;
left = count;
tot = 0;
if (_sys_getmode(fd) == O_BINARY)
{
while (left)
{
r = _sys_rawwrite(fd, p, left);
if (r <= 0)
return tot ? tot : r;
tot += r;
p += r;
left -= r;
}
}
else
{
/* If the file descriptor is an O_TEXT fd, translate LFs to CRLFs. */
while (left)
{
q = memchr(p, '\n', left);
if (!q)
return _sys_rawwrite(fd, p, left);
n = q - p;
if (n)
{
r = _sys_rawwrite(fd, p, n);
if (r <= 0)
return tot ? tot : r;
tot += r;
p += r;
left -= r;
if (r != n)
break;
}
r = _sys_rawwrite(fd, crlf, sizeof crlf);
if (r != 2)
{
if (r > 0)
r = 0;
return tot ? tot : r;
}
++tot;
++p;
--left;
}
}
return tot;
}

24
plat/msdos386/README Normal file
View file

@ -0,0 +1,24 @@
The msdos386 platform
=====================
msdos386 is an i386-based BSP that produces DPMI DOS executables. It should
have the same functionality as msdos86 (as the libc is all the same code), but
has been much less well tested.
The code runs in flat 32-bit mode, so normal 32-bit pointers work with no
worries about segmentation. It requires a DPMI host to run; it was tested with
CWSDPMI. It will _not_ automatically load the host --- you need to manually
install the DPMI hsot first.
IEEE floating point is available, but requires an FPU.
Example command line
====================
ack -mmsdos386 -O -o msdos386.exe examples/paranoia.c
David Given
dg@cowlark.com

235
plat/msdos386/boot.s Normal file
View file

@ -0,0 +1,235 @@
#
! $Source$
! $State$
! $Revision$
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
#define STACK_BUFFER 128 /* number of bytes to leave for stack */
! g 18b to break at the retf before coming here
begtext:
! On entry, the stub has cs and ds pointing at the 32-bit
! segment, but ss is still pointing at the 16-bit segment.
!
! ax: pmode code segment of stub
! bx: pmode data segment of stub
! cx: rmode segment of stub
! dx: pointer to realloc routine (in stub)
! si: pointer to interrupt routine (in stub)
! di: pointer to transfer buffer (in stub)
! bp: rmode PSP segment
! Resize the segment to include the BSS.
o16 mov (realloc_ptr+4), ax
mov (realloc_ptr+0), edx
o16 mov (pmode_cs), ax
o16 mov (pmode_ds), bx
o16 mov (rmode), cx
o16 mov (interrupt_ptr+4), ax
mov (interrupt_ptr+0), esi
o16 mov (transfer_buffer_ptr), di
mov eax, __end
callf (realloc_ptr)
! Clear BSS.
mov edi, begbss
mov ecx, endbss+3
sub ecx, edi
shr ecx, 2
xor eax, eax
cld
rep stos
! It's now safe to switch stacks.
cli
mov eax, ds
mov ss, eax
mov sp, .stack
sti
! Create a handle for low 1MB access.
o16 mov ax, 0x0000
o16 mov cx, 1
int 0x31 ! allocate LDT
o16 mov (.doshandle), ax
mov es, ax
xchg ebx, eax
xor ecx, ecx
xor edx, edx
o16 mov ax, 0x0007
int 0x31 ! set base address to 0
o16 dec dx
o16 mov cx, 0x000f
o16 mov ax, 0x0008
int 0x31 ! set limit to 1MB
mov cx, ds
and cx, 3
shl cx, 5
or cx, 0xc093 ! 32-bit, big, data, r/w, expand-up
mov ax, 0x0009
int 0x31 ! set descriptor access rights
! Locate the PSP.
movzx esi, bp
shl esi, 4 ! convert to linear address
! Copy the whole thing into 32-bit memory.
mov ecx, 0x100/4
mov edi, .psp
cld
1:
eseg lods
mov (edi), eax
add edi, 4
loop 1b
! Find the environment.
mov es, (.psp + 0x002C) ! converted to pmode segment
! Count the size of the environment variable block.
xorb al, al ! byte to test for
xor edi, edi
xor edx, edx
cld
mov ecx, -1
scasb
jz 2f ! empty environment
1:
repnz scasb
inc edx
scasb
jnz 1b
2:
add edi, 2 ! skip the mysterious word
repnz scasb ! program name
! Copy the whole environment block onto the stack.
mov esi, edi
add esi, 3
and esi, ~3 ! round up
jz empty_environment
mov ecx, esi
shr ecx, 2 ! number of dwords
std
sub esi, 4
1:
eseg lods
push eax
loop 1b
empty_environment:
mov ecx, esp ! environment data
! Reset DF and es properly.
cld
push ss
pop es
! Reserve space for argc and the argv and envp pointers on the
! stack. These will be passed to __m_a_i_n later.
sub esp, 3*4
mov eax, esp
! Build up argc, argv[], and envp[].
push eax ! output buffer for argc, argv, envp
push 3 ! MS-DOS version
push ecx ! env. string data
push edx ! count of env. vars.
push .psp+0x80 ! raw command line
call __sys_initmain
add esp, 5*4
! Bail out if something went wrong.
test eax, eax
jnz no_room
! argc, argv, and envp are now at the stack top. Now go.
call __m_a_i_n
add sp, 3*4
push ax
call _exit
no_room:
mov dx, no_room_msg
!call dos_msg
movb al, -1
jmp al_exit
! Exit.
.define __exit
.extern __exit
.define EXIT
.extern EXIT
__exit:
EXIT:
pop bx
pop ax
al_exit:
movb ah, 0x4c
int 0x21
! These must be in the code segment due to bootstrap issues.
realloc_ptr: .space 6 ! far
interrupt_ptr: .space 6 ! far
transfer_buffer_ptr: .space 2 ! near
rmode: .space 2
pmode_cs: .space 2
pmode_ds: .space 2
.define realloc_ptr
.define interrupt_ptr
.define rmode
.define pmode_cs
.define pmode_ds
.define transfer_buffer_ptr
! Define symbols at the beginning of our various segments, so that we can find
! them. (Except .text, which has already been done.)
.define begtext, begdata, begbss
.sect .data; begdata:
.sect .rom; begrom:
.sect .bss; begbss:
.sect .rom
! Some text messages.
bad_sys_msg: .ascii 'Bad DOS$'
no_room_msg: .ascii 'No room$'
! Some magic data. All EM systems need these.
.define .trppc, .ignmask, _errno
.comm .trppc, 4
.comm .ignmask, 4
.comm _errno, 4
.comm .doshandle, 2
.comm .psp, 256
.sect .bss
.space 32*1024
.stack:
! vim: ts=4 sw=4 et ft=asm

View file

@ -0,0 +1,53 @@
include("plat/build.lua")
ackfile {
name = "boot",
srcs = { "./boot.s" },
vars = { plat = "msdos386" }
}
ackfile {
name = "stub",
srcs = { "./stub.s" },
deps = { "plat/msdos386/libsys+headers" },
vars = { plat = "msdos386" }
}
normalrule {
name = "stub_aout",
ins = {
"util/led+led",
"+stub"
},
outleaves = { "stub.aout" },
commands = { "%{ins[1]} %{ins[2]} -o %{outs[1]}" }
}
normalrule {
name = "stub_exe",
ins = {
"util/amisc+aslod",
"+stub_aout"
},
outleaves = { "stub.exe" },
commands = { "%{ins[1]} %{ins[2]} %{outs[1]}" }
}
build_plat_libs {
name = "libs",
arch = "i386",
plat = "msdos386",
}
installable {
name = "pkg",
map = {
"+tools",
"+libs",
"./include+pkg",
["$(PLATIND)/msdos386/boot.o"] = "+boot",
["$(PLATIND)/msdos386/stub.exe"] = "+stub_exe",
["$(PLATIND)/msdos386/libsys.a"] = "./libsys+lib",
}
}

View file

@ -0,0 +1,21 @@
include("plat/build.lua")
build_as {
name = "as",
arch = "i386",
}
build_ncg {
name = "ncg",
arch = "i386",
}
return installable {
name = "tools",
map = {
["$(PLATDEP)/msdos386/as"] = "+as",
["$(PLATDEP)/msdos386/ncg"] = "+ncg",
["$(PLATIND)/descr/msdos386"] = "./descr",
"util/opt+pkg",
}
}

77
plat/msdos386/descr Normal file
View file

@ -0,0 +1,77 @@
var w=4
var wa=2
var p=4
var pa=2
var s=2
var sa=2
var l=4
var la=2
var f=4
var fa=2
var d=8
var da=2
var x=8
var xa=2
var ARCH=i386
var PLATFORM=msdos386
var PLATFORMDIR={EM}/share/ack/{PLATFORM}
var CPP_F=-D__MSDOS__ -D__DOS__ -D_DOS
var ALIGN=-a0:2 -a1:2 -a2:2 -a3:2
var MACHOPT_F=-m8
var EGO_PLAT_FLAGS=-M{EM}/share/ack/ego/{ARCH}.descr
# Override the setting in fe so that files compiled for this platform can see
# the platform-specific headers.
var C_INCLUDES=-I{PLATFORMDIR}/include -I{EM}/share/ack/include/ansi
name be
from .m.g
to .s
program {EM}/lib/ack/{PLATFORM}/ncg
args <
stdout
need .e
end
name as
from .s.so
to .o
program {EM}/lib/ack/{PLATFORM}/as
args - -o > <
prep cond
end
name led
from .o.a
to .out
program {EM}/lib/ack/em_led
mapflag -l* LNAME={PLATFORMDIR}/lib*
mapflag -fp FLOATS={EM}/{ILIB}fp
args {ALIGN} \
({RTS}:.b=-u _i_main) \
(.e:{HEAD}={PLATFORMDIR}/boot.o) \
({RTS}:.ocm.bas.b={PLATFORMDIR}/c-ansi.o) \
({RTS}:.c={PLATFORMDIR}/c-ansi.o) \
({RTS}:.mod={PLATFORMDIR}/modula2.o) \
({RTS}:.p={PLATFORMDIR}/pascal.o) \
-o > < \
(.p:{TAIL}={PLATFORMDIR}/libpascal.a) \
(.b:{TAIL}={PLATFORMDIR}/libb.a) \
(.bas:{TAIL}={PLATFORMDIR}/libbasic.a) \
(.mod:{TAIL}={PLATFORMDIR}/libmodula2.a) \
(.ocm:{TAIL}={PLATFORMDIR}/liboccam.a) \
(.ocm.bas.mod.b.c.p:{TAIL}={PLATFORMDIR}/libc.a) \
{FLOATS?} \
(.e:{TAIL}={PLATFORMDIR}/libem.a \
{PLATFORMDIR}/libsys.a \
{PLATFORMDIR}/libc.a \
{PLATFORMDIR}/libem.a \
{PLATFORMDIR}/libend.a)
linker
end
name cv
from .out
to .exe
program {EM}/bin/aslod
args -p {PLATFORMDIR}/stub.exe < >
outfile msdos386.exe
end

View file

@ -0,0 +1,6 @@
#ifndef _ACK_PLAT_H
#define _ACK_PLAT_H
#define ACKCONF_WANT_O_TEXT_O_BINARY 1
#endif

View file

@ -0,0 +1,24 @@
include("plat/build.lua")
headermap = {}
packagemap = {}
local function addheader(h)
headermap[h] = "./"..h
packagemap["$(PLATIND)/msdos386/include/"..h] = "./"..h
end
addheader("ack/plat.h")
addheader("sys/types.h")
acklibrary {
name = "headers",
hdrs = headermap
}
installable {
name = "pkg",
map = packagemap
}

View file

@ -0,0 +1,9 @@
#ifndef _SYS_TYPES_H
#define _SYS_TYPES_H
typedef long pid_t;
typedef int mode_t;
typedef long time_t;
typedef long suseconds_t;
#endif

View file

@ -0,0 +1,14 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
.sect .bss
! This data block is used to store information about the current line number
! and file.
.define hol0
.comm hol0, 8

View file

@ -0,0 +1,16 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
.define _brk
_brk:
mov ebx, esp
mov eax, 1*4(ebx)
callf (realloc_ptr)
xor eax, eax
ret

View file

@ -0,0 +1,44 @@
bundle {
name = "headers",
srcs = {
"./libsysasm.h"
}
}
acklibrary {
name = "lib",
srcs = {
"./_hol0.s",
"./brk.s",
"./close.s",
"./errno.s",
"./getpid.s",
"./isatty.s",
"./rename.s",
"./sbrk.c",
"./sys_cpyin.s",
"./sys_cpyout.s",
"./sys_dpmidos.s",
"./sys_exists.s",
"./sys_getdate.s",
"./sys_gettime.s",
"./sys_isopen.s",
"./sys_isreadyr.s",
"./sys_rawcreat.s",
"./sys_rawlseek.s",
"./sys_rawopen.s",
"./sys_rawread.s",
"./sys_rawwrite.s",
"./sys_scpyout.s",
"./sys_xret.s",
"./unlink.s",
"plat/msdos/libsys+srcs",
},
deps = {
"plat/msdos/libsys+headers",
"+headers"
},
vars = {
plat = "msdos386"
}
}

View file

@ -0,0 +1,16 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
! Close a file.
.define _close
_close:
mov ebx, esp
mov ebx, 1*4(esp)
movb ah, 0x3E
int 0x21
jmp .sys_zret

View file

@ -0,0 +1,23 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
#define D(e) .define e; e
.sect .data
! Define various ACK error numbers. Note that these are *not* ANSI C
! errnos, and are used for different purposes.
D(ERANGE) = 1
D(ESET) = 2
D(EIDIVZ) = 6
D(EHEAP) = 17
D(EILLINS) = 18
D(EODDZ) = 19
D(ECASE) = 20
D(EBADMON) = 25

View file

@ -0,0 +1,39 @@
#
! $Source$
! $State$
! $Revision$
! Copyright (c) 2021--2022 TK Chia
!
! The authors hereby grant permission to use, copy, modify, distribute,
! and license this software and its documentation for any purpose, provided
! that existing copyright notices are retained in all copies and that this
! notice is included verbatim in any distributions. No written agreement,
! license, or royalty fee is required for any of the authorized uses.
! Modifications to this software may be copyrighted by their authors
! and need not follow the licensing terms described here, provided that
! the new terms are clearly indicated on the first page of each file where
! they apply.
#include "libsysasm.h"
! Get the current process identifier. For MS-DOS, use the Program Segment
! Prefix (PSP) segment as the PID, unless the system supports an actual
! `getpid' syscall, e.g. European MS-DOS 4.0.
!
! (Note that pid_t is a 32-bit type. This is to allow for PSP segments and
! MS-DOS PIDs above 0x7FFF.)
.define _getpid
_getpid:
movb ah, 0x87
stc
int 0x21
jnc .eur_dos
movb ah, 0x51
mov ebx, 0x210000
callf (interrupt_ptr)
xchg ebx, eax
.eur_dos:
movzx eax, ax
ret

View file

@ -0,0 +1,30 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
! Say whether a given file descriptor number refers to a terminal.
.define _isatty
_isatty:
mov ebx, esp
mov ebx, 4(ebx)
o16 mov ax, 0x4400
int 0x21
jc error
testb dl, dl
jz not_tty
mov eax, 1
ret
not_tty:
mov (_errno), 25 ! ENOTTY
not_tty_2:
xor eax, eax
ret
error:
push eax
call __sys_seterrno
pop ecx
jmp not_tty_2

View file

@ -0,0 +1,24 @@
/*
* © 2013 David Given
* © 2022 TK Chia
* This file is redistributable under the terms of the 3-clause BSD license.
* See the file 'Copying' in the root of the distribution for the full text.
*/
#ifndef LIBSYSASM_H
#define LIBSYSASM_H
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
! Size of transfer buffer, for 32-bit DPMI mode.
TRANSFER_BUFFER_SIZE = 32 * 1024
#endif

View file

@ -0,0 +1,42 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
! Rename a file.
.define _rename
_rename:
push edi
! Destination filename.
mov eax, 4+8(esp)
#define DEST_NAME_OFFSET (TRANSFER_BUFFER_SIZE / 2)
mov edx, DEST_NAME_OFFSET
mov ecx, edx
call .sys_sncpyout
jc 9f
xchg edi, eax
! Source filename.
mov eax, 4+4(esp)
xor edx, edx
call .sys_sncpyout
jc 9f
xchg edx, eax
! Make the DOS call.
movb ah, 0x56
call .sys_dpmidos
pop edi
jmp .sys_zret
9:
pop edi
jmp .sys_toolongret

View file

@ -0,0 +1,40 @@
/* $Source$
* $State$
* $Revision$
*/
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include "libsys.h"
extern char _end[1];
static char* current = _end;
void* sbrk(int increment)
{
char* old;
char* new;
if (increment == 0)
return current;
old = current;
new = old + increment;
if ((increment > 0) && (new <= old))
goto out_of_memory;
else if ((increment < 0) && (new >= old))
goto out_of_memory;
if (brk(new) < 0)
goto out_of_memory;
current = new;
return old;
out_of_memory:
errno = ENOMEM;
return OUT_OF_MEMORY;
}

View file

@ -0,0 +1,32 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
! .sys_cpyin: copy ecx bytes from the transfer buffer to es:eax (= ds:eax).
! Preserve all registers, except the flags.
.extern .sys_cpyin
.sys_cpyin:
push eax
push ecx
push esi
push edi
movzx esi, (transfer_buffer_ptr)
xchg edi, eax
mov ds, (pmode_ds)
mov eax, ecx
shr ecx, 2
rep movs
andb al, 3
movb cl, al
rep movsb
mov eax, ss
mov ds, eax
pop edi
pop esi
pop ecx
pop eax
ret

View file

@ -0,0 +1,34 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
! .sys_cpyout: copy ecx bytes starting from ds:eax to the transfer
! buffer. Return eax := offset of transfer buffer in the real mode data
! segment. Preserve all other registers, except the flags.
.extern .sys_cpyout
.sys_cpyout:
push ecx
push edx
push esi
push edi
xchg esi, eax
movzx edi, (transfer_buffer_ptr)
mov eax, edi
mov es, (pmode_ds)
mov edx, ecx
shr ecx, 2
rep movs
movb cl, dl
andb cl, 3
rep movsb
mov ecx, ss
mov es, ecx
pop edi
pop esi
pop edx
pop ecx
ret

View file

@ -0,0 +1,16 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
! .sys_dpmidos: simulate a call to real-mode int 0x21 with the current
! 16-bit register values.
.extern .sys_dpmidos
.sys_dpmidos:
movzx ebx, bx
or ebx, 0x210000
callf (interrupt_ptr)
ret

View file

@ -0,0 +1,21 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
! Say whether a file exists with the given name.
.define __sys_exists
__sys_exists:
mov eax, 4(esp)
call .sys_scpyout
jc 9f
xchg edx, eax
mov eax, 0x4300
call .sys_dpmidos
9:
sbb eax, eax
inc eax
ret

View file

@ -0,0 +1,18 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
! Get the current system date from MS-DOS.
.define __sys_getdate
__sys_getdate:
movb ah, 0x2a
int 0x21
mov ebx, esp
mov ebx, 4(ebx)
o16 mov 0(ebx), dx
o16 mov 2(ebx), cx
ret

View file

@ -0,0 +1,18 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
! Get the current system time from MS-DOS.
.define __sys_gettime
__sys_gettime:
movb ah, 0x2c
int 0x21
mov ebx, esp
mov ebx, 4(ebx)
o16 mov 0(ebx), cx
o16 mov 2(ebx), dx
ret

View file

@ -0,0 +1,18 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
! Say whether a given file descriptor number refers to a valid open file
! descriptor.
.define __sys_isopen
__sys_isopen:
mov ebx, 4(esp)
mov eax, 0x4400
int 0x21
sbb eax, eax
inc eax
ret

View file

@ -0,0 +1,22 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
! Say whether a file descriptor is ready for input, i.e. reading from the
! fd will immediately return something.
.define __sys_isreadyr
__sys_isreadyr:
mov ebx, esp
mov eax, 0x4406
mov ebx, 4(ebx)
int 0x21
jnc ok
movb al, 0
ok:
o16 cbw
cwde
ret

View file

@ -0,0 +1,28 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
! Create or truncate a file.
.define __sys_rawcreat
__sys_rawcreat:
! Copy filename to transfer buffer.
mov eax, 4(esp)
call .sys_scpyout
jc 9f
! Make the DOS call.
xchg edx, eax
movb ah, 0x3c
movb al, 8(esp)
call .sys_dpmidos
jmp .sys_axret
9:
jmp .sys_toolongret

View file

@ -0,0 +1,20 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
! Move the current file position for a file descriptor.
.define __sys_rawlseek
__sys_rawlseek:
movb ah, 0x42
mov ebx, esp
mov edx, 8(ebx)
mov ecx, edx
shr ecx, 16
movb al, 12(ebx)
mov ebx, 4(ebx)
int 0x21
jmp .sys_dxaxret

View file

@ -0,0 +1,24 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
! Open an existing file.
.define __sys_rawopen
__sys_rawopen:
! Copy filename to transfer buffer.
mov eax, 4(esp)
call .sys_scpyout
! Make the DOS call.
xchg edx, eax
movb ah, 0x3d
movb al, 8(esp)
call .sys_dpmidos
jmp .sys_axret

View file

@ -0,0 +1,50 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
! Read bytes from a file descriptor. These routines do not do any
! translation between CRLF and LF line endings.
!
! Note that, for MS-DOS, a raw "write" request of zero bytes will truncate
! (or extend) the file to the current file position.
.define __sys_rawread
__sys_rawread:
file_handle = 1*4
write_buffer = 2*4
amount_to_read = 3*4
mov eax, amount_to_read(esp)
mov ecx, TRANSFER_BUFFER_SIZE
cmp eax, ecx
jge 2f
mov ecx, eax
2:
! Read from DOS into the transfer buffer.
movb ah, 0x3f
o16 mov dx, (transfer_buffer_ptr)
o16 mov bx, file_handle(esp)
call .sys_dpmidos
jc 3f
! Copy eax bytes out of the transfer buffer.
movzx ecx, ax
mov eax, write_buffer(esp)
call .sys_cpyin
xchg eax, ecx
ret
3:
! Process errors.
push eax
call __sys_seterrno
pop ecx
ret

View file

@ -0,0 +1,48 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
! Write bytes to/to a file descriptor. These routines do not do any
! translation between CRLF and LF line endings.
!
! Note that, for MS-DOS, a raw "write" request of zero bytes will truncate
! (or extend) the file to the current file position.
.define __sys_rawwrite
__sys_rawwrite:
file_handle = 1*4
read_buffer = 2*4
amount_to_write = 3*4
mov eax, amount_to_write(esp)
mov ecx, TRANSFER_BUFFER_SIZE
cmp eax, ecx
jge 2f
mov ecx, eax
2:
! Copy ecx bytes into the transfer buffer.
mov eax, read_buffer(esp)
call .sys_cpyout
! Write from the transfer buffer to DOS.
xchg edx, eax
movb ah, 0x40
o16 mov bx, file_handle(esp)
call .sys_dpmidos
movzx eax, ax
jnc exit
push eax
call __sys_seterrno
pop ecx
exit:
ret
! vim: sw=4 ts=4 et

View file

@ -0,0 +1,58 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
! .sys_scpyout: copy the ASCIIZ string starting from ds:eax to the
! transfer buffer. The resulting ASCIIZ string, including the final null,
! should fit into the entire transfer buffer. Return eax := offset of
! transfer buffer in real mode data segment. Return CF = 1 if and only if
! the ASCIIZ string is too long to fit. Preserve all other registers,
! except the flags.
!
! .sys_sncpyout: copy the ASCIIZ string starting from ds:eax to offset
! edx into the transfer buffer. The resulting ASCIIZ string should occupy
! at most ecx bytes, including the final null. Return eax : offset of
! string in real mode data segment. Return CF = 1 if and only if the ASCIIZ
! string is too long to fit. Preserve all other registers, except the
! flags.
.extern .sys_scpyout
.extern .sys_sncpyout
.sys_scpyout:
push ecx
push edx
mov ecx, TRANSFER_BUFFER_SIZE
xor edx, edx
jmp .cont
.sys_sncpyout:
push ecx
push edx
.cont:
push esi
push edi
mov esi, eax
movzx edi, (transfer_buffer_ptr)
add edi, edx
mov edx, edi
mov es, (pmode_ds)
jcxz .error
.loop:
lodsb
stosb
testb al, al
loopnz .loop
jz .ok
.error:
stc
.ok:
xchg edx, eax
mov edx, ss
mov es, edx
pop edi
pop esi
pop edx
pop ecx
ret

View file

@ -0,0 +1,54 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
! .sys_zret: if the carry flag is set, then set `errno' from the DOS error
! code in ax, and return an int -1. If the carry flag is clear, just
! return an int zero.
!
! .sys_axret: if the carry flag is set, then set `errno' from the DOS error
! code in ax, and return a shortword -1. If the carry flag is clear, just
! return eax := ax as a return value.
!
! .sys_dxaxret: same as .sys_axret, but return -1 or eax := dx:ax as a
! return value.
!
! .sys_toolongret: a file name is too long: set `errno' to E2BIG, and return
! -1.
.extern .sys_zret
.extern .sys_axret
.extern .sys_dxaxret
.extern .sys_toolongret
.sys_zret:
jc error
xor eax, eax
no_error:
ret
.sys_axret:
jc error
movzx eax, ax
ret
.sys_dxaxret:
jc error
shl edx, 16
o16 mov dx, ax
mov eax, edx
ret
.sys_toolongret:
mov (_errno), 7 ! E2BIG
or eax, -1
ret
error:
movzx eax, ax
push eax
call __sys_seterrno
pop ecx
ret

View file

@ -0,0 +1,23 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
! Remove a file.
.define _unlink
_unlink:
! Copy filename to transfer buffer.
mov eax, 4(esp)
call .sys_scpyout
! Make the DOS call.
xchg edx, eax
movb ah, 0x41
call .sys_dpmidos
jmp .sys_zret

414
plat/msdos386/stub.s Normal file
View file

@ -0,0 +1,414 @@
#
! $Source$
! $State$
! $Revision$
#include "libsysasm.h"
.use16
exe_header:
.data2 0x5a4d ! magic number
.data2 exe_last_page ! number of bytes in last loadable page
.data2 exe_text_pages ! size of .exe, in pages
.data2 0 ! number of relocation entries
.data2 0 ! start of loadable area, in 16-byte paragraphs
.data2 exe_ram_paras ! required RAM size, in 16-byte paragraphs
.data2 exe_ram_paras ! maximum RAM siz, in 16-byte paragraphs
.data2 0 ! initial SS, relative to program
.data2 stack ! initial SP
.data2 0 ! checksum (ignored)
.data2 exe_start ! initial IP
.data2 0 ! initial CS, relative to program
.data2 0 ! offset of relocation table
.data2 0 ! overlay number
exe_start:
! On entry, DS=ES=PSP. Make DS=CS, so we're running in tiny mode.
push cs
pop ds
mov (rseg), ds
mov (pspseg), es
! Ensure that at least two handles are available by blindly closing some.
mov bx, 19 ! close handle
movb ah, 0x3e
int 0x21
mov bx, 18 ! close handle
movb ah, 0x3e
int 0x21
! Scan the environment to find the program name.
eseg mov es, (0x2c) ! environment pointer segment from PSP
xor di, di
xorb al, al
mov cx, 0xffff
cld
1:
repne scasb ! find end of next string
eseg cmpb (di), 0
jnz 1b
2:
add di, 3
mov si, di
repne scasb
! es:si now points at the program name, and es:di at one past the end.
mov cx, di
sub cx, si
sub sp, cx ! allocate space on stack
mov di, sp ! es:si source, ds:di dest
1:
eseg movb al, (si)
inc si
movb (di), al
inc di
cmpb al, 0
jnz 1b
! Open the file. (0x5f)
mov dx, sp
mov ax, 0x3d00
int 0x21
jc no_file
mov (fh), ax
add sp, cx ! don't need filename any more
! Get the file length (== text len).
mov bx, ax
mov ax, 0x4202
xor cx, cx ! high offset
xor dx, dx ! low offset
int 0x21 ! lseek
mov (pmemlen+0), ax
mov (pmemlen+2), dx
! Initialise DPMI.
mov ax, 0x1687
int 0x2f
or ax, ax
jnz no_dpmi
mov (pmode_switch+0), di ! write back PMODE switch routine
mov (pmode_switch+2), es
or si, si ! do we need a DPMI private area?
jz 1f
mov bx, si
movb ah, 0x48
int 0x21 ! allocate memory from DOS
mov es, ax ! data area segment -> es
1:
! Switch to protected mode.
mov ax, 1 ! 32-bit app
callf (pmode_switch)
jc bad_dpmi
! We're now in protected mode.
mov (psegcs), cs
mov (psegds), ds
! Allocate space for the code segment.
mov ax, 0x0000
mov cx, 1
int 0x31 ! allocate LDT
jc bad_dpmi
mov fs, ax
mov (psegcs32), ax
mov cx, (pmemlen+0)
mov bx, (pmemlen+2)
mov ax, 0x0501
int 0x31 ! allocate linear address
jc bad_dpmi
mov (pmemhandle+0), di
mov (pmemhandle+2), si
mov dx, cx
mov cx, bx
mov bx, fs
mov ax, 0x0007
int 0x31 ! set segment base address
jc bad_dpmi
mov bx, fs
mov dx, (pmemlen+0)
mov cx, (pmemlen+2)
sub dx, 1
sbb cx, 0
mov ax, 0x0008
int 0x31 ! set segment limit
mov cx, cs
and cx, 3
shl cx, 5
or cx, 0xc09b ! 32-bit, big, code, non-conforming, readable
mov bx, fs
mov ax, 0x0009
int 0x31 ! set descriptor access rights
! Allocate the data segment (as a simple clone of the code segment). (10e)
mov ax, 0x000a
mov bx, fs
int 0x31
mov es, ax
mov (psegds32), ax
mov cx, ax
and cx, 3
shl cx, 5
or cx, 0xc093 ! 32-bit, big, data, r/w, expand-up
mov bx, es
mov ax, 0x0009
int 0x31 ! set descriptor access rights
! Load the program.
mov bx, (fh)
mov ax, 0x4200
xor cx, cx ! high offset
mov dx, text_top ! low offset
int 0x21 ! lseek
o32 xor edi, edi ! destination 32-bit register
1:
movb ah, 0x3f
mov bx, (fh)
mov cx, TRANSFER_BUFFER_SIZE
mov dx, transfer_buffer
o32 push edi
call int21 ! read up to 32kB into transfer buffer
o32 pop edi
cmp ax, 0
je 2f
o32 movzx ecx, ax ! number of bytes read
o32 mov esi, transfer_buffer
cld
rep a32 movsb
jmp 1b
2:
! Close the file.
movb ah, 0x3e
mov bx, (fh)
int 0x21 ! close
! Jump to the new segment and enter 32-bit mode!
o32 movzx eax, (psegcs)
o32 movzx ebx, (psegds)
o32 movzx ecx, (rseg)
o32 mov edx, realloc
o32 mov esi, interruptf
o32 mov edi, transfer_buffer
o32 movzx ebp, (pspseg)
push es
pop ds
push fs
push 0
retf ! 19b
! Helper routine which reallocates the linear block that the 32-bit code
! is running from. This can't happen from inside the 32-bit code itself
! because it might move.
!
! On entry, ds and ss are ignored. On exit, ds and es are set to the
! 32-bit segment.
! eax: new block size
realloc:
cseg mov ds, (psegds)
cli ! atomically switch stacks
o32 mov (dpmi_ebp), esp ! yes, saving esp into the ebp field
mov (dpmi_ss), ss
mov ss, (psegds)
mov sp, dosstack
sti
pusha
o32 add eax, 1024*1024 - 1
o32 and eax, ~[1024*1024 - 1]
o32 mov (pmemlen), eax
mov cx, (pmemlen+0)
mov bx, (pmemlen+2)
mov di, (pmemhandle+0)
mov si, (pmemhandle+2)
mov ax, 0x0503
int 0x31 ! resize memory block
jc bad_dpmi
mov (pmemhandle+0), di
mov (pmemhandle+2), si
mov (pmemaddr+0), cx
mov (pmemaddr+2), bx
mov bx, (psegcs32)
mov dx, (pmemaddr+0)
mov cx, (pmemaddr+2)
mov ax, 0x0007
int 0x31 ! set cs linear address
jc bad_dpmi
mov bx, (psegds32)
int 0x31 ! set ds linear address
jc bad_dpmi
mov dx, (pmemlen+0)
mov cx, (pmemlen+2)
sub dx, 1
sbb cx, 0
mov ax, 0x0008
int 0x31 ! set ds segment limit
jc bad_dpmi
popa
o32 mov eax, (pmemlen)
cli ! atomically switch stacks back
mov ss, (dpmi_ss)
o32 mov esp, (dpmi_ebp)
mov es, (psegds32)
mov ds, (psegds32)
sti
o32 retf
bad_dpmi:
mov si, bad_dpmi_msg
jmp exit_with_error
no_file:
mov si, no_file_msg
jmp exit_with_error
no_dpmi:
mov si, no_dpmi_msg
! fall through
! Displays the message in si and exits.
! This uses a loop because from protected mode any interrupt which
! takes a pointer parameter requires special treatment.
exit_with_error:
movb dl, (si)
cmpb dl, 0
je 1f
inc si
movb ah, 2
int 0x21 ! print character
jmp exit_with_error
1:
mov ax, 0x4cff
int 0x21 ! terminate with error code al
! Simulate DOS interrupt.
int21:
o32 movzx ebx, bx
o32 or ebx, 0x210000
! Simulate interrupt in the high half of ebx.
interrupt:
o32 mov (dpmi_eax), eax
o32 mov (dpmi_ebx), ebx
o32 mov (dpmi_ecx), ecx
o32 mov (dpmi_edx), edx
o32 mov (dpmi_esi), esi
o32 mov (dpmi_edi), edi
pushf
pop (dpmi_flags)
mov ax, (rseg)
mov (dpmi_ds), ax
mov (dpmi_ss), ax
push es
mov (dpmi_sp), dosstack ! auto stack is too small
push ds
pop es
o32 mov edi, dpmi_edi
mov ax, 0x300
o32 shr ebx, 16
xor cx, cx
int 0x31 ! simulate DOS interrupt
pop es
o32 mov eax, (dpmi_eax)
o32 mov ebx, (dpmi_ebx)
o32 mov ecx, (dpmi_ecx)
o32 mov edx, (dpmi_edx)
o32 mov esi, (dpmi_esi)
o32 mov edi, (dpmi_edi)
push (dpmi_flags)
popf
ret
! Far call wrapper around interrupt.
interruptf:
push ds
cseg mov ds, (psegds)
call interrupt
pop ds
o32 retf
bad_dpmi_msg:
.asciz "DPMI error during setup"
no_file_msg:
.asciz "Couldn't open .exe"
no_dpmi_msg:
.asciz "No DPMI host installed"
.align 4
text_top:
exe_text_pages = [text_top - exe_header + 511] / 512
exe_last_page = [text_top - exe_header] % 512
.sect .rom
.sect .bss
bss_start:
dpmi_edi: .space 4
dpmi_esi: .space 4
dpmi_ebp: .space 4
.space 4 ! reserved
dpmi_ebx: .space 4
dpmi_edx: .space 4
dpmi_ecx: .space 4
dpmi_eax: .space 4
dpmi_flags: .space 2
dpmi_es: .space 2
dpmi_ds: .space 2
dpmi_fs: .space 2
dpmi_gs: .space 2
dpmi_ip: .space 2
dpmi_cs: .space 2
dpmi_sp: .space 2
dpmi_ss: .space 2
pmode_switch: .space 4
rseg: .space 2 ! real mode
pspseg: .space 2 ! real mode
psegcs: .space 2 ! protected mode 16-bit code segment
psegds: .space 2 ! protected mode 16-bit data segment
psegcs32: .space 2 ! protected mode 32-bit code segment
psegds32: .space 2 ! protected mode 32-bit data segment
pmemaddr: .space 4 ! protected mode linear memory address
pmemhandle: .space 4 ! protected mode linear memory handle
pmemlen: .space 4 ! protected mode linear memory length
fh: .space 2
.space 512
stack:
.space 512
dosstack:
transfer_buffer:
.space TRANSFER_BUFFER_SIZE
bss_top:
exe_ram_paras = [bss_top - bss_start + 15] / 16
! vim: ts=4 sw=4 et ft=asm

View file

@ -84,5 +84,5 @@ name cv
mapflag -i LOD=amzlod
program {EM}/bin/{LOD}
args < >
outfile msdos86.exe
outfile msdos86.com
end

View file

@ -3,41 +3,28 @@ acklibrary {
srcs = {
"./brk.c",
"./close.s",
"./creat.c",
"./errno.s",
"./getpid.s",
"./gettimeofday.c",
"./_hol0.s",
"./isatty.s",
"./kill.c",
"./lseek.c",
"./open.c",
"./read.c",
"./setmode.c",
"./signal.c",
"./rename.s",
"./sys_exists.s",
"./sys_fdmodes.c",
"./sys_getdate.s",
"./sys_getmode.c",
"./sys_gettime.s",
"./sys_initmain.c",
"./sys_iseof.c",
"./sys_isopen.s",
"./sys_isreadyr.s",
"./sys_rawcreat.s",
"./sys_rawlseek.s",
"./sys_rawopen.s",
"./sys_rawrw.s",
"./sys_seteof.c",
"./sys_seterrno.c",
"./sys_setmode.c",
"./sys_xret.s",
"./unlink.s",
"./write.c",
"plat/msdos/libsys+srcs",
},
deps = {
"lang/cem/libcc.ansi/headers+headers",
"plat/msdos86/include+headers",
"plat/msdos/libsys+headers",
},
vars = {
plat = "msdos86"

View file

@ -0,0 +1,26 @@
#
! $Source$
! $State$
! $Revision$
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
! Rename a file.
.define _rename
_rename:
mov bx, sp
push di
mov dx, 2(bx)
mov di, 4(bx)
movb ah, 0x56
int 0x21
pop di
jmp .sys_zret

View file

@ -1,61 +0,0 @@
/* $Source$
* $State$
* $Revision$
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include "libsys.h"
ssize_t write(int fd, void* buffer, size_t count)
{
static const char crlf[2] = "\r\n";
int i;
char *p, *q;
size_t left, n;
ssize_t r, tot;
if (!count)
return 0;
if (_sys_getmode(fd) == O_BINARY)
return _sys_rawwrite(fd, buffer, count);
/* If the file descriptor is an O_TEXT fd, translate LFs to CRLFs. */
p = buffer;
left = count;
tot = 0;
while (left)
{
q = memchr(p, '\n', left);
if (!q)
return _sys_rawwrite(fd, p, left);
n = q - p;
if (n)
{
r = _sys_rawwrite(fd, p, n);
if (r <= 0)
return tot ? tot : r;
tot += r;
p += r;
left -= r;
if (r != n)
break;
}
r = _sys_rawwrite(fd, crlf, sizeof crlf);
if (r != 2)
{
if (r > 0)
r = 0;
return tot ? tot : r;
}
++tot;
++p;
--left;
}
return tot;
}

View file

@ -302,8 +302,23 @@ argv: .data2 exename, 0
envp: .data2 0
exename: .asciz 'pc86.img'
! A degenerate "hard disk partition table" covering this boot
! sector (possibly needed if booting from a USB drive).
.seek 0x1BE
.data1 0x80 ! boot indicator (0x80 = active)
.data1 0x00 ! partition start head
.data1 0x01 ! partition start sector and start track high
.data1 0x00 ! partition start track low
.data1 0x7F ! OS or filesystem indicator
.data1 0xFF ! partition end head
.data1 0xFF ! partition end sector and end track high
.data1 0xFF ! partition end track low
.data4 0 ! partition start LBA
.data4 0xFFFFFFFF ! length of partition in sectors
! ...and we need this to fool the PC into booting our boot sector.
.seek 0x1FE
.data2 0xAA55

View file

@ -12,29 +12,9 @@ errcho() {
get_test_output() {
case $method in
qemu-system-*)
if ! command -v $method >/dev/null 2>&1 ; then
errcho "Warning: $method not installed, skipping test"
echo "@@SKIPPED"
exit 0
fi
case $method in
qemu-system-i386) img="-drive file=$img,if=floppy,format=raw" ;;
qemu-system-ppc) img="-kernel $img" ;;
esac
$timeoutprog -t $timeout -- $method -nographic $img 2>&1
;;
qemu-*)
if ! command -v $method >/dev/null 2>&1 ; then
errcho "Warning: $method not installed, skipping test"
echo "@@SKIPPED"
exit 0
fi
$method $img 2>&1
errcho "qemu tests no longer supported (method $method)"
exit 1
;;
*)

View file

@ -42,11 +42,11 @@ $(OBJDIR)/%.o: %.c
$(OBJDIR)/%.pdf: doc/%.n doc/LLgen.refs
@mkdir -p $(dir $@)
refer $< | groff -G -e -g -p -ms -Tpdf $< > $@
refer -p doc/LLgen.refs $< | groff -G -e -g -p -ms -Tpdf $< > $@
$(OBJDIR)/%/index.html: doc/%.n doc/LLgen.refs
@mkdir -p $(dir $@)
refer $< | (cd $(dir $@) && groff -I$(PWD) -G -e -g -p -ms -Thtml) > $@
(cd $(dir $@) && refer -p $(PWD)/doc/LLgen.refs | groff -G -e -g -p -ms -Thtml) < $< > $@
doc:: $(PDFS) $(HTMLS)

View file

@ -10,7 +10,6 @@
\}\
\}
.R1
database doc/LLgen.refs
accumulate
.R2
.ND

View file

@ -1,4 +1,3 @@
.RP
.TL

View file

@ -3,7 +3,7 @@
aslod \- ACK simple loader
.SH SYNOPSIS
.B aslod
[\-h] [\-v] inputfile outputfile
[\-h] [\-v] [\-p prefixfile] inputfile outputfile
.SH DESCRIPTION
.I aslod
converts an absolute ack.out file into a simple binary memory dump.
@ -16,5 +16,8 @@ The file must have all references resolved and be linked to a
fixed address. aslod will dump the segments, in order, such
that the first byte of TEXT is at offset 0 in the file
(regardless of where it is in memory).
.PP
If a prefix file is specified, this is prepended to the output before writing.
No changes to the output itself are made.
.SH "SEE ALSO"
ack.out(5)

View file

@ -37,6 +37,7 @@ struct outsect outsect[S_MAX];
char* stringarea;
char* outputfile = NULL; /* Name of output file, or NULL */
char* prefixfile = NULL; /* Name of prefix file, or NULL */
char* program; /* Name of current program: argv[0] */
FILE* input; /* Input stream */
@ -136,6 +137,19 @@ void emits(struct outsect* section, struct outsect* nextsect)
}
}
void emitprefixfile(void)
{
FILE* fp = fopen(prefixfile, "rb");
while (!feof(fp))
{
char buffer[BUFSIZ];
size_t blocksize = fread(buffer, 1, BUFSIZ, fp);
writef(buffer, 1, blocksize);
}
fclose(fp);
}
/* Macros from modules/src/object/obj.h */
#define Xchar(ch) ((ch) & 0377)
@ -204,6 +218,12 @@ int main(int argc, char* argv[])
verbose = true;
break;
case 'p':
argv++;
argc--;
prefixfile = argv[1];
break;
default:
syntaxerror:
fatal("syntax error --- try -h for help");
@ -288,6 +308,8 @@ int main(int argc, char* argv[])
/* And go! */
if (prefixfile)
emitprefixfile();
emits(&outsect[TEXT], &outsect[ROM]);
emits(&outsect[ROM], &outsect[DATA]);
emits(&outsect[DATA], NULL);

View file

@ -18,7 +18,7 @@ normalrule {
},
outleaves = { "enterkeyw.c" },
commands = {
"$LUA %{ins[1]} < %{ins[2]} > %{outs[1]}"
"$(LUA) %{ins[1]} < %{ins[2]} > %{outs[1]}"
}
}