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

								#()
# ASSEMBLER DIRECTIVES/HELPERS					 ()
						# config
allocated=yes
echo "allocated=yes. No ELFism."
output=~binary
listing=~listing
declare -ai textlength
	#	here	cell		
declare -i 	H 	C
octalbyte=({0,1,2,3}{0,1,2,3,4,5,6,7}{0,1,2,3,4,5,6,7})
		## several doodads to have meta listings notes


hex=(\
{0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f}{0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f})


opnote 		( ) { # 	internal			=shasm
	if test "$pass" = "2"		;then
		echo -n "			"$*"	" >> $listing
	fi
	}


ascii		( ) { # use  text  or   tag 			=shasm
if test "$1" = "h" ; then  echo -e "\n\n
 Simple stick-text-in-assembly. For named self-sized text see \"text\".
The annoyance is that the shell does a lot of handy parsing of the
commandline that is completely in the way vis-a-vis literal strings. The
work-around that's easiest to implement is to make sure strings have no
actual whitespace in them as arguments to ascii or string. That means use
\040 for space, \t for tab, and \n for linefeed (unix end-of-line). Or use
\"text\". You also have \"asciibyte\" for ascii to binary."
							else ###############
	if test "$pass" = "1" 			;then
		H=H+${#1}
	else
		echo  -en   $1		>> $output
		echo 	$1  "ASCII "  		>> $listing
		H=H+${#1}
	fi
							fi
							}


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


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


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


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


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


align		() { # like e.g. .align				=shasm
if test "$1" = "h" ; then  echo -e  "\n\n\n\n
Don't use this until \$H has been bumped above 0."
						else ####################
	declare lop pad
	if test "$H"	&& test "$H" != "0"	;then
		let	lop=$H%$1
		let	pad=$1-$lop
		if test $lop -ne 0		;then
			allot $pad
		fi
	else
		echo "For simplicity, align doesn't work until
		\$H has been set greater than 0."
	fi
						fi
						}



allot		() { # relative .org, like Forth ALLOT		=shasm
if test "$1" = "h" ; then  echo -e "\n\n
 This is like Forth ALLOT, which is relative, unlike asm .org, which is
(file address) absolute. Allot takes one amount argument in byte units.
If \$H is 0x200 then after 	allot 0x10    \$H is 0x210. \$H
is the next un-assembled file address.
\n\n"
						else ############

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


asciibyte	() { # e.g.      ab  `asciibyte c q d`		=shasm
if test "$1" = "hoilp" ; then  echo -e "\n\n

This isn't help on h. That led to one of the more twisted bugs I've
encountered. I assume you're reading the script, or did 
							asciibyte hoilp

Similar to BASIC ASC or C 't'. Unlike osimpa \"text\" or \"ascii\" in that
it doesn't assemble the byte itself. You'll probably use it with grave
accents, ala

	 = `asciibyte T` to DI
"
						else ################

for aschr in $* 						; do	
	case $aschr in		
			# some duplicates for performance. Heh.
cr)		echo  $((0015)) ;;  # 0x0D  CARRIAGE RETURN
space)		echo  $((0040)) ;;  # 0x20  SPACE
tab)		echo  $((0011)) ;;  # 0x09  HORIZONTAL TABULATION
bell)		echo  $((0007)) ;;  # 0x07  BELL
backspace)	echo  $((0010)) ;;  # 0x08  BACKSPACE
formfeed)	echo  $((0014)) ;;  # 0x0C  FORM FEED
"!")		echo  $((0041)) ;;  # 0x21  EXCLAMATION MARK

null)		echo  $((0000)) ;;  # 0x00  NULL
header)		echo  $((0001)) ;;  # 0x01  START OF HEADING
sot)		echo  $((0002)) ;;  # 0x02  START OF TEXT
binary)		echo  $((0003)) ;;  # 0x03  END OF TEXT
eot)		echo  $((0004)) ;;  # 0x04  END OF TRANSMISSION
enqiry)		echo  $((0005)) ;;  # 0x05  ENQUIRY
ack)		echo  $((0006)) ;;  # 0x06  ACKNOWLEDGE
bell)		echo  $((0007)) ;;  # 0x07  BELL
backspace)	echo  $((0010)) ;;  # 0x08  BACKSPACE
tab)		echo  $((0011)) ;;  # 0x09  HORIZONTAL TABULATION
linefeed)	echo  $((0012)) ;;  # 0x0A  LINE FEED
vtab)		echo  $((0013)) ;;  # 0x0B  VERTICAL TABULATION
formfeed)	echo  $((0014)) ;;  # 0x0C  FORM FEED
cr)		echo  $((0015)) ;;  # 0x0D  CARRIAGE RETURN
out)		echo  $((0016)) ;;  # 0x0E  SHIFT OUT
in)		echo  $((0017)) ;;  # 0x0F  SHIFT IN
outofband)	echo  $((0020)) ;;  # 0x10  DATA LINK ESCAPE
control1)	echo  $((0021)) ;;  # 0x11  DEVICE CONTROL ONE
control2)	echo  $((0022)) ;;  # 0x12  DEVICE CONTROL TWO
control3)	echo  $((0023)) ;;  # 0x13  DEVICE CONTROL THREE
control4)	echo  $((0024)) ;;  # 0x14  DEVICE CONTROL FOUR
nack)		echo  $((0025)) ;;  # 0x15  NEGATIVE ACKNOWLEDGE
standby)	echo  $((0026)) ;;  # 0x16  SYNCHRONOUS IDLE
eoblock)	echo  $((0027)) ;;  # 0x17  END OF TRANSMISSION BLOCK
cancel)		echo  $((0030)) ;;  # 0x18  CANCEL
eomedia)	echo  $((0031)) ;;  # 0x19  END OF MEDIUM
substitute)	echo  $((0032)) ;;  # 0x1A  SUBSTITUTE
escape)		echo  $((0033)) ;;  # 0x1B  ESCAPE
filesep)	echo  $((0034)) ;;  # 0x1C  FILE SEPARATOR
groupsep)	echo  $((0035)) ;;  # 0x1D  GROUP SEPARATOR
recordsep)	echo  $((0036)) ;;  # 0x1E  RECORD SEPARATOR
unitsep)	echo  $((0037)) ;;  # 0x1F  UNIT SEPARATOR
space)	       echo  $((0040)) ;; # 0x20  SPACE
"!")		echo  $((0041)) ;;  # 0x21  EXCLAMATION MARK
"\"")		echo  $((0042)) ;;  # 0x22  QUOTATION MARK
"#")		echo  $((0043)) ;;  # 0x23  NUMBER SIGN
"$")		echo  $((0044)) ;;  # 0x24  DOLLAR SIGN
"%")		echo  $((0045)) ;;  # 0x25  PERCENT SIGN
"&")		echo  $((0046)) ;;  # 0x26  AMPERSAND
"\'")		echo  $((0047)) ;;  # 0x27  APOSTROPHE
"(")		echo  $((0050)) ;;  # 0x28  LEFT PARENTHESIS
")")		echo  $((0051)) ;;  # 0x29  RIGHT PARENTHESIS
"\*")		echo  $((0052)) ;;  # 0x2A  ASTERISK
"+")		echo  $((0053)) ;;  # 0x2B  PLUS SIGN
,)		echo  $((0054)) ;;  # 0x2C  COMMA
-)		echo  $((0055)) ;;  # 0x2D  HYPHEN-MINUS
.)		echo  $((0056)) ;;  # 0x2E  FULL STOP
"/")		echo  $((0057)) ;;  # 0x2F  SOLIDUS
0)		echo  $((0060)) ;;  # 0x30  DIGIT ZERO
1)		echo  $((0061)) ;;  # 0x31  DIGIT ONE
2)		echo  $((0062)) ;;  # 0x32  DIGIT TWO
3)		echo  $((0063)) ;;  # 0x33  DIGIT THREE
4)		echo  $((0064)) ;;  # 0x34  DIGIT FOUR
5)		echo  $((0065)) ;;  # 0x35  DIGIT FIVE
6)		echo  $((0066)) ;;  # 0x36  DIGIT SIX
7)		echo  $((0067)) ;;  # 0x37  DIGIT SEVEN
8)		echo  $((0070)) ;;  # 0x38  DIGIT EIGHT
9)		echo  $((0071)) ;;  # 0x39  DIGIT NINE
":")		echo  $((0072)) ;;  # 0x3A  COLON
";")		echo  $((0073)) ;;  # 0x3B  SEMICOLON
"<")		echo  $((0074)) ;;  # 0x3C  LESS-THAN SIGN
"=")		echo  $((0075)) ;;  # 0x3D  EQUALS SIGN
">")		echo  $((0076)) ;;  # 0x3E  GREATER-THAN SIGN
"?")		echo  $((0077)) ;;  # 0x3F  QUESTION MARK
@)		echo  $((0100)) ;;  # 0x40  COMMERCIAL AT
A)		echo  $((0101)) ;;  # 0x41  
B)		echo  $((0102)) ;;  # 0x42  
C)		echo  $((0103)) ;;  # 0x43  
D)		echo  $((0104)) ;;  # 0x44  
E)		echo  $((0105)) ;;  # 0x45  
F)		echo  $((0106)) ;;  # 0x46  
G)		echo  $((0107)) ;;  # 0x47  
H)		echo  $((0110)) ;;  # 0x48  
I)		echo  $((0111)) ;;  # 0x49  
J)		echo  $((0112)) ;;  # 0x4A  
K)		echo  $((0113)) ;;  # 0x4B  
L)		echo  $((0114)) ;;  # 0x4C  
M)		echo  $((0115)) ;;  # 0x4D  
N)		echo  $((0116)) ;;  # 0x4E  
O)		echo  $((0117)) ;;  # 0x4F  
P)		echo  $((0120)) ;;  # 0x50  
Q)		echo  $((0121)) ;;  # 0x51  
R)		echo  $((0122)) ;;  # 0x52  
S)		echo  $((0123)) ;;  # 0x53  
T)		echo  $((0124)) ;;  # 0x54  
U)		echo  $((0125)) ;;  # 0x55  
V)		echo  $((0126)) ;;  # 0x56  
W)		echo  $((0127)) ;;  # 0x57  
X)		echo  $((0130)) ;;  # 0x58  
Y)		echo  $((0131)) ;;  # 0x59  
Z)		echo  $((0132)) ;;  # 0x5A  
"[")		echo  $((0133)) ;;  # 0x5B  LEFT SQUARE BRACKET
"\\")		echo  $((0134)) ;;  # 0x5C  REVERSE SOLIDUS
"]")		echo  $((0135)) ;;  # 0x5D  RIGHT SQUARE BRACKET
"^")		echo  $((0136)) ;;  # 0x5E  CIRCUMFLEX ACCENT
_)		echo  $((0137)) ;;  # 0x5F  LOW LINE
"\`")		echo  $((0140)) ;;  # 0x60  GRAVE ACCENT
a)		echo  $((0141)) ;;  # 0x61  
b)		echo  $((0142)) ;;  # 0x62  
c)		echo  $((0143)) ;;  # 0x63  
d)		echo  $((0144)) ;;  # 0x64  
e)		echo  $((0145)) ;;  # 0x65  
f)		echo  $((0146)) ;;  # 0x66  
g)		echo  $((0147)) ;;  # 0x67  
h)		echo  $((0150)) ;;  # 0x68  
i)		echo  $((0151)) ;;  # 0x69  
j)		echo  $((0152)) ;;  # 0x6A  
k)		echo  $((0153)) ;;  # 0x6B  
l)		echo  $((0154)) ;;  # 0x6C  
m)		echo  $((0155)) ;;  # 0x6D  
n)		echo  $((0156)) ;;  # 0x6E  
o)		echo  $((0157)) ;;  # 0x6F  
p)		echo  $((0160)) ;;  # 0x70  
q)		echo  $((0161)) ;;  # 0x71  
r)		echo  $((0162)) ;;  # 0x72  
s)		echo  $((0163)) ;;  # 0x73  
t)		echo  $((0164)) ;;  # 0x74  
u)		echo  $((0165)) ;;  # 0x75  
v)		echo  $((0166)) ;;  # 0x76  
w)		echo  $((0167)) ;;  # 0x77  
x)		echo  $((0170)) ;;  # 0x78  
y)		echo  $((0171)) ;;  # 0x79  
z)		echo  $((0172)) ;;  # 0x7A  
"\{")		echo  $((0173)) ;;  # 0x7B  LEFT CURLY BRACKET
"|")		echo  $((0174)) ;;  # 0x7C  VERTICAL LINE
"\}")		echo  $((0175)) ;;  # 0x7D  RIGHT CURLY BRACKET
"~")		echo  $((0176)) ;;  # 0x7E  TILDE
delete)		echo  $((0177)) ;;  # 0x7F  DELETE
	esac	
		done	
					fi
					}


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
		binaryout $num
		echo -en "    "
		octalout $num
		echo -en "    "
		hexout $num
		echo $(($num)) "dec"
	done
						fi
}


binary		() { # binary <strings of ones and zeros>	=shasm
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t

The osimplay   binary   word has mostly been superceded by a set of names for
the values 0-255 where zeros are _ and ones are I, e.g. 3 is \$______II.

 Convert the binary string representation of a number to it's value.
Accepts it's single argument in multiple segments, for keeping things in
byte-sized parcels.

		e.g.
			let T=\`binary 0100 01001010\`
or for just looking,
			binary 0101010011010011

#binary (and all of shasm) uses Bash arithmatic, with Bash\'s limits,
which looks like 32 bits H.

"
						else ####################
	remaining=$1$2$3$4$5$6$7$8
	let placebit=1
	value=0
	while test ! "$remaining" = ""				;do
		if test "${remaining:${#remaining}-1:1}" = "1"	;then
			let value=$value+$placebit
		fi
		placebit=$(($placebit*2))
		remaining=`chom $remaining`
	done
	echo $value
					fi
					}


append		( ) { # help-only on dealing with binary appendages
echo -e  "\n\t\t\t\t\t
 
 You don't want to be recompembling tables of constants and so on with
every run of a compembler written in Bash. So cheat. Use cat, and make a
proto-linker at the very end of your osimplay source file, so you have the
right H to offset into the appended files. Let's say you have 3
precompembled RELOCATABLE objects, lookup data tables, say, named Larry,
Moe and Curly. Assume they are 3 different sizes. At the very end of your
top sourcefile...

Larry=\$H
Moe=\$((\$H+\$Larry))
Curly=\$((\$Moe+\$Larry))
Shemp=\$((\$Curly+\$Moe))

mv binary inary
cat inary Larry Moe Curly > binary
rm inary				
			and you can     = A to @ Larry     as desired 

elsewhere in the sources. We left out Shemp. osimplay doesn't use anything
but a sh, but that's osimplay's problem. Your sources are shell scripts.

"
	}



binaryout       ( ) { # print a binary string of a number	=shasm
let val=$1
bstring=
for maskbit in -2147483648 1073741824 536870912 268435456 134217728 \
67108864 33554432 16777216 8388608 4194304 2097152 1048576 524288   \
262144 131072 65536 32768 16384 8192 4096 2048 1024 512 256 128 64  \
32 16 8 4 2 1
do
        let bitval=$val\&$maskbit
        bitchar=0
        if test "$bitval" -ne 0 ;then
                bitchar=1
        fi
        bstring=$bstring$bitchar
        if test $maskbit = 16777216 -o  $maskbit = 65536 -o \
                $maskbit = 256
                                        then
                bstring=$bstring"_"
        fi
done
echo -n $bstring
}


branch 		( ) { # resolve a relative branch		=shasm
if test "$1" = "h" ; then  echo "\n\n\n
 A branch resolver. Called by branching opers, which pass this the label
name and branch size.\n\n"
elif test "$pass" = "1"
then			# on pass 1 just skip the branch byte/dual/quad
	H=H+$2
else				# Pass 2 is on us. Pass 1 was L.
	let relativ=$1-H-$2
	case $2 in
		1)bytes $relativ		;;
		2)duals $relativ		;;
		4)quads $relativ		;;
		*) echo "
	The second argument to branch isn't 1, 2 or 4. "
						;;
	esac
							fi
							}


bytes		( ) { # 		internal		=shasm
	H=H+$#
	for a in $*	;do
		if test $(($a)) -gt 255 ; then
			echo " H " $H ":  index into hex[] > 255"
		fi
		if test "$allocated" = "yes"	;then
			echo -en \\${octalbyte[$a&0xff]}	>> $output
		fi
		if test $(($a)) -lt 0	;then
			let a=$a+256
		fi
		echo -n ${hex[$a]}" " 			>> $listing
	done
						}


chom		() { # chom chomp  returns  chom		=shasm
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
chom chomp   will leave chom	"
						else    ###############
    echo ${1:0:${#1}-1}			;fi
					}


duals		( ) { #		internal			=shasm
H=H+$#*2
for a in $*	;do
	if test "$allocated" = "yes"	;then
		echo -en \\${octalbyte[$a&0xff]}	>> $output
	fi
	echo -en ${hex[$a&0xff]}" "		>> $listing
	if test "$allocated" = "yes"	;then
		echo -en \\${octalbyte[$((a>>8))&0xff]} >> $output
	fi
	echo -en ${hex[$((a>>8))&0xff]}" "	>> $listing
done
					}


quads		( ) { #		internal			=shasm
H=H+$#*4
for a in $*	;do
	if test "$allocated" = "yes"	;then
		echo -en \\${octalbyte[$a&0xff]}	>>	$output
	fi
	echo -en ${hex[$a&0xff]}" "		>>	$listing
	if test "$allocated" = "yes"	;then
		echo -en \\${octalbyte[$((a>>8))&0xff]}	>> 	$output
	fi
	echo -en ${hex[$((a>>8))&0xff]}" "	>>	$listing
	if test "$allocated" = "yes"	;then
		echo -en \\${octalbyte[$((a>>16))&0xff]}>>	$output
	fi
	echo -en ${hex[$((a>>16))&0xff]}" "	>>	$listing
	if test "$allocated" = "yes"	;then
		echo -en \\${octalbyte[$((a>>24))&0xff]}>>	$output
	fi
	echo -en ${hex[$((a>>24))&0xff]}" "	>>	$listing
done
						}


hexout          ( ) { # print a hex representation of a number	=shasm
declare val bstring
let val=$1
bstring=0x
for shift in 28 24 20 16 12 8 4 0      ;do
        let nybble=$val\>\>$shift\&15
        case $nybble in
        0) bstring=$bstring"0"                          ;;
        1) bstring=$bstring"1"                          ;;
        2) bstring=$bstring"2"                          ;;
        3) bstring=$bstring"3"                          ;;
        4) bstring=$bstring"4"                          ;;
        5) bstring=$bstring"5"                          ;;
        6) bstring=$bstring"6"                          ;;
        7) bstring=$bstring"7"                          ;;
        8) bstring=$bstring"8"                          ;;
        9) bstring=$bstring"9"                          ;;
        10) bstring=$bstring"a"                         ;;
        11) bstring=$bstring"b"                         ;;
        12) bstring=$bstring"c"                         ;;
        13) bstring=$bstring"d"                         ;;
        14) bstring=$bstring"e"                         ;;
        15) bstring=$bstring"f"                         ;;
        esac
done
echo -n $bstring" "
}


hexquad		( ) { #		internal			=shasm
# BIG-endian non-spaced hex 4-byte int for $H
if test "$1" ; then
	echo -en \
${hex[$1>>24&0xff]}${hex[$1>>16&0xff]}${hex[$1>>8&0xff]}${hex[$1&0xff]}"  "\
		>>	$listing
else
	echo "No arg for hexquad. Set \$H?"
						fi
						}


HL	( ) { #		internal			=shasm
if test $pass = 2	;then
	echo >> $listing
	hexquad $H
						fi
						}


homp		() { # homp chomp  is  homp			=shasm
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
   homp chomp 	produces homp"
						else    ###############
	echo ${1:1:${#1}-1} 		;fi
					}


fillabsolute	() { # like .org, but fill to just BEFORE arg	=shasm
if test "$1" = "h" ; then  echo -e "\n\n

This was buggy, and 
			allot \$((bla-\$H)) 
							does the right
thing, so use that.
\n\n"
						else ############

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


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

	L mylabel

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

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


octalout        ( ) { # print a number as an octal string
let val=$1
bstring=
for ushift in 30 27 24 21 18 15 12 9 6 3 0       ;do
        let nybble=$val\>\>$ushift\&7
        case $nybble in
        0) bstring=$bstring"0"                          ;;
        1) bstring=$bstring"1"                          ;;
        2) bstring=$bstring"2"                          ;;
        3) bstring=$bstring"3"                          ;;
        4) bstring=$bstring"4"                          ;;
        5) bstring=$bstring"5"                          ;;
        6) bstring=$bstring"6"                          ;;
        7) bstring=$bstring"7"                          ;;
        esac
done
echo -n $bstring" "
}


page		( ) { # 		internal		=shasm
	if test "$allocated" = "yes"	;then
echo -en "\
\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\
\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" >> $output
				fi
				}



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

declare -i registers


Size		( ) { #		internal			=x86
if test $1 = "1" ;then
size[$source]=1			#
size[$dest]=1			# was [$side]
fi
		}


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


octacode	( ) { # 	internal			=x86

register[$side+${registers[$side]}]=$1
registers[$side]=${registers[$side]}+1
if test ${registers[$side]} -gt 1  ; then
	memside=$side
	MEM=4
fi
if test $numsseen -gt 0  ; then
	memside=$side
	MEM=4
fi

	}


			# modestring builders
segment		( ) { #		internal			=x86
octacode $1
#size 2
mode[$side]=segm
		}


specialC	( ) { #		internal			=x86
octacode $1
mode[$side]=speC
		}


specialD	( ) { #		internal			=x86
octacode $1
mode[$side]=speD
		}


specialT	( ) { #		internal			=x86
octacode $1
mode[$side]=speT
		}


small		( ) { #		internal			=x86
octacode $1
mode[$side]=D
size 1
		}


dualreg 	( ) { #		internal			=x86
octacode $1
mode[$side]=D
size 2
		}


initop		( ) { #		internal			=x86
# wipe op parser state
	unset number register mode size memside MEM index mo
	source=0
	registers[0]=0
	registers[2]=0
	indexi=9  	# Nein! default is no index
	numsseen=0
	side=0			# left=0, right=2?.
	let sides=1
	SC=0
	BAS=5
	}


tokencase	( ) { # 	internal			=x86
case $arg in
to) 	sides=2 ; source=0 ; 	dest=2 ; side=2 ; numsseen=0	;;
A) octacode "0" ;;	C) octacode "1" ;;
D) octacode "2" ;;	B) octacode "3" ;;
SP) octacode "4" ;;	E|BP) octacode "5" ;;
+|@)	mode[$side]="M"	; memside=$side  ;;
SI) octacode "6" ;;	DI) octacode "7" ;;
byte) Size "1" ;;
dual) Size "c" ;;	quad) Size "c" ;;	# do nothing
CS) segment 1	;;	DS)	segment 3	;;
SS) segment 2	;;	ES)	segment 0	;;
FS) segment 4	;;	GS)     segment 5	;;
AL) small 0 ;;	CL) small 1 ;;	DL) small 2 ;;	BL) small 3 ;;
AH) small 4 ;;	CH) small 5 ;;	DH) small 6 ;;	BH) small 7 ;;
AX) dualreg 0 ;; CX) dualreg 1 ;; DX) dualreg 2 ;; BX) dualreg 3 ;;
CR0) specialC 0 ;;	CR2) specialC 2 ;;	CR3) specialC 3 ;;
DR0) specialD 0 ;;	DR1) specialD 1 ;;	DR2) specialD 2 ;;
DR3) specialD 3 ;;	DR6) specialD 6 ;;	DR7) specialD 7 ;;
TR6) specialT 6 ;;	TR7) specialT 7 ;;

"*2^")	let indexi=$side+${registers[$side]}-1
	index=${register[$indexi]}
	memside=$side				# memory side
	wasshifter="yes"
	mode[$side]="M"		;;

with) 	sides=2 ; source=0 ; 	dest=2 ; side=2 ; numsseen=0	;;
from)	sides=2 ; source=2 ; 	dest=0 ; side=2	; numsseen=0	;;
*)		 number[$side+$numsseen]=$arg
		let numsseen=$numsseen+1			;;
  esac		
}


buildmode	( ) { #		internal			=x86
## todo.... figure out that a dest number only is a memref ####

			# accumulate a case switch string 
	modestring=${size[$source]}
	if   test -n "${mode[$source]}"		;then
		modestring=$modestring${mode[$source]}
	elif test "${registers[$source]}" = "2" ;then
		modestring=$modestring"M"
	elif test -n "${number[$source]}"	;then
		modestring=$modestring"I"
	else
		modestring=$modestring"D"
	fi
	if test "$sides" = 2 ;then
		if   test -n "${mode[$dest]}"	;then
			modestring=$modestring${mode[$dest]}
		elif test "${registers[$dest]}" = "2" 	;then
			modestring=$modestring"M"
		else
			modestring=$modestring"D"
		fi
	fi
	}


parse	( ) { #		internal				=x86
initop
for arg in $*			;do
   if test "$wasshifter" = "yes"	;then
		wasshifter="no"
		SC=$arg			# SNAG SC without making it a 
					#	$number[]
	else
		tokencase $arg
	fi
done			 # end args loop, resume reasonable indentation.
buildmode
			}	# end of pmode parse  #######



memneck		( ) { #		internal			=x86
	if test "$cell"	 = 2		;then
		rmodememneck $* 
	else
		pmodememneck $*
	fi
	}


# memory reference modR/M  register/code/memref byte
#		|  |   |   |    |     |     |     |     |
# pmode		| mode | offreg/code  | memref reg/code |
# mode 0 is  offreg or code &
#	single memref register	or
	needSIB=4	# MEM=4  or
#	32 bit displacement	
# mode 1 is offreg or code &
#	byte displ. and reg	or
#	byte displ. and needSIB=4
# mode 2 is offreg or code &
#	32 bit displ. and reg     or
#	32 bit displ. and needSIB=4 
# mode 3 is offreg or code &
#	single memref register, any size , 4=SP 

# SIB is
#	|   |   |    |   |   |   |   |   |
#	| scale |  index reg |  base reg |
baseonly=4 # in index reg slot. 
# 386 Scale may vary without an index. Gas warns on that.  

nobase=5


pmodememneck	( ) { #		internal			=x86

REG=$1	MEM=	MO=

if test "${number[$memside]}" 	;then		# displacement

	if test ${number[$memside]} -lt 0  		;then
		disptest=$((0-${number[$memside]}))
	else
		disptest=${number[$memside]}
	fi
	if test $disptest -lt 128 			;then
		MO=1			# tentatively
	else
		MO=2
	fi
fi

case ${registers[$memside]} in
	0)	MO=0			# overwriting MO=1 or 2
		MEM="5"		;;
	1)	if test $indexi -eq 9	;then	# reg+disp
			MEM=${register[$memside]}
		else			# need SIB		
			MEM=$needSIB
			# SC is known from tokencase
			index=${register[$indexi]}
			BAS=$nobase
		fi	
		if ! test "$MO" ; then		# no displacement
			MO=0
			if test {register[$memside]} = 4	\
			-o {register[$memside]} = 5 		;then
				MO=3	# reg is SP or BP
			fi
		fi
	;;
	2)	MEM=$needSIB
		if test "$indexi" = "9"   ;then
			index=${register[$memside]}
			BAS=${register[$memside+1]} # just pick
		else		# base is index with NOT of low bit
			BAS=${register[$indexi^1]}
			index=${register[$indexi]}
		fi
		if ! test "$MO" ; then		# no displacement
			MO=0
 		fi
	;;
	*) echo " >2 regs on a memside?" ;;		
esac

			## assemble modR/M
ao $MO$REG$MEM
			# SIB
if test "$MEM" = "$needSIB" 			;then 
	if test	$MO -ne 3		;then
		ao $SC$index$BAS
	fi
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
			} # end PROTECTED memneck


rmodememneck	( ) { #		internal			=x86
# takes $source/$dest of memref and the off register/code
## mo and MEM
REG=$1 

if ! test "$memside"					;then	# direct
    MEM=${register[$1]}
    mo=3
fi

if test -n "${number[$memside]}"	             ;then      # displ
        if test ${registers[$memside]} -eq 0    ;then            #no reg
		mo=0
		MEM=6
        elif test "${size[$memside]}" = "1"  ;then	   # regs, byte
           mo=1
	   case ${register[$memside]}${register[$memside+1]} in
           	63|36) MEM=0 ;;
           	73|37) MEM=1 ;;
           	65|56) MEM=2 ;;
           	75|57) MEM=3 ;;
           	 6) MEM=4 ;;
           	 7) MEM=5 ;;
           	 5) MEM=6 ;;
           	 3) MEM=7 ;;
		*) echo `hexout $H`  ": \
		real mode parser is looking for MEM value unsuccessfully.
		Rmode memref register combinations with byte are limited to  
			DI/B, SI/B, DI/BP, SI/BP, B, BP, SI and DI.
		" ;;
	   esac
        else 						  # regs, cell
           mo=2
	   case ${register[$memside]}${register[$memside+1]} in
           	63|36) MEM=0 ;;
           	73|37) MEM=1 ;;
           	65|56) MEM=2 ;;
           	75|57) MEM=3 ;;
           	 6) MEM=4 ;;
           	 7) MEM=5 ;;
           	 5) MEM=6 ;;
           	 3) MEM=7 ;;
		*) echo `hexout $H`  ": \
		real mode parser is looking for MEM value unsuccessfully.
		Rmode memref register combinations with lit. are limited to  
			DI/B, SI/B, DI/BP, SI/BP, B, BP, SI and DI.";;
	   esac
        fi
    else					              # no displ
           mo=0
	   case ${register[$memside]}${register[$memside+1]} in
           	63|36) MEM=0 ;;
           	73|37) MEM=1 ;;
           	65|56) MEM=2 ;;
           	75|57) MEM=3 ;;
           	 6) MEM=4 ;;
           	 7) MEM=5 ;;
           	 3) MEM=7 ;;
		*) echo `hexout $H`  ": \
		real mode parser is looking for MEM value unsuccessfully.
		Rmode memref register combinations without displ. are limited 
		to  	DI/B, SI/B, DI/BP, SI/BP, B, SI and DI.";;
	esac
fi


ao $mo$REG$MEM
if test "$mo" = "1" 	;then   	# 	byte  displacement
	ab ${number[$memside]}
fi
if test "$mo" = "2" 	;then   	# 	cell displacement
	ac ${number[$memside]}
fi					# none

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

		}	###### REAL #################


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


								#( )
#_______________x86_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 ################
        HL
	ab 0x66
	opnote otheroperandsize $*
					fi
					}


otheraddresssize ( ) { #					-x86 prefix
if test "$1" = "h" ; then  echo -e  "\n\n\n\n
following instruction is to be interpreted at the opposite address size
from what the current default is, as per this segment\'s descriptor.\n"
						else ################
        HL
	ab 0x67
	opnote otheraddresssize $*
					fi
					}

								#()
# x86 general instructions					()


=		() { # Clobber. many variants, most freq. insn	-x86 MOV
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel MOV\n
 catch-all. copy. The most prevalent insn. The only access to special
registers.  The fast mem<->A forms may be A= and =A by the time you read
this.

! would be better but it conflicts with Bash.

"
						else ################
        HL
	parse $*
	case $modestring in

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		*)		modenotsupported = 			;;
	esac
	opnote = $*
			fi
			}


=extend		( ) { # copy with sign extension		-x86 MOVSX
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel MOVSX\n
copy, sign-extending the destination.\n\n"
			 			else ################
        HL
	parse $*
	case $modestring in

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

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

		*)		modenotsupported =extend		;;
	esac
	opnote =extend $*
				fi
				}


=0extend	( ) { # copy with zero-extension		-x86 MOVZX
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel MOVZX\n
copy, filling the high-order bits of the destination with zeros. Much
different than a less-than-whole-register copy.\n\n\n"
	 					else ################
        HL
	parse $*
	case $modestring in

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

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

		*)		modenotsupported =0extend		;;

	esac
	opnote =0extend $*
				fi
				}


1+		() { #	increment				-x86 INC
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel INC\n
add 1 to whatever. Effects flags.\n\n"
						else ################
        HL
	parse $*
	case $modestring in

		1*)		ab 0xfe
				memneck 0				;;

		M)		ab 0xff
				memneck 0				;;

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

		*)		modenotsupported increment		;;
	esac
	opnote 1+ $*
			fi
			}


1-		() { #	minus one, decrement			-x86 DEC
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel DEC\n
decrement.	2 clocks.\n\n\n"
						else ################
        HL
	parse $*
	case $modestring in

		1*)		ab 0xfe
				memneck 1				;;

		M)		ab 0xff
				memneck 1				;;

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

		*)		modenotsupported decrement	
				echo $H	;;
	esac
	opnote 1- $*
			fi
			}


/		() { # divide. 38 clocks.			-x86 DIV
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel DIV\n
 38 clocks, no early-out. Divides D:A by arg, modulo in D, quotient in A,
I think."
						else ################
        HL
	parse $*
	ab 0xf7
	ao 36${register[$source]}
	opnote / $*
					fi
					}


+		() { # add					-x86 ADD
if test "$1" = "h" ; then  echo -e  "\n\n
 Add without including the carry (flag) bit. \n	
See also +byte and +A.
"
						else ################
        HL
	parse $*
	case $modestring in

		ID)		ab 0x81
				ao 30${register[2]}
				ac ${number[$source]}			;;
									
		DD)		ab 0x01
				ao 3${register[0]}${register[2]}	;;

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

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

		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 support only at this point.

"
						else ############
        HL
	parse $*
		ab 5 
		aq ${number[0]}
		opnote +A $*
				fi
				}


+byte		() { # add immediate byte			-x86 ADD
if test "$1" = "h" ; then  echo -e  "\n\n

 Add without including the carry (flag) bit, a byte to be sign-extended
and a register or memory argument of any size. If the value added to is
a page-aligned array you have a fast ring index, yes?

"
						else ############
        HL
	parse $*
		ab 0x83
		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 ################
        HL
	parse $*
	case $modestring in

		ID)		ab 0x81
				ao 32${register[2]}
				ac ${number[$source]}			;;
									
		DD)		ab 0x11
				ao 3${register[0]}${register[2]}	;;

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

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

		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 +carry $*
				fi
				}


+carryA		( ) { # add immediate to A, 32 bit only		-x86 ADC
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel SUB\n
add with carry immediate to A, 32 bit only."
						else ################
        HL
		ab 0x15 
		aq ${number[0]}
		opnote +carryA $*
					fi
					}



-		() { # subtract.				-x86 SUB
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel SUB\n
Subtract without including borrow (carry) bit.\n\n"
						else ################
        HL
	parse $*
	case $modestring in

		ID)		ab 0x81
				ao 35${register[2]}
				ac ${number[$source]}			;;
									
		DD)		ab 0x29
				ao 3${register[0]}${register[2]}	;;

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

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

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

		ID)	if test "${register[$dest]}" = 0 ; then
					ab 0x3d
					ac ${number[0]}
			else
					ab 0x81
					ao 37${register[2]}
					ac ${number[$source]}
			fi						;;

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

		DM)		ab 0x39
				memneck ${register[$source]}		;;

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

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

		ID)		ab 0x81
				ao 33${register[2]}
				ac ${number[$source]}			;;
									
		DD)		ab 0x19
				ao 3${register[0]}${register[2]}	;;

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

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

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

        HL
	parse $*
	case "$modestring" in

		ID)		ab 0x81
				ao 34${register[2]}
				ac ${number[$source]}			;;
									
		DD)		ab 0x21
				ao 3${register[0]}${register[2]}	;;

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

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

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

		ID)	if test "${register[$dest]}" = 0 ; then
					ab 0xa9
					ac ${number[0]}
			else
					ab 0xf7
					ao 30${register[2]}
					ac ${number[$source]}
			fi						;;
									
		DD)		ab 0x85
				ao 3${register[0]}${register[2]}	;;

		DM)		ab 0x85
				memneck ${register[$source]}		;;

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

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

		M)		
				ab 0xf7
		                memneck 2 				;;

		1M)
				ab 0xf6
				memneck 2				;;

		1D)		ab 0xf6
				ao 32${register[0]}			;;

		*)		modenotsupported not			;;

	esac
	opnote NOT $*
			fi
			}



OR		() { # Boolean bitwise OR			-x86   =
if test "$1" = "h" ; then  echo -e  "\n\n
Boolean bitwise OR. AKA "inclusive OR". If either source bit, A OR B, is
1, then result is 1.

one-bit results (truth table) with input bits A and B

			    B
		   	1	0
		  _|_______________
		   |
		 0 |    1	0
	     A	   |
		 1 |	1	1

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

        HL
	parse $*
	case $modestring in

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

		ID)	if test "${register[$dest]}" = 0 ; then
					ab 0x0d
					ac ${number[0]}
			else
					ab 0x81
					ao 31${register[2]}
					ac ${number[$source]}
			fi						;;
									
		DD)		ab 0x09
				ao 3${register[0]}${register[2]}	;;

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

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

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

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

		1MD)		ab 0x0a
				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 ################

        HL
	parse $*
	case $modestring in

		ID)	if test "${register[$dest]}" = 0 ; then
					ab 0x35
					ac ${number[0]}
			else
					ab 0x81
					ao 36${register[2]}
					ac ${number[$source]}
			fi						;;
									
		DD)		ab 0x31
				ao 3${register[0]}${register[2]}	;;

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

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

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

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

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


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


		IM)		ab 0x0f 0xba
				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 ################
        HL
	parse $*
	case $modestring in

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

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

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

		IM)		ab 0x0f 0xba
				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 on occurance of the return
(Intel RET) instruction. Frames vary widely by TYPE of call on 386+. There
are intersegment jumps, which are selectors defining gates of various
types in (32 bit) protected mode. call or similar is also known as jsr or
gosub on other machines. The variants of call usually require a FAR
syntactic spamatazoan in other assemblers. shasm syntaxes for the more
mutated forms of call are...

Call intersegment to full pointer given, shasm syntax...
		  segment       offset
	call dual 0xxxx to quad 0xxxxx

LAAETTR (gas doesn't do segments explicitly either, IIRC. Nor do most
other CPUs, BTW.)


\n\n"
		 			else ################
        HL
	parse $*
	case $modestring in

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

		D)		ab 0xff
				ao 32$register				;;

		*)		modenotsupported call 			;;

	esac
	opnote call $*
			fi
			}


compares	( ) { # plural -test, used by match		-x86 CMPSx
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel CMPSx

use \"match\". This isn't useful otherwise that I can see.  :o) 

Any argument to \"compares\" is taken as \"use bytes\". "
					else ################
        HL
	parse $*
	if test "$1" 	 	;then
		ab 0xa6		# if tH was a size spec it was byte
	else
		ab 0xa7
	fi
	opnote compares $*
				fi
				}



clearcarry	( ) { # set carry bit/flag to 0			-x86 CLC
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel CLC\n
unset the carry flag. Make it 0.\n\n\n"
					else ################
        HL
	ab 0xf8
	opnote clearcarry $*
						fi
						}


decreasing	( ) { # tell plurals to decr. C. Not the default.-x86 STD
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel 	STD\n
Set memory segment loop (string)  operations direction flag to
towards-lower-addresses. Segment ops then will traverse the segments
high-to-low.  \n"
					else ################
        HL
	ab 0xfd
	opnote decreasing $*
						fi
						}


downroll	() { # down-significance bit-rotate		-x86 ROR
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
						Intel ROR\n
down-significance roll, rotate. Roll amount is source if immediate."
					else ################
        HL
	parse $*
	case $modestring in

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

		IM)		ab 0xc1
				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 ################
        HL
	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. TH are two. This is the one that fills the
high side vacated bits with 0.  The low-order bit is shifted into the
carry flag. The other preserves the sign. That (Intel SAR) isn't present
in osimpa as I write this.\n"
					else ################
        HL
	parse $*
	case $modestring in

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

		IM)		ab 0xc1
				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 ################
        HL
	ab 0x99
	opnote extend $*
				fi
				}


fetches		() { # @ SI to A, scaled in/decr. SI, decr. C	-x86 LODSx
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel LODSx
 fetches loads the A register with the memory byte, word, or doubleword at
the location pointed to by the SI register. After the transfer is made,
the SI register is automatically advanced. If the direction flag is 0
(increasing was executed, which is the default state), the source index
increments. fetches doesn't decrement C. fetches doesn't make sense with
repeating and friends, but is useful if alone or in a loop of some kind,
for checksums, for example. See sum, xsum.

Any argument to fetches is taken as a directive to use bytes rather than
cells. "
					else #######################
        HL
	parse $*
	if test "$1" 	 	;then
		ab 0xac		# if there was a size spec it was byte
	else
		ab 0xad
	fi
	opnote fetches $*
				fi
				}


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

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

		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 ################
        HL
	ab 0x9f
	opnote flags $*
				fi
				}



halt		( ) { # cool the CPU until an interrupt 	-x86 HLT
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel HLT\n
halt processor until next hardware interrupt. Be nice to your CPU.
This is what the Linux \"idle task\" does.\n\n

This probably wants to be at the start of the top loop of a kernel H3sm.
"
						else ################
        HL
	ab 0xf4
	opnote halt $*
						fi
						}


increasing	( ) { # set plurals to incr C. The default.	-x86 CLD
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel CLD\n
Setstring operations direction flag to toward-higher-addresses\n\n"
						else ################
        HL
	ab 0xfc
	opnote increasing $*
					fi
					}


invertbit	( ) { #	bit test and complement			-x86 BTC
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel BTC\n
Save specified bit of operand into carry flag and complement it in operand.
\n\n"
						else ################
        HL
	parse $*
	case $modestring in

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

		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 ################
        HL
	ab 0xf5
	opnote invertcarry $*
					fi
					}

 


jump		() { # unconditional branch			-x86 JMP
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel JMP\n

 Partial support here. Hand-roll your far jumps.
Unconditional branch. Various addressing modes.
 \n\n\n"
						else ################
        HL
	parse $*
	case $modestring in

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

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

					# register absolute
		D)		ab 0xff
				memneck 4
									;;
					# register and literal offset
		M)		ab 0xff
				memneck 4
				ac $number				;;

		*)		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 does 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 ################
        HL
	parse $*
	ab 0x8d
	memneck ${register[$dest]}
	opnote address $*
					fi
					}


lookup		() { # byte lookup for e.g. ASCII->EBCDIC	-x86 XLATB
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\tIntel XLATB\n
 
 Set AL to memory byte DS:[BX + unsigned AL]. One-byte instruction.

In other words, exchange AL for the byte in the table at BX that AL was
the index to.

This seems to be geared for rapid conversions such as between ASCII and
EBCDIC. The index value is replaced by the indexed value. 

5 clocks. 

\n"
						else ################
        HL
	ab 0xd7
	opnote lookup $*
				fi
				}


ls1bit		() { # bit number of least significant 1-bit	-x86 BSF
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel BSF\n
Find least significant ON-bit. 10 clocks +. Result is the number
of leading 0 bits.

BSF scans the bits in the second word or doubleword operand starting with
bit 0. The ZF flag is cleared if the bits are all 0; otherwise, the ZF flag
is set and the destination register is loaded with the bit index of the
first set bit.


\n\n"
						else ################
        HL
	parse $*
	ab 0x0f 0xbc
	memneck ${register[$dest]}
	opnote ls1bit $*
						fi
						}

ms1bit		() { # bit number of most significant 1-bit	-x86 BSR
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel BSR\n
0F	r32,r/m32  10+3n     Bit scan reverse on r/m cell
Find most significant ON-bit. 10 + \(3 x offbits\) clocks.
Flags effected:  Zero\n\n"
						else ################

        HL
	ab 0x0f 0xbd
	parse $*
	memneck ${register[$dest]}
	opnote ms1bit $*
						fi
						}


multiply	() { # arg * A --> D:A				-x86 MUL
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel MUL\n
9 to 41 clocks. Early-out algorithm, unlike division.

source * A to D:A
\n\n"
						else ################

        HL
	parse $*
	case $modestring in

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

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

Two's complement negate, 2 or 6 clocks. simple NOT, then increment.\n\n"
					else ################
        HL
	parse $*
	case $modestring in

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

		M)
				ab 0xf7
		                memneck 3 				;;

		1M)
				ab 0xf6
				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 ################

        HL
	ab 0x90
	opnote nop $*
				fi
				}


recieve 	( ) { # x86 "IO port" instruction		-x86 IN
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel IN\n
Input from port

Takes a byte/dual/quad arg followed by an optional port IO address. The
port address will be compembled as a byte. Use DX to specify a high port.

	recieve dual [ 0x20 ]

The actual code is the same for dual or quad, and a port in EDX is
effectively 16 bits. The recieved byte or cell will be in A.

\n\n\n\n"
					else ################
        HL
	if test  "$1" = "byte"		;then
		if test $2		;then			
			ab 0xe4 $2
		else
			ab 0xec
		fi
	else
               if test $2              ;then
			ab 0xe5  $2
		else
			ab 0xed
		fi			
	fi
	opnote recieve $*
fi
}




return		() { # return from a near call			-x86 RET
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel RET\n

 Return from a call. An immediate argument assembled the frame-dropping
form. The immediate value is 16 bits to drop the stack (add to SP) by that
many bytes. osimplay entrance procedures use that form.


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

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

		*)		ab 0xc3					;;
	esac
	opnote return $*
	HL
				fi
				}


send		( ) { # x86 "IO port" instruction		-x86 OUT
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel OUT\n
 Output to a port. immediate byte or contents of DX is port #. Datum is
always in A, which is byte or cell.


\n\n"
					else ################
        HL
	if test $1 = "byte"	;then
		if test $2	;then
			ab 0xe6 $2
		else
			ab 0xee
		fi
	else
		ab 0xef	
	fi
	opnote send $*
					fi
					}


setcarry	( ) { # set carry flag/bit to 1			-x86 STC
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel STC\n
assert carry=true, 1.\n\n"
					else ################
        HL
	ab 0xf9
	opnote setcarry $*
				fi
				}


setflags	() { # copy AH to the FLAGS register-half	-x86 SAHF
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel SAHF\n
copy AH to the FLAGS register-half.\n\n"
					else ################
        HL
	ab 0x9e
	opnote setflags $*
					fi
					}


signedmultiply	() { # partial					-x86 IMUL
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel IMUL\n

these you'll have to roll by hand

6B  /r ib   IMUL r16,r/m16,immbyte    9-14/12-17  word register  r/m16 *
                                               sign-extended immediate byte

69  /r ic   IMUL r16,r/m16,immcell   9-22/12-25  word register  r/m16 *
                                               immediate word

from 386INTEL.TXT
IMUL has three variations:

  3.  A three-operand form; two are source and one is the destination
      operand. One of the source operands is an immediate value stored in
      the instruction; the second may be in memory or in any general
      register. The product may be stored in any general register. The
      immediate operand is treated as signed. If the immediate operand is a
      byte, the processor automatically sign-extends it to the size of the
      second operand before performing the multiplication.

3 distinct arguments. Gee I hate it when that happens. Use a directive to
tag the immediate value on the end of the thing. This isn't worth
reworking the parser. This thing is hideous and only about as fast as
floats anyway.
\n\n"
					else ################

        HL
	parse $*
	case "$modestring" in

		*)		modenotsupported signedmultiply		;;

	esac
	opnote signedmultiply $*
					fi #nal
					}


signeddivide	() { #						-x86 IDIV
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel IDIV\n
19 to 43 clocks. No early-out.	


F7 /7       IDIV EAX,r/m32     43       Signed divide EDX:EAX by DWORD
                                        byte (EAX=Quo, EDX=Rem)

Operation

temp ^[ dividend / divisor;
IF temp does not fit in quotient
THEN Interrupt 0;
ELSE
   quotient ^[ temp;
   remainder ^[ dividend MOD (r/m);
FI;
	
				"
					else ################

        HL
	parse $*
	case $modestring in
#		11*)		ab 0xf6
#				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. swap locks the
bus during the swap, and Plan 9 therefor uses it for test-and-set. I
believe this is only an issue with SMP. All such instructions are atomic
vis-a-vis one CPU.

\n\n\n"
					else ################
        HL
	parse $*
	case $modestring in

		D)		ab $((0x90+${register[0]}))		;;
				# and yes, "swap A"  is in fact NOP  ;o)  

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

		DM)		ab 0x87
				memneck ${register[$source]}		;;

		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 ################
        HL
	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 ################
        HL
	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. Right shift, in the usual parlance. Zeros roll
in on low-significance end, bits are lost on the high-significance end.
Several forms take the shift amount from C implicitly. As of this writing,
I don't think those will get assembled more economically. FIX THIS
Shifts rock.

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

        HL
	parse $*
	case $modestring in

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

		IM)		ab 0xc1
				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, THer's MORE!(TM) I folded the variants of the Intel LOOP instruction
in here also. LOOP isn't a loop at all; it does forward branches just
fine. It is therefor  when C-1  in osimplay, to imply that it performs the
decrement. There's also when C-1&zero and C-1&nonzero, which both also
decrement C before the test, and when C=0 which does NOT decrement C.
These latter when's only do short branches, but I haven't checked for the
branch width in the assembler. There are some thorny recursion issues with
figuring branch widths. The branches based on flags, i.e. not on C, can be
a byte or a cell. Cell (quad) is the default, and there's a \"short\"
token you can insert. I _COULD_ suss out backward branch widths without
that, but I haven't at this writing. Patches welcome. It seems the best
thing you can do for branch performance is rig your branches so the branch
is usually not taken, which is normally a bit better than a 2:1 win."

		else	 #################
if test "$pass" = "2"		;then
	HL

	whenopnote="when "$*
fi
	for branchtarg in $*				;do
		:
	done				# $branchtarg  is now branch target

branchsize=
	opbase=0x70
	let bla_=$#-1
	while test $bla_ != "0"				;do
		case $1 in		# prevalence order roughly
			not)		let opbase=$opbase+1		;;

			zero)		let opbase=$opbase+4		;;

			sign)		let opbase=$opbase+8		;;
		
			short)		branchsize=short		;;
					
			carry)		let opbase=$opbase+2		;;

			overflow)	let opbase=$opbase		;;

			C-1)		let opbase=0xe2	
					branchsize=short		;;

			C=0)		let opbase=0xe3	
					branchsize=short		;;

			C-1\&zero)	let opbase=0xe1	
					branchsize=short		;;

			C-1\&nonzero)	let opbase=0xe0	
					branchsize=short		;;
			
			parity)		let opbase=$opbase+10		;;

			skew)		let opbase=$opbase+12		;;

			*)		echo $1 " 
					isn't a valid \"when\" qualifier."
									;;
		esac
		shift
		let bla_=$bla_-1
	done
	if  test $branchsize	;then
	#small
		ab $opbase $(($branchtarg-$H-2))
		if test $(($branchtarg-$H-2)) -gt 120 \
		-o $(($branchtarg-$H-2)) -lt -120 ; then
			echo "branchsize issue, a short branch may not be"
			echo $H
		fi
	else
	#BIG
		ab 0x0f $(($opbase+16))
		ac $(($branchtarg-$H-$cell))
	fi
if test "$pass" = "2"		;then
	echo -en "  "$whenopnote 	>> $listing
fi
						fi
						}


widedownshift	() { # partial					-x86 SHRD
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel SHRD\n
0F AC    r/m32,r32,imm8        r/m32 gets SHR of r/m32 concatenatedwithr32
0F AD    r/m32,r32,CL          r/m32 gets SHR of r/m32 concatenated withr32

down-significance bitshift of a composite operand made of ??

This is another 3-op. Roll that one by hand. 

This is used by  scale  , which made a bit of a splash in comp.lang.forth.

\n\n"
					else ################
        HL
	parse $*
	ab 0x0f 0xad
	memneck ${register[$source]}
	opnote widedownshift $*
					fi
					}


wideupshift	() { # partial					-x86 SHLD
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel SHLD\n
0F  A4   SHLD r/m16,r16,imm8       r/m16 gets SHL of r/m16concatenated
                                       with r16
0F  A4   SHLD r/m32,r32,imm8       r/m32 gets SHL of r/m32concatenated
                                       with r32
0F  A5   SHLD r/m16,r16,CL      r/m16 gets SHL of r/m16concatenated

partial support. 

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

        HL
	ab 0x0f 0xa5
	parse $*
	memneck ${register[$source]}
	opnote wideupshift $*
					fi
					}


within		() { # check value against 2 bounding values	-x86 BOUND
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel BOUND\n
 62/r  BOUND r32,m32&32  10
 Check if r32 is within bounds, (passes test). Bounds are adjacent
32 bit values in memory. Failure causes an exception,
Interrupt 5 if the bounds test fails.
C is notorious for letting you walk off the end of an array, but I don't
know that this is the appropriate cure.
\n\n"
					else ################

        HL
	parse $*
	ab 0x62
	memneck ${register[$dest]}
	opnote within $*
					fi
					}


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ system ops		( )


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

        HL
	ab 0x2e
	opnote CS $*
				fi
				}


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

        HL
	ab 0x36
	opnote SS $*
				fi
				}


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

        HL
	ab 0x3e
	opnote DS $*
				fi
				}


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

        HL
	ab 0x26
	opnote ES $*
				fi
				}


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

       HL
	ab 0x64
	opnote FS $*
				fi
				}


GS		( ) { #						-x86 prefix
if test "$1" = "h" ; then  echo -e  "\n \n
 Following instruction is to use segment GS. Prefix. FS and GS will 
probably get the NULL selector in Ha3sm.

\n"
					else ################
        HL
	ab 0x65
	opnote GS $*
				fi
				}


lock		( ) { #						-x86 prefix
if test "$1" = "h" ; then  echo -e  "\n\nIntel LOCK\n
 SMP instruction atomicity extender. exchange does a LOCK anyway, which is 
what Plan9 uses. A   pull SS   also locks the next instruction, usually
 pull SP. 
\n"
else
	HL
	ab 0xf0
	opnote lock $*
				fi
				}


clearswitched	( ) { #			 			-386
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel CLTS\n
clear the task-switched flag in EFLAGS. Ha3sm doesn't use the 386
task-switch facilities, BTW. Most 386 unices do, I think.\n\n"
					else ################

        HL
	ab 0x0f 6
	opnote clearswitched $*
					fi
					}


frame		( ) { # Pascal in hardware. Know a use?		-x86 = wENTER
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel ENTER\n

Pascal in hardware. Know a use?

frame is a 386 complex instruction for creating a lexical level frame for
lexical block languages like Pascal. It fetches a list of pointers and
pushes thier values onto the stack.
See also: dropframe
frame  ( 16bit framesize, 8bit lexical levels)
This is the psuedocode description of the thing based on INTEL386.TXT
...................................................................
{
level = level MOD 32		// level is byte 4 of instr encoding
				// MOD 32 is AND 11111b
Push BP
frame-ptr = SP			// frame-ptr is a CPU-internal temp var
if level > 0
	{
	for (i =  1 TO (level - 1))
		{
         	BP = BP - 4
         	Push value _at_ BP
   		}
   	Push frame-ptr
	}
BP = frame-ptr				// BP is now old
SP = SP - ZeroExtend(First operand)
}
...................................................................
 frame can take up to 139 clocks, depending on the levels argument. A
task-switch is about 300. The most interesting things I see about frame is
that it uses two internal variables that aren't registers, and that it
does a looping dereference over an array of up to 31 pointers. That is, it
collects dispersed values. It does all this atomically, which is important
when molesting the return stack. frame/dropframe is what gives BP it's
framepointer designation. They are the only instructions that use BP
implicitly. frame would need it's own syntax for it's arguments, and I'll
probably never use the thing, so I just coopt the shasm source/dest for
levels/size. In shasm syntax levels is source, frame size is dest, i.e.
source and dest don't mean what they usually do e.g.

             levels       size 
	frame 20     to   3    \n\n"
 					else ################
        HL
	parse $*
	ab 0xc8
	ad ${number[$source]}
	ab ${number[$dest]}
	opnote enter $*
					fi
					}


dismiss		( ) { #						-x86 IRET
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel IRET\n
 Interrupt return. Various stack effects per system state. A dismiss frame
is (E)IP, CS and (E)FLAGS, EIP at TOS. Some CPU protection exceptions push
an error code also.

\n"
					else ################
        HL
	ab 0xcf
	opnote dismiss $*
	HL
	HL
				fi
				}


farSS		( ) { #						-386
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel LSS\n
Load SS:r32 with pointer from memory\n"
					else ################
        HL
	parse $*
	ab 0x0f 0xb2
	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 ################
        HL
	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 ################
        HL
	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 ################

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

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

#You set it, right?
#
#GDT		( ) { #						-386
#if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel SGDT\n
#store contents of Global Descriptor Table Register to memory at physical
#address. The obverse of this is crucial to protected mode.
#\n"
#					else ################
#
 #       HL
#	parse $*
#	ab 0x0f 0x01 
#	if test "$cell" = 2 ; then
#		ab 6
#	else
#		ab 5
#	fi
#	ac ${number[$source]}		# physical address
#	opnote GDT $*
#				fi
#				}
#
#
#IDT		( ) { #						-386
#if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel SIDT\n
#store contents of Interrupt Descriptor Table Register to memory at
#physical address. The obverse of this is crucial to protected mode.
#\n"
#					else ################
#
 #       HL
##	parse $*
#	ab 0x0f 1  
#	if test "$cell" = 2 ; then
#		ab 0x0e
#	else
#		ab 0x0d
#	fi
#	ac ${number[$source]}		# physical address
#	opnote IDT $*
#				fi
#				}
#
#
#LDT		( ) { #						-386
#if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel SLDT\n
#store Local Descriptor Table Register to memory physical address.
#
#If you use the 386 task register stuff and TSSs then I believe the deal
#is that each task has/needs an LDT.
#\n\n"
#					else ################
 #       HL
#	parse $*
#	ab 0x0f 0 
#	if test "$cell" = 2 ; then
#		ab 6
#	else
#		ab 5
#	fi
#	ac ${number[$source]}		# physical address
#	opnote LDT $*
#				fi
#				}




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

\n\n"
					else ################
        HL
	ab 0xfb
	opnote IRQson $*
				fi
				}



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

\n\n"
					else ################
        HL
	ab 0xfa
	opnote IRQsoff $*
				fi
				}



dropframe		( ) { #					-x86 LEAVE  
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\tIntel LEAVE\n
 De-allocate a Pascal-style module frame. Enter and leave are what makes
BP/ebp the \"frame pointer\". see also: enter. I don't see these as real
useful, but \"enter\" does a lot of stuff."
					else ################

        HL
	ab 0xc9
	opnote dropframe $*
				fi
				}

limit		( ) { #						-x86 LSL
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\tIntel LSL\n
load the limit value from a segment descriptor. \n"
					else ################

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


IRQsoff	( ) { #						-x86 CLI
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel CLI\n
 Disable external hardware interrupts to the CPU.
Useful at boot time maybe and for profound state-changes like
process-switches.\n\n"
					else ################

        HL
	ab 0xfa
	opnote IRQSoff  $*
					fi
					}


overflowtrap	( ) { #						-x86 INTO
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel INTO\n
cause invocation of a trap handler IF overflow bit is set.\n\n"
					else ################

        HL
	ab 0xce
	opnote overflowtrap $*
					fi
					}


readable	( ) { #						-386 VERR
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel VERR\n
Set zero flag to true if segment of given selector can be read by
this process.
\n\n"
					else ################

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

        HL
	ab 0x61
	opnote pullcore $*
				fi
				}


pull		( ) { #	partial	# unstack			-386 POP
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel POP\n
Copy top of stack into operand, adjusting stack pointer SP accordingly.

pop, to my mind, means remove and lose a stack item. Hence pull.

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

        HL
	parse $*
	case $modestring in

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

		M)		ab 0x8f
				memneck 0				;;

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

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

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

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

			elif test ${register[$source]} = "5" ;then	# GS
				ab 0x0f 0xa9
			else
		        	echo -e "\n\npush doesn't support
				" $modestring " mode. "
			fi						;;

		*)		modenotsupported pull			;;

	esac
	opnote pull $*
				fi
				}


pullflags	( ) { #						-386 POPF
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel POPF\n
copy top of stack into flags reg, adjusting stack pointer SP
accordingly.\n\n"
					else ################

        HL
	ab 0x9d
	opnote pullflags $*
				fi
				}


pushcore	( ) { #						-386 PUSHA
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel PUSHA\n
copy the eight main regs onto the stack, adjusting stack pointer SP
accordingly. 

This takes 18 clocks, and pullcore takes 24. That's 5.25 clocks per reg,
or 6 not counting SP. A push/pull of one reg takes 6. (2 for push, 4 for 
pull.)

SO, piecemeal IS worth it if you don't need 7 registers.

Also of note, otheroperandsize will make this push all 8 at the opposite
modesize.

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

        HL
	ab 0x60
	opnote pushcore $*
				fi
				}


pushflags	( ) { #						-386 PUSHF
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel PUSHF\n
copy FLAGS onto the top of stack, adjusting stack pointer SP
accordingly.\n\n"
					else ################

        HL
	ab 0x9c
	opnote pushflags $*
					fi
					}


push		( ) { #						-386 PUSH
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel PUSH\n
copy operand onto top of stack, adjusting stack pointer SP
accordingly. See pushcore also for timings.\n\n"
					else ################

        HL
	parse $*
	case $modestring in

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

		M)		ab 0xff 
				memneck  6				;;


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

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

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

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

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

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

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

			elif test ${register[$source]} = "5" ;then	# GS
				ab 0x0f 0xa8
			else
			        echo -e "\n\npush doesn't support
				" $modestring " mode. "
			fi						;;

		*)		modenotsupported push 			;;

	esac
	opnote push $*
				fi
				}


recieves	( ) { #						-x86 INSB
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel INSB\n
Input cells/bytes from port DX into ES:DI. Doing this with \"repeating\"
is too fast for many external devices. Any arg means use bytes,

e.g.
	= 0x220 to D
	recieves bytes		"
					else ################

        HL
	if test -n "$1"		;then
			ab 0x6c
	else
			 ab 0x6d
	fi
	opnote recieves $*
					fi
					}


sends		( ) { #						-x86 OUTS
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel OUTS\n
Send bytes/cells to an x86 IO port address.\n\n"
					else ################
        HL
	if test -n ${size[$source]}	;then
		ab 0x6e		# if there was a size spec it was byte
	else
		ab 0x6f
	fi
					fi
					}


submit		( ) { #	trap to supervisor			-x86 INT
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel INT\n
see also: trapoverflow, debug, Linux version of osimplay\n\n"

							else ################
        HL
	ab 0xcd $1
	opnote submit $*
					fi
					}

returnfar	( ) { #	 					-x86 RET/RETF
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel RET(F) AT&T lret\n
Return from a far (intersegment) call. \n\n"
							else ################
        HL
	ab 0xcb
	opnote returnfar $*
					fi
					}


priviledge	( ) { #						-386
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel ARPL\n
Adjust requested priviledge level  of r/m16 to not less than RPL of r16\n\n"
							else  ################
        HL
	ab 0x63
	parse $*
	memneck ${register[$source]}
	opnote priviledge $*
					fi
					}


loadmachinestatusdual ( ) { #					-286
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel LMSW\n
286 control register shortcut, 16 bit. Deprecated, but using CR0 explicitly
musses some other register, which is usuallt a problem when switching modes.

\n\n"
							else ################
        HL
	ab 0x0f 1
	parse $*
	memneck 6
	opnote loadmachinestatusdual $*
					fi
					}

# useless.
#				#		Got milk?
#storemachinestatusdual ( ) { #					-286
#if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel SMSW\n
#Store machine status dual to EA   dual#
#
#Legacy 286 thing. Can save a byte or two on a bootsector.\n\n"
#							else ################
 #       HL
#	parse $*
#	# check possible
#	ab 0x0f 1
#	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 ################
        HL
	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 ################
        HL
	ab 0xce
	opnote trapoverflow $*
					fi
					}


rights		( ) { #						-x86 LAR
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel LAR\n

r16 becomes r/m16 masked by FF00	\n\n"
							else ################
        HL
	ab 0x0f 2
	parse $*
	memneck ${register[$dest]}
	opnote rights $*
					fi
					}


setGDT		( ) { #						-386
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel LGDT\n
 Load pointer at memory operand into Global Descriptor Table Register.
This and setIDT are the only instructions that always interpret an address
as physical, since they are the trunk of the memory protection
scheme.

\n\n"
							else ################
        HL
	parse $*
	ab 0x0f 01 
	if test "$cell" == 2 ; then 
		ab 0x16
	else 
		ab 0x15
	fi
	ac ${number[$source]}
	opnote setGDT $*
					fi
					}


setIDT		( ) { #						-386
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel LIDT\n
Load pointer at memory operand into Interrupt Descriptor Table Register.
This and setGDT are the only instructions that always interpret an address
as physical, since they are the trunk of the memory protection scheme.

\n\n"
							else ################
        HL
	ab 0x0f 0x01 
	parse $*
	if test "$cell" == 2 ; then 
		ab 0x1e
	else 
		ab 0x1d
	fi
	ac ${number[$source]}
	opnote setIDT $*
					fi
					}


setLDT		( ) { #						-386
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel LLDT\n
Load pointer at memory operand into Local Descriptor Table Register.\n\n"
							else ################
        HL
	ab 0x0f 0x00 
	if test "$cell" == 2 ; then 
		ab 0x16
	else 
		ab 0x15
	fi
	parse $*
	ac ${number[$source]}
	opnote setLDT $*
					fi
					}

# You loaded it, right?
#savetask	( ) { #						-386
#if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel STR\n
# Load task register into EA dual. Ha3sm won't use the 386+ task handling
#facilities. Most 386 unices do I think.\n\n "
#						else ################
 #       HL
#	ab 0x0f 0x00
#	parse $*
#	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 ################
        HL
	ab 0x0f 0
	parse $*
	memneck 5
	opnote writeable $*
					fi
					}

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


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 named binary and listing in 
the current working directory."
					else ############################
	cell=4
	initop		# SUSPECT	
	let textinstance=0
	H=0
	: > $output
	pass=1
	. $1
        cell=4
        initop               # SUSPECT
        let textinstance=0
        H=0
        : > $output
        pass=1
        . $1
	let textinstance=0
	let lastH=$H
	H=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

# 	In C you hear a lot about "tell the compiler...". Here
#   		the assembler state is analagous.
#	If you want to assemble things piecemeal set $pass by hand.

# an args checker routine would be handy

beam		() { # Fill a range of an xray with a jump address
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
Fill in a range in an xray execution array with calls to a word.

	beam routine_address <start> <end>

To save myself some hassle, <end> is required, but may be equal to start for
single-wave beams (a wave is one xray section entry). Several beams
between xray and yarx specify the code addresses in an xray execution
array. Each beam spec overwrites previous ones, if any, in the assembly
state of the xray. Thus to set the array default you do the default first
over the whole xray. When yarx happens it asserts the accumulated specs in
the assembly output. A single xray entry is an address and a hike for the
routine tied to that array index. Indexing is from 0, so max end is
size-1. That is, start and end are integers. See yarx h for more. "
						else ################
	if test "$pass" = "2"					;then
		bi=$2
		while test $bi -le $3			;do
#			beams[$bi]=$1-$xray_H
			beams[$bi]=$1
			let bi=$bi+1
		done
	fi
					fi
					}


cell		() { # name/allot a cell-size storage location
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
e.g.
	cell argc

makes room and a label for a global cell-size variable. Doesn't align.
Usually used after \"heap\" (ELF thing).

"
						else    ###############
	for item in $*	
	do
		L $item
		allot $cell
	done
				fi
				}


copies		() { # plural, range-to-range copy, handles overlaps
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t

 Assemble code to copy a range of memory at SI of length C*size to DI. The
copy proceeds in whatever direction DF is, usually increasing. If you give
it an argument it copies bytes at a time, rather than cells. C is
decremented per copy, not bytes. SI and DI are also in/decremented
accordingly. DF becomes an issue if source and destination ranges overlap.

copies is use-either. It is the same opcode in use16 or use32.

 "	
 else

	HL
	ab 0xf3		
	opnote repeating
	copy $1
fi
			}


copy		( ) { # one "copies". @ SI to @ DI		-x86 MOVSD
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel MOVSD
 
 Move data @ SI to @ DI. 7 clocks per datum. SI and DI cannot be offset.
SI and DI are incremented or decremented in parallel by the size of a
datum. \"copies\" is usually used with \"repeating\", in which case it
will block-move C data items. See copies. copy takes one optional
argument, anything, to specify that data are in bytes, so I usually do
something cute like
			copy byte
						where \"of\" is the
argument and the \"bytes\" is in effect a comment.

See also: \"copies\".

 copy is interesting because it is an atomic memory-to-memory move, and the
only example of such a move that comes to mind on x86. That is, x86 doesn't 
have    add mem, mem    as a stock addressing mode, as many CISCs do.

The mems get you, of course. copy is 7 clocks. It isn't guaranteed to lock
the bus though, like exchange is, which matters on SMP. I'm also intrigued
because copy uses the x86 Phantom Register they don't give you explicit
access to. maybe it's the icache or something.

copies is use-either. It is the same opcode in use16 or use32.


 \n\n\n"
					else ################
        HL
	if test "$1" 	 	;then
		ab 0xa4		# if there was any argument copy bytes
	else
		ab 0xa5
	fi
	opnote copy $*
			fi
			}


clump		() { # C struct() kinda, data associations namer
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
 clump creates a set of related offset names.  This sort of thing is
popular for associating arbitrary data items into groups. The data
associations can themselves be useful information. Data items in a clump
are defined by thier spacing, and thus have implied sizes.

	clump clumptypename item0 <size> item1 <size> ...

will make \$clumptypename_item0 \$clumptypename_item1 and so on be the
appropriate offsets from \$clumptypename in the assembler state.  
\$clumptypename itself has a value of zero. A clumptypename_size is also
created that records the overall size of this type of clump. Sizes are in
bytes. Those offset values can then be used during assembly to instantiate
and perhaps initialize clumps of that type, or as address offsets at
runtime, including offsets on the return stack. 

There's nothing yet to automate instantiating a clump. All I have is the
names of the field offsets of the clump type. Let's say you have a defined
PIC clump. You can make one ala...

L PIC1
allot PIC_size

And set a fields ala 
	
	= 0 to 		\$((PIC1+PIC_counter))
	= 300 to	\$((PIC1+PIC_count))

et cetera. It's about 5% of C structs. Personnally I think it's 80% of the
useful aspects of C structs. How to nest them is left as an excercise to 
the reader.

 "
						else    ###############
	cname=$1
	let os=0
	let $cname=0
	shift
	while test "$1"  				;do
		let ${cname}_$1=$os
		let os=os+$2
		shift ; shift
	done
	let ${cname}_size=$os
				fi
				}


leave		() { # return from the current reentrant procedure
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t

This is not 386+ LEAVE.

 A leave is a termination point of an osimplay reentrant routine. There
may be 0, 1 or more of these in a procedure. The current routine remains
current for assembly until the next routine definition starts, so there
may be several leaves to an entrance. entrance/enter/leave routines are
reentrant if you use the 15 stack familial variables for parameters and
return values. 
 "
						else    ###############
	HL
	ab $((0xc2))
	ad $((15*$cell))
	opnote  leave
			fi
			}


fill		() { # plural, copies A across range @ DI	-x86 STOSD
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel STOSD\n
 The fill opcode seems to only be useful with \"repeating\", and so that's
what osimpa \"fill\" does. osimpa \"fill\" is therfor a 2-byte composite
instruction with the repeating prefix, and I call this class of things
plurals. It wants the fill value in A, the fill range start in DI, the
count of bytes or cells in C, and follows the direction flag, which is
normally increasing, across the fill range. Any argument to fill is taken
as a spec that it fill by bytes rather than cells.

# cell-wise 0-fill a buffer
	increasing   
	zero A
	= \$buffer to DI
	= \$buffersize to C
	downshift C
	downshift C
	fill

"
						else ################
        HL
	parse $*
	if test "$1" 	 	;then
		ab 0xf3 0xaa
	else
		ab 0xf3 0xab
	fi
	opnote FILL $*
			fi
			}


flag		() { # assert the zero/sign flags of a register's value
 if test "$1" = "h" ; then echo -e  "\n\t\t\t\t\t
 Convenience wird to ANDtest the argument register with itself to generate
the conditionals flags."
						else    ###############
	ANDtest $1 with $1
						fi
						}


enter		() { # how you invoke an osimplay reentrant procedure
 if test "$1" = "h" ; then
 echo -e "\n\t\t\t\t\t 
 osimpa frame-allocating subroutine call. Hikes SP. This is NOT x86 ENTER,
which is "frame" in osimplay. osimplay reentrant routines are slighly
composite (macro'ed) use of the x86
return-from-subroutine-with-literal-amount-to-drop-from-stack. This is how
you invoke a routine with stack space for locals. enter implements 16-cell
stack frames counting the return address, giving 15 \"familial
variables\".

	enter my_reentrant_routine 

The space provided for reentrance, i.e. instance data besides the return
addy, is hardwired to 15 cells of return stack space in all cases, since
it's one extra instruction total that way. Gcc misses the functionality of
osimplay leave, BTW, last I looked. Typical gcc 386 assembly output is
mostly references to offsets of SP, which is what osimplay familial
variables allow you to do by hand.

See also: locals.
"
						else    ###############
	- 60 to SP
	call $1
						fi
						}



lsoepot		( ) { # figure (max(2^N) <|= arg) for masks
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
 	Largest Smaller Or Equal Power Of Two
 This is a standalone calculation convenience, basically. Return the
largest power of two that is equal to or less than \$1, the argument. Used
to generate strand (ring buffer, e.g.) mask values. Interactively, have a
look-see at
		lsoepot 83274
				et cetera. As with most of these, you can
put it in compembled code with grave (\`) accents.

"
						else    ###############
	placebit=1
	while test "$placebit" -le "$1"			;do
		previous=$placebit
		placebit=$(($placebit*2))
	done
	echo $previous				;fi
						}


maskbyte	() { # mask arg down to a byte
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t
 Assembles an instruction to AND the argument with 255, masking it to one
meaningful byte and high-order zeros."
						else #################
	AND 255 to $1
					fi
					}


maskdual	() { # mask arg down to it's lowest-significance 16 bits
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t
 Assembles an instruction to AND the argument with 16 on-bits, masking it to
two live bytes and high-order zeros."
       						else #################
	AND $((0xffff)) to $1
			fi
			}


match		() { # plural, range-range compare, zero flag=match
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
 \"match\" is a plural that compembles inline code to bitwise compare
memory ranges at SI and DI, for all of C bytes of length. D is also
clobbered. \"match\" takes no arguments.  If the memory ranges are exactly
the same for C bytes then the zero flag will be true. Follow it ala

					# SI, DI and C are set up by now
	match
	   when zero	whatever	# successful match action
	stuff				# match failed
L whatever
	tother stuff

\"match\" uses bytewise comparison, because I don't see a way to cell-wise
compare on bytewise increments. (Later x86en have such?) No matter, that's
a small win anyhooo. 
 "
						else ################
	HL			
	ab 0xf3			# rep
	opnote			 repeat while C \& ZF 
 compares of bytes
						fi
						}


max		() { # max arg1 arg2     leaves greater in arg2
    if test "$1" = "h" ;then
        echo -e "\n\t\t\t\t
 Assembles 3 inline instructions to leave the greater of two args in the
second arg using a conditional branch.

		min arg1 arg2 uniquelabel
				
leaves the larger value of arg1 or arg2 in arg2. 
arg1 is unchanged. The
second arg can be a register or an address. The first may be a literal
also. As with a single instruction, one of the args must not be an
address. That is, the x86 doesn't do a memref-to-memref addressing mode,
and osimplay min doesn't either. You must use quotes to delimit first and
second args if one arg is more than one token, e.g...

		min \"@ 3000 B\" someplace

MIN and MAX are beloved by Forth programmers. In osimplay the result will
be in the dest (second) argument. The necessity of the label argument,
which YOU have to make unique, and the lack of the to/from makes
implementation gobs easier.

"		
	else ##################
	
	-test $2 with $1
	when short sign $3
        = $1 to $2
	L $3
fi
}



min () 		{ # min arg1 arg2     leaves lesser in arg2
    if test "$1" = "h" ;then
        echo -e "\n\t\t\t\t
 Assembles 3 inline instructions to leave the lesser of two args in the
second arg.
		min A B uniquelabel
				leaves the smaller value of A or B in B. A
is unchanged. The second arg can be a register or an address. The first
may be a literal also. As with a single instruction, one of the args must
not be an address. That is, the x86 doesn't do a memref-to-memref
addressing mode, and osimplay min doesn't either. You must use quotes to
delimit first and second args if one arg is more than one token, e.g...

		min \"@ 3000 B\" A

MIN and MAX are beloved by Forth programmers. In osimplay the result will
be in the dest (second) argument. The necessity of the label argument,
which YOU have to make unique, and the lack of the to/from makes
implementation gobs easier.

"		
	else ##################

	-test $1 with $2
	when short sign 				$3
        = $1 to $2
	L $3	
    fi
}


numbering	() { # C enum, but not strictly constants and no commas
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
 Assign sequential integer values to a series of names in assembly state.
This would have been C enum or BCPL MANIFEST if I had bothered with the
Bash \"constant\" type-declaration schtuff. You want em constant, don't
change em. Interactively, have a go with...
	
	numbering zero one 16 sixteen seventeen eighteen
	echo \$zero \$one  \$eighteen

 As in C, the sequence can be coerced to a new starting count, which "16"
does in the above, except that you can't reset the sequence to 0 because
of a number/string parsing hack. Just use another numbering for another 0.
Or a shell variable declaration :o) \"numbering\" strikes me as kinda
dumb, but it was easy to implement, and C has it. The Plan 9 from Bell
Labs code has a lot of use of enum, I guess because it's a lot less typing
than using cpp for lots of constants. It also supposedly gives the
compiler more to munch on, but that doesn't pertain to osimplay.
 "
						else    ###############
let ecount=0
for arg in $*							;do
	if test $(($arg)) -ne 0				;then
		let ecount=$(($arg))
	else
		let $arg=$ecount		# assignment to $tring. :o)
		let ecount=$ecount+1
	fi
done					;fi;}




quad		() { # name/allot a 4-byte storage location
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
e.g.
	quad argc
			would make room and a label named argc for a
global named 4-byte storage location. It doesn't align. Works good after
\"heap\"."
						else    ###############
	L $1
	allot 4
				fi
				}



range		() { # name/allot some count-cell prefixed memory 
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
 A range is a count-cell-prefixed data allocation. A strand is similar,
but with more metadata. range assembles the size prefix cell, sets a
label, and allots to size. e.g.

	range scantable \$((256*10))

 \"text\"  is also similar but takes an initial shell here-document of
data and figures out the count from that. Also see strand h. The label the
range's name represents is byte 0 of the net data. The count is one cell
below that in memory.
 "
						else    ###############
	quads $2
	echo "			range" >> $listing
	L $1
	allot $2				;fi
						}



ring		() { # ring-wise increment a strand's index
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t

This is the basic action of a ring buffer. Given a strand of pointers,
say, called objectring,
			ring objectring cell
						will point you at the
ring-wise next object. In more detail, ring is a bounded ring-wise
increment of the index into a strand array. The default increment is byte.
A \"cell\" or \"ply\" token causes \$cell or the strand's defined ply size
to be the increment. Bounding is done with the strand's defined mask. The
byte and cell increment versions are 3 instructions, ply version is 4.  
See strand h also. ring clobbers A. ring keeps the index within the size
bounds of the strand, so don't forget to add it to the strand's address
when you reference the actual data.

 " 
 else
	if ! test "$2" ; then			# byte incr. 
		1+ @ $(($1-($cell*4)))			# index + 1	
		= @ $(($1-($cell*3))) to A		# mask
		AND A to @ $(($1-($cell*4)))
	else
		if test "$2" = "cell" ; then	# cell incr.
			+ $cell to @ $(($1-($cell*4)))
        	        = @ $(($1-($cell*3))) to A
                	AND A to @ $(($1-($cell*4)))
		else					# ply incr.
			if ! test "$2" = "ply" ; then
			  echo "Creative ring increment spec, assuming ply."	
			fi
			= @ $(($1-($cell*2))) to A	# ply
			+ @ $(($1-($cell*4))) to A
			AND @ $(($1-($cell*3))) to A
			= A to @ $(($1-($cell*4)))
		fi
	fi
					fi
					}



ringstore	() {	# ringstore ring reg/lit [arg3 means cell]  !A

        = @ $(($1-($cell*4))) to A        # index
	if test "$3" ; then		# cell store 
        	= $2 to @ $1  A
	else
        	= byte $2 to @ $1  A
	fi
	}	



scale 		() {  Forth */ with a shift instead of a divide
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t

osimplay scaling composite. This 2-instruction composite of a double
multiply followed by a double downshift allows scaling a 32 bit number by
a 32:32 scaling ratio without any unnecessary information loss due to
truncation. The final result will have as many meaningful bits as the
factor and arguments allow. Using a shift instead of a divide is for
speed.
	scale D 18
			multiplies A by D, keeping a double intermediate
result in D:A. It subsequently downshifts that intermediate result by 18
bit places, leaving the final result in A. Another form is

	scale D
			which implicitly gets the downshift value from the
C register. Downshifts are masked to 5-bit values, 0 to 31.

This is Forth */ or BCPL MULDIV, but using a shift instead of a divide for
even better speed. The resolution limitation of bitshifts is ameliorated
by the fact that a single is 32 bits, and that the multiply is full
resolution, so most odd ratios, like the twelfth root of 2 for example,
1.0925something:1, on which pitches of musical notes are based, can be
closely approximated. A downshift is about 3 clocks. A divide is more like
43. Multiplies are between 9 and 40 clocks worst case. Worst case is based
on the highest significant bit of the multiplier. Divides are always worst
case. Worst case is about 43 CPU cycles overall, plus memory accesses,
which is just the optional immediate byte.

This means you pay 10 clocks plus just the precision you use. For example,
	
	= 5 to D
	scale D 2
			should be about 18 clocks total. Not bad. The
multiply by five is only 3 significant bits. This also leaves us at about
the magnitude we came in with, so precision of the operation and magnitude
of the result are independant.

The wide downshift opcode is a pain to assemble. I don't think GNU Gas
does it right. I assemble it by hand here, and limit the possible shifted
registers to D:A, which meshes nicely with multiply. 

To reiterate, multiply A by arg 1, intermediate result in D:A. Downshift
that by C, or by immediate value. Final result in A.

"
else
	multiply $1
	HL
	if ! test "$2" ; then
		ab 0x0f 0xad 0320 		# wide downshift D:A by C
	else
		ab 0x0f 0xac 0320 $2		# wide downshift D:A by byte
	fi
	opnote wide downshift
				fi
				}



scan		() { # plural, compare A to memory range @ DI until hit/miss
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\tIntel SCASB\n

 Used to compare all of a range of memory against A. Quits when it hits.
Any argument to \"scan\" is taken as \"use cells\".  Otherwise it does a
bytewise scan.  Bytewise is probably much more widely useful because the
cellwise form also increments cellwise, so it only works properly for
cell-resolution data. In other words, if you scan for a quad you'll only
hit on the quad-aligned occurances of you match quad. The comparison is
between A and the current @ DI, which gets incremented as per argument
size. osimpa \"scan\" is a \`plural\', and uses the 386
repeat-while-non-zero prefix. When \"scan\" finishes repeating, if ZF is
1, i.e. if zero=true, then DI is the byte address of the byte or cell
FOLLOWING the match. If the direction flag is \"increasing\" that's how
\"scan\" proceeds, i.e. toward higher addresses.

C is decremented by ones. DI is decremented by the operand size, which
defaults to byte. \"scan\" will stop on a hit, or continue until the count
in C is 0. The count is per item, which may be cells or bytes.

Example use:

				# assuming A, DI and C are set up already
				# keep DF in mind also
scan 			
  when zero 	got_a_hit

\"scan\" is an inline composite, i.e. a macro, but it's only 2 bytes."
						else ################
        HL
	if ! test "$1"		;then
		ab 0xf2 0xae
	else
		ab 0xf2 0xaf
	fi
	opnote SCAN  $*
					fi
					}


strand		() { # name/allot a general array and 8 cells of metadata  
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
 A strand is a count-cell-prefixed string, a byte array, a sized array, a
masked ring, a multi-reader ring buffer, a dessert topping AND a
floorwax.  e.g.  
			strand <sname> <bytes> [<ply> <other stuff>]

The idea of strands is take a data allocation, add metadata as negative
offsets from the 0-byte address of the net data, keep the metadata in a
known consistent order for all types and subtypes, and you have an
interlocking set of datatypes that can all use all the ops for thier own
type and any simpler type. Sortof.

The data format of a strand is currently...

			lower addresses
cell undecided
cell seen
cell west
cell east
cell index
cell mask
cell ply
cell size in bytes
		<----- nominal address
DATA
DATADATA
DATADATADATA...
			higher addresses

ZO, a full strand definition might be...

	strand thangipoo 1024 4 1023 128 \$otherstrand \$tuthastrand

but this is a first cut, the sequence may change yet."
						else    ###############
#	eval ${1}_ply=$3
	let mask=`lsoepot $2`-1
#	eval  let ${1}_mask=`lsoepot $2`-1
	HL
	aq 0 
	opnote strand $1
	HL
	aq 0 
	HL
	aq 0 
	HL
	aq 0 
	HL
	aq 0 
	opnote index
	HL
	aq $mask
	opnote mask
	HL
	if test "$3"			; then
		aq $3
	else
		aq 1
	fi
	opnote ply
	HL
#	eval ${1}_bytes=$2			# eh?
	aq $2	
	opnote size
	L $1
	allot $2 			;fi
					}



sum		() { # plural, additive checksum 
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t 
 Arithmetic cell checksum.  Add Count contiguous cells to B. Sets B to 0
first. sum is an inlining of 3 looping instructions. A is clobbered also."
						else    ###############
opnote 				SUM	
woobie=$RANDOM
	zero B		
L sum$woobie
	fetches
	+ A to B
	when C-1			sum$woobie
opnote   			.
					fi
					}


tag		() { # tag <string>    ASCII bytes and zeros in a cell    
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t
 tag is a cellsize character(s) literal.    tag blahzay    on a machine
where cell=4 labels a cell as blahzay, allocates the cell, and initializes 
it with the ASCII values of b, l, a and h. The ASCII value of b will be in
the byte within the cell with the lowest address. If the given string has 
less characters than a cell has bytes then the cell is padded out with 
zero-bytes. 

Since the 386 is little-endian,   tag a   will have a numeric value of
0x61, but tags aren't really for arithmatic. They are for unique test
arguments. Tags are handy for simple and thus fast commandsets, case
switching, inner interpreters and so on. You see a lot of C case switches
based on 'c' character literals. tag is just as fast, more general, and
more hackish. I was doing this sort of thing a lot in the editor I was
writing, by hand, and I see a lot of the same basic thing in Menuet, so it
seems to be worth having an actual directive for. Don't sweat the
endianness non-issue.

When defining tags, shell quoting applies. tag uses osimplay asciibyte
in a loop.
 "
 				else
	L $1
	HL
	tagbyte=0
	tagfeed=${#1}
	tmore=yes
	while test "$tmore"		;do
		curchar=${1:$tagbyte:1}
		if test "$curchar"			;then
			ab `asciibyte $curchar`
		else 
			ab 0
		fi
		let tagbyte=$tagbyte+1
		if test $tagbyte -eq $cell	;then
			tmore=
		fi
	done
	opnote tag
				fi
				}


text		() { # name/allot some text
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
 Assemble text literally from a shell here-document. If a name is given
assemble a count-cell prefix and label the following byte with the given name.
e.g.

text ernie <<!

stuff
			other stuff
!

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

		echo -en "\012" >> $output
fi
		let temp=$temp+${#line}+1
	done
	textlength[$textinstance]=$temp
	IFS="
"
					fi
					}


cells="*4"

# frame (familial) variables. Look at the assembly 386 gcc produces. 
#  GOBS of this. 
sa=" @ 4 SP "
sb=" 8  + SP "
sc=" 12 + SP "
sd=" 16 + SP "
se=" 20 + SP "
sf=" 24 + SP "
sg=" 28 + SP "
sh=" 32 + SP "
si=" 36 + SP "
sj=" 40 + SP "
sk=" 44 + SP "
sl=" 48 + SP "
sm=" 52 + SP "
sn=" 56 + SP "
so=" 60 + SP "
# 64
pa="68 + SP"
pb="72 + SP"
pc="76 + SP"
pd="80 + SP"
pe="84 + SP"
pf="88 + SP"
pg="92 + SP"
ph="96 + SP"
pi="100 + SP"
pj="104 + SP"
pk="108 + SP"
pl="112 + SP"
pm="116 + SP"
pn="120 + SP"
po="124 + SP"


west		( ) { # get a strand's pointing-west pointer
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
 Given the 0-offset address of a strand's data in I, get the \"west\"
pointer into A."
						else #################
	= -$(($cell*5)) I to A
					fi
					}





xjump		() { # indexed jump into an xray execution array
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
Jump on an xray beam. i.e. use an execution array. Example...

	xjump \$dave B

where dave is an existing xray, compembles a jump through the xray beam at
the cell offset in the B register in the execution array (jump table)
\"dave\". Masking to the size of the array and similar prudent activities
are your problem. Only the register given gets clobblered. The cost of an
xjump is 11+ clocks. With a tree-of-conditionals switch/case your single
best case is two clocks to get into a condition, but then you also have to
get out. With an execution array best case and worst case are the same.

There appear to be two ways to use an xray. You can make the beams osimpa
\"define\" routines, in which case the beam will return to the caller's
caller, or you can hand-roll a bunch of branches for the beams.

xray currently only works with cell=4 . 
"
					else	############################
	= $1 A *2^ 2  to A
	ab 0xff 0xe0
						fi
						}


xray		() { # name an execution array for beam, yarx and xjump
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
 xray is the first of three words used to assemble an execution array.
xrays are named jump tables and the table items are subroutines, so they
return to the xray's \"caller\". xray sets the assembly state, does some
checks, and waits for beams. beam sets a range of jump targets to a
routine's address. Beam specs clobber earlier beam specs they overlap, so
you set the default ones first and overwrite them with the special cases.
When you have all your beams lined up you use yarx (xray backwards) and
yarx actually compembles the jump table based on the accumulated compembly
state.

You give xray a name and a table size in items. Possible code...

	xray asciithang 256

should work. I'll put a longer example in yarx h ."
						else #####################
		L $1
		xraysize=$2
		xray_H=$H
					fi
					}


xsum		() { # plural, XOR-ing checksum 
if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t 
 XOR cell checksum.  XOR Count contiguous cells into B. Sets B to 0 first.
sum is an inlining of 3 looping instructions. A is clobbered also. It's
sensitive to the direction flag, which defaults to \"increasing\" in
osimplay."
						else    ###############
opnote 				XSUM	
xwoobie=$RANDOM
	zero B		
L xsum$xwoobie
	fetches
	XOR A to B
	when C-1			xsum$xwoobie
opnote   			.
					fi
					}



yarx		() { # finish compembling an xray and it's beams
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t
Complete the compembling of an xray. Assuming a 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 tH's a
complete xray specified in assembler state and yarx finishes up, doing the
output of actual bytes. Given the above yarx will create a jump table with
jumps to the control routine if A is between 0 and 32 when the now-named
specs xray is invoked, the delete routine if A is 127, and the glyph
routine otherwise. glyph could have been 0 127 with the same end result.
The clobberableness factor might be handy for complex xray layouts. "
					else ######################
		yi=0
		while test $yi -lt $xraysize		;do
			HL
			ac ${beams[$yi]}
			let yi=$yi+1
		done
					fi
					}


zero		() { # simple convenience to set (a) register(s) to 0
if test "$1" = "h" ; then  echo -e  "\n\t\t\t\t\t

	Use XOR to set a list of registers to zero. More convenience
	than elegance. Well, improves readability too.

	zero A C D
	

"
else ######################


	for reg in $*	;do
		XOR $reg with $reg
	done			
fi			
}



locals 		() { # help-only word on osimplay familial frame variables
echo "





osimplay enter and leave are nothing like the 386 instructions with the
INTeL mnemonics ENTER and LEAVE. They are much simpler procedures using
the 386 return-from-subroutine-with-drop-stack-frame. Stack frames is how
you provide reentrant local variables on a register machine. osimplay
enter/leave provides 15 useable cells per frame, so a reeentrant or
recursive routine can have 15 locals. More than that, talk to the
chaplain.
Welding frames to 16 cells including the return EIP in all cases wastes
some stack, but keeps the implementation trivial, and provides a simple
and oddly flexible class of locals. The current frame is self. self has
sa, sb, sc...so  available for e.g.

	= A to \$sc
			and you can also refer to cells in the parent
frame with pa, pb pc...

A regular label will suffice for a routine you enter and leave, rather
than call and return from. It's up to you to keep such routines distinct.
If you don't like the pre-defined locals names, redefine them.

The cheapest way to pass values between generations is leave them in
registers. If you do have to copy e.g. pa to sa, you have to use a
register, which you may have to save first. The thing about the names
enter and leave is they imply space. The familial variables are the space
for each invocation of a routine, the locals. Registers are more
transient.

The overhead for all this is a + 60 to SP on enter and the immediate
offset form of the return instruction, which adds two bytes of code and
supposedly no clocks for the frame drop regardless of frame size, and the
stack waste.

Once you go enter/leave, you want to avoid push/pull, or rather, you want
to use locals for push/pull. If you do a push all the locals are off by a
cell. In other words, we're getting away from pure assembly here. 

"
}


 meg=0x100000                   # meg is max and min's oldest daughter
 k64=0x10000
 k16=0x4000
  k4=0x1000
  k1=0x400

bit0=1 bit1=2 bit2=4 bit3=8 bit4=16 bit5=32 bit6=64 bit7=128 bit8=256
bit9=512 bit10=1024 bit11=2048 bit12=4096 bit13=8192 bit14=16384
bit15=32768 bit16=65536 bit17=131072 bit18=262144 bit19=524288
bit20=1048576 bit21=2097152 bit22=4194304 bit23=8388608 bit24=16777216
bit25=33554432 bit26=67108864 bit27=134217728 bit28=268435456
bit29=536870912 bit30=1073741824
bit31=-2147483648


# variable names representing bytes in binary

 
numbering					\
________ _______I ______I_ ______II _____I__	\
_____I_I _____II_ _____III ____I___ ____I__I	\
____I_I_ ____I_II ____II__ ____II_I ____III_	\
____IIII ___I____ ___I___I ___I__I_ ___I__II	\
___I_I__ ___I_I_I ___I_II_ ___I_III ___II___	\
___II__I ___II_I_ ___II_II ___III__ ___III_I	\
___IIII_ ___IIIII __I_____ __I____I __I___I_	\
__I___II __I__I__ __I__I_I __I__II_ __I__III	\
__I_I___ __I_I__I __I_I_I_ __I_I_II __I_II__	\
__I_II_I __I_III_ __I_IIII __II____ __II___I	\
__II__I_ __II__II __II_I__ __II_I_I __II_II_	\
__II_III __III___ __III__I __III_I_ __III_II	\
__IIII__ __IIII_I __IIIII_ __IIIIII _I______	\
_I_____I _I____I_ _I____II _I___I__ _I___I_I	\
_I___II_ _I___III _I__I___ _I__I__I _I__I_I_	\
_I__I_II _I__II__ _I__II_I _I__III_ _I__IIII	\
_I_I____ _I_I___I _I_I__I_ _I_I__II _I_I_I__	\
_I_I_I_I _I_I_II_ _I_I_III _I_II___ _I_II__I	\
_I_II_I_ _I_II_II _I_III__ _I_III_I _I_IIII_	\
_I_IIIII _II_____ _II____I _II___I_ _II___II	\
_II__I__ _II__I_I _II__II_ _II__III _II_I___	\
_II_I__I _II_I_I_ _II_I_II _II_II__ _II_II_I	\
_II_III_ _II_IIII _III____ _III___I _III__I_	\
_III__II _III_I__ _III_I_I _III_II_ _III_III	\
_IIII___ _IIII__I _IIII_I_ _IIII_II _IIIII__	\
_IIIII_I _IIIIII_ _IIIIIII I_______ I______I	\
I_____I_ I_____II I____I__ I____I_I I____II_	\
I____III I___I___ I___I__I I___I_I_ I___I_II	\
I___II__ I___II_I I___III_ I___IIII I__I____	\
I__I___I I__I__I_ I__I__II I__I_I__ I__I_I_I	\
I__I_II_ I__I_III I__II___ I__II__I I__II_I_	\
I__II_II I__III__ I__III_I I__IIII_ I__IIIII	\
I_I_____ I_I____I I_I___I_ I_I___II I_I__I__	\
I_I__I_I I_I__II_ I_I__III I_I_I___ I_I_I__I	\
I_I_I_I_ I_I_I_II I_I_II__ I_I_II_I I_I_III_	\
I_I_IIII I_II____ I_II___I I_II__I_ I_II__II	\
I_II_I__ I_II_I_I I_II_II_ I_II_III I_III___	\
I_III__I I_III_I_ I_III_II I_IIII__ I_IIII_I	\
I_IIIII_ I_IIIIII II______ II_____I II____I_	\
II____II II___I__ II___I_I II___II_ II___III	\
II__I___ II__I__I II__I_I_ II__I_II II__II__	\
II__II_I II__III_ II__IIII II_I____ II_I___I	\
II_I__I_ II_I__II II_I_I__ II_I_I_I II_I_II_	\
II_I_III II_II___ II_II__I II_II_I_ II_II_II	\
II_III__ II_III_I II_IIII_ II_IIIII III_____	\
III____I III___I_ III___II III__I__ III__I_I	\
III__II_ III__III III_I___ III_I__I III_I_I_	\
III_I_II III_II__ III_II_I III_III_ III_IIII	\
IIII____ IIII___I IIII__I_ IIII__II IIII_I__	\
IIII_I_I IIII_II_ IIII_III IIIII___ IIIII__I	\
IIIII_I_ IIIII_II IIIIII__ IIIIII_I IIIIIII_	\
IIIIIIII	
