This is a dynamic version of the handbook that can be edited by anyone with a valid key. Please be aware that this uses a custom markup language that is not directly related to other markup languages.


reload | edit
Other Formats: ascii, ps


Welcome to RetroForth

RetroForth is an implementation of the Forth programming language. It provides a flexible set of words, block editor, and interactive environment in one small package.

The implementation is built over the Rx Core; a very small library that provides the core functionality needed by a Forth system. The development of Rx Core and RetroForth are related. This handbook is based on the Rx Handbook, and some areas will reflect that.

RetroForth does not follow any of the various standards for Forth that have been created over the years. Rather, the words and their behaviour has evolved over a period of eight years to service those using it. The code is continually being refined; new things added, and obsolete things phased out. It is an active codebase.


What are word classes?

A concept called word classes is at the heart of RetroForth. Word classes are a way to group related words, based on their compilation and execution behaviors. A special word, called a class handler, is defined to handle an execution token passed to it on the stack. The compiler uses a variable named class to set the default class when compiling a word. We'll take a closer look at class later. Rx provides several classes with differing behaviors:

.class ( xt -- )
The class handler for the provided classes. 
During compilation, a call to the xt is compiled. 
During interpretation, the xt is executed.

.data ( xt -- )
.data is used for data structures and numbers. It 
will leave the xt on the stack during interpretation 
and compile the xt as a literal during compilation.

.forth ( xt -- )
The class handler for forth words. 
Semantics are the same as with .class

.self ( xt -- )
Words using this class are always called, unless used 
in a definition with a prefixing word immediately 
before them. Generally words using this class will 
need to be state-smart.

.macro ( xt -- )
Used for compiler macros, words with a class of .macro 
are always called during compilation (unless preceded 
by a prefixing word). They are ignored during 
interpretation.

.inline ( xt -- )
This is a special class used for certain primitives. 
It will copy the machine code (up to a hex value of 
$C3, the ret instruction) into the current definition 
during compilation. During interpretation the word 
is called normally. Only words that do not use $c3 
and have no calls to other words can use the .inline 
class.

The class mechanism is not limited to these classes. You can write custom classes at any time. On entry the custom handler should take the XT passed on the stack and do something with it. Generally the handler should also check the compiler state to determine what to do in either interpretation or compilation. As a simple example, let's take a look at the .data hander, written in Forth:

( handler for data types )
( This compiles the XT as a literal in a definition. If interpreting, just leave )
( the XT on the data stack. )
( For constants, we make use of a clever trick. We set the XT to the value of the )
( constant and give it a class of .data allowing it to work without requiring any )
( special code or create/does> constructs )
: .data ( xt -- xt | ) state @ if literal, then ;

When writing programs in Rx, you will need to set the class of each word you write. There are numerous ways to do this. A look at the example below will show how each method appears in source. We'll use the .forth class in the examples, but the same approach applies to all of the classes.

' .forth class ! ( set the default class to .forth )
' .forth reclass ( set the class of the last defined word to .forth )
' .forth reclass: wordname ( set wordname to the .forth class )

Because changing the default class will be done regularly, the main classes also have wrapper functions provided. These wrappers are given a class of .forth and are listed below. In general, it's recommended to use these wrappers to set the default class for readability reasons.

forth  ( -- )
Set .forth as the active class

macro  ( -- )
Set .macro as the active class

self   ( -- )
Set .self as the active class

inline ( -- )
Set .inline as the active class

Dictionary Headers

The Rx Core has a single dictionary consisting of a linked list of headers. The current form of a header is shown in the chart below. Pay special attention to the accessors. Each of these words corresponds to a field in the dictionary header. When dealing with dictionary headers, it is recommended that you use the accessors to access the fields since it is expected that the exact structure of the header will change over time.

accessor:  :link
offset:    0
Use:       Link to the previous header. If set to 0, 
           no prior entries are visible.

accessor:  :xt
offset:    4
Use:       Link to the compiled code definition. For 
           constants, the value of the constant is stored 
           in this field.

accessor:  :class
offset:    8
Use:       Link to the class handler.

accessor:  :doc
offset:    12
Use:       Store a pointer to a documentation string, source, or
           other documentation source.

accessor:  :name
offset:    16
Use:       The name of the word, stored as a packed string.

Word names are stored in the dictionary as packed strings. Use unpack to obtain a counted string from it. In memory, a packed string looks like:

count (one byte)
actual string (a sequence of bytes)

Because of the use of a single byte for the count, word names are limited to 255 characters.

The most recently created header can be accessed by using a variable named last. To access a specific header, you can use >entry to get the header that points to a specified XT. A special variable named which is also updated by find each time a word is looked up in the dictionary. This variable always points to the entry of the most recently found word.

Given only a single dictionary, it might seem limiting, however both vocabularies and lexical scope have been implemented on top of it. We will look at each of these now.


Lexical Scope

There will be often be times you would like to "hide" some words that you only use once or twice to build other words. In Rx, the ability to do this is provided by the lexical scoping words loc: and ;loc

A typical use looks like:

loc:                     ( start a lexically scoped sequence )
  loc:                   ( start a lexically scoped sequence )
    10 constant ten
    20 constant twenty
    10 20 * constant two-hundred
    here ] two-hundred ; ( anonymous definition )
    words                ( debugging; show words in dictionary )
  ;loc is value          ( close the lexically scoped sequence and reveal )
                         ( the anonymous definition as "value" )
  words                  ( debugging; show words in dictionary )
  : a value 10 / ;
  : b value 20 / ;
  here ] a b * . cr ;    ( anonymous definition )
;loc is foo              ( close the lexically scoped sequence and reveal )
                         ( the anonymous definition as "foo" )
words ( debugging; show words in dictionary )

As you can see in the example above, when a lexically scoped definition is closed, the headers for all words inside the scoped region are removed. Only words explicitly revealed are kept in in the global dictionary. Also, as is shown in the example, you can nest lexically scoped sequences. Nesting is possible up to four levels deep.

Lexical scopes are used frequently by the developers of Rx and allow a good level of factoring without polluting the actual dictionary with words that are not needed outside a select few definitions.


Handling Syntax Errors

By default, the Rx Core ignores syntax errors. This behavior can be altered freely by those using Rx by replacing the word? hook with a new definition. The word? hook is the "last chance" error handler. When the interpreter obtains a token it tries to process it in a defined order:

1. Look up the token in the dictionary. If found, execute its class handler
2. Attempt to convert the token to a number. If valid, execute the .data
   class handler to determine the course of action to take
3. Call word? to hopefully resolve the problem.

You can extend this or simply report errors by changing word? to point to a new definition. As a vectored word, this is easily done. A simple example is shown below.

( Example of a custom error handler. )
here is word? ] ( ac-f ) ." could not identify: " type cr false ;

This replaces the default definition of word? with a new handler that displays a nice error message.

Of course it is possible to write significantly more flexible error handlers. For instance, you could write a handler for entering floating point numbers, say >float, and hook that into word? to allow the user to enter floating point numbers directly at the interpreter.


Vectored Words

Many words in Rx are defined as vectored words. These are similar to, but more flexible than, deferred words in ANS Forth. A vectored word will have vector as the first word in its definition. This is the only step necessary to make a word vectorable. Four words are provided for creating and working with vectors.

vector   ( - )
Put this as the first word in a definition to make it 
into a vectorable word.

is       ( a"- )
Set the following wordname to point to the address on 
the stack. If the word does not exist, create it as a
vector defaulting to the passed address.

devector ( "- )
Remove the vector set by is.

default: ( a"- )
Used in a special form, e.g:
 : foo vector vector ;
This will set the *second* vector to the passed
address.

As an example:

( simple example of vectors )
: a vector 12 ;
: b vector 23 ;
: c vector a b * ;
: d a . ." times " b . ." equals " c . cr ;
d
here is a ] 23 ;
d
here is b ] 13 ;
d
here is c ] a b + ;

d
devector a
d
devector b
d
devector c
d

While a bit simplistic, this does show how to use vectors. Vectors can also be used with lexically scoped sequences to make revealing multiple words cleaner:

( another example )
: a vector ;
: b vector ;

loc:
10 constant ten
20 constant twenty

: * later prior * ;
: + later prior + ;

10 + 20 constant thirty
20 * 20 constant four-hundred

here is a ] ." 10+20=" thirty . cr ;
here is b ] ." 20*20=" four-hundred . cr ;
;loc

Learning to use vectored words will allow you to explore new options in your programming.


Word Prefixes

Release 9.2 of RetroForth introduced word prefixes, which are single character prefixes that can enhance readability and add new features. More to come.

~name         -- return the xt of name
`name         -- compile a call to name
!name @name   -- store and fetch from a variable called name
+name -name   -- add to and subtract from the variable name's current contents

Quotes

coming soon

Can be nested up to four levels deep. Useful inside words or at the console.

[[
]]

Appendix 1: Glossary of Words

.class ( xt -- ) .class
Class handler for the provided classes

.forth ( xt -- ) .class
Class handler for Forth words

.macro ( xt -- ) .class
Class handler for compiler macros

.inline ( xt -- ) .class
Class handler for Forth words that can be inlined

.data ( xt -- ) .class
Class handler for basic data structures

forth ( -- ) .forth
Set the .forth class as the active class

macro ( -- ) .forth
Set the .macro class as the active class

self ( -- ) .forth
Set the .self class as the active class

inline ( -- ) .forth
Set the .inline class as the active class

entry ( addr count -- ) .forth
Create a dictionary entry pointing to here. A string containing the name
of the word to be created is passed on the stack. The word is created with
the current class.

lookup ( addr count --- xt flag | addr count flag ) .forth
Attempt to find a word in the dictionary. If successful, it returns the
address of the word and the true value. If unsuccessful, it leaves the
string containing the word to search for on the stack and returns a value
of false.

>number ( addr count -- n flag | addr count flag ) .forth
Attempt to convert a string to a number in the current base. If successful,
leave the number and a value of true on the stack. If unsuccessful, leaves
the string containing the number on the stack and a return value of false.

] ( -- ) .forth
Turn the compiler mode on. This sets state to -1.

compile ( xt -- ) .forth
Compile a call to the specified xt.
Technical note:
  The call instruction is relative. To obtain the address of a word
  from a compiled definition, try something like:
     : foo words ; ' foo 1+ @ here + 5 +
  This will adjust the first call (1+ skips over the call opcode) to
  point to the valid address for the function used. The "5 +" is the
  adjustment, based on the size of a call. Call opcodes on x86 are
  5 bytes long.

, ( n -- ) .forth
Place a cell (4 byte) value into the heap

1, ( n -- ) .forth
Place a single byte into the heap

2, ( n -- ) .forth
Place a word (2 bytes) into the heap

3, ( n -- ) .forth
Place three bytes into the heap

eval ( addr count -- ) .forth
Evaluate (using the current compiler state) the string. The results
of the evaluation are left on the stack.

parse ( delimiter -- addr count ) .forth
Search ahead in the tib for the delimiter. This skips leading whitespace
and, if not found, will return a string containing the rest of the
input line.

reset ( ... -- ) .forth
Remove all values from the stack

last ( -- addr ) .forth
Get the address of a pointer to the most recent dictionary entry

tib ( -- addr ) .forth
Get the address of the text input buffer

word? ( addr count -- flag ) .forth
Last-chance error handler for syntax errors. Receives a string
pointing to a token that was not located in the dictionary or converted
to a number. The returned flag should tell whether it was handled
successfully or not.

not ( n -- n ) .forth
Perform a bitwise NOT operation on the TOS

@ ( addr -- n ) .forth
Fetch a cell-sized value from the address specified

! ( n addr -- ) .forth
Store a cell-sized value into the address specified

w@ ( addr -- n ) .forth
Fetch a word-sized value from the address specified

w! ( n addr -- ) .forth
Store a word-sized value into the address specified

c@ ( addr -- n ) .forth
Fetch a single byte from the address specified

c! ( n addr -- ) .forth
Store a single byte into the address specified

+ ( x y -- z ) .forth
- ( x y -- z ) .forth
* ( x y -- z ) .forth
/ ( x y -- z ) .forth
mod ( x y -- z ) .forth
These are the basic math words. Insert the operation between the "x" and
"y" in the stack comment to convert to infix notation.

/mod ( x y -- z n ) .forth
Divide, returning the result and the remainder (x/y)

wsparse ( "word" -- addr count ) .forth
Parse ahead in the TIB until either a space or end of line is reached.

lnparse ( "line" -- addr count ) .forth
Parse ahead in the tib until the end of the line is reached

' ( "name" -- xt | addr count ) .forth
Return the address of a function in the dictionary or the name of
the word if it doesn't exist.
   Technical note:
     This word will set which to the dictionary entry of the word
     if found.

x' ( "name" -- xt flag | addr count flag ) .forth
Return the address of a function in the dictionary or the name of
the word if it doesn't exist. Also returns a flag that can be used
with the conditional words.

>> ( x y -- z ) .forth
Perform a bitwise right shift

<< ( x y -- z ) .forth
Perform a bitwise left shift

here ( -- addr ) .forth
Return the value on the top of the stack

:link ( dt -- addr ) .forth
Return the address of the link field in a dictionary entry

:xt ( dt -- addr ) .forth
Return the address of the xt field in a dictionary entry

:class ( dt -- addr ) .forth
Return the address of the class field in a dictionary entry

:doc ( dt -- addr ) .forth
Return the address of the docstring

:name ( dt -- addr ) .forth
Return the address of the name field in a dictionary entry.
Use count to convert this to a string.

reclass ( class -- ) .forth
Change the class of the most recently defined word to the specified
class.

reclass: ( xt "name" -- ) .forth
Change the class of the word specified by name to the specified class

cells ( x -- y ) .forth
Return x multiplied by the size of a cell (4 bytes)

cell+ ( x -- y ) .forth
Add the size of a cell (4 bytes) to x

cell- ( x -- y ) .forth
Subtract the size of a cell (4 bytes) from x

word+ ( x -- y ) .forth
Add the size of a word (2 bytes) to x

word- ( x -- y ) .forth
Subtract the size of a word (2 bytes) from x

rot ( x y z -- y z x ) .forth
Rotate the stack so that the third element is on top.

-rot ( x y z -- z x y ) .forth
Rotate the stack twice. Basically the same as doing rot rot.

over ( x y -- x y x ) .forth
Place a copy of NOS above TOS

tuck ( x y -- y x y ) .forth
Place a copy of TOS under NOS

2dup ( x y -- x y x y ) .forth
Duplicate the top two items on the stack

later ( rs:x rs:y -- rs:y rs:x ) .forth
Defer execution of the rest of the definition until the caller
finishes executing.

0; ( n -- n | ) .forth
Exit a word if the TOS is equal to 0. Drops TOS before exiting.
Leave it alone if non-zero.

execute ( xt -- ) .forth
Call the code that xt points to. This word ignores the class!
  Technical note:
    This treats the passed xt as a .self word.
  General note:
    You can use get-class to obtain the class handler for the
    specified xt.

create: ( "name" -- ) .forth
Create a new word with a name of name and the current class

literal, ( n -- ) .forth
Compile a literal into a definition. This is different from the
macro word literal which is called at compile time. This word
is called at runtime.

literal? ( n -- n | ) .forth
If compiling, compile 'n' as a literal. If interpreting, leave
it on the stack.

create ( "name" -- ) .forth
Create a new word named name using the .data class.

variable ( "name" -- ) .forth
Create a variable named name with an initial value of 0

variable: ( n "name" -- ) .forth
Create a variable named name with an initial value of n.

constant ( n "name" -- ) .forth
Create a constant named name with a value of n.

+! ( n addr -- ) .forth
Add n to the contents of the memory location specified by addr.

-! ( n addr -- ) .forth
Subtract n from the contents of the memory location specified
by addr.

allot ( n -- ) .forth
Allocate n bytes in the heap


alias ( xt "name" -- ) .forth
Create an alternate dictionary entry for xt with a name of name

loc: ( -- ) .forth
Start a lexically scoped section of code. These can be nested up to
four deep.

;loc ( -- ) .forth
End the current lexically scoped section of code.

fill ( addr count value -- ) .forth
Fill the memory location specified by addr with count bytes of the
specified value.

move ( source dest count -- ) .forth
Move count bytes from the memory address specified by source to dest.

copy ( source dest count -- dest count ) .forth
Copy "count" bytes from source to dest, leaving the destination and
count on the stack.

pad ( -- addr ) .forth
Return the address of the pad, a floating buffer above here which is
used for temporary strings and other structures.

" ( "string" -- addr count ) .forth
Parse ahead until a " is encountered. Place this string in the PAD and
return a pointer to it.

z" ( "string" -- addr ) .forth
Like ", but returns a zero-terminated string

$, ( addr count -- ) .forth
Compile a string into the current definition.

hex ( -- ) .forth
Switch to base 16

decimal ( -- ) .forth
Switch to base 10

binary ( -- ) .forth
Switch to base 2

octal ( -- ) .forth
Switch to base 8

>entry ( xt -- dt ) .forth
Given an xt, obtain the corresponding dictionary entry.

unpack ( addr -- addr count ) .forth
Convert a packed string to a counted string (useful with dictionary
entries)

is ( xt "name" -- ) .forth
Change the vectored word name to point to xt. If name does not exist,
create a new vectored word with a default definition of xt.

default: zt "name" -- ) .forth
Change the second vector in a double-vectored word to point to xt.

devector ( "name" -- ) .forth
Remove the vector of name, restoring the original definition.

zt ( addr count -- addr ) .forth
Convert a counted string to a zero-terminated string. Cleanup of
allocated space is done automatically.

get-class ( xt -- xt class ) .forth
Obtain the class for a specified xt; if the xt is not found in the
dictionary, assumes a default class of .forth

=   ( x y -- flag ) .forth
<>  ( x y -- flag ) .forth
<   ( x y -- flag ) .forth
>   ( x y -- flag ) .forth
Compare x any y, return a true/false flag that can be used with
the new conditionals or 'if'.

t   ( flag xt -- ) .forth
Execute xt if the flag is true

f   ( flag xt -- ) .forth
Execute the xt if the flag is false

t/f ( flag xt1 xt2 -- ) .forth
Execute xt1 if the flag is true, execute xt2 if the flag is false

on ( addr -- ) .forth
off ( addr -- ) .forth
'on' sets a variable to 'true', 'off' sets it to false

toggle ( addr -- ) .forth
Toggle a variable between true and false

( ( "comment" -- ) .self
Parse ahead until the ) character or end of line is found. Ignore
everything that was parsed.

: ( "name" -- ) .self
Create a new word with a name of "name" and start the compiler. The
word is created in the current class.

[[ ( -- ) .self
]] ( -- xt ) .self
Start and end an anonymous definition. This can be used inside
other definitions, and nested up to four levels deep.

; ( -- ) .macro
End the current definition
See also: ;;

[ ( -- ) .macro
Switch to interpretation mode. Sets state to 0.

;; ( -- ) .macro
Compile an exit to the current word without ending the definition
   Technical note:
     This word will compile either a "ret" ($c3 opcode) or change
     the last compiled call opcode to a jump opcode. This provides
     inherent tail call elimination and allows for safe recursion.
      As ; uses this, the same applies to it.

literal ( n -- ) .macro
Compile a literal from the stack into the current definition

x: ( "name" -- ) .macro
Compile a call to name ignoring the action defined by the word's class.
This treats the "name" as a .self word.

['] ( "name" -- ) .macro
Compile the xt of name into the current word

c: ( "name" -- ) .macro
Compile the code needed to compile a call to name into the current
definition. Does the same thing as: ['] name compile

as ( "class" -- ) .macro
Change the class of the most recently created word to the specified
class

>r ( n -- rs:n ) .macro
Move TOS onto the return stack.

r> ( rs:n -- n ) .macro
Move TORS to the data stack

r ( -- n ) .macro
Get a copy of TORS on the data stack.
   Historical Note:
      In most Forths this word is called r@. In RetroForth, it has
      traditionally been called r. We chose to continue with the
      RetroForth naming in this case.
   Technical Note:
      This has the same effect as: r> dup >r

rdrop ( rs:n -- ) .macro
Drop the TORS.
   Technical Note:
      Has the same effect as: r> drop

repeat ( -- ) .macro
Start an unconditional loop

again ( -- ) .macro
Close an unconditional loop, branching back to the most recent repeat
   Technical Note:
      Loops constructed using repeat and again are properly
      tail recursive.

for ( n -- rs:counter ) .macro
Start a counted loop. The counter is put on the return stack

next ( rs:counter -- rs:counter | ) .macro
Close a loop that starts with for. Decrements the counter. If 0, exit,
otherwise branch back to for.

(if) ( n -- ) .macro
Factor of most of the forms of if
General Note:
There is seldom any reason to call this word in normal code. Its
sole purpose is to factor out a chunk of code shared between the
other conditionals.

<>if ( x y -- ) .macro
=if ( x y -- ) .macro
if ( x y -- ) .macro
We describe these together since they are very similar in functionality.
To visualize the action, insert the symbol between "x" and "y" in the
stack diagram. <> denotes inequality.
These words start a conditional, ending with then.

if ( flag -- ) .macro
If the flag is true, execute the code between if and then. If not
true, skips ahead to then.

then ( -- ) .macro
Terminate any of the if constructs. This patches the conditional jump
to point to the proper offset in the compiled definition.

;then ( -- ) .macro
Compile an exit to the word and then terminate any if construct.
   Technical Note:
      The same as: ;; then

if; ( flag -- ) .macro
If true, then exit the word. Otherwise continue execution of the word.
   Technical Note:
      The same as: if ;; then

prior ( "word" -- ) .macro
Compile a call to an earlier definition of word.

vector ( -- ) .macro
Make the word a vectored word. This must be the first word in the
definition if you use it.

s" ( "string" -- addr count ) .macro
Compile a string into the definition

{ ( "}" -- ) .macro
Compile a sequence of code to be evaluated at runtime. The compiler
state is not altered by this.

dup ( n -- n n ) .inline
Duplicate the top item on the stack

1+ ( x -- y ) .inline
Increment the TOS

1- ( x -- y ) .inline
Decrement the TOS

swap ( x y -- y x ) .inline
Exchange the location of the top two items on the stack

drop ( x -- ) .inline
Discard the top element of the stack

nip ( x y -- y ) .inline
Discard the second element on the stack

2drop ( x y -- ) .inline
Discard the top two elements on the stack

and ( x y -- z ) .inline
Perform a bitwise AND operation

or ( x y -- z ) .inline
Perform a bitwise OR operation

xor ( x y -- z ) .inline
Perform a bitwise XOR operation

h0 ( -- addr ) .data
Variable pointing to the top of the heap

base ( -- addr ) .data
Variable holding the current base

>in ( -- addr ) .data
Variable that holds the current address in the sequence being
evaluated.

class ( -- addr ) .data
Variable that stores the address of the current class handler for
creating new words.

state ( -- addr ) .data
Variable which contains the state of the compiler. true is compiling,
false is interpreting.

which ( -- addr ) .data
Variable pointing to the dictionary entry of the most recently
found word.

whitespace ( -- addr ) .data
Variable which contains a character used as the whitespace to search
for.

list ( -- addr ) .data
\ nVariable pointing to a data structure used to implement lexical scoping. 
true ( -- addr ) .data
Constant returning the value -1

false ( -- addr ) .data
Constant returning the value 0

Appendix 2: Assembly Listings

Many words in the bootstrap code for Rx are written using raw opcodes for various instructions. This has been a cause of much confusion, so we'd like to take the time now to document what each word actually compiles to. Due to the little-endian nature of the x86, the opcodes in the source are in reverse order. These listings have the normal order.

dup
8946FC mov [esi-4], eax
8D76FC lea esi, [esi-4]

drop
AD lodsd

swap
8706 xchg eax, [esi]

nip
83C604 add esi, 4

1+
40 inc eax

1-
48 dec eax

2drop
AD lodsd
AD lodsd

and
2306 and eax, [esi]
83C604 add esi, 4

or
0B06 or eax, [esi]
83C604 add esi, 4

xor
3306 xor eax, [esi]
83C604 add esi, 4

r>

58 pop eax

>r
50 push eax
AD lodsd

r

58 pop eax
50 push eax

rdrop
5B pop ebx

=if
0F85 jnz

if
0F8D jnl

<>if
0F84 jz

(if)
3B06 cmp eax, [esi]
AD lodsd
AD lodsd
???? 
?????? 

then

$90 nop

vector
$E9 jmp
?????? 

+
0603 add eax, [esi]
83C604 add esi, 4

-
2906 sub [esi], eax
AD lodsd

*
F726 mul dword [esi]
83C604 add esi, 4

/mod
89C3 mov ebx, eax
AD lodsd
99 cdq
F7FB idiv ebx
8946FC mov [esi-4], eax
8D76FC lea esi, [esi-4]
89D0 mov eax, edx
8706 xchg eax, [esi]

@
8B00 mov eax, [eax]

!
89C2 mov edx, eax
AD lodsd
8902 mov [edx], eax
AD drop

c!
89C2 mov edx, eax
AD lodsd
8802 mov [edx], al
AD drop

w!
89C2 mov edx, eax
AD lodsd
668902 mov [edx], ax
AD drop

next

58 pop eax
48 dec eax
0F8F jg near
???? 
AD lodsd

>>
89C1 mov ecx, eax
AD lodsd
D3E8 shr eax, cl

<<
89C1 mov ecx, eax
AD lodsd
D3E0 shl eax, cl