Files
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

1131 lines
25 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

$title('assembler for ddt86')
$date(6/15/81)
$compact
$optimize(2)
ddtasm: do;
declare
logical literally 'byte',
true literally '1',
false literally '0',
tab literally '09h',
cr literally '0dh',
lf literally '0ah';
declare
tab$ptrs (5) address external,
nops (5) byte external,
opn$in (6) byte external,
optab (480) byte external;
declare
token (32) byte, /* ASCII token */
token$word address at (.token), /* used for word compares */
token$len byte, /* number of chars in token */
token$type byte, /* number, string, or delimiter */
string$type literally '0',
number$type literally '1',
delim$type literally '2',
token$value address, /* value if token$type = number */
next$ch byte; /* next char of input stream */
declare
assem$ptr pointer,
assem$offset address at (.assem$ptr),
assem$segment address at (.assem$ptr+2),
assem$byte based assem$ptr byte;
declare
seg$reg (8) byte EXTERNAL,
reg16 (16) byte EXTERNAL,
reg8 (16) byte EXTERNAL,
base$reg (*) byte data ('BX', 'BP'),
index$reg (*) byte data ('SI', 'DI');
declare
op$base address,
(op1, op2, op based op$base) structure (
seg$reg$num byte,
reg$num byte,
base$reg$num byte,
index$reg$num byte,
seg$reg$type logical,
reg$16$type logical,
reg$8$type logical,
num$type logical,
acc$type logical,
null$type logical,
far$type logical,
mod$rm$type logical,
w$bit byte,
bw$prefix byte,
sub$type byte,
seg$value address,
value address);
/*
op.sub$types
1 reg8
2 reg16
3 [num]
4 [pointer reg]
5 [index reg]
6 [pointer reg + index reg]
7 num [pointer reg]
8 num [index reg]
9 num [pointer reg + index reg]
*/
declare
prefix (3) byte, /* up to 3 prefixes */
nprefix byte, /* number of prefixes */
seg$prefix$flag byte,
lock$flag byte,
rep$flag byte;
declare
mod$bits byte,
reg$bits byte,
rm$bits byte,
op$type byte,
mnemonic$index byte,
instr$1 byte,
disp$len byte, /* number of bytes in optional disp field */
disp$word address; /* value of optional disp field */
declare
delimiters (*) byte data (',', ':', '+', cr, ' ', tab),
left$bracket byte data ('['),
right$bracket byte data (']'),
num$delims literally '(length (delimiters) + 2)';
go$ddt: procedure external; /* return to assem loop in ddt on error */
end go$ddt;
ddt$set: procedure (assem$off, b) external;
declare assem$off address, b byte;
end ddt$set;
ddt$getline: procedure external;
end ddt$getline;
conin: procedure byte external;
end conin;
conout: procedure (b) external;
declare b byte;
end conout;
printm: procedure (a) EXTERNAL;
declare a address;
end printm;
crlf: procedure;
call conout (cr);
call conout (lf);
end crlf;
getline: procedure;
call ddt$getline;
next$ch = 0;
end getline;
print$word: procedure (a) EXTERNAL;
declare a address;
end print$word;
error: procedure;
call crlf;
call conout ('?');
call go$ddt;
end error;
ambig: procedure;
call printm (.(cr,lf,'ambiguous operand$'));
call go$ddt;
end ambig;
check$ambig: procedure;
if (op1.w$bit and op2.w$bit) = 0ffh then call ambig;
if (op1.w$bit xor op2.w$bit) = 1 then call error;
end check$ambig;
compare: procedure (a, b, c) logical;
/* should be able to replace this with a PL/M builtin */
declare (a, b) address, c byte;
declare a1 based a byte, b1 based b byte;
do while (c := c - 1) <> 255;
if a1 <> b1 then return false;
a = a + 1;
b = b + 1;
end;
return true;
end compare;
/*********************************/
/* */
/* token scanning procedures */
/* */
/*********************************/
delimiter: procedure (b) logical;
declare b byte;
declare i byte;
do i = 0 to num$delims - 1;
if delimiters (i) = b then return true;
end;
return false;
end delimiter;
number: procedure (b) logical;
declare b byte;
return (b - '0' <= 9) or (b - 'A' <= 5);
end number;
hex: procedure (b) byte;
declare b byte;
if b - '0' <= 9 then return b - '0';
else return b - 'A' + 10;
end hex;
get$token: procedure;
declare numeric logical;
token$value = 0;
numeric = true;
token$len = 0;
if next$ch = 0 then next$ch = conin;
do while true;
token (token$len) = next$ch;
if (token$len := token$len + 1) >= length (token) then
call error;
if delimiter (next$ch) then
do;
if token$len = 1 then
do;
token$type = delim$type;
next$ch = 0;
end;
else
do;
token$len = token$len - 1;
if numeric then token$type = number$type;
else token$type = string$type;
end;
return;
end;
if numeric then
do;
if number (next$ch) then token$value =
shl (token$value,4) or hex (next$ch);
else numeric = false;
end;
next$ch = conin;
end;
end get$token;
get$real$token: procedure;
do while true;
call get$token;
if token (0) <> ' ' and token (0) <> tab then return;
end;
end get$real$token;
/***********************************/
/* */
/* operand scanning procedures */
/* */
/***********************************/
look$up$reg: procedure (reg$tab$ptr, tab$len, reg$num$ptr) logical;
declare (reg$tab$ptr, reg$num$ptr) address;
declare reg$word based reg$tab$ptr address;
declare reg$num based reg$num$ptr byte;
declare tab$len byte;
declare i byte;
do i = 0 to tab$len - 1;
if token$word = reg$word then
do;
reg$num = i;
return true;
end;
reg$tab$ptr = reg$tab$ptr + 2;
end;
return false;
end look$up$reg;
check$base$reg: procedure logical;
return look$up$reg (.base$reg, 2, .op.base$reg$num);
end check$base$reg;
check$index$reg: procedure logical;
return look$up$reg (.index$reg, 2, .op.index$reg$num);
end check$index$reg;
check$seg$reg: procedure logical;
if look$up$reg (.seg$reg, 4, .op.seg$reg$num) then
do;
op.seg$reg$type = true;
return true;
end;
return false;
end check$seg$reg;
check$reg$8: procedure logical;
if look$up$reg (.reg$8, 8, .op.reg$num) then
do;
op.reg$8$type, op.modrm$type = true;
if op.reg$num = 0 then op.acc$type = true;
op.sub$type = 1;
op.w$bit = 0;
return true;
end;
return false;
end check$reg$8;
check$reg$16: procedure logical;
if look$up$reg (.reg$16, 8, .op.reg$num) then
do;
op.reg$16$type, op.modrm$type = true;
if op.reg$num = 0 then op.acc$type = true;
op.sub$type = 2;
op.w$bit = 1;
return true;
end;
return false;
end check$reg$16;
check$bw$prefix: procedure logical;
if token$type = string$type then
do;
if token$word = 'YB' then op.bw$prefix = 0;
else if token$word = 'OW' then op.bw$prefix = 1;
if op.bw$prefix <> 0ffh then return true;
end;
return false;
end check$bw$prefix;
check$seg$prefix: procedure logical;
return check$seg$reg and (next$ch = ':');
end check$seg$prefix;
set$prefix: procedure (b);
declare b byte;
prefix (nprefix) = b;
nprefix = nprefix + 1;
end set$prefix;
set$seg$prefix: procedure;
if seg$prefix$flag then call error;
call set$prefix (26h or shl (op.seg$reg$num, 3));
seg$prefix$flag = true;
call get$token; /* eat ':' */
op.seg$reg$type = false; /* operand is not a seg reg */
end set$seg$prefix;
get$prefix: procedure;
do while true;
call get$real$token;
if token$type = delim$type then
do;
if token (0) = cr then op.null$type = true;
return;
end;
if check$seg$prefix then call set$seg$prefix;
else if check$bw$prefix then
do;
if op.w$bit <> 0ffh then call error;
else op.w$bit = op.bw$prefix;
end;
else return; /* must not be a prefix */
end;
end get$prefix;
reg$indirect: procedure (x);
declare x byte;
op.modrm$type = true;
if check$base$reg then
do;
call get$token;
if token (0) = right$bracket then
do;
op.modrm$type = true;
op.sub$type = 7 - x;
return;
end;
else if token (0) = '+' then
do;
op.sub$type = 9 - x;
call get$token;
end;
else call error;
end;
else op.sub$type = 8 - x;
if check$index$reg then
do;
call get$token;
if token (0) = right$bracket then return;
end;
call error;
end reg$indirect;
get$operand: procedure;
op.seg$reg$type, op.reg$16$type, op.reg$8$type, op.num$type,
op.acc$type, op.null$type, op.far$type, op.modrm$type = false;
op.bw$prefix, op.w$bit = 0ffh; /* indeterminate */
call get$prefix;
if op.null$type then return;
if check$seg$reg then return;
else if check$reg$8 then return;
else if check$reg$16 then return;
else if token$type = number$type then
do;
op.value = token$value;
if next$ch = left$bracket then
do;
call get$token; /* eat '[' */
call get$token;
call reg$indirect (0);
end;
else if next$ch = ':' then
do;
op.seg$value = token$value;
call get$token; /* eat ':' */
call get$token;
if token$type <> number$type then call error;
op.value = token$value;
op.far$type = true;
end;
else op.num$type = true;
end;
else
do;
if token (0) <> left$bracket then call error;
call get$token;
if token$type = number$type then
do;
op.value = token$value;
call get$token;
if token (0) = right$bracket then
do;
op.sub$type = 3;
op.modrm$type = true;
end;
else call error;
end;
else call reg$indirect (3);
end;
end get$operand;
get$operand$1: procedure;
op$base = .op1;
call get$operand;
call get$token;
if token (0) <> ',' then call error;
end get$operand$1;
get$operand$2: procedure;
op$base = .op2;
call get$operand;
if op.null$type then return;
call get$token;
if token (0) <> cr then call error;
end get$operand$2;
/**********************************/
/* */
/* opcode scanning procedures */
/* */
/**********************************/
lookup$optab: procedure;
declare i address;
do i = 0 to last (optab) by 4;
if optab (i) = mnemonic$index then
do;
op$type = op$tab (i+1);
instr$1 = op$tab (i+2);
reg$bits = op$tab (i+3);
return;
end;
end;
call error; /* should never get here */
end lookup$optab;
valid$mnemonic: procedure logical;
declare
i byte,
table$base address,
mnemonic based table$base byte;
if token$len - 2 > 4 then call error;
table$base = tab$ptrs (token$len - 2);
do i = 0 to nops (token$len - 2) - 1;
if compare (.token, .mnemonic, token$len) then
do;
mnemonic$index = i + opn$in (token$len - 2);
return true;
end;
table$base = table$base + token$len;
end;
return false;
end valid$mnemonic;
get$opcode: procedure logical;
nprefix = 0;
rep$flag, lock$flag, seg$prefix$flag = false;
do while true;
call get$real$token;
if token (0) = cr or token (0) = '.' then return false; /* no opcode */
if check$seg$prefix then call set$seg$prefix;
else if valid$mnemonic then
do;
call look$up$op$tab;
if op$type = 0ffh then
do;
if lock$flag then call error;
lock$flag = true;
call set$prefix (instr$1);
end;
else if op$type = 0feh then /* repeat */
do;
if rep$flag then call error;
rep$flag = true;
call set$prefix (instr$1);
end;
else return true; /* opcode present */
end;
else call error;
end;
end get$opcode;
/******************************************/
/* */
/* forming and output of instructions */
/* */
/******************************************/
put$mem: procedure (b);
declare b byte;
call ddt$set (assem$offset, b);
assem$offset = assem$offset + 1;
end put$mem;
put$prefix: procedure;
declare i byte;
i = 0;
do while (nprefix := nprefix - 1) <> 0ffh;
call put$mem (prefix (i));
i = i + 1;
end;
end put$prefix;
put$instr: procedure;
call put$prefix;
call put$mem (instr$1);
end put$instr;
set$instr: procedure (b);
declare b byte;
instr$1 = b;
call put$instr;
end set$instr;
put$word: procedure (a);
declare a address;
call put$mem (low (a));
call put$mem (high (a));
end put$word;
set$w$bit: procedure;
if (op1.w$bit and op2.w$bit) = 1 then instr$1 = instr$1 or 1;
end set$w$bit;
set$mod: procedure;
if op.value > 7FH then mod$bits, disp$len = 2;
else mod$bits, disp$len = 1;
end set$mod;
set$modrm: procedure;
disp$len = 0;
disp$word = op.value;
do case op.sub$type - 1;
do; /* 1 */
mod$bits = 3;
rm$bits = op.reg$num;
end;
do; /* 2 */
mod$bits = 3;
rm$bits = op.reg$num;
end;
do; /* 3 */
mod$bits = 0;
rm$bits = 6;
disp$len = 2;
end;
do; /* 4 */
if op.base$reg$num = 1 then /* BP */
do;
mod$bits = 1;
rm$bits = 6;
disp$len = 1;
disp$word = 0;
end;
else
do;
mod$bits = 0;
rm$bits = 7;
end;
end;
do; /* 5 */
mod$bits = 0;
rm$bits = op.index$reg$num + 4;
end;
do; /* 6 */
mod$bits = 0;
rm$bits = op.base$reg$num * 2 + op.index$reg$num;
end;
do; /* 7 */
call set$mod;
rm$bits = 7 - op.base$reg$num;
end;
do; /* 8 */
call set$mod;
rm$bits = op.index$reg$num + 4;
end;
do; /* 9 */
call set$mod;
rm$bits = op.base$reg$num * 2 + op.index$reg$num;
end;
end;
end set$modrm;
do$modrm: procedure (op$ptr);
declare op$ptr address;
op$base = op$ptr;
call set$modrm;
call put$instr;
call put$mem (shl (mod$bits, 6) or shl (reg$bits, 3) or rm$bits);
if disp$len > 0 then call put$mem (low (disp$word));
if disp$len > 1 then call put$mem (high (disp$word));
end do$modrm;
modrm$w: procedure (ins, reg, op$ptr);
declare (ins, reg) byte;
declare op$ptr address;
op$base = op$ptr;
if op.modrm$type then
do;
if op.w$bit = 0ffh then call ambig;
instr$1 = ins or op.w$bit;
reg$bits = reg;
call do$modrm (op$ptr);
end;
else call error;
end modrm$w;
modrm$16: procedure (ins, reg);
declare (ins, reg) byte;
if op2.modrm$type and not op2.reg$8$type then
do;
instr$1 = ins;
reg$bits = reg;
call do$modrm (.op2);
end;
else call error;
end modrm$16;
modregrm: procedure;
call check$ambig;
if op1.sub$type <= 2 then /* first op is register */
do;
reg$bits = op1.reg$num;
call do$modrm (.op2);
end;
else
do;
instr$1 = instr$1 or op2.w$bit;
reg$bits = op2.reg$num;
call do$modrm (.op1);
end;
end modregrm;
modregrm$w: procedure;
call set$w$bit;
call modregrm;
end modregrm$w;
modregrm$dw: procedure;
if op1.modrm$type and (op1.sub$type <= 2) then instr$1 = instr$1 or 2; /* set d bit */
call modregrm$w;
end modregrm$dw;
put$immed: procedure;
call put$mem (low (op2.value));
if op1.w$bit then call put$mem (high (op2.value));
end put$immed;
put$immed$s: procedure;
call put$mem (low (op2.value));
if (instr$1 and 3) = 1 then call put$mem (high (op2.value));
end put$immed$s;
check$w$and$val: procedure;
if op1.w$bit = 0 and op2.value > 255 then call error;
if op1.w$bit = 0ffh then
do;
if op2.value >= 256 then op1.w$bit = 1;
else call ambig;
end;
end check$w$and$val;
acc$immed: procedure;
call check$w$and$val;
call set$w$bit;
call put$instr;
call put$immed;
end acc$immed;
modrm$immed: procedure;
call check$w$and$val;
call set$w$bit;
call do$modrm (.op1);
call put$immed;
end modrm$immed;
set$instr$w: procedure (b);
declare b byte;
if op2.sub$type <= 2 then instr$1 = b or op2.w$bit;
else instr$1 = b or op1.w$bit;
end set$instr$w;
check$acc$immed: procedure logical;
return op1.acc$type and op2.num$type;
end check$acc$immed;
check$modrm$immed: procedure logical;
return op1.modrm$type and op2.num$type;
end check$modrm$immed;
check$modrm$modrm: procedure logical;
return op1.modrm$type and op2.modrm$type and not
(op1.sub$type > 2 and op2.sub$type > 2);
end check$modrm$modrm;
/**************************************/
/* */
/* mnemonic processing procedures */
/* */
/**************************************/
type1: procedure;
/*
mnemonics: simple one-byte instructions
operands: none
forms: xxxxxxxx
*/
call get$operand$2;
if op2.null$type then call put$instr;
else call error;
end type1;
type2: procedure;
/*
mnemonics: aad aam
operands: none
forms: xxxxxxxx 00001010
*/
call get$operand$2;
if op2.null$type then
do;
call put$instr;
call put$mem (0ah);
end;
else call error;
end type2;
type3: procedure;
/*
mnemonics: conditional jumps loopxx jmps
operands: 1 (number)
forms: xxxxxxxx IP-INC8
*/
call get$operand$2;
if op2.num$type and (op2.value - (assem$offset - 7eh) <= 255) then
do;
call put$instr;
call put$mem (low (op2.value - (assem$offset + 1)));
end;
else call error;
end type3;
type4: procedure;
/*
mnemonics: logical shifts and rotates
operands: 2
forms: xxxxxxvw mmxxxr/m [disp-lo] [disp-hi]
*/
call get$operand$1;
call get$operand$2;
if op1.modrm$type then
do;
if op2.reg$8$type and (op2.reg$num = 1) then
instr$1 = instr$1 or 2; /* set v bit */
else if not (op2.num$type and (op2.value = 1)) then call error;
call modrm$w (instr1, reg$bits, .op1);
end;
else call error;
end type4;
type5: procedure;
/*
mnemonics: div idiv mul imul not neg
operands: 1
forms: xxxxxxxw mmxxxr/m [disp-lo] [disp-hi]
*/
call get$operand$2;
call modrm$w (instr$1, reg$bits, .op2);
end type5;
type6: procedure;
/*
mnemonics: les lds lea
operands: 2 (reg16, memory)
forms: xxxxxxxx mmregr/m [disp-lo] [disp-hi]
*/
call get$operand$1;
call get$operand$2;
if op1.reg$16$type and op2.modrm$type and (op2.sub$type > 2) then
call mod$reg$rm;
else call error;
end type6;
declare
type$7$struc (2) structure (op$val (2) byte) initial (
048h, 001h, /* dec */
040h, 000h); /* inc */
type7: procedure;
/*
mnemonics: inc dec
operands: 1
forms: xxxxxreg
xxxxxxxw mmxxxr/m [disp-lo] [disp-hi]
*/
call get$operand$2;
if op2.reg$16$type then call set$instr (type$7$struc (instr$1).op$val (0) or op2.reg$num);
else call modrm$w (0feh, type$7$struc (instr$1).op$val (1), .op2);
end type7;
type8or8a: procedure;
if op2.seg$reg$type then call set$instr (type$8$struc (instr$1).op$val (0) or
shl (op2.seg$reg$num, 3));
else if op2.reg$16$type then call set$instr (type$8$struc (instr$1).op$val (1) or op2.reg$num);
else call modrm$16 (type$8$struc (instr$1).op$val (2),
type$8$struc (instr$1).op$val (3));
end type8or8a;
declare
type$8$struc (2) structure (op$val (4) byte) initial (
07h, 58h, 8fh, 00h, /* pop */
06h, 50h, 0ffh, 06h); /* push */
type8: procedure;
/*
mnemonics: push
operands: 1
forms: xxxSRxxx
xxxxxreg
xxxxxxxx mmxxxr/m [disp-lo] [disp-hi]
*/
call get$operand2;
call type8or8a;
end type8;
type8a: procedure;
/*
mnemonics: pop
operands: 1
forms: xxxSRxxx
xxxxxreg
xxxxxxxx mmxxxr/m [disp-lo] [disp-hi]
*/
call get$operand2;
if op2.seg$reg$type and (op2.seg$reg$num = 1) then call error;
else call type8or8a;
end type8a;
type9or10: procedure (type);
declare type byte;
call get$operand$1;
call get$operand$2;
if check$acc$immed then
do;
instr$1 = instr$1 or 4;
call acc$immed;
end;
else if check$modrm$immed then
do;
instr$1 = 80h;
if type = 9 then
do;
if op2.value < 80h or op2.value >= 0ff80h then
instr$1 = instr$1 or 2;
call check$w$and$val;
call set$w$bit;
call do$modrm (.op1);
call put$immed$s;
end;
else call modrm$immed;
end;
else if check$modrm$modrm then call mod$reg$rm$dw;
else call error;
end type9or10;
type9: procedure;
/*
mnemonics: add adc cmp sub sbb
operands: 2
forms: xxxxxxxw data-lo data-hi (if w=1)
xxxxxxsw mmxxxr/m [disp-lo] [disp-hi] data-lo data-hi (if sw=01)
xxxxxxdw mmregr/m [disp-lo] [disp-hi]
*/
call type9or10 (9);
end type9;
type10: procedure;
/*
mnemonics: and or xor
operands: 2
forms: xxxxxxxw data-lo data-hi (if w=1)
xxxxxxxw mmxxxr/m [disp-lo] [disp-hi] data-lo data-hi (if w=1)
xxxxxxdw mmregr/m [disp-lo] [disp-hi]
*/
call type9or10 (10);
end type10;
declare type11$struc (2) structure (op$val (2) byte) initial (
0e8h, 2, /* call */
0e9h, 4); /* jmp */
type11: procedure;
/*
mnemonics: call jmp
operands: 1
forms: xxxxxxxx IP-INC16
xxxxxxxx mmxxxr/m [disp-lo] [disp-hi]
*/
call get$operand$2;
if op2.num$type then
do;
call set$instr (type11$struc (instr$1).op$val (0));
call put$word (op2.value - (assem$offset + 2));
end;
else call modrm$16 (0ffh, type$11$struc (instr$1).op$val (1));
end type11;
type$int: procedure;
call get$operand$2;
if op2.num$type and (op2.value <= 255) then
do;
if op2.value = 3 then call set$instr (0cch);
else
do;
call set$instr (0cdh);
call put$mem (low (op2.value));
end;
end;
else call error;
end type$int;
type$esc: procedure;
call get$operand$1;
call get$operand$2;
if op1.num$type and (op1.value < 64) and op2.modrm$type then
do;
instr$1 = instr$1 or shr (low (op1.value), 3);
reg$bits = low (op1.value) and 7;
call do$modrm (.op2);
end;
else call error;
end type$esc;
type$ret$retf: procedure;
op$base = .op2;
call get$operand;
if op2.null$type then call set$instr (instr$1 or 1);
else if op2.num$type then
do;
call put$instr;
call put$word (op2.value);
end;
else call error;
end type$ret$retf;
type$in: procedure;
call get$operand$1;
if not op1.acc$type then call error;
call get$operand$2;
if op2.reg$16$type and (op2.reg$num = 2) /* DX */ then
call set$instr (instr$1 or 8 or op1.w$bit);
else if op2.num$type and (op2.value <= 255) then
do;
call put$instr;
call put$mem (low (op2.value));
end;
else call error;
end type$in;
type$out: procedure;
declare temp byte;
call get$operand$1;
if op1.reg$16$type and (op1.reg$num = 2) /* DX */ then temp = 1;
else if op1.num$type and (op1.value <= 255) then temp = 0;
else call error;
call get$operand$2;
if not op2.acc$type then call error;
call put$mem (instr$1 or shl (temp,3) or op2.w$bit);
if temp = 0 then call put$mem (low (op1.value));
end type$out;
declare cjstruc (2) structure (op$val (2) byte) initial (
09ah, 3, /* callf */
0eah, 5); /* jmpf */
type$callf$jmpf: procedure;
call get$operand$2;
if op2.far$type then
do;
call set$instr (cjstruc (instr$1).op$val (0));
call put$word (op2.value);
call put$word (op2.seg$value);
end;
else call modrm$16 (0ffh, cj$struc (instr$1).op$val (1));
end type$callf$jmpf;
type$test: procedure;
call get$operand$1;
call get$operand$2;
if check$acc$immed then
do;
instr$1 = 0a8h;
call acc$immed;
end;
else if check$modrm$immed then
do;
instr$1 = 0f6h;
call modrm$immed;
end;
else if check$modrm$modrm then
do;
call set$instr$w (84h);
call mod$reg$rm;
end;
else call error;
end type$test;
type$xchg: procedure;
call get$operand$1;
call get$operand$2;
if op1.acc$type and op1.w$bit and op2.reg$16$type then
call set$instr (90h + op2.reg$num);
else if check$modrm$modrm then
do;
call set$instr$w (86h);
call modregrm;
end;
else call error;
end type$xchg;
type$mov: procedure;
call get$operand$1;
call get$operand$2;
if op1.seg$reg$type and op2.modrm$type and not op2.reg$8$type then
do;
instr$1 = 8eh;
reg$bits = op1.seg$reg$num;
call do$modrm (.op2);
end;
else if op1.modrm$type and not (op1.sub$type = 1) and op2.seg$reg$type then
do;
instr$1 = 8ch;
reg$bits = op2.seg$reg$num;
call do$modrm (.op1);
end;
else if op1.acc$type and op2.modrm$type and (op2.subtype = 3) then
do;
instr$1 = 0a0h or op1.w$bit;
call put$instr;
call put$word (op2.value);
end;
else if op2.acc$type and op1.modrm$type and (op1.sub$type = 3) then
do;
instr$1 = 0a2h or op2.w$bit;
call put$instr;
call put$word (op1.value);
end;
else if check$modrm$immed then
do;
if op1.sub$type <= 2 then
do;
call set$instr (0b0h or shl (op1.w$bit, 3) or op1.reg$num);
call put$immed;
end;
else
do;
instr$1 = 0c6h;
call modrm$immed;
end;
end;
else if check$modrm$modrm then
do;
instr$1 = 88h;
call mod$reg$rm$dw;
end;
else call error;
end type$mov;
asm: procedure (loc) address public;
declare loc pointer;
assem$ptr = loc;
call crlf;
call print$word (assem$segment);
call conout (':');
call print$word (assem$offset);
call conout (' ');
call get$line;
op$base = .op1; /* must be set for prefix scan */
if get$opcode then
do;
if op$type > 21 then call error;
do case optype;
call error;
call type1;
call type2;
call type3;
call type4;
call type5;
call type6;
call type7;
call type8;
call type9;
call type10;
call type11;
call type$int;
call type$esc;
call type$ret$retf;
call type$in;
call type$out;
call type$callf$jmpf;
call type$test;
call type$xchg;
call type$mov;
call type8a;
end;
end;
else call put$prefix; /* may be prefix without opcode */
return assem$offset;
end asm;
end ddtasm;