Here is some sample Pentium code for the Iota code samples given in the Iota language definition. These code samples were actually generated from the Iota functions rewritten as C equivalents, with some contrived constructions to illustrate some of the Pentium code. The instructions given assume a Windows operating system and Microsoft development tools.
You will need to set up your command-line environment so that
certain programs (the Microsoft C compiler and linker) are in your
PATH. All of the batch files below are available in the directory \\goose.csuglab.cornell.edu\courses\cs412-sp01\iotalib\
.
MASM is installed on the CSUGLAB machines at:
\\goose.csuglab.cornell.edu\courses\cs412-sp01\masm611You can copy the following text into a script to make it easier to set up your working environment:
@echo off call "c:\program files\microsoft visual studio\vc98\bin\vcvars32" set PATH=\\goose\courses\cs412-sp01\masm611\bin;%PATH%
We've also provided a sample script (env.bat) that you can use as a starting point for your environment.
The next script (c2asm.bat
) allows you to create
your own assembly code from C code. The switches are case-sensitive (in particular,
/FAs
):
@echo off cl /FAs /nologo /c %1 %2 %3 %4 %5 %6 %7 %8 %9
We have provided batch files to assemble your code (asm.bat
)
and link your programs (ilink.bat
).
The C code in this document was written in samples.c
, and the Pentium
assembler code samples.asm
was generated by issuing:
Z:\> c2asm samples.c
The resulting assembler code was assembled to samples.obj
:
Z:\> asm samples.asm
Now link your program:
Z:\> ilink samples.obj
This will produce an executable file of the same name as the first
object file provided as a command-line argument to ILINK (in this
case, samples.exe
)
There are some notes about the assembler code generated by the Microsoft C compiler, and what kind of assembler code is strictly necessary:
DWORD PTR
(32-bit
values, the usual case) or BYTE PTR
(8-bit values, i.e., string
indexing).jl
, jge
, jmp
,
etc.) can be written simply with a
destination label; there is no need for the SHORT
modifier keyword.ret
" rather than "ret
0
". The "0
" does not refer to the return value; the return value
goes in the eax
register.OFFSET <
identifier>
;
specifying OFFSET FLAT:<
identifier>
is unnecessary.EXTRN
and PUBLIC
declarations,
DATA
and TEXT
segments, assembler
aliases) can be placed in any order. The example below shows one large data segment and
one large text segment. However, this could also have been coded as multiple data and text
segments; you can generate your own assembly code to see this for yourself.Now let's start coding!
This is the format you should use in all of your generated assembler code:
; beginning of file TITLE hello.im ; name of original source file .486 ; target processor .model FLAT ; flat memory model, as opposed to old MS-DOS models ; now list all externally-defined function names, they are all EXTRN NEAR ; these include function names that you be calling in the iota.obj file EXTRN _io__print:NEAR EXTRN _iota__newarray:NEAR EXTRN _iota__abort:NEAR ; now list all exported functions available to other modules to be linked in later ; (for example, "static" functions in C would not be listed here since they have only ; file scope). PUBLIC _iota__main ; Data segment: place all statically-allocated data (such as string constants) here. ; These directives are on MASM Programmer's Guide page 86. ; <masm_identifier> BYTE "string" for strings (BYTE arrays) ; <masm_identifier> DWORD <unsigned 32-bit value> ; <masm_identifier> SDWORD <signed 32-bit value> ; Hex digits must be prefixed with a zero and suffixed with an 'H', and should be ; separated from other characters in the string by a comma. Strings should ; be padded out with zeros to a multiple of the word size. Example below _DATA SEGMENT string1Length DWORD 14 string1 BYTE "Hello world!", 0dH, 0aH, 00H, 00H _DATA ENDS ; finally, our _TEXT segment follows ... _TEXT SEGMENT ;assembly code goes here _TEXT ENDS END
fibonacci(x: int): int = ( if (x < 2) (1); else fibonacci(x - 1) + fibonacci(x - 2); )
;The header section TITLE fib.im .486 .model FLAT ;All external functions that might be called EXTRN _iota__strconcat:NEAR EXTRN _iota__strcompare:NEAR EXTRN _iota__newarray:NEAR EXTRN _iota__abort:NEAR;There's no data needed for this program _DATA SEGMENT _DATA ENDS;And finally our code _TEXT SEGMENT _fib__fibonacci PROC NEARpush ebp ;function prologue mov ebp, esp add esp, -12 ;if (x<2) cmp DWORD PTR [ebp + 8], 2 ;check if x<2 jl $IF_TRUE0 $IF_FALSE0:;fibonacci(x-1)+fibonacci(x-2); mov eax, DWORD PTR [ebp + 8] ;calculate x-1 sub eax, 1 push eax ;push it on as an argument call _fib__fibonacci ;make the call to fib(x-1) mov DWORD PTR [ebp-8], eax ;save the returned value in [ebp-8] mov eax, DWORD PTR [ebp + 8] ;calculate x-2 sub eax, 2 push eax ;push it on as an argument call _fib__fibonacci ;make the call to fib(x-2) mov DWORD PTR [ebp-12], eax ;save the returned value in [ebp-12] mov eax, DWORD PTR [ebp-8] add eax, DWORD PTR [ebp-12] ;move fib(x-1)+fib(x-2) into eax mov DWORD PTR [ebp-4], eax ;move the value into a temporary for returning $IF_END0: mov eax, DWORD PTR [ebp-4] ;move the temporary with the return ;value into the return register $fibonacci_epi: mov esp, ebp ;function epilogue pop ebp ret $IF_TRUE0:;(1); mov DWORD PTR [ebp-4], 1 ;move 1 into a temporary to be returned jmp $IF_END0 _fib__fibonacci ENDP _TEXT ENDS END
square(x:int):int=(x*x;)
;The header section TITLE square.im .486 .model FLAT ;All external functions that might be called EXTRN _iota__strconcat:NEAR EXTRN _iota__strcompare:NEAR EXTRN _iota__newarray:NEAR EXTRN _iota__abort:NEAR;There's no data needed for this program _DATA SEGMENT _DATA ENDS;And finally our code _TEXT SEGMENT _square__square PROC NEAR push ebp ;function prologue mov ebp, esp add esp, -4;x*x mov eax, DWORD PTR [ebp + 8];put x into eax for multiplication imul DWORD PTR [ebp + 8] ;multiply it by x mov DWORD PTR [ebp-4], eax ;save the result in a temporary mov eax, DWORD PTR [ebp-4] ;put it into eax for returning $square_epi: mov esp, ebp ;function epilogue pop ebp ret_square__square ENDP _TEXT ENDS END
gcd(x: int, y: int): int = ( while (!(x == 0)) ( if (x < y) ( temp:int = x; x = y; y = temp; ) x = x % y; ) y; )
TITLE gcd2.im ;The header info .486 .model FLAT EXTRN _iota__strconcat:NEAR EXTRN _iota__strcompare:NEAR EXTRN _iota__newarray:NEAR EXTRN _iota__abort:NEAR _DATA SEGMENT _DATA ENDS _TEXT SEGMENT _gcd2__gcd PROC NEAR push ebp ;function prologue mov ebp, esp add esp, -8 $beginWhile0: cmp DWORD PTR [ebp + 8], 0 ;while(!(x==0)) jne $bodyWhile0 $endWhile0: mov eax, DWORD PTR [ebp + 12] ;y; $gcd_epi: mov esp, ebp ;function epilogue pop ebp ret $bodyWhile0:;if (x<y) mov eax, DWORD PTR [ebp + 12] ;move y into eax for comparison cmp DWORD PTR [ebp + 8], eax ;check x<y jl $IF_TRUE0 $IF_FALSE0: ;no else, so just fall through $IF_END0:;x=x%y; mov eax, DWORD PTR [ebp + 8] ;put x into eax for divison cdq ;Pentium funkiness with division idiv DWORD PTR [ebp + 12] ;do x%y using an idiv mov DWORD PTR [ebp-8], edx ;the remainder will be in edx ;which we save in a temporary mov eax, DWORD PTR [ebp-8] ;Then we do the assignment mov DWORD PTR [ebp + 8], eax ;by moving the result to x jmp $beginWhile0 $IF_TRUE0: mov eax, DWORD PTR [ebp + 8] ;temp:int=x mov DWORD PTR [ebp - 4], eax mov eax, DWORD PTR [ebp + 12] ;x=y mov DWORD PTR [ebp + 8], eax mov eax, DWORD PTR [ebp - 4] ;y=temp; mov DWORD PTR [ebp + 12], eax jmp $IF_END0 _gcd2__gcd ENDP _TEXT ENDS END
quicksort(a: array[int], low: int, high: int) = ( // A good sorting routine for large arrays. Not a stable sort. if (!(low < high)) return; mid: int = partition(a, low, high); quicksort(a, low, mid); quicksort(a, mid + 1, high) ) partition(a: array[int], low: int, high: int): int = ( /* Reorder the elements in a into two contiguous groups. If ret is the return value, then the first group is the elements in low..ret, and the second group is the elements in ret+1..high. Each element in the second group will be at least as large as every element in the first group. */ x: int = a[low]; i: int = low - 1; j: int = high + 1; while (true) ( while (a[(j = j - 1)] > x) (); while (a[(i = i + 1)] < x) (); if (i < j) ( temp: int = a[i]; a[i] = a[j]; a[j] = temp; ) else return j ) )
TITLE quicksort2.im ;The header stuff .486 .model FLAT EXTRN _iota__strconcat:NEAR EXTRN _iota__strcompare:NEAR EXTRN _iota__newarray:NEAR EXTRN _iota__abort:NEAR _DATA SEGMENT _DATA ENDS _TEXT SEGMENT _quicksort2__quicksort PROC NEAR push ebp ;function prologue mov ebp, esp add esp, -72;if (!(low<high)) return; mov eax, DWORD PTR [ebp + 16] ;low<high cmp DWORD PTR [ebp + 12], eax jge $IF_TRUE0 $IF_FALSE0: $IF_END0:;mid:int=partition(a,low,high); push DWORD PTR [ebp + 16] ;push high push DWORD PTR [ebp + 12] ;push low push DWORD PTR [ebp + 8] ;push a call _quicksort2__partition ;partition(a,low,high) mov DWORD PTR [ebp - 4], eax ;mid=partition(a,low,high) ;quicksort(a,low,mid); push DWORD PTR [ebp - 4] ;push mid push DWORD PTR [ebp + 12] ;push low push DWORD PTR [ebp + 8] ;push a call _quicksort2__quicksort ;quicksort(a,low,mid) ;quicksort(a,mid+1,high); push DWORD PTR [ebp + 16] ;push high mov eax, DWORD PTR [ebp - 4] ;move mid into eax f add eax, 1 push eax ;push mid+1 push DWORD PTR [ebp + 8] ;push a call _quicksort2__quicksort ;quicksort(a,mid+1,high) $quicksort_epi: mov esp, ebp ;function epilogue pop ebp ret $IF_TRUE0: jmp $quicksort_epi ;return _quicksort2__quicksort ENDP _quicksort2__partition PROC NEAR push ebp ;function proglogue mov ebp, esp add esp, -256;x:int=a[low] push DWORD PTR [ebp + 12] ;push low push DWORD PTR [ebp + 8] ;push a ;the array bounds ;which returns the array pointer mov ebx, eax ;load the array pointer into ebx mov ecx, DWORD PTR [ebp+12] ;load the index in ecx mov eax, DWORD PTR [ebx + ecx*4] ;move the array element into eax mov DWORD PTR [ebp - 4], eax ;put the value in x;i:int=low-1 mov eax, DWORD PTR [ebp + 12] ;move low into eax for subtraction sub eax, 1 ;subtract 1 mov DWORD PTR [ebp - 8], eax ;put the value into i;j:int=high+1 mov eax, DWORD PTR [ebp + 16] ;move high into eax for addition add eax, 1 ;add 1 mov DWORD PTR [ebp - 12], eax ;put the value into j $beginWhile0: mov eax, 1 ;while (true) cmp eax, 1 je $bodyWhile0 $endWhile0:;0; mov eax, 0 ;return 0 $partition_epi: mov esp, ebp pop ebp ret $bodyWhile0: $beginWhile2:;while (a[(j=j-1;)]>x); mov eax, DWORD PTR [ebp - 12] ;move j into eax for subtraction sub eax, 1 ;subtract 1 mov DWORD PTR [ebp - 12], eax ;put the value into j push DWORD PTR [ebp - 12] ;push j push DWORD PTR [ebp + 8] ;push a ;the array bounds ;which returns the array pointer mov ebx, eax ;load the array pointer into ebx mov ecx, DWORD PTR [ebp-12] ;load the index in ecx mov eax, DWORD PTR [ebp-4] ;move x into eax for comparison cmp DWORD PTR [ebx + ecx*4], eax ;do the compare jg $bodyWhile2 $endWhile2: ;fall through to the next while $beginWhile1:;while(a[(i=i+1;)]<x); mov eax, DWORD PTR [ebp - 8] ;move i into eax for adding add eax, 1 ;add 1 mov DWORD PTR [ebp - 8], eax ;put the value into x push DWORD PTR [ebp - 8] ;push i push DWORD PTR [ebp + 8] ;push a ;the array bounds ;which returns the array pointer mov ebx, eax ;load the array pointer into ebx mov ecx, DWORD PTR [ebp-12] ;load the index in ecx mov eax, DWORD PTR [ebp-4] ;move x into eax for comparison cmp DWORD PTR [ebx + ecx*4], eax ;do the compare jl $bodyWhile1 $endWhile1: ;fall through to the rest of the function;if (i<j) mov eax, DWORD PTR [ebp - 12] ;move j into eax for comparison cmp DWORD PTR [ebp - 8], eax ;i<j jl $IF_TRUE1 $IF_FALSE1:;return j mov eax, DWORD PTR [ebp - 12] ;move j into eax to be returned jmp $partition_epi $bodyWhile2: jmp $beginWhile2 $bodyWhile1: jmp $beginWhile1 $IF_TRUE1:;temp:int=a[i]; push DWORD PTR [ebp - 8] ;push i push DWORD PTR [ebp + 8] ;push a ;the array bounds ;which returns the array pointer mov ebx, eax ;load the array pointer into ebx mov ecx, DWORD PTR [ebp-8] ;load the index in ecx mov eax, DWORD PTR [ebx + ecx*4] ;move the value into eax mov DWORD PTR [ebp - 16], eax ;put the value into temp;a[i]=a[j]; push DWORD PTR [ebp - 12] ;push j push DWORD PTR [ebp + 8] ;push a ;the array bounds ;which returns the array pointer mov ebx, eax mov ecx, DWORD PTR [ebp-12] mov eax, DWORD PTR [ebx + ecx*4] ;put a[j] into eax mov DWORD PTR [ebp-20], eax ;then into a temp push DWORD PTR [ebp-8] ;push i push DWORD PTR [ebp + 8] ;push a ;the array bounds ;which returns the array pointer mov ebx, eax ;load ebx with the array pointer mov ecx, DWORD PTR [ebp-48] ;load the index into ecx mov eax, DWORD PTR [ebp-20] ;put a[j] into eax mov DWORD PTR [ebx + ecx*4], eax ;move eax into a[i];a[j]=temp; push DWORD PTR [ebp-12] ;push j push DWORD PTR [ebp + 8] ;push a ;the array bounds ;which returns the array pointer mov ebx, eax ;load ebx with the array pointer mov ecx, DWORD PTR [ebp-12] ;load the index into ecx mov eax, DWORD PTR [ebp-16] ;put temp into eax for moving mov DWORD PTR [ebx + ecx*4], eax ;move temp into a[j] $IF_END1: jmp $beginWhile0 _quicksort2__partition ENDP _TEXT ENDS END
uses io.print main(args: array[string]):int = ( print("Hello world!\N"); 0 )
TITLE hello2.im .486 .model FLAT EXTRN _io__print:NEAR EXTRN _iota__strconcat:NEAR EXTRN _iota__strcompare:NEAR EXTRN _iota__newarray:NEAR EXTRN _iota__abort:NEAR _DATA SEGMENT $STRING0Length DWORD 14 ;We use the data segment $STRING0 BYTE "Hello world!", 0dH, 0aH, 00H, 00H ;to store strings _DATA ENDS _TEXT SEGMENT _iota__main PROC NEAR ;note the special case of naming ;with the main function push ebp ;function prologue mov ebp, esp add esp, -4;print("Hello world!\N"); mov DWORD PTR [ebp-4], OFFSET FLAT:$STRING0 ;move our string constant push DWORD PTR [ebp-4] ;into a temp and push it ;as an argument call _io__print ;call print mov eax, 0 $main_epi: mov esp, ebp ;function epilogue pop ebp ret _iota__main ENDP _TEXT ENDS END
Here's how you can use the _iota_strcat
function to create new strings.
concat(x:string,y:string):string = { x+y; }
TITLE stringcat.im .486 .model FLAT EXTRN _iota__strconcat:NEAR EXTRN _iota__strcompare:NEAR EXTRN _iota__newarray:NEAR EXTRN _iota__abort:NEAR _DATA SEGMENT _DATA ENDS _TEXT SEGMENT _stringcat__concat PROC NEAR push ebp ;function prologue mov ebp, esp;x+y; push DWORD PTR [ebp + 12] ;push x push DWORD PTR [ebp + 8] ;push y call _iota__strconcat ;call the iota lib $concat_epi: mov esp, ebp ;function epilog pop ebp ret _stringcat__concat ENDP _TEXT ENDS END
Here's how you use the _iota__newarray
function to create new arrays.
main(args:array[string]):int = { a:array[int]=new int[10](1); 0 }
TITLE new.im .486 .model FLAT EXTRN _iota__strconcat:NEAR EXTRN _iota__strcompare:NEAR EXTRN _iota__newarray:NEAR EXTRN _iota__abort:NEAR _DATA SEGMENT _DATA ENDS _TEXT SEGMENT _iota__main PROC NEAR push ebp ;function prologue mov ebp, esp add esp, -16;a:array[int]=new int[10]=1; push 1 ;push the initial value push 10 ;push the size of the array call _iota__newarray ;call the iota lib ;which returns a pointer to the array mov DWORD PTR [ebp - 4], eax;put the array pointer into a mov eax, 0 $main_epi: mov esp, ebp ;function epilogue pop ebp ret _iota__main ENDP _TEXT ENDS END
There are a few calls available to assembler code that are not exposed to the Iota programmer:
_iota__newstring
. Takes one argument, the number of non-NULL
characters to allocate for the new string. The return value is a pointer in memory to the
allocated storage. _iota__newarray
. Takes two arguments, the first argument is the
desired number of elements in the array, the second is a value to which all elements will
be initialized. _iota__strconcat
. Takes two arguments, the first argument is the
first string, the second is the second string and returns a pointer to the
new string. _iota__strcompare
. Takes two arguments, the first argument is the
first string, the second is the second string and returns a number less than
0 if the first string is lexographically before the second, a 0 if they are
equal, and a number greater than 0 if the first string is lexographically
after the second. _iota__debug
. Takes one argument, the number 1 or 0. This sets an
internal Iota debug flag for the io
and conv
modules to display
what they are doing, to aid you in determining whether or not you are passing the right
arguments to the Iota libraries. _iota__abort
. No arguments. Causes your program to crash. Visual C++
will start another instance of itself at the location of the crash. The top few layers
will be NT and Iota library code. A few layers down from the top will be the actual Iota
assembler code that called _iota__abort
. Compiler-writer routines available to assembler code:
char * iota__newstring (int numchars); // number of non-NULL characters int * iota__newarray (int elements, int val); // number of elements, and // their initial value void iota__debug (int flag); void iota__abort (void);
The routines available to both the compiler-writer and the Iota programmer, from the io
and conv
modules:
void io__print (char *s); void io__printi (int i); void io__putc (int i); char * io__readln (void); int io__getc (void); int io__eof (void); char * conv__itos (int i); int conv__stoi (char *string, int error); char * conv__itoc (int i); char * conv__atos (int *a); int * conv__stoa (char *s);