Merge pull request #269 from davidgiven/dtrg-msdos386

Add a plat for 32-bit DOS executables.
This commit is contained in:
David Given 2022-08-19 21:00:43 +02:00 committed by GitHub
commit 0165c07c7b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
61 changed files with 2277 additions and 529 deletions

3
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

View file

@ -12,6 +12,7 @@ vars.plats = {
"linuxppc",
"linuxmips",
"msdos86",
"msdos386",
"osx386",
"osxppc",
"pc86",

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

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

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

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

@ -0,0 +1,236 @@
#
! $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)e
! si: pointer to interrupt routine (in stub)
! di: pointer to transfer buffer (in stub)
! Resize the segment to include the BSS.
o16 cseg mov (realloc_ptr+4), ax
cseg mov (realloc_ptr+0), edx
cseg o16 mov (pmode_cs), ax
cseg o16 mov (pmode_ds), bx
cseg o16 mov (rmode), cx
cseg o16 mov (interrupt_ptr+4), ax
cseg mov (interrupt_ptr+0), esi
cseg o16 mov (transfer_buffer_ptr), di
mov eax, __end
cseg callf (realloc_ptr)
! Clear BSS.
mov edi, begbss
mov ecx, endbss+1
sub ecx, edi
shr ecx, 1
xor eax, eax
cld
rep stosw
! 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
mov es, ax
o16 mov bx, ax
o16 mov (.doshandle), bx
mov es, bx
xor ecx, ecx
xor edx, edx
o16 mov ax, 0x0007
int 0x31 ! set base address to 0
o16 mov cx, 0x0010
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.
movb ah, 0x62
int 0x21
movzx esi, bx
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,52 @@
include("plat/build.lua")
ackfile {
name = "boot",
srcs = { "./boot.s" },
vars = { plat = "msdos386" }
}
ackfile {
name = "stub",
srcs = { "./stub.s" },
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,19 @@
#
! $Source$
! $State$
! $Revision$
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.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,18 @@
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
.define _brk
_brk:
mov ebx, esp
mov eax, 1*4(ebx)
callf (realloc_ptr)
xor eax, eax
ret

View file

@ -0,0 +1,35 @@
acklibrary {
name = "lib",
srcs = {
"./_hol0.s",
"./brk.s",
"./close.s",
"./errno.s",
"./getpid.s",
"./isatty.s",
"./rename.s",
"./sbrk.c",
"./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_xret.s",
"./unlink.s",
"plat/msdos/libsys+srcs",
},
deps = {
"lang/cem/libcc.ansi/headers+headers",
"plat/msdos386/include+headers",
"plat/msdos/libsys+headers",
},
vars = {
plat = "msdos386"
}
}

View file

@ -0,0 +1,23 @@
#
! $Source$
! $State$
! $Revision$
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
! 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,28 @@
#
! $Source$
! $State$
! $Revision$
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
#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,45 @@
#
! $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.
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
! 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
int 0x21
xchg ebx, eax
.eur_dos:
xor edx, edx
ret

View file

@ -0,0 +1,37 @@
#
! $Source$
! $State$
! $Revision$
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
! 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,54 @@
#
! $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 ebx, esp
push esi
push edi
! Source filename.
mov esi, 4(ebx)
movzx edi, (transfer_buffer_ptr)
mov es, (pmode_ds)
cld
1:
lodsb
stosb
testb al, al
jnz 1b
! Destination filename.
mov eax, edi
mov esi, 8(ebx)
1:
lodsb
stosb
testb al, al
jnz 1b
! Make the DOS call.
o16 mov dx, (transfer_buffer_ptr)
o16 mov di, ax
movb ah, 0x56
or ebx, 0x210000
callf (interrupt_ptr)
pop edi
pop esi
jmp .sys_zret

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,25 @@
#
! $Source$
! $State$
! $Revision$
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
! Say whether a file exists with the given name.
.define __sys_exists
__sys_exists:
mov ebx, esp
mov edx, 4(ebx)
mov eax, 0x4300
int 0x21
sbb eax, eax
inc eax
ret

View file

@ -0,0 +1,25 @@
#
! $Source$
! $State$
! $Revision$
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
! 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,26 @@
#
! $Source$
! $State$
! $Revision$
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
! Get the current system time from MS-DOS.
.define __sys_gettime
__sys_gettime:
int 3
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,26 @@
#
! $Source$
! $State$
! $Revision$
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
! Say whether a given file descriptor number refers to a valid open file
! descriptor.
.define __sys_isopen
__sys_isopen:
mov ebx, esp
mov ebx, 4(bx)
mov eax, 0x4400
int 0x21
sbb eax, eax
inc eax
ret

View file

@ -0,0 +1,29 @@
#
! $Source$
! $State$
! $Revision$
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
! 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,45 @@
#
! $Source$
! $State$
! $Revision$
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
! Create or truncate a file.
.define __sys_rawcreat
__sys_rawcreat:
! Copy filename to transfer buffer.
mov ebx, esp
push esi
push edi
mov esi, 4(ebx)
movzx edi, (transfer_buffer_ptr)
mov es, (pmode_ds)
cld
1:
lodsb
stosb
testb al, al
jnz 1b
! Make the DOS call.
movb ah, 0x3c
movzx edx, (transfer_buffer_ptr)
movb al, 8(ebx)
or ebx, 0x210000
callf (interrupt_ptr)
pop edi
pop esi
jmp .sys_axret

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
! Move the current file position for a file descriptor.
.define __sys_rawlseek
__sys_rawlseek:
movb ah, 0x42
mov ebx, esp
mov edx, 8(bx)
mov ecx, 12(bx)
movb al, 16(bx)
mov ebx, 4(bx)
int 0x21
jmp .sys_dxaxret

View file

@ -0,0 +1,45 @@
#
! $Source$
! $State$
! $Revision$
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
! Open an existing file.
.define __sys_rawopen
__sys_rawopen:
! Copy filename to transfer buffer.
mov ebx, esp
push esi
push edi
mov esi, 4(ebx)
movzx edi, (transfer_buffer_ptr)
mov es, (pmode_ds)
cld
1:
lodsb
stosb
testb al, al
jnz 1b
! Make the DOS call.
movb ah, 0x3d
o16 mov dx, (transfer_buffer_ptr)
movb al, 8(ebx)
or ebx, 0x210000
callf (interrupt_ptr)
pop edi
pop esi
jmp .sys_axret

View file

@ -0,0 +1,74 @@
#
! $Source$
! $State$
! $Revision$
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
! 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:
enter 0, 0
push esi
push edi
file_handle = 2*4
write_buffer = 3*4
amount_to_read = 4*4
mov eax, amount_to_read(ebp)
mov ecx, 32*1024
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(ebp)
or ebx, 0x210000
callf (interrupt_ptr)
jnc success
! Process errors.
push eax
call __sys_seterrno
pop ecx
jmp exit
success:
! Copy eax bytes out of the transfer buffer.
push eax
mov ecx, eax
movzx esi, (transfer_buffer_ptr)
mov edi, write_buffer(ebp)
mov es, (pmode_ds)
cld
1:
eseg lodsb
movb (edi), al
inc edi
loop 1b
pop eax
exit:
pop edi
pop esi
leave
ret

View file

@ -0,0 +1,66 @@
#
! $Source$
! $State$
! $Revision$
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
! 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:
enter 0, 0
push esi
push edi
file_handle = 2*4
read_buffer = 3*4
amount_to_write = 4*4
mov eax, amount_to_write(ebp)
mov ecx, 32*1024 ! size of transfer buffer
cmp eax, ecx
jge 2f
mov ecx, eax
2:
! Copy ecx bytes into the transfer buffer.
push ecx
mov esi, read_buffer(ebp)
movzx edi, (transfer_buffer_ptr)
mov es, (pmode_ds)
cld
rep movsb
pop ecx
! Write from the transfer buffer to DOS.
movb ah, 0x40
o16 mov dx, (transfer_buffer_ptr)
o16 mov bx, file_handle(ebp)
or ebx, 0x210000
callf (interrupt_ptr)
jnc exit
push eax
call __sys_seterrno
pop ecx
exit:
pop edi
pop esi
leave
ret
! vim: sw=4 ts=4 et

View file

@ -0,0 +1,50 @@
#
! $Source$
! $State$
! $Revision$
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
! .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 ax as a return value.
!
! .sys_dxaxret: same as .sys_axret, but return -1 or dx:ax as a return value.
.extern .sys_zret
.extern .sys_axret
.extern .sys_dxaxret
.sys_zret:
jc error
xor eax, eax
no_error:
ret
.sys_axret:
jc error
ret
.sys_dxaxret:
jc error
shl edx, 16
o16 mov dx, ax
mov eax, edx
ret
error:
movzx eax, ax
push eax
call __sys_seterrno
pop ecx
ret

View file

@ -0,0 +1,44 @@
#
! $Source$
! $State$
! $Revision$
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
! Remove a file.
.define _unlink
_unlink:
! Copy filename to transfer buffer.
mov ebx, esp
push esi
push edi
mov esi, 4(ebx)
movzx edi, (transfer_buffer_ptr)
mov es, (pmode_ds)
cld
1:
lodsb
stosb
testb al, al
jnz 1b
! Make the DOS call.
movzx edi, (transfer_buffer_ptr)
movb ah, 0x41
or ebx, 0x210000
callf (interrupt_ptr)
pop edi
pop esi
jmp .sys_zret

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

@ -0,0 +1,410 @@
#
! $Source$
! $State$
! $Revision$
! Declare segments (the order is important).
.sect .text
.sect .rom
.sect .data
.sect .bss
.sect .text
.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 es, 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, es
mov ax, 0x0007
int 0x31 ! set segment base address
jc bad_dpmi
mov bx, es
mov dx, (pmemlen+0)
mov cx, (pmemlen+2)
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, es
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, es
int 0x31
mov fs, 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, fs
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
o32 rep 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
push es
pop ds
push es
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 is 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, (psegds32)
mov dx, (pmemlen+0)
mov cx, (pmemlen+2)
mov ax, 0x0008
int 0x31 ! set ds segment limit
jc bad_dpmi
mov dx, (pmemaddr+0)
mov cx, (pmemaddr+2)
mov ax, 0x0007
int 0x31 ! set ds linear address
jc bad_dpmi
mov bx, (psegcs32)
int 0x31 ! set cs linear address
jc bad_dpmi
popa
o32 mov eax, (pmemlen)
cli ! atomically switch stacks back
mov ss, (dpmi_ss)
o32 mov esp, (dpmi_ebp)
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 or ebx, 0x210000
! Simulate interrupt in the high half of ebx.
interrupt:
mov (dpmi_eax), ax
mov (dpmi_ebx), bx
mov (dpmi_ecx), cx
mov (dpmi_edx), dx
mov (dpmi_esi), si
mov (dpmi_edi), di
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
int 0x31 ! simulate DOS interrupt
pop es
o32 movzx eax, (dpmi_eax)
o32 movzx ebx, (dpmi_ebx)
o32 movzx ecx, (dpmi_ecx)
o32 movzx edx, (dpmi_edx)
o32 movzx esi, (dpmi_esi)
o32 movzx 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_SIZE = 32*1024
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,42 +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",
"./rename.s",
"./setmode.c",
"./signal.c",
"./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

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

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