// This tutorial is part of a series that explains the basic concepts of the APRON simulation system.
// Please ensure you have read and understood any previous tutorial examples.
//
// © David R W Barr 2009
//
// Name: 		PROGRAM FLOW CONTROL
// Description:	This tutorial will introduce techniques that illustrate the are
//				of program flow, i.e loops, labels, subroutines and conditions.
// Usage:		Please STEP-THROUGH this algorithm in the simulator.


// Always include this line for an APRON simulation. This file includes many pre-defined
// types and functions that are used in a standard APRON simulation.
!include('apron.aps')

// Let's get started by defining some registers
!define(RW, 8)
!define(RH, 8)

!create_reg('a', RW, RH) 
!create_reg('b', RW, RH) 
!create_reg('c', RW, RH)

// Program flow is important because it allows algorithms to make decisions, supports
// code re-use and generally makes life for the programmer much better!

// The simplest method of program flow control is jumping to labels. Labels are defined
// by a preceding '#' character. Here we jump to the #start label, which is further down in the
// program. The label itself is not executed and the simulation will actually jump to the
// line immediately after the label
jump(#start)



// ...... Void area!!!



// We will eventually jump here, just to end the simulation
#finish
// This is the end of the tutorial. We can stop the simulation by...
end



// Here we define the #start label. Note only one label in the entire program can be called
// #start. This is true of all labels. They must each be unique. Note that the only way to reach
// this location was via the 'jump(#start)'. If the 'jump' function was not there, the simulation would have
// run to the 'end' function.
#start
	// It is conventional to indent code after a label has been defined, although this is not enforced.
	
	// Jumps can be nested as many times as you like. There is no stack implementation behind them
	// they simply set the program counter to a desired location.
	
	// Unlike jumps, there are also 'calls'. These do have a stack implementation, but can also be nested.
	// calls work in conjunction with 'return' statements. When a call function is encountered, it's address 
	// is placed on the stack. Subsequently, when a return function is encountered, the address is popped off 
	// the stack and is assigned to the program counter. This gives the possibility of sub-routines.
	
	// This tutorial will implement a register gradient-fill function. 
	
	// All resources and variables in APRON are considered global. That means there is no point of passing
	// arguments to a sub-routine. Instead, it is better practice to specify control flags using variables
	// Here we create the variable 'fill_direction' which will act as a flag as to whether a horizontal or
	// vertical fill is performed.
	
	// The flag values would be ideal candidates for defines. e.g. !define(GRADFILL_H, 1) and !define(GRADFILL_V, 0)
	// but we'll save that for another day...
	fill_direction = 1 // 1 = horizontal fill
	call(#sub_grad_fill)
	
	fill_direction = 0 // 0 = vertical fill
	call(#sub_grad_fill)
	
	
	// FINAL WORD!
	// ~~~~~~~~~~~
	//
	// 'if', 'jumpif' and 'callif' all require conditional arguments. These are always of the form
	// VARIABLE, EQUALITY, VARIABLE    or    VARIABLE, EQUALITY, NUMBER
	//
	// The six standard equalities are desccribed as:
	// == equal to
	// != not equal to
	// >  greater than
	// <  less than
	// >= greater than or equal to
	// <= less than or equal to
	//
	// Note that 'jumpif' and 'callif' only perform if the condition is true.
	// The syntax for 'callif' is exactly the same as 'jumpif', although it 
	// is not used in this tutorial.
	
	// Let's go to the end of the algorithm
	jump(#finish)
	

	
// This is the start of a sub-routine called #sub_grad_fill. It will fill register r[a] with a gradient
// in steps of 10 per row or column, depending upon the value of fill_direction. As in Tutorial 1, we will
// shift in new values at the edge of the register, and shift across values already in the register. We want to 
// do this the same number of times as there are rows/columns in the register
#sub_grad_fill

	// Initialise the loop counter and target register to 0
	counter = 0
	r[a] = 0
	
	// This label is for the loop
	#loop
		// Generate the value to insert at the edge of the array, based on the counter
		value = counter * 10
		
		// The if command tests a condition, in this case if 'fill_direction == 0'. If it is true then the code
		// immediately after the 'if' is executed. If it is false, then the code after the 'else' is executed.
		
		if(fill_direction, ==, 0)
			r[a] = shift_north(r[a], value)
		else
			r[a] = shift_west(r[a], value)
		endif
		
		// Note on IF
		// ~~~~~~~~~~
		// If is a powerful command, although it may at first exhibit strange behaviour in the simulator. Notice
		// that whatever the result of the if condition is, that all the code in the if...endif block is executed.
		// APRON recognises that IF is actually not a trivial mechanism to implement in hardware devices, and adopts
		// an alternative way, with exactly the same behaviour. APRON uses IF to set write permissions. If the write
		// permissions are false, then variables and registers cannot be updated. 'else' inverts the current write
		// permissions. 'endif' sets them to how they where prior to the 'if' function being called. Therefore
		// it is possible to have nested if blocks. APRON supports up to 32 levels of nested if...endif blocks
		//
		// e.g.
		//
		// if(a, ==, 5)
		//     	...
		// 		if(b, <, 8)
		//			...
		//		endif
		//		...
		// else
		// 		...
		//		if(b, >, 8)
		//			if(c, !=, 0)
		//				...
		//			else
		//				...
		//			endif
		//		else
		//			...
		//		endif
		//		...
		// endif
		
		
		// Back to the gradient fill!
		// We must implement the counter each iteration. This creates the next value, but also 
		// makes sure we don't exceed the size of the register...
		counter = counter + 1
		
		// ... because we can do conditional jumping. Here we test the value of counter is less than
		// the size of the register and only jump to the labelled location if the condition is true.
		jumpif(counter, <, RW, #loop)
		
		// We get here only if the above condition was false. Here we call return which causes the
		// simulation to jump back to the location this sub-routine was called.
	return
		