; EX12_1.asm
;
; Program to support the laboratory exercise in Chapter 12.
;
; This program combines iterators, passing parameters as parameters,
; and procedural parameters all into the same program.
;
;
; This program implements the following iterators (examples written in panacea):
;
; program EX12_1;
;
; fib:iterator(n:integer):integer;
; var
;	CurIndex:integer;
;	Fn1:	 integer;
;	Fn2:	 integer;
; endvar;
; begin fib;
;
;	yield 1;	(* Always have at least n=0 *)
;	if (n <> 0) then
;
;		yield 1;	(* Have at least n=1 at this point *)
;
;		Fn1 := 1;
;		Fn2 := 1;
;		foreach CurIndex in 2..n do
;
;			yield Fn1+Fn2;
;			Fn2 = Fn1;
;			Fn1 = CurIndex;
;
;		endfor;
;	endif;
;
; end fib;
;
;
;
; UpDown:iterator(n:integer):integer;
; var
;	CurIndex:integer;
; endvar;
; begin UpDown;
;
;	foreach CurIndex in 0..n do
;
;		yield CurIndex;
;
;	endfor;
;	foreach CurIndex in n-1..0 do
;
;		yield CurIndex;
;
;	endfor;
; 
; end UpDown;
;
;
;
; SumToN:iterator(n:integer):integer;
; var
;	CurIndex:integer;
;	Sum:	 integer;
; endvar;
; begin SumToN;
;
;	Sum := 0;
;	foreach CurIndex in 0..n do
;
;		Sum := Sum + CurIndex;
;		yield Sum;
;
;	endfor;
;
; end SumToN;
;
;
; MultiIter returns a pointer to an iterator that accepts a single integer parameter.
;
; MultiIter: iterator: [iterator(n:integer)];
; begin MultiIter;
;
;	yield @Fib;	(* Return pointers to the three iterators above *)
;	yield @UpDown;	(* as the result of this iterator.		*)
;	yield @SumToN;
;
; end MultiIter;
;
;
; var
;	i:integer;
;	n:integer;
;	iter:[iterator(n:integer)];
; endvar;
;
; begin EX12_1;
;
;	(* The following for loop repeats six times, passing its loop index as	*)
;	(* the parameter to the Fib, UpDown, and SumToN parameters.		*)
;
;	foreach n in 0..5 do
;
;
;		(* The following (funny looking) iterator sequences through	*)
;		(* each of the three iterators: Fib, UpDown, and SumToN.  It	*)
;		(* returns a pointer as the iterator value.  The innermost	*)
;		(* foreach loop uses this pointer to call the appropriate	*)
;		(* iterator.							*)
;
;		foreach iter in MultiIter do
;
;			(* Okay, this for loop invokes whatever iterator was	*)
;			(* return by the MultiIter iterator above.		*)
;
;			foreach i in [MultiIter](n) do
;
;				write(i:3);
;
;			endfor;
;			writeln;
;
;		endfor;
;		writeln;
;
;	endfor;
;
; end EX12_1;

		.xlist
		include 	stdlib.a
		includelib	stdlib.lib
		.list

		.286				;Allow extra adrs modes.


wp		textequ	<word ptr>


dseg		segment	para public 'code'
dseg		ends


cseg		segment	para public 'code'
		assume	cs:cseg, ss:sseg




; The following macro builds a resume frame and the returns to the caller
; of an iterator.  It assumes that the iterator and whoever called the
; iterator have the standard activation record defined above and that we
; are building the standard resume frame described above.
;
; This code wipes out the DX register.  Whoever calls the iterator cannot
; count on DX being preserved, likewise, the iterator cannot count on DX
; being preserved across a yield.  Presumably, the iterator returns its
; value in AX.


Yield		macro
                mov     dx, [BP+2]              ;Place to yield back to.
		push	bp			;Save Iterator link
                mov     bp, [bp]                ;Get ptr to caller's A.R.
		call	dx			;Push resume address and rtn.
		pop	bp			;Restore ptr to our A. R.
		endm






; Fib(n) - Yields the sequence of fibonacci numbers from F(0)..F(n).
;	   The fibonacci sequence is defined as:
;
;		F(0) and F(1) = 1.
;		F(n) = F(n-1) + F(n-2) for n > 1.


; The following structure defines the activation record for Fib

CurIndex	textequ	<[bp-6]>	;Current sequence value.
Fn1		textequ	<[bp-4]>	;F(n-1) value.
Fn2		textequ	<[bp-2]>	;F(n-2) value.
DynamicLink	textequ	<[bp]>		;Saved BP value.
YieldAdrs	textequ	<[bp+2]>	;Return Adrs for proc.
FailAdrs        textequ <[bp+4]>        ;Go here when we fail
n               textequ <[bp+6]>        ;The initial parameter


Fib		proc
		push	bp
		mov	bp, sp
		sub	sp, 6		;Make room for local variables.

; We will also begin yielding values starting at F(0).
; Since F(0) and F(1) are special cases, yield their values here.

		mov	ax, 1		;Yield F(0) (we always return at least
		yield			; F(0)).

		cmp	wp n, 1		;See if user called this with n=0.
		jb	FailFib
                mov     ax, 1
                yield

; Okay, n >=1 so we need to go into a loop to handle the remaining values.
; First, begin by initializing Fn1 and Fn2 as appropriate.

                mov     wp Fn1, 1
                mov     wp Fn2, 1
                mov     wp CurIndex, 2

WhlLp:		mov     ax, CurIndex		;See if CurIndex > n.
                cmp     ax, n
                ja      FailFib

                push    Fn1
                mov     ax, Fn1
                add     ax, Fn2
                pop     Fn2			;Fn1 becomes the new Fn2 value.
                mov     Fn1, ax			;Current value becomes new Fn1 value.
                yield				;Yield the current value.

                inc     wp CurIndex
                jmp     WhlLp



FailFib:	mov     sp, bp			;Deallocate local vars.
                pop     bp			;Restore Dynamic Link.
                add     sp, 2			;Skip ret adrs.
                ret     2			;Return through fail address.
Fib		endp





; UpDown-       This function yields the sequence 0, 1, 2, ..., n, n-1,
;               n-2, ..., 1, 0.


i               textequ <[bp-2]>        ;F(n-2) value.

UpDown		proc
		push    bp
		mov	bp, sp
		sub	sp, 2		;Make room for i.

		mov	wp i, 0		;Initialize our index variable (i).
UptoN:		mov	ax, i
		cmp	ax, n
		jae	GoDown

		yield

		inc	wp i
		jmp	UpToN


GoDown:		mov	ax, i
		yield
		mov	ax, i
		cmp	ax, 0
		je	UpDownDone
		dec	wp i
		jmp	GoDown

UpDownDone:     mov     sp, bp                  ;Deallocate local vars.
                pop     bp                      ;Restore Dynamic Link.
                add     sp, 2                   ;Skip ret adrs.
                ret     2                       ;Return through fail address.
UpDown		endp





; SumToN(n)-	This iterator returns 1, 2, 3, 6, 10, ... sum(n) where
;		sum(n) = 1+2+3+4+...+n  (e.g., n(n+1)/2);

j               textequ <[bp-2]>        
k		textequ	<[bp-4]>

SumToN		proc
		push    bp
		mov	bp, sp
		sub	sp, 4		;Make room for j and k.

		mov	wp j, 0		;Initialize our index variable (j).
		mov	wp k, 0		;Initialize our sum (k).
SumLp:		mov	ax, j
		cmp	ax, n
		ja	SumDone

		add	ax, k
		mov	k, ax

		yield

		inc	wp j
		jmp	SumLp

SumDone:	mov     sp, bp                  ;Deallocate local vars.
                pop     bp                      ;Restore Dynamic Link.
                add     sp, 2                   ;Skip ret adrs.
                ret     2                       ;Return through fail address.
SumToN		endp





; MultiIter-	This iterator returns a pointer to each of the above iterators.

MultiIter	proc
		push	bp
		mov	bp, sp

		mov	ax, offset Fib
		yield
		mov	ax, offset UpDown
		yield
		mov	ax, offset SumToN
		yield

		pop	bp
		add	sp, 2
		ret
MultiIter	endp




Main		proc
		mov	ax, dseg
		mov	ds, ax
		mov	es, ax


		meminit

; foreach bx in 0..5 do

		mov	bx, 0		 ;Loop control variable for outer loop.
WhlBXle5:	

; foreach ax in MultiIter do

		push	offset MultiDone ;Failure address.
		call	MultiIter	 ;Get iterator to call.


; foreach i in [ax](bx) do

		push	bx		;Push "n" (bx) onto the stack.
		push	offset IterDone	;Failure Address
		call	ax		;Call the iterator pointed at by the
;					; return value from MultiIter.
;
; write(ax:3);

		mov	cx, 3
		putisize	
		ret

; endfor, writeln;

IterDone:	putcr			;Writeln;
		ret

; endfor, writeln;

MultiDone:	putcr
		inc	bx
		cmp	bx, 5
		jbe	WhlBXle5

; endfor

Quit:		ExitPgm			;DOS macro to quit program.
Main		endp

cseg            ends


sseg		segment	para stack 'stack'
stk		word	1024 dup (0)
sseg		ends

zzzzzzseg	segment	para public 'zzzzzz'
LastBytes	db	16 dup (?)
zzzzzzseg	ends
		end	Main
