CS 412/413
Introduction to Compilers
Cornell University Computer Science Department, Spring 2001

Iota Pentium Code Samples


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.


Work Environment

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\masm611  
You 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)


Notes about Assembler code

There are some notes about the assembler code generated by the Microsoft C compiler, and what kind of assembler code is strictly necessary:

Now let's start coding!

 


Assembly Code File Format

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

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 NEAR
			push 	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

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

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

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

Hello, world!

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

Concat

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


New array

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

Iota Implementation

There are a few calls available to assembler code that are not exposed to the Iota programmer:


Reference

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