10-25-04 01:46 AM
echo "
Recent osimplays didn't do memory references right vis-a-vis BP and SP.
This one seems to.
grep () gives a nice glossary of osimplay.
Rick Hohensee Oct 23 2004
" > /dev/null
# shasm to osimpa to osimplay
# OSes as simple as childsplay
echo " @ BP fixed"
#()
# ASSEMBLER DIRECTIVES/HELPERS ()
# config
allocated=yes
executable=yes
output=~binary
listing=~listing
#H=0
declare -ai textlength
# here cell
declare -i H C
octalbyte=({0,1,2,3}{0,1,2,3,4,5,6,7}{0,1,2,3,4,5,6,7})
## several doodads to have meta listings notes
hex=(\
{0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f}{0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f}
)
opnote ( ) { # internal =shasm
if test "$pass" = "2" ;then
echo -n " "$*" " >> $listing
fi
}
ascii ( ) { # use text or tag =shasm
if test "$1" = "h" ; then echo -e "\n\n
Simple stick-text-in-assembly. For named self-sized text see \"text\".
The annoyance is that the shell does a lot of handy parsing of the
commandline that is completely in the way vis-a-vis literal strings. The
work-around that's easiest to implement is to make sure strings have no
actual whitespace in them as arguments to ascii or string. That means use
\040 for space, \t for tab, and \n for linefeed (unix end-of-line). Or use
\"text\". You also have \"asciibyte\" for ascii to binary.
I REPEAT. OSIMPLAY ASCII CANNOT HANDLE WHITESPACE AS SUCH.
"
else ###############
if test "$pass" = "1" ;then
H=H+${#1}
else
echo -en $1 >> $output
echo -n $1 "ASCII " >> $listing
H=H+${#1}
fi
fi
}
ab () { # assemble bytes. pass-sensitive. =shasm
if test "$pass" = "2" ;then
bytes $*
else
H=H+$#
fi
}
ao () { # assemble one octal string as a byte =shasm
if test "$pass" = "2" ;then
echo -en \\$1 >> $output
echo -en $1" " >> $listing
fi
H=H+1
}
ad () { # assemble duals. pass-sensitive. =shasm
if test "$pass" = "2" ;then
duals $*
else
H=H+$#*2
fi
}
aq () { # assemble quads. pass-sensitive quads =shasm
if test "$pass" = "2" ;then
quads $*
else
H=H+$#*4
fi
}
ac () { # assemble cells. pass-cell-sensitive. =shasm
if test "$pass" = "2" ;then
if test "$cell" = "2" ;then
duals $*
else
quads $*
fi
else
H=H+$#*$cell
fi
}
align () { # like e.g. .align =shasm
if test "$1" = "h" ; then echo -e "\n\n\n\n
Don't use this until \$H has been bumped above 0."
else ####################
declare lop pad
if test "$H" && test "$H" != "0" ;then
let lop=$H%$1
let pad=$1-$lop
if test $lop -ne 0 ;then
allot $pad
fi
else
echo "For simplicity, align doesn't work until
\$H has been set greater than 0."
fi
fi
}
allot () { # relative .org, like Forth ALLOT =shasm
if test "$1" = "h" ; then echo -e "\n\n
This is like Forth ALLOT, which is relative, unlike asm .org, which is
(file address) absolute. Allot takes one amount argument in byte units.
If \$H is 0x200 then after allot 0x10 \$H is 0x210. \$H
is the next un-assembled file address.
\n\n"
else ############
HL
if test "$pass" = "2" ;then
echo -n "00... " >> $listing
fi
let tempint=$1+H
if test $pass -eq 1 ;then
H=$1+H
else ###### pass 2
echo -en "\t\t\tALLOT $1">>$listing
while test $H -lt $(($tempint)) ;do
if test $(($tempint-$H>>8)) -eq 0 ;then
# could loop H too
if test "$allocated" = "yes" ;then
echo -en "\000" >> $output
fi
H=H+1
else
page
H=H+256
fi
done
fi
fi
}
asciibyte () { # e.g. ab `asciibyte c q d` =shasm
if test "$1" = "hoilp" ; then echo -e "\n\n
This isn't help on h. That led to one of the more twisted bugs I've
encountered. I assume you're reading the script, or did
asciibyte hoilp
Similar to BASIC ASC or C 't'. Unlike osimpa \"text\" or \"ascii\" in that
it doesn't assemble the byte itself. You'll probably use it with grave
accents, ala
= `asciibyte T` to DI
"
else ################
for aschr in $* ; do
case $aschr in
# some duplicates for performance. Heh.
cr) echo $((0015)) ;; # 0x0D CARRIAGE RETURN
space) echo $((0040)) ;; # 0x20 SPACE
tab) echo $((0011)) ;; # 0x09 HORIZONTAL TABULATION
bell) echo $((0007)) ;; # 0x07 BELL
backspace) echo $((0010)) ;; # 0x08 BACKSPACE
formfeed) echo $((0014)) ;; # 0x0C FORM FEED
"!") echo $((0041)) ;; # 0x21 EXCLAMATION MARK
null) echo $((0000)) ;; # 0x00 NULL
header) echo $((0001)) ;; # 0x01 START OF HEADING
sot) echo $((0002)) ;; # 0x02 START OF TEXT
binary) echo $((0003)) ;; # 0x03 END OF TEXT
eot) echo $((0004)) ;; # 0x04 END OF TRANSMISSION
enqiry) echo $((0005)) ;; # 0x05 ENQUIRY
ack) echo $((0006)) ;; # 0x06 ACKNOWLEDGE
bell) echo $((0007)) ;; # 0x07 BELL
backspace) echo $((0010)) ;; # 0x08 BACKSPACE
tab) echo $((0011)) ;; # 0x09 HORIZONTAL TABULATION
linefeed) echo $((0012)) ;; # 0x0A LINE FEED
vtab) echo $((0013)) ;; # 0x0B VERTICAL TABULATION
formfeed) echo $((0014)) ;; # 0x0C FORM FEED
cr) echo $((0015)) ;; # 0x0D CARRIAGE RETURN
out) echo $((0016)) ;; # 0x0E SHIFT OUT
in) echo $((0017)) ;; # 0x0F SHIFT IN
outofband) echo $((0020)) ;; # 0x10 DATA LINK ESCAPE
control1) echo $((0021)) ;; # 0x11 DEVICE CONTROL ONE
control2) echo $((0022)) ;; # 0x12 DEVICE CONTROL TWO
control3) echo $((0023)) ;; # 0x13 DEVICE CONTROL THREE
control4) echo $((0024)) ;; # 0x14 DEVICE CONTROL FOUR
nack) echo $((0025)) ;; # 0x15 NEGATIVE ACKNOWLEDGE
standby) echo $((0026)) ;; # 0x16 SYNCHRONOUS IDLE
eoblock) echo $((0027)) ;; # 0x17 END OF TRANSMISSION BLOCK
cancel) echo $((0030)) ;; # 0x18 CANCEL
eomedia) echo $((0031)) ;; # 0x19 END OF MEDIUM
substitute) echo $((0032)) ;; # 0x1A SUBSTITUTE
escape) echo $((0033)) ;; # 0x1B ESCAPE
filesep) echo $((0034)) ;; # 0x1C FILE SEPARATOR
groupsep) echo $((0035)) ;; # 0x1D GROUP SEPARATOR
recordsep) echo $((0036)) ;; # 0x1E RECORD SEPARATOR
unitsep) echo $((0037)) ;; # 0x1F UNIT SEPARATOR
space) echo $((0040)) ;; # 0x20 SPACE
"!") echo $((0041)) ;; # 0x21 EXCLAMATION MARK
"\"") echo $((0042)) ;; # 0x22 QUOTATION MARK
"#") echo $((0043)) ;; # 0x23 NUMBER SIGN
"$") echo $((0044)) ;; # 0x24 DOLLAR SIGN
"%") echo $((0045)) ;; # 0x25 PERCENT SIGN
"&") echo $((0046)) ;; # 0x26 AMPERSAND
"'") echo $((0047)) ;; # 0x27 APOSTROPHE
"(") echo $((0050)) ;; # 0x28 LEFT PARENTHESIS
")") echo $((0051)) ;; # 0x29 RIGHT PARENTHESIS
"\*") echo $((0052)) ;; # 0x2A ASTERISK
"+") echo $((0053)) ;; # 0x2B PLUS SIGN
,) echo $((0054)) ;; # 0x2C COMMA
-) echo $((0055)) ;; # 0x2D HYPHEN-MINUS
.) echo $((0056)) ;; # 0x2E FULL STOP
"/") echo $((0057)) ;; # 0x2F SOLIDUS
0) echo $((0060)) ;; # 0x30 DIGIT ZERO
1) echo $((0061)) ;; # 0x31 DIGIT ONE
2) echo $((0062)) ;; # 0x32 DIGIT TWO
3) echo $((0063)) ;; # 0x33 DIGIT THREE
4) echo $((0064)) ;; # 0x34 DIGIT FOUR
5) echo $((0065)) ;; # 0x35 DIGIT FIVE
6) echo $((0066)) ;; # 0x36 DIGIT SIX
7) echo $((0067)) ;; # 0x37 DIGIT SEVEN
8) echo $((0070)) ;; # 0x38 DIGIT EIGHT
9) echo $((0071)) ;; # 0x39 DIGIT NINE
":") echo $((0072)) ;; # 0x3A COLON
";") echo $((0073)) ;; # 0x3B SEMICOLON
"<") echo $((0074)) ;; # 0x3C LESS-THAN SIGN
"=") echo $((0075)) ;; # 0x3D EQUALS SIGN
">") echo $((0076)) ;; # 0x3E GREATER-THAN SIGN
"?") echo $((0077)) ;; # 0x3F QUESTION MARK
@) echo $((0100)) ;; # 0x40 COMMERCIAL AT
A) echo $((0101)) ;; # 0x41
B) echo $((0102)) ;; # 0x42
C) echo $((0103)) ;; # 0x43
D) echo $((0104)) ;; # 0x44
E) echo $((0105)) ;; # 0x45
F) echo $((0106)) ;; # 0x46
G) echo $((0107)) ;; # 0x47
H) echo $((0110)) ;; # 0x48
I) echo $((0111)) ;; # 0x49
J) echo $((0112)) ;; # 0x4A
K) echo $((0113)) ;; # 0x4B
L) echo $((0114)) ;; # 0x4C
M) echo $((0115)) ;; # 0x4D
N) echo $((0116)) ;; # 0x4E
O) echo $((0117)) ;; # 0x4F
P) echo $((0120)) ;; # 0x50
Q) echo $((0121)) ;; # 0x51
R) echo $((0122)) ;; # 0x52
S) echo $((0123)) ;; # 0x53
T) echo $((0124)) ;; # 0x54
U) echo $((0125)) ;; # 0x55
V) echo $((0126)) ;; # 0x56
W) echo $((0127)) ;; # 0x57
X) echo $((0130)) ;; # 0x58
Y) echo $((0131)) ;; # 0x59
Z) echo $((0132)) ;; # 0x5A
"[") echo $((0133)) ;; # 0x5B LEFT SQUARE BRACKET
"\\") echo $((0134)) ;; # 0x5C REVERSE SOLIDUS
"]") echo $((0135)) ;; # 0x5D RIGHT SQUARE BRACKET
"^") echo $((0136)) ;; # 0x5E CIRCUMFLEX ACCENT
_) echo $((0137)) ;; # 0x5F LOW LINE
"\`") echo $((0140)) ;; # 0x60 GRAVE ACCENT
a) echo $((0141)) ;; # 0x61
b) echo $((0142)) ;; # 0x62
c) echo $((0143)) ;; # 0x63
d) echo $((0144)) ;; # 0x64
e) echo $((0145)) ;; # 0x65
f) echo $((0146)) ;; # 0x66
g) echo $((0147)) ;; # 0x67
h) echo $((0150)) ;; # 0x68
i) echo $((0151)) ;; # 0x69
j) echo $((0152)) ;; # 0x6A
k) echo $((0153)) ;; # 0x6B
l) echo $((0154)) ;; # 0x6C
m) echo $((0155)) ;; # 0x6D
n) echo $((0156)) ;; # 0x6E
o) echo $((0157)) ;; # 0x6F
p) echo $((0160)) ;; # 0x70
q) echo $((0161)) ;; # 0x71
r) echo $((0162)) ;; # 0x72
s) echo $((0163)) ;; # 0x73
t) echo $((0164)) ;; # 0x74
u) echo $((0165)) ;; # 0x75
v) echo $((0166)) ;; # 0x76
w) echo $((0167)) ;; # 0x77
x) echo $((0170)) ;; # 0x78
y) echo $((0171)) ;; # 0x79
z) echo $((0172)) ;; # 0x7A
"\{") echo $((0173)) ;; # 0x7B LEFT CURLY BRACKET
"|") echo $((0174)) ;; # 0x7C VERTICAL LINE
"\}") echo $((0175)) ;; # 0x7D RIGHT CURLY BRACKET
"~") echo $((0176)) ;; # 0x7E TILDE
delete) echo $((0177)) ;; # 0x7F DELETE
esac
done
fi
}
# interactive use helper
bases () { # print # in decimal, binary, octal, hex =shasm
if test $# -lt 1 ;then
echo "
This is an interactive calculations helper. Gimme some numbers to convert
to binary, octal and hex.
"
else #########
for num in $* ;do
binaryout $num
echo -en " "
octalout $num
echo -en " "
hexout $num
echo $(($num)) "dec"
done
fi
}
binary () { # binary <strings of ones and zeros> =shasm
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
The osimplay binary word has mostly been superceded by a set of names fo
r
the values 0-255 where zeros are _ and ones are I, e.g. 3 is \$______II.
Convert the binary string representation of a number to it's value.
Accepts it's single argument in multiple segments, for keeping things in
byte-sized parcels.
e.g.
let T=\`binary 0100 01001010\`
or for just looking,
binary 0101010011010011
#binary (and all of shasm) uses Bash arithmatic, with Bash's limits,
which looks like 32 bits H.
"
else ####################
remaining=$1$2$3$4$5$6$7$8
let placebit=1
value=0
while test ! "$remaining" = "" ;do
if test "${remaining:${#remaining}-1:1}" = "1" ;then
let value=$value+$placebit
fi
placebit=$(($placebit*2))
remaining=`chom $remaining`
done
echo $value
fi
}
append ( ) { # help-only on dealing with binary appendages
echo -e "\n\t\t\t\t\t
help-only on dealing with binary appendages
You don't want to be recompembling tables of constants and so on with
every run of a compembler written in Bash. So cheat. Use cat, and make a
proto-linker at the very end of your osimplay source file, so you have the
right H to offset into the appended files. Let's say you have 3
precompembled RELOCATABLE objects, lookup data tables, say, named Larry,
Moe and Curly. Assume they are 3 different sizes. At the very end of your
top sourcefile...
Larry=\$H
Moe=\$((\$H+\$Larry))
Curly=\$((\$Moe+\$Larry))
Shemp=\$((\$Curly+\$Moe))
mv binary inary
cat inary Larry Moe Curly > binary
rm inary
and you can = A to @ Larry as desired
elsewhere in the sources. We left out Shemp. osimplay doesn't use anything
but a sh, but that's osimplay's problem. Your sources are shell scripts.
"
}
binaryout ( ) { # print a binary string of a number =shasm
let val=$1
bstring=
for maskbit in -2147483648 1073741824 536870912 268435456 134217728 \
67108864 33554432 16777216 8388608 4194304 2097152 1048576 524288 \
262144 131072 65536 32768 16384 8192 4096 2048 1024 512 256 128 64 \
32 16 8 4 2 1
do
let bitval=$val\&$maskbit
bitchar=0
if test "$bitval" -ne 0 ;then
bitchar=1
fi
bstring=$bstring$bitchar
if test $maskbit = 16777216 -o $maskbit = 65536 -o \
$maskbit = 256
then
bstring=$bstring"_"
fi
done
echo -n $bstring
}
branch ( ) { # resolve a relative branch =shasm
if test "$1" = "h" ; then echo "\n\n\n
A branch resolver. This is an internal. Use jump, call and so on. Called
by branching opers, which pass this the label name and branch size.\n\n"
elif test "$pass" = "1"
then # on pass 1 just skip the branch byte/dual/quad
H=H+$2
else # Pass 2 is on us. Pass 1 was L.
let relativ=$1-H-$2
case $2 in
1)bytes $relativ ;;
2)duals $relativ ;;
4)quads $relativ ;;
*) echo "
The second argument to branch isn't 1, 2 or 4. "
;;
esac
fi
}
bytes ( ) { # internal =shasm
H=H+$#
for a in $* ;do
if test $(($a)) -gt 255 ; then
echo " H " $H ": index into hex[] > 255"
fi
if test "$allocated" = "yes" ;then
echo -en \\${octalbyte[$a&0xff]} >> $output
fi
if test $(($a)) -lt 0 ;then
let a=$a+256
fi
echo -n ${hex[$a]}" " >> $listing
done
}
chom () { # chom chomp returns chom =shasm
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
chom chomp will leave chom "
else ###############
echo ${1:0:${#1}-1} ;fi
}
duals ( ) { # internal =shasm
H=H+$#*2
for a in $* ;do
if test "$allocated" = "yes" ;then
echo -en \\${octalbyte[$a&0xff]} >> $output
fi
echo -en ${hex[$a&0xff]}" " >> $listing
if test "$allocated" = "yes" ;then
echo -en \\${octalbyte[$((a>>8))&0xff]} >> $output
fi
echo -en ${hex[$((a>>8))&0xff]}" " >> $listing
done
}
quads ( ) { # internal =shasm
H=H+$#*4
for a in $* ;do
if test "$allocated" = "yes" ;then
echo -en \\${octalbyte[$a&0xff]} >> $output
fi
echo -en ${hex[$a&0xff]}" " >> $listing
if test "$allocated" = "yes" ;then
echo -en \\${octalbyte[$((a>>8))&0xff]} >> $output
fi
echo -en ${hex[$((a>>8))&0xff]}" " >> $listing
if test "$allocated" = "yes" ;then
echo -en \\${octalbyte[$((a>>16))&0xff]}>> $output
fi
echo -en ${hex[$((a>>16))&0xff]}" " >> $listing
if test "$allocated" = "yes" ;then
echo -en \\${octalbyte[$((a>>24))&0xff]}>> $output
fi
echo -en ${hex[$((a>>24))&0xff]}" " >> $listing
done
}
hexout ( ) { # print a hex representation of a number =shasm
declare val bstring
let val=$1
bstring=0x
for shift in 28 24 20 16 12 8 4 0 ;do
let nybble=$val\>\>$shift\&15
case $nybble in
0) bstring=$bstring"0" ;;
1) bstring=$bstring"1" ;;
2) bstring=$bstring"2" ;;
3) bstring=$bstring"3" ;;
4) bstring=$bstring"4" ;;
5) bstring=$bstring"5" ;;
6) bstring=$bstring"6" ;;
7) bstring=$bstring"7" ;;
8) bstring=$bstring"8" ;;
9) bstring=$bstring"9" ;;
10) bstring=$bstring"a" ;;
11) bstring=$bstring"b" ;;
12) bstring=$bstring"c" ;;
13) bstring=$bstring"d" ;;
14) bstring=$bstring"e" ;;
15) bstring=$bstring"f" ;;
esac
done
echo -n $bstring" "
}
hexquad ( ) { # internal =shasm
# BIG-endian non-spaced hex 4-byte int for $H
if test "$1" ; then
echo -en \
${hex[$1>>24&0xff]}${hex[$1>>16&0xff]}${hex[$1>>8&0xff]}${he
x[$1&0xff]}" "\[vbcol=seagreen]
else
echo "No arg for hexquad. Set \$H?"
fi
}
HL ( ) { # internal =shasm
if test $pass = 2 ;then
echo >> $listing
hexquad $H
fi
}
homp () { # homp chomp is homp =shasm
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
homp chomp produces homp"
else ###############
echo ${1:1:${#1}-1} ;fi
}
fillabsolute () { # like .org, but fill to just BEFORE arg =shasm
if test "$1" = "h" ; then echo -e "\n\n
This was buggy, and
allot \$((bla-\$H))
does the right
thing, so use that.
\n\n"
else ############
HL
if test "$pass" = "2" ;then
echo -n "00 " >> $listing
fi
if test $pass -eq 1 ;then
H=$1+H
else ###### pass 2
echo -e "\t\t\tfill to just before $1">>$listing
echo -e " \||/ 00...">>$listing
while test $H -lt $1 ;do
if test $(($1-$H>>8)) -eq 0 ;then
if test "$allocated" = "yes" ;then
echo -en "\000" >> $output
fi
H=H+1
else
page
H=H+256
fi
done
H=H-1
HL
echo "00 " >> $listing
H=H+1
fi
fi
}
L () { # label, set a branch bla at $H =shasm
if test "$1" = "h" ;then
echo "\n\n\n
L mylabel
sets \$mylabel to \$H.
"
elif ########################################
#
test "$pass" = "1" ;then
eval $1=$H
else
HL
echo -n " (O) "$1 >> $listing
fi
}
octalout ( ) { # print a number as an octal string
let val=$1
bstring=
for ushift in 30 27 24 21 18 15 12 9 6 3 0 ;do
let nybble=$val\>\>$ushift\&7
case $nybble in
0) bstring=$bstring"0" ;;
1) bstring=$bstring"1" ;;
2) bstring=$bstring"2" ;;
3) bstring=$bstring"3" ;;
4) bstring=$bstring"4" ;;
5) bstring=$bstring"5" ;;
6) bstring=$bstring"6" ;;
7) bstring=$bstring"7" ;;
esac
done
echo -n $bstring" "
}
page ( ) { # internal =shasm
if test "$allocated" = "yes" ;then
echo -en "\
\000\000\000\000\000\000\000\000\000\000
\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000
\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000
\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000
\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000
\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000
\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000
\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000
\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000
\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000
\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000
\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000
\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000
\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000
\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000
\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000
\000\000\000\000\000\000" >> $output
fi
}
## 80386 shtuff see Intel's 386INTEL.TXT etc.
# tH's a webpage of the above
cell="4" # global, 4 or 2. 386 or real
declare -i registers
Size ( ) { # internal =x86
if test $1 = "1" ;then
size[$source]=1 #
size[$dest]=1 # was [$side]
fi
}
#TYPE ( ) { # internal =x86
#mode[$side]=$1
# }
octacode ( ) { # internal =x86
register[$side+${registers[$side]}]=$1
registers[$side]=${registers[$side]}+1
if test ${registers[$side]} -gt 1 ; then
memside=$side
MEM=4 # wtf
fi
if test $numsseen -gt 0 ; then
memside=$side
MEM=4 # wtf
fi
}
# modestring builders
segment ( ) { # internal =x86
octacode $1
#size 2
mode[$side]=segm
}
specialC ( ) { # internal =x86
octacode $1
mode[$side]=speC
}
specialD ( ) { # internal =x86
octacode $1
mode[$side]=speD
}
specialT ( ) { # internal =x86
octacode $1
mode[$side]=speT
}
small ( ) { # internal =x86
octacode $1
mode[$side]=D
Size 1
}
dualreg ( ) { # internal =x86
octacode $1
mode[$side]=D
Size 2
}
initop ( ) { # internal =x86
# wipe op parser state
unset number register mode size memside MEM index mo
source=0
registers[0]=0
registers[2]=0
shifted= # Nein! default is no index
numsseen=0
side=0 # left=0, right=2?.
let sides=1
Scale=0
BAS=5
}
tokencase ( ) { # internal =x86
case $arg in
to) sides=2 ; source=0 ; dest=2 ; side=2 ; numsseen=0 ;;
A) octacode "0" ;; C) octacode "1" ;;
D) octacode "2" ;; B) octacode "3" ;;
SP) octacode "4" ;; E|BP) octacode "5" ;;
+|@) mode[$side]="M" ; memside=$side ;;
SI) octacode "6" ;; DI) octacode "7" ;;
byte) Size "1" ;;
dual) Size "c" ;; quad) Size "c" ;; # do nothing
CS) segment 1 ;; DS) segment 3 ;;
SS) segment 2 ;; ES) segment 0 ;;
FS) segment 4 ;; GS) segment 5 ;;
AL) small 0 ;; CL) small 1 ;; DL) small 2 ;; BL) small 3 ;;
AH) small 4 ;; CH) small 5 ;; DH) small 6 ;; BH) small 7 ;;
AX) dualreg 0 ;; CX) dualreg 1 ;; DX) dualreg 2 ;; BX) dualreg 3 ;;
CR0) specialC 0 ;; CR2) specialC 2 ;; CR3) specialC 3 ;;
DR0) specialD 0 ;; DR1) specialD 1 ;; DR2) specialD 2 ;;
DR3) specialD 3 ;; DR6) specialD 6 ;; DR7) specialD 7 ;;
TR6) specialT 6 ;; TR7) specialT 7 ;;
"*2^") let shifted=$side+${registers[$side]}-1
index=${register[$shifted]}
memside=$side # memory side
wasshifter="yes"
mode[$side]="M" ;;
with) sides=2 ; source=0 ; dest=2 ; side=2 ; numsseen=0 ;;
from) sides=2 ; source=2 ; dest=0 ; side=2 ; numsseen=0 ;;
*) number[$side+$numsseen]=$arg
let numsseen=$numsseen+1 ;;
esac
}
buildmode ( ) { # internal =x86
## todo.... figure out that a dest-number-only is a memref ####
# accumulate a case switch string
modestring=${size[$source]}
if test -n "${mode[$source]}" ;then
modestring=$modestring${mode[$source]}
elif test "${registers[$source]}" = "2" ;then
modestring=$modestring"M"
elif test -n "${number[$source]}" ;then
modestring=$modestring"I"
else
modestring=$modestring"D"
fi
if test "$sides" = 2 ;then
if test -n "${mode[$dest]}" ;then
modestring=$modestring${mode[$dest]}
elif test "${registers[$dest]}" = "2" ;then
modestring=$modestring"M"
else
modestring=$modestring"D"
fi
fi
}
parse ( ) { # internal =x86
initop
for arg in $* ;do
if test "$wasshifter" = "yes" ;then
wasshifter="no"
Scale=$arg # SNAG SC without making it a
# $number[]
else
tokencase $arg
fi
done # end args loop, resume reasonable indentation.
buildmode
} # end of pmode parse #######
memref ( ) { # internal =x86
if test "$cell" = 2 ;then
rmodememref $*
else
pmodememref $*
fi
}
# mem reference modR/M register/code/memref byte
# | | | | | | | | |
# pmode | mode | offreg/code | memref reg/code |
# mode 0 is offreg or code &
# single memref register or
needSIB=4 # MEM=4 or
# 32 bit displacement
# mode 1 is offreg or code &
# byte displ. and reg or
# byte displ. and needSIB=4
# mode 2 is offreg or code &
# 32 bit displ. and reg or
# 32 bit displ. and needSIB=4
# mode 3 is offreg or code &
# single memref register, any size , 4=SP
# SIB is
# | | | | | | | | |
# | scale | index reg | base reg |
# 386 Scale may vary without an index. Gas warns on that.
# arg is MODE/R/M R, memref register
pmodememref ( ) { # internal =x86
REG=$1 MEM= MOD=0 zerobytedisp=
# REG is the OFF reg and just gets ao'd
case ${registers[$memside]} in
0) MEM="5"
;;
1) MEM=${register[$memside]}
if ! test $shifted ;then ## no *2^
if test ${register[$memside]} = 5 ; then
# @ BP has to be coded as @ byte 0 BP or something
# because MEM=5 above is displ 32 NO REG
MOD=1 # byte disp
zerobytedisp=yes # byte displacement
fi
if test ${register[$memside]} = 4 ; then
MEM=4 # = needs sib
BAS=4
index=4 # per gas
fi
else # had a *2^
BAS=5 # 5 is no base
MEM=$needSIB # which is SP=4
index=${register[$shifted]}
# Scale is known from tokencase
fi
;;
2) MEM=$needSIB
if test $shifted ;then
index=${register[$memside]}
BAS=${register[$memside+1]} # just pick
else # base is index with NOT of low bit
BAS=${register[$shifted^1]}
index=${register[$shifted]}
fi
;;
*) echo "$H ; >2 regs on a memside?"
;;
esac
# displacement sizer
if test "${number[$memside]}" ;then # displacement
# get ABS of disp for size test
if test ${number[$memside]} -lt 0 ;then
disptest=$((0-${number[$memside]}))
else
disptest=${number[$memside]}
fi
# set MODe from displacement size
if test ${registers[memside]} -gt 0 ; then
if test $disptest -lt 128 ;then
MOD=1
else
MOD=2
fi
fi
fi
## assemble modR/M
ao $MOD$REG$MEM
# SIB
if test "$MEM" = "$needSIB" ;then
if test $MOD -ne 3 ;then # eh?
ao $Scale$index$BAS
fi
fi
# @ BP kludge
if test $zerobytedisp ; then
ab 0
fi
if test "$MOD" = "1" ;then
ab ${number[$memside]}
fi
if test "$MOD" = "2" ;then
ac ${number[$memside]}
fi
if test "$MOD" = "0" -a $MEM = "5" ;then
ac ${number[$memside]}
fi
} # end PROTECTED memref
rmodememref ( ) { # internal =x86
# takes $source/$dest of memref and the off register/code
## mo and MEM
REG=$1
if ! test "$memside" ;then # direct
MEM=${register[$1]}
mo=3
fi
if test -n "${number[$memside]}" ;then # displ
if test ${registers[$memside]} -eq 0 ;then #no reg
mo=0
MEM=6
elif test "${size[$memside]}" = "1" ;then # regs, byte
mo=1
case ${register[$memside]}${register[$memside+1]} in
63|36) MEM=0 ;;
73|37) MEM=1 ;;
65|56) MEM=2 ;;
75|57) MEM=3 ;;
6) MEM=4 ;;
7) MEM=5 ;;
5) MEM=6 ;;
3) MEM=7 ;;
*) echo `hexout $H` ": \
real mode parser is looking for MEM value unsuccessfully.
Rmode memref register combinations with byte are limited to
DI/B, SI/B, DI/BP, SI/BP, B, BP, SI and DI.
" ;;
esac
else # regs, cell
mo=2
case ${register[$memside]}${register[$memside+1]} in
63|36) MEM=0 ;;
73|37) MEM=1 ;;
65|56) MEM=2 ;;
75|57) MEM=3 ;;
6) MEM=4 ;;
7) MEM=5 ;;
5) MEM=6 ;;
3) MEM=7 ;;
*) echo `hexout $H` ": \
real mode parser is looking for MEM value unsuccessfully.
Rmode memref register combinations with lit. are limited to
DI/B, SI/B, DI/BP, SI/BP, B, BP, SI and DI.";;
esac
fi
else # no displ
mo=0
case ${register[$memside]}${register[$memside+1]} in
63|36) MEM=0 ;;
73|37) MEM=1 ;;
65|56) MEM=2 ;;
75|57) MEM=3 ;;
6) MEM=4 ;;
7) MEM=5 ;;
3) MEM=7 ;;
*) echo `hexout $H` ": \
real mode parser is looking for MEM value unsuccessfully.
Rmode memref register combinations without displ. are limited
to DI/B, SI/B, DI/BP, SI/BP, B, SI and DI.";;
esac
fi
ao $mo$REG$MEM
if test "$mo" = "1" ;then # byte displacement
ab ${number[$memside]}
fi
if test "$mo" = "2" ;then # cell displacement
ac ${number[$memside]}
fi # none
if test "$mo" = "0" && test $MEM = 6 ;then # displ. only
ac ${number[$memside]}
fi
} ###### REAL #################
modenotsupported ( ) { # internal
echo -e `hexout $H`": osimpa $1 doesn't support " $modestring " mode."
}
#( )
#_______________x86_x86x86x86x86x86x86x8
6x86x86_________ ops ( )
echo x86
# instruction prefixes ( )
otheroperandsize ( ) { # -x86 prefix
if test "$1" = "h" ; then echo -e "\n\n\n\n
The following instruction is to be interpreted at the opposite operand
size from what the current default is, as per this segment's
descriptor.\n\n"
else ################
HL
ab 0x66
opnote otheroperandsize $*
fi
}
otheraddresssize ( ) { # -x86 prefix
if test "$1" = "h" ; then echo -e "\n\n\n\n
following instruction is to be interpreted at the opposite address size
from what the current default is, as per this segment's descriptor.\n"
else ################
HL
ab 0x67
opnote otheraddresssize $*
fi
}
#()
# x86 general instructions ()
= () { # Clobber. many variants, most freq. insn -x86 MOV
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel MOV\n
catch-all. copy. The most prevalent insn. The only access to special
registers. Widest range of memory addressing forms. The fast mem<->A
forms may be A= and =A by the time you read this.
! would be better but it conflicts with Bash.
"
else ################
HL
parse $*
case $modestring in
ID) ao 27${register[$dest]}
ac ${number[$source]} ;;
DD) ab 0x89
ao 3${register[0]}${register[2]} ;;
MD) ab 0x8b
memref ${register[2]} ;;
DM) ab 0x89
memref ${register[$source]} ;;
IM) ab 0xc7
memref 0
ac ${number[$source]} ;;
1ID) ao 26${register[$dest]}
ab ${number[$source]} ;;
1DD) ab 0x88
ao 3${register[0]}${register[2]} ;;
1MD) ab 0x8a
memref ${register[2]} ;;
1DM) ab 0x88
memref ${register[$source]} ;;
1IM) ab 0xc6
memref 0
ab ${number[$source]} ;;
Dsegm) ab 0x8e # typo in 386INTEL.TXT
ao 3${register[$dest]}${register[source]};;
segmD) ab 0x8c
ao 3${register[$source]}${register[dest]};;
*speC) ab 0x0f 0x22
ao 3${register[$dest]}${register[source]};;
*speD) ab 0x0f 0x23
ao 3${register[$source]}${register[source]};;
*speT) ab 0x0f 0x26
ao 3${register[$dest]}${register[source]};;
*speC*) ab 0x0f 0x20
ao 3${register[$source]}${register[dest]};;
*speD*) ab 0x0f 0x21
ao 3${register[$source]}${register[dest]};;
*speT*) ab 0x0f 0x24
ao 3${register[$source]}${register[dest]};;
*) modenotsupported = ;;
esac
opnote = $*
fi
}
=extend ( ) { # copy with sign extension -x86 MOVSX
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel MOVSX\n
copy, sign-extending the destination.\n\n"
else ################
HL
parse $*
case $modestring in
1M*) ab 0x0f 0xbe
memref ${register[$dest]} ;;
M*) ab 0x0f 0xbf
memref ${register[$dest]} ;;
*) modenotsupported =extend ;;
esac
opnote =extend $*
fi
}
=0extend ( ) { # copy with zero-extension -x86 MOVZX
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel MOVZX\n
copy, filling the high-order bits of the destination with zeros. Much
different than a less-than-whole-register copy.\n\n\n"
else ################
HL
parse $*
case $modestring in
1M*) ab 0x0f 0xb6
memref ${register[$dest]} ;;
M*) ab 0x0f 0xb7
memref ${register[$dest]} ;;
*) modenotsupported =0extend ;;
esac
opnote =0extend $*
fi
}
1+ () { # increment -x86 INC
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel INC\n
add 1 to whatever. Effects flags.\n\n"
else ################
HL
parse $*
case $modestring in
1*) ab 0xfe
memref 0 ;;
M) ab 0xff
memref 0 ;;
D) ao 10${register[$source]} ;;
*) modenotsupported increment ;;
esac
opnote 1+ $*
fi
}
1- () { # minus one, decrement -x86 DEC
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel DEC\n
decrement. 2 clocks.\n\n\n"
else ################
HL
parse $*
case $modestring in
1*) ab 0xfe
memref 1 ;;
M) ab 0xff
memref 1 ;;
D) ao 11${register[$source]} ;;
*) modenotsupported decrement
echo $H ;;
esac
opnote 1- $*
fi
}
/ () { # divide. 38 clocks. -x86 DIV
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel DIV\n
38 clocks, no early-out. Divides D:A by arg, modulo in D, quotient in A,
I think."
else ################
HL
parse $*
ab 0xf7
ao 36${register[$source]}
opnote / $*
fi
}
+ () { # add -x86 ADD
if test "$1" = "h" ; then echo -e "\n\n
Add without including the carry (flag) bit. \n
See also +byte and +A.
"
else ################
HL
parse $*
case $modestring in
ID) ab 0x81
ao 30${register[2]}
ac ${number[$source]} ;;
DD) ab 0x01
ao 3${register[0]}${register[2]} ;;
MD) ab 0x03
memref ${register[2]} ;;
DM) ab 0x01
memref ${register[$source]} ;;
IM) ab 0x81
memref 0
ac ${number[$source]} ;;
1ID) ab 0x80
ao 30${register[2]}
ab ${number[$source]} ;;
1DD) ab 0x00
ao 3${register[0]}${register[2]} ;;
1MD) ab 0x02
memref ${register[2]} ;;
1DM) ab 0x00
memref ${register[0]} ;;
1IM) ab 0x80
memref 0
ab ${number[$source]} ;;
*) modenotsupported + ;;
esac
opnote + $*
fi
}
+A () { # add immediate to A, 32 bit only -x86 ADD
if test "$1" = "h" ; then echo -e "\n\n
Add immediate to A, 32 bit support only at this point.
"
else ############
HL
parse $*
ab 5
aq ${number[0]}
opnote +A $*
fi
}
+byte () { # add immediate byte -x86 ADD
if test "$1" = "h" ; then echo -e "\n\n
Add without including the carry (flag) bit, a byte to be sign-extended
and a register or memory argument of any size. If the value added to is
a page-aligned array you have a fast ring index, yes?
"
else ############
HL
parse $*
ab 0x83
memref 0
ab ${number[source]}
opnote +byte $*
fi
}
+carry () { # add with carry. -x86 ADC
if test "$1" = "h" ; then echo -e "\n\n
add two args and the carry bit. \n "
else ################
HL
parse $*
case $modestring in
ID) ab 0x81
ao 32${register[2]}
ac ${number[$source]} ;;
DD) ab 0x11
ao 3${register[0]}${register[2]} ;;
MD) ab 0x13
memref ${register[2]} ;;
DM) ab 0x11
memref ${register[$source]} ;;
IM) ab 0x81
memref 2
ac ${number[$source]} ;;
1ID) ab 0x80
ao 32${register[2]}
ab ${number[$source]} ;;
1DD) ab 0x10
ao 3${register[0]}${register[2]} ;;
1MD) ab 0x02
memref ${register[2]} ;;
1DM) ab 0x10
memref ${register[0]} ;;
1IM) ab 0x80
memref 2
ab ${number[$source]} ;;
*) modenotsupported +carry ;;
esac
opnote +carry $*
fi
}
+carryA ( ) { # add immediate to A, 32 bit only -x86 ADC
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SUB\n
add with carry immediate to A, 32 bit only."
else ################
HL
ab 0x15
aq ${number[0]}
opnote +carryA $*
fi
}
- () { # subtract. -x86 SUB
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SUB\n
Subtract without including borrow (carry) bit.\n\n"
else ################
HL
parse $*
case $modestring in
ID) ab 0x81
ao 35${register[2]}
ac ${number[$source]} ;;
DD) ab 0x29
ao 3${register[0]}${register[2]} ;;
MD) ab 0x2b
memref ${register[2]} ;;
DM) ab 0x29
memref ${register[$source]} ;;
IM) ab 0x81
memref 5
ac ${number[$source]} ;;
1ID) ab 0x80
ao 35${register[2]}
ab ${number[$source]} ;;
1DD) ab 0x28
ao 3${register[0]}${register[2]} ;;
1MD) ab 0x2a
memref ${register[2]} ;;
1DM) ab 0x28
memref ${register[0]} ;;
1IM) ab 0x80
memref 5
ab ${number[$source]} ;;
*) modenotsupported - ;;
esac
opnote - $*
fi
}
-test () { # do a subtract but save only the flags -x86 CMP
if test "$1" = "h" ; then echo -e "\n\n
Do a subtract but save only the flags."
else ################
HL
parse $*
case $modestring in
ID) if test "${register[$dest]}" = 0 ; then
ab 0x3d
ac ${number[0]}
else
ab 0x81
ao 37${register[2]}
ac ${number[$source]}
fi ;;
DD) ab 0x39
ao 3${register[0]}${register[2]} ;;
DM) ab 0x39
memref ${register[$source]} ;;
MD) ab 0x3b
memref ${register[$dest]} ;;
IM) ab 0x81
memref 7
ac ${number[$source]} ;;
1ID) if test "${register[$dest]}" = 0 ; then
ab 0x3c
ac ${number[0]}
else
ab 0x80
ao 32${register[2]}
ab ${number[$source]}
fi ;;
1DD) ab 0x38
ao 3${register[0]}${register[2]} ;;
1DM) ab 0x38
memref ${register[0]} ;;
1IM) ab 0x80
memref 7
ab ${number[$source]} ;;
*) modenotsupported -test ;;
esac
opnote -test $*
fi
}
-borrow () { # dest - source - carry --> dest -x86 SBB
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SBB\n
Subtract with borrow (carry). \n\n"
else ################
HL
parse $*
case $modestring in
ID) ab 0x81
ao 33${register[2]}
ac ${number[$source]} ;;
DD) ab 0x19
ao 3${register[0]}${register[2]} ;;
MD) ab 0x1b
memref ${register[2]} ;;
DM) ab 0x19
memref ${register[$source]} ;;
IM) ab 0x81
memref 3
ac ${number[$source]} ;;
1ID) ab 0x80
ao 33${register[2]}
ab ${number[$source]} ;;
1DD) ab 0x18
ao 3${register[0]}${register[2]} ;;
1MD) ab 0x1a
memref ${register[2]} ;;
1DM) ab 0x18
memref ${register[0]} ;;
1IM) ab 0x80
memref 3
ab ${number[$source]} ;;
esac
opnote -borrow $*
fi
}
AND () { # Boolean bitwise AND -x86 =
if test "$1" = "h" ;then echo -e "\n\n
Boolean bitwise AND. Result is true only if A AND B are true.
one-bit results (truth table) with two input bits A and B
B
1 0
_|_______________
|
0 | 0 0
A |
1 | 0 1
\n"
else ################
HL
parse $*
case "$modestring" in
ID) ab 0x81
ao 34${register[2]}
ac ${number[$source]} ;;
DD) ab 0x21
ao 3${register[0]}${register[2]} ;;
MD) ab 0x23
memref ${register[2]} ;;
DM) ab 0x21
memref ${register[$source]} ;;
IM) ab 0x81
memref 4
ac ${number[$source]} ;;
1ID) ab 0x80
ao 34${register[2]}
ab ${number[$source]} ;;
1DD) ab 0x20
ao 3${register[0]}${register[2]} ;;
1MD) ab 0x22
memref ${register[2]} ;;
1DM) ab 0x20
memref ${register[0]} ;;
1IM) ab 0x80
memref 4
ab ${number[$source]} ;;
*) modenotsupported AND ;;
esac
opnote AND $*
fi
}
ANDtest () { # AND with no result but flags -x86 TEST
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel TEST\n
Do an AND and set the flags accordingly, but don't actually assert the
result value on either of the arguments. The quick A form requires A be the
destination arg and the immediate be the source, which is entirely syntactic
since nothing gets moved.
\n\n"
else ################
HL
parse $*
case $modestring in
ID) if test "${register[$dest]}" = 0 ; then
ab 0xa9
ac ${number[0]}
else
ab 0xf7
ao 30${register[2]}
ac ${number[$source]}
fi ;;
DD) ab 0x85
ao 3${register[0]}${register[2]} ;;
DM) ab 0x85
memref ${register[$source]} ;;
IM) ab 0xf7
memref 0
ac ${number[$source]} ;;
1ID) if test "${register[$dest]}" = 0 ; then
ab 0xa8
ac ${number[0]}
else
ab 0xf6
ao 30${register[2]}
ab ${number[$source]}
fi ;;
1DD) ab 0x84
ao 3${register[0]}${register[2]} ;;
1DM) ab 0x84
memref ${register[0]} ;;
1IM) ab 0xf7
memref 0
ab ${number[$source]} ;;
*) modenotsupported ANDtest
echo "386 ANDtest doesn't do MD)"
;;
esac
opnote ANDtest $*
fi
}
NOT () { # invert the bits -x86 =
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel NOT\n
Boolean bitwise not. Invert all the bits. All zeros become ones and
vice-versa.\n\n"
else ################
HL
parse $*
case $modestring in
D) ab 0xf7
ao 32${register[0]} ;;
M)
ab 0xf7
memref 2 ;;
1M)
ab 0xf6
memref 2 ;;
1D) ab 0xf6
ao 32${register[0]} ;;
*) modenotsupported not ;;
esac
opnote NOT $*
fi
}
OR () { # Boolean bitwise OR -x86 =
if test "$1" = "h" ; then echo -e "\n\n
Boolean bitwise OR. AKA "inclusive OR". If either source bit, A OR B, is
1, then result is 1.
one-bit results (truth table) with input bits A and B
B
1 0
_|_______________
|
0 | 1 0
A |
1 | 1 1
\n" ; else ################
HL
parse $*
case $modestring in
1DD) ab 0x08
ao 3${register[0]}${register[2]} ;;
ID) if test "${register[$dest]}" = 0 ; then
ab 0x0d
ac ${number[0]}
else
ab 0x81
ao 31${register[2]}
ac ${number[$source]}
fi ;;
DD) ab 0x09
ao 3${register[0]}${register[2]} ;;
MD) ab 0x0b
memref ${register[2]} ;;
DM) ab 0x09
memref ${register[$source]} ;;
IM) ab 0x81
memref 1
ac ${number[$source]} ;;
1ID) if test "${register[$dest]}" = 0 ; then
ab 0x0c
ab ${number[0]}
else
ab 0x80
ao 31${register[2]}
ab ${number[$source]}
fi ;;
1MD) ab 0x0a
memref ${register[2]} ;;
1DM) ab 0x08
memref ${register[0]} ;;
1IM) ab 0x80
memref 1
ab ${number[$source]} ;;
*) modenotsupported OR ;;
esac
opnote OR $*
fi
}
XOR () { # Boolean bitwise exclusive OR -x86 =
if test "$1" = "h" ; then echo -e "\n
Boolean bitwise Exclusive-OR. Result is true if exclusively A OR B is
true. A XOR 1 toggles A, for example.
one-bit results (truth table) with input bits A and B
B
1 0
_|_______________
|
0 | 1 0
A |
1 | 0 1
\n\n" ; else ################
HL
parse $*
case $modestring in
ID) if test "${register[$dest]}" = 0 ; then
ab 0x35
ac ${number[0]}
else
ab 0x81
ao 36${register[2]}
ac ${number[$source]}
fi ;;
DD) ab 0x31
ao 3${register[0]}${register[2]} ;;
MD) ab 0x33
memref ${register[2]} ;;
DM) ab 0x31
memref ${register[$source]} ;;
IM) ab 0x81
memref 6
ac ${number[$source]} ;;
1ID) if test "${register[$dest]}" = 0 ; then
ab 0x34
ac ${number[0]}
else
ab 0x80
ao 36${register[2]}
ab ${number[$source]}
fi ;;
1DD) ab 0x30
ao 3${register[0]}${register[2]} ;;
1MD) ab 0x32
memref ${register[2]} ;;
1DM) ab 0x30
memref ${register[0]} ;;
1IM) ab 0x80
memref 6
ab ${number[$source]} ;;
*) modenotsupported XOR ;;
esac
opnote XOR $*
fi
}
biton () { # set a particular bit to 1 -x86 BTS
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel BTS\n
Save bit in carry flag and set addressed bit to 1 in source value. 6
clocks.
\n\n"
else ################
HL
parse $*
case $modestring in
DM) ab 0x0f 0xab
memref ${register[$source]} ;;
DD) ab 0x0f 0xab
ao 3${register[0]}${register[2]} ;;
ID) ab 0x0f 0xba
ao 35${register[2]}
ac ${number[$source]} ;;
IM) ab 0x0f 0xba
memref 5
ac ${number[$source]} ;;
*) modenotsupported biton ;;
esac
opnote biton $*
fi
}
bitoff () { # set a particular bit to 0 -x86 BTR
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel BTR\n
Save bit in carry flag and reset to 0.\n\n"
else ################
HL
parse $*
case $modestring in
DM) ab 0x0f 0xb3
memref ${register[$source]} ;;
DD) ab 0x0f 0xb3
ao 3${register[0]}${register[2]} ;;
ID) ab 0x0f 0xba
ao 36${register[2]}
ac ${number[$source]} ;;
IM) ab 0x0f 0xba
memref 6
ac ${number[$source]} ;;
*) modenotsupported bitoff ;;
esac
opnote bitoff $*
fi
}
flagbit () { # get designated bit to the carry flag -x86 BT
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\Intel BT\n\n\n\n
Takes a 1 byte immediate or a reg/mem as a bit offset and a base address
or register spec and puts the value of the bit so specified into the carry
flag.
It's two bytes for the opcode, one for the literal maybe, or the usual
mod/sib bletchery. INTeL seems to prefer the carry bit as the general
Boolean, but I don't see why. In most coding situations I'd use ANDtest, a
4-byte literal, and ZF, thus staying more generic for a couple bytes. I
wouldn't have it in here except I used it in asmacs for askodd.
Lightly tested, if that.
\n\n\n"
else ################
HL
parse $*
case $modestring in
DM) ab 0x0f 0xa3
memref ${register[$source]} ;;
DD) ab 0x0f 0xa3
ao 3${register[0]}${register[2]} ;;
ID) ab 0x0f 0xba
ao 34${register[2]}
ab ${number[$source]} ;;
IM) ab 0x0f 0xba
memref 4
ab ${number[$source]} ;;
*) modenotsupported flagbit ;;
esac
opnote flagbit $*
fi
}
call () { # jump to subroutine, push return address -x86 CALL
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel CALL\n\n\n
Jump to the immediately following address or other value, normally that
of a subroutine, stacking a frame to return to on occurance of the return
(Intel RET) instruction. Frames vary widely by TYPE of call on 386+. There
are intersegment jumps, which are selectors defining gates of various
types in (32 bit) protected mode. call or similar is also known as jsr or
gosub on other machines. The variants of call usually require a FAR
syntactic spamatazoan in other assemblers. shasm syntaxes for the more
mutated forms of call are...
Call intersegment to full pointer given, shasm syntax...
segment offset
call dual 0xxxx to quad 0xxxxx
LAAETTR (gas doesn't do segments explicitly either, IIRC. Nor do most
other CPUs, BTW.)
\n\n"
else ################
HL
parse $*
case $modestring in
I) ab 0xe8
branch $1 $cell ;;
D) ab 0xff
ao 32$register ;;
*) modenotsupported call ;;
esac
opnote call $*
fi
}
compares ( ) { # plural -test, used by match -x86 CMPSx
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel CMPSx
use \"match\". This isn't useful otherwise that I can see. :o)
Any argument to \"compares\" is taken as \"use bytes\". "
else ################
HL
parse $*
if test "$1" ;then
ab 0xa6 # if tH was a size spec it was byte
else
ab 0xa7
fi
opnote compares $*
fi
}
clearcarry ( ) { # set carry bit/flag to 0 -x86 CLC
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel CLC\n
unset the carry flag. Make it 0.\n\n\n"
else ################
HL
ab 0xf8
opnote clearcarry $*
fi
}
decreasing ( ) { # tell plurals to decr. C. Not the default.-x86 STD
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel STD\n
Set memory segment loop (string) operations direction flag to
towards-lower-addresses. Segment ops then will traverse the segments
high-to-low. \n"
else ################
HL
ab 0xfd
opnote decreasing $*
fi
}
downroll () { # down-significance bit-rotate -x86 ROR
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
Intel ROR\n
down-significance roll, rotate. Roll amount is source if immediate."
else ################
HL
parse $*
case $modestring in
ID) ab 0xc1
ao 31${register[2]}
ab ${number[$source]} ;;
IM) ab 0xc1
memref 1
ab ${number[$source]} ;;
D) ab 0xd3
ao 31${register[0]} ;;
M) ab 0xd3
ao 01${register[0]} ;;
1ID) ab 0xc0
ao 31${register[2]}
ab ${number[$source]} ;;
1IM) ab 0xc0
memref 1
ab ${number[$source]} ;;
1D) ab 0xd2
ao 31${register[0]} ;;
1M) ab 0xd2
ao 01${register[0]} ;;
*) modenotsupported downroll ;;
esac
opnote downroll $*
fi
}
downrollcarry ( ) { # down-significance bit-rotate thru carry -x86 RCR
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel RCR\n
down-significance roll, rotate. The carry bit is part of the roll.\n\n\n"
else ################
HL
parse $*
case $modestring in
ID) ab 0xc1
ao 33${register[2]}
ab ${number[$source]} ;;
IM) ab 0xc1
memref 3
ab ${number[$source]} ;;
D) ab 0xd3
ao 33${register[0]} ;;
M) ab 0xd3
ao 03${register[0]} ;;
1ID) ab 0xc0
ao 33${register[2]}
ab ${number[$source]} ;;
1IM) ab 0xc0
memref 3
ab ${number[$source]} ;;
1D) ab 0xd2
ao 33${register[0]} ;;
1M) ab 0xd2
ao 03${register[0]} ;;
*) modenotsupported downrollcarry ;;
esac
opnote downrollcarry $*
fi
}
downshift () { # down-significance bitshift, 0-fill high. -x86 SHR
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SAR\n
down-significance bitshift. TH are two. This is the one that fills the
high side vacated bits with 0. The low-order bit is shifted into the
carry flag. The other preserves the sign. That (Intel SAR) isn't present
in osimpa as I write this.\n"
else ################
HL
parse $*
case $modestring in
ID) ab 0xc1
ao 35${register[2]}
ab ${number[$source]} ;;
IM) ab 0xc1
memref 5
ab ${number[$source]} ;;
D) ab 0xd3
ao 35${register[0]} ;;
M) ab 0xd3
ao 05${register[0]} ;;
1ID) ab 0xc0
ao 35${register[2]}
ab ${number[$source]} ;;
1D) ab 0xd2
ao 35${register[0]} ;;
1M) ab 0xd2
memref 5
ab ${number[$source]} ;;
*) modenotsupported downshift ;;
esac
opnote downshift $*
fi
}
signeddownshift () { # down-significance bitshift, sign-fill high.-x86
SAR
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SAR\n
down-significance bitshift. There are two. This is the one that fills the
high side vacated bits with . The low-order bit is shifted into the
carry flag. The other preserves the sign. That (Intel SAR) isn't present
in osimpa as I write this.
Is now.
\n"
else ################
HL
parse $*
case $modestring in
ID) ab 0xc1
ao 37${register[2]}
ab ${number[$source]} ;;
IM) ab 0xc1
memref 7
ab ${number[$source]} ;;
D) ab 0xd3
ao 37${register[0]} ;;
M) ab 0xd3
ao 05${register[0]} ;;
1ID) ab 0xc0
ao 37${register[2]}
ab ${number[$source]} ;;
1D) ab 0xd2
ao 37${register[0]} ;;
1M) ab 0xd2
memref 7
ab ${number[$source]} ;;
*) modenotsupported downshift ;;
esac
opnote downshift $*
fi
}
extend () { # sign-extend A to D:A -x86 CWD
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel CWD\n
Sign-extend A into A:D, i.e. D becomes all the same as the sign bit of
A.\n\n"
else ################
HL
ab 0x99
opnote extend $*
fi
}
fetches () { # @ SI to A, scaled in/decr. SI, decr. C -x86 LODSx
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LODSx
fetches loads the A register with the memory byte, word, or doubleword at
the location pointed to by the SI register. After the transfer is made,
the SI register is automatically advanced. If the direction flag is 0
(increasing was executed, which is the default state), the source index
increments. fetches doesn't decrement C. fetches doesn't make sense with
repeating and friends, but is useful if alone or in a loop of some kind,
for checksums, for example. See sum, xsum.
Any argument to fetches is taken as a directive to use bytes rather than
cells. "
else #######################
HL
parse $*
if test "$1" ;then
ab 0xac # if there was a size spec it was byte
else
ab 0xad
fi
opnote fetches $*
fi
}
flags () { # copy FLAGS into AX -x86 LAHF
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LAHF\n
copy FLAGS into AX
\h\h"
else ################
HL
ab 0x9f
opnote flags $*
fi
}
halt ( ) { # cool the CPU until an interrupt -x86 HLT
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel HLT\n
halt processor until next hardware interrupt. Be nice to your CPU.
This is what the Linux \"idle task\" does.\n\n
This probably wants to be at the start of the top loop of a kernel H3sm.
"
else ################
HL
ab 0xf4
opnote halt $*
fi
}
increasing ( ) { # set plurals to incr C. The default. -x86 CLD
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel CLD\n
Setstring operations direction flag to toward-higher-addresses\n\n"
else ################
HL
ab 0xfc
opnote increasing $*
fi
}
invertbit ( ) { # bit test and complement -x86 BTC
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel BTC\n
Save specified bit of operand into carry flag and complement it in operand.
\n\n"
else ################
HL
parse $*
case $modestring in
DM) ab 0xbb
memref ${register[$source]} ;;
DD) ab 0xbb
ao 3${register[0]}${register[2]} ;;
ID) ab 0xba
ao 37${register[2]}
ac ${number[$source]} ;;
IM) ab 0xba
memref 7
ac ${number[$source]} ;;
*) modenotsupported invertbit ;;
esac
opnote invertbit $*
fi
}
invertcarry ( ) { # flip the carry bit/flag -x86 CMC
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel CMC\n
flip the carry flag bit\n\n"
else ################
HL
ab 0xf5
opnote invertcarry $*
fi
}
jump () { # unconditional branch -x86 JMP
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel JMP\n
Partial support here. Hand-roll your far jumps.
Unconditional branch. Various addressing modes.
x86 has register INdirect absolute, but not direct or relative.
See jumpshort also.
\n\n\n"
else ################
HL
parse $*
case $modestring in
1I) ab 0xeb
branch $1 1 ;;
I) ab 0xe9
branch $1 $cell ;;
# register absolute
D) ab 0xff
memref 4
;;
# register and literal offset
M) ab 0xff
memref 4
ac $number ;;
*) modenotsupported jump ;;
esac
opnote jump $*
fi
}
jumpshort () { # unconditional branch, 1-byte range -x86
JMP
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel JMP\n
This is a different opcode than the cellwise jump. And I'm not in the mood
for parsing one token. And osimplay doesn't address the surprisingly nasty
problem of deciding jump ranges. It's your problem. So here. The 2-byte
jump. The usual is 5.
L bla
stuff # 127 bytes or less of stuff
jumpshort bla
\n\n\n"
else ################
HL
ab 0xeb
let jt=$1-$H-1
ab $jt
opnote jumpshort $*
fi
}
address () { # x86 artifact quick address-math thingy -x86 LEA
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LEA\n
Store effective address for memory reference in register, i.e. do the
address math but don't do the fetch, rather just store the address. This
does the address arithmatic and leaves the result of that, and doesn't
fetch the referenced object. This op is an artifact of the 386
architechture, and thus has limited applicability, but is extremely
efficient when it is useful. Write yourself a times5 macro to see what I
mean.
address does as much as
constant_literal + register + ( register *2^ [1,2,3]) to reg/mem dest
in two clock ticks, the minimum for any instruction. register can all be
the same or whatever. This is a 386 linear address, with no paging or
segment implications.
The register getting shifted cannot be SP in normal memrefs, and probably
not here.
This can be fun for optimizing things when it fits the task, but is
pathologically x86-specific, being an artifact of the 386's unusual
addressing prowess.
address @ 400 A A *2^ 2 to C
SHOULD put 400+(A*5) in C. Test liberally.
\n\n"
else ################
HL
parse $*
ab 0x8d
memref ${register[$dest]}
opnote address $*
fi
}
lookup () { # byte lookup for e.g. ASCII->EBCDIC -x86 XLATB
if test "$1" = "h" ; then echo -e "\n\t\t\t\tIntel XLATB\n
Set AL to memory byte DS:[BX + unsigned AL]. One-byte instruction.
In other words, exchange AL for the byte in the table at BX that AL was
the index to.
This seems to be geared for rapid conversions such as between ASCII and
EBCDIC. The index value is replaced by the indexed value.
5 clocks.
\n"
else ################
HL
ab 0xd7
opnote lookup $*
fi
}
ls1bit () { # bit number of least significant 1-bit -x86 BSF
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel BSF\n
Find least significant ON-bit. 10 clocks +. Result is the number
of leading 0 bits.
BSF scans the bits in the second word or doubleword operand starting with
bit 0. The ZF flag is cleared if the bits are all 0; otherwise, the ZF flag
is set and the destination register is loaded with the bit index of the
first set bit.
\n\n"
else ################
HL
parse $*
ab 0x0f 0xbc
memref ${register[$dest]}
opnote ls1bit $*
fi
}
ms1bit () { # bit number of most significant 1-bit -x86 BSR
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel BSR\n
0F r32,r/m32 10+3n Bit scan reverse on r/m cell
Find most significant ON-bit. 10 + \(3 x offbits\) clocks.
Flags effected: Zero\n\n"
else ################
HL
ab 0x0f 0xbd
parse $*
memref ${register[$dest]}
opnote ms1bit $*
fi
}
multiply () { # arg * A --> D:A -x86 MUL
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel MUL\n
9 to 41 clocks. Early-out algorithm, unlike division.
source * A to D:A
\n\n"
else ################
HL
parse $*
case $modestring in
D) ab 0xf7
ao 34${register[0]} ;;
M) ab 0xf7
memref 4 ;;
1D) ab 0xf7
ao 34${register[0]} ;;
1M) ab 0xf7
memref 4 ;;
*) modenotsupported multiply ;;
esac
opnote multiply $*
fi
}
negate () { # 2's-complement; Boolean NOT, then incr. -x86 NEG
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel NEG\n
Two's complement negate, 2 or 6 clocks. simple NOT, then increment.\n\n"
else ################
HL
parse $*
case $modestring in
D) ab 0xf7
ao 33${register[0]} ;;
M)
ab 0xf7
memref 3 ;;
1M)
ab 0xf6
memref 3 ;;
1D) ab 0xf6
ao 33${register[0]} ;;
*) modenotsupported negate ;;
esac
opnote negate $*
fi
}
nop () { # burn 3 clocks with no state effect -x86 NOP
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel NOP\n
no operation, eat some clock. x86 trivia, it's actually swap A with A or
something.
\n\n"
else ################
HL
ab 0x90
opnote nop $*
fi
}
recieve ( ) { # x86 "IO port" instruction -x86 IN
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel IN\n
Input from port
Takes a byte/dual/quad arg followed by an optional port IO address. The
port address will be compembled as a byte. Use DX to specify a high port.
recieve dual [ 0x20 ]
The actual code is the same for dual or quad, and a port in EDX is
effectively 16 bits. The recieved byte or cell will be in A.
\n\n\n\n"
else ################
HL
if test "$1" = "byte" ;then
if test $2 ;then
ab 0xe4 $2
else
ab 0xec
fi
else
if test $2 ;then
ab 0xe5 $2
else
ab 0xed
fi
fi
opnote recieve $*
fi
}
return () { # return from a near call -x86 RET
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel RET\n
Return from a call. An immediate argument assembled the frame-dropping
form. The immediate value is 16 bits to drop the stack (add to SP) by that
many bytes. osimplay entrance procedures use that form.
\n\n"
else ################
HL
parse $*
case $modestring in
I) ab 0xc2
ad ${number[$source]} ;;
*) ab 0xc3 ;;
esac
opnote return $*
HL
fi
}
send ( ) { # x86 "IO port" instruction -x86 OUT
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel OUT\n
Output to a port. immediate byte or contents of DX is port #. Datum is
always in A, which is byte or cell.
\n\n"
else ################
HL
if test $1 = "byte" ;then
if test $2 ;then
ab 0xe6 $2
else
ab 0xee
fi
else
ab 0xef
fi
opnote send $*
fi
}
setcarry ( ) { # set carry flag/bit to 1 -x86 STC
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel STC\n
assert carry=true, 1.\n\n"
else ################
HL
ab 0xf9
opnote setcarry $*
fi
}
setflags () { # copy AH to the FLAGS register-half -x86 SAHF
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SAHF\n
copy AH to the FLAGS register-half.\n\n"
else ################
HL
ab 0x9e
opnote setflags $*
fi
}
signedmultiply () { # partial -x86 IMUL
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel IMUL\n
these you'll have to roll by hand
6B /r ib IMUL r16,r/m16,immbyte 9-14/12-17 word register r/m16 *
sign-extended immediate byte
69 /r ic IMUL r16,r/m16,immcell 9-22/12-25 word register r/m16 *
immediate word
from 386INTEL.TXT
IMUL has three variations:
3. A three-operand form; two are source and one is the destination
operand. One of the source operands is an immediate value stored in
the instruction; the second may be in memory or in any general
register. The product may be stored in any general register. The
immediate operand is treated as signed. If the immediate operand is a
byte, the processor automatically sign-extends it to the size of the
second operand before performing the multiplication.
3 distinct arguments. Gee I hate it when that happens. Use a directive to
tag the immediate value on the end of the thing. This isn't worth
reworking the parser. This thing is hideous and only about as fast as
floats anyway.
\n\n"
else ################
HL
parse $*
case "$modestring" in
*) modenotsupported signedmultiply ;;
esac
opnote signedmultiply $*
fi #nal
}
signeddivide () { # -x86 IDIV
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel IDIV\n
19 to 43 clocks. No early-out.
F7 /7 IDIV EAX,r/m32 43 Signed divide EDX:EAX by DWORD
byte (EAX=Quo, EDX=Rem)
Operation
temp ^[ dividend / divisor;
IF temp does not fit in quotient
THEN Interrupt 0;
ELSE
quotient ^[ temp;
remainder ^[ dividend MOD (r/m);
FI;
"
else ################
HL
parse $*
case $modestring in
# 11*) ab 0xf6
# memref 7 ;;
#
# 1c*) ab 0xf7
# memref 7 ;;
#
*) modenotsupported signeddivide ;;
esac
opnote signeddivide $*
fi
}
swap () { # swap two values. -x86 XCHG
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel XCHG\n
Exchange 2 values in one, usually 3 clock, instruction. swap locks the
bus during the swap, and Plan 9 therefor uses it for test-and-set. I
believe this is only an issue with SMP. All such instructions are atomic
vis-a-vis one CPU.
\n\n\n"
else ################
HL
parse $*
case $modestring in
D) ab $((0x90+${register[0]})) ;;
# and yes, "swap A" is in fact NOP ;o)
DD) ab 0x87
ao 3${register[0]}${register[2]} ;;
DM) ab 0x87
memref ${register[$source]} ;;
1DD) ab 0x86
ao 3${register[0]}${register[2]} ;;
1DM) ab 0x86
memref ${register[0]} ;;
*) modenotsupported swap
echo "
osimpa doesn't support using a memref
as the source operand to swap, which seems to be
slower that using a memref for the destination,
which seems to be merely an implementation
artifact, ie. for no real reason. The simplest
thing is to insist that memrefs be dest."
;;
esac
opnote exchange $*
fi
}
uprollcarry () { # up-significance bit roll through carry -x86 RCL
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel RCL\n
up-significance bit roll including carry in the ring of bits rolled"
else ################
HL
parse $*
case $modestring in
ID) ab 0xc1
ao 32${register[2]}
ab ${number[$source]} ;;
IM) ab 0xc1
memref 2
ab ${number[$source]} ;;
D) ab 0xd3
ao 32${register[0]} ;;
M) ab 0xd3
ao 02${register[0]} ;;
1ID) ab 0xc0
ao 32${register[2]}
ab ${number[$source]} ;;
1IM) ab 0xc0
memref 2
ab ${number[$source]} ;;
1D) ab 0xd2
ao 32${register[0]} ;;
1M) ab 0xd2
ao 02${register[0]} ;;
*) modenotsupported uprollcarry ;;
esac
opnote uprollcarry $*
fi
}
uproll () { # -x86 ROL
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel ROL\n
Rotate the bits in the up-significance direction. Bits rolling off the
high side roll back in on the low side.
"
else ################
HL
parse $*
case $modestring in
ID) ab 0xc1
ao 30${register[2]}
ab ${number[$source]} ;;
IM) ab 0xc1
memref 0
ab ${number[$source]} ;;
D) ab 0xd3
ao 30${register[0]} ;;
M) ab 0xd3
ao 00${register[0]} ;;
1ID) ab 0xc0
ao 30${register[2]}
ab ${number[$source]} ;;
1IM) ab 0xc0
memref 0
ab ${number[$source]} ;;
1D) ab 0xd2
ao 30${register[0]} ;;
1M) ab 0xd2
ao 00${register[0]} ;;
*) modenotsupported uproll ;;
esac
opnote uproll $*
fi
}
upshift () { # -x86 SAL
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SAL\n
Up-significance bitshift. Right shift, in the usual parlance. Zeros roll
in on low-significance end, bits are lost on the high-significance end.
Several forms take the shift amount from C implicitly. As of this writing,
I don't think those will get assembled more economically. FIX THIS
Shifts rock.
\n\n"
else ################
HL
parse $*
case $modestring in
ID) ab 0xc1
ao 34${register[2]}
ab ${number[$source]} ;;
IM) ab 0xc1
memref 4
ab ${number[$source]} ;;
D) ab 0xd3
ao 34${register[0]} ;;
M) ab 0xd3
ao 04${register[0]} ;;
1ID) ab 0xc0
ao 34${register[2]}
ab ${number[$source]} ;;
1IM) ab 0xc0
memref 4
ab ${number[$source]} ;;
1D) ab 0xd2
ao 34${register[0]} ;;
1M) ab 0xd2
ao 04${register[0]} ;;
*) modenotsupported upshift ;;
esac
opnote upshift $*
fi
}
when () { # IF. conditional branch. Many variants. -x86 jxx
if test "$1" = "h" ; then echo -e "\n\t\t\t\tIntel jxx
\"when\" is a wrapper for all the x86 conditional branches based on FLAGS
and also on the count register, C. \"when\" is used instead of \"if\" or
similar to not conflict with the shell, and I kinda like it subjectively.
when <not> <overflow> <<zero><carry>> <parity> <skew> $BRANCH_TARGET
e.g.
when not carry $target_labelname
skew is when
sign<>overflow. The actual opcode is constructed from these
tokens/values... not=1 overflow=0 sign=8 carry=2 zero=4 parity=10 skew=12
and this is trivial to implement because that's how the actual opcode is
built, so it seems to kinda want to be this way.
BUT, THer's MORE!(TM) I folded the variants of the Intel LOOP instruction
in here also. LOOP isn't a loop at all; it does forward branches just
fine. It is therefor when C-1 in osimplay, to imply that it performs the
decrement. There's also when C-1&zero and C-1&nonzero, which both also
decrement C before the test, and when C=0 which does NOT decrement C.
These latter when's only do short branches, but I haven't checked for the
branch width in the assembler. There are some thorny recursion issues with
figuring branch widths. The branches based on flags, i.e. not on C, can be
a byte or a cell. Cell (quad) is the default, and there's a \"short\"
token you can insert. I _COULD_ suss out backward branch widths without
that, but I haven't at this writing. Patches welcome. It seems the best
thing you can do for branch performance is rig your branches so the branch
is usually not taken, which is normally a bit better than a 2:1 win."
else #################
if test "$pass" = "2" ;then
HL
whenopnote="when "$*
fi
for branchtarg in $* ;do
:
done # $branchtarg is now branch target
branchsize=
opbase=0x70
let bla_=$#-1
while test $bla_ != "0" ;do
case $1 in # prevalence order roughly
not) let opbase=$opbase+1 ;;
zero) let opbase=$opbase+4 ;;
sign) let opbase=$opbase+8 ;;
short) branchsize=short ;;
carry) let opbase=$opbase+2 ;;
overflow) let opbase=$opbase ;;
C-1) let opbase=0xe2
branchsize=short ;;
C=0) let opbase=0xe3
branchsize=short ;;
C-1\&zero) let opbase=0xe1
branchsize=short ;;
C-1\&nonzero) let opbase=0xe0
branchsize=short ;;
parity) let opbase=$opbase+10 ;;
skew) let opbase=$opbase+12 ;;
*) echo $1 "
isn't a valid \"when\" qualifier."
;;
esac
shift
let bla_=$bla_-1
done
if test $branchsize ;then
#small
ab $opbase $(($branchtarg-$H-2))
if test $(($branchtarg-$H-2)) -gt 120 \
-o $(($branchtarg-$H-2)) -lt -120 ; then
echo "branchsize issue, a short branch may not be"
echo $H
fi
else
#BIG
ab 0x0f $(($opbase+16))
ac $(($branchtarg-$H-$cell))
fi
if test "$pass" = "2" ;then
echo -en " "$whenopnote >> $listing
fi
fi
}
widedownshift () { # partial -x86 SHRD
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SHRD\n
0F AC r/m32,r32,imm8 r/m32 gets SHR of r/m32 concatenatedwithr32
0F AD r/m32,r32,CL r/m32 gets SHR of r/m32 concatenated withr32
down-significance bitshift of a composite operand made of ??
This is another 3-op. Roll that one by hand.
This is used by scale , which made a bit of a splash in comp.lang.forth.
\n\n"
else ################
HL
parse $*
ab 0x0f 0xad
memref ${register[$source]}
opnote widedownshift $*
fi
}
wideupshift () { # partial -x86 SHLD
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SHLD\n
0F A4 SHLD r/m16,r16,imm8 r/m16 gets SHL of r/m16concatenated
with r16
0F A4 SHLD r/m32,r32,imm8 r/m32 gets SHL of r/m32concatenated
with r32
0F A5 SHLD r/m16,r16,CL r/m16 gets SHL of r/m16concatenated
partial support.
composite up-significance bitshift."
else ################
HL
ab 0x0f 0xa5
parse $*
memref ${register[$source]}
opnote wideupshift $*
fi
}
within () { # check value against 2 bounding values -x86 BOUND
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel BOUND\n
62/r BOUND r32,m32&32 10
Check if r32 is within bounds, (passes test). Bounds are adjacent
32 bit values in memory. Failure causes an exception,
Interrupt 5 if the bounds test fails.
C is notorious for letting you walk off the end of an array, but I don't
know that this is the appropriate cure.
\n\n"
else ################
HL
parse $*
ab 0x62
memref ${register[$dest]}
opnote within $*
fi
}
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ system ops ( )
CS ( ) { # -x86 prefix
if test "$1" = "h" ; then echo -e "\n\n
Following instruction is to use segment CS\n"
else ################
HL
ab 0x2e
opnote CS $*
fi
}
SS ( ) { # -x86 prefix
if test "$1" = "h" ; then echo -e "\n
\nFollowing instruction is to use segment SS\n"
else ################
HL
ab 0x36
opnote SS $*
fi
}
DS ( ) { # -x86 prefix
if test "$1" = "h" ; then echo -e "\n
\nFollowing instruction is to use segment DS\n"
else ################
HL
ab 0x3e
opnote DS $*
fi
}
ES ( ) { # -x86 prefix
if test "$1" = "h" ; then echo -e "\n
\nFollowing instruction is to use segment ES.
Certain uses of DI are tied to ES.
\n"
else ################
HL
ab 0x26
opnote ES $*
fi
}
FS ( ) { # -x86 prefix
if test "$1" = "h" ; then echo -e "\n
\nFollowing instruction is to use segment FS.
FS gets the NULL selector in Ha3sm.
\n"
else #################
HL
ab 0x64
opnote FS $*
fi
}
GS ( ) { # -x86 prefix
if test "$1" = "h" ; then echo -e "\n \n
Following instruction is to use segment GS. Prefix. FS and GS will
probably get the NULL selector in Ha3sm.
\n"
else ################
HL
ab 0x65
opnote GS $*
fi
}
lock ( ) { # -x86 prefix
if test "$1" = "h" ; then echo -e "\n\nIntel LOCK\n
SMP instruction atomicity extender. exchange does a LOCK anyway, which is
what Plan9 uses. A pull SS also locks the next instruction, usually
pull SP.
\n"
else
HL
ab 0xf0
opnote lock $*
fi
}
clearswitched ( ) { # -386
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel CLTS\n
clear the task-switched flag in EFLAGS. Ha3sm doesn't use the 386
task-switch facilities, BTW. Most 386 unices do, I think.\n\n"
else ################
HL
ab 0x0f 6
opnote clearswitched $*
fi
}
frame ( ) { # Pascal in hardware. Know a use? -x86 = wENTER
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel ENTER\n
Pascal in hardware. Know a use?
frame is a 386 complex instruction for creating a lexical level frame for
lexical block languages like Pascal. It fetches a list of pointers and
pushes thier values onto the stack.
See also: dropframe
frame ( 16bit framesize, 8bit lexical levels)
This is the psuedocode description of the thing based on INTEL386.TXT
...................................................................
{
level = level MOD 32 // level is byte 4 of instr encoding
// MOD 32 is AND 11111b
Push BP
frame-ptr = SP // frame-ptr is a CPU-internal temp var
if level > 0
{
for (i = 1 TO (level - 1))
{
BP = BP - 4
Push value _at_ BP
}
Push frame-ptr
}
BP = frame-ptr // BP is now old
SP = SP - ZeroExtend(First operand)
}
...................................................................
frame can take up to 139 clocks, depending on the levels argument. A
task-switch is about 300. The most interesting things I see about frame is
that it uses two internal variables that aren't registers, and that it
does a looping dereference over an array of up to 31 pointers. That is, it
collects dispersed values. It does all this atomically, which is important
when molesting the return stack. frame/dropframe is what gives BP it's
framepointer designation. They are the only instructions that use BP
implicitly. frame would need it's own syntax for it's arguments, and I'll
probably never use the thing, so I just coopt the shasm source/dest for
levels/size. In shasm syntax levels is source, frame size is dest, i.e.
source and dest don't mean what they usually do e.g.
levels size
frame 20 to 3 \n\n"
else ################
HL
parse $*
ab 0xc8
ad ${number[$source]}
ab ${number[$dest]}
opnote enter $*
fi
}
dismiss ( ) { # -x86 IRET
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel IRET\n
Interrupt return. Various stack effects per system state. A dismiss frame
is (E)IP, CS and (E)FLAGS, EIP at TOS. Some CPU protection exceptions push
an error code also.
\n"
else ################
HL
ab 0xcf
opnote dismiss $*
HL
HL
fi
}
farSS ( ) { # -386
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LSS\n
Load SS:r32 with pointer from memory\n"
else ################
HL
parse $*
ab 0x0f 0xb2
memref $dest
opnote farSS $*
fi
}
farDS ( ) { # -386
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LDS\n
Load DS:r32 with pointer from memory\n\n"
else ################
HL
parse $*
ab 0xc5
memref $dest
opnote farDS $*
fi
}
farES ( ) { # -386
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LES\n
Load ES:register with pointer from memory\n\n"
else ################
HL
parse $*
ab 0xc4
memref $dest
opnote farES $*
fi
}
farFS ( ) { # -386
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LFS\n
Load FS:register with pointer from memory\n\n"
else ################
HL
parse $*
ab 0x0f 0xb4
memref $dest
opnote farFS $*
fi
}
farGS ( ) { # -386
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LGS\n
Load GS:r32 with pointer from memory\n\n"
else ################
HL
parse $*
ab 0x0f 0xb4
memref $dest
opnote farGS $*
fi
}
#You set it, right?
#
#GDT ( ) { # -386
#if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SGDT\n
#store contents of Global Descriptor Table Register to memory at physical
#address. The obverse of this is crucial to protected mode.
#\n"
# else ################
#
# HL
# parse $*
# ab 0x0f 0x01
# if test "$cell" = 2 ; then
# ab 6
# else
# ab 5
# fi
# ac ${number[$source]} # physical address
# opnote GDT $*
# fi
# }
#
#
#IDT ( ) { # -386
#if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SIDT\n
#store contents of Interrupt Descriptor Table Register to memory at
#physical address. The obverse of this is crucial to protected mode.
#\n"
# else ################
#
# HL
## parse $*
# ab 0x0f 1
# if test "$cell" = 2 ; then
# ab 0x0e
# else
# ab 0x0d
# fi
# ac ${number[$source]} # physical address
# opnote IDT $*
# fi
# }
#
#
#LDT ( ) { # -386
#if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SLDT\n
#store Local Descriptor Table Register to memory physical address.
#
#If you use the 386 task register stuff and TSSs then I believe the deal
#is that each task has/needs an LDT.
#\n\n"
# else ################
# HL
# parse $*
# ab 0x0f 0
# if test "$cell" = 2 ; then
# ab 6
# else
# ab 5
# fi
# ac ${number[$source]} # physical address
# opnote LDT $*
# fi
# }
IRQson () { # -386
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel STI\n
Allow external hardware to interrupts the CPU. \"nosurprises\"
turns them off.
\n\n"
else ################
HL
ab 0xfb
opnote IRQson $*
fi
}
IRQsoff () { # -386
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel STI\n
Allow external hardware to interrupts the CPU. \"nosurprises\"
turns them off.
\n\n"
else ################
HL
ab 0xfa
opnote IRQsoff $*
fi
}
dropframe ( ) { # -x86 LEAVE
if test "$1" = "h" ; then echo -e "\n\t\t\t\tIntel LEAVE\n
De-allocate a Pascal-style module frame. Enter and leave are what makes
BP/ebp the \"frame pointer\". see also: enter. I don't see these as real
useful, but \"enter\" does a lot of stuff."
else ################
HL
ab 0xc9
opnote dropframe $*
fi
}
limit ( ) { # -x86 LSL
if test "$1" = "h" ; then echo -e "\n\t\t\t\tIntel LSL\n
load the limit value from a segment descriptor. \n"
else ################
HL
ab 0x0f 3
parse $*
memref ${register[$dest]}
opnote limit $*
fi
}
IRQsoff ( ) { # -x86 CLI
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel CLI\n
Disable external hardware interrupts to the CPU.
Useful at boot time maybe and for profound state-changes like
process-switches.\n\n"
else ################
HL
ab 0xfa
opnote IRQSoff $*
fi
}
overflowtrap ( ) { # -x86 INTO
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel INTO\n
cause invocation of a trap handler IF overflow bit is set.\n\n"
else ################
HL
ab 0xce
opnote overflowtrap $*
fi
}
readable ( ) { # -386 VERR
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel VERR\n
Set zero flag to true if segment of given selector can be read by
this process.
\n\n"
else ################
HL
ab 0x0f 0
parse $*
memref 4
opnote readable $*
fi
}
pullcore ( ) { # -386 POPA
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel POPA\n
copy (pop) DI, SI, BP, SP, B, D, C, and A off the stack.
adjusting stack pointer SP accordingly.\n\n"
else ################
HL
ab 0x61
opnote pullcore $*
fi
}
pull ( ) { # partial # unstack -386 POP
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel POP\n
Copy top of stack into operand, adjusting stack pointer SP accordingly.
pop, to my mind, means remove and lose a stack item. Hence pull.
\n\n"
else ################
HL
parse $*
case $modestring in
D) ao 13${register[$source]} ;;
M) ab 0x8f
memref 0 ;;
1I) ab 0x6a ${number[$source]} ;;
*segm) if test ${register[$source]} = "0" ;then # ES
ab 0x07
elif test ${register[$source]} = "2" ;then # SS
ab 0x17
elif test ${register[$source]} = "3" ;then # DS
ab 0x1f
elif test ${register[$source]} = "4" ;then # FS
ab 0x0f 0xa1
elif test ${register[$source]} = "5" ;then # GS
ab 0x0f 0xa9
else
echo -e "\n\npush doesn't support
" $modestring " mode. "
fi ;;
*) modenotsupported pull ;;
esac
opnote pull $*
fi
}
pullflags ( ) { # -386 POPF
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel POPF\n
copy top of stack into flags reg, adjusting stack pointer SP
accordingly.\n\n"
else ################
HL
ab 0x9d
opnote pullflags $*
fi
}
pushcore ( ) { # -386 PUSHA
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel PUSHA\n
copy the eight main regs onto the stack, adjusting stack pointer SP
accordingly.
This takes 18 clocks, and pullcore takes 24. That's 5.25 clocks per reg,
or 6 not counting SP. A push/pull of one reg takes 6. (2 for push, 4 for
pull.)
SO, piecemeal IS worth it if you don't need 7 registers.
Also of note, otheroperandsize will make this push all 8 at the opposite
modesize.
\n\n"
else ################
HL
ab 0x60
opnote pushcore $*
fi
}
pushflags ( ) { # -386 PUSHF
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel PUSHF\n
copy FLAGS onto the top of stack, adjusting stack pointer SP
accordingly.\n\n"
else ################
HL
ab 0x9c
opnote pushflags $*
fi
}
push ( ) { # -386 PUSH
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel PUSH\n
copy operand onto top of stack, adjusting stack pointer SP
accordingly. See pushcore also for timings.\n\n"
else ################
HL
parse $*
case $modestring in
D) ao 12${register[$source]} ;;
M) ab 0xff
memref 6 ;;
I) ab 0x68
ac ${number[$source]} ;;
1I) ab 0x6a ${number[$source]} ;;
*segm)
if test ${register[$source]} = "0" ;then # ES
ab 0x06
elif test ${register[$source]} = "1" ;then # CS
ab 0x0e
elif test ${register[$source]} = "2" ;then # SS
ab 0x16
elif test ${register[$source]} = "3" ;then # DS
ab 0x1e
elif test ${register[$source]} = "4" ;then # FS
ab 0x0f 0xa0
elif test ${register[$source]} = "5" ;then # GS
ab 0x0f 0xa8
else
echo -e "\n\npush doesn't support
" $modestring " mode. "
fi ;;
*) modenotsupported push ;;
esac
opnote push $*
fi
}
recieves ( ) { # -x86 INSB
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel INSB\n
Input cells/bytes from port DX into ES:DI. Doing this with \"repeating\"
is too fast for many external devices. Any arg means use bytes,
e.g.
= 0x220 to D
recieves bytes "
else ################
HL
if test -n "$1" ;then
ab 0x6c
else
ab 0x6d
fi
opnote recieves $*
fi
}
sends ( ) { # -x86 OUTS
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel OUTS\n
Send bytes/cells to an x86 IO port address.\n\n"
else ################
HL
if test -n ${size[$source]} ;then
ab 0x6e # if there was a size spec it was byte
else
ab 0x6f
fi
fi
}
submit ( ) { # trap to supervisor -x86 INT
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel INT\n
see also: trapoverflow, debug, Linux version of osimplay\n\n"
else ################
HL
ab 0xcd $1
opnote submit $*
fi
}
returnfar ( ) { # -x86 RET/RETF
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel RET(F) AT&T lret\n
Return from a far (intersegment) call. \n\n"
else ################
HL
ab 0xcb
opnote returnfar $*
fi
}
priviledge ( ) { # -386
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel ARPL\n
Adjust requested priviledge level of r/m16 to not less than RPL of r16\n\n"
else ################
HL
ab 0x63
parse $*
memref ${register[$source]}
opnote priviledge $*
fi
}
loadmachinestatusdual ( ) { # -286
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LMSW\n
286 control register shortcut, 16 bit. Deprecated, but using CR0 explicitly
musses some other register, which is usuallt a problem when switching modes.
\n\n"
else ################
HL
ab 0x0f 1
parse $*
memref 6
opnote loadmachinestatusdual $*
fi
}
# useless.
# # Got milk?
#storemachinestatusdual ( ) { # -286
#if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SMSW\n
#Store machine status dual to EA dual#
#
#Legacy 286 thing. Can save a byte or two on a bootsector.\n\n"
# else ################
# HL
# parse $*
# # check possible
# ab 0x0f 1
# memref 4
# opnote storemachinestatusdual $*
# fi
# }
#
task ( ) { # -x86 LTR
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LTR\n
Load EA dual into task register. \n\n"
else ################
HL
ab 0x0f 0
parse $*
memref 3
opnote task $*
fi
}
trapoverflow ( ) { # -x86
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel INT\n
see also: trapoverflow, debug\n\n"
else ################
HL
ab 0xce
opnote trapoverflow $*
fi
}
rights ( ) { # -x86 LAR
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LAR\n
r16 becomes r/m16 masked by FF00 \n\n"
else ################
HL
ab 0x0f 2
parse $*
memref ${register[$dest]}
opnote rights $*
fi
}
setGDT ( ) { # -386
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LGDT\n
Load pointer at memory operand into Global Descriptor Table Register.
This and setIDT are the only instructions that always interpret an address
as physical, since they are the trunk of the memory protection
scheme.
\n\n"
else ################
HL
parse $*
ab 0x0f 01
if test "$cell" == 2 ; then
ab 0x16
else
ab 0x15
fi
ac ${number[$source]}
opnote setGDT $*
fi
}
setIDT ( ) { # -386
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LIDT\n
Load pointer at memory operand into Interrupt Descriptor Table Register.
This and setGDT are the only instructions that always interpret an address
as physical, since they are the trunk of the memory protection scheme.
\n\n"
else ################
HL
ab 0x0f 0x01
parse $*
if test "$cell" == 2 ; then
ab 0x1e
else
ab 0x1d
fi
ac ${number[$source]}
opnote setIDT $*
fi
}
setLDT ( ) { # -386
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LLDT\n
Load pointer at memory operand into Local Descriptor Table Register.\n\n"
else ################
HL
ab 0x0f 0x00
if test "$cell" == 2 ; then
ab 0x16
else
ab 0x15
fi
parse $*
ac ${number[$source]}
opnote setLDT $*
fi
}
# You loaded it, right?
#savetask ( ) { # -386
#if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel STR\n
# Load task register into EA dual. Ha3sm won't use the 386+ task handling
#facilities. Most 386 unices do I think.\n\n "
# else ################
# HL
# ab 0x0f 0x00
# parse $*
# memref 1
# opnote savetask $*
# fi
# }
#
#
writeable ( ) { # -386
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel VERW\n
Test if current process is allowed to write to given segment.
This and \"readable\" are the same Intel mnemonic, VERW."
else ################
HL
ab 0x0f 0
parse $*
memref 5
opnote writeable $*
fi
}
#()
# OSIMPA COMPEMBLER ]]]]] ()
# oooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooosimpa
# Rick Hohensee www.clienux.com humbubba@smart.net
# jan/feb 2001
# compembler for shasm with COWPP(TM) Rick Hohensee 2001
# In C you hear a lot about "tell the compiler...". Here
# the assembler state is analagous.
# If you want to assemble things piecemeal set $pass by hand.
# an args checker routine would be handy
beam () { # Fill a range of an xray with a jump address
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
Fill in a range in an xray execution array with calls to a word.
beam routine_address <start> <end>
To save myself some hassle, <end> is required, but may be equal to start for
single-wave beams (a wave is one xray section entry). Several beams
between xray and yarx specify the code addresses in an xray execution
array. Each beam spec overwrites previous ones, if any, in the assembly
state of the xray. Thus to set the array default you do the default first
over the whole xray. When yarx happens it asserts the accumulated specs in
the assembly output. A single xray entry is an address and a hike for the
routine tied to that array index. Indexing is from 0, so max end is
size-1. That is, start and end are integers. See yarx h for more. "
else ################
if test "$pass" = "2" ;then
bi=$2
while test $bi -le $3 ;do
# beams[$bi]=$1-$xray_H
beams[$bi]=$1
let bi=$bi+1
done
fi
fi
}
cell () { # name/allot a cell-size storage location
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
e.g.
cell argc
makes room and a label for a global cell-size variable. Doesn't align.
Usually used after \"heap\" (ELF thing).
"
else ###############
for item in $*
do
L $item
allot $cell
done
fi
}
copies () { # plural, range-to-range copy, handles overlaps
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
Assemble code to copy a range of memory at SI of length C*size to DI. The
copy proceeds in whatever direction DF is, usually increasing. If you give
it an argument it copies bytes at a time, rather than cells. C is
decremented per copy, not bytes. SI and DI are also in/decremented
accordingly. DF becomes an issue if source and destination ranges overlap.
copies is use-either. It is the same opcode in use16 or use32. Otherwise,
any argument to copy or copies means copy byte-wise.
"
else
HL
ab 0xf3
opnote repeating
copy $1
fi
}
copy ( ) { # one "copies". @ SI to @ DI -x86 MOVSD
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel MOVSD
Move data @ SI to @ DI. 7 clocks per datum. SI and DI cannot be offset.
SI and DI are incremented or decremented in parallel by the size of a
datum. \"copies\" is usually used with \"repeating\", in which case it
will block-move C data items. See copies. copy takes one optional
argument, anything, to specify that data are in bytes, so I usually do
something cute like
copy byte
where \"of\" is the
argument and the \"bytes\" is in effect a comment.
See also: \"copies\".
copy is interesting because it is an atomic memory-to-memory move, and the
only example of such a move that comes to mind on x86. That is, x86 doesn't
have add mem, mem as a stock addressing mode, as many CISCs do.
The mems get you, of course. copy is 7 clocks. It isn't guaranteed to lock
the bus though, like exchange is, which matters on SMP. I'm also intrigued
because copy uses the x86 Phantom Register they don't give you explicit
access to. maybe it's the icache or something.
copies is use-either. It is the same opcode in use16 or use32. Otherwise,
any argument to copy or copies means copy byte-wise.
\n\n\n"
else ################
HL
if test "$1" ;then
ab 0xa4 # if there was any argument copy bytes
else
ab 0xa5
fi
opnote copy $*
fi
}
clump () { # C struct() kinda, data associations namer
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
clump creates a set of related offset names. This sort of thing is
popular for associating arbitrary data items into groups. The data
associations can themselves be useful information. Data items in a clump
are defined by thier spacing, and thus have implied sizes.
clump clumptypename item0 <size> item1 <size> ...
will make \$clumptypename_item0 \$clumptypename_item1 and so on be the
appropriate offsets from \$clumptypename in the assembler state.
\$clumptypename itself has a value of zero. A clumptypename_size is also
created that records the overall size of this type of clump. Sizes are in
bytes. Those offset values can then be used during assembly to instantiate
and perhaps initialize clumps of that type, or as address offsets at
runtime, including offsets on the return stack.
There's nothing yet to automate instantiating a clump. All I have is the
names of the field offsets of the clump type. Let's say you have a defined
PIC clump. You can make one ala...
L PIC1
allot PIC_size
And set a fields ala
= 0 to \$((PIC1+PIC_counter))
= 300 to \$((PIC1+PIC_count))
et cetera. It's about 5% of C structs. Personnally I think it's 80% of the
useful aspects of C structs. How to nest them is left as an excercise to
the reader.
"
else ###############
cname=$1
let os=0
let $cname=0
shift
while test "$1" ;do
let ${cname}_$1=$os
let os=os+$2
shift ; shift
done
let ${cname}_size=$os
fi
}
leave () { # return from the current reentrant procedure
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
This is not 386+ LEAVE.
A leave is a termination point of an osimplay reentrant routine. There
may be 0, 1 or more of these in a procedure. The current routine remains
current for assembly until the next routine definition starts, so there
may be several leaves to an entrance. entrance/enter/leave routines are
reentrant if you use the 15 stack familial variables for parameters and
return values.
"
else ###############
HL
ab $((0xc2))
ad $((15*$cell))
opnote leave
fi
}
fill () { # plural, copies A across range @ DI -x86 STOSD
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel STOSD\n
The fill opcode seems to only be useful with \"repeating\", and so that's
what osimpa \"fill\" does. osimpa \"fill\" is therfor a 2-byte composite
instruction with the repeating prefix, and I call this class of things
plurals. It wants the fill value in A, the fill range start in DI, the
count of bytes or cells in C, and follows the direction flag, which is
normally increasing, across the fill range. Any argument to fill is taken
as a spec that it fill by bytes rather than cells.
# cell-wise 0-fill a buffer
increasing
zero A
= \$buffer to DI
= \$buffersize to C
downshift C
downshift C
fill
"
else ################
HL
parse $*
if test "$1" ;then
ab 0xf3 0xaa
else
ab 0xf3 0xab
fi
opnote FILL $*
fi
}
flag () { # assert the zero/sign flags of a register's value
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
Convenience wird to ANDtest the argument register with itself to generate
the conditionals flags."
else ###############
ANDtest $1 with $1
fi
}
enter () { # how you invoke an osimplay reentrant procedure
if test "$1" = "h" ; then
echo -e "\n\t\t\t\t\t
osimpa frame-allocating subroutine call. Hikes SP. This is NOT x86 ENTER,
which is "frame" in osimplay. osimplay reentrant routines are slighly
composite (macro'ed) use of the x86
return-from-subroutine-with-literal-amount-to-drop-from-stack. This is how
you invoke a routine with stack space for locals. enter implements 16-cell
stack frames counting the return address, giving 15 \"familial
variables\".
enter my_reentrant_routine
The space provided for reentrance, i.e. instance data besides the return
addy, is hardwired to 15 cells of return stack space in all cases, since
it's one extra instruction total that way. Gcc misses the functionality of
osimplay leave, BTW, last I looked. Typical gcc 386 assembly output is
mostly references to offsets of SP, which is what osimplay familial
variables allow you to do by hand.
See also: locals.
"
else ###############
- 60 to SP
call $1
fi
}
lsoepot ( ) { # figure (max(2^N) <|= arg) for masks
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
Largest Smaller Or Equal Power Of Two
This is a standalone calculation convenience, basically. Return the
largest power of two that is equal to or less than \$1, the argument. Used
to generate strand (ring buffer, e.g.) mask values. Interactively, have a
look-see at
lsoepot 83274
et cetera. As with most of these, you can
put it in compembled code with grave (\`) accents.
"
else ###############
placebit=1
while test "$placebit" -le "$1" ;do
previous=$placebit
placebit=$(($placebit*2))
done
echo $previous ;fi
}
maskbyte () { # mask arg down to a byte
if test "$1" = "h" ; then echo -e "\n\t\t\t\t
Assembles an instruction to AND the argument with 255, masking it to one
meaningful byte and high-order zeros."
else #################
AND 255 to $1
fi
}
maskdual () { # mask arg down to it's lowest-significance 16 bits
if test "$1" = "h" ; then echo -e "\n\t\t\t\t
Assembles an instruction to AND the argument with 16 on-bits, masking it to
two live bytes and high-order zeros."
else #################
AND $((0xffff)) to $1
fi
}
match () { # plural, range-range compare, zero flag=match
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
\"match\" is a plural that compembles inline code to bitwise compare
memory ranges at SI and DI, for all of C bytes of length. D is also
clobbered. \"match\" takes no arguments. If the memory ranges are exactly
the same for C bytes then the zero flag will be true. Follow it ala
# SI, DI and C are set up by now
match
when zero whatever # successful match action
stuff # match failed
L whatever
tother stuff
\"match\" uses bytewise comparison, because I don't see a way to cell-wise
compare on bytewise increments. (Later x86en have such?) No matter, that's
a small win anyhooo.
"
else ################
HL
ab 0xf3 # rep
opnote repeat while C \& ZF
compares of bytes
fi
}
max () { # max arg1 arg2 leaves greater in arg2
if test "$1" = "h" ;then
echo -e "\n\t\t\t\t
Assembles 3 inline instructions to leave the greater of two args in the
second arg using a conditional branch.
min arg1 arg2 uniquelabel
leaves the larger value of arg1 or arg2 in arg2.
arg1 is unchanged. The
second arg can be a register or an address. The first may be a literal
also. As with a single instruction, one of the args must not be an
address. That is, the x86 doesn't do a memref-to-memref addressing mode,
and osimplay min doesn't either. You must use quotes to delimit first and
second args if one arg is more than one token, e.g...
min \"@ 3000 B\" someplace
MIN and MAX are beloved by Forth programmers. In osimplay the result will
be in the dest (second) argument. The necessity of the label argument,
which YOU have to make unique, and the lack of the to/from makes
implementation gobs easier.
"
else ##################
-test $2 with $1
when short sign $3
= $1 to $2
L $3
fi
}
min () { # min arg1 arg2 leaves lesser in arg2
if test "$1" = "h" ;then
echo -e "\n\t\t\t\t
Assembles 3 inline instructions to leave the lesser of two args in the
second arg.
min A B uniquelabel
leaves the smaller value of A or B in B. A
is unchanged. The second arg can be a register or an address. The first
may be a literal also. As with a single instruction, one of the args must
not be an address. That is, the x86 doesn't do a memref-to-memref
addressing mode, and osimplay min doesn't either. You must use quotes to
delimit first and second args if one arg is more than one token, e.g...
min \"@ 3000 B\" A
MIN and MAX are beloved by Forth programmers. In osimplay the result will
be in the dest (second) argument. The necessity of the label argument,
which YOU have to make unique, and the lack of the to/from makes
implementation gobs easier.
"
else ##################
-test $1 with $2
when short sign $3
= $1 to $2
L $3
fi
}
numbering () { # C enum, but not strictly constants and no commas
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
Assign sequential integer values to a series of names in assembly state.
This would have been C enum or BCPL MANIFEST if I had bothered with the
Bash \"constant\" type-declaration schtuff. You want em constant, don't
change em. Interactively, have a go with...
numbering zero one 16 sixteen seventeen eighteen
echo \$zero \$one \$eighteen
As in C, the sequence can be coerced to a new starting count, which "16"
does in the above, except that you can't reset the sequence to 0 because
of a number/string parsing hack. Just use another numbering for another 0.
Or a shell variable declaration :o) \"numbering\" strikes me as kinda
dumb, but it was easy to implement, and C has it. The Plan 9 from Bell
Labs code has a lot of use of enum, I guess because it's a lot less typing
than using cpp for lots of constants. It also supposedly gives the
compiler more to munch on, but that doesn't pertain to osimplay.
"
else ###############
let ecount=0
for arg in $* ;do
if test $(($arg)) -ne 0 ;then
let ecount=$(($arg))
else
let $arg=$ecount # assignment to $tring. :o)
let ecount=$ecount+1
fi
done ;fi;}
quad () { # name/allot a 4-byte storage location
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
e.g.
quad argc
would make room and a label named argc for a
global named 4-byte storage location. It doesn't align. Works good after
\"heap\"."
else ###############
L $1
allot 4
fi
}
range () { # name/allot some count-cell prefixed memory
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
A range is a count-cell-prefixed data allocation. A strand is similar,
but with more metadata. range assembles the size prefix cell, sets a
label, and allots to size. e.g.
range scantable \$((256*10))
\"text\" is also similar but takes an initial shell here-document of
data and figures out the count from that. Also see strand h. The label the
range's name represents is byte 0 of the net data. The count is one cell
below that in memory.
"
else ###############
quads $2
echo " range" >> $listing
L $1
allot $2 ;fi
}
ring () { # ring-wise increment a strand's index
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
This is the basic action of a ring buffer. Given a strand of pointers,
say, called objectring,
ring objectring cell
will point you at the
ring-wise next object. In more detail, ring is a bounded ring-wise
increment of the index into a strand array. The default increment is byte.
A \"cell\" or \"ply\" token causes \$cell or the strand's defined ply size
to be the increment. Bounding is done with the strand's defined mask. The
byte and cell increment versions are 3 instructions, ply version is 4.
See strand h also. ring clobbers A. ring keeps the index within the size
bounds of the strand, so don't forget to add it to the strand's address
when you reference the actual data.
"
else
if ! test "$2" ; then # byte incr.
1+ @ $(($1-($cell*4))) # index + 1
= @ $(($1-($cell*3))) to A # mask
AND A to @ $(($1-($cell*4)))
else
if test "$2" = "cell" ; then # cell incr.
+ $cell to @ $(($1-($cell*4)))
= @ $(($1-($cell*3))) to A
AND A to @ $(($1-($cell*4)))
else # ply incr.
if ! test "$2" = "ply" ; then
echo "Creative ring increment spec, assuming ply."
fi
= @ $(($1-($cell*2))) to A # ply
+ @ $(($1-($cell*4))) to A
AND @ $(($1-($cell*3))) to A
= A to @ $(($1-($cell*4)))
fi
fi
fi
}
ringstore () { # ringstore ring reg/lit [arg3 means cell] !A
= @ $(($1-($cell*4))) to A # index
if test "$3" ; then # cell store
= $2 to @ $1 A
else
= byte $2 to @ $1 A
fi
}
scale () { Forth */ with a shift instead of a divide
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
osimplay scaling composite. This 2-instruction composite of a double
multiply followed by a double downshift allows scaling a 32 bit number by
a 32:32 scaling ratio without any unnecessary information loss due to
truncation. The final result will have as many meaningful bits as the
factor and arguments allow. Using a shift instead of a divide is for
speed.
scale D 18
multiplies A by D, keeping a double intermediate
result in D:A. It subsequently downshifts that intermediate result by 18
bit places, leaving the final result in A. Another form is
scale D
which implicitly gets the downshift value from the
C register. Downshifts are masked to 5-bit values, 0 to 31.
This is Forth */ or BCPL MULDIV, but using a shift instead of a divide for
even better speed. The resolution limitation of bitshifts is ameliorated
by the fact that a single is 32 bits, and that the multiply is full
resolution, so most odd ratios, like the twelfth root of 2 for example,
1.0925something:1, on which pitches of musical notes are based, can be
closely approximated. A downshift is about 3 clocks. A divide is more like
43. Multiplies are between 9 and 40 clocks worst case. Worst case is based
on the highest significant bit of the multiplier. Divides are always worst
case. Worst case is about 43 CPU cycles overall, plus memory accesses,
which is just the optional immediate byte.
This means you pay 10 clocks plus just the precision you use. For example,
= 5 to D
scale D 2
should be about 18 clocks total. Not bad. The
multiply by five is only 3 significant bits. This also leaves us at about
the magnitude we came in with, so precision of the operation and magnitude
of the result are independant.
The wide downshift opcode is a pain to assemble. I don't think GNU Gas
does it right. I assemble it by hand here, and limit the possible shifted
registers to D:A, which meshes nicely with multiply.
To reiterate, multiply A by arg 1, intermediate result in D:A. Downshift
that by C, or by immediate value. Final result in A.
"
else
multiply $1
HL
if ! test "$2" ; then
ab 0x0f 0xad 0320 # wide downshift D:A by C
else
ab 0x0f 0xac 0320 $2 # wide downshift D:A by byte
fi
opnote wide downshift
fi
}
scan () { # plural, compare A to memory range @ DI until hit/miss
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SCASB\n
Used to compare all of a range of memory against A. Quits when it hits.
Any argument to \"scan\" is taken as \"use cells\". Otherwise it does a
bytewise scan. Bytewise is probably much more widely useful because the
cellwise form also increments cellwise, so it only works properly for
cell-resolution data. In other words, if you scan for a quad you'll only
hit on the quad-aligned occurances of you match quad. The comparison is
between A and the current @ DI, which gets incremented as per argument
size. osimpa \"scan\" is a \`plural', and uses the 386
repeat-while-non-zero prefix. When \"scan\" finishes repeating, if ZF is
1, i.e. if zero=true, then DI is the byte address of the byte or cell
FOLLOWING the match. If the direction flag is \"increasing\" that's how
\"scan\" proceeds, i.e. toward higher addresses.
C is decremented by ones. DI is decremented by the operand size, which
defaults to byte. \"scan\" will stop on a hit, or continue until the count
in C is 0. The count is per item, which may be cells or bytes.
Example use:
# assuming A, DI and C are set up already
# keep DF in mind also
scan
when zero got_a_hit
\"scan\" is an inline composite, i.e. a macro, but it's only 2 bytes."
else ################
HL
if ! test "$1" ;then
ab 0xf2 0xae
else
ab 0xf2 0xaf
fi
opnote SCAN $*
fi
}
strand () { # name/allot a general array and 8 cells of metadata
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
A strand is a count-cell-prefixed string, a byte array, a sized array, a
masked ring, a multi-reader ring buffer, a dessert topping AND a
floorwax. e.g.
strand <sname> <bytes> [<ply> <other stuff>]
The idea of strands is take a data allocation, add metadata as negative
offsets from the 0-byte address of the net data, keep the metadata in a
known consistent order for all types and subtypes, and you have an
interlocking set of datatypes that can all use all the ops for thier own
type and any simpler type. Sortof.
The data format of a strand is currently...
lower addresses
cell undecided
cell seen
cell west
cell east
cell index
cell mask
cell ply
cell size in bytes
<----- nominal address
DATA
DATADATA
DATADATADATA...
higher addresses
ZO, a full strand definition might be...
strand thangipoo 1024 4 1023 128 \$otherstrand \$tuthastrand
but this is a first cut, the sequence may change yet."
else ###############
# eval ${1}_ply=$3
let mask=`lsoepot $2`-1
# eval let ${1}_mask=`lsoepot $2`-1
HL
aq 0
opnote strand $1
HL
aq 0
HL
aq 0
HL
aq 0
HL
aq 0
opnote index
HL
aq $mask
opnote mask
HL
if test "$3" ; then
aq $3
else
aq 1
fi
opnote ply
HL
# eval ${1}_bytes=$2 # eh?
aq $2
opnote size
L $1
allot $2 ;fi
}
sum () { # plural, additive checksum
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
Arithmetic cell checksum. Add Count contiguous cells to B. Sets B to 0
first. sum is an inlining of 3 looping instructions. A is clobbered also."
else ###############
opnote SUM
woobie=$RANDOM
zero B
L sum$woobie
fetches
+ A to B
when C-1 sum$woobie
opnote .
fi
}
tag () { # tag <string> ASCII bytes and zeros in a cell
if test "$1" = "h" ; then echo -e "\n\t\t\t\t
tag is a cellsize character(s) literal. tag blahzay on a machine
where cell=4 labels a cell as blahzay, allocates the cell, and initializes
it with the ASCII values of b, l, a and h. The ASCII value of b will be in
the byte within the cell with the lowest address. If the given string has
less characters than a cell has bytes then the cell is padded out with
zero-bytes.
Since the 386 is little-endian, tag a will have a numeric value of
0x61, but tags aren't really for arithmatic. They are for unique test
arguments. Tags are handy for simple and thus fast commandsets, case
switching, inner interpreters and so on. You see a lot of C case switches
based on 'c' character literals. tag is just as fast, more general, and
more hackish. I was doing this sort of thing a lot in the editor I was
writing, by hand, and I see a lot of the same basic thing in Menuet, so it
seems to be worth having an actual directive for. Don't sweat the
endianness non-issue.
When defining tags, shell quoting applies. tag uses osimplay asciibyte
in a loop.
"
else
L $1
HL
tagbyte=0
tagfeed=${#1}
tmore=yes
while test "$tmore" ;do
curchar=${1:$tagbyte:1}
if test "$curchar" ;then
ab `asciibyte $curchar`
else
ab 0
fi
let tagbyte=$tagbyte+1
if test $tagbyte -eq $cell ;then
tmore=
fi
done
opnote tag
fi
}
text () { # name/allot some text
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
Assemble text literally from a shell here-document. If a name is given
assemble a count-cell prefix and label the following byte with the given nam
e.
That is, the label is at the data, not the length cell prefix.
e.g.
text ernie <<!
stuff
other stuff
!
"
else ###############
IFS=
let textinstance=$textinstance+1
let temp=0
if test $1 ;then
HL
aq ${textlength[$textinstance]}
if test $pass = 2 ; then
echo -n " TEXT length" >> $listing
fi
L $1
echo >> $listing
fi
while read line ;do
H=$H+1
ascii $line
if test $pass = 2 ; then
echo -en "\012" >> $output
fi
let temp=$temp+${#line}+1
done
textlength[$textinstance]=$temp
IFS="
"
fi
}
cells="*4"
# frame (familial) variables. Look at the assembly 386 gcc produces.
# GOBS of this.
sa=" @ 4 SP "
sb=" 8 + SP "
sc=" 12 + SP "
sd=" 16 + SP "
se=" 20 + SP "
sf=" 24 + SP "
sg=" 28 + SP "
sh=" 32 + SP "
si=" 36 + SP "
sj=" 40 + SP "
sk=" 44 + SP "
sl=" 48 + SP "
sm=" 52 + SP "
sn=" 56 + SP "
so=" 60 + SP "
# 64
pa="68 + SP"
pb="72 + SP"
pc="76 + SP"
pd="80 + SP"
pe="84 + SP"
pf="88 + SP"
pg="92 + SP"
ph="96 + SP"
pi="100 + SP"
pj="104 + SP"
pk="108 + SP"
pl="112 + SP"
pm="116 + SP"
pn="120 + SP"
po="124 + SP"
west ( ) { # get a strand's pointing-west pointer
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
Given the 0-offset address of a strand's data in I, get the \"west\"
pointer into A."
else #################
= -$(($cell*5)) I to A
fi
}
xjump () { # indexed jump into an xray execution array
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
Jump on an xray beam. i.e. use an execution array. Example...
xjump \$dave B
where dave is an existing xray, compembles a jump through the xray beam at
the cell offset in the B register in the execution array (jump table)
\"dave\". Masking to the size of the array and similar prudent activities
are your problem. Only the register given gets clobblered. The cost of an
xjump is 11+ clocks. With a tree-of-conditionals switch/case your single
best case is two clocks to get into a condition, but then you also have to
get out. With an execution array best case and worst case are the same.
There appear to be two ways to use an xray. You can make the beams osimpa
\"define\" routines, in which case the beam will return to the caller's
caller, or you can hand-roll a bunch of branches for the beams.
xray currently only works with cell=4 .
"
else ############################
= $1 A *2^ 2 to A
ab 0xff 0xe0
fi
}
xray () { # name an execution array for beam, yarx and xjump
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
xray is the first of three words used to assemble an execution array.
xrays are named jump tables and the table items are subroutines, so they
return to the xray's \"caller\". xray sets the assembly state, does some
checks, and waits for beams. beam sets a range of jump targets to a
routine's address. Beam specs clobber earlier beam specs they overlap, so
you set the default ones first and overwrite them with the special cases.
When you have all your beams lined up you use yarx (xray backwards) and
yarx actually compembles the jump table based on the accumulated compembly
state.
You give xray a name and a table size in items. Possible code...
xray asciithang 256
should work. I'll put a longer example in yarx h ."
else #####################
L $1
xraysize=$2
xray_H=$H
fi
}
xsum () { # plural, XOR-ing checksum
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
XOR cell checksum. XOR Count contiguous cells into B. Sets B to 0 first.
sum is an inlining of 3 looping instructions. A is clobbered also. It's
sensitive to the direction flag, which defaults to \"increasing\" in
osimplay."
else ###############
opnote XSUM
xwoobie=$RANDOM
zero B
L xsum$xwoobie
fetches
XOR A to B
when C-1 xsum$xwoobie
opnote .
fi
}
yarx () { # finish compembling an xray and it's beams
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
Complete the compembling of an xray. Assuming a memref like...
xray specs 128
beam glyph 33 127 # some no-op or something
beam control 0 32
beam delete 127 127
yarx
The example, BTW,
is a rudimentry ASCII handler. When the above gets to yarx tH's a
complete xray specified in assembler state and yarx finishes up, doing the
output of actual bytes. Given the above yarx will create a jump table with
jumps to the control routine if A is between 0 and 32 when the now-named
specs xray is invoked, the delete routine if A is 127, and the glyph
routine otherwise. glyph could have been 0 127 with the same end result.
The clobberableness factor might be handy for complex xray layouts. "
else ######################
yi=0
while test $yi -lt $xraysize ;do
HL
ac ${beams[$yi]}
let yi=$yi+1
done
fi
}
zero () { # simple convenience to set (a) register(s) to 0
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t
Use XOR to set a list of registers to zero. More convenience
than elegance. Well, improves readability too.
zero A C D
"
else ######################
for reg in $* ;do
XOR $reg with $reg
done
fi
}
locals () { # help-only word on osimplay familial frame variables
echo "
osimplay enter and leave are nothing like the 386 instructions with the
INTeL mnemonics ENTER and LEAVE. They are much simpler procedures using
the 386 return-from-subroutine-with-drop-stack-frame. Stack |