# shasm to osimpa to osimplay
# OSes as simple as childsplay

								#()
# ASSEMBLER DIRECTIVES/HELPERS					 ()
						# config
machine=i386
executable=yes
output=output			# just defaults. The linker will change
				#  these. This reduces replication of
				#  base  stuff.
listing=listing
declare -ai textlength
let loadaddress=0
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
let suffixes=1

suffixout ( ) { 				# retrofitted into herelist
	let suffixcounter=$suffixes
	totalsuffix=
	while ! test "$suffixcounter" = "1"
		do
		totalsuffix=$totalsuffix${suffix[$suffixcounter]}
		let suffixcounter=$suffixcounter-1
	done
	echo -n "	##" $totalsuffix >> $listing
}


addtosuffix ( ) { # takes a string argument to prefix the current prefix with
	let suffixes=$suffixes+1
	suffix[$suffixes]=$1
}


dropsuffix ( ) { # reverse the above
	let suffixes=$suffixes-1
}


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})

declare -i charcount
charcount=0

opnote 		( ) { # 	internal			=shasm
	if test "$pass" = "2"		;then
		let charcount=26-$charcount"%"26
		while test $charcount -ne 0
		do
			let charcount=$charcount-1
			echo -ne " " 			>> $listing
		done
			echo -n $* 			>> $listing
			echo -ne "	"	>> $listing
			suffixout
	fi
						}


ascii		( ) { #		internal 			=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."
							else ###############
	if test "$pass" = "1" 			;then
		let here=$here+${#1}
	else
		echo  -en   $1		>> $output
		echo 	$1  "ASCII "  		>> $listing
		let here=$here+${#1}
	fi
							fi
							}


ab		() { # assemble bytes.  pass-sensitive.		=shasm
if test "$pass" = "2"	;then
	bytes $*
else
	let here=$here+$#
				fi
				}


ao 		() { # assemble one octal string as a byte	=shasm
if test "$pass" = "2"	;then
	echo -en \\$1			>> $output
	echo -en $1" "			>> $listing
	let here=$here+1
	charcount=$charcount+4
else
	let here=$here+1
				fi
				}


ad 		() { # assemble duals.   pass-sensitive.	=shasm
if test "$pass" = "2"	;then
	duals $*
else
	let here=$here+$#*2
				fi
				}


aq		() { # assemble quads. pass-sensitive quads	=shasm
if test "$pass" = "2"	;then
	quads $*
else
	let here=$here+$#*4
				fi
				}


ac		() { # assemble cells. pass-cell-sensitive.	=shasm
if test "$pass" = "2"	 ;then
	if test "$cell" = "2" ;then
		duals $*
	else
		quads $*
	fi
else
	let here=$here+$#*$cell
				fi
				}


align		() { # like e.g. .align				=shasm
if test "$1" = "h" ; then  echo -e  "\n\n\n\n
Don't use this until \$here has been bumped above 0."
						else ####################
	if test "$here"	&& test "$here" != "0"	;then
		let	lop=$here%$1
		let	pad=$1-$lop
		if test $lop -ne 0		;then
			allot $pad
		fi
	else
		echo "For simplicity, align doesn't work until
		\$here 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 \$here is 0x200 then after 	allot 0x10    \$here is 0x210. \$here
is the next un-assembled file address.
\n\n"
						else ############

	herelist
	if test "$pass" = "2" ;then
		echo -n "00... "			>> $listing
	fi
	let tempint=$1+$here
	if test $pass -eq 1				;then
		let here=$1+$here
	else	###### pass 2
	echo -en "\t\t\tALLOT $1">>$listing
		while test $here -lt $(($tempint))	;do
		if test $(($tempint-$here>>8)) -eq 0 ;then
			# could loop here too
			if test "$allocated" = "yes" ;then
				echo -en "\000" >> $output
			fi
			let here=$here+1
		else
			page
			let here=$here+256
		fi
		done
		let here=$here-1
#		herelist
#		echo  "00   "				>> $listing
		let here=$here+1
	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
text)		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
					}	


bases           () { # print # in decimal, binary, octal, hex	=shasm
if test $# -lt 1                ;then
echo "
gimme some numbers to convert to binary, octal and hex.
"
else #########
for num in $*                   ;do
echo -en $num"  "
binaryout $num
echo -en "    "
octalout $num
echo -en "    "
hexout $num
echo
done
fi
}


binary		() { # binary <strings of ones and zeros>	=shasm
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
 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 here."
						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
					}


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. 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
	here=$here+$2
else				# Pass 2 is on us. Pass 1 was L.
	let relativ=$1-$here-$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
	let here="$here+$#"
	for a in $*	;do
		if test $(($a)) -gt 255 ; then
			echo " here " $here ":  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
		charcount=$charcount+3
	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
let here="$here+$#*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
	charcount=$charcount+6
done
					}


quads		( ) { #		internal			=shasm
let here="$here+$#*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
	charcount=$charcount+12
done
						}


hexout          ( ) { # print a hex representation of a number	=shasm
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 $here
if test "$1" ; then
	echo -en ${hex[$1>>24&0xff]}		>>	$listing
	echo -en ${hex[$1>>16&0xff]}		>>	$listing
	echo -en ${hex[$1>>8&0xff]}		>>	$listing
	echo -en ${hex[$1&0xff]}"  "		>>	$listing
else
	echo "no arg for hexquad. set \$here?"
						fi
						}


herelist	( ) { #		internal			=shasm
if test "$pass" = "2"	;then
	echo >> $listing
	hexquad $here
						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-\$here)) 
							does the right
thing, so use that.
\n\n"
						else ############

	herelist
	if test "$pass" = "2" ;then
		echo -n "00 "				>> $listing
	fi
	if test $pass -eq 1				;then
		let here=$1+$here
	else	###### pass 2
		echo -e "\t\t\tfill to just before $1">>$listing
		echo -e "  \||/    00...">>$listing
		while test $here -lt $1				;do
			if test $(($1-$here>>8)) -eq 0 ;then
				if test "$allocated" = "yes" ;then
					echo -en "\000" >> $output
				fi
				let here=$here+1
			else
					page
				let here=$here+256
			fi
		done
		let here=$here-1
		herelist
		echo  "00   "				>> $listing
		let here=$here+1
	fi
					fi
					}


L 		() { # label, set a branch bla at $here		=shasm
if test "$1" = "h" 	;then
echo "\n\n\n

	L mylabel

sets \$mylabel to \$here.
"
elif #########################################

	test "$pass" = "1"	;then
	eval $1=$here
else
	herelist
	echo -n " 		(O) "$1			>> $listing
					fi
					}


octalout        ( ) { # print a number as an octal string
let val=$1
bstring=
for shift in 30 27 24 21 18 15 12 9 6 3 0       ;do
        let nybble=$val\>\>$shift\&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
				}


state		() { # write out entire current assembly state	 =shasm
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
 This is provided for debugging and so on due to an inconvenience osimpa
has in common with unix \"make\". The global state of the build is
ephemeral. To catch it you can put \"state\" in a sourcefile to snag the
assembly state in a ./state file and you'll record the state at that point
in time. Currently state happens in pass 2."
						else #####################
	
echo " 
This is an osimpa assembly state dump.

" > state 
	set >> state
			fi
			}



##     80386 shtuff		 see Intel's 386INTEL.TXT etc.
					#   there's a webpage of the above
cell="4"				# global, 4 or 2. 386 or real

declare -i registers


size		( ) { #		internal			=shasm
if test $1 = 1 ;then
size[$side]=1
fi
		}


TYPE		( ) { # 	internal			=shasm
mode[$side]=$1
		}


octacode	( ) { # 	internal			=shasm

register[$side*2+${registers[$side]}]=$1
registers[$side]=${registers[$side]}+1
if test ${registers[$side]} -gt 1
then
	memside=$side
	MEM=4					# 	and MEM
			fi
			}


			# modestring builders
segment		( ) { #		internal			=shasm
octacode $1
size 2
TYPE segm
		}


specialC	( ) { #		internal			=shasm
octacode $1
TYPE speC
		}


specialD	( ) { #		internal			=shasm
octacode $1
TYPE speD
		}


specialT	( ) { #		internal			=shasm
octacode $1
TYPE speT
		}


small		( ) { #		internal			=shasm
octacode $1
TYPE dire
size 1
		}


dualreg 	( ) { #		internal			=shasm
octacode $1
TYPE dire
size 2
		}


parse		( ) { #		internal			=shasm
# any value for $addressmode means real mode

if test "$addressmode"		;then
	rmodeparse $*	
else
	pmodeparse $*
fi
				}


initparse	( ) { #		internal		=shasm
	## initial defaults of per-oper state
source=0
register[0]=""	register[1]=""	register[2]=""	register[3]=""
memside=""						# memory side
MEM=""
mode[0]=""		mode[1]=""
size[0]=		size[1]=		shift="0"
registers[0]="0"
registers[1]="0"
index=""
let indexi=9  	# Nein! default is no index
number[0]=""		number[1]=""
number[2]=""		number[3]=""
side=0			# left=0, right=1.
let sides=1
SC=0
BAS="5"
mo=""
			}


tokencase	( ) { # 	internal		=shasm
case $arg in
to) 	sides=2 ; source=0 ; 	dest=1 ; side=1 	;;
A) octacode "0" ;;	C) octacode "1" ;;
D) octacode "2" ;;	B) octacode "3" ;;
SP) octacode "4" ;;	BP) octacode "5" ;;
+|@)	mode[$side]="M"	; memside=$side  ;;
SI) octacode "6" ;;	DI) octacode "7" ;;
I) octacode "7" ;;
byte) size "1" ;;	dual) size "c" ;;	quad) size "c" ;;
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 indexi=$side*2+${registers[$side]}-1
	index=${register[$indexi]}
	memside=$side				# memory side
	wasshifter="yes"
	mode[$side]="M"		;;
with) 	sides=2 ; source=0 ; 	dest=1 ; side=1 	;;
from)	sides=2 ; source=1 ; 	dest=0 ; side=1		;;
*)     number[$side]=$arg		;;

  esac			   # end tokens case-switch
}


buildmode	( ) { #		internal			=shasm
			# 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
				}


pmodeparse	( ) { #		internal			=shasm
 if test "$1" = "h" ; then  echo -e  "\n HELP STUFF "
 else
initparse
  for arg in $*
  do
   if test "$wasshifter" = "yes"	# preempt the rest if last was *2^
	then
		wasshifter="no"
		let shift=$arg
		SC=$arg
	else
		# do  type pmodeparse   for a nicely indented display

	tokencase $*
 fi			  # end *2^ short-circuit
done			 # end args loop, resume reasonable indentation.
buildmode $*
			fi
			}	# end of pmode parse  #######


rmodeparse	( ) { #		internal			=shasm
initparse
  for arg in $*
  do
	tokencase $*
done		
	buildmode	$*
				}	# end of rmode parse  #######


memneck		( ) { #		internal			=shasm
if test "$1" = "h" ; then  echo -e  "\n\n\n\n
internal 
Does the ModR/M - SIB thing. "
					else ################
if test "$addressmode"				;then
	rmodememneck $* 	# no SIB
else
	pmodememneck $*
fi
			fi
			}


pmodememneck	( ) { #		internal			=shasm
if test "$1" = "h" ; then  echo -e  "\n\n\n\n

takes the off register/code as it's argument, which goes in REG. using MO
REG MEM and SC IND BAS for ModR/M and SIB field names."
					else ##################
REG=$1	nobase=5	MEM=4
	if test "${number[$memside]}" 				;then
		if test ${number[$memside]} -lt 0  		;then
			disptest=$((0-${number[$memside]}))
		else
			disptest=${number[$memside]}
		fi
		if test $disptest -lt 128			;then
			MO=1
		else
			MO=2
		fi
	else
			MO=0
	fi
	if test $indexi -eq 9 					;then
	# no scaled indexing
		case ${registers[$memside]} in
			0)	# just displacement
				MO=0
				MEM="5"		;;
			1)	# base only
				MEM=${register[$memside*2]}
						# SIB if SP
				if test $MEM = "4" ; then
					SC=0
					BAS=4
					index=4
				#	number[$memside]=0
				fi			;;
			2)	index=${register[$memside*2+1]}
				# arbitrary without *2^
				BAS=${register[$memside*2]}		;;
			*)	echo "	
			The protected mode parser got a memside register
			count greater than 2, which is impossible. "
					;;
								esac
	else
	# __indexed__, had a  *2^	 9 ist NEIN!
		if test ${registers[$memside]} -eq 1  		;then
			# index, no base.
			BAS=$nobase
			index=${register[$indexi]}
			MO=0
		else
			# 2 regs, memside
			BAS=${register[$indexi^1]}
			# base is NOT of index
			index=${register[$indexi]}
		fi
	fi
## assemble modR/M
ao $MO$REG$MEM
# SIB
if test "$MEM" = "4" -a $MO -ne 3				;then
	ao $SC$index$BAS
fi
# displacement
if test "$MO" = "1" 						;then
	ab ${number[$memside]}
fi
if test "$MO" = "2" 						;then
	ac ${number[$memside]}
fi
if test "$MO" = "0" -a $MEM = "5"				;then
        ac ${number[$memside]}
fi
					fi
					} # end pmodememref


rmodememneck	( ) { #		internal			=shasm
# 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*2]}${register[$memside*2+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 $here`  ": \
		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*2]}${register[$memside*2+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 $here`  ": \
		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*2]}${register[$memside*2+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 $here`  ": \
		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   	# # displacement	byte
	ab ${number[$memside]}
fi
if test "$mo" = "2" 	;then   	# 			cell
	ac ${number[$memside]}
fi					# none

if test "$mo" = "0" && test $MEM = 6	;then		# displ. only
		ac ${number[$memside]}
fi

				}	#######################


modenotsupported ( ) { #	internal
      	echo -e `hexout $here`": osimpa $1 doesn't support " $modestring " mode."
}


								#( )
#_______________x86_x86x86x86x86x86x86x86x86x86_________ 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 ################
        herelist
	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 ################
        herelist
	ab 0x67
	opnote otheraddresssize $*
					fi
					}

								#()
# x86 general instructions					()


=		() { # Copy. 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.  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 ################
        herelist
	parse $*
	case $modestring in

		ID)		ao 27${register[$dest*2]}
				ac ${number[$source]}			;;
									
		DD)		ab 0x89
				ao 3${register[0]}${register[2]}	;;

		MD)		ab 0x8b
				memneck ${register[2]}			;;

		DM)		ab 0x89
				memneck ${register[$source*2]}		;;

		IM)		ab 0xc7
				memneck 0
				ac ${number[$source]}			;;

		1ID)		ao 26${register[$dest*2]}
				ab ${number[$source]}			;;
									
		1DD)		ab 0x88
				ao 3${register[0]}${register[2]}	;;

		1MD)		ab 0x8a
				memneck ${register[2]}			;;

		1DM)		ab 0x88
				memneck ${register[$source*2]}		;;

		1IM)		ab 0xc6
				memneck 0
				ab ${number[$source]}			;;

		Dsegm)		ab 0x8e		# typo in 386INTEL.TXT
				ao 3${register[$dest*2]}${register[source*2]};;

		segmD)		ab 0x8c
				ao 3${register[$source*2]}${register[dest*2]};;

		*speC)		ab 0x0f 0x22
				ao 3${register[$dest*2]}${register[source*2]};;

		*speD)		ab 0x0f 0x23
				ao 3${register[$source*2]}${register[source*2]};;

		*speT)		ab 0x0f 0x26
				ao 3${register[$dest*2]}${register[source*2]};;

		*speC*)		ab 0x0f 0x20
				ao 3${register[$source*2]}${register[dest*2]};;

		*speD*)		ab 0x0f 0x21
				ao 3${register[$source*2]}${register[dest*2]};;

		*speT*)		ab 0x0f 0x24
				ao 3${register[$source*2]}${register[dest*2]};;

		*)		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 ################
        herelist
	parse $*
	case $modestring in

		1M*)		ab 0x0f 0xbe
				memneck ${register[$dest*2]}		;;

		M*)		ab 0x0f 0xbf
				memneck ${register[$dest*2]}		;;

		*)		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 ################
        herelist
	parse $*
	case $modestring in

		1M*)		ab 0x0f 0xb6
				memneck ${register[$dest*2]}		;;

		M*)		ab 0x0f 0xb7
				memneck ${register[$dest*2]}		;;

		*)		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 ################
        herelist
	parse $*
	case $modestring in

		1*)		ab 0xfe
				memneck 0				;;

		M)		ab 0xff
				memneck 0				;;

		D)		ao 10${register[$source*2]}		;;

		*)		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 ################
        herelist
	parse $*
	case $modestring in

		1*)		ab 0xfe
				memneck 1				;;

		M)		ab 0xff
				memneck 1				;;

		D)		ao 11${register[$source*2]}		;;

		*)		modenotsupported decrement	
				echo $here	;;
	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 ################
        herelist
	parse $*
	ab 0xf7
	ao 36${register[$source*2]}
	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 ################
        herelist
	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
				memneck ${register[2]}			;;

		DM)		ab 0x01
				memneck ${register[$source*2]}		;;

		IM)		ab 0x81
				memneck 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
				memneck ${register[2]}			;;

		1DM)		ab 0x00
				memneck ${register[0]}			;;

		1IM)		ab 0x80
				memneck 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 only."
						else ############
        herelist
	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. This makes for 256-byte 
address rings, for one thing.  "
						else ############
        herelist
	parse $*
		ab 0x83
		memneck 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 ################
        herelist
	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
				memneck ${register[2]}			;;

		DM)		ab 0x11
				memneck ${register[$source*2]}		;;

		IM)		ab 0x81
				memneck 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
				memneck ${register[2]}			;;

		1DM)		ab 0x10
				memneck ${register[0]}			;;

		1IM)		ab 0x80
				memneck 2
				ab ${number[$source]}			;;

		*)		modenotsupported +carry 		;;
	esac
	opnote + $*
				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 ################
        herelist
		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 ################
        herelist
	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
				memneck ${register[2]}			;;

		DM)		ab 0x29
				memneck ${register[$source*2]}		;;

		IM)		ab 0x81
				memneck 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
				memneck ${register[2]}			;;

		1DM)		ab 0x28
				memneck ${register[0]}			;;

		1IM)		ab 0x80
				memneck 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 ################
        herelist
	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
				memneck ${register[$source*2]}		;;

		MD)		ab 0x3b
				memneck ${register[$dest*2]}		;;

		IM)		ab 0x81
				memneck 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
				memneck ${register[0]}			;;

		1IM)		ab 0x80
				memneck 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 ################
        herelist
	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
				memneck ${register[2]}			;;

		DM)		ab 0x19
				memneck ${register[$source*2]}		;;

		IM)		ab 0x81
				memneck 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
				memneck ${register[2]}			;;

		1DM)		ab 0x18
				memneck ${register[0]}			;;

		1IM)		ab 0x80
				memneck 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 ################

        herelist
	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
				memneck ${register[2]}			;;

		DM)		ab 0x21
				memneck ${register[$source*2]}		;;

		IM)		ab 0x81
				memneck 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
				memneck ${register[2]}			;;

		1DM)		ab 0x20
				memneck ${register[0]}			;;

		1IM)		ab 0x80
				memneck 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 ################
        herelist
	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
				memneck ${register[$source*2]}		;;

		IM)		ab 0xf7
				memneck 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
				memneck ${register[0]}			;;

		1IM)		ab 0xf7
				memneck 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 ################
        herelist
	parse $*
	case $modestring in

		1*)		ab 0xf6
				memneck 2				;;

		*)		ab 0xf7
				memneck 2				;;

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

        herelist
	parse $*
	case $modestring in

		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
				memneck ${register[2]}			;;

		DM)		ab 0x09
				memneck ${register[$source*2]}		;;

		IM)		ab 0x81
				memneck 1
				ac ${number[$source]}			;;

		1ID)	if test "${register[$dest]}" = 0 ; then
					ab 0x0c
					ac ${number[0]}
			else
					ab 0x80
					ao 31${register[2]}
					ab ${number[$source]}
			fi						;;
									
		1DD)		ab 0x08
				ao 3${register[0]}${register[2]}	;;

		1MD)		ab 0x0a
				memneck ${register[2]}			;;

		1DM)		ab 0x08
				memneck ${register[0]}			;;

		1IM)		ab 0x80
				memneck 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 ################

        herelist
	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
				memneck ${register[2]}			;;

		DM)		ab 0x31
				memneck ${register[$source*2]}		;;

		IM)		ab 0x81
				memneck 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
				memneck ${register[2]}			;;

		1DM)		ab 0x30
				memneck ${register[0]}			;;

		1IM)		ab 0x80
				memneck 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 ################
        herelist
	parse $*
	case $modestring in

		DM)		ab 0x0f 0xab
				memneck ${register[$source*2]}		;;

				
		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
				memneck 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 ################
        herelist
	parse $*
	case $modestring in

		DM)		ab 0x0f 0xb3
				memneck ${register[$source*2]}		;;

		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
				memneck 6
				ac ${number[$source]}			;;

		*)		modenotsupported bitoff			;;

	esac
	opnote bitoff $*
				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 or 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 ################
        herelist
	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 ################
        herelist
	parse $*
	if test "$1" 	 	;then
		ab 0xa6		# if there was a size spec it was byte
	else
		ab 0xa7
	fi
	opnote compares $*
				fi
				}




copies		( ) { # copy, decr. C, scaled de/incr. SI/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 copyrange. copies takes one optional
argument, anything, to specify that data are in bytes, so I usually do
something cute like
			copies of bytes
						where \"of\" is the
argument and the \"bytes\" is in effect a comment.

See also: \"copyrange\".\n\n\n"
					else ################
        herelist
	if test "$1" 	 	;then
		ab 0xa4		# if there was a size spec it was byte
	else
		ab 0xa5
	fi
	opnote copies $*
				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 ################
        herelist
	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 ################
        herelist
	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 ################
        herelist
	parse $*
	case $modestring in

		ID)		ab 0xc1
				ao 31${register[2]}			
				ab ${number[$source]}			;;

		IM)		ab 0xc1
				memneck 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
				memneck 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 ################
        herelist
	parse $*
	case $modestring in

		ID)		ab 0xc1
				ao 33${register[2]}			
				ab ${number[$source]}			;;

		IM)		ab 0xc1
				memneck 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
				memneck 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. There 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 ################
        herelist
	parse $*
	case $modestring in

		ID)		ab 0xc1
				ao 35${register[2]}			
				ab ${number[$source]}			;;

		IM)		ab 0xc1
				memneck 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
				memneck 5
				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 ################
        herelist
	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 #######################
        herelist
	parse $*
	if test "$1" 	 	;then
		ab 0xac		# if there was a size spec it was byte
	else
		ab 0xad
	fi
	opnote fetches $*
				fi
				}


flagbit		( ) { # copy addressed bit to carry flag	-x86 BT
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel BT\n
Save bit in carry flag. Bit position to act on is \"source\" argument
in osimpa.\n"
						else ################
        herelist
	parse $*
	case $modestring in

		DM)		ab 0xa3
				memneck ${register[$source*2]}		;;

		DD)		ab 0xa3
				ao 3${register[0]}${register[2]}	;;

		ID)		ab 0xba
				ao 34${register[2]}	
				ac ${number[$source]}			;;

		IM)		ab 0xba
				memneck 4
				ac ${number[$source]}			;;

		*)		modenotsupported flagbit 		;;

	esac
	opnote flagbit $*
				fi
				}


flags		() { #	copy  FLAGS into AX			-x86 LAHF
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel LAHF\n
\h\h"
						else ################
        herelist
	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 ################
        herelist
	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 ################
        herelist
	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 ################
        herelist
	parse $*
	case $modestring in

		DM)		ab 0xbb
				memneck ${register[$source*2]}		;;

		DD)		ab 0xbb
				ao 3${register[0]}${register[2]}	;;

		ID)		ab 0xba
				ao 37${register[2]}	
				ac ${number[$source]}			;;

		IM)		ab 0xba
				memneck 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 ################
        herelist
	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.
Unconditional branch. Various addressing modes.\n\n\n"
						else ################
        herelist
	parse $*
	case $modestring in

		1I)		ab 0xeb
				branch $1 1				;;

		I)		ab 0xe9
				branch $1 $cell				;;

		D)		ab 0xff
				memneck ${register[$source*2]}	;;

		M)		ab 0xff
				ab $((0xa0+${register[$source]}))
				branch $number $cell			;;

		*)		modenotsupported jump 			;;
	esac
	opnote jump $*
					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 is as much as

constant_literal + register + ( register *2^ [1,2,3]) to register

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 
calculations. \n\n"
						else ################
        herelist
	parse $*
	ab 0x8d
	memneck ${register[$dest*2]}
	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.
This seems to be geared for rapid conversions such as between ASCII and
EBCDIC. The index value is replaced by the indexed value. \n"
						else ################
        herelist
	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.\n\n"
						else ################
        herelist
	parse $*
	ab 0x0f 0xbc
	memneck ${register[$dest*2]}
	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 ################

        herelist
	ab 0x0f 0xbd
	parse $*
	memneck ${register[$dest*2]}
	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 ################

        herelist
	parse $*
	case $modestring in

		D)		ab 0xf7
				ao 34${register[0]}		;;

		M)		ab 0xf7
				memneck 4			;;

		1D)		ab 0xf7
				ao 34${register[0]}		;;

		1M)		ab 0xf7
				memneck 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
F6 /3r/m82/6       Two's complement negate r/m byte
F7 /3r/m32  2/6

Two's complement negate, 2 or 6 clocks. simple NOT, then increment.\n\n"
					else ################

        herelist
	parse $*
	case $modestring in

		D)		ab 0xf7
				ao 33${register[0]}			;;

		M)		ab 0xf7
		                memneck 3 				;;

		1M)		ab 0xf6
				memneck 3				;;

		M)		ab 0xf7
		                memneck 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 ################

        herelist
	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\n\n"
					else ################

        herelist
	parse $*
	case $modestring in

#		*imme*)		if test  "${size[$source]}" = "1"	;then
#					ab 0xe4 ${number[$source]}
#				else
#					ab 0xe5
#					ac ${number[$source]}
#				fi 					;;
#
		*)
				if test  "${size[$source]}" = "1"	;then
					ab 0xec
				else
					ab 0xed
				fi 					;;
	esac
	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. Various stack frames by call type. osimpa def
routines' fed's use the form with a 16 bit immediate that drops a stack
frame in parallel with the return jump. See also returnfar \n\n"
					else ################

        herelist
	parse $*
	case $modestring in
		I)		ab 0xc2
				ad ${number[$source]}			;;

		*)		ab 0xc3					;;
	esac
	opnote return $*
				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 #.
\n\n"
					else ################
        herelist
	parse $*
	case $modestring in

#		11*imme*)		ab 0xe6
#				ab ${number[$source]}			;;
#
#		1c*imme*)		ab 0xe7
#				ac ${number[$source]}			;;
#
#		11*)		ab 0xee					;;
#
#		1c*)		ab 0xef					;;
#
		*)		modenotsupported send 			;;
	esac
	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 ################
        herelist
	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 ################
        herelist
	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 ################

        herelist
	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.		"
					else ################

        herelist
	parse $*
	case $modestring in
#		11*)		ab 0xf6
#				memneck 7				;;
#	
#		1c*)		ab 0xf7
#				memneck 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.\n\n\n"
					else ################
        herelist
	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
				memneck ${register[$source*2]}		;;

		1DD)		ab 0x86
				ao 3${register[0]}${register[2]}	;;

		1DM)		ab 0x86
				memneck ${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 ################
        herelist
	parse $*
	case $modestring in

		ID)		ab 0xc1
				ao 32${register[2]}			
				ab ${number[$source]}			;;

		IM)		ab 0xc1
				memneck 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
				memneck 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 ################
        herelist
	parse $*
	case $modestring in

		ID)		ab 0xc1
				ao 30${register[2]}			
				ab ${number[$source]}			;;

		IM)		ab 0xc1
				memneck 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
				memneck 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. zeros roll in on low-significance end, bits are lost
on the high-significance end. Several forms take the shift amount from C.
\n\n"
					else ################

        herelist
	parse $*
	case $modestring in

		ID)		ab 0xc1
				ao 34${register[2]}			
				ab ${number[$source]}			;;

		IM)		ab 0xc1
				memneck 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
				memneck 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, There'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 osimpa, 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 a bit better than a 2:1 win."
		else	 #################
if test "$pass" = "2"		;then
	herelist

	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-$here-2))
		if test $(($branchtarg-$here-2)) -gt 120 \
		-o $(($branchtarg-$here-2)) -lt -120 ; then
			echo "branchsize issue, a short branch may not be"
		fi
	else
	#BIG
		ab 0x0f $(($opbase+16))
		ac $(($branchtarg-$here-$cell))
	fi
if test "$pass" = "2"		;then
	let charcount=26-$charcount"%"26
	while test $charcount -ne 0
	do
	let charcount=$charcount-1
	echo -ne " " 			>> $listing
	done
	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. \n\n"
					else ################
        herelist
	parse $*
	ab 0x0f 0xad
	memneck ${register[$source*2]}
	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

composite up-significance bitshift."
					else ################

        herelist
	ab 0x0f 0xa5
	parse $*
	memneck ${register[$source*2]}
	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.\n\n"
					else ################

        herelist
	parse $*
	ab 0x62
	memneck ${register[$dest*2]}
	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 ################

        herelist
	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 ################

        herelist
	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 ################

        herelist
	ab 0x3e
	opnote DS $*
				fi
				}


ES 		( ) { #						-x86 prefix
if test "$1" = "h" ; then  echo -e  "\n
\nFollowing instruction is to use segment ES\n"
					else ################

        herelist
	ab 0x26
	opnote ES $*
				fi
				}


FS 		( ) { #						-x86 prefix
if test "$1" = "h" ; then  echo -e  "\n
\nFollowing instruction is to use segment FS\n"
					else #################

        herelist
	ab 0x64
	opnote FS $*
				fi
				}


GS		( ) { #						-x86 prefix
if test "$1" = "h" ; then  echo -e  "\n
\nFollowing instruction is to use segment GS\n"
					else ################
        herelist
	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.\n"
else
	herelist
	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 ################

        herelist
	ab 0x0f 6
	opnote clearswitched $*
					fi
					}


frame		( ) { # Pascal in hardware. Know a use?		-x86 = ENTER
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel ENTER\n
Enter is a complex instruction for creating a lexical level frame for
lexical block languages like Pascal. See also: dropframe
frame  ( 16bit framesize, 8bit lexical levels)
...................................................................
{
level = level MOD 32		// level is byte 4 of instr encoding
Push BP
frame-ptr = SP			// frame-ptr is a hardware 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. 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.
In shasm syntax levels is source, frame size is dest, i.e. source and dest
don't mean what they usually do
e.g.
	frame 200 to 3 \n\n"
 					else ################
        herelist
	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.\n"
					else ################
        herelist
	ab 0xcf
	opnote escape $*
				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 ################
        herelist
	parse $*
	ab 0x0f 0xb2
	memneck $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 ################
        herelist
	parse $*
	ab 0xc5
	memneck $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 ################
        herelist
	parse $*
	ab 0xc4
	memneck $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 ################

        herelist
	parse $*
	ab 0x0f 0xb4
	memneck $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 ################

        herelist
	parse $*
	ab 0x0f 0xb4
	memneck $dest
	opnote farGS $*
				fi
				}


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

        herelist
	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 ################

        herelist
	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 ################
        herelist
	parse $*
	ab 0x0f 0 
	if test "$cell" = 2 ; then
		ab 6
	else
		ab 5
	fi
	ac ${number[$source]}		# physical address
	opnote LDT $*
				fi
				}


surprises	( ) { #						-386
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel STI\n
Allow external hardware to interrupt the CPU. \"nosurprises\" 
turns them off. \n\n"
					else ################

        herelist
	ab 0xfb
	opnote surprises $*
				fi
				}


dropframe		( ) { #						-x86   =
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."
					else ################

        herelist
	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 ################

        herelist
	ab 0x0f 3
	parse $*
	memneck ${register[$dest*2]}
	opnote limit $*
				fi
				}


nosurprises	( ) { #						-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 ################

        herelist
	ab 0xfa
	opnote nosurprises $*
					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 ################

        herelist
	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 ################

        herelist
		ab 0x0f 0
	parse $*
		memneck 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 ################

        herelist
	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.
\n\n"
					else ################

        herelist
	parse $*
	case $modestring in

		D)		ao 13${register[$source*2]}		;;

		M)		ab 0x8f
				memneck 0				;;

		1I)		ab 0x6a ${number[$source]}		;;

		*segm)	if test ${register[$source*2]} = "0" ;then # ES
					ab 0x07
			elif test ${register[$source*2]} = "2" ;then	# SS
				ab 0x17

			elif test ${register[$source*2]} = "3" ;then	# DS
				ab 0x1f

			elif test ${register[$source*2]} = "4" ;then	# FS
				ab 0x0f 0xa1

			elif test ${register[$source*2]} = "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 ################

        herelist
	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.

\n\n"
					else ################

        herelist
	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 ################

        herelist
	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 ################

        herelist
	parse $*
	case $modestring in

		D)		ao 12${register[$source*2]}		;;

		M)		ab 0xff 
				memneck  6				;;


		I)		ab 0x68
				ac ${number[$source]}			;;

		1I)		ab 0x6a ${number[$source]}		;;

		*segm)
			if test ${register[$source*2]} = "0" ;then	# ES
				ab 0x06

			elif test ${register[$source*2]} = "1" ;then	# CS
				ab 0x0e

			elif test ${register[$source*2]} = "2" ;then	# SS
				ab 0x16

			elif test ${register[$source*2]} = "3" ;then	# DS
				ab 0x1e

			elif test ${register[$source*2]} = "4" ;then	# FS
				ab 0x0f 0xa0

			elif test ${register[$source*2]} = "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 ################

        herelist
	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 ################
        herelist
	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\n\n"
							else ################
        herelist
	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 ################
        herelist
	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  ################
        herelist
	ab 0x63
	parse $*
	memneck ${register[$source*2]}
	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.\n\n"
							else ################
        herelist
	ab 0x0f 1
	parse $*
	memneck 6
	opnote loadmachinestatusdual $*
					fi
					}


				#		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 ################
        herelist
	parse $*
	# check possible
	ab 0x0f 1
	memneck 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 ################
        herelist
	ab 0x0f 0
	parse $*
	memneck 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 ################
        herelist
	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 ################
        herelist
	ab 0x0f 2
	parse $*
	memneck ${register[$dest*2]}
	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 (linear), since they are the trunk of the memory protection
scheme.\n\n"
							else ################
        herelist
	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 ################
        herelist
	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 ################
        herelist
	ab 0x0f 0x00 
	if test "$cell" == 2 ; then 
		ab 0x16
	else 
		ab 0x15
	fi
	parse $*
	ac ${number[$source]}
	opnote setLDT $*
					fi
					}


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 ################
        herelist
	ab 0x0f 0x00
	parse $*
	memneck 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 ################
        herelist
	ab 0x0f 0
	parse $*
	memneck 5
	opnote writeable $*
					fi
					}

								#()
# OSIMPA COMPEMBLER 					]]]]]	()

#segment stuff
let spam=0
let segreadable=4
let segwriteable=2
let segexecutable=1
let segallperms=7
let loadaddress=0
let segphysaddress=$spam
let segfileoffset=0
let segalign=4096

#ELF header itself stuff
let entrypoint=$loadaddress+52+32	# one segment only


ELFsegment	( ) { # not user useable yet.
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
 compemblink a program header table entry. A segment spec in other words.
We do one by default, R/W/X perms. You can make the load size bigger than
the store size for a .bss equivalent."
						else #########################
	aq 1 0	$loadaddress $loadaddress
	echo "	segment header" >> $listing
		aq $segstoresize $segloadsize 7 0x1000
	echo >> $listing
							fi
							}


ELFcalcs	( ) { # 					=shasm ELF
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
 calculate segloadsize and segstoresize using pass-1-produced data."
						else ###############
	if test $pass = 2 	;then
		if test $endinitialized				;then
			let segstoresize=$endinitialized
		else
			let segstoresize=$lasthere
		fi
		let segloadsize=$lasthere
		echo " segstoresize=		"	$segstoresize
		echo " endinitialized=		"	$endinitialized
		echo " here			"	$here
		echo " lasthere			"	$lasthere
		echo "memory load size	"		$segloadsize
	fi
							fi
							}


ELFid		( ) { #	begin ELF header			=shasm ELF
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
This puts the first 16 bytes of ELF header together, with the E L F,
some machine spec numbers, and o s i m p a <linefeed>."
							else #########
	echo -en "\177ELF" > $output
	echo -n  "7f E  L  F  " > $listing
	let here=$here+4
	ab 1 1 1 0 0 					# 386 stuff
	echo -en "osimpa" >> $output
	echo -en " o  s  i  m  p  a " >> $listing 	# (TM)    ;o)
	ab 0x0a
	let here=$here+6
							fi
							}


ELFheader	( ) { # just the 52 bytes of the main header
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
Construct an ELF header for a static executable using \$loadaddress
and pass 1 assembly state info."
							else ################

		allocated="yes"
		  ELFid
		    echo >> $listing
		ad 2 3
		  aq  1  $entrypoint   52
		    echo >> $listing
		aq 	0 0
		  ad 	52 32 1 40
		    echo >> $listing
		ad 0 0
 		  echo >> $listing
						fi
						}


ELF		() { # build simple but complete ELF header
	ELFcalcs
	ELFheader
	ELFsegment
}


osimplay		() { # main(). e.g. osimpa <your source file>
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
 The osimpa command should be followed by the name of one existing file
to assemble. assemble will execute that file as a shell script. This has
security ramifications for root on multi-user systems. You can also

	. osimpa

with no args and all the osimpa routines will be in your shell state
as shell commands. In that case you'll get this message anyway.
Bash \"set\" does a nice job of indenting code, by the way, as does Bash
\"type\", e.g.      type pmodememref

Output is the files output and listing in the current working directory."
					else ############################
	echo "loadaddress " $loadaddress
	segstoresize=0
	segloadsize=0
	cell=4
	initparse		# SUSPECT	
	charcount=0
	let textinstance=0
	endinitialized=
	let here=0
	: > $output
	pass=1
	. $1
        echo "loadaddress " $loadaddress
        segstoresize=0
        segloadsize=0
        cell=4
        initparse               # SUSPECT
        charcount=0
        let textinstance=0
        endinitialized=
        let here=0
        : > $output
        pass=1
        . $1
	unset suffix		# SUSPECT
	let textinstance=0
	let lasthere=$here
	let here=0
	pass=2
	echo "PASS 1 ABOVE======== ^- BUGS -v ==================PASS 2 BELOW"
	. $1
						fi
						}


# oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooosimpa
# Rick Hohensee   www.clienux.com         humbubba@smart.net
# jan/feb  2001
# compembler for shasm with COWPP(TM) 			Rick Hohensee 2001
# m4/gas version LAAETTR

# 	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_here
			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\"."
						else    ###############
	for item in $*	
	do
		L $item
		allot $cell
	done
				fi
				}


copyrange	() { # 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 to DI. D is
also clobbered. SI and DI are left where they left off, C is left = to 0.
copyrange figures out which way to go up-address or down so there's no
clobbering of the source range in case they overlap. copyrange is for
inlining, and doesn't do a \"return\". One instantiation of copyrange is
about 30 bytes of code. If you want a subroutine version write a wrapper
using return.  copyrange always leaves DF increasing, the osimplay default."
						else    ###############
addtosuffix " copyrange "
	increasing
	= C to D				# get the odd bytes count
	AND 3 with D				# 	"
	-test SI with DI			# sidestep overlap issues
	herelist
		ab 0x78 0x05			# 
	opnote     when sign either_way
	decreasing
	+ C to SI
	+ C to DI 
L either_way
	downshift C 
	downshift C
	ab 0xf3		## repeat while C & !ZF 
	copies
	= D to C
	ab 0xf3		## repeat while C & !ZF 
	 copies of bytes
	increasing				# the default anyway
dropsuffix
			fi
			}


clump		() { # C struct() kinda, data associations namer
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
 Clumps are something like Neolithic C structs. A clump is an affiliated
set of offset names in assembly state. Data items in a clump are defined
by thier spacing, and thus have implied sizes. Osimpa doesn't do any
typing, however. e.g.

	order clumpname item0 <size> item1 <size> ...

will make \$clumpname_item0 \$clumpname_item1 \$clumpname_size be the
appropriate offsets in the assembler state. This doesn't allocate any
static space. Thus _ is analagous to . in C. Use

	strand	\$clumpname_size

or allot. The offset of a particular item is the total space before it,
i.e. the sum of offsets preceding it in the clump. Try some."
						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
						}


east		(  ) { # get a strand's pointing-east 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, SUSPECT get the
\"east\" pointer into A."
						else #################
	= -$(($cell*4)) I to A
					fi
					}


entrance	() { # name/begin a reentrant procedure 
 if test "$1" = "h" ; then
 echo -e "\n\t\t\t\t\t
 Start the assembly state for an osimpa-calling-format named routine that
allocates a 15-cell stack frame, not including the return address. This is
primarily needed for recursion. I guess. \"entrance\" sets a label for it
too. The calling mechanism just allocates the space on the stack; it
doesn't push/pull (pop) anything. See \"inherit\". This creates a tendency
to write code that implements efficient copy-on-write parameter passing.
Otherwise you can use the regular call/return instructions, or inlining.
An entrance can have several exuents. The name exuent is used to
distinguish between procedure termination and the unix exit syscall, which
is program termination."
						else    ###############
	rehikeParents
	L $1
					fi
					}


leave		() { # return from the current reentrant procedure
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
 A leave is a termination point of an \"entrance\" 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    ###############
	herelist
	bytes $((0xc2))
	duals $((16*$cell))
	opnote  "\t\texuent" >>	$listing
						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 therefor 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 ################
        herelist
	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 defined with \"entrance\". 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."
						else    ###############
	hike
	call $1
						fi
						}


heap		() { # start uninititialized data sub-section
 if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
 Unix tradition is that there's a loader segment called .bss that has
defined locations in it for stuff, but isn't allocated in the program's
stored image. This is great for uninitialized global data. osimpa \"heap\"
allows you to specify an analagous sub-section of the one main segment the
\"ELF\" command creates at the end of said segment. This allows the
example program \"get\" for example to not have to contain a 4k buffer in
the stored program image.
"
						else    ###############
	echo $here "at heap"
	opnote heap ALLOCATED_ABOVE UNALLOCATED_BELOW
	align 16
	endinitialized=$here
	allocated=no
				fi
				}


hike ( ) 	{ # hike the stack by 15 cells. Part of enter. 
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t 
 Hike stack by 15 cells. Used by \"enter\", which pushes a return address
also, making for 16-cell stack frames including the return address."
						else    ###############
	subtract $((15*$cell)) to SP		;fi
						}


index		( ) { # get the metadata (global) index value of a strand
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, put the current index
value in A. "
						else #################
	= -$(($cell*6)) I to A
					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. OIt 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 ################
	herelist			
	ab 0xf3			# rep
	opnote			 repeat while C \& ZF 
 compares of bytes
						fi
						}


max		() { # BROKE  inline, max bla zay  to zay 
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t
 Assembles 3 inline instructions to leave the larger of two args in the
second arg. 
		max A B
					leaves the larger value of A or B
in B. Saves one instruction at runtime when the second arg is larger. A is
unchanged. max can use one or zero memory locations.

Building unique branches internal to numerous instantiations of the same 
routine is tricky. It might work if both args are registers :o) 

Takes a third argument for a labels uniqueifier for if a program has more
than one max in it, ala   max 1024 C jkdhnkhjg
		DOESN't WORK EITHER

"
					   	else #################
	addtosuffix " max "
	-test $1 with  $2
		when not sign  $3
	= $1 to $2
	dropsuffix
L $3
			fi
			}


min		() { # BROKE    inline, min bla zay   to zay
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 
				leaves the smaller value of A or B in B.
min is one instruction faster at runtime when the second arg is smaller. A
is unchanged. 

Building unique branches internal to numerous instantiations of the same 
routine is tricky. It might work if both args are registers :o) 
"
			   			else #################
if test $pass = 1 	;then
minrandom=_$RANDOM
fi
	addtosuffix " min "
	-test $1 with  $2
		when sign  	2
	= $1 to $2
	dropsuffix
L $minrandom
			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, which doesn't pertain to osimplay."
						else    ###############
let count=0
for arg in $*							;do
	if test $(($arg)) -ne 0				;then
		let count=$(($arg))
	else
		let $arg=$count		# assignment to $tring. :o)
		let count=$count+1
	fi
done					;fi;}


ply		( ) { # get the size of a quantum in an array strand
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, put the ply value
in A."
					else #################
	= -$(($cell*2)) I to A
				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
				}


quadtohex	() { # quad value to ASCII hexadecimal string
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
 quadtohex takes a cell value in A and creates a hexidecimal ASCII string
of that value at the given location. e.g.
					
	quadtohex \$stringbuffer

A, B and C get clobbered. quadtohex also needs the \$digit global."
						else #####################
	if test $digit	; then
		quadtohexrand=$RANDOM
			= 8 to C
		L _$quadtohexrand
			= A to B
			AND 0x0f to B
			= @ B $digit to B
			= B to C $1
			downshift 4 to A
			1- C
				whenmore _$quadtohexrand
	else
		echo "quadtohex needs \$digit, i.e. Ldigits "
	fi
						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 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
						}


rehikeParents	(  ) { # def has to do this
pa="$((18$cells)) + SP"
pb="$((19$cells)) + SP"
pc="$((20$cells)) + SP"
pd="$((21$cells)) + SP"
pe="$((22$cells)) + SP"
pf="$((23$cells)) + SP"
pg="$((24$cells)) + SP"
ph="$((25$cells)) + SP"
pi="$((26$cells)) + SP"
pj="$((27$cells)) + SP"
pk="$((28$cells)) + SP"
pl="$((29$cells)) + SP"
pm="$((30$cells)) + SP"
pn="$((31$cells)) + SP"
po="$((32$cells)) + SP"
				}


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-aligned data. The comparison is between A and the current @ DI, which
gets incremented as per argument size. osimpa \"scan\" is a \`plural\', and uses
the repeat-while-non-zero prefix byte implicitly. 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 ################
        herelist
	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 consistant 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
cell west
cell east
cell index
cell mask
cell ply
cell bytes
DATA		<----- nominal address
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
	aq 0 0 0 0 0
	aq $mask
	if test "$3"			; then
		aq $3
	else
		aq 1
	fi
	eval ${1}_bytes=$2
	aq $2
	L $1
	os=$here+$2
	allot  $os 			;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
					}


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 name.
e.g.

text ernie <<!

stuff
			other stuff
!

"
						else    ###############
	IFS=
	let textinstance=$textinstance+1
	let temp=0
	if test $1 						;then
			herelist
			aq ${textlength[$textinstance]}
			if test $pass = 2		; then
			   echo -n "		TEXT length" >> $listing
			fi
			L $1
			echo >> $listing
	fi
	while read line				;do
		let here=$here+1
		ascii $line
		echo -en "\012" >> output
		let temp=$temp+${#line}+1
	done
	textlength[$textinstance]=$temp
	IFS="
"
					fi
					}


cells="*4"

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 "


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.
"
					else	############################
	= $(($1-$here-5)) $2 *2^ 2  to $2
	jump $(($1-$here-4)) + $2
						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_here=$here
					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 memneck 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 there'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
			ac ${beams[$yi]}
			let yi=$yi+1
		done
		echo >> $listing
					fi
					}


zero		() { # simple convenience to set (a) register(s) to 0
	for reg in $*	;do
		XOR $reg with $reg
	done			
			}


								# ()
# HOST OS DEPENDANT	()

echo "libo"
# call		number	args

_llseek="140"
_sysctl="149"
access="33"
acct="	51"
adjtimex="124"
alarm="	"
bdflush=""
brk="	"
capget=""
capset=""
chdir="	"
chmod="	15"
chown="	182"
chroot="61"
clone="	120"
close="	6"
creat="	8"
dup2="	63"
dup=" 41"
execve=" 11"
exit=" 1"
fchdir=" 133"
fchmod=" 94"
fchown=" 95"
fcntl="	55"
fdatasync=" 148"
flock="	143"
fork=" 2"
fstat="	108"
fstatfs=" 100"
fsync="	118"
ftruncate=" 93"
get_kernel_syms=" 130"
getcwd=" 183"
getdents=" 141"
getegid=" 50"
geteuid=" 49"
getgid=" 47"
getgroups=" 80"
getitimer=" 105"
getpgid=" 132"
getpgrp=" 65"
getpid=" 20"
getppid=" 64"
getpriority=" 96"
getresgid=" 171"
getrusage=" 77"
gettimeofday=" 78"
getuid=" 24"
idle=" 112"
ioctl="	54"
ioperm=" 101"
iopl=" 110"
ipc=" 117"
kill=" 37"
lchown=" 16"
link=" 9"
lock=" 53"
lseek="	19"
lstat="	107"
mkdir="	39"
mknod="	14"
mlock="	150"
mlockall=" 152"
mmap=" 90"
mount="	21"
mprotect=" 125"
mremap=" 163"
msync="	144"
munlock=" 151"
munlockall=" 153"
munmap=" 91"
nanosleep=" 162"
nfsservctl=" 169"
nice=" 34"
oldfstat=" 28"
oldlstat=" 84"
open=" 5"
pause=" 29"
pipe=" 42"
poll=" 168"
prctl="	172"
pread="	180"
ptrace=" 26"
pwrite=" 181"
quotactl=" 131"
read=" 3"
readdir=" 89"
readlink=" 85"
readv="	145"
reboot=" 88"
rename=" 38"
rmdir="	40"
sched_get_priority_max=" 159"
sched_get_priority_min=" 160"
sched_getparam=" 155"
sched_getscheduler=" 157"
sched_rr_get_interval="	161"
sched_setparam=" 154"
sched_setscheduler=" 156"
sched_yield=" 158"
sendfile=" 187"
setdomainname="	121"
setfsgid=" 139"
setfsuid=" 138"
setgid=" 46"
setgroups=" 81"
sethostname=" 74"
setitimer=" 104"
setpgid=" 57"
setpriority=" 97"
setregid=" 71"
setresgid=" 170"
setresuid=" 164"
setreuid=" 70"
setrlimit=" 75"
setsid=" 66"
settimeofday=" 79"
setuid=" 23"
sgetmask=" 68"
sigaction=" 67"
sigaltstack=" 186"
sigpending=" 73"
sigprocmask=" 126"
sigreturn=" 119"
sigsuspend=" 72"
socketcall=" 102"
ssetmask=" 69"
stat=" 106"
statfs=" 99"
stime="	25"
swapoff=" 115"
swapon=" 87"
symlink=" 83"
sync=" 36"
sysfs=" 135"
sysinfo=" 116"
syslog=" 103"
time=" 13"
times=" 43"
truncate=" 92"
umask="	60"
umount=" 22"
uname="	122"
unlink=" 10"
uselib=" 86"
ustat="	62"
utime="	30"
vhangup=" 111"
vm86=" 166"
wait4="	114"
waitpid=" 7"
write="	4"
writev=" 146"


				# pytes missing
chown32=" 212"
truncate64=" 193"
stat64=" 195"
fchown32="	207	3"
setresuid32="	208	"
lchown32="	198	"
getgid32="	200	0"
setgid32="	214	"
setuid32="	213	"
setregid32="	204	"
fcntl64="	221	3"
getresgid32="	211	3"
setfsgid32="	216	"
setfsuid32="	215	"
setresgid32="	210	"
setgroups32="	206	"
fstat64="	197	"
getresuid32="	209	"
ftruncate64="	194	2"
getgroups32="	205	"
setreuid32="	203	"
getuid32="	199	"
lstat64="	196	"
getdents64="	220	3"
getegid32="	202	0"
geteuid32="	201	0"
				# can't find argc
break="	17"
ftime="	35"
vfork="	190"
stty=" 31"
mpx=" 56"
modify_ldt=" 123"
umount2=" 52"
getpmsg=" 188"
ugetrlimit=" 191	/* SuS compliant getrlimit */	"
ulimit=" 58"
personality=" 136"
vm86old=" 113"
getresuid=" 165"
prof=" 44"
profil=" 98"
getresuid=" 165"
kgetsid=" 147"
mmap2="	192"
signal=" 48"
pivot_root=" 217"
_newselect=" 142"
gtty=" 32"
madvise1=" 219	/* delete when C lib stub is removed */	"
madvise=" 219"
mincore=" 218"
putpmsg=" 189	/* some people actually want streams */	"
oldolduname=" 59"
oldstat=" 18"
olduname=" 109"
query_module=" 167"
select=" 82"
#                                                   ?	?
#					         ?		?
#				# POSIX realtime?
rt_sigaction="			174	"
rt_sigpending="			176	"
rt_sigprocmask="		175	"
rt_sigqueueinfo="		178	"
rt_sigreturn="			173	"
rt_sigsuspend="			179	"
rt_sigtimedwait="		177	"



Linux		() { # syscalls, e.g.  Linux $read, many available
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
\"Linux\", the osimpa command, takes a syscall number. There are variables
named for many of the syscalls, so you can do

	Linux \$read

for example. That does the trap and sets A to the pertinent syscall
number, but you still have to put the arguments in B, C, D... as per usual
depending on the arguments the syscall takes. For Linux syscalls the
C-oriented manpages give an argument order that maps onto B, C, D, SI, DI.

osimpa Linux assumes/requires the syscall number argument, and then can
accept further args. The args go in the manpage order.

Just do      
		type Linux    
				It's not that obscure.	:o)
"
							else	##########
addtosuffix " Linux "
	= $1 to A
	if test "$2" ;then
	eval	= $2 to B
	fi
	if test "$3" ;then
	eval = $3 to C
	fi
	if test "$4" ;then
	eval =  $4 to D
	fi
	if test "$5" ;then
	eval = $5 to SI
	fi
	if test "$6" ;then
	eval = $6 to DI
	fi
	submit 128
dropsuffix
						fi
						}


print		() { # write all of a range's net data to stdout
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
If you've named a region, say, BABBLE,
					print $BABBLE
							will send it's
contents to stdout. This is handy for static text. It writes the whole
range as defined, so you don't have to count characters. It doesn't do
variables insertions like C printf. See \"text\" also."
					else #########################
addtosuffix " print "
	= @ $(($1-4)) to D
	= $1 to C
	= 1 to B
	Linux $write
dropsuffix
				fi
				}


regspew		() { # raw binary register dump to stderr
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
raw binary dump of the usual 8 registers and FLAGS to FD 2. Give it an
argument to uniqueify labels by if you have more than one regspew in a 
program. regspew writes out pad bytes to 48 total so things are lined 
up nice in a binary editor."
						else ###############
addtosuffix " regspew "
	pushcore
	pushflags
	= A  to @  $spewzone
	= $spewzone to A
	= B  to @ 4  A
	= C  to @ 8  A
	= D  to @ 12 A
	= SP to @ 16 A
	= BP to @ 20 A
	= SI to @ 24 A
	= DI to @ 28 A
	pull B 
	= B to @ 32 A
	= 2 to B
	= $spewzone to C 
	= 48 to D
	  Linux $write
	pullcore
	jump $endspewzone
L spewzone
allot 42
ab `asciibyte _ _ S P E W `
L endspewzone
dropsuffix
			fi
			}


newline		() { # write a (unix) newline to stdout
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t

Highly counter-optimal, but handy. Forth cr basically."
						else ######################

jump $(($here+8))		# label-avoidance ugliness
ab  10 10 10
= $(($here-2)) to C
= 1 to B
= B to D
Linux $write
			fi
			}


