Tags: assembler, buggy, echo, glossary, grep, latest, least, linux, memory, osimplay, osimplays, programming, references, rick, script, shell, unix, vis-a-vis, x86

osimplay x86 assembler shell script, latest and least buggy

On Programmer » Unix & Linux

148,826 words with 8 Comments; publish: Wed, 30 Apr 2008 09:54:00 GMT; (200125.00, « »)

echo "

Recent osimplays didn't do memory references right vis-a-vis BP and SP.

This one seems to.

grep () gives a nice glossary of osimplay.

Rick Hohensee Oct 23 2004

" > /dev/null

# shasm to osimpa to osimplay

# OSes as simple as childsplay

echo " .unix-linux.todaysummary.com. BP fixed"

#()

# ASSEMBLER DIRECTIVES/HELPERS ()

# config

allocated=yes

executable=yes

output=~binary

listing=~listing

#H=0

declare -ai textlength

# here cell

declare -i H C

octalbyte=({0,1,2,3}{0,1,2,3,4,5,6,7}{0,

1,2,3,4,5,6,7})

## several doodads to have meta listings notes

hex=(\

{0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f}{0,1,2,

3,4,5,6,7,8,9,a,b,c,d,e,f})

opnote ( ) { # internal =shasm

if test "$pass" = "2" ;then

echo -n " "$*" " >> $listing

fi

}

ascii ( ) { # use text or tag =shasm

if test "$1" = "h" ; then echo -e "\n\n

Simple stick-text-in-assembly. For named self-sized text see \"text\".

The annoyance is that the shell does a lot of handy parsing of the

commandline that is completely in the way vis-a-vis literal strings. The

work-around that's easiest to implement is to make sure strings have no

actual whitespace in them as arguments to ascii or string. That means use

\040 for space, \t for tab, and \n for linefeed (unix end-of-line). Or use

\"text\". You also have \"asciibyte\" for ascii to binary.

I REPEAT. OSIMPLAY ASCII CANNOT HANDLE WHITESPACE AS SUCH.

"

else ###############

if test "$pass" = "1" ;then

H=H+${#1}

else

echo -en $1 >> $output

echo -n $1 "ASCII " >> $listing

H=H+${#1}

fi

fi

}

ab () { # assemble bytes. pass-sensitive. =shasm

if test "$pass" = "2" ;then

bytes $*

else

H=H+$#

fi

}

ao () { # assemble one octal string as a byte =shasm

if test "$pass" = "2" ;then

echo -en \\$1 >> $output

echo -en $1" " >> $listing

fi

H=H+1

}

ad () { # assemble duals. pass-sensitive. =shasm

if test "$pass" = "2" ;then

duals $*

else

H=H+$#*2

fi

}

aq () { # assemble quads. pass-sensitive quads =shasm

if test "$pass" = "2" ;then

quads $*

else

H=H+$#*4

fi

}

ac () { # assemble cells. pass-cell-sensitive. =shasm

if test "$pass" = "2" ;then

if test "$cell" = "2" ;then

duals $*

else

quads $*

fi

else

H=H+$#*$cell

fi

}

align () { # like e.g. .align =shasm

if test "$1" = "h" ; then echo -e "\n\n\n\n

Don't use this until \$H has been bumped above 0."

else ####################

declare lop pad

if test "$H" && test "$H" != "0" ;then

let lop=$H%$1

let pad=$1-$lop

if test $lop -ne 0 ;then

allot $pad

fi

else

echo "For simplicity, align doesn't work until

\$H has been set greater than 0."

fi

fi

}

allot () { # relative .org, like Forth ALLOT =shasm

if test "$1" = "h" ; then echo -e "\n\n

This is like Forth ALLOT, which is relative, unlike asm .org, which is

(file address) absolute. Allot takes one amount argument in byte units.

If \$H is 0x200 then after allot 0x10 \$H is 0x210. \$H

is the next un-assembled file address.

\n\n"

else ############

HL

if test "$pass" = "2" ;then

echo -n "00... " >> $listing

fi

let tempint=$1+H

if test $pass -eq 1 ;then

H=$1+H

else ###### pass 2

echo -en "\t\t\tALLOT $1">>$listing

while test $H -lt $(($tempint)) ;do

if test $(($tempint-$H>>8)) -eq 0 ;then

# could loop H too

if test "$allocated" = "yes" ;then

echo -en "\000" >> $output

fi

H=H+1

else

page

H=H+256

fi

done

fi

fi

}

asciibyte () { # e.g. ab `asciibyte c q d` =shasm

if test "$1" = "hoilp" ; then echo -e "\n\n

This isn't help on h. That led to one of the more twisted bugs I've

encountered. I assume you're reading the script, or did

asciibyte hoilp

Similar to BASIC ASC or C 't'. Unlike osimpa \"text\" or \"ascii\" in that

it doesn't assemble the byte itself. You'll probably use it with grave

accents, ala

= `asciibyte T` to DI

"

else ################

for aschr in $* ; do

case $aschr in

# some duplicates for performance. Heh.

cr) echo $((0015)) ;; # 0x0D CARRIAGE RETURN

space) echo $((0040)) ;; # 0x20 SPACE

tab) echo $((0011)) ;; # 0x09 HORIZONTAL TABULATION

bell) echo $((0007)) ;; # 0x07 BELL

backspace) echo $((0010)) ;; # 0x08 BACKSPACE

formfeed) echo $((0014)) ;; # 0x0C FORM FEED

"!") echo $((0041)) ;; # 0x21 EXCLAMATION MARK

null) echo $((0000)) ;; # 0x00 NULL

header) echo $((0001)) ;; # 0x01 START OF HEADING

sot) echo $((0002)) ;; # 0x02 START OF TEXT

binary) echo $((0003)) ;; # 0x03 END OF TEXT

eot) echo $((0004)) ;; # 0x04 END OF TRANSMISSION

enqiry) echo $((0005)) ;; # 0x05 ENQUIRY

ack) echo $((0006)) ;; # 0x06 ACKNOWLEDGE

bell) echo $((0007)) ;; # 0x07 BELL

backspace) echo $((0010)) ;; # 0x08 BACKSPACE

tab) echo $((0011)) ;; # 0x09 HORIZONTAL TABULATION

linefeed) echo $((0012)) ;; # 0x0A LINE FEED

vtab) echo $((0013)) ;; # 0x0B VERTICAL TABULATION

formfeed) echo $((0014)) ;; # 0x0C FORM FEED

cr) echo $((0015)) ;; # 0x0D CARRIAGE RETURN

out) echo $((0016)) ;; # 0x0E SHIFT OUT

in) echo $((0017)) ;; # 0x0F SHIFT IN

outofband) echo $((0020)) ;; # 0x10 DATA LINK ESCAPE

control1) echo $((0021)) ;; # 0x11 DEVICE CONTROL ONE

control2) echo $((0022)) ;; # 0x12 DEVICE CONTROL TWO

control3) echo $((0023)) ;; # 0x13 DEVICE CONTROL THREE

control4) echo $((0024)) ;; # 0x14 DEVICE CONTROL FOUR

nack) echo $((0025)) ;; # 0x15 NEGATIVE ACKNOWLEDGE

standby) echo $((0026)) ;; # 0x16 SYNCHRONOUS IDLE

eoblock) echo $((0027)) ;; # 0x17 END OF TRANSMISSION BLOCK

cancel) echo $((0030)) ;; # 0x18 CANCEL

eomedia) echo $((0031)) ;; # 0x19 END OF MEDIUM

substitute) echo $((0032)) ;; # 0x1A SUBSTITUTE

escape) echo $((0033)) ;; # 0x1B ESCAPE

filesep) echo $((0034)) ;; # 0x1C FILE SEPARATOR

groupsep) echo $((0035)) ;; # 0x1D GROUP SEPARATOR

recordsep) echo $((0036)) ;; # 0x1E RECORD SEPARATOR

unitsep) echo $((0037)) ;; # 0x1F UNIT SEPARATOR

space) echo $((0040)) ;; # 0x20 SPACE

"!") echo $((0041)) ;; # 0x21 EXCLAMATION MARK

"\"") echo $((0042)) ;; # 0x22 QUOTATION MARK

"#") echo $((0043)) ;; # 0x23 NUMBER SIGN

"$") echo $((0044)) ;; # 0x24 DOLLAR SIGN

"%") echo $((0045)) ;; # 0x25 PERCENT SIGN

"&") echo $((0046)) ;; # 0x26 AMPERSAND

"'") echo $((0047)) ;; # 0x27 APOSTROPHE

"(") echo $((0050)) ;; # 0x28 LEFT PARENTHESIS

")") echo $((0051)) ;; # 0x29 RIGHT PARENTHESIS

"\*") echo $((0052)) ;; # 0x2A ASTERISK

"+") echo $((0053)) ;; # 0x2B PLUS SIGN

,) echo $((0054)) ;; # 0x2C COMMA

-) echo $((0055)) ;; # 0x2D HYPHEN-MINUS

.) echo $((0056)) ;; # 0x2E FULL STOP

"/") echo $((0057)) ;; # 0x2F SOLIDUS

0) echo $((0060)) ;; # 0x30 DIGIT ZERO

1) echo $((0061)) ;; # 0x31 DIGIT ONE

2) echo $((0062)) ;; # 0x32 DIGIT TWO

3) echo $((0063)) ;; # 0x33 DIGIT THREE

4) echo $((0064)) ;; # 0x34 DIGIT FOUR

5) echo $((0065)) ;; # 0x35 DIGIT FIVE

6) echo $((0066)) ;; # 0x36 DIGIT SIX

7) echo $((0067)) ;; # 0x37 DIGIT SEVEN

8) echo $((0070)) ;; # 0x38 DIGIT EIGHT

9) echo $((0071)) ;; # 0x39 DIGIT NINE

":") echo $((0072)) ;; # 0x3A COLON

";") echo $((0073)) ;; # 0x3B SEMICOLON

"<") echo $((0074)) ;; # 0x3C LESS-THAN SIGN

"=") echo $((0075)) ;; # 0x3D EQUALS SIGN

">") echo $((0076)) ;; # 0x3E GREATER-THAN SIGN

"?") echo $((0077)) ;; # 0x3F QUESTION MARK

.unix-linux.todaysummary.com.) echo $((0100)) ;; # 0x40 COMMERCIAL AT

A) echo $((0101)) ;; # 0x41

B) echo $((0102)) ;; # 0x42

C) echo $((0103)) ;; # 0x43

D) echo $((0104)) ;; # 0x44

E) echo $((0105)) ;; # 0x45

F) echo $((0106)) ;; # 0x46

G) echo $((0107)) ;; # 0x47

H) echo $((0110)) ;; # 0x48

I) echo $((0111)) ;; # 0x49

J) echo $((0112)) ;; # 0x4A

K) echo $((0113)) ;; # 0x4B

L) echo $((0114)) ;; # 0x4C

M) echo $((0115)) ;; # 0x4D

N) echo $((0116)) ;; # 0x4E

O) echo $((0117)) ;; # 0x4F

P) echo $((0120)) ;; # 0x50

Q) echo $((0121)) ;; # 0x51

R) echo $((0122)) ;; # 0x52

S) echo $((0123)) ;; # 0x53

T) echo $((0124)) ;; # 0x54

U) echo $((0125)) ;; # 0x55

V) echo $((0126)) ;; # 0x56

W) echo $((0127)) ;; # 0x57

X) echo $((0130)) ;; # 0x58

Y) echo $((0131)) ;; # 0x59

Z) echo $((0132)) ;; # 0x5A

"[") echo $((0133)) ;; # 0x5B LEFT SQUARE BRACKET

"\\") echo $((0134)) ;; # 0x5C REVERSE SOLIDUS

"]") echo $((0135)) ;; # 0x5D RIGHT SQUARE BRACKET

"^") echo $((0136)) ;; # 0x5E CIRCUMFLEX ACCENT

_) echo $((0137)) ;; # 0x5F LOW LINE

"\`") echo $((0140)) ;; # 0x60 GRAVE ACCENT

a) echo $((0141)) ;; # 0x61

b) echo $((0142)) ;; # 0x62

c) echo $((0143)) ;; # 0x63

d) echo $((0144)) ;; # 0x64

e) echo $((0145)) ;; # 0x65

f) echo $((0146)) ;; # 0x66

g) echo $((0147)) ;; # 0x67

h) echo $((0150)) ;; # 0x68

i) echo $((0151)) ;; # 0x69

j) echo $((0152)) ;; # 0x6A

k) echo $((0153)) ;; # 0x6B

l) echo $((0154)) ;; # 0x6C

m) echo $((0155)) ;; # 0x6D

n) echo $((0156)) ;; # 0x6E

o) echo $((0157)) ;; # 0x6F

p) echo $((0160)) ;; # 0x70

q) echo $((0161)) ;; # 0x71

r) echo $((0162)) ;; # 0x72

s) echo $((0163)) ;; # 0x73

t) echo $((0164)) ;; # 0x74

u) echo $((0165)) ;; # 0x75

v) echo $((0166)) ;; # 0x76

w) echo $((0167)) ;; # 0x77

x) echo $((0170)) ;; # 0x78

y) echo $((0171)) ;; # 0x79

z) echo $((0172)) ;; # 0x7A

"\{") echo $((0173)) ;; # 0x7B LEFT CURLY BRACKET

"|") echo $((0174)) ;; # 0x7C VERTICAL LINE

"\}") echo $((0175)) ;; # 0x7D RIGHT CURLY BRACKET

"~") echo $((0176)) ;; # 0x7E TILDE

delete) echo $((0177)) ;; # 0x7F DELETE

esac

done

fi

}

# interactive use helper

bases () { # print # in decimal, binary, octal, hex =shasm

if test $# -lt 1 ;then

echo "

This is an interactive calculations helper. Gimme some numbers to convert

to binary, octal and hex.

"

else #########

for num in $* ;do

binaryout $num

echo -en " "

octalout $num

echo -en " "

hexout $num

echo $(($num)) "dec"

done

fi

}

binary () { # binary <strings of ones and zeros> =shasm

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

The osimplay binary word has mostly been superceded by a set of names fo

r

the values 0-255 where zeros are _ and ones are I, e.g. 3 is \$______II.

Convert the binary string representation of a number to it's value.

Accepts it's single argument in multiple segments, for keeping things in

byte-sized parcels.

e.g.

let T=\`binary 0100 01001010\`

or for just looking,

binary 0101010011010011

#binary (and all of shasm) uses Bash arithmatic, with Bash's limits,

which looks like 32 bits H.

"

else ####################

remaining=$1$2$3$4$5$6$7$8

let placebit=1

value=0

while test ! "$remaining" = "" ;do

if test "${remaining:${#remaining}-1:1}" = "1" ;then

let value=$value+$placebit

fi

placebit=$(($placebit*2))

remaining=`chom $remaining`

done

echo $value

fi

}

append ( ) { # help-only on dealing with binary appendages

echo -e "\n\t\t\t\t\t

help-only on dealing with binary appendages

You don't want to be recompembling tables of constants and so on with

every run of a compembler written in Bash. So cheat. Use cat, and make a

proto-linker at the very end of your osimplay source file, so you have the

right H to offset into the appended files. Let's say you have 3

precompembled RELOCATABLE objects, lookup data tables, say, named Larry,

Moe and Curly. Assume they are 3 different sizes. At the very end of your

top sourcefile...

Larry=\$H

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

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

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

mv binary inary

cat inary Larry Moe Curly > binary

rm inary

and you can = A to .unix-linux.todaysummary.com. Larry as desired

elsewhere in the sources. We left out Shemp. osimplay doesn't use anything

but a sh, but that's osimplay's problem. Your sources are shell scripts.

"

}

binaryout ( ) { # print a binary string of a number =shasm

let val=$1

bstring=

for maskbit in -2147483648 1073741824 536870912 268435456 134217728 \

67108864 33554432 16777216 8388608 4194304 2097152 1048576 524288 \

262144 131072 65536 32768 16384 8192 4096 2048 1024 512 256 128 64 \

32 16 8 4 2 1

do

let bitval=$val\&$maskbit

bitchar=0

if test "$bitval" -ne 0 ;then

bitchar=1

fi

bstring=$bstring$bitchar

if test $maskbit = 16777216 -o $maskbit = 65536 -o \

$maskbit = 256

then

bstring=$bstring"_"

fi

done

echo -n $bstring

}

branch ( ) { # resolve a relative branch =shasm

if test "$1" = "h" ; then echo "\n\n\n

A branch resolver. This is an internal. Use jump, call and so on. Called

by branching opers, which pass this the label name and branch size.\n\n"

elif test "$pass" = "1"

then # on pass 1 just skip the branch byte/dual/quad

H=H+$2

else # Pass 2 is on us. Pass 1 was L.

let relativ=$1-H-$2

case $2 in

1)bytes $relativ ;;

2)duals $relativ ;;

4)quads $relativ ;;

*) echo "

The second argument to branch isn't 1, 2 or 4. "

;;

esac

fi

}

bytes ( ) { # internal =shasm

H=H+$#

for a in $* ;do

if test $(($a)) -gt 255 ; then

echo " H " $H ": index into hex[] > 255"

fi

if test "$allocated" = "yes" ;then

echo -en \\${octalbyte[$a&0xff]} >> $output

fi

if test $(($a)) -lt 0 ;then

let a=$a+256

fi

echo -n ${hex[$a]}" " >> $listing

done

}

chom () { # chom chomp returns chom =shasm

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

chom chomp will leave chom "

else ###############

echo ${1:0:${#1}-1} ;fi

}

duals ( ) { # internal =shasm

H=H+$#*2

for a in $* ;do

if test "$allocated" = "yes" ;then

echo -en \\${octalbyte[$a&0xff]} >> $output

fi

echo -en ${hex[$a&0xff]}" " >> $listing

if test "$allocated" = "yes" ;then

echo -en \\${octalbyte[$((a>>8))&0xff]} >> $output

fi

echo -en ${hex[$((a>>8))&0xff]}" " >> $listing

done

}

quads ( ) { # internal =shasm

H=H+$#*4

for a in $* ;do

if test "$allocated" = "yes" ;then

echo -en \\${octalbyte[$a&0xff]} >> $output

fi

echo -en ${hex[$a&0xff]}" " >> $listing

if test "$allocated" = "yes" ;then

echo -en \\${octalbyte[$((a>>8))&0xff]} >> $output

fi

echo -en ${hex[$((a>>8))&0xff]}" " >> $listing

if test "$allocated" = "yes" ;then

echo -en \\${octalbyte[$((a>>16))&0xff]}>> $output

fi

echo -en ${hex[$((a>>16))&0xff]}" " >> $listing

if test "$allocated" = "yes" ;then

echo -en \\${octalbyte[$((a>>24))&0xff]}>> $output

fi

echo -en ${hex[$((a>>24))&0xff]}" " >> $listing

done

}

hexout ( ) { # print a hex representation of a number =shasm

declare val bstring

let val=$1

bstring=0x

for shift in 28 24 20 16 12 8 4 0 ;do

let nybble=$val\>\>$shift\&15

case $nybble in

0) bstring=$bstring"0" ;;

1) bstring=$bstring"1" ;;

2) bstring=$bstring"2" ;;

3) bstring=$bstring"3" ;;

4) bstring=$bstring"4" ;;

5) bstring=$bstring"5" ;;

6) bstring=$bstring"6" ;;

7) bstring=$bstring"7" ;;

8) bstring=$bstring"8" ;;

9) bstring=$bstring"9" ;;

10) bstring=$bstring"a" ;;

11) bstring=$bstring"b" ;;

12) bstring=$bstring"c" ;;

13) bstring=$bstring"d" ;;

14) bstring=$bstring"e" ;;

15) bstring=$bstring"f" ;;

esac

done

echo -n $bstring" "

}

hexquad ( ) { # internal =shasm

# BIG-endian non-spaced hex 4-byte int for $H

if test "$1" ; then

echo -en \

${hex[$1>>24&0xff]}${hex[$1>>16&0xff]}${hex[$1>>8&0xff]}${hex[$1&0xff]}" "\

else

echo "No arg for hexquad. Set \$H?"

fi

}

HL ( ) { # internal =shasm

if test $pass = 2 ;then

echo >> $listing

hexquad $H

fi

}

homp () { # homp chomp is homp =shasm

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

homp chomp produces homp"

else ###############

echo ${1:1:${#1}-1} ;fi

}

fillabsolute () { # like .org, but fill to just BEFORE arg =shasm

if test "$1" = "h" ; then echo -e "\n\n

This was buggy, and

allot \$((bla-\$H))

does the right

thing, so use that.

\n\n"

else ############

HL

if test "$pass" = "2" ;then

echo -n "00 " >> $listing

fi

if test $pass -eq 1 ;then

H=$1+H

else ###### pass 2

echo -e "\t\t\tfill to just before $1">>$listing

echo -e " \||/ 00...">>$listing

while test $H -lt $1 ;do

if test $(($1-$H>>8)) -eq 0 ;then

if test "$allocated" = "yes" ;then

echo -en "\000" >> $output

fi

H=H+1

else

page

H=H+256

fi

done

H=H-1

HL

echo "00 " >> $listing

H=H+1

fi

fi

}

L () { # label, set a branch bla at $H =shasm

if test "$1" = "h" ;then

echo "\n\n\n

L mylabel

sets \$mylabel to \$H.

"

elif ########################################

#

test "$pass" = "1" ;then

eval $1=$H

else

HL

echo -n " (O) "$1 >> $listing

fi

}

octalout ( ) { # print a number as an octal string

let val=$1

bstring=

for ushift in 30 27 24 21 18 15 12 9 6 3 0 ;do

let nybble=$val\>\>$ushift\&7

case $nybble in

0) bstring=$bstring"0" ;;

1) bstring=$bstring"1" ;;

2) bstring=$bstring"2" ;;

3) bstring=$bstring"3" ;;

4) bstring=$bstring"4" ;;

5) bstring=$bstring"5" ;;

6) bstring=$bstring"6" ;;

7) bstring=$bstring"7" ;;

esac

done

echo -n $bstring" "

}

page ( ) { # internal =shasm

if test "$allocated" = "yes" ;then

echo -en "\

\000\000\000\000\000\000\000\000\000\000

\000\000\000\000\000\000\

\000\000\000\000\000\000\000\000\000\000

\000\000\000\000\000\000\

\000\000\000\000\000\000\000\000\000\000

\000\000\000\000\000\000\

\000\000\000\000\000\000\000\000\000\000

\000\000\000\000\000\000\

\000\000\000\000\000\000\000\000\000\000

\000\000\000\000\000\000\

\000\000\000\000\000\000\000\000\000\000

\000\000\000\000\000\000\

\000\000\000\000\000\000\000\000\000\000

\000\000\000\000\000\000\

\000\000\000\000\000\000\000\000\000\000

\000\000\000\000\000\000\

\000\000\000\000\000\000\000\000\000\000

\000\000\000\000\000\000\

\000\000\000\000\000\000\000\000\000\000

\000\000\000\000\000\000\

\000\000\000\000\000\000\000\000\000\000

\000\000\000\000\000\000\

\000\000\000\000\000\000\000\000\000\000

\000\000\000\000\000\000\

\000\000\000\000\000\000\000\000\000\000

\000\000\000\000\000\000\

\000\000\000\000\000\000\000\000\000\000

\000\000\000\000\000\000\

\000\000\000\000\000\000\000\000\000\000

\000\000\000\000\000\000\

\000\000\000\000\000\000\000\000\000\000

\000\000\000\000\000\000" >> $output

fi

}

## 80386 shtuff see Intel's 386INTEL.TXT etc.

# tH's a webpage of the above

cell="4" # global, 4 or 2. 386 or real

declare -i registers

Size ( ) { # internal =x86

if test $1 = "1" ;then

size[$source]=1 #

size[$dest]=1 # was [$side]

fi

}

#TYPE ( ) { # internal =x86

#mode[$side]=$1

# }

octacode ( ) { # internal =x86

register[$side+${registers[$side]}]=$1

registers[$side]=${registers[$side]}+1

if test ${registers[$side]} -gt 1 ; then

memside=$side

MEM=4 # wtf

fi

if test $numsseen -gt 0 ; then

memside=$side

MEM=4 # wtf

fi

}

# modestring builders

segment ( ) { # internal =x86

octacode $1

#size 2

mode[$side]=segm

}

specialC ( ) { # internal =x86

octacode $1

mode[$side]=speC

}

specialD ( ) { # internal =x86

octacode $1

mode[$side]=speD

}

specialT ( ) { # internal =x86

octacode $1

mode[$side]=speT

}

small ( ) { # internal =x86

octacode $1

mode[$side]=D

Size 1

}

dualreg ( ) { # internal =x86

octacode $1

mode[$side]=D

Size 2

}

initop ( ) { # internal =x86

# wipe op parser state

unset number register mode size memside MEM index mo

source=0

registers[0]=0

registers[2]=0

shifted= # Nein! default is no index

numsseen=0

side=0 # left=0, right=2?.

let sides=1

Scale=0

BAS=5

}

tokencase ( ) { # internal =x86

case $arg in

to) sides=2 ; source=0 ; dest=2 ; side=2 ; numsseen=0 ;;

A) octacode "0" ;; C) octacode "1" ;;

D) octacode "2" ;; B) octacode "3" ;;

SP) octacode "4" ;; E|BP) octacode "5" ;;

+|.unix-linux.todaysummary.com.) mode[$side]="M" ; memside=$side ;;

SI) octacode "6" ;; DI) octacode "7" ;;

byte) Size "1" ;;

dual) Size "c" ;; quad) Size "c" ;; # do nothing

CS) segment 1 ;; DS) segment 3 ;;

SS) segment 2 ;; ES) segment 0 ;;

FS) segment 4 ;; GS) segment 5 ;;

AL) small 0 ;; CL) small 1 ;; DL) small 2 ;; BL) small 3 ;;

AH) small 4 ;; CH) small 5 ;; DH) small 6 ;; BH) small 7 ;;

AX) dualreg 0 ;; CX) dualreg 1 ;; DX) dualreg 2 ;; BX) dualreg 3 ;;

CR0) specialC 0 ;; CR2) specialC 2 ;; CR3) specialC 3 ;;

DR0) specialD 0 ;; DR1) specialD 1 ;; DR2) specialD 2 ;;

DR3) specialD 3 ;; DR6) specialD 6 ;; DR7) specialD 7 ;;

TR6) specialT 6 ;; TR7) specialT 7 ;;

"*2^") let shifted=$side+${registers[$side]}-1

index=${register[$shifted]}

memside=$side # memory side

wasshifter="yes"

mode[$side]="M" ;;

with) sides=2 ; source=0 ; dest=2 ; side=2 ; numsseen=0 ;;

from) sides=2 ; source=2 ; dest=0 ; side=2 ; numsseen=0 ;;

*) number[$side+$numsseen]=$arg

let numsseen=$numsseen+1 ;;

esac

}

buildmode ( ) { # internal =x86

## todo... figure out that a dest-number-only is a memref ####

# accumulate a case switch string

modestring=${size[$source]}

if test -n "${mode[$source]}" ;then

modestring=$modestring${mode[$source]}

elif test "${registers[$source]}" = "2" ;then

modestring=$modestring"M"

elif test -n "${number[$source]}" ;then

modestring=$modestring"I"

else

modestring=$modestring"D"

fi

if test "$sides" = 2 ;then

if test -n "${mode[$dest]}" ;then

modestring=$modestring${mode[$dest]}

elif test "${registers[$dest]}" = "2" ;then

modestring=$modestring"M"

else

modestring=$modestring"D"

fi

fi

}

parse ( ) { # internal =x86

initop

for arg in $* ;do

if test "$wasshifter" = "yes" ;then

wasshifter="no"

Scale=$arg # SNAG SC without making it a

# $number[]

else

tokencase $arg

fi

done # end args loop, resume reasonable indentation.

buildmode

} # end of pmode parse #######

memref ( ) { # internal =x86

if test "$cell" = 2 ;then

rmodememref $*

else

pmodememref $*

fi

}

# mem reference modR/M register/code/memref byte

# | | | | | | | | |

# pmode | mode | offreg/code | memref reg/code |

# mode 0 is offreg or code &

# single memref register or

needSIB=4 # MEM=4 or

# 32 bit displacement

# mode 1 is offreg or code &

# byte displ. and reg or

# byte displ. and needSIB=4

# mode 2 is offreg or code &

# 32 bit displ. and reg or

# 32 bit displ. and needSIB=4

# mode 3 is offreg or code &

# single memref register, any size , 4=SP

# SIB is

# | | | | | | | | |

# | scale | index reg | base reg |

# 386 Scale may vary without an index. Gas warns on that.

# arg is MODE/R/M R, memref register

pmodememref ( ) { # internal =x86

REG=$1 MEM= MOD=0 zerobytedisp=

# REG is the OFF reg and just gets ao'd

case ${registers[$memside]} in

0) MEM="5"

;;

1) MEM=${register[$memside]}

if ! test $shifted ;then ## no *2^

if test ${register[$memside]} = 5 ; then

# .unix-linux.todaysummary.com. BP has to be coded as .unix-linux.todaysummary.com. byte 0 BP or something

# because MEM=5 above is displ 32 NO REG

MOD=1 # byte disp

zerobytedisp=yes # byte displacement

fi

if test ${register[$memside]} = 4 ; then

MEM=4 # = needs sib

BAS=4

index=4 # per gas

fi

else # had a *2^

BAS=5 # 5 is no base

MEM=$needSIB # which is SP=4

index=${register[$shifted]}

# Scale is known from tokencase

fi

;;

2) MEM=$needSIB

if test $shifted ;then

index=${register[$memside]}

BAS=${register[$memside+1]} # just pick

else # base is index with NOT of low bit

BAS=${register[$shifted^1]}

index=${register[$shifted]}

fi

;;

*) echo "$H ; >2 regs on a memside?"

;;

esac

# displacement sizer

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

# get ABS of disp for size test

if test ${number[$memside]} -lt 0 ;then

disptest=$((0-${number[$memside]}))

else

disptest=${number[$memside]}

fi

# set MODe from displacement size

if test ${registers[memside]} -gt 0 ; then

if test $disptest -lt 128 ;then

MOD=1

else

MOD=2

fi

fi

fi

## assemble modR/M

ao $MOD$REG$MEM

# SIB

if test "$MEM" = "$needSIB" ;then

if test $MOD -ne 3 ;then # eh?

ao $Scale$index$BAS

fi

fi

# .unix-linux.todaysummary.com. BP kludge

if test $zerobytedisp ; then

ab 0

fi

if test "$MOD" = "1" ;then

ab ${number[$memside]}

fi

if test "$MOD" = "2" ;then

ac ${number[$memside]}

fi

if test "$MOD" = "0" -a $MEM = "5" ;then

ac ${number[$memside]}

fi

} # end PROTECTED memref

rmodememref ( ) { # internal =x86

# takes $source/$dest of memref and the off register/code

## mo and MEM

REG=$1

if ! test "$memside" ;then # direct

MEM=${register[$1]}

mo=3

fi

if test -n "${number[$memside]}" ;then # displ

if test ${registers[$memside]} -eq 0 ;then #no reg

mo=0

MEM=6

elif test "${size[$memside]}" = "1" ;then # regs, byte

mo=1

case ${register[$memside]}${register[$memside

+1]} in

63|36) MEM=0 ;;

73|37) MEM=1 ;;

65|56) MEM=2 ;;

75|57) MEM=3 ;;

6) MEM=4 ;;

7) MEM=5 ;;

5) MEM=6 ;;

3) MEM=7 ;;

*) echo `hexout $H` ": \

real mode parser is looking for MEM value unsuccessfully.

Rmode memref register combinations with byte are limited to

DI/B, SI/B, DI/BP, SI/BP, B, BP, SI and DI.

" ;;

esac

else # regs, cell

mo=2

case ${register[$memside]}${register[$memside

+1]} in

63|36) MEM=0 ;;

73|37) MEM=1 ;;

65|56) MEM=2 ;;

75|57) MEM=3 ;;

6) MEM=4 ;;

7) MEM=5 ;;

5) MEM=6 ;;

3) MEM=7 ;;

*) echo `hexout $H` ": \

real mode parser is looking for MEM value unsuccessfully.

Rmode memref register combinations with lit. are limited to

DI/B, SI/B, DI/BP, SI/BP, B, BP, SI and DI.";;

esac

fi

else # no displ

mo=0

case ${register[$memside]}${register[$memside

+1]} in

63|36) MEM=0 ;;

73|37) MEM=1 ;;

65|56) MEM=2 ;;

75|57) MEM=3 ;;

6) MEM=4 ;;

7) MEM=5 ;;

3) MEM=7 ;;

*) echo `hexout $H` ": \

real mode parser is looking for MEM value unsuccessfully.

Rmode memref register combinations without displ. are limited

to DI/B, SI/B, DI/BP, SI/BP, B, SI and DI.";;

esac

fi

ao $mo$REG$MEM

if test "$mo" = "1" ;then # byte displacement

ab ${number[$memside]}

fi

if test "$mo" = "2" ;then # cell displacement

ac ${number[$memside]}

fi # none

if test "$mo" = "0" && test $MEM = 6 ;then # displ. only

ac ${number[$memside]}

fi

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

modenotsupported ( ) { # internal

echo -e `hexout $H`": osimpa $1 doesn't support " $modestring " mode."

}

#( )

#_______________x86_x86x86x86x86x86x86x8

6x86x86_________ ops ( )

echo x86

# instruction prefixes ( )

otheroperandsize ( ) { # -x86 prefix

if test "$1" = "h" ; then echo -e "\n\n\n\n

The following instruction is to be interpreted at the opposite operand

size from what the current default is, as per this segment's

descriptor.\n\n"

else ################

HL

ab 0x66

opnote otheroperandsize $*

fi

}

otheraddresssize ( ) { # -x86 prefix

if test "$1" = "h" ; then echo -e "\n\n\n\n

following instruction is to be interpreted at the opposite address size

from what the current default is, as per this segment's descriptor.\n"

else ################

HL

ab 0x67

opnote otheraddresssize $*

fi

}

#()

# x86 general instructions ()

= () { # Clobber. many variants, most freq. insn -x86 MOV

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel MOV\n

catch-all. copy. The most prevalent insn. The only access to special

registers. Widest range of memory addressing forms. The fast mem<->A

forms may be A= and =A by the time you read this.

! would be better but it conflicts with Bash.

"

else ################

HL

parse $*

case $modestring in

ID) ao 27${register[$dest]}

ac ${number[$source]} ;;

DD) ab 0x89

ao 3${register[0]}${register[2]} ;;

MD) ab 0x8b

memref ${register[2]} ;;

DM) ab 0x89

memref ${register[$source]} ;;

IM) ab 0xc7

memref 0

ac ${number[$source]} ;;

1ID) ao 26${register[$dest]}

ab ${number[$source]} ;;

1DD) ab 0x88

ao 3${register[0]}${register[2]} ;;

1MD) ab 0x8a

memref ${register[2]} ;;

1DM) ab 0x88

memref ${register[$source]} ;;

1IM) ab 0xc6

memref 0

ab ${number[$source]} ;;

Dsegm) ab 0x8e # typo in 386INTEL.TXT

ao 3${register[$dest]}${register[source]};;

segmD) ab 0x8c

ao 3${register[$source]}${register[dest]};;

*speC) ab 0x0f 0x22

ao 3${register[$dest]}${register[source]};;

*speD) ab 0x0f 0x23

ao 3${register[$source]}${register[source]}

;;

*speT) ab 0x0f 0x26

ao 3${register[$dest]}${register[source]};;

*speC*) ab 0x0f 0x20

ao 3${register[$source]}${register[dest]};;

*speD*) ab 0x0f 0x21

ao 3${register[$source]}${register[dest]};;

*speT*) ab 0x0f 0x24

ao 3${register[$source]}${register[dest]};;

*) modenotsupported = ;;

esac

opnote = $*

fi

}

=extend ( ) { # copy with sign extension -x86 MOVSX

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel MOVSX\n

copy, sign-extending the destination.\n\n"

else ################

HL

parse $*

case $modestring in

1M*) ab 0x0f 0xbe

memref ${register[$dest]} ;;

M*) ab 0x0f 0xbf

memref ${register[$dest]} ;;

*) modenotsupported =extend ;;

esac

opnote =extend $*

fi

}

=0extend ( ) { # copy with zero-extension -x86 MOVZX

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel MOVZX\n

copy, filling the high-order bits of the destination with zeros. Much

different than a less-than-whole-register copy.\n\n\n"

else ################

HL

parse $*

case $modestring in

1M*) ab 0x0f 0xb6

memref ${register[$dest]} ;;

M*) ab 0x0f 0xb7

memref ${register[$dest]} ;;

*) modenotsupported =0extend ;;

esac

opnote =0extend $*

fi

}

1+ () { # increment -x86 INC

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel INC\n

add 1 to whatever. Effects flags.\n\n"

else ################

HL

parse $*

case $modestring in

1*) ab 0xfe

memref 0 ;;

M) ab 0xff

memref 0 ;;

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

*) modenotsupported increment ;;

esac

opnote 1+ $*

fi

}

1- () { # minus one, decrement -x86 DEC

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel DEC\n

decrement. 2 clocks.\n\n\n"

else ################

HL

parse $*

case $modestring in

1*) ab 0xfe

memref 1 ;;

M) ab 0xff

memref 1 ;;

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

*) modenotsupported decrement

echo $H ;;

esac

opnote 1- $*

fi

}

/ () { # divide. 38 clocks. -x86 DIV

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel DIV\n

38 clocks, no early-out. Divides D:A by arg, modulo in D, quotient in A,

I think."

else ################

HL

parse $*

ab 0xf7

ao 36${register[$source]}

opnote / $*

fi

}

+ () { # add -x86 ADD

if test "$1" = "h" ; then echo -e "\n\n

Add without including the carry (flag) bit. \n

See also +byte and +A.

"

else ################

HL

parse $*

case $modestring in

ID) ab 0x81

ao 30${register[2]}

ac ${number[$source]} ;;

DD) ab 0x01

ao 3${register[0]}${register[2]} ;;

MD) ab 0x03

memref ${register[2]} ;;

DM) ab 0x01

memref ${register[$source]} ;;

IM) ab 0x81

memref 0

ac ${number[$source]} ;;

1ID) ab 0x80

ao 30${register[2]}

ab ${number[$source]} ;;

1DD) ab 0x00

ao 3${register[0]}${register[2]} ;;

1MD) ab 0x02

memref ${register[2]} ;;

1DM) ab 0x00

memref ${register[0]} ;;

1IM) ab 0x80

memref 0

ab ${number[$source]} ;;

*) modenotsupported + ;;

esac

opnote + $*

fi

}

+A () { # add immediate to A, 32 bit only -x86 ADD

if test "$1" = "h" ; then echo -e "\n\n

Add immediate to A, 32 bit support only at this point.

"

else ############

HL

parse $*

ab 5

aq ${number[0]}

opnote +A $*

fi

}

+byte () { # add immediate byte -x86 ADD

if test "$1" = "h" ; then echo -e "\n\n

Add without including the carry (flag) bit, a byte to be sign-extended

and a register or memory argument of any size. If the value added to is

a page-aligned array you have a fast ring index, yes?

"

else ############

HL

parse $*

ab 0x83

memref 0

ab ${number[source]}

opnote +byte $*

fi

}

+carry () { # add with carry. -x86 ADC

if test "$1" = "h" ; then echo -e "\n\n

add two args and the carry bit. \n "

else ################

HL

parse $*

case $modestring in

ID) ab 0x81

ao 32${register[2]}

ac ${number[$source]} ;;

DD) ab 0x11

ao 3${register[0]}${register[2]} ;;

MD) ab 0x13

memref ${register[2]} ;;

DM) ab 0x11

memref ${register[$source]} ;;

IM) ab 0x81

memref 2

ac ${number[$source]} ;;

1ID) ab 0x80

ao 32${register[2]}

ab ${number[$source]} ;;

1DD) ab 0x10

ao 3${register[0]}${register[2]} ;;

1MD) ab 0x02

memref ${register[2]} ;;

1DM) ab 0x10

memref ${register[0]} ;;

1IM) ab 0x80

memref 2

ab ${number[$source]} ;;

*) modenotsupported +carry ;;

esac

opnote +carry $*

fi

}

+carryA ( ) { # add immediate to A, 32 bit only -x86 ADC

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SUB\n

add with carry immediate to A, 32 bit only."

else ################

HL

ab 0x15

aq ${number[0]}

opnote +carryA $*

fi

}

- () { # subtract. -x86 SUB

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SUB\n

Subtract without including borrow (carry) bit.\n\n"

else ################

HL

parse $*

case $modestring in

ID) ab 0x81

ao 35${register[2]}

ac ${number[$source]} ;;

DD) ab 0x29

ao 3${register[0]}${register[2]} ;;

MD) ab 0x2b

memref ${register[2]} ;;

DM) ab 0x29

memref ${register[$source]} ;;

IM) ab 0x81

memref 5

ac ${number[$source]} ;;

1ID) ab 0x80

ao 35${register[2]}

ab ${number[$source]} ;;

1DD) ab 0x28

ao 3${register[0]}${register[2]} ;;

1MD) ab 0x2a

memref ${register[2]} ;;

1DM) ab 0x28

memref ${register[0]} ;;

1IM) ab 0x80

memref 5

ab ${number[$source]} ;;

*) modenotsupported - ;;

esac

opnote - $*

fi

}

-test () { # do a subtract but save only the flags -x86 CMP

if test "$1" = "h" ; then echo -e "\n\n

Do a subtract but save only the flags."

else ################

HL

parse $*

case $modestring in

ID) if test "${register[$dest]}" = 0 ; then

ab 0x3d

ac ${number[0]}

else

ab 0x81

ao 37${register[2]}

ac ${number[$source]}

fi ;;

DD) ab 0x39

ao 3${register[0]}${register[2]} ;;

DM) ab 0x39

memref ${register[$source]} ;;

MD) ab 0x3b

memref ${register[$dest]} ;;

IM) ab 0x81

memref 7

ac ${number[$source]} ;;

1ID) if test "${register[$dest]}" = 0 ; then

ab 0x3c

ac ${number[0]}

else

ab 0x80

ao 32${register[2]}

ab ${number[$source]}

fi ;;

1DD) ab 0x38

ao 3${register[0]}${register[2]} ;;

1DM) ab 0x38

memref ${register[0]} ;;

1IM) ab 0x80

memref 7

ab ${number[$source]} ;;

*) modenotsupported -test ;;

esac

opnote -test $*

fi

}

-borrow () { # dest - source - carry --> dest -x86 SBB

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SBB\n

Subtract with borrow (carry). \n\n"

else ################

HL

parse $*

case $modestring in

ID) ab 0x81

ao 33${register[2]}

ac ${number[$source]} ;;

DD) ab 0x19

ao 3${register[0]}${register[2]} ;;

MD) ab 0x1b

memref ${register[2]} ;;

DM) ab 0x19

memref ${register[$source]} ;;

IM) ab 0x81

memref 3

ac ${number[$source]} ;;

1ID) ab 0x80

ao 33${register[2]}

ab ${number[$source]} ;;

1DD) ab 0x18

ao 3${register[0]}${register[2]} ;;

1MD) ab 0x1a

memref ${register[2]} ;;

1DM) ab 0x18

memref ${register[0]} ;;

1IM) ab 0x80

memref 3

ab ${number[$source]} ;;

esac

opnote -borrow $*

fi

}

AND () { # Boolean bitwise AND -x86 =

if test "$1" = "h" ;then echo -e "\n\n

Boolean bitwise AND. Result is true only if A AND B are true.

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

B

1 0

_|_______________

|

0 | 0 0

A |

1 | 0 1

\n"

else ################

HL

parse $*

case "$modestring" in

ID) ab 0x81

ao 34${register[2]}

ac ${number[$source]} ;;

DD) ab 0x21

ao 3${register[0]}${register[2]} ;;

MD) ab 0x23

memref ${register[2]} ;;

DM) ab 0x21

memref ${register[$source]} ;;

IM) ab 0x81

memref 4

ac ${number[$source]} ;;

1ID) ab 0x80

ao 34${register[2]}

ab ${number[$source]} ;;

1DD) ab 0x20

ao 3${register[0]}${register[2]} ;;

1MD) ab 0x22

memref ${register[2]} ;;

1DM) ab 0x20

memref ${register[0]} ;;

1IM) ab 0x80

memref 4

ab ${number[$source]} ;;

*) modenotsupported AND ;;

esac

opnote AND $*

fi

}

ANDtest () { # AND with no result but flags -x86 TEST

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel TEST\n

Do an AND and set the flags accordingly, but don't actually assert the

result value on either of the arguments. The quick A form requires A be the

destination arg and the immediate be the source, which is entirely syntactic

since nothing gets moved.

\n\n"

else ################

HL

parse $*

case $modestring in

ID) if test "${register[$dest]}" = 0 ; then

ab 0xa9

ac ${number[0]}

else

ab 0xf7

ao 30${register[2]}

ac ${number[$source]}

fi ;;

DD) ab 0x85

ao 3${register[0]}${register[2]} ;;

DM) ab 0x85

memref ${register[$source]} ;;

IM) ab 0xf7

memref 0

ac ${number[$source]} ;;

1ID) if test "${register[$dest]}" = 0 ; then

ab 0xa8

ac ${number[0]}

else

ab 0xf6

ao 30${register[2]}

ab ${number[$source]}

fi ;;

1DD) ab 0x84

ao 3${register[0]}${register[2]} ;;

1DM) ab 0x84

memref ${register[0]} ;;

1IM) ab 0xf7

memref 0

ab ${number[$source]} ;;

*) modenotsupported ANDtest

echo "386 ANDtest doesn't do MD)"

;;

esac

opnote ANDtest $*

fi

}

NOT () { # invert the bits -x86 =

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel NOT\n

Boolean bitwise not. Invert all the bits. All zeros become ones and

vice-versa.\n\n"

else ################

HL

parse $*

case $modestring in

D) ab 0xf7

ao 32${register[0]} ;;

M)

ab 0xf7

memref 2 ;;

1M)

ab 0xf6

memref 2 ;;

1D) ab 0xf6

ao 32${register[0]} ;;

*) modenotsupported not ;;

esac

opnote NOT $*

fi

}

OR () { # Boolean bitwise OR -x86 =

if test "$1" = "h" ; then echo -e "\n\n

Boolean bitwise OR. AKA "inclusive OR". If either source bit, A OR B, is

1, then result is 1.

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

B

1 0

_|_______________

|

0 | 1 0

A |

1 | 1 1

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

HL

parse $*

case $modestring in

1DD) ab 0x08

ao 3${register[0]}${register[2]} ;;

ID) if test "${register[$dest]}" = 0 ; then

ab 0x0d

ac ${number[0]}

else

ab 0x81

ao 31${register[2]}

ac ${number[$source]}

fi ;;

DD) ab 0x09

ao 3${register[0]}${register[2]} ;;

MD) ab 0x0b

memref ${register[2]} ;;

DM) ab 0x09

memref ${register[$source]} ;;

IM) ab 0x81

memref 1

ac ${number[$source]} ;;

1ID) if test "${register[$dest]}" = 0 ; then

ab 0x0c

ab ${number[0]}

else

ab 0x80

ao 31${register[2]}

ab ${number[$source]}

fi ;;

1MD) ab 0x0a

memref ${register[2]} ;;

1DM) ab 0x08

memref ${register[0]} ;;

1IM) ab 0x80

memref 1

ab ${number[$source]} ;;

*) modenotsupported OR ;;

esac

opnote OR $*

fi

}

XOR () { # Boolean bitwise exclusive OR -x86 =

if test "$1" = "h" ; then echo -e "\n

Boolean bitwise Exclusive-OR. Result is true if exclusively A OR B is

true. A XOR 1 toggles A, for example.

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

B

1 0

_|_______________

|

0 | 1 0

A |

1 | 0 1

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

HL

parse $*

case $modestring in

ID) if test "${register[$dest]}" = 0 ; then

ab 0x35

ac ${number[0]}

else

ab 0x81

ao 36${register[2]}

ac ${number[$source]}

fi ;;

DD) ab 0x31

ao 3${register[0]}${register[2]} ;;

MD) ab 0x33

memref ${register[2]} ;;

DM) ab 0x31

memref ${register[$source]} ;;

IM) ab 0x81

memref 6

ac ${number[$source]} ;;

1ID) if test "${register[$dest]}" = 0 ; then

ab 0x34

ac ${number[0]}

else

ab 0x80

ao 36${register[2]}

ab ${number[$source]}

fi ;;

1DD) ab 0x30

ao 3${register[0]}${register[2]} ;;

1MD) ab 0x32

memref ${register[2]} ;;

1DM) ab 0x30

memref ${register[0]} ;;

1IM) ab 0x80

memref 6

ab ${number[$source]} ;;

*) modenotsupported XOR ;;

esac

opnote XOR $*

fi

}

biton () { # set a particular bit to 1 -x86 BTS

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel BTS\n

Save bit in carry flag and set addressed bit to 1 in source value. 6

clocks.

\n\n"

else ################

HL

parse $*

case $modestring in

DM) ab 0x0f 0xab

memref ${register[$source]} ;;

DD) ab 0x0f 0xab

ao 3${register[0]}${register[2]} ;;

ID) ab 0x0f 0xba

ao 35${register[2]}

ac ${number[$source]} ;;

IM) ab 0x0f 0xba

memref 5

ac ${number[$source]} ;;

*) modenotsupported biton ;;

esac

opnote biton $*

fi

}

bitoff () { # set a particular bit to 0 -x86 BTR

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel BTR\n

Save bit in carry flag and reset to 0.\n\n"

else ################

HL

parse $*

case $modestring in

DM) ab 0x0f 0xb3

memref ${register[$source]} ;;

DD) ab 0x0f 0xb3

ao 3${register[0]}${register[2]} ;;

ID) ab 0x0f 0xba

ao 36${register[2]}

ac ${number[$source]} ;;

IM) ab 0x0f 0xba

memref 6

ac ${number[$source]} ;;

*) modenotsupported bitoff ;;

esac

opnote bitoff $*

fi

}

flagbit () { # get designated bit to the carry flag -x86 BT

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\Intel BT\n\n\n\n

Takes a 1 byte immediate or a reg/mem as a bit offset and a base address

or register spec and puts the value of the bit so specified into the carry

flag.

It's two bytes for the opcode, one for the literal maybe, or the usual

mod/sib bletchery. INTeL seems to prefer the carry bit as the general

Boolean, but I don't see why. In most coding situations I'd use ANDtest, a

4-byte literal, and ZF, thus staying more generic for a couple bytes. I

wouldn't have it in here except I used it in asmacs for askodd.

Lightly tested, if that.

\n\n\n"

else ################

HL

parse $*

case $modestring in

DM) ab 0x0f 0xa3

memref ${register[$source]} ;;

DD) ab 0x0f 0xa3

ao 3${register[0]}${register[2]} ;;

ID) ab 0x0f 0xba

ao 34${register[2]}

ab ${number[$source]} ;;

IM) ab 0x0f 0xba

memref 4

ab ${number[$source]} ;;

*) modenotsupported flagbit ;;

esac

opnote flagbit $*

fi

}

call () { # jump to subroutine, push return address -x86 CALL

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel CALL\n\n\n

Jump to the immediately following address or other value, normally that

of a subroutine, stacking a frame to return to on occurance of the return

(Intel RET) instruction. Frames vary widely by TYPE of call on 386+. There

are intersegment jumps, which are selectors defining gates of various

types in (32 bit) protected mode. call or similar is also known as jsr or

gosub on other machines. The variants of call usually require a FAR

syntactic spamatazoan in other assemblers. shasm syntaxes for the more

mutated forms of call are...

Call intersegment to full pointer given, shasm syntax...

segment offset

call dual 0xxxx to quad 0xxxxx

LAAETTR (gas doesn't do segments explicitly either, IIRC. Nor do most

other CPUs, BTW.)

\n\n"

else ################

HL

parse $*

case $modestring in

I) ab 0xe8

branch $1 $cell ;;

D) ab 0xff

ao 32$register ;;

*) modenotsupported call ;;

esac

opnote call $*

fi

}

compares ( ) { # plural -test, used by match -x86 CMPSx

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel CMPSx

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

Any argument to \"compares\" is taken as \"use bytes\". "

else ################

HL

parse $*

if test "$1" ;then

ab 0xa6 # if tH was a size spec it was byte

else

ab 0xa7

fi

opnote compares $*

fi

}

clearcarry ( ) { # set carry bit/flag to 0 -x86 CLC

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel CLC\n

unset the carry flag. Make it 0.\n\n\n"

else ################

HL

ab 0xf8

opnote clearcarry $*

fi

}

decreasing ( ) { # tell plurals to decr. C. Not the default.-x86 STD

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel STD\n

Set memory segment loop (string) operations direction flag to

towards-lower-addresses. Segment ops then will traverse the segments

high-to-low. \n"

else ################

HL

ab 0xfd

opnote decreasing $*

fi

}

downroll () { # down-significance bit-rotate -x86 ROR

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

Intel ROR\n

down-significance roll, rotate. Roll amount is source if immediate."

else ################

HL

parse $*

case $modestring in

ID) ab 0xc1

ao 31${register[2]}

ab ${number[$source]} ;;

IM) ab 0xc1

memref 1

ab ${number[$source]} ;;

D) ab 0xd3

ao 31${register[0]} ;;

M) ab 0xd3

ao 01${register[0]} ;;

1ID) ab 0xc0

ao 31${register[2]}

ab ${number[$source]} ;;

1IM) ab 0xc0

memref 1

ab ${number[$source]} ;;

1D) ab 0xd2

ao 31${register[0]} ;;

1M) ab 0xd2

ao 01${register[0]} ;;

*) modenotsupported downroll ;;

esac

opnote downroll $*

fi

}

downrollcarry ( ) { # down-significance bit-rotate thru carry -x86 RCR

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel RCR\n

down-significance roll, rotate. The carry bit is part of the roll.\n\n\n"

else ################

HL

parse $*

case $modestring in

ID) ab 0xc1

ao 33${register[2]}

ab ${number[$source]} ;;

IM) ab 0xc1

memref 3

ab ${number[$source]} ;;

D) ab 0xd3

ao 33${register[0]} ;;

M) ab 0xd3

ao 03${register[0]} ;;

1ID) ab 0xc0

ao 33${register[2]}

ab ${number[$source]} ;;

1IM) ab 0xc0

memref 3

ab ${number[$source]} ;;

1D) ab 0xd2

ao 33${register[0]} ;;

1M) ab 0xd2

ao 03${register[0]} ;;

*) modenotsupported downrollcarry ;;

esac

opnote downrollcarry $*

fi

}

downshift () { # down-significance bitshift, 0-fill high. -x86 SHR

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SAR\n

down-significance bitshift. TH are two. This is the one that fills the

high side vacated bits with 0. The low-order bit is shifted into the

carry flag. The other preserves the sign. That (Intel SAR) isn't present

in osimpa as I write this.\n"

else ################

HL

parse $*

case $modestring in

ID) ab 0xc1

ao 35${register[2]}

ab ${number[$source]} ;;

IM) ab 0xc1

memref 5

ab ${number[$source]} ;;

D) ab 0xd3

ao 35${register[0]} ;;

M) ab 0xd3

ao 05${register[0]} ;;

1ID) ab 0xc0

ao 35${register[2]}

ab ${number[$source]} ;;

1D) ab 0xd2

ao 35${register[0]} ;;

1M) ab 0xd2

memref 5

ab ${number[$source]} ;;

*) modenotsupported downshift ;;

esac

opnote downshift $*

fi

}

signeddownshift () { # down-significance bitshift, sign-fill high.-x86 SAR

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SAR\n

down-significance bitshift. There are two. This is the one that fills the

high side vacated bits with . The low-order bit is shifted into the

carry flag. The other preserves the sign. That (Intel SAR) isn't present

in osimpa as I write this.

Is now.

\n"

else ################

HL

parse $*

case $modestring in

ID) ab 0xc1

ao 37${register[2]}

ab ${number[$source]} ;;

IM) ab 0xc1

memref 7

ab ${number[$source]} ;;

D) ab 0xd3

ao 37${register[0]} ;;

M) ab 0xd3

ao 05${register[0]} ;;

1ID) ab 0xc0

ao 37${register[2]}

ab ${number[$source]} ;;

1D) ab 0xd2

ao 37${register[0]} ;;

1M) ab 0xd2

memref 7

ab ${number[$source]} ;;

*) modenotsupported downshift ;;

esac

opnote downshift $*

fi

}

extend () { # sign-extend A to D:A -x86 CWD

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel CWD\n

Sign-extend A into A:D, i.e. D becomes all the same as the sign bit of

A.\n\n"

else ################

HL

ab 0x99

opnote extend $*

fi

}

fetches () { # .unix-linux.todaysummary.com. SI to A, scaled in/decr. SI, decr. C -x86 LODSx

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LODSx

fetches loads the A register with the memory byte, word, or doubleword at

the location pointed to by the SI register. After the transfer is made,

the SI register is automatically advanced. If the direction flag is 0

(increasing was executed, which is the default state), the source index

increments. fetches doesn't decrement C. fetches doesn't make sense with

repeating and friends, but is useful if alone or in a loop of some kind,

for checksums, for example. See sum, xsum.

Any argument to fetches is taken as a directive to use bytes rather than

cells. "

else #######################

HL

parse $*

if test "$1" ;then

ab 0xac # if there was a size spec it was byte

else

ab 0xad

fi

opnote fetches $*

fi

}

flags () { # copy FLAGS into AX -x86 LAHF

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LAHF\n

copy FLAGS into AX

\h\h"

else ################

HL

ab 0x9f

opnote flags $*

fi

}

halt ( ) { # the CPU until an interrupt -x86 HLT

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel HLT\n

halt processor until next hardware interrupt. Be nice to your CPU.

This is what the Linux \"idle task\" does.\n\n

This probably wants to be at the start of the top loop of a kernel H3sm.

"

else ################

HL

ab 0xf4

opnote halt $*

fi

}

increasing ( ) { # set plurals to incr C. The default. -x86 CLD

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel CLD\n

Setstring operations direction flag to toward-higher-addresses\n\n"

else ################

HL

ab 0xfc

opnote increasing $*

fi

}

invertbit ( ) { # bit test and complement -x86 BTC

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel BTC\n

Save specified bit of operand into carry flag and complement it in operand.

\n\n"

else ################

HL

parse $*

case $modestring in

DM) ab 0xbb

memref ${register[$source]} ;;

DD) ab 0xbb

ao 3${register[0]}${register[2]} ;;

ID) ab 0xba

ao 37${register[2]}

ac ${number[$source]} ;;

IM) ab 0xba

memref 7

ac ${number[$source]} ;;

*) modenotsupported invertbit ;;

esac

opnote invertbit $*

fi

}

invertcarry ( ) { # flip the carry bit/flag -x86 CMC

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel CMC\n

flip the carry flag bit\n\n"

else ################

HL

ab 0xf5

opnote invertcarry $*

fi

}

jump () { # unconditional branch -x86 JMP

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel JMP\n

Partial support here. Hand-roll your far jumps.

Unconditional branch. Various addressing modes.

x86 has register INdirect absolute, but not direct or relative.

See jumpshort also.

\n\n\n"

else ################

HL

parse $*

case $modestring in

1I) ab 0xeb

branch $1 1 ;;

I) ab 0xe9

branch $1 $cell ;;

# register absolute

D) ab 0xff

memref 4

;;

# register and literal offset

M) ab 0xff

memref 4

ac $number ;;

*) modenotsupported jump ;;

esac

opnote jump $*

fi

}

jumpshort () { # unconditional branch, 1-byte range -x86 JMP

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel JMP\n

This is a different opcode than the cellwise jump. And I'm not in the mood

for parsing one token. And osimplay doesn't address the surprisingly nasty

problem of deciding jump ranges. It's your problem. So here. The 2-byte

jump. The usual is 5.

L bla

stuff # 127 bytes or less of stuff

jumpshort bla

\n\n\n"

else ################

HL

ab 0xeb

let jt=$1-$H-1

ab $jt

opnote jumpshort $*

fi

}

address () { # x86 artifact quick address-math thingy -x86 LEA

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LEA\n

Store effective address for memory reference in register, i.e. do the

address math but don't do the fetch, rather just store the address. This

does the address arithmatic and leaves the result of that, and doesn't

fetch the referenced object. This op is an artifact of the 386

architechture, and thus has limited applicability, but is extremely

efficient when it is useful. Write yourself a times5 macro to see what I

mean.

address does as much as

constant_literal + register + ( register *2^ [1,2,3]) to reg/mem dest

in two clock ticks, the minimum for any instruction. register can all be

the same or whatever. This is a 386 linear address, with no paging or

segment implications.

The register getting shifted cannot be SP in normal memrefs, and probably

not here.

This can be fun for optimizing things when it fits the task, but is

pathologically x86-specific, being an artifact of the 386's unusual

addressing prowess.

address .unix-linux.todaysummary.com. 400 A A *2^ 2 to C

SHOULD put 400+(A*5) in C. Test liberally.

\n\n"

else ################

HL

parse $*

ab 0x8d

memref ${register[$dest]}

opnote address $*

fi

}

lookup () { # byte lookup for e.g. ASCII->EBCDIC -x86 XLATB

if test "$1" = "h" ; then echo -e "\n\t\t\t\tIntel XLATB\n

Set AL to memory byte DS:[BX + unsigned AL]. One-byte instruction.

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

the index to.

This seems to be geared for rapid conversions such as between ASCII and

EBCDIC. The index value is replaced by the indexed value.

5 clocks.

\n"

else ################

HL

ab 0xd7

opnote lookup $*

fi

}

ls1bit () { # bit number of least significant 1-bit -x86 BSF

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel BSF\n

Find least significant ON-bit. 10 clocks +. Result is the number

of leading 0 bits.

BSF scans the bits in the second word or doubleword operand starting with

bit 0. The ZF flag is cleared if the bits are all 0; otherwise, the ZF flag

is set and the destination register is loaded with the bit index of the

first set bit.

\n\n"

else ################

HL

parse $*

ab 0x0f 0xbc

memref ${register[$dest]}

opnote ls1bit $*

fi

}

ms1bit () { # bit number of most significant 1-bit -x86 BSR

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel BSR\n

0F r32,r/m32 10+3n Bit scan reverse on r/m cell

Find most significant ON-bit. 10 + \(3 x offbits\) clocks.

Flags effected: Zero\n\n"

else ################

HL

ab 0x0f 0xbd

parse $*

memref ${register[$dest]}

opnote ms1bit $*

fi

}

multiply () { # arg * A --> D:A -x86 MUL

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel MUL\n

9 to 41 clocks. Early-out algorithm, unlike division.

source * A to D:A

\n\n"

else ################

HL

parse $*

case $modestring in

D) ab 0xf7

ao 34${register[0]} ;;

M) ab 0xf7

memref 4 ;;

1D) ab 0xf7

ao 34${register[0]} ;;

1M) ab 0xf7

memref 4 ;;

*) modenotsupported multiply ;;

esac

opnote multiply $*

fi

}

negate () { # 2's-complement; Boolean NOT, then incr. -x86 NEG

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel NEG\n

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

else ################

HL

parse $*

case $modestring in

D) ab 0xf7

ao 33${register[0]} ;;

M)

ab 0xf7

memref 3 ;;

1M)

ab 0xf6

memref 3 ;;

1D) ab 0xf6

ao 33${register[0]} ;;

*) modenotsupported negate ;;

esac

opnote negate $*

fi

}

nop () { # burn 3 clocks with no state effect -x86 NOP

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel NOP\n

no operation, eat some clock. x86 trivia, it's actually swap A with A or

something.

\n\n"

else ################

HL

ab 0x90

opnote nop $*

fi

}

recieve ( ) { # x86 "IO port" instruction -x86 IN

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel IN\n

Input from port

Takes a byte/dual/quad arg followed by an optional port IO address. The

port address will be compembled as a byte. Use DX to specify a high port.

recieve dual [ 0x20 ]

The actual code is the same for dual or quad, and a port in EDX is

effectively 16 bits. The recieved byte or cell will be in A.

\n\n\n\n"

else ################

HL

if test "$1" = "byte" ;then

if test $2 ;then

ab 0xe4 $2

else

ab 0xec

fi

else

if test $2 ;then

ab 0xe5 $2

else

ab 0xed

fi

fi

opnote recieve $*

fi

}

return () { # return from a near call -x86 RET

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel RET\n

Return from a call. An immediate argument assembled the frame-dropping

form. The immediate value is 16 bits to drop the stack (add to SP) by that

many bytes. osimplay entrance procedures use that form.

\n\n"

else ################

HL

parse $*

case $modestring in

I) ab 0xc2

ad ${number[$source]} ;;

*) ab 0xc3 ;;

esac

opnote return $*

HL

fi

}

send ( ) { # x86 "IO port" instruction -x86 OUT

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel OUT\n

Output to a port. immediate byte or contents of DX is port #. Datum is

always in A, which is byte or cell.

\n\n"

else ################

HL

if test $1 = "byte" ;then

if test $2 ;then

ab 0xe6 $2

else

ab 0xee

fi

else

ab 0xef

fi

opnote send $*

fi

}

setcarry ( ) { # set carry flag/bit to 1 -x86 STC

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel STC\n

assert carry=true, 1.\n\n"

else ################

HL

ab 0xf9

opnote setcarry $*

fi

}

setflags () { # copy AH to the FLAGS register-half -x86 SAHF

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SAHF\n

copy AH to the FLAGS register-half.\n\n"

else ################

HL

ab 0x9e

opnote setflags $*

fi

}

signedmultiply () { # partial -x86 IMUL

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel IMUL\n

these you'll have to roll by hand

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

sign-extended immediate byte

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

immediate word

from 386INTEL.TXT

IMUL has three variations:

3. A three-operand form; two are source and one is the destination

operand. One of the source operands is an immediate value stored in

the instruction; the second may be in memory or in any general

register. The product may be stored in any general register. The

immediate operand is treated as signed. If the immediate operand is a

byte, the processor automatically sign-extends it to the size of the

second operand before performing the multiplication.

3 distinct arguments. Gee I hate it when that happens. Use a directive to

tag the immediate value on the end of the thing. This isn't worth

reworking the parser. This thing is hideous and only about as fast as

floats anyway.

\n\n"

else ################

HL

parse $*

case "$modestring" in

*) modenotsupported signedmultiply ;;

esac

opnote signedmultiply $*

fi #nal

}

signeddivide () { # -x86 IDIV

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel IDIV\n

19 to 43 clocks. No early-out.

F7 /7 IDIV EAX,r/m32 43 Signed divide EDX:EAX by DWORD

byte (EAX=Quo, EDX=Rem)

Operation

temp ^[ dividend / divisor;

IF temp does not fit in quotient

THEN Interrupt 0;

ELSE

quotient ^[ temp;

remainder ^[ dividend MOD (r/m);

FI;

"

else ################

HL

parse $*

case $modestring in

# 11*) ab 0xf6

# memref 7 ;;

#

# 1c*) ab 0xf7

# memref 7 ;;

#

*) modenotsupported signeddivide ;;

esac

opnote signeddivide $*

fi

}

swap () { # swap two values. -x86 XCHG

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel XCHG\n

Exchange 2 values in one, usually 3 clock, instruction. swap locks the

bus during the swap, and Plan 9 therefor uses it for test-and-set. I

believe this is only an issue with SMP. All such instructions are atomic

vis-a-vis one CPU.

\n\n\n"

else ################

HL

parse $*

case $modestring in

D) ab $((0x90+${register[0]})) ;;

# and yes, "swap A" is in fact NOP ;o)

DD) ab 0x87

ao 3${register[0]}${register[2]} ;;

DM) ab 0x87

memref ${register[$source]} ;;

1DD) ab 0x86

ao 3${register[0]}${register[2]} ;;

1DM) ab 0x86

memref ${register[0]} ;;

*) modenotsupported swap

echo "

osimpa doesn't support using a memref

as the source operand to swap, which seems to be

slower that using a memref for the destination,

which seems to be merely an implementation

artifact, ie. for no real reason. The simplest

thing is to insist that memrefs be dest."

;;

esac

opnote exchange $*

fi

}

uprollcarry () { # up-significance bit roll through carry -x86 RCL

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel RCL\n

up-significance bit roll including carry in the ring of bits rolled"

else ################

HL

parse $*

case $modestring in

ID) ab 0xc1

ao 32${register[2]}

ab ${number[$source]} ;;

IM) ab 0xc1

memref 2

ab ${number[$source]} ;;

D) ab 0xd3

ao 32${register[0]} ;;

M) ab 0xd3

ao 02${register[0]} ;;

1ID) ab 0xc0

ao 32${register[2]}

ab ${number[$source]} ;;

1IM) ab 0xc0

memref 2

ab ${number[$source]} ;;

1D) ab 0xd2

ao 32${register[0]} ;;

1M) ab 0xd2

ao 02${register[0]} ;;

*) modenotsupported uprollcarry ;;

esac

opnote uprollcarry $*

fi

}

uproll () { # -x86 ROL

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel ROL\n

Rotate the bits in the up-significance direction. Bits rolling off the

high side roll back in on the low side.

"

else ################

HL

parse $*

case $modestring in

ID) ab 0xc1

ao 30${register[2]}

ab ${number[$source]} ;;

IM) ab 0xc1

memref 0

ab ${number[$source]} ;;

D) ab 0xd3

ao 30${register[0]} ;;

M) ab 0xd3

ao 00${register[0]} ;;

1ID) ab 0xc0

ao 30${register[2]}

ab ${number[$source]} ;;

1IM) ab 0xc0

memref 0

ab ${number[$source]} ;;

1D) ab 0xd2

ao 30${register[0]} ;;

1M) ab 0xd2

ao 00${register[0]} ;;

*) modenotsupported uproll ;;

esac

opnote uproll $*

fi

}

upshift () { # -x86 SAL

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SAL\n

Up-significance bitshift. Right shift, in the usual parlance. Zeros roll

in on low-significance end, bits are lost on the high-significance end.

Several forms take the shift amount from C implicitly. As of this writing,

I don't think those will get assembled more economically. FIX THIS

Shifts rock.

\n\n"

else ################

HL

parse $*

case $modestring in

ID) ab 0xc1

ao 34${register[2]}

ab ${number[$source]} ;;

IM) ab 0xc1

memref 4

ab ${number[$source]} ;;

D) ab 0xd3

ao 34${register[0]} ;;

M) ab 0xd3

ao 04${register[0]} ;;

1ID) ab 0xc0

ao 34${register[2]}

ab ${number[$source]} ;;

1IM) ab 0xc0

memref 4

ab ${number[$source]} ;;

1D) ab 0xd2

ao 34${register[0]} ;;

1M) ab 0xd2

ao 04${register[0]} ;;

*) modenotsupported upshift ;;

esac

opnote upshift $*

fi

}

when () { # IF. conditional branch. Many variants. -x86 jxx

if test "$1" = "h" ; then echo -e "\n\t\t\t\tIntel jxx

\"when\" is a wrapper for all the x86 conditional branches based on FLAGS

and also on the count register, C. \"when\" is used instead of \"if\" or

similar to not conflict with the shell, and I kinda like it subjectively.

when <not> <overflow> <<zero><carry>> <parity> <skew> $BRANCH_TARGET

e.g.

when not carry $target_labelname

skew is when

sign<>overflow. The actual opcode is constructed from these

tokens/values... not=1 overflow=0 sign=8 carry=2 zero=4 parity=10 skew=12

and this is trivial to implement because that's how the actual opcode is

built, so it seems to kinda want to be this way.

BUT, THer's MORE!(TM) I folded the variants of the Intel LOOP instruction

in here also. LOOP isn't a loop at all; it does forward branches just

fine. It is therefor when C-1 in osimplay, to imply that it performs the

decrement. There's also when C-1&zero and C-1&nonzero, which both also

decrement C before the test, and when C=0 which does NOT decrement C.

These latter when's only do short branches, but I haven't checked for the

branch width in the assembler. There are some thorny recursion issues with

figuring branch widths. The branches based on flags, i.e. not on C, can be

a byte or a cell. Cell (quad) is the default, and there's a \"short\"

token you can insert. I _COULD_ suss out backward branch widths without

that, but I haven't at this writing. Patches welcome. It seems the best

thing you can do for branch performance is rig your branches so the branch

is usually not taken, which is normally a bit better than a 2:1 win."

else #################

if test "$pass" = "2" ;then

HL

whenopnote="when "$*

fi

for branchtarg in $* ;do

:

done # $branchtarg is now branch target

branchsize=

opbase=0x70

let bla_=$#-1

while test $bla_ != "0" ;do

case $1 in # prevalence order roughly

not) let opbase=$opbase+1 ;;

zero) let opbase=$opbase+4 ;;

sign) let opbase=$opbase+8 ;;

short) branchsize=short ;;

carry) let opbase=$opbase+2 ;;

overflow) let opbase=$opbase ;;

C-1) let opbase=0xe2

branchsize=short ;;

C=0) let opbase=0xe3

branchsize=short ;;

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

branchsize=short ;;

C-1\&nonzero) let opbase=0xe0

branchsize=short ;;

parity) let opbase=$opbase+10 ;;

skew) let opbase=$opbase+12 ;;

*) echo $1 "

isn't a valid \"when\" qualifier."

;;

esac

shift

let bla_=$bla_-1

done

if test $branchsize ;then

#small

ab $opbase $(($branchtarg-$H-2))

if test $(($branchtarg-$H-2)) -gt 120 \

-o $(($branchtarg-$H-2)) -lt -120 ; then

echo "branchsize issue, a short branch may not be"

echo $H

fi

else

#BIG

ab 0x0f $(($opbase+16))

ac $(($branchtarg-$H-$cell))

fi

if test "$pass" = "2" ;then

echo -en " "$whenopnote >> $listing

fi

fi

}

widedownshift () { # partial -x86 SHRD

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SHRD\n

0F AC r/m32,r32,imm8 r/m32 gets SHR of r/m32 concatenatedwithr32

0F AD r/m32,r32,CL r/m32 gets SHR of r/m32 concatenated withr32

down-significance bitshift of a composite operand made of '

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

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

\n\n"

else ################

HL

parse $*

ab 0x0f 0xad

memref ${register[$source]}

opnote widedownshift $*

fi

}

wideupshift () { # partial -x86 SHLD

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SHLD\n

0F A4 SHLD r/m16,r16,imm8 r/m16 gets SHL of r/m16concatenated

with r16

0F A4 SHLD r/m32,r32,imm8 r/m32 gets SHL of r/m32concatenated

with r32

0F A5 SHLD r/m16,r16,CL r/m16 gets SHL of r/m16concatenated

partial support.

composite up-significance bitshift."

else ################

HL

ab 0x0f 0xa5

parse $*

memref ${register[$source]}

opnote wideupshift $*

fi

}

within () { # check value against 2 bounding values -x86 BOUND

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel BOUND\n

62/r BOUND r32,m32&32 10

Check if r32 is within bounds, (passes test). Bounds are adjacent

32 bit values in memory. Failure causes an exception,

Interrupt 5 if the bounds test fails.

C is notorious for letting you walk off the end of an array, but I don't

know that this is the appropriate cure.

\n\n"

else ################

HL

parse $*

ab 0x62

memref ${register[$dest]}

opnote within $*

fi

}

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

CS ( ) { # -x86 prefix

if test "$1" = "h" ; then echo -e "\n\n

Following instruction is to use segment CS\n"

else ################

HL

ab 0x2e

opnote CS $*

fi

}

SS ( ) { # -x86 prefix

if test "$1" = "h" ; then echo -e "\n

\nFollowing instruction is to use segment SS\n"

else ################

HL

ab 0x36

opnote SS $*

fi

}

DS ( ) { # -x86 prefix

if test "$1" = "h" ; then echo -e "\n

\nFollowing instruction is to use segment DS\n"

else ################

HL

ab 0x3e

opnote DS $*

fi

}

ES ( ) { # -x86 prefix

if test "$1" = "h" ; then echo -e "\n

\nFollowing instruction is to use segment ES.

Certain uses of DI are tied to ES.

\n"

else ################

HL

ab 0x26

opnote ES $*

fi

}

FS ( ) { # -x86 prefix

if test "$1" = "h" ; then echo -e "\n

\nFollowing instruction is to use segment FS.

FS gets the NULL selector in Ha3sm.

\n"

else #################

HL

ab 0x64

opnote FS $*

fi

}

GS ( ) { # -x86 prefix

if test "$1" = "h" ; then echo -e "\n \n

Following instruction is to use segment GS. Prefix. FS and GS will

probably get the NULL selector in Ha3sm.

\n"

else ################

HL

ab 0x65

opnote GS $*

fi

}

lock ( ) { # -x86 prefix

if test "$1" = "h" ; then echo -e "\n\nIntel LOCK\n

SMP instruction atomicity extender. exchange does a LOCK anyway, which is

what Plan9 uses. A pull SS also locks the next instruction, usually

pull SP.

\n"

else

HL

ab 0xf0

opnote lock $*

fi

}

clearswitched ( ) { # -386

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel CLTS\n

clear the task-switched flag in EFLAGS. Ha3sm doesn't use the 386

task-switch facilities, BTW. Most 386 unices do, I think.\n\n"

else ################

HL

ab 0x0f 6

opnote clearswitched $*

fi

}

frame ( ) { # Pascal in hardware. Know a use? -x86 = wENTER

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel ENTER\n

Pascal in hardware. Know a use?

frame is a 386 complex instruction for creating a lexical level frame for

lexical block languages like Pascal. It fetches a list of pointers and

pushes thier values onto the stack.

See also: dropframe

frame ( 16bit framesize, 8bit lexical levels)

This is the psuedocode description of the thing based on INTEL386.TXT

...................................................

{

level = level MOD 32 // level is byte 4 of instr encoding

// MOD 32 is AND 11111b

Push BP

frame-ptr = SP // frame-ptr is a CPU-internal temp var

if level > 0

{

for (i = 1 TO (level - 1))

{

BP = BP - 4

Push value _at_ BP

}

Push frame-ptr

}

BP = frame-ptr // BP is now old

SP = SP - ZeroExtend(First operand)

}

...................................................

frame can take up to 139 clocks, depending on the levels argument. A

task-switch is about 300. The most interesting things I see about frame is

that it uses two internal variables that aren't registers, and that it

does a looping dereference over an array of up to 31 pointers. That is, it

collects dispersed values. It does all this atomically, which is important

when molesting the return stack. frame/dropframe is what gives BP it's

framepointer designation. They are the only instructions that use BP

implicitly. frame would need it's own syntax for it's arguments, and I'll

probably never use the thing, so I just coopt the shasm source/dest for

levels/size. In shasm syntax levels is source, frame size is dest, i.e.

source and dest don't mean what they usually do e.g.

levels size

frame 20 to 3 \n\n"

else ################

HL

parse $*

ab 0xc8

ad ${number[$source]}

ab ${number[$dest]}

opnote enter $*

fi

}

dismiss ( ) { # -x86 IRET

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel IRET\n

Interrupt return. Various stack effects per system state. A dismiss frame

is (E)IP, CS and (E)FLAGS, EIP at TOS. Some CPU protection exceptions push

an error code also.

\n"

else ################

HL

ab 0xcf

opnote dismiss $*

HL

HL

fi

}

farSS ( ) { # -386

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LSS\n

Load SS:r32 with pointer from memory\n"

else ################

HL

parse $*

ab 0x0f 0xb2

memref $dest

opnote farSS $*

fi

}

farDS ( ) { # -386

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LDS\n

Load DS:r32 with pointer from memory\n\n"

else ################

HL

parse $*

ab 0xc5

memref $dest

opnote farDS $*

fi

}

farES ( ) { # -386

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LES\n

Load ES:register with pointer from memory\n\n"

else ################

HL

parse $*

ab 0xc4

memref $dest

opnote farES $*

fi

}

farFS ( ) { # -386

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LFS\n

Load FS:register with pointer from memory\n\n"

else ################

HL

parse $*

ab 0x0f 0xb4

memref $dest

opnote farFS $*

fi

}

farGS ( ) { # -386

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LGS\n

Load GS:r32 with pointer from memory\n\n"

else ################

HL

parse $*

ab 0x0f 0xb4

memref $dest

opnote farGS $*

fi

}

#You set it, right?

#

#GDT ( ) { # -386

#if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SGDT\n

#store contents of Global Descriptor Table Register to memory at physical

#address. The obverse of this is crucial to protected mode.

#\n"

# else ################

#

# HL

# parse $*

# ab 0x0f 0x01

# if test "$cell" = 2 ; then

# ab 6

# else

# ab 5

# fi

# ac ${number[$source]} # physical address

# opnote GDT $*

# fi

# }

#

#

#IDT ( ) { # -386

#if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SIDT\n

#store contents of Interrupt Descriptor Table Register to memory at

#physical address. The obverse of this is crucial to protected mode.

#\n"

# else ################

#

# HL

## parse $*

# ab 0x0f 1

# if test "$cell" = 2 ; then

# ab 0x0e

# else

# ab 0x0d

# fi

# ac ${number[$source]} # physical address

# opnote IDT $*

# fi

# }

#

#

#LDT ( ) { # -386

#if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SLDT\n

#store Local Descriptor Table Register to memory physical address.

#

#If you use the 386 task register stuff and TSSs then I believe the deal

#is that each task has/needs an LDT.

#\n\n"

# else ################

# HL

# parse $*

# ab 0x0f 0

# if test "$cell" = 2 ; then

# ab 6

# else

# ab 5

# fi

# ac ${number[$source]} # physical address

# opnote LDT $*

# fi

# }

IRQson () { # -386

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel STI\n

Allow external hardware to interrupts the CPU. \"nosurprises\"

turns them off.

\n\n"

else ################

HL

ab 0xfb

opnote IRQson $*

fi

}

IRQsoff () { # -386

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel STI\n

Allow external hardware to interrupts the CPU. \"nosurprises\"

turns them off.

\n\n"

else ################

HL

ab 0xfa

opnote IRQsoff $*

fi

}

dropframe ( ) { # -x86 LEAVE

if test "$1" = "h" ; then echo -e "\n\t\t\t\tIntel LEAVE\n

De-allocate a Pascal-style module frame. Enter and leave are what makes

BP/ebp the \"frame pointer\". see also: enter. I don't see these as real

useful, but \"enter\" does a lot of stuff."

else ################

HL

ab 0xc9

opnote dropframe $*

fi

}

limit ( ) { # -x86 LSL

if test "$1" = "h" ; then echo -e "\n\t\t\t\tIntel LSL\n

load the limit value from a segment descriptor. \n"

else ################

HL

ab 0x0f 3

parse $*

memref ${register[$dest]}

opnote limit $*

fi

}

IRQsoff ( ) { # -x86 CLI

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel CLI\n

Disable external hardware interrupts to the CPU.

Useful at boot time maybe and for profound state-changes like

process-switches.\n\n"

else ################

HL

ab 0xfa

opnote IRQSoff $*

fi

}

overflowtrap ( ) { # -x86 INTO

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel INTO\n

cause invocation of a trap handler IF overflow bit is set.\n\n"

else ################

HL

ab 0xce

opnote overflowtrap $*

fi

}

readable ( ) { # -386 VERR

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel VERR\n

Set zero flag to true if segment of given selector can be read by

this process.

\n\n"

else ################

HL

ab 0x0f 0

parse $*

memref 4

opnote readable $*

fi

}

pullcore ( ) { # -386 POPA

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel POPA\n

copy (pop) DI, SI, BP, SP, B, D, C, and A off the stack.

adjusting stack pointer SP accordingly.\n\n"

else ################

HL

ab 0x61

opnote pullcore $*

fi

}

pull ( ) { # partial # unstack -386 POP

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel POP\n

Copy top of stack into operand, adjusting stack pointer SP accordingly.

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

\n\n"

else ################

HL

parse $*

case $modestring in

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

M) ab 0x8f

memref 0 ;;

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

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

ab 0x07

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

ab 0x17

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

ab 0x1f

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

ab 0x0f 0xa1

elif test ${register[$source]} = "5" ;then # GS

ab 0x0f 0xa9

else

echo -e "\n\npush doesn't support

" $modestring " mode. "

fi ;;

*) modenotsupported pull ;;

esac

opnote pull $*

fi

}

pullflags ( ) { # -386 POPF

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel POPF\n

copy top of stack into flags reg, adjusting stack pointer SP

accordingly.\n\n"

else ################

HL

ab 0x9d

opnote pullflags $*

fi

}

pushcore ( ) { # -386 PUSHA

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel PUSHA\n

copy the eight main regs onto the stack, adjusting stack pointer SP

accordingly.

This takes 18 clocks, and pullcore takes 24. That's 5.25 clocks per reg,

or 6 not counting SP. A push/pull of one reg takes 6. (2 for push, 4 for

pull.)

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

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

modesize.

\n\n"

else ################

HL

ab 0x60

opnote pushcore $*

fi

}

pushflags ( ) { # -386 PUSHF

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel PUSHF\n

copy FLAGS onto the top of stack, adjusting stack pointer SP

accordingly.\n\n"

else ################

HL

ab 0x9c

opnote pushflags $*

fi

}

push ( ) { # -386 PUSH

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel PUSH\n

copy operand onto top of stack, adjusting stack pointer SP

accordingly. See pushcore also for timings.\n\n"

else ################

HL

parse $*

case $modestring in

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

M) ab 0xff

memref 6 ;;

I) ab 0x68

ac ${number[$source]} ;;

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

*segm)

if test ${register[$source]} = "0" ;then # ES

ab 0x06

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

ab 0x0e

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

ab 0x16

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

ab 0x1e

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

ab 0x0f 0xa0

elif test ${register[$source]} = "5" ;then # GS

ab 0x0f 0xa8

else

echo -e "\n\npush doesn't support

" $modestring " mode. "

fi ;;

*) modenotsupported push ;;

esac

opnote push $*

fi

}

recieves ( ) { # -x86 INSB

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel INSB\n

Input cells/bytes from port DX into ES:DI. Doing this with \"repeating\"

is too fast for many external devices. Any arg means use bytes,

e.g.

= 0x220 to D

recieves bytes "

else ################

HL

if test -n "$1" ;then

ab 0x6c

else

ab 0x6d

fi

opnote recieves $*

fi

}

sends ( ) { # -x86 OUTS

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel OUTS\n

Send bytes/cells to an x86 IO port address.\n\n"

else ################

HL

if test -n ${size[$source]} ;then

ab 0x6e # if there was a size spec it was byte

else

ab 0x6f

fi

fi

}

submit ( ) { # trap to supervisor -x86 INT

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel INT\n

see also: trapoverflow, debug, Linux version of osimplay\n\n"

else ################

HL

ab 0xcd $1

opnote submit $*

fi

}

returnfar ( ) { # -x86 RET/RETF

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel RET(F) AT&T lret\n

Return from a far (intersegment) call. \n\n"

else ################

HL

ab 0xcb

opnote returnfar $*

fi

}

priviledge ( ) { # -386

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel ARPL\n

Adjust requested priviledge level of r/m16 to not less than RPL of r16\n\n"

else ################

HL

ab 0x63

parse $*

memref ${register[$source]}

opnote priviledge $*

fi

}

loadmachinestatusdual ( ) { # -286

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LMSW\n

286 control register shortcut, 16 bit. Deprecated, but using CR0 explicitly

musses some other register, which is usuallt a problem when switching modes.

\n\n"

else ################

HL

ab 0x0f 1

parse $*

memref 6

opnote loadmachinestatusdual $*

fi

}

# useless.

# # Got milk?

#storemachinestatusdual ( ) { # -286

#if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel SMSW\n

#Store machine status dual to EA dual#

#

#Legacy 286 thing. Can save a byte or two on a bootsector.\n\n"

# else ################

# HL

# parse $*

# # check possible

# ab 0x0f 1

# memref 4

# opnote storemachinestatusdual $*

# fi

# }

#

task ( ) { # -x86 LTR

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LTR\n

Load EA dual into task register. \n\n"

else ################

HL

ab 0x0f 0

parse $*

memref 3

opnote task $*

fi

}

trapoverflow ( ) { # -x86

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel INT\n

see also: trapoverflow, debug\n\n"

else ################

HL

ab 0xce

opnote trapoverflow $*

fi

}

rights ( ) { # -x86 LAR

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LAR\n

r16 becomes r/m16 masked by FF00 \n\n"

else ################

HL

ab 0x0f 2

parse $*

memref ${register[$dest]}

opnote rights $*

fi

}

setGDT ( ) { # -386

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LGDT\n

Load pointer at memory operand into Global Descriptor Table Register.

This and setIDT are the only instructions that always interpret an address

as physical, since they are the trunk of the memory protection

scheme.

\n\n"

else ################

HL

parse $*

ab 0x0f 01

if test "$cell" == 2 ; then

ab 0x16

else

ab 0x15

fi

ac ${number[$source]}

opnote setGDT $*

fi

}

setIDT ( ) { # -386

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LIDT\n

Load pointer at memory operand into Interrupt Descriptor Table Register.

This and setGDT are the only instructions that always interpret an address

as physical, since they are the trunk of the memory protection scheme.

\n\n"

else ################

HL

ab 0x0f 0x01

parse $*

if test "$cell" == 2 ; then

ab 0x1e

else

ab 0x1d

fi

ac ${number[$source]}

opnote setIDT $*

fi

}

setLDT ( ) { # -386

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel LLDT\n

Load pointer at memory operand into Local Descriptor Table Register.\n\n"

else ################

HL

ab 0x0f 0x00

if test "$cell" == 2 ; then

ab 0x16

else

ab 0x15

fi

parse $*

ac ${number[$source]}

opnote setLDT $*

fi

}

# You loaded it, right?

#savetask ( ) { # -386

#if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel STR\n

# Load task register into EA dual. Ha3sm won't use the 386+ task handling

#facilities. Most 386 unices do I think.\n\n "

# else ################

# HL

# ab 0x0f 0x00

# parse $*

# memref 1

# opnote savetask $*

# fi

# }

#

#

writeable ( ) { # -386

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel VERW\n

Test if current process is allowed to write to given segment.

This and \"readable\" are the same Intel mnemonic, VERW."

else ################

HL

ab 0x0f 0

parse $*

memref 5

opnote writeable $*

fi

}

#()

# OSIMPA COMPEMBLER ]]]]] ()

# oooooooooooooooooooooooooooooooooooooooo

oooooooooooooooooooooooosimpa

# Rick Hohensee www.clienux.com humbubba.unix-linux.todaysummary.com.smart.net

# jan/feb 2001

# compembler for shasm with COWPP(TM) Rick Hohensee 2001

# In C you hear a lot about "tell the compiler...". Here

# the assembler state is analagous.

# If you want to assemble things piecemeal set $pass by hand.

# an args checker routine would be handy

beam () { # Fill a range of an xray with a jump address

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

Fill in a range in an xray execution array with calls to a word.

beam routine_address <start> <end>

To save myself some hassle, <end> is required, but may be equal to start for

single-wave beams (a wave is one xray section entry). Several beams

between xray and yarx specify the code addresses in an xray execution

array. Each beam spec overwrites previous ones, if any, in the assembly

state of the xray. Thus to set the array default you do the default first

over the whole xray. When yarx happens it asserts the accumulated specs in

the assembly output. A single xray entry is an address and a hike for the

routine tied to that array index. Indexing is from 0, so max end is

size-1. That is, start and end are integers. See yarx h for more. "

else ################

if test "$pass" = "2" ;then

bi=$2

while test $bi -le $3 ;do

# beams[$bi]=$1-$xray_H

beams[$bi]=$1

let bi=$bi+1

done

fi

fi

}

cell () { # name/allot a cell-size storage location

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

e.g.

cell argc

makes room and a label for a global cell-size variable. Doesn't align.

Usually used after \"heap\" (ELF thing).

"

else ###############

for item in $*

do

L $item

allot $cell

done

fi

}

copies () { # plural, range-to-range copy, handles overlaps

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

Assemble code to copy a range of memory at SI of length C*size to DI. The

copy proceeds in whatever direction DF is, usually increasing. If you give

it an argument it copies bytes at a time, rather than cells. C is

decremented per copy, not bytes. SI and DI are also in/decremented

accordingly. DF becomes an issue if source and destination ranges overlap.

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

any argument to copy or copies means copy byte-wise.

"

else

HL

ab 0xf3

opnote repeating

copy $1

fi

}

copy ( ) { # one "copies". .unix-linux.todaysummary.com. SI to .unix-linux.todaysummary.com. DI -x86 MOVSD

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\tIntel MOVSD

Move data .unix-linux.todaysummary.com. SI to .unix-linux.todaysummary.com. DI. 7 clocks per datum. SI and DI cannot be offset.

SI and DI are incremented or decremented in parallel by the size of a

datum. \"copies\" is usually used with \"repeating\", in which case it

will block-move C data items. See copies. copy takes one optional

argument, anything, to specify that data are in bytes, so I usually do

something cute like

copy byte

where \"of\" is the

argument and the \"bytes\" is in effect a comment.

See also: \"copies\".

copy is interesting because it is an atomic memory-to-memory move, and the

only example of such a move that comes to mind on x86. That is, x86 doesn't

have add mem, mem as a stock addressing mode, as many CISCs do.

The mems get you, of course. copy is 7 clocks. It isn't guaranteed to lock

the bus though, like exchange is, which matters on SMP. I'm also intrigued

because copy uses the x86 Phantom Register they don't give you explicit

access to. maybe it's the icache or something.

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

any argument to copy or copies means copy byte-wise.

\n\n\n"

else ################

HL

if test "$1" ;then

ab 0xa4 # if there was any argument copy bytes

else

ab 0xa5

fi

opnote copy $*

fi

}

clump () { # C struct() kinda, data associations namer

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

clump creates a set of related offset names. This sort of thing is

popular for associating arbitrary data items into groups. The data

associations can themselves be useful information. Data items in a clump

are defined by thier spacing, and thus have implied sizes.

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

will make \$clumptypename_item0 \$clumptypename_item1 and so on be the

appropriate offsets from \$clumptypename in the assembler state.

\$clumptypename itself has a value of zero. A clumptypename_size is also

created that records the overall size of this type of clump. Sizes are in

bytes. Those offset values can then be used during assembly to instantiate

and perhaps initialize clumps of that type, or as address offsets at

runtime, including offsets on the return stack.

There's nothing yet to automate instantiating a clump. All I have is the

names of the field offsets of the clump type. Let's say you have a defined

PIC clump. You can make one ala...

L PIC1

allot PIC_size

And set a fields ala

= 0 to \$((PIC1+PIC_counter))

= 300 to \$((PIC1+PIC_count))

et cetera. It's about 5% of C structs. Personnally I think it's 80% of the

useful aspects of C structs. How to nest them is left as an excercise to

the reader.

"

else ###############

cname=$1

let os=0

let $cname=0

shift

while test "$1" ;do

let ${cname}_$1=$os

let os=os+$2

shift ; shift

done

let ${cname}_size=$os

fi

}

leave () { # return from the current reentrant procedure

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

This is not 386+ LEAVE.

A leave is a termination point of an osimplay reentrant routine. There

may be 0, 1 or more of these in a procedure. The current routine remains

current for assembly until the next routine definition starts, so there

may be several leaves to an entrance. entrance/enter/leave routines are

reentrant if you use the 15 stack familial variables for parameters and

return values.

"

else ###############

HL

ab $((0xc2))

ad $((15*$cell))

opnote leave

fi

}

fill () { # plural, copies A across range .unix-linux.todaysummary.com. 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 \".unix-linux.todaysummary.com. 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 \".unix-linux.todaysummary.com. 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+ .unix-linux.todaysummary.com. $(($1-($cell*4))) # index + 1

= .unix-linux.todaysummary.com. $(($1-($cell*3))) to A # mask

AND A to .unix-linux.todaysummary.com. $(($1-($cell*4)))

else

if test "$2" = "cell" ; then # cell incr.

+ $cell to .unix-linux.todaysummary.com. $(($1-($cell*4)))

= .unix-linux.todaysummary.com. $(($1-($cell*3))) to A

AND A to .unix-linux.todaysummary.com. $(($1-($cell*4)))

else # ply incr.

if ! test "$2" = "ply" ; then

echo "Creative ring increment spec, assuming ply."

fi

= .unix-linux.todaysummary.com. $(($1-($cell*2))) to A # ply

+ .unix-linux.todaysummary.com. $(($1-($cell*4))) to A

AND .unix-linux.todaysummary.com. $(($1-($cell*3))) to A

= A to .unix-linux.todaysummary.com. $(($1-($cell*4)))

fi

fi

fi

}

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

= .unix-linux.todaysummary.com. $(($1-($cell*4))) to A # index

if test "$3" ; then # cell store

= $2 to .unix-linux.todaysummary.com. $1 A

else

= byte $2 to .unix-linux.todaysummary.com. $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 .unix-linux.todaysummary.com. 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 .unix-linux.todaysummary.com. DI, which gets incremented as per argument

size. osimpa \"scan\" is a \`plural', and uses the 386

repeat-while-non-zero prefix. When \"scan\" finishes repeating, if ZF is

1, i.e. if zero=true, then DI is the byte address of the byte or cell

FOLLOWING the match. If the direction flag is \"increasing\" that's how

\"scan\" proceeds, i.e. toward higher addresses.

C is decremented by ones. DI is decremented by the operand size, which

defaults to byte. \"scan\" will stop on a hit, or continue until the count

in C is 0. The count is per item, which may be cells or bytes.

Example use:

# assuming A, DI and C are set up already

# keep DF in mind also

scan

when zero got_a_hit

\"scan\" is an inline composite, i.e. a macro, but it's only 2 bytes."

else ################

HL

if ! test "$1" ;then

ab 0xf2 0xae

else

ab 0xf2 0xaf

fi

opnote SCAN $*

fi

}

strand () { # name/allot a general array and 8 cells of metadata

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

A strand is a count-cell-prefixed string, a byte array, a sized array, a

masked ring, a multi-reader ring buffer, a dessert topping AND a

floorwax. e.g.

strand <sname> <bytes> [<ply> <other stuff>]

The idea of strands is take a data allocation, add metadata as negative

offsets from the 0-byte address of the net data, keep the metadata in a

known consistent order for all types and subtypes, and you have an

interlocking set of datatypes that can all use all the ops for thier own

type and any simpler type. Sortof.

The data format of a strand is currently...

lower addresses

cell undecided

cell seen

cell west

cell east

cell index

cell mask

cell ply

cell size in bytes

<-- nominal address

DATA

DATADATA

DATADATADATA...

higher addresses

ZO, a full strand definition might be...

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

but this is a first cut, the sequence may change yet."

else ###############

# eval ${1}_ply=$3

let mask=`lsoepot $2`-1

# eval let ${1}_mask=`lsoepot $2`-1

HL

aq 0

opnote strand $1

HL

aq 0

HL

aq 0

HL

aq 0

HL

aq 0

opnote index

HL

aq $mask

opnote mask

HL

if test "$3" ; then

aq $3

else

aq 1

fi

opnote ply

HL

# eval ${1}_bytes=$2 # eh?

aq $2

opnote size

L $1

allot $2 ;fi

}

sum () { # plural, additive checksum

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

Arithmetic cell checksum. Add Count contiguous cells to B. Sets B to 0

first. sum is an inlining of 3 looping instructions. A is clobbered also."

else ###############

opnote SUM

woobie=$RANDOM

zero B

L sum$woobie

fetches

+ A to B

when C-1 sum$woobie

opnote .

fi

}

tag () { # tag <string> ASCII bytes and zeros in a cell

if test "$1" = "h" ; then echo -e "\n\t\t\t\t

tag is a cellsize character(s) literal. tag blahzay on a machine

where cell=4 labels a cell as blahzay, allocates the cell, and initializes

it with the ASCII values of b, l, a and h. The ASCII value of b will be in

the byte within the cell with the lowest address. If the given string has

less characters than a cell has bytes then the cell is padded out with

zero-bytes.

Since the 386 is little-endian, tag a will have a numeric value of

0x61, but tags aren't really for arithmatic. They are for unique test

arguments. Tags are handy for simple and thus fast commandsets, case

switching, inner interpreters and so on. You see a lot of C case switches

based on 'c' character literals. tag is just as fast, more general, and

more hackish. I was doing this sort of thing a lot in the editor I was

writing, by hand, and I see a lot of the same basic thing in Menuet, so it

seems to be worth having an actual directive for. Don't sweat the

endianness non-issue.

When defining tags, shell quoting applies. tag uses osimplay asciibyte

in a loop.

"

else

L $1

HL

tagbyte=0

tagfeed=${#1}

tmore=yes

while test "$tmore" ;do

curchar=${1:$tagbyte:1}

if test "$curchar" ;then

ab `asciibyte $curchar`

else

ab 0

fi

let tagbyte=$tagbyte+1

if test $tagbyte -eq $cell ;then

tmore=

fi

done

opnote tag

fi

}

text () { # name/allot some text

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

Assemble text literally from a shell here-document. If a name is given

assemble a count-cell prefix and label the following byte with the given nam

e.

That is, the label is at the data, not the length cell prefix.

e.g.

text ernie <<!

stuff

other stuff

!

"

else ###############

IFS=

let textinstance=$textinstance+1

let temp=0

if test $1 ;then

HL

aq ${textlength[$textinstance]}

if test $pass = 2 ; then

echo -n " TEXT length" >> $listing

fi

L $1

echo >> $listing

fi

while read line ;do

H=$H+1

ascii $line

if test $pass = 2 ; then

echo -en "\012" >> $output

fi

let temp=$temp+${#line}+1

done

textlength[$textinstance]=$temp

IFS="

"

fi

}

cells="*4"

# frame (familial) variables. Look at the assembly 386 gcc produces.

# GOBS of this.

sa=" .unix-linux.todaysummary.com. 4 SP "

sb=" 8 + SP "

sc=" 12 + SP "

sd=" 16 + SP "

se=" 20 + SP "

sf=" 24 + SP "

sg=" 28 + SP "

sh=" 32 + SP "

si=" 36 + SP "

sj=" 40 + SP "

sk=" 44 + SP "

sl=" 48 + SP "

sm=" 52 + SP "

sn=" 56 + SP "

so=" 60 + SP "

# 64

pa="68 + SP"

pb="72 + SP"

pc="76 + SP"

pd="80 + SP"

pe="84 + SP"

pf="88 + SP"

pg="92 + SP"

ph="96 + SP"

pi="100 + SP"

pj="104 + SP"

pk="108 + SP"

pl="112 + SP"

pm="116 + SP"

pn="120 + SP"

po="124 + SP"

west ( ) { # get a strand's pointing-west pointer

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

Given the 0-offset address of a strand's data in I, get the \"west\"

pointer into A."

else #################

= -$(($cell*5)) I to A

fi

}

xjump () { # indexed jump into an xray execution array

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

Jump on an xray beam. i.e. use an execution array. Example...

xjump \$dave B

where dave is an existing xray, compembles a jump through the xray beam at

the cell offset in the B register in the execution array (jump table)

\"dave\". Masking to the size of the array and similar prudent activities

are your problem. Only the register given gets clobblered. The cost of an

xjump is 11+ clocks. With a tree-of-conditionals switch/case your single

best case is two clocks to get into a condition, but then you also have to

get out. With an execution array best case and worst case are the same.

There appear to be two ways to use an xray. You can make the beams osimpa

\"define\" routines, in which case the beam will return to the caller's

caller, or you can hand-roll a bunch of branches for the beams.

xray currently only works with cell=4 .

"

else ############################

= $1 A *2^ 2 to A

ab 0xff 0xe0

fi

}

xray () { # name an execution array for beam, yarx and xjump

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

xray is the first of three words used to assemble an execution array.

xrays are named jump tables and the table items are subroutines, so they

return to the xray's \"caller\". xray sets the assembly state, does some

checks, and waits for beams. beam sets a range of jump targets to a

routine's address. Beam specs clobber earlier beam specs they overlap, so

you set the default ones first and overwrite them with the special cases.

When you have all your beams lined up you use yarx (xray backwards) and

yarx actually compembles the jump table based on the accumulated compembly

state.

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

xray asciithang 256

should work. I'll put a longer example in yarx h ."

else #####################

L $1

xraysize=$2

xray_H=$H

fi

}

xsum () { # plural, XOR-ing checksum

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

XOR cell checksum. XOR Count contiguous cells into B. Sets B to 0 first.

sum is an inlining of 3 looping instructions. A is clobbered also. It's

sensitive to the direction flag, which defaults to \"increasing\" in

osimplay."

else ###############

opnote XSUM

xwoobie=$RANDOM

zero B

L xsum$xwoobie

fetches

XOR A to B

when C-1 xsum$xwoobie

opnote .

fi

}

yarx () { # finish compembling an xray and it's beams

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

Complete the compembling of an xray. Assuming a memref like...

xray specs 128

beam glyph 33 127 # some no-op or something

beam control 0 32

beam delete 127 127

yarx

The example, BTW,

is a rudimentry ASCII handler. When the above gets to yarx tH's a

complete xray specified in assembler state and yarx finishes up, doing the

output of actual bytes. Given the above yarx will create a jump table with

jumps to the control routine if A is between 0 and 32 when the now-named

specs xray is invoked, the delete routine if A is 127, and the glyph

routine otherwise. glyph could have been 0 127 with the same end result.

The clobberableness factor might be handy for complex xray layouts. "

else ######################

yi=0

while test $yi -lt $xraysize ;do

HL

ac ${beams[$yi]}

let yi=$yi+1

done

fi

}

zero () { # simple convenience to set (a) register(s) to 0

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

Use XOR to set a list of registers to zero. More convenience

than elegance. Well, improves readability too.

zero A C D

"

else ######################

for reg in $* ;do

XOR $reg with $reg

done

fi

}

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

echo "

osimplay enter and leave are nothing like the 386 instructions with the

INTeL mnemonics ENTER and LEAVE. They are much simpler procedures using

the 386 return-from-subroutine-with-drop-stack-frame. Stack 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.

"

}

regspew () { # raw binary register dump to stderr

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

Raw binary dump of the usual 8 core registers and FLAGS to FD 2. Give it

an argument to uniqueify labels by if you have more than one regspew in a

program. regspew writes out pad bytes to 48 total so things are lined up

nice in a binary editor. The order is NOT as per how they are stacked, A B

C D SP BP SI DI. Stick a regspew in your code, assemble, do

~binary 2> bla

dump bla

and the regs in the dump will be...

A B C D

00000000 00 00 00 00 EF BE AD DE 00 00 00 00 00 00 00 00 ..........

..

SP BP SI DI

00000010 94 FB FF BF F8 FB FF FF 37 01 00 00 00 00 00 00 ......7...

..

FLAGS who's yer diddy?

00000020 86 02 00 00 00 00 00 00 00 00 5F 5F 53 50 45 57 ........__S

PEW

"

else ###############

addtosuffix " regspew "

pushcore

pushflags

= A to .unix-linux.todaysummary.com. $spewzone

= $spewzone to A

= B to .unix-linux.todaysummary.com. 4 A

= C to .unix-linux.todaysummary.com. 8 A

= D to .unix-linux.todaysummary.com. 12 A

= SP to .unix-linux.todaysummary.com. 16 A

= BP to .unix-linux.todaysummary.com. 20 A

= SI to .unix-linux.todaysummary.com. 24 A

= DI to .unix-linux.todaysummary.com. 28 A

pull B

= B to .unix-linux.todaysummary.com. 32 A

= 2 to B

= $spewzone to C

= 48 to D

Linux $write

pullcore

jump $endspewzone

L spewzone

allot 42

ab `asciibyte _ _ S P E W `

L endspewzone

dropsuffix

fi

}

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

heap () { # start uninititialized data sub-section

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

Unix tradition is that there's a loader segment called .bss that has

defined locations in it for stuff, but isn't allocated in the program's

stored image. This is great for uninitialized global data. osimpa \"heap\"

allows you to specify an analagous sub-section of the one main segment the

\"ELF\" command creates at the end of said segment. This allows the

example program \"get\" for example to not have to contain a 4k buffer in

the stored program image.

"

else ###############

echo $H "at heap"

opnote heap ALLOCATED_ABOVE UNALLOCATED_BELOW

align 16

endinitialized=$H

allocated=no

fi

}

echo ELF

#segment stuff

let spam=0

let segreadable=4

let segwriteable=2

let segexecutable=1

let segallperms=7

let loadaddress=0

let segphydress=$spam

let segfileoffset=0

let segalign=4096

#ELF header itself stuff

let entrypoint=$loadaddress+52+32 # one segment only

ELFsegment ( ) { # not user useable yet.

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

compemblink a program header table entry. A segment spec in other words.

We do one by default, R/W/X perms. You can make the load size bigger than

the store size for a .bss equivalent."

else #########################

aq 1 0 $loadaddress $loadaddress

echo " segment header" >> $listing

aq $segstoresize $segloadsize 7 0x1000

echo >> $listing

fi

}

ELFcalcs ( ) { # =shasm ELF

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

calculate segloadsize and segstoresize using pass-1-produced data."

else ###############

if test $pass = 2 ;then

if test $endinitialized ;then

let segstoresize=$endinitialized

else

let segstoresize=$lasthere

fi

let segloadsize=$lasthere

echo " segstoresize= " $segstoresize

echo " endinitialized= " $endinitialized

echo " here " $H

echo " lasthere " $lasthere

echo "memory load size " $segloadsize

fi

fi

}

ELFid ( ) { # begin ELF header =shasm ELF

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

This puts the first 16 bytes of ELF header together, with the E L F,

some machine spec numbers, and o s i m p a <linefeed>."

else #########

echo -en "\177ELF" > $output

echo -n "7f E L F " > $listing

let H=$H+4

ab 1 1 1 0 0 # 386 stuff

echo -en "osimpa" >> $output

echo -en " o s i m p a " >> $listing # (TM) ;o)

ab 0x0a

let H=$H+6

fi

}

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

echo "loadaddress " $loadaddress

segstoresize=0

segloadsize=0

cell=4

initop # SUSPECT

charcount=0

let textinstance=0

endinitialized=

let H=0

: > $output

pass=1

. $1

echo "loadaddress " $loadaddress

segstoresize=0

segloadsize=0

cell=4

initop # SUSPECT

charcount=0

let textinstance=0

endinitialized=

let H=0

: > $output

pass=1

. $1

unset suffix # SUSPECT

let textinstance=0

let lasthere=$H

let H=0

pass=2

echo "PASS 1 ABOVE======== ^- BUGS -v ==================PASS 2 BELOW"

. $1

fi

}

ELFheader ( ) { # just the 52 bytes of the main header

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

Construct an ELF header for a static executable using \$loadaddress

and pass 1 assembly state info."

else ################

allocated="yes"

ELFid

echo >> $listing

ad 2 3

aq 1 $entrypoint 52

echo >> $listing

aq 0 0

ad 52 32 1 40

echo >> $listing

ad 0 0

echo >> $listing

fi

}

ELF () { # build simple but complete ELF header

ELFcalcs

ELFheader

ELFsegment

}

echo "Linux syscalls "

## several doodads to have meta listings notes

let suffixes=1

suffixout ( ) { # retrofitted into herelist

let suffixcounter=$suffixes

totalsuffix=

while ! test "$suffixcounter" = "1"

do

totalsuffix=$totalsuffix${suffix[$suffix

counter]}

let suffixcounter=$suffixcounter-1

done

echo -n " ##" $totalsuffix >> $listing

}

addtosuffix ( ) { # takes a string argument to prefix the current prefix wit

h

let suffixes=$suffixes+1

suffix[$suffixes]=$1

}

dropsuffix ( ) { # reverse the above

let suffixes=$suffixes-1

}

# ()

# HOST OS DEPENDANT ()

# call number args

_lls="140"

_sysctl="149"

access="33"

acct=" 51"

adjtimex="124"

alarm=" "

bdflush=""

brk=" "

capget=""

capset=""

chdir=" "

chmod=" 15"

chown=" 182"

chroot="61"

clone=" 120"

close=" 6"

creat=" 8"

dup2=" 63"

dup=" 41"

execve=" 11"

exit=" 1"

fchdir=" 133"

fchmod=" 94"

fchown=" 95"

fcntl=" 55"

fdatasync=" 148"

flock=" 143"

fork=" 2"

fstat=" 108"

fstatfs=" 100"

fsync=" 118"

ftruncate=" 93"

get_kernel_syms=" 130"

getcwd=" 183"

getdents=" 141"

getegid=" 50"

geteuid=" 49"

getgid=" 47"

getgroups=" 80"

getitimer=" 105"

getpgid=" 132"

getpgrp=" 65"

getpid=" 20"

getppid=" 64"

getpriority=" 96"

getresgid=" 171"

getrusage=" 77"

gettimeofday=" 78"

getuid=" 24"

idle=" 112"

ioctl=" 54"

ioperm=" 101"

iopl=" 110"

ipc=" 117"

kill=" 37"

lchown=" 16"

link=" 9"

lock=" 53"

ls=" 19"

lstat=" 107"

mkdir=" 39"

mknod=" 14"

mlock=" 150"

mlockall=" 152"

mmap=" 90"

mount=" 21"

mprotect=" 125"

mremap=" 163"

msync=" 144"

munlock=" 151"

munlockall=" 153"

munmap=" 91"

nanosleep=" 162"

nfsservctl=" 169"

nice=" 34"

oldfstat=" 28"

oldlstat=" 84"

open=" 5"

pause=" 29"

pipe=" 42"

poll=" 168"

prctl=" 172"

pread=" 180"

ptrace=" 26"

pwrite=" 181"

quotactl=" 131"

read=" 3"

readdir=" 89"

readlink=" 85"

readv=" 145"

reboot=" 88"

rename=" 38"

rmdir=" 40"

sched_get_priority_max=" 159"

sched_get_priority_min=" 160"

sched_getparam=" 155"

sched_getscheduler=" 157"

sched_rr_get_interval=" 161"

sched_setparam=" 154"

sched_setscheduler=" 156"

sched_yield=" 158"

sendfile=" 187"

setdomainname=" 121"

setfsgid=" 139"

setfsuid=" 138"

setgid=" 46"

setgroups=" 81"

sethostname=" 74"

setitimer=" 104"

setpgid=" 57"

setpriority=" 97"

setregid=" 71"

setresgid=" 170"

setresuid=" 164"

setreuid=" 70"

setrlimit=" 75"

setsid=" 66"

settimeofday=" 79"

setuid=" 23"

sgetmask=" 68"

sigaction=" 67"

sigaltstack=" 186"

sigpending=" 73"

sigprocmask=" 126"

sigreturn=" 119"

sigsuspend=" 72"

socketcall=" 102"

ssetmask=" 69"

stat=" 106"

statfs=" 99"

stime=" 25"

swapoff=" 115"

swapon=" 87"

symlink=" 83"

sync=" 36"

sysfs=" 135"

sysinfo=" 116"

syslog=" 103"

time=" 13"

times=" 43"

truncate=" 92"

umask=" 60"

umount=" 22"

uname=" 122"

unlink=" 10"

uselib=" 86"

ustat=" 62"

utime=" 30"

vhangup=" 111"

vm86=" 166"

wait4=" 114"

waitpid=" 7"

write=" 4"

writev=" 146"

# pytes missing

chown32=" 212"

truncate64=" 193"

stat64=" 195"

fchown32=" 207 3"

setresuid32=" 208 "

lchown32=" 198 "

getgid32=" 200 0"

setgid32=" 214 "

setuid32=" 213 "

setregid32=" 204 "

fcntl64=" 221 3"

getresgid32=" 211 3"

setfsgid32=" 216 "

setfsuid32=" 215 "

setresgid32=" 210 "

setgroups32=" 206 "

fstat64=" 197 "

getresuid32=" 209 "

ftruncate64=" 194 2"

getgroups32=" 205 "

setreuid32=" 203 "

getuid32=" 199 "

lstat64=" 196 "

getdents64=" 220 3"

getegid32=" 202 0"

geteuid32=" 201 0"

# can't find argc

break=" 17"

ftime=" 35"

vfork=" 190"

stty=" 31"

mpx=" 56"

modify_ldt=" 123"

umount2=" 52"

getpmsg=" 188"

ugetrlimit=" 191 /* SuS compliant getrlimit */ "

ulimit=" 58"

personality=" 136"

vm86old=" 113"

getresuid=" 165"

prof=" 44"

profil=" 98"

getresuid=" 165"

kgetsid=" 147"

mmap2=" 192"

signal=" 48"

pivot_root=" 217"

_newselect=" 142"

gtty=" 32"

madvise1=" 219 /* delete when C lib stub is removed */ "

madvise=" 219"

mincore=" 218"

putpmsg=" 189 /* some people actually want streams */ "

oldolduname=" 59"

oldstat=" 18"

olduname=" 109"

query_module=" 167"

select=" 82"

# ? ?

# ? ?

# # POSIX realtime?

rt_sigaction=" 174 "

rt_sigpending=" 176 "

rt_sigprocmask=" 175 "

rt_sigqueueinfo=" 178 "

rt_sigreturn=" 173 "

rt_sigsuspend=" 179 "

rt_sigtimedwait=" 177 "

Linux () { # syscalls, e.g. Linux $read, many available

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

\"Linux\", the osimpa command, takes a syscall number. There are variables

named for many of the syscalls, so you can do

Linux \$read

for example. That does the trap and sets A to the pertinent syscall

number, but you still have to put the arguments in B, C, D... as per usual

depending on the arguments the syscall takes. For Linux syscalls the

C-oriented manpages give an argument order that maps onto B, C, D, SI, DI.

osimpa Linux assumes/requires the syscall number argument, and then can

accept further args. The args go in the manpage order.

Just do

type Linux

It's not that obscure. :o)

"

else ##########

addtosuffix " Linux "

= $1 to A

if test "$2" ;then

eval = $2 to B

fi

if test "$3" ;then

eval = $3 to C

fi

if test "$4" ;then

eval = $4 to D

fi

if test "$5" ;then

eval = $5 to SI

fi

if test "$6" ;then

eval = $6 to DI

fi

submit 128

dropsuffix

fi

}

print () { # write all of a range's net data to stdout

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

If you've named a region, say, BABBLE,

print $BABBLE

will send it's

contents to stdout. This is handy for static text. It writes the whole

range as defined, so you don't have to count characters. It doesn't do

variables insertions like C printf. See \"text\" also."

else #########################

addtosuffix " print "

= .unix-linux.todaysummary.com. $(($1-4)) to D

= $1 to C

= 1 to B

Linux $write

dropsuffix

fi

}

newline () { # write a (unix) newline to stdout

if test "$1" = "h" ; then echo -e "\n\t\t\t\t\t

Highly counter-optimal, but handy. Related to Forth cr. Prints a decimal

10, which is ASCII linefeed, not carriage-return) to stdout.

If you use osimplay \"text\" and \"print\" you get the linefeeds inately.

Unix text EOL is LF. (The one thing Mickeysoft got right and Bell Labs got

wrong.)

"

else ######################

jump $(($here+8)) # label-avoidance ugliness

ab 10 10 10 # literal data causing the above

= $(($here-2)) to C

= 1 to B

= B to D

Linux $write

fi

}

All Comments

Leave a comment...

  • 8 Comments
    • On Mon, 25 Oct 2004, cLIeNUX user wrote:

      Rick, do you have to keep posting this here? Wouldn't a simple

      several-line summary about this, including a link to the project's

      web site, be better?

      Rich Teer, SCNA, SCSA, author of "Solaris Systems Programming"

      President,

      Rite Online Inc.

      Voice: +1 (250) 979-1638

      URL: http://www.rite-group.com/rich

      #1; Wed, 30 Apr 2008 09:55:00 GMT
    • humbubba.unix-linux.todaysummary.com.smart.net

      >On Mon, 25 Oct 2004, cLIeNUX user wrote:

      >Rick, do you have to keep posting this here? Wouldn't a simple

      >several-line summary about this, including a link to the project's

      >web site, be better?

      >--

      >Rich Teer, SCNA, SCSA, author of "Solaris Systems Programming"

      >President,

      >Rite Online Inc.

      >Voice: +1 (250) 979-1638

      >URL: http://www.rite-group.com/rich

      WHy is posting it in this dead newsgroup a problem?

      Rick (Richard Allen) Hohensee

      write-in candidate, President of the United States of America

      platform ftp://linux01.gwdg.de/pub/cLIeNUX/interim/platform2

      personal webpage http://linux01.gwdg.de/~rhohen

      active in Usenet alt.politics colorg on IRC

      humbubba.unix-linux.todaysummary.com.smart.net Maryland, USA

      Ground troops out of Iraq Put the CIA under INS Save Darfur

      Semi-legalize drugs Prosecute Bush Tighten the borders

      Isolate Israel Tax churches halve military aquisitions

      government jobs for Iraq-wounded soldiers and 9-11 survivors

      please email my platform to friends, blogs and countrymen

      ---

      #2; Wed, 30 Apr 2008 09:56:00 GMT
    • r.unix-linux.todaysummary.com.cLIeNUX. (cLIeNUX user) writes:

      > humbubba.unix-linux.todaysummary.com.smart.net

      > WHy is posting it in this dead newsgroup a problem?

      Trolling is *always* a problem.

      Mns Rullgrd

      mru.unix-linux.todaysummary.com.inprovide.com

      #3; Wed, 30 Apr 2008 09:57:00 GMT
    • On Mon, 25 Oct 2004, cLIeNUX user wrote:

      > WHy is posting it in this dead newsgroup a problem?

      The other posters' comments notwithstanding:

      1. If the newsgroup is dead (which one? You crossposted

      to three.), why are you wasting the time and effort

      (not to mention the bandwidth) to post here?

      2. The group where I'm reading this, comp.unix.programmer,

      is FAR from dead.

      Rich Teer, SCNA, SCSA, author of "Solaris Systems Programming"

      President,

      Rite Online Inc.

      Voice: +1 (250) 979-1638

      URL: http://www.rite-group.com/rich

      #4; Wed, 30 Apr 2008 09:58:00 GMT
    • In article <Pine.SOL.4.58.0410250924310.2262.unix-linux.todaysummary.com.zaphod.rite-group.com>, Rich Teer wrote:[color

      =darkred]

      > On Mon, 25 Oct 2004, cLIeNUX user wrote:

      >

      > The other posters' comments notwithstanding:

      > 1. If the newsgroup is dead (which one? You crossposted

      > to three.), why are you wasting the time and effort

      > (not to mention the bandwidth) to post here?

      > 2. The group where I'm reading this, comp.unix.programmer,

      > is FAR from dead.

      >[/color]

      Maybe he doesn't know what a newsgroup is.

      Jim Cochrane; jtc.unix-linux.todaysummary.com.dimensional.com

      [When responding by email, include the term non-spam in the subject line to

      get through my spam filter.]

      #5; Wed, 30 Apr 2008 09:59:00 GMT
    • humbubba.unix-linux.todaysummary.com.smart.net

      >In article <Pine.SOL.4.58.0410250924310.2262.unix-linux.todaysummary.com.zaphod.rite-group.com>, Rich T

      eer wrote:

      >Maybe he doesn't know what a newsgroup is.

      >

      I've heard that about 90% of Usenet is binaries. You guys might check some

      of them out. There might be mpgs of how to pull your head out of your ass.

      Rick (Richard Allen) Hohensee

      write-in candidate, President of the United States of America

      platform ftp://linux01.gwdg.de/pub/cLIeNUX/interim/platform2

      personal webpage http://linux01.gwdg.de/~rhohen

      active in Usenet alt.politics colorg on IRC

      humbubba.unix-linux.todaysummary.com.smart.net Maryland, USA

      Ground troops out of Iraq Put the CIA under INS Save Darfur

      Semi-legalize drugs Prosecute Bush Tighten the borders

      Isolate Israel Tax churches halve military aquisitions

      government jobs for Iraq-wounded soldiers and 9-11 survivors

      please email my platform to friends, blogs and countrymen

      ---

      >--

      >Jim Cochrane; jtc.unix-linux.todaysummary.com.dimensional.com

      >[When responding by email, include the term non-spam in the subject line to

      >get through my spam filter.]

      #6; Wed, 30 Apr 2008 10:00:00 GMT
    • Rich Teer <rich.teer.unix-linux.todaysummary.com.rite-group.com> wrote:

      > On Mon, 25 Oct 2004, cLIeNUX user wrote:

      >

      > The other posters' comments notwithstanding:

      > 1. If the newsgroup is dead (which one? You crossposted

      > to three.), why are you wasting the time and effort

      > (not to mention the bandwidth) to post here?

      > 2. The group where I'm reading this, comp.unix.programmer,

      > is FAR from dead.

      yes, only one of the 3 is close to "dead". Google counts for the past

      year are:

      15,900 in comp.unix.programmer

      12,800 in comp.lang.forth

      137 in alt.os.assembly

      I have no idea what releveance the original post has to Forth.

      #7; Wed, 30 Apr 2008 10:01:00 GMT
    • humbubba.unix-linux.todaysummary.com.smart.net

      >Rich Teer <rich.teer.unix-linux.todaysummary.com.rite-group.com> wrote:

      >

      >yes, only one of the 3 is close to "dead". Google counts for the past

      >year are:

      >15,900 in comp.unix.programmer

      >12,800 in comp.lang.forth

      >137 in alt.os.assembly

      >

      >I have no idea what releveance the original post has to Forth.

      I like to think osimplay is what you get if you have to have native code

      performance on one-stack hardware but you learned a lot from Forth.

      Rick (Richard Allen) Hohensee

      write-in candidate, President of the United States of America

      platform ftp://linux01.gwdg.de/pub/cLIeNUX/interim/platform2

      personal webpage http://linux01.gwdg.de/~rhohen

      active in Usenet alt.politics colorg on IRC

      humbubba.unix-linux.todaysummary.com.smart.net Maryland, USA

      Ground troops out of Iraq Put the CIA under INS Save Darfur

      Semi-legalize drugs Prosecute Bush Tighten the borders

      Isolate Israel Tax churches halve military aquisitions

      government jobs for Iraq-wounded soldiers and 9-11 survivors

      please email my platform to friends, blogs and countrymen

      ---

      #8; Wed, 30 Apr 2008 10:02:00 GMT