Browse Source

Moved ARM backend and Minos Object File from OC repository to A2 trunk

git-svn-id: https://svn.inf.ethz.ch/svn/lecturers/a2/trunk@6327 8c9fc860-2736-0410-a75d-ab315db34111
felixf 9 years ago
parent
commit
8299ab8c4b

BIN
source/Fox.Tool


+ 400 - 0
source/FoxARMAssembler.Mod

@@ -0,0 +1,400 @@
+MODULE FoxARMAssembler; (** AUTHOR ""; PURPOSE ""; *)
+
+IMPORT InstructionSet := FoxARMInstructionSet, FoxAssembler,  (*D := Debugging,*) Scanner := FoxScanner, Diagnostics, Strings;
+
+CONST Trace = FoxAssembler.Trace;
+
+TYPE
+	Assembler* = OBJECT(FoxAssembler.Assembler)
+	VAR
+		PROCEDURE & Init2*(diagnostics: Diagnostics.Diagnostics);
+		BEGIN Init(diagnostics)
+		END Init2;
+
+		(** parse a register name **)
+		PROCEDURE GetRegister*(VAR registerNumber: LONGINT): BOOLEAN;
+		VAR
+			result: BOOLEAN;
+		BEGIN
+			registerNumber := InstructionSet.None;
+			result := FALSE;
+			IF symbol.token = Scanner.Identifier THEN
+				registerNumber := InstructionSet.RegisterNumberFromName(symbol.identifierString);
+				IF registerNumber # InstructionSet.None THEN
+					result := TRUE;
+					NextSymbol
+				END
+			END;
+			RETURN result
+		END GetRegister;
+		
+		PROCEDURE GetRegisterList(VAR registerList: SET): BOOLEAN;
+		VAR num: LONGINT;
+		BEGIN
+			registerList := {};
+			IF symbol.token = Scanner.LeftBrace THEN
+				REPEAT
+					NextSymbol;
+					IF GetRegister(num) THEN
+						IF (num > 16) THEN
+							Error(errorPosition, "invalid register in list (not yet implemented)")
+						END;
+						INCL(registerList, num);
+					END;
+				UNTIL symbol.token # Scanner.Comma;
+				IF symbol.token # Scanner.RightBrace THEN 
+					Error(errorPosition, "'}' expected.")
+				ELSE
+					NextSymbol;
+					RETURN TRUE
+				END;
+			END;
+			RETURN FALSE;
+		END GetRegisterList;
+		
+
+		(** parse a special register name, along with fields **)
+		PROCEDURE GetSpecialRegisterWithFields(VAR registerNumber: LONGINT; VAR fields: SET): BOOLEAN;
+		VAR
+			result: BOOLEAN;
+			i: LONGINT;
+			strings: Strings.StringArray;
+		BEGIN
+			result := FALSE;
+			registerNumber := InstructionSet.None;
+			fields := {};
+
+			IF symbol.token = Scanner.Identifier THEN
+				strings := Strings.Split(symbol.identifierString, '_'); (* split the identifier at the underscore symbol *)
+				IF LEN(strings) = 2 THEN
+					IF (strings[0]^ = "CPSR") OR (strings[0]^ = "SPSR") THEN
+						IF strings[0]^ = "CPSR" THEN registerNumber := InstructionSet.CPSR
+						ELSE registerNumber := InstructionSet.SPSR
+						END;
+						IF strings[1]^ # "" THEN
+							FOR i := 0 TO LEN(strings[1]) - 1 DO
+								CASE strings[1][i] OF
+								| 'f': INCL(fields, InstructionSet.fieldF)
+								| 's': INCL(fields, InstructionSet.fieldS)
+								| 'x': INCL(fields, InstructionSet.fieldX)
+								| 'c': INCL(fields, InstructionSet.fieldC)
+								ELSE
+								END
+							END;
+							result := TRUE;
+							NextSymbol
+						END
+					END
+				END
+			END;
+			RETURN result
+		END GetSpecialRegisterWithFields;
+
+		(** parse a shift mode name **)
+		PROCEDURE GetShiftMode*(VAR shiftModeNumber: LONGINT): BOOLEAN;
+		VAR
+			result: BOOLEAN;
+		BEGIN
+			shiftModeNumber := InstructionSet.None;
+			result := FALSE;
+			IF symbol.token = Scanner.Identifier THEN
+				shiftModeNumber := InstructionSet.ShiftModeNumberFromName(symbol.identifierString);
+				IF shiftModeNumber # InstructionSet.None THEN
+					result := TRUE;
+					NextSymbol
+				END
+			END;
+			RETURN result
+		END GetShiftMode;
+
+		(** parse a coprocessor name **)
+		PROCEDURE GetCoprocessor*(VAR coprocessorNumber: LONGINT): BOOLEAN;
+		VAR
+			result: BOOLEAN;
+		BEGIN
+			coprocessorNumber := InstructionSet.None;
+			result := FALSE;
+
+			IF symbol.token = Scanner.Identifier THEN
+				coprocessorNumber := InstructionSet.CoprocessorNumberFromName(symbol.identifierString);
+				IF coprocessorNumber # InstructionSet.None THEN
+					result := TRUE;
+					NextSymbol
+				END
+			END;
+
+			RETURN result
+		END GetCoprocessor;
+
+		(* parse coprocessor opcode *)
+		PROCEDURE GetCoprocessorOpcode*(VAR coprocessorOpcode: LONGINT): BOOLEAN;
+		VAR
+			result: BOOLEAN;
+		BEGIN
+			IF (symbol.token = Scanner.Number) & (symbol.numberType = Scanner.Integer) & (symbol.integer >= 0) & (symbol.integer <= 7) THEN
+				coprocessorOpcode := symbol.integer;
+				result := TRUE;
+				NextSymbol
+			ELSE
+				coprocessorOpcode := InstructionSet.None;
+				result := FALSE
+			END;
+			RETURN result
+		END GetCoprocessorOpcode;
+
+		(** parse any expression that evaluates to a constant value **)
+		PROCEDURE GetPlainValue*(VAR value: LONGINT): BOOLEAN;
+		VAR
+			assemblerResult: FoxAssembler.Result;
+			result: BOOLEAN;
+		BEGIN
+			IF Expression(assemblerResult, FALSE) & ((assemblerResult.type = FoxAssembler.ConstantInteger) OR (assemblerResult.type = FoxAssembler.Offset)) THEN
+				value := assemblerResult.value;
+				result := TRUE
+			ELSE
+				value := 0;
+				result := FALSE
+			END;
+			RETURN result
+		END GetPlainValue;
+
+		(** parse an ARM immediate value
+			i.e., the '#'-sign followed by any expression that evaluates to a constant value
+		**)
+		PROCEDURE GetImmediateValue*(VAR immediateValue: LONGINT): BOOLEAN;
+		BEGIN RETURN ThisToken(Scanner.Unequal) & GetPlainValue(immediateValue)
+		END GetImmediateValue;
+
+		PROCEDURE Instruction*(CONST mnemonic: ARRAY OF CHAR);
+		VAR
+			instruction: InstructionSet.Instruction;
+			operands: ARRAY InstructionSet.MaxOperands OF InstructionSet.Operand;
+			position, opCode, condition, i, operandNumber: LONGINT;
+			flags: SET;
+			newOperandExpected: BOOLEAN;
+			result: FoxAssembler.Result;
+			
+			(** parse an operand
+				- note that a subsequent comma is consumed as well
+				- 'newOperandExpected' indicates if any more operands are expected
+			**)
+			PROCEDURE ParseOperand;
+			VAR
+				operand: InstructionSet.Operand;
+				indexingMode, fields: SET;
+				registerNumber, offsetRegisterNumber, shiftModeNumber, shiftRegisterNumber, shiftImmediateValue, position, offsetImmediateValue, value: LONGINT;
+				isImmediateOffset, bracketIsOpen: BOOLEAN;
+				registerList: SET;
+			BEGIN
+				newOperandExpected := FALSE;
+				position := errorPosition;
+				IF operandNumber >= InstructionSet.MaxOperands THEN
+					Error(position, "too many operands")
+				ELSE
+					InstructionSet.InitOperand(operand);
+					IF ThisToken(Scanner.LeftBracket) THEN
+						bracketIsOpen := TRUE;
+						(* memory operand *)
+						indexingMode := {};
+						IF GetRegister(registerNumber) THEN
+							IF ThisToken(Scanner.RightBracket) THEN
+								bracketIsOpen := FALSE;
+								(* post indexing *)
+								INCL(indexingMode, InstructionSet.PostIndexed)
+							END;
+							IF ExpectToken(Scanner.Comma) THEN
+								IF GetImmediateValue(offsetImmediateValue) THEN
+									(* immediate offset memory operand *)
+									isImmediateOffset := TRUE;
+									IF ABS(offsetImmediateValue) < InstructionSet.Bits12 THEN
+										IF offsetImmediateValue >= 0 THEN
+											INCL(indexingMode, InstructionSet.Increment)
+										ELSE
+											INCL(indexingMode, InstructionSet.Decrement)
+										END;
+										offsetImmediateValue := ABS(offsetImmediateValue)
+									ELSE
+										Error(errorPosition, "immediate offset is out of range")
+									END
+								ELSE
+									(* register offset memory operand *)
+									isImmediateOffset := FALSE;
+
+									(* parse sign *)
+									IF ThisToken(Scanner.Plus) THEN
+										INCL(indexingMode, InstructionSet.Increment)
+									ELSIF ThisToken(Scanner.Minus) THEN
+										INCL(indexingMode, InstructionSet.Decrement)
+									ELSE
+										Error(errorPosition, "plus or minus sign expected")
+									END;
+
+									IF ~error THEN
+										(* parse offset register *)
+										IF GetRegister(offsetRegisterNumber) THEN
+											shiftModeNumber := InstructionSet.None;
+											shiftImmediateValue := 0;
+											(* parse optional shift *)
+											IF GetShiftMode(shiftModeNumber) THEN
+												IF GetImmediateValue(shiftImmediateValue) THEN
+													IF shiftImmediateValue >= InstructionSet.Bits5 THEN
+														Error(errorPosition, "immediate shift amount is out of range")
+													END
+												ELSE
+													Error(errorPosition, "immediate shift amount expected")
+												END
+											END
+										ELSE
+											Error(errorPosition, "register expected")
+										END
+									END
+								END
+							END;
+
+							IF bracketIsOpen THEN
+								IF ExpectToken(Scanner.RightBracket) THEN
+									IF ThisToken(Scanner.ExclamationMark) THEN
+										(* preindexing *)
+										INCL(indexingMode, InstructionSet.PreIndexed)
+									END
+								END
+							END
+
+						ELSE
+							Error(errorPosition, "register expected")
+						END;
+
+						IF ~error THEN
+							IF isImmediateOffset THEN
+								InstructionSet.InitImmediateOffsetMemory(operand, registerNumber, offsetImmediateValue, indexingMode)
+							ELSE
+								InstructionSet.InitRegisterOffsetMemory(operand, registerNumber, offsetRegisterNumber, shiftModeNumber, shiftImmediateValue, indexingMode);
+							END
+						END
+
+					ELSIF GetSpecialRegisterWithFields(registerNumber, fields) THEN
+						ASSERT((registerNumber = InstructionSet.CPSR) OR (registerNumber = InstructionSet.SPSR));
+						InstructionSet.InitRegisterWithFields(operand, registerNumber, fields);
+
+					ELSIF GetRegister(registerNumber) THEN
+						(* register *)
+						shiftModeNumber := InstructionSet.None; (* defaults *)
+						shiftRegisterNumber := InstructionSet.None;
+						shiftImmediateValue := 0;
+
+						IF ThisToken(Scanner.ExclamationMark) THEN
+							INCL(flags, InstructionSet.flagBaseRegisterUpdate);
+						END;
+						IF ThisToken(Scanner.Comma) THEN
+							(* parse shift mode *)
+							IF GetShiftMode(shiftModeNumber) THEN
+								IF shiftModeNumber # InstructionSet.shiftRRX THEN (* RRX shift amount is always 1 *)
+									(* parse shift amount *)
+									IF ~GetRegister(shiftRegisterNumber) & ~GetImmediateValue(shiftImmediateValue) THEN
+										Error(position, "invalid shift amount")
+									END
+								END
+							ELSE
+								newOperandExpected := TRUE
+							END
+						END;
+						IF ~error THEN
+							InstructionSet.InitRegister(operand, registerNumber, shiftModeNumber, shiftRegisterNumber, shiftImmediateValue)
+						END
+					ELSIF GetRegisterList(registerList) THEN
+						InstructionSet.InitRegisterList(operand, InstructionSet.R0, registerList);
+						IF ThisToken(Scanner.Arrow) THEN
+							INCL(flags, InstructionSet.flagUserMode);
+						END;
+	
+					ELSIF GetCoprocessor(value) THEN
+						(* coprocessor name *)
+						InstructionSet.InitCoprocessor(operand, value)
+
+					ELSIF GetCoprocessorOpcode(value) THEN (* integer constant in the range 0 .. 7 *)
+						(* coprocessor opcode *)
+						InstructionSet.InitOpcode(operand, value)
+
+					ELSIF GetImmediateValue(value) THEN (* expression that evaluates to constant value starting with '#' *)
+						(* ARM immediate value *)
+						InstructionSet.InitImmediate(operand, value)
+
+					ELSIF GetNonConstant(errorPosition,symbol.identifierString, result) THEN
+						InstructionSet.InitImmediate(operand,result.value);
+						IF result.fixup # NIL THEN
+							InstructionSet.AddFixup(operand,result.fixup);
+						END;
+						NextSymbol;
+					ELSIF GetPlainValue(value) THEN (* expression that evaluates to constant value *)
+						(* resolved label name *)
+						InstructionSet.InitImmediate(operand, value)
+
+					ELSE
+						Error(position, "invalid operand")
+					END;
+
+					IF ThisToken(Scanner.ExclamationMark) THEN
+						INCL(flags, InstructionSet.flagBaseRegisterUpdate);
+					END;
+
+					IF ~newOperandExpected THEN newOperandExpected := ThisToken(Scanner.Comma) END; (* a comma means that there is one more operand *)
+
+					operands[operandNumber] := operand;
+					
+				END
+			END ParseOperand;
+
+		BEGIN
+			(*
+			IF Trace THEN D.String("Instruction: "); D.String(mnemonic);  D.String(" "); D.Ln END;
+			*)
+
+			position := errorPosition;
+			IF InstructionSet.FindMnemonic(mnemonic, opCode, condition, flags) THEN
+				
+				(*IF Trace THEN
+					D.String("    opCode="); D.Int(opCode, 0); D.Ln;
+					D.String("    condition="); D.Int(condition, 0); D.Ln;
+					D.String("    flags="); D.Set(flags); D.Ln;
+				END;*)
+				
+
+				FOR i := 0 TO InstructionSet.MaxOperands - 1 DO
+					InstructionSet.InitOperand(operands[i])
+				END;
+
+				operandNumber := 0;
+				IF symbol.token # Scanner.Ln THEN
+					REPEAT
+						ParseOperand;
+
+						INC(operandNumber);
+					UNTIL error OR ~newOperandExpected;
+				END;
+
+				IF ~error THEN
+					IF ~InstructionSet.MakeInstruction(instruction, opCode, condition, flags, operands) THEN
+						ErrorSS(position, "wrong instruction format: ", mnemonic);
+					ELSE
+						IF pass < FoxAssembler.MaxPasses THEN
+							(* not last pass: only increment the current PC by 4 units *)
+							section.resolved.SetPC(section.resolved.pc + 4)
+						ELSE
+							(* last pass: emit the instruction *)
+							IF ~InstructionSet.EmitInstruction(instruction, section.resolved) THEN
+								ErrorSS(position, "wrong instruction format (encoding failed): ", mnemonic);								
+							END;
+						END
+					END
+				END
+			ELSE
+				ErrorSS(position, "unknown mnemonic: ", mnemonic)
+			END
+		END Instruction;
+
+	END Assembler;
+
+END FoxARMAssembler.
+
+SystemTools.Free FoxARMAssembler FoxARMInstructionSet ~
+Alwazs

+ 3556 - 0
source/FoxARMBackend.Mod

@@ -0,0 +1,3556 @@
+MODULE FoxARMBackend; (** AUTHOR ""; PURPOSE "backend for ARM (advanced RISC machines)"; *)
+
+IMPORT
+	Basic := FoxBasic, SyntaxTree := FoxSyntaxTree, Global := FoxGlobal, Backend := FoxBackend, Sections := FoxSections,
+	IntermediateCode := FoxIntermediateCode, IntermediateBackend := FoxIntermediateBackend, CodeGenerators := FoxCodeGenerators, BinaryCode := FoxBinaryCode,
+	SemanticChecker := FoxSemanticChecker, Formats := FoxFormats, Assembler := FoxARMAssembler, InstructionSet := FoxARMInstructionSet,
+	SYSTEM, Diagnostics, Streams, Options, WMUtilities, Strings, ObjectFile, Scanner := FoxScanner, ObjectFileFormat := FoxGenericObjectFile,
+	ActiveCells := FoxActiveCells, D := Debugging;
+
+CONST
+	Trace = FALSE; (* general trace *)
+	TraceFixups = FALSE;
+	DefaultRuntimeModuleName = "ARMRuntime";
+
+	None = -1;
+
+	(* parts of an ARM operand *)
+	Low = 0; High = 1;
+
+	(* mnemonics of the ARM instruction set *)
+	opADC = InstructionSet.opADC; opADD = InstructionSet.opADD;
+	opAND = InstructionSet.opAND; opB = InstructionSet.opB;
+	opBIC = InstructionSet.opBIC; opBKPT = InstructionSet.opBKPT;
+	opBL = InstructionSet.opBL; opBLX = InstructionSet.opBLX;
+	opBX = InstructionSet.opBX; opCDP = InstructionSet.opCDP;
+	opCDP2 = InstructionSet.opCDP2; opCLZ = InstructionSet.opCLZ;
+	opCMN = InstructionSet.opCMN; opCMP = InstructionSet.opCMP;
+	opEOR = InstructionSet.opEOR; opFABSD = InstructionSet.opFABSD;
+	opFABSS = InstructionSet.opFABSS; opFADDD = InstructionSet.opFADDD;
+	opFADDS = InstructionSet.opFADDS; opFCMPD = InstructionSet.opFCMPD;
+	opFCMPED = InstructionSet.opFCMPED; opFCMPES = InstructionSet.opFCMPES;
+	opFCMPEZD = InstructionSet.opFCMPEZD; opFCMPEZS = InstructionSet.opFCMPEZS;
+	opFCMPS = InstructionSet.opFCMPS; opFCMPZD = InstructionSet.opFCMPZD;
+	opFCMPZS = InstructionSet.opFCMPZS; opFCPYD = InstructionSet.opFCPYD;
+	opFCPYS = InstructionSet.opFCPYS; opFCVTDS = InstructionSet.opFCVTDS;
+	opFCVTSD = InstructionSet.opFCVTSD; opFDIVD = InstructionSet.opFDIVD;
+	opFDIVS = InstructionSet.opFDIVS; opFLDD = InstructionSet.opFLDD;
+	opFLDMIAD = InstructionSet.opFLDMIAD; opFLDMIAS = InstructionSet.opFLDMIAS;
+	opFLDMIAX = InstructionSet.opFLDMIAX; opFLDMDBD = InstructionSet.opFLDMDBD;
+	opFLDMDBS = InstructionSet.opFLDMDBS; opFLDMDBX = InstructionSet.opFLDMDBX;
+	opFLDS = InstructionSet.opFLDS; opFMACD = InstructionSet.opFMACD;
+	opFMACS = InstructionSet.opFMACS; opFMDHR = InstructionSet.opFMDHR;
+	opFMDLR = InstructionSet.opFMDLR; opFMRDH = InstructionSet.opFMRDH;
+	opFMRDL = InstructionSet.opFMRDL; opFMRS = InstructionSet.opFMRS;
+	opFMRX = InstructionSet.opFMRX; opFMSCD = InstructionSet.opFMSCD;
+	opFMSCS = InstructionSet.opFMSCS; opFMSR = InstructionSet.opFMSR;
+	opFMSTAT = InstructionSet.opFMSTAT; opFMULD = InstructionSet.opFMULD;
+	opFMULS = InstructionSet.opFMULS; opFMXR = InstructionSet.opFMXR;
+	opFNEGD = InstructionSet.opFNEGD; opFNEGS = InstructionSet.opFNEGS;
+	opFNMACD = InstructionSet.opFNMACD; opFNMACS = InstructionSet.opFNMACS;
+	opFNMSCD = InstructionSet.opFNMSCD; opFNMSCS = InstructionSet.opFNMSCS;
+	opFNMULD = InstructionSet.opFNMULD ; opFNMULS = InstructionSet.opFNMULS;
+	opFSITOD = InstructionSet.opFSITOD; opFSITOS = InstructionSet.opFSITOS;
+	opFSQRTD = InstructionSet.opFSQRTD; opFSQRTS = InstructionSet.opFSQRTS;
+	opFSTD = InstructionSet.opFSTD; opFSTMIAD = InstructionSet.opFSTMIAD;
+	opFSTMIAS = InstructionSet.opFSTMIAS; opFSTMIAX = InstructionSet.opFSTMIAX;
+	opFSTMDBD = InstructionSet.opFSTMDBD; opFSTMDBS = InstructionSet.opFSTMDBS;
+	opFSTMDBX = InstructionSet.opFSTMDBX; opFSTS = InstructionSet.opFSTS;
+	opFSUBD = InstructionSet.opFSUBD; opFSUBS = InstructionSet.opFSUBS;
+	opFTOSID = InstructionSet.opFTOSID; opFTOSIZD = InstructionSet.opFTOSIZD;
+	opFTOSIS = InstructionSet.opFTOSIS; opFTOSIZS = InstructionSet.opFTOSIZS;
+	opFTOUID = InstructionSet.opFTOUID; opFTOUIZD = InstructionSet.opFTOUIZD;
+	opFTOUIS = InstructionSet.opFTOUIS; opFTOUIZS = InstructionSet.opFTOUIZS;
+	opFUITOD = InstructionSet.opFUITOD; opFUITOS = InstructionSet.opFUITOS;
+	opLDC = InstructionSet.opLDC; opLDC2 = InstructionSet.opLDC2;
+	opLDM = InstructionSet.opLDM; opLDR = InstructionSet.opLDR;
+	opMCR = InstructionSet.opMCR; opMCR2 = InstructionSet.opMCR2;
+	opMCRR = InstructionSet.opMCRR; opMLA = InstructionSet.opMLA;
+	opMOV = InstructionSet.opMOV; opMRC = InstructionSet.opMRC;
+	opMRC2 = InstructionSet.opMRC2; opMRRC = InstructionSet.opMRRC;
+	opMRS = InstructionSet.opMRS; opMSR = InstructionSet.opMSR;
+	opMUL = InstructionSet.opMUL; opMVN = InstructionSet.opMVN;
+	opORR = InstructionSet.opORR; opPLD = InstructionSet.opPLD;
+	opQADD = InstructionSet.opQADD; opQDADD = InstructionSet.opQDADD;
+	opQDSUB = InstructionSet.opQDSUB; opQSUB = InstructionSet.opQSUB;
+	opRSB = InstructionSet.opRSB; opRSC = InstructionSet.opRSC;
+	opSBC = InstructionSet.opSBC; opSMLABB = InstructionSet.opSMLABB;
+	opSMLABT = InstructionSet.opSMLABT; opSMLAL = InstructionSet.opSMLAL;
+	opSMLATB = InstructionSet.opSMLATB; opSMLATT = InstructionSet.opSMLATT;
+	opSMLALBB = InstructionSet.opSMLALBB; opSMLALBT = InstructionSet.opSMLALBT;
+	opSMLALTB = InstructionSet.opSMLALTB; opSMLALTT = InstructionSet.opSMLALTT;
+	opSMLAWB = InstructionSet.opSMLAWB; opSMLAWT = InstructionSet.opSMLAWT;
+	opSMULBB = InstructionSet.opSMULBB; opSMULBT = InstructionSet.opSMULBT;
+	opSMULTB = InstructionSet.opSMULTB; opSMULTT = InstructionSet.opSMULTT;
+	opSMULWB = InstructionSet.opSMULWB; opSMULWT = InstructionSet.opSMULWT;
+	opSMULL = InstructionSet.opSMULL; opSTC = InstructionSet.opSTC;
+	opSTC2 = InstructionSet.opSTC2; opSTM = InstructionSet.opSTM;
+	opSTR = InstructionSet.opSTR; opSUB = InstructionSet.opSUB;
+	opSWI = InstructionSet.opSWI; opSWP = InstructionSet.opSWP;
+	opTEQ = InstructionSet.opTEQ; opTST = InstructionSet.opTST;
+	opUMLAL = InstructionSet.opUMLAL; opUMULL = InstructionSet.opUMULL;
+
+	MaximumFixupDistance = (*4103*) 1024; (* = 2^12-1+8 (maximum distance [in bytes] between a symbol fixup location and an instruction that uses the symbol) *)
+
+	(* builtin backend specific system instructions *)
+	GetSP = 0; SetSP = 1;
+	GetFP = 2; SetFP = 3;
+	GetLNK = 4; SetLNK = 5;
+	GetPC = 6; SetPC = 7;
+	LDPSR = 8; STPSR = 9;
+	LDCPR = 10; STCPR = 11;
+	FLUSH = 12;
+	NULL = 13; XOR = 14; MULD = 15; ADDC = 16;
+	PACK = 17; UNPK = 18;
+
+	UseFPUFlag = "useFPU";
+
+TYPE
+	Operand = InstructionSet.Operand;
+
+	Ticket = CodeGenerators.Ticket;
+
+	(* a citation of a symbol, i.e., an ARM instruction that requires a symbol's address *)
+	Citation = OBJECT
+	VAR
+		pc: LONGINT; (* program counter of the ARM instruction *)
+		next: Citation;
+	END Citation;
+
+	(* a reference to a symbol and offset in IR units that is used by at least one instruction *)
+	Reference = OBJECT
+	VAR
+		firstCitation, lastCitation: Citation; (* linked list of citations *)
+		next: Reference;
+
+		PROCEDURE & Init;
+		BEGIN
+			firstCitation := NIL; lastCitation := NIL; next := NIL;
+		END Init;
+
+		PROCEDURE AddCitation(pc: LONGINT);
+		VAR
+			citation: Citation;
+		BEGIN
+			NEW(citation); citation.pc := pc; citation.next := NIL;
+			IF firstCitation = NIL THEN firstCitation := citation ELSE lastCitation.next := citation END;
+			lastCitation := citation
+		END AddCitation;
+
+	END Reference;
+
+	ImmediateReference = OBJECT (Reference)
+	VAR value: LONGINT;
+
+		PROCEDURE & InitImm(v: LONGINT);
+		BEGIN
+			Init;
+			SELF.value := v;
+		END InitImm;
+
+	END ImmediateReference;
+
+	(* a reference to a symbol and offset in IR units that is used by at least one instruction *)
+	SymbolReference = OBJECT (Reference)
+	VAR
+		symbol: Sections.SectionName;
+		fingerprint: LONGINT;
+		symbolOffset: LONGINT; (* offset to the symbol in IR units *)
+
+		PROCEDURE & InitSym(s: Sections.SectionName; fp: LONGINT; offs: LONGINT);
+		BEGIN
+			Init;
+			SELF.symbol := s; SELF.symbolOffset := offs; fingerprint := fp;
+		END InitSym;
+
+	END SymbolReference;
+
+	ListOfReferences = OBJECT
+	VAR
+		firstReference, lastReference: Reference; (* linked list of all symbol references *)
+		referenceCount: LONGINT; (* the number of reference = length of the required fixup block *)
+		pcOfFirstCitation: LONGINT; (* the PC of the first instruction that cites a symbol or immediate *)
+
+		PROCEDURE & Init;
+		BEGIN
+			firstReference := NIL; lastReference := NIL;
+			referenceCount := 0;
+			pcOfFirstCitation := None;
+		END Init;
+
+		PROCEDURE AddSymbol(symbol: Sections.SectionName; fingerprint: LONGINT; symbolOffset: LONGINT; pc: LONGINT);
+		VAR
+			reference, foundReference: Reference; symbolReference: SymbolReference;
+		BEGIN
+			(* go through the list of symbol/offset-combinations and check if there already is an entry for the symbol and offset in question *)
+			reference := firstReference;
+			WHILE reference # NIL DO
+				IF reference IS SymbolReference THEN
+				WITH reference: SymbolReference DO
+					IF (reference.symbol = symbol) & (reference.symbolOffset = symbolOffset) THEN
+						foundReference := reference (* an entry already exists *)
+					END;
+				END;
+				END;
+				reference := reference.next
+			END;
+
+			IF foundReference # NIL THEN
+				reference := foundReference
+			ELSE
+				(* no entry was found for the symbol/offset combination: create a new one *)
+				NEW(symbolReference, symbol, fingerprint, symbolOffset);
+				reference := symbolReference;
+
+				IF firstReference = NIL THEN firstReference := reference ELSE lastReference.next := reference END;
+				lastReference := reference;
+
+				INC(referenceCount)
+			END;
+
+			(* add a citation to the reference *)
+			reference.AddCitation(pc);
+
+			IF pcOfFirstCitation = None THEN pcOfFirstCitation := pc END
+		END AddSymbol;
+
+		PROCEDURE AddImmediate(value: LONGINT; pc: LONGINT);
+		VAR
+			reference, foundReference: Reference; immediateReference: ImmediateReference;
+		BEGIN
+			(* go through the list of symbol/offset-combinations and check if there already is an entry for the symbol and offset in question *)
+			reference := firstReference;
+			WHILE reference # NIL DO
+				IF reference IS ImmediateReference THEN
+				WITH reference: ImmediateReference DO
+					IF (reference.value = value) THEN
+						foundReference := reference (* an entry already exists *)
+					END;
+				END;
+				END;
+				reference := reference.next
+			END;
+
+			IF foundReference # NIL THEN
+				reference := foundReference
+			ELSE
+				(* no entry was found for the symbol/offset combination: create a new one *)
+				NEW(immediateReference, value);
+				reference := immediateReference;
+
+				IF firstReference = NIL THEN firstReference := reference ELSE lastReference.next := reference END;
+				lastReference := reference;
+
+				INC(referenceCount)
+			END;
+
+			(* add a citation to the reference *)
+			reference.AddCitation(pc);
+
+			IF pcOfFirstCitation = None THEN pcOfFirstCitation := pc END
+
+		END AddImmediate;
+
+
+	END ListOfReferences;
+
+	PhysicalRegisters* = OBJECT(CodeGenerators.PhysicalRegisters)
+	VAR
+		toVirtual: ARRAY InstructionSet.NumberRegisters OF Ticket; (* registers real register -> none / reserved / split / blocked / virtual register (>0) *)
+		reserved: ARRAY InstructionSet.NumberRegisters OF BOOLEAN;
+		unusable: Ticket;
+		hint: LONGINT;
+		useFPU: BOOLEAN;
+
+		PROCEDURE & InitPhysicalRegisters(supportFramePointer, useFPU: BOOLEAN);
+		VAR
+			i: LONGINT;
+			unusable: Ticket;
+		BEGIN
+			SELF.useFPU := useFPU;
+
+			FOR i := 0 TO LEN(toVirtual) - 1 DO
+				toVirtual[i] := NIL;
+				reserved[i] := FALSE
+			END;
+			NEW(unusable);
+
+			(* reserve special purpose registers *)
+			toVirtual[InstructionSet.RES] := unusable; (* low part result register *)
+			toVirtual[InstructionSet.RESHI] := unusable; (* high part result register *)
+			toVirtual[InstructionSet.RESFS] := unusable; (* single precision floatin point result register *)
+			toVirtual[InstructionSet.SP] := unusable; (* stack pointer *)
+			toVirtual[InstructionSet.FP] := unusable; (* frame pointer *)
+			toVirtual[InstructionSet.PC] := unusable; (* program counter *)
+			toVirtual[InstructionSet.LR] := unusable; (* link register *)
+			toVirtual[InstructionSet.CPSR] := unusable; (* current program state register *)
+			toVirtual[InstructionSet.SPSR] := unusable; (* saved program state register *)
+
+			(* disable coprocessor registers *)
+			FOR i := InstructionSet.CR0 TO InstructionSet.CR15 DO toVirtual[i] := unusable END;
+
+			IF ~useFPU THEN
+				(* disable single precision VFP registers *)
+				FOR i := InstructionSet.SR0 TO InstructionSet.SR15 DO toVirtual[i] := unusable END
+			END;
+
+			(* disable double precision VFP registers *)
+			FOR i := InstructionSet.DR0 TO InstructionSet.DR15 DO toVirtual[i] := unusable END;
+
+		END InitPhysicalRegisters;
+
+		(** the number of physical registers **)
+		PROCEDURE NumberRegisters(): LONGINT;
+		BEGIN RETURN InstructionSet.NumberRegisters
+		END NumberRegisters;
+
+		(** allocate, i.e., map, a physical register to a ticket **)
+		PROCEDURE Allocate(physicalRegisterNumber: LONGINT; ticket: Ticket);
+		BEGIN
+			ASSERT(~ticket.spilled);
+			Assert(toVirtual[physicalRegisterNumber] = NIL,"register already allocated");
+			toVirtual[physicalRegisterNumber] := ticket
+		END Allocate;
+
+		(** set whether a certain physical register is reserved or not **)
+		PROCEDURE SetReserved(physicalRegisterNumber: LONGINT; isReserved: BOOLEAN);
+		BEGIN reserved[physicalRegisterNumber] := isReserved
+		END SetReserved;
+
+		(** whether a certain physical register is reserved **)
+		PROCEDURE Reserved(physicalRegisterNumber: LONGINT): BOOLEAN;
+		BEGIN RETURN (physicalRegisterNumber > 0) & reserved[physicalRegisterNumber]
+		END Reserved;
+
+		(** free a certain physical register **)
+		PROCEDURE Free(physicalRegisterNumber: LONGINT);
+		BEGIN
+			Assert((toVirtual[physicalRegisterNumber] # NIL), "register not reserved");
+			toVirtual[physicalRegisterNumber] := NIL
+		END Free;
+
+		(** get the number of the next free physical register for a certain data type
+		- if a register hint has been set, it is respected if possible
+		**)
+		PROCEDURE NextFree(CONST type: IntermediateCode.Type): LONGINT;
+		VAR
+			result, i: LONGINT;
+		BEGIN
+			result := None;
+
+			IF (type.form IN IntermediateCode.Integer) OR ~useFPU THEN
+				ASSERT(type.sizeInBits <= 32); (* integers of larger size have already been split *)
+
+				(* allocate a regular general purpose ARM register *)
+				FOR i := InstructionSet.R0 TO InstructionSet.R15 DO
+					IF (toVirtual[i] = NIL) & ((result = None) OR (i = hint)) THEN result := i END
+				END
+
+			ELSIF type.form = IntermediateCode.Float THEN
+				IF type.sizeInBits = 32 THEN
+					(* allocate a single precision VFP register *)
+					FOR i := InstructionSet.SR0 TO InstructionSet.SR31 DO
+						IF (toVirtual[i] = NIL) & ((result = None) OR (i = hint)) THEN result := i END
+					END
+				ELSIF type.sizeInBits = 64 THEN
+					(* allocate a double precision VFP register *)
+					HALT(200); (* not supported yet *)
+				ELSE
+					HALT(100)
+				END
+
+			ELSE
+				HALT(100)
+			END;
+
+			IF result # None THEN ASSERT(toVirtual[result] = NIL) END;
+			RETURN result
+		END NextFree;
+
+		(** give the register allocator a hint on what physical register to use next **)
+		PROCEDURE AllocationHint(physicalRegisterNumber: LONGINT);
+		BEGIN hint := physicalRegisterNumber
+		END AllocationHint;
+
+		(** get the ticket that is currently mapped to a certain physical register **)
+		PROCEDURE Mapped(physicalRegisterNumber: LONGINT): Ticket;
+		BEGIN RETURN toVirtual[physicalRegisterNumber]
+		END Mapped;
+
+		(** dump the current register mapping to a stream **)
+		PROCEDURE Dump(w: Streams.Writer);
+		VAR i: LONGINT; virtual: Ticket;
+		BEGIN
+			w.String("---- registers ----"); w.Ln;
+			FOR i := 0 TO LEN(toVirtual)-1 DO
+				virtual := toVirtual[i];
+				IF virtual # unusable THEN
+					w.String("reg "); w.Int(i,1); w.String(": ");
+					IF virtual = NIL THEN w.String("free")
+					ELSE	w.String(" r"); w.Int(virtual.register,1);
+					END;
+					IF reserved[i] THEN w.String("reserved") END;
+					w.Ln
+				END
+			END
+		END Dump;
+
+	END PhysicalRegisters;
+
+	CodeGeneratorARM = OBJECT(CodeGenerators.GeneratorWithTickets)
+	VAR
+		runtimeModuleName: SyntaxTree.IdentifierString;
+		backend: BackendARM;
+
+		opSP, opFP, opPC, opLR, opRES, opRESHI, opRESFS: InstructionSet.Operand;
+
+		listOfReferences: ListOfReferences;
+
+		spillStackStart, pushChainLength: LONGINT;
+
+		stackSize: LONGINT; (* the size of the current stack frame *)
+		stackSizeKnown: BOOLEAN; (* whether the size of the current stack frame is known at compile time *)
+
+		inStackAllocation: BOOLEAN;
+
+		fixupPattern: ObjectFile.FixupPatterns; (* pattern for an absolute 32-bit fixup *)
+
+		PROCEDURE & InitGeneratorARM(CONST runtimeModuleName: SyntaxTree.IdentifierString; diagnostics: Diagnostics.Diagnostics; backend: BackendARM);
+		VAR
+			physicalRegisters: PhysicalRegisters;
+		BEGIN
+			SELF.runtimeModuleName := runtimeModuleName;
+			SELF.backend := backend;
+
+			IF Trace THEN IF backend.useFPU THEN D.String("use FPU"); D.Ln ELSE D.String("don't use FPU"); D.Ln END END;
+
+			NEW(physicalRegisters, TRUE, backend.useFPU);
+			InitTicketGenerator(diagnostics, backend.optimize, 2, physicalRegisters);
+			error := FALSE;
+
+			inStackAllocation := FALSE;
+			pushChainLength := 0;
+
+			opSP := InstructionSet.NewRegister(InstructionSet.SP, None, None, 0);
+			opFP := InstructionSet.NewRegister(InstructionSet.FP, None, None, 0);
+			opPC := InstructionSet.NewRegister(InstructionSet.PC, None, None, 0);
+			opLR := InstructionSet.NewRegister(InstructionSet.LR, None, None, 0);
+			opRES := InstructionSet.NewRegister(InstructionSet.RES, None, None, 0);
+			opRESHI := InstructionSet.NewRegister(InstructionSet.RESHI, None, None, 0);
+			opRESFS := InstructionSet.NewRegister(InstructionSet.RESFS, None, None, 0);
+
+			dump := NIL;
+
+			NEW(fixupPattern, 1);
+			fixupPattern[0].offset := 0;
+			fixupPattern[0].bits := 32;
+
+			NEW(listOfReferences);
+
+
+		END InitGeneratorARM;
+
+		(*------------------- overwritten methods ----------------------*)
+
+		(* TODO: revise this *)
+		PROCEDURE Section(in: IntermediateCode.Section; out: BinaryCode.Section);
+		VAR
+			oldSpillStackSize: LONGINT;
+
+			PROCEDURE CheckEmptySpillStack(): BOOLEAN;
+			BEGIN
+				IF spillStack.Size() # 0 THEN
+					Error(inPC,"implementation error, spill stack not cleared");
+					IF dump # NIL THEN
+						spillStack.Dump(dump);
+						tickets.Dump(dump)
+					END;
+					RETURN FALSE
+				ELSE
+					RETURN TRUE
+				END
+			END CheckEmptySpillStack;
+
+		BEGIN
+			stackSizeKnown := TRUE;
+			stackSize := 0; (* TODO: ok? *)
+
+			tickets.Init; spillStack.Init; listOfReferences.Init;
+
+			Section^(in, out); (* pass 1 *)
+			EmitFinalFixupBlock; (* force the emission of fixups for all references *)
+
+			IF stackSizeKnown = FALSE THEN
+				tickets.Init; spillStack.Init; listOfReferences.Init;
+				out.Reset;
+				Section^(in, out); (* pass 2 *)
+				EmitFinalFixupBlock (* force the emission of fixups for all references *)
+			END;
+
+			IF CheckEmptySpillStack() & (spillStack.MaxSize() > 0) THEN
+				listOfReferences.Init;
+				oldSpillStackSize := spillStack.MaxSize();
+				out.Reset;
+				Section^(in, out); (* pass 3 *)
+				EmitFinalFixupBlock; (* force the emission of fixups for all references *)
+				ASSERT(spillStack.MaxSize() = oldSpillStackSize);
+			END;
+			IF CheckEmptySpillStack() THEN END
+		END Section;
+
+		(* TODO: complete this *)
+		(** whether the code generator can generate code for a certain intermediate code intstruction
+		if not, the location of a runtime is returned **)
+		PROCEDURE Supported(CONST irInstruction: IntermediateCode.Instruction; VAR moduleName, procedureName: ARRAY OF CHAR): BOOLEAN;
+		VAR
+			result: BOOLEAN; value: HUGEINT; exp: LONGINT;
+		BEGIN
+			CASE irInstruction.opcode OF
+			| IntermediateCode.add, IntermediateCode.sub, IntermediateCode.mul, IntermediateCode.abs, IntermediateCode.neg:
+				
+				IF (irInstruction.opcode = IntermediateCode.mul) & IsInteger(irInstruction.op1) & IsInteger(irInstruction.op2) & (IsComplex(irInstruction.op1) OR IsComplex(irInstruction.op2)) THEN
+					result := FALSE;
+				ELSE
+					result := backend.useFPU & IsSinglePrecisionFloat(irInstruction.op1) OR ~IsFloat(irInstruction.op1)
+				END;
+
+			| IntermediateCode.div:
+				result := backend.useFPU & IsSinglePrecisionFloat(irInstruction.op1);
+				(*
+				result := result OR IntermediateCode.IsConstantInteger(irInstruction.op3,value) & PowerOf2(value,exp)
+				*)
+
+			| IntermediateCode.conv:
+				result := backend.useFPU & (IsSinglePrecisionFloat(irInstruction.op1) OR IsSinglePrecisionFloat(irInstruction.op2)) OR ~IsFloat(irInstruction.op1) & ~IsFloat(irInstruction.op2) (* if no FPU and either operand is a float *)
+
+			| IntermediateCode.mod:
+				result := FALSE;
+				(*
+				result := IntermediateCode.IsConstantInteger(irInstruction.op3,value) & PowerOf2(value,exp)
+				*)
+
+			| IntermediateCode.rol, IntermediateCode.ror:
+				result := ~IsComplex(irInstruction.op1)
+
+			ELSE
+				result := TRUE
+			END;
+
+			IF ~result THEN
+				COPY(runtimeModuleName, moduleName);
+				GetRuntimeProcedureName(irInstruction, procedureName);
+			END;
+			RETURN result
+		END Supported;
+
+		(* determines the name of a runtime procedure to handle a certain IR instruction *)
+		PROCEDURE GetRuntimeProcedureName(CONST irInstruction: IntermediateCode.Instruction; VAR resultingName: ARRAY OF CHAR);
+
+			PROCEDURE AppendType(VAR string: ARRAY OF CHAR; type: IntermediateCode.Type);
+			VAR
+				sizeString: ARRAY 3 OF CHAR;
+			BEGIN
+				CASE type.form OF
+				| IntermediateCode.SignedInteger: Strings.AppendChar(string, 'S')
+				| IntermediateCode.UnsignedInteger: Strings.AppendChar(string, 'U')
+				| IntermediateCode.Float:Strings.AppendChar(string, 'F')
+				ELSE HALT(200)
+				END;
+				Strings.IntToStr(type.sizeInBits, sizeString); Strings.Append(string, sizeString)
+			END AppendType;
+
+		BEGIN
+			COPY(IntermediateCode.instructionFormat[irInstruction.opcode].name, resultingName);
+			Strings.UpperCaseChar(resultingName[0]);
+			AppendType(resultingName, irInstruction.op1.type);
+			IF irInstruction.op1.mode # IntermediateCode.Undefined THEN
+				IF (irInstruction.op1.type.form # irInstruction.op2.type.form) OR (irInstruction.op1.type.sizeInBits # irInstruction.op2.type.sizeInBits) THEN
+					AppendType(resultingName, irInstruction.op2.type);
+				END
+			END;
+
+			IF Trace THEN D.Ln; D.String(" runtime procedure name: "); D.String(resultingName); D.Ln; D.Update END
+		END GetRuntimeProcedureName;
+
+		(* check whether the instruction modifies the stack pointer (outside of a stack allocation )*)
+		PROCEDURE CheckStackPointer(CONST destination: Operand);
+		BEGIN
+			IF stackSizeKnown & ~inStackAllocation THEN
+				IF (destination.mode = InstructionSet.modeRegister) & (destination.register = InstructionSet.SP) THEN
+					IF dump # NIL THEN dump.String("stackSize unkown"); dump.Ln END;
+					stackSizeKnown := FALSE
+				END
+			END
+		END CheckStackPointer;
+
+		(** emit an ARM instruction with an arbitrary amount of operands **)
+		PROCEDURE Emit(opCode, condition: LONGINT; flags: SET; CONST operands: ARRAY InstructionSet.MaxOperands OF Operand);
+		VAR
+			i: LONGINT;
+		BEGIN
+			(* check whether the instruction modifies the stack pointer *)
+			CheckStackPointer(operands[0]);
+
+			(*
+			(* dump the instruction *)
+			IF Trace THEN
+				D.String("opCode="); D.Int(opCode, 0); D.Ln;
+				D.String("condition="); D.Int(condition, 0); D.Ln;
+				D.String("flags="); D.Set(flags); D.Ln;
+
+				FOR i := 0 TO InstructionSet.MaxOperands - 1 DO
+					D.String("operand #"); D.Int(i, 0); D.String(": ");
+					InstructionSet.DumpOperand(D.Log, operands[i]);
+					D.Ln
+				END;
+				D.Ln;
+				D.Ln
+			END;
+			*)
+
+			(* emit the instruction *)
+			InstructionSet.Emit(opCode, condition, flags, operands, out)
+		END Emit;
+
+		(** emit an ARM instruction with no operand **)
+		PROCEDURE Emit0(opCode: LONGINT);
+		VAR
+			operands: ARRAY InstructionSet.MaxOperands OF Operand;
+		BEGIN
+			ASSERT(InstructionSet.MaxOperands = 6);
+			operands[0] := emptyOperand;
+			operands[1] := emptyOperand;
+			operands[2] := emptyOperand;
+			operands[3] := emptyOperand;
+			operands[4] := emptyOperand;
+			operands[5] := emptyOperand;
+			Emit(opCode, InstructionSet.unconditional, {}, operands)
+		END Emit0;
+
+		(** emit an ARM instruction with 1 operand **)
+		PROCEDURE Emit1(opCode: LONGINT; op: Operand);
+		VAR
+			operands: ARRAY InstructionSet.MaxOperands OF Operand;
+		BEGIN
+			ASSERT(InstructionSet.MaxOperands = 6);
+			operands[0] := op;
+			operands[1] := emptyOperand;
+			operands[2] := emptyOperand;
+			operands[3] := emptyOperand;
+			operands[4] := emptyOperand;
+			operands[5] := emptyOperand;
+			Emit(opCode, InstructionSet.unconditional, {}, operands)
+		END Emit1;
+
+		(** emit an ARM instruction with 2 operands **)
+		PROCEDURE Emit2(opCode: LONGINT; op1, op2: Operand);
+		VAR
+			operands: ARRAY InstructionSet.MaxOperands OF Operand;
+		BEGIN
+			ASSERT(InstructionSet.MaxOperands = 6);
+			operands[0] := op1;
+			operands[1] := op2;
+			operands[2] := emptyOperand;
+			operands[3] := emptyOperand;
+			operands[4] := emptyOperand;
+			operands[5] := emptyOperand;
+			Emit(opCode, InstructionSet.unconditional, {}, operands)
+		END Emit2;
+
+		(** emit an ARM instruction with 3 operands **)
+		PROCEDURE Emit3(opCode: LONGINT; op1, op2, op3: Operand);
+		VAR
+			operands: ARRAY InstructionSet.MaxOperands OF Operand;
+		BEGIN
+			ASSERT(InstructionSet.MaxOperands = 6);
+			operands[0] := op1;
+			operands[1] := op2;
+			operands[2] := op3;
+			operands[3] := emptyOperand;
+			operands[4] := emptyOperand;
+			operands[5] := emptyOperand;
+			Emit(opCode, InstructionSet.unconditional, {}, operands)
+		END Emit3;
+
+		(** emit an ARM instruction with 4 operands **)
+		PROCEDURE Emit4(opCode: LONGINT; op1, op2, op3, op4: Operand);
+		VAR
+			operands: ARRAY InstructionSet.MaxOperands OF Operand;
+		BEGIN
+			ASSERT(InstructionSet.MaxOperands = 6);
+			operands[0] := op1;
+			operands[1] := op2;
+			operands[2] := op3;
+			operands[3] := op4;
+			operands[4] := emptyOperand;
+			operands[5] := emptyOperand;
+			Emit(opCode, InstructionSet.unconditional, {}, operands)
+		END Emit4;
+
+		(** emit an ARM instruction with 6 operands **)
+		PROCEDURE Emit6(opCode: LONGINT; op1, op2, op3, op4, op5, op6: Operand);
+		VAR
+			operands: ARRAY InstructionSet.MaxOperands OF Operand;
+		BEGIN
+			ASSERT(InstructionSet.MaxOperands = 6);
+			operands[0] := op1;
+			operands[1] := op2;
+			operands[2] := op3;
+			operands[3] := op4;
+			operands[4] := op5;
+			operands[5] := op6;
+			Emit(opCode, InstructionSet.unconditional, {}, operands)
+		END Emit6;
+
+		(** emit an ARM instruction with 2 operands and certain flags **)
+		PROCEDURE Emit2WithFlags(opCode: LONGINT; op1, op2: Operand; flags: SET);
+		VAR
+			operands: ARRAY InstructionSet.MaxOperands OF Operand;
+		BEGIN
+			ASSERT(InstructionSet.MaxOperands = 6);
+			operands[0] := op1;
+			operands[1] := op2;
+			operands[2] := emptyOperand;
+			operands[3] := emptyOperand;
+			operands[4] := emptyOperand;
+			operands[5] := emptyOperand;
+
+			Emit(opCode, InstructionSet.unconditional, flags, operands)
+		END Emit2WithFlags;
+
+		(** emit an ARM instruction with 3 operands and certain flags **)
+		PROCEDURE Emit3WithFlags(opCode: LONGINT; op1, op2, op3: Operand; flags: SET);
+		VAR
+			operands: ARRAY InstructionSet.MaxOperands OF Operand;
+		BEGIN
+			ASSERT(InstructionSet.MaxOperands = 6);
+			operands[0] := op1;
+			operands[1] := op2;
+			operands[2] := op3;
+			operands[3] := emptyOperand;
+			operands[4] := emptyOperand;
+			operands[5] := emptyOperand;
+			Emit(opCode, InstructionSet.unconditional, flags, operands)
+		END Emit3WithFlags;
+
+		(** emit an ARM instruction with 1 operand and a condition **)
+		PROCEDURE Emit1WithCondition(opCode: LONGINT; op1: Operand; condition: LONGINT);
+		VAR
+			operands: ARRAY InstructionSet.MaxOperands OF Operand;
+		BEGIN
+			ASSERT(InstructionSet.MaxOperands = 6);
+			operands[0] := op1;
+			operands[1] := emptyOperand;
+			operands[2] := emptyOperand;
+			operands[3] := emptyOperand;
+			operands[4] := emptyOperand;
+			operands[5] := emptyOperand;
+			Emit(opCode, condition, {}, operands)
+		END Emit1WithCondition;
+
+		(** emit an ARM instruction with 2 operands and a condition **)
+		PROCEDURE Emit2WithCondition(opCode: LONGINT; op1, op2: Operand; condition: LONGINT);
+		VAR
+			operands: ARRAY InstructionSet.MaxOperands OF Operand;
+		BEGIN
+			ASSERT(InstructionSet.MaxOperands = 6);
+			operands[0] := op1;
+			operands[1] := op2;
+			operands[2] := emptyOperand;
+			operands[3] := emptyOperand;
+			operands[4] := emptyOperand;
+			operands[5] := emptyOperand;
+			Emit(opCode, condition, {}, operands)
+		END Emit2WithCondition;
+
+		(** emit an ARM instruction with 3 operands and a condition **)
+		PROCEDURE Emit3WithCondition(opCode: LONGINT; op1, op2, op3: Operand; condition: LONGINT);
+		VAR
+			operands: ARRAY InstructionSet.MaxOperands OF Operand;
+		BEGIN
+			ASSERT(InstructionSet.MaxOperands = 6);
+			operands[0] := op1;
+			operands[1] := op2;
+			operands[2] := op3;
+			operands[3] := emptyOperand;
+			operands[4] := emptyOperand;
+			operands[5] := emptyOperand;
+			Emit(opCode, condition, {}, operands)
+		END Emit3WithCondition;
+
+		(**
+		- generate an arbitrary 32 bit value with as few as possible instructions and move the result into a specified target register
+		- return the number of instructions required
+		- if 'doEmit' is TRUE, emit the instructions
+		**)
+		PROCEDURE ValueComposition(value: LONGINT; doEmit: BOOLEAN; CONST targetRegister: Operand): LONGINT;
+		VAR
+			result: LONGINT;
+		BEGIN
+			IF doEmit THEN ASSERT(targetRegister.mode = InstructionSet.modeRegister) END;
+
+			IF Trace & doEmit THEN D.Ln; D.String("original value: "); DBin(value, -32); D.String(" ("); D.Int(value, 0); D.String(") "); D.Ln; END;
+
+			IF ValueComposition2(value, FALSE, emptyOperand) <= ValueComposition2(-value, FALSE, emptyOperand) + 1 THEN
+				(* more efficient to calculate the value directly *)
+				result := ValueComposition2(value, doEmit, targetRegister)
+			ELSE
+				(* more efficient to calculate the negation of the value and then negate it *)
+				result := ValueComposition2(-value, doEmit, targetRegister) + 1;
+				IF doEmit THEN
+					Emit3(opRSB, targetRegister, targetRegister, InstructionSet.NewImmediate(0))
+				END
+			END;
+
+			ASSERT((result >= 1) & (result <= 4));
+			RETURN result
+		END ValueComposition;
+
+		(* note: used by 'ValueComposition'. do not call directly *)
+		PROCEDURE ValueComposition2(value: LONGINT; doEmit: BOOLEAN; CONST targetRegister: Operand): LONGINT;
+		VAR
+			immediateOperand: Operand;
+			result, position, partialValue, i: LONGINT;
+			valueAsSet: SET;
+			isFirst: BOOLEAN;
+		BEGIN
+			IF doEmit THEN ASSERT(targetRegister.mode = InstructionSet.modeRegister) END;
+
+			IF Trace & doEmit THEN D.String("value to use: "); DBin(value, -32); D.String(" ("); D.Int(value, 0); D.String(") "); D.Ln; END;
+
+			IF (value >= 0) & (value <= 255) THEN
+				(* directly encodable as ARM immediate *)
+				result := 1;
+				IF doEmit THEN
+					Emit2(opMOV, targetRegister, InstructionSet.NewImmediate(value))
+				END
+			ELSE
+				valueAsSet := SYSTEM.VAL(SET, value);
+				result := 0;
+				position := 0;
+				isFirst := TRUE;
+				WHILE position < 32 DO
+					IF (position IN valueAsSet) OR (position + 1 IN valueAsSet) THEN
+
+						(* determine partial value for the 8 bit block *)
+						partialValue := 0;
+						FOR i := 7 TO 0 BY -1 DO
+							partialValue := partialValue * 2;
+							IF ((position + i) < 32) & ((position + i) IN valueAsSet) THEN INC(partialValue) END
+						END;
+
+						IF Trace & doEmit THEN
+							D.String("  block found @ "); D.Int(position, 0); D.Ln;
+							D.String("  unshifted partialValue: "); DBin(partialValue, -32); D.String(" ("); D.Int(partialValue, 0); D.String(") "); D.Ln;
+							D.String("  shifted partialValue: "); DBin(ASH(partialValue, position), -32); D.String(" ("); D.Int(ASH(partialValue, position), 0); D.String(") "); D.Ln;
+						END;
+						ASSERT(~ODD(position));
+
+						INC(result);
+						IF doEmit THEN
+							immediateOperand := InstructionSet.NewImmediate(ASH(partialValue, position)); (* TODO: check shift direction *)
+							IF isFirst THEN
+								Emit2(opMOV, targetRegister, immediateOperand);
+								isFirst := FALSE
+							ELSE
+								Emit3(opADD, targetRegister, targetRegister, immediateOperand)
+							END
+						END;
+
+						INC(position, 8)
+					ELSE
+						INC(position, 2)
+					END
+				END
+			END;
+
+			ASSERT((result >= 1) & (result <= 4));
+			RETURN result
+		END ValueComposition2;
+
+		(** get the physical register number that corresponds to a virtual register number and part **)
+		PROCEDURE PhysicalRegisterNumber(virtualRegisterNumber: LONGINT; part: LONGINT): LONGINT;
+		VAR
+			ticket: Ticket;
+			result: LONGINT;
+		BEGIN
+			IF virtualRegisterNumber = IntermediateCode.FP THEN
+				result := InstructionSet.FP
+			ELSIF virtualRegisterNumber = IntermediateCode.SP THEN
+				result := InstructionSet.SP
+			ELSE
+				ticket := virtualRegisters.Mapped(virtualRegisterNumber, part);
+				IF ticket = NIL THEN
+					result := None
+				ELSE
+					result := ticket.register
+				END
+			END;
+			RETURN result
+		END PhysicalRegisterNumber;
+
+		(** get an ARM memory operand that represents a spill location (from a ticket) **)
+		PROCEDURE GetSpillOperand(ticket: Ticket): Operand;
+		VAR
+			offset: LONGINT;
+			result: Operand;
+		BEGIN
+			ASSERT(ticket.spilled);
+
+			offset := spillStackStart + ticket.offset + 1; (* TODO: check this *)
+			ASSERT((0 <= offset) & (offset < InstructionSet.Bits12));
+
+			result := InstructionSet.NewImmediateOffsetMemory(PhysicalRegisterNumber(IntermediateCode.FP, Low), offset, {InstructionSet.Decrement});
+
+			ASSERT(result.mode = InstructionSet.modeMemory);
+			RETURN result
+		END GetSpillOperand;
+
+		(** get an ARM operand that represents a certain ticket (might be spilled or not) **)
+		PROCEDURE OperandFromTicket(ticket: Ticket): Operand;
+		VAR
+			result: Operand;
+		BEGIN
+			ASSERT(ticket # NIL);
+			IF ticket.spilled THEN
+				(* the ticket is spilled *)
+				result := GetSpillOperand(ticket)
+			ELSE
+				result := InstructionSet.NewRegister(ticket.register, None, None, 0)
+			END;
+			RETURN result
+		END OperandFromTicket;
+
+		(** get a free temporary register that holds data of a certain type **)
+		PROCEDURE GetFreeRegister(CONST type: IntermediateCode.Type): Operand;
+		VAR
+			result: Operand;
+		BEGIN
+			result := OperandFromTicket(TemporaryTicket(IntermediateCode.GeneralPurposeRegister, type));
+			ASSERT(result.mode = InstructionSet.modeRegister);
+			RETURN result
+		END GetFreeRegister;
+
+		(** get a new free ARM register
+		- if a register hint is provided that can hold data of the required type, it is returned instead
+		**)
+		PROCEDURE GetFreeRegisterOrHint(CONST type: IntermediateCode.Type; CONST registerHint: Operand): Operand;
+		VAR
+			result: Operand;
+		BEGIN
+			IF (registerHint.mode = InstructionSet.modeRegister) & IsRegisterForType(registerHint.register, type) THEN
+				result := registerHint
+			ELSE
+				result := GetFreeRegister(type)
+			END;
+
+			ASSERT(result.mode = InstructionSet.modeRegister);
+			RETURN result
+		END GetFreeRegisterOrHint;
+
+		(** whether a register can hold data of a certain IR type **)
+		PROCEDURE IsRegisterForType(registerNumber: LONGINT; CONST type: IntermediateCode.Type): BOOLEAN;
+		VAR
+			result: BOOLEAN;
+		BEGIN
+			result := FALSE;
+			IF type.form IN IntermediateCode.Integer THEN
+				IF type.sizeInBits <= 32 THEN
+					result := (registerNumber >= InstructionSet.R0) & (registerNumber <= InstructionSet.R15)
+				END
+			ELSIF type.form = IntermediateCode.Float THEN
+				IF type.sizeInBits = 32 THEN
+					result := (registerNumber >= InstructionSet.SR0) & (registerNumber <= InstructionSet.SR31)
+				ELSE
+					HALT(200)
+				END
+			ELSE
+				HALT(100)
+			END;
+			RETURN result
+		END IsRegisterForType;
+
+		(** get an ARM register that that is set off by a certain amount **)
+		PROCEDURE RegisterAfterAppliedOffset(register: Operand; offset: LONGINT; registerHint: Operand): Operand;
+		VAR
+			result, offsetOperand: Operand;
+		BEGIN
+			IF offset = 0 THEN
+				result := register
+			ELSE
+				result := GetFreeRegisterOrHint(IntermediateCode.UnsignedIntegerType(32), registerHint);
+				offsetOperand := OperandFromValue(ABS(offset), result); (* might be immediate operand or register (tempRegister is given as a register hint) *)
+				IF offset > 0 THEN
+					Emit3(opADD, result, register, offsetOperand)
+				ELSE
+					Emit3(opSUB, result, register, offsetOperand)
+				END
+			END;
+			RETURN result
+		END RegisterAfterAppliedOffset;
+
+		(** get an ARM register from an IR register
+		- use register hint if provided
+		**)
+		PROCEDURE RegisterFromIrRegister(CONST irRegisterOperand: IntermediateCode.Operand; part: LONGINT; registerHint: Operand): Operand;
+		VAR
+			result, offsetOperand, tempReg: Operand;
+		BEGIN
+			ASSERT(irRegisterOperand.mode = IntermediateCode.ModeRegister);
+
+			result := InstructionSet.NewRegister(PhysicalRegisterNumber(irRegisterOperand.register, part), None, None, 0);
+			result := RegisterAfterAppliedOffset(result, irRegisterOperand.offset, registerHint);
+
+			ASSERT(result.mode = InstructionSet.modeRegister);
+			RETURN result
+		END RegisterFromIrRegister;
+
+		PROCEDURE Load(targetRegister, memoryOperand: Operand; irType: IntermediateCode.Type);
+		BEGIN
+			IF (irType.form IN IntermediateCode.Integer) OR ~(backend.useFPU) THEN
+				CASE irType.sizeInBits OF
+				| 8: Emit2WithFlags(opLDR, targetRegister, memoryOperand, {InstructionSet.flagB}) (* LDRB *)
+				| 16: Emit2WithFlags(opLDR, targetRegister, memoryOperand, {InstructionSet.flagH}) (* LDRH *)
+				| 32: (* TM*)
+					Emit2(opLDR, targetRegister, memoryOperand)
+				ELSE HALT(100)
+				END
+			ELSIF irType.form = IntermediateCode.Float THEN
+				ASSERT(irType.sizeInBits = 32, 200);
+				Emit2(opFLDS, targetRegister, memoryOperand)
+			ELSE
+				HALT(100)
+			END
+		END Load;
+
+		PROCEDURE Store(sourceRegister, memoryOperand: Operand; type: IntermediateCode.Type);
+		BEGIN
+			IF (type.form IN IntermediateCode.Integer) OR ~backend.useFPU THEN
+				CASE type.sizeInBits OF
+				| 8: Emit2WithFlags(opSTR, sourceRegister, memoryOperand, {InstructionSet.flagB}) (* STRB *)
+				| 16: Emit2WithFlags(opSTR, sourceRegister, memoryOperand, {InstructionSet.flagH}) (* STRH *)
+				| 32: Emit2(opSTR, sourceRegister, memoryOperand)
+				ELSE HALT(100)
+				END
+			ELSIF type.form = IntermediateCode.Float THEN
+				ASSERT(type.sizeInBits = 32, 200);
+				Emit2(opFSTS, sourceRegister, memoryOperand)
+			ELSE
+				HALT(100)
+			END
+		END Store;
+
+		(** get an ARM register that contains the address of a symbol/section
+		- use register hint if provided **)
+		PROCEDURE RegisterFromSymbol(symbol: Sections.SectionName; fingerprint: LONGINT; resolved: Sections.Section; symbolOffset: LONGINT; CONST registerHint: Operand): Operand;
+		VAR
+			address: LONGINT;
+			result: Operand;
+			irSection: IntermediateCode.Section;
+		BEGIN
+			IF resolved # NIL THEN
+				irSection := resolved(IntermediateCode.Section);
+			END;
+
+			IF (irSection # NIL) & (irSection.resolved # NIL) & (irSection.resolved.os.fixed) THEN
+				(* optimization: if the IR section is already resolved and positioned at a fixed location, no fixup is required *)
+				address := irSection.resolved.os.alignment + irSection.instructions[symbolOffset].pc;
+				result := RegisterFromValue(address, registerHint)
+			ELSE
+				result := GetFreeRegisterOrHint(IntermediateCode.UnsignedIntegerType(32), registerHint);
+				listOfReferences.AddSymbol(symbol, fingerprint, symbolOffset, out.pc);
+				Emit2(opLDR, result, InstructionSet.NewImmediateOffsetMemory(opPC.register, 0, {InstructionSet.Increment})); (* LDR ..., [PC, #+???] *)
+			END;
+
+			ASSERT(result.mode = InstructionSet.modeRegister);
+			RETURN result
+		END RegisterFromSymbol;
+
+		(** get an ARM memory operand from an IR memory operand
+		- note that the constraints on memory operands depend on the type of data (e.g., the allowed offset range is more restricted for memory operands on floating point values)
+		**)
+		PROCEDURE MemoryOperandFromIrMemoryOperand(VAR irMemoryOperand: IntermediateCode.Operand; part: LONGINT; CONST registerHint: Operand): Operand;
+		VAR
+			baseAddressRegisterNumber, offset: LONGINT;
+			indexingMode: SET;
+			result, baseAddressRegister, offsetRegister, tempRegister: Operand;
+		BEGIN
+			ASSERT(irMemoryOperand.mode = IntermediateCode.ModeMemory);
+
+			(* determine base address register *)
+			IF irMemoryOperand.register # IntermediateCode.None THEN
+				(* case 1: [r1] or [r1 + 7] *)
+				ASSERT(irMemoryOperand.symbol.name = "");
+				baseAddressRegisterNumber := PhysicalRegisterNumber(irMemoryOperand.register, Low); (* addresses always are in the lower part *)
+
+			ELSIF irMemoryOperand.symbol.name # "" THEN
+				(* case 2: [symbol], [symbol:3], [symbol + 7] or [symbol:3 + 7] *)
+				Resolve(irMemoryOperand);
+				baseAddressRegister := RegisterFromSymbol(irMemoryOperand.symbol.name, irMemoryOperand.symbol.fingerprint, irMemoryOperand.resolved, irMemoryOperand.symbolOffset, registerHint);
+				baseAddressRegisterNumber := baseAddressRegister.register
+
+			ELSE
+				(* case 3: [123456] *)
+				ASSERT(irMemoryOperand.offset = 0);
+				baseAddressRegister := RegisterFromValue(LONGINT(irMemoryOperand.intValue), registerHint);
+				baseAddressRegisterNumber := baseAddressRegister.register
+			END;
+			ASSERT(baseAddressRegisterNumber # None);
+
+			(* get offset of part in question *)
+			offset := irMemoryOperand.offset + part * 4;
+
+			(* determine indexing mode *)
+			IF offset >= 0 THEN indexingMode := {InstructionSet.Increment} ELSE indexingMode := {InstructionSet.Decrement} END;
+
+			IF irMemoryOperand.type.form IN IntermediateCode.Integer THEN
+				(* regular ARM memory operand *)
+				(*! LDRH supports only 8 bits immediates, while LDR and LDRB support 12 bits immediates *)
+				IF ((irMemoryOperand.type.sizeInBits = 16) & (ABS(offset) < 256)) OR ((irMemoryOperand.type.sizeInBits # 16) & (ABS(offset) < InstructionSet.Bits12)) THEN
+					(* offset can be encoded directly *)
+					result := InstructionSet.NewImmediateOffsetMemory(baseAddressRegisterNumber, ABS(offset), indexingMode)
+				ELSE
+					(* offset has to be provided in a register *)
+					offsetRegister := RegisterFromValue(ABS(offset), emptyOperand);
+					result := InstructionSet.NewRegisterOffsetMemory(baseAddressRegisterNumber, offsetRegister.register, None, 0, indexingMode)
+				END
+			ELSIF irMemoryOperand.type.form = IntermediateCode.Float THEN
+				(* VFP memory operand *)
+				ASSERT((ABS(offset) MOD 4) = 0);
+				IF ABS(offset) >= 1024 THEN
+					(* offset cannot be encoded directly _> it has to be provided by means of an adapted base register *)
+					tempRegister := RegisterFromValue(ABS(offset), emptyOperand);
+					IF offset < 0 THEN
+						Emit3(opSUB, tempRegister, tempRegister, baseAddressRegister)
+					ELSE
+						Emit3(opADD, tempRegister, tempRegister, baseAddressRegister)
+					END;
+					ReleaseHint(baseAddressRegister.register);
+					baseAddressRegister := tempRegister;
+					baseAddressRegisterNumber := baseAddressRegister.register;
+					offset := 0;
+				END;
+				result := InstructionSet.NewImmediateOffsetMemory(baseAddressRegisterNumber, ABS(offset), indexingMode)
+			ELSE
+				HALT(100)
+			END;
+
+			ASSERT(result.mode = InstructionSet.modeMemory);
+			RETURN result
+		END MemoryOperandFromIrMemoryOperand;
+
+		(** get an ARM immediate operand or register from any IR operand
+		- if possible, the an immediate is returned
+		- if needed, use register hint if provided
+		**)
+		PROCEDURE RegisterOrImmediateFromIrOperand(VAR irOperand: IntermediateCode.Operand; part: LONGINT; registerHint: Operand): Operand;
+		VAR
+			result: Operand;
+		BEGIN
+			IF IrOperandIsDirectlyEncodable(irOperand, part) THEN
+				result := InstructionSet.NewImmediate(ValueOfPart(irOperand.intValue, part))
+			ELSE
+				result := RegisterFromIrOperand(irOperand, part, registerHint)
+			END;
+			RETURN result
+		END RegisterOrImmediateFromIrOperand;
+
+		(** get an ARM register operand from any IR operand
+		- use register hint if provided
+		**)
+		PROCEDURE RegisterFromIrOperand(VAR irOperand: IntermediateCode.Operand; part: LONGINT; registerHint: Operand): Operand;
+		VAR
+			value: LONGINT;
+			result: Operand;
+		BEGIN
+			CASE irOperand.mode OF
+			| IntermediateCode.ModeRegister:
+				ASSERT((irOperand.intValue = 0) & (irOperand.symbol.name = ""));
+				result := RegisterFromIrRegister(irOperand, part, registerHint)
+
+			| IntermediateCode.ModeMemory:
+				result := GetFreeRegisterOrHint(PartType(irOperand.type, part), registerHint);
+				Load(result, MemoryOperandFromIrMemoryOperand(irOperand, part, result), PartType(irOperand.type, part))
+
+			| IntermediateCode.ModeImmediate:
+				ASSERT(irOperand.register = IntermediateCode.None);
+				IF irOperand.symbol.name # "" THEN
+					Resolve(irOperand);
+					result := RegisterFromSymbol(irOperand.symbol.name, irOperand.symbol.fingerprint, irOperand.resolved, irOperand.symbolOffset, emptyOperand);
+					result := RegisterAfterAppliedOffset(result, irOperand.offset, registerHint);
+				ELSE
+					ASSERT(irOperand.offset = 0);
+					IF IsInteger(irOperand) THEN result := RegisterFromValue(ValueOfPart(irOperand.intValue, part), registerHint)
+					ELSIF ~backend.useFPU THEN
+						IF IsSinglePrecisionFloat(irOperand) THEN
+							result := RegisterFromValue(BinaryCode.ConvertReal(SHORT(irOperand.floatValue)), registerHint)
+						ELSE
+							result := RegisterFromValue(ValueOfPart(BinaryCode.ConvertLongreal(irOperand.floatValue),part), registerHint);
+						END;
+					ELSIF IsSinglePrecisionFloat(irOperand) THEN result := SinglePrecisionFloatRegisterFromValue(REAL(irOperand.floatValue), registerHint)
+					ELSE HALT(200)
+					END
+
+				END
+
+			ELSE
+				HALT(100)
+			END;
+
+			ASSERT(result.mode = InstructionSet.modeRegister);
+			RETURN result
+		END RegisterFromIrOperand;
+
+		(** whether an IR operand is complex, i.e., requires more than one ARM operands to be represented **)
+		PROCEDURE IsComplex(CONST irOperand: IntermediateCode.Operand): BOOLEAN;
+		VAR
+			result: BOOLEAN;
+		BEGIN
+			IF (irOperand.type.form IN IntermediateCode.Integer) OR ~backend.useFPU THEN
+				result := irOperand.type.sizeInBits > 32 (* integers above 32 bits have to be represented in multiple registers *)
+			ELSIF irOperand.type.form = IntermediateCode.Float THEN
+				result := FALSE (* for all types of floating point numbers there are dedicated VFP registers *)
+			ELSE
+				HALT(100)
+			END;
+			RETURN result
+		END IsComplex;
+
+		(** whether an IR operand hold a single precision floating point value **)
+		PROCEDURE IsSinglePrecisionFloat(CONST irOperand: IntermediateCode.Operand): BOOLEAN;
+		BEGIN RETURN (irOperand.type.sizeInBits = 32) & (irOperand.type.form = IntermediateCode.Float)
+		END IsSinglePrecisionFloat;
+
+		(** whether an IR operand hold a single precision floating point value **)
+		PROCEDURE IsDoublePrecisionFloat(CONST irOperand: IntermediateCode.Operand): BOOLEAN;
+		BEGIN RETURN (irOperand.type.sizeInBits = 64) & (irOperand.type.form = IntermediateCode.Float)
+		END IsDoublePrecisionFloat;
+
+		PROCEDURE IsFloat(CONST irOperand: IntermediateCode.Operand): BOOLEAN;
+		BEGIN
+			RETURN irOperand.type.form = IntermediateCode.Float
+		END IsFloat;
+
+
+		(** whether an IR operand hold am integer value **)
+		PROCEDURE IsInteger(CONST irOperand: IntermediateCode.Operand): BOOLEAN;
+		BEGIN RETURN irOperand.type.form IN IntermediateCode.Integer
+		END IsInteger;
+
+		PROCEDURE PartType(CONST type: IntermediateCode.Type; part: LONGINT): IntermediateCode.Type;
+		VAR
+			result: IntermediateCode.Type;
+		BEGIN
+			GetPartType(type, part, result);
+			RETURN result
+		END PartType;
+
+		(* the intermediate code type of a part
+		- a part type is by definition directly representable in a register *)
+		PROCEDURE GetPartType(CONST type: IntermediateCode.Type; part: LONGINT; VAR partType: IntermediateCode.Type);
+		BEGIN
+			ASSERT((part = Low) OR (part = High));
+			IF (type.form = IntermediateCode.Float) & backend.useFPU THEN
+				IF part = Low THEN
+					partType := type
+				ELSE
+					partType := IntermediateCode.undef
+				END
+			ELSIF (type.form IN IntermediateCode.Integer) OR ~backend.useFPU THEN
+				IF type.sizeInBits <= 32 THEN
+					IF part = Low THEN
+						partType := type
+					ELSE
+						partType := IntermediateCode.undef
+					END
+				ELSIF type.sizeInBits = 64 THEN
+					IF part = Low THEN
+						partType := IntermediateCode.NewType(IntermediateCode.UnsignedInteger, 32) (* conceptually the low part is always unsigned *)
+					ELSE
+						partType := IntermediateCode.NewType(type.form, 32)
+					END
+				ELSE
+					HALT(100)
+				END
+			ELSE
+				HALT(100)
+			END
+		END GetPartType;
+
+		(** the value of a 32 bit part **)
+		PROCEDURE ValueOfPart(value: HUGEINT; part: LONGINT): LONGINT;
+		VAR
+			result: LONGINT;
+		BEGIN
+			IF part = Low THEN
+				result := LONGINT(value) (* get the 32 least significant bits *)
+			ELSIF part = High THEN
+				result := LONGINT(ASH(value, -32)) (* get the 32 most significant bits *)
+			ELSE
+				HALT(100)
+			END;
+			RETURN result
+		END ValueOfPart;
+
+		(** whether a 32 bit value can be directly encoded as an ARM immediate (using a 8-bit base value and 4-bit half rotation) **)
+		PROCEDURE ValueIsDirectlyEncodable(value: LONGINT): BOOLEAN;
+		VAR
+			baseValue, halfRotation: LONGINT;
+			result: BOOLEAN;
+		BEGIN
+			result := InstructionSet.EncodeImmediate(value, baseValue, halfRotation);
+			RETURN result
+		END ValueIsDirectlyEncodable;
+
+		(* whether an IR operand (or part thereof) can be directly encoded as an ARM immediate *)
+		PROCEDURE IrOperandIsDirectlyEncodable(irOperand: IntermediateCode.Operand; part: LONGINT): BOOLEAN;
+		BEGIN RETURN
+				(irOperand.mode = IntermediateCode.ModeImmediate) &
+				(irOperand.symbol.name = "") &
+				(irOperand.type.form IN IntermediateCode.Integer) &
+				ValueIsDirectlyEncodable(ValueOfPart(irOperand.intValue, part))
+		END IrOperandIsDirectlyEncodable;
+
+		(* whether the negation of an IR operand (or part thereof) can be directly encoded as an ARM immediate *)
+		PROCEDURE NegatedIrOperandIsDirectlyEncodable(irOperand: IntermediateCode.Operand; part: LONGINT): BOOLEAN;
+		BEGIN RETURN
+				(irOperand.mode = IntermediateCode.ModeImmediate) &
+				(irOperand.symbol.name = "") &
+				(irOperand.type.form IN IntermediateCode.Integer) &
+				ValueIsDirectlyEncodable(ValueOfPart(-irOperand.intValue, part)) (* note the minus sign *)
+		END NegatedIrOperandIsDirectlyEncodable;
+
+		(** generate code for a certain IR instruction **)
+		PROCEDURE Generate(VAR irInstruction: IntermediateCode.Instruction);
+		VAR
+			ticket: Ticket;
+			(* hwreg, lastUse: LONGINT; *)
+		BEGIN
+			(* CheckFixups; *)
+			EmitFixupBlockIfNeeded;
+
+			(*
+			IF ((irInstruction.opcode = IntermediateCode.mov) OR (irInstruction.opcode = IntermediateCode.pop)) & (instruction.op1.register <= IntermediateCode.ParameterRegister) THEN
+				hwreg := ParameterRegister(IntermediateCode.ParameterRegister-instruction.op1.register, instruction.op1.type);
+				Spill(physicalRegisters.Mapped(hwreg));
+				lastUse := inPC+1;
+				WHILE (lastUse < in.pc) &
+					((in.instructions[lastUse].opcode # IntermediateCode.push) OR (in.instructions[lastUse].op1.register # instruction.op1.register)) & (in.instructions[lastUse].opcode # IntermediateCode.call) DO
+					INC(lastUse)
+				END;
+				ticket := ReservePhysicalRegister(instruction.op1.type,hwreg,lastUse);
+			END;
+			*)
+
+			ReserveOperandRegisters(irInstruction.op1, TRUE);
+			ReserveOperandRegisters(irInstruction.op2, TRUE);
+			ReserveOperandRegisters(irInstruction.op3, TRUE);
+
+			CASE irInstruction.opcode OF
+			| IntermediateCode.nop: (* do nothing *)
+			| IntermediateCode.mov: EmitMov(irInstruction, Low); IF IsComplex(irInstruction.op1) THEN EmitMov(irInstruction, High) END
+			| IntermediateCode.conv: EmitConv(irInstruction)
+			| IntermediateCode.call: EmitCall(irInstruction)
+			| IntermediateCode.enter: EmitEnter(irInstruction)
+			| IntermediateCode.leave: EmitLeave(irInstruction)
+			| IntermediateCode.exit: EmitExit(irInstruction)
+			| IntermediateCode.return: EmitReturn(irInstruction, Low); IF IsComplex(irInstruction.op1) THEN EmitReturn(irInstruction, High) END;
+			| IntermediateCode.result: EmitResult(irInstruction, Low); IF IsComplex(irInstruction.op1) THEN EmitResult(irInstruction, High) END;
+			| IntermediateCode.trap: EmitTrap(irInstruction);
+			| IntermediateCode.br .. IntermediateCode.brlt: EmitBr(irInstruction)
+			| IntermediateCode.pop: EmitPop(irInstruction.op1, Low); IF IsComplex(irInstruction.op1) THEN EmitPop(irInstruction.op1, High) END
+			| IntermediateCode.push: IF IsComplex(irInstruction.op1) THEN EmitPush(irInstruction.op1, High) END; EmitPush(irInstruction.op1, Low)
+			| IntermediateCode.neg: EmitNeg(irInstruction)
+			| IntermediateCode.not: EmitNot(irInstruction, Low); IF IsComplex(irInstruction.op1) THEN EmitNot(irInstruction, High) END
+			| IntermediateCode.abs: EmitAbs(irInstruction)
+			| IntermediateCode.mul: EmitMul(irInstruction)
+			| IntermediateCode.div: EmitDiv(irInstruction)
+			| IntermediateCode.mod: EmitMod(irInstruction)
+			| IntermediateCode.sub, IntermediateCode.add: EmitAddOrSub(irInstruction)
+			| IntermediateCode.and: EmitAnd(irInstruction, Low); IF IsComplex(irInstruction.op1) THEN EmitAnd(irInstruction, High) END
+			| IntermediateCode.or: EmitOr(irInstruction, Low); IF IsComplex(irInstruction.op1) THEN EmitOr(irInstruction, High) END
+			| IntermediateCode.xor: EmitXor(irInstruction, Low); IF IsComplex(irInstruction.op1) THEN EmitXor(irInstruction, High) END
+			| IntermediateCode.shl: EmitShiftOrRotation(irInstruction)
+			| IntermediateCode.shr: EmitShiftOrRotation(irInstruction)
+			| IntermediateCode.rol: EmitShiftOrRotation(irInstruction)
+			| IntermediateCode.ror: EmitShiftOrRotation(irInstruction)
+			| IntermediateCode.copy: EmitCopy(irInstruction)
+			| IntermediateCode.fill: EmitFill(irInstruction, FALSE)
+			| IntermediateCode.asm: EmitAsm(irInstruction)
+			| IntermediateCode.special: EmitSpecial(irInstruction)
+			END;
+
+			ReserveOperandRegisters(irInstruction.op3, FALSE);
+			ReserveOperandRegisters(irInstruction.op2 ,FALSE);
+			ReserveOperandRegisters(irInstruction.op1, FALSE);
+
+		END Generate;
+
+		PROCEDURE PostGenerate(CONST instruction: IntermediateCode.Instruction);
+		VAR ticket: Ticket;
+		BEGIN
+			TryUnmap(instruction.op3); TryUnmap(instruction.op2); TryUnmap(instruction.op1);
+			ticket := tickets.live;
+			WHILE (ticket # NIL) & (ticket.lastuse = inPC) DO
+				UnmapTicket(ticket);
+				ticket := tickets.live
+			END;
+		END PostGenerate;
+
+		PROCEDURE EmitFinalFixupBlock;
+		BEGIN
+			IF listOfReferences.referenceCount > 0 THEN
+				ASSERT(in.pc > 0);
+				IF in.instructions[in.pc - 1].opcode # IntermediateCode.exit THEN
+					(* there is no exit instruction at the end of the IR section -> emit a branch that skips the fixup block (in particular used by @BodyStub procedures)*)
+					Emit1(opB, InstructionSet.NewImmediate((listOfReferences.referenceCount + 1) * 4 - 8))
+				END
+			END;
+			EmitFixupBlock; (* emit the fixup block *)
+		END EmitFinalFixupBlock;
+
+		(* if needed, emit fixup block for all used symbol references
+		 - the fixup block is skipped by a branch instruction
+		 - afterwards, the list of references is cleared
+		*)
+		PROCEDURE EmitFixupBlockIfNeeded;
+		BEGIN
+			IF out.pc - listOfReferences.pcOfFirstCitation + listOfReferences.referenceCount + 1 > MaximumFixupDistance THEN
+				Emit1(opB, InstructionSet.NewImmediate((listOfReferences.referenceCount + 1) * 4 - 8)); (* emit branch instruction that skips the fixup block *)
+				EmitFixupBlock; (* emit the fixup block *)
+				listOfReferences.Init (* clear the list *)
+			END
+		END EmitFixupBlockIfNeeded;
+
+		(* emit fixup block for all used symbol references, and clear the list *)
+		PROCEDURE EmitFixupBlock;
+		VAR
+			reference: Reference;
+			citation: Citation;
+			fixup: BinaryCode.Fixup;
+			patchValue: LONGINT;
+			identifier: ObjectFile.Identifier;
+		BEGIN
+			IF listOfReferences.referenceCount > 0 THEN
+				IF out.comments # NIL THEN
+					out.comments.String("REFERENCES BLOCK"); out.comments.String(" (");
+					out.comments.Int(listOfReferences.referenceCount, 0);
+					out.comments.String(" references):"); out.comments.Ln; out.comments.Update
+				END;
+
+				reference := listOfReferences.firstReference;
+				WHILE reference # NIL DO
+					(* 1. patch all of the citations, i.e., the LDR instructions that use the symbol reference *)
+					citation := reference.firstCitation;
+					WHILE citation # NIL DO
+						patchValue := out.pc - 8 - citation.pc;
+						ASSERT((0 <= patchValue) & (patchValue < InstructionSet.Bits12));
+						out.PutBitsAt(citation.pc, patchValue, 12);
+						citation := citation.next
+					END;
+
+					IF reference IS SymbolReference THEN
+					WITH reference: SymbolReference DO
+
+						(* alternative version that relies on the fixup mechanism:
+						NEW(fixupPattern12, 1);
+						fixupPattern12[0].offset := 0;
+						fixupPattern12[0].bits := 12;
+						fixup := BinaryCode.NewFixup(BinaryCode.Relative, entry.pc, in, 0, out.pc - 8, 0, fixupPattern12); (* TODO: determine the correct displacement *)
+						out.fixupList.AddFixup(fixup);
+						*)
+
+						(* 2. add an absolute fixup for the symbol reference and emit space *)
+						IF out.comments # NIL THEN
+							out.comments.String("fixup location for ");
+							Basic.WriteSegmentedName(out.comments, reference.symbol);
+							out.comments.String(":"); out.comments.Int(reference.symbolOffset, 0);
+							out.comments.String(" :"); out.comments.Ln; out.comments.Update
+						END;
+						identifier.name := reference.symbol;
+						identifier.fingerprint := reference.fingerprint;
+
+						fixup := BinaryCode.NewFixup(BinaryCode.Absolute, out.pc, identifier, reference.symbolOffset, 0, 0, fixupPattern);
+						out.fixupList.AddFixup(fixup);
+						out.PutBits(0, 32);
+					END;
+					ELSIF reference IS ImmediateReference THEN
+					WITH reference: ImmediateReference DO
+						IF out.comments # NIL THEN
+							out.comments.String("immediate value"); out.comments.Ln; out.comments.Update;
+						END;
+						out.PutBits(reference.value,32);
+					END
+					END;
+
+					reference := reference.next
+				END
+			END
+		END EmitFixupBlock;
+
+		(** get an ARM operand that hold a certain value
+		- if possible the value is returned as an ARM immediate operand
+		- otherwise a register is returned instead (if a register hint is present, it is used) **)
+		PROCEDURE OperandFromValue(value: LONGINT; registerHint: Operand): Operand;
+		VAR
+			result: Operand;
+		BEGIN
+			IF ValueIsDirectlyEncodable(value) THEN
+				result := InstructionSet.NewImmediate(value)
+			ELSE
+				result := RegisterFromValue(value, registerHint)
+			END;
+			RETURN result
+		END OperandFromValue;
+
+		(** get a single precision VFP register that holds a certain floating point value **)
+		PROCEDURE SinglePrecisionFloatRegisterFromValue(value: REAL; registerHint: Operand): Operand;
+		VAR
+			intValue, dummy: LONGINT;
+			result, temp: Operand;
+		BEGIN
+			intValue := SYSTEM.VAL(LONGINT, value);
+			(* alternative: integerValue := BinaryCode.ConvertReal(value) *)
+
+			temp := GetFreeRegisterOrHint(IntermediateCode.UnsignedIntegerType(32), registerHint);
+			dummy := ValueComposition(intValue, TRUE, temp);
+			result := GetFreeRegisterOrHint(IntermediateCode.FloatType(32), registerHint);
+			Emit2(opFMSR, result, temp);
+
+			ASSERT(result.mode = InstructionSet.modeRegister);
+			ASSERT((result.register >= InstructionSet.SR0) & (result.register <= InstructionSet.SR31));
+			RETURN result;
+		END SinglePrecisionFloatRegisterFromValue;
+
+		(** get an ARM register that holds a certain integer value
+		- if a register hint is present, it is used **)
+		PROCEDURE RegisterFromValue(value: LONGINT; registerHint: Operand): Operand;
+		VAR
+			dummy: LONGINT;
+			result: Operand;
+		BEGIN
+			result := GetFreeRegisterOrHint(IntermediateCode.SignedIntegerType(32), registerHint);
+			IF ValueComposition(value, FALSE, result) < 3 THEN
+				dummy := ValueComposition(value, TRUE, result);
+			ELSE
+				result := GetFreeRegisterOrHint(IntermediateCode.UnsignedIntegerType(32), registerHint);
+				listOfReferences.AddImmediate(value, out.pc);
+				Emit2(opLDR, result, InstructionSet.NewImmediateOffsetMemory(opPC.register, 0, {InstructionSet.Increment})); (* LDR ..., [PC, #+???] *)
+			END;
+
+			ASSERT(result.mode = InstructionSet.modeRegister);
+			ASSERT((result.register >= InstructionSet.R0) & (result.register <= InstructionSet.R15));
+			RETURN result
+		END RegisterFromValue;
+
+		(** allocate or deallocate on the stack
+			- note: updateStackSize is important as intermediate RETURNs should not change stack size
+		**)
+		PROCEDURE AllocateStack(allocationSize: LONGINT; doUpdateStackSize: BOOLEAN; clear: BOOLEAN);
+		VAR
+			operand, zero: InstructionSet.Operand; i: LONGINT;
+		BEGIN
+			inStackAllocation := TRUE;
+			operand := OperandFromValue(ABS(allocationSize), emptyOperand);
+			IF allocationSize > 0 THEN
+				IF clear THEN
+					Emit2(opMOV, InstructionSet.NewRegister(0, None, None, 0), InstructionSet.NewImmediate(0));
+					FOR i := 0 TO allocationSize-1 BY 4 DO
+						Emit2(opSTR, InstructionSet.NewRegister(0, None, None, 0), InstructionSet.NewImmediateOffsetMemory(InstructionSet.SP, 4, {InstructionSet.Decrement, InstructionSet.PreIndexed}));
+					END;
+				ELSE
+					Emit3(opSUB, opSP, opSP, operand) (* decreasing SP: allocation *)
+				END;
+			ELSIF allocationSize < 0 THEN
+				Emit3(opADD, opSP, opSP, operand) (* increasing SP: deallocation *)
+			END;
+			IF doUpdateStackSize THEN stackSize := stackSize + allocationSize END;
+			inStackAllocation := FALSE
+		END AllocateStack;
+
+		(** whether two ARM operands represent the same physical register **)
+		PROCEDURE IsSameRegister(CONST a, b: Operand): BOOLEAN;
+		BEGIN RETURN (a.mode = InstructionSet.modeRegister) & (b.mode = InstructionSet.modeRegister) & (a.register = b.register)
+		END IsSameRegister;
+
+		(** emit a MOV instruction if the two operands do not represent the same register
+		- for moves involving floating point registers special VFP instructions opFCPYS, opFMSR and opFMRS are used
+		**)
+		PROCEDURE MovIfDifferent(CONST a, b: Operand);
+		BEGIN
+			IF ~IsSameRegister(a, b) THEN
+				ASSERT(a.mode = InstructionSet.modeRegister);
+
+				IF IsRegisterForType(a.register, IntermediateCode.FloatType(32)) THEN
+					IF IsRegisterForType(b.register, IntermediateCode.FloatType(32)) THEN
+						(* mov float, float: *)
+						Emit2(opFCPYS, a, b)
+					ELSE
+						(* mov float, int: *)
+						Emit2(opFMSR, a, b)
+					END
+				ELSE
+					IF IsRegisterForType(b.register, IntermediateCode.FloatType(32)) THEN
+						(* mov int, float: *)
+						Emit2(opFMRS, a, b)
+					ELSE
+						(* mov int, int: *)
+						Emit2(opMOV, a, b)
+					END
+				END
+			END
+		END MovIfDifferent;
+
+		(** acquire an ARM register fr oa IR destination operand part
+			- if IR operand is a memory location, get a temporary register (if provided the hinted register is used)
+			- if IR operand is an IR register, get the ARM register that is mapped to the corresponding part
+		**)
+		PROCEDURE AcquireDestinationRegister(CONST irDestinationOperand: IntermediateCode.Operand; part: LONGINT; registerHint: Operand): Operand;
+		VAR
+			result: Operand;
+		BEGIN
+			IF irDestinationOperand.mode = IntermediateCode.ModeMemory THEN
+				result := GetFreeRegisterOrHint(PartType(irDestinationOperand.type, part), registerHint)
+			ELSIF irDestinationOperand.mode = IntermediateCode.ModeRegister THEN
+				ASSERT(irDestinationOperand.offset = 0);
+				IF virtualRegisters.Mapped(irDestinationOperand.register, part) = NIL THEN TryAllocate(irDestinationOperand, part) END; (* create the mapping if not yet done *)
+				result := InstructionSet.NewRegister(PhysicalRegisterNumber(irDestinationOperand.register, part), None, None, 0)
+			ELSE
+				HALT(100)
+			END;
+
+			ASSERT(result.mode = InstructionSet.modeRegister);
+			RETURN result
+		END AcquireDestinationRegister;
+
+		(** write the content of an ARM register to an IR destination operand (memory location or IR register)
+			- afterwards, try to release the register
+		**)
+		PROCEDURE WriteBack(VAR irDestinationOperand: IntermediateCode.Operand; part: LONGINT; register: Operand);
+		VAR
+			mappedArmRegister: Operand;
+		BEGIN
+			ASSERT(register.mode = InstructionSet.modeRegister);
+
+			IF irDestinationOperand.mode = IntermediateCode.ModeMemory THEN
+				Store(register, MemoryOperandFromIrMemoryOperand(irDestinationOperand, part, emptyOperand), PartType(irDestinationOperand.type, part))
+			ELSIF irDestinationOperand.mode = IntermediateCode.ModeRegister THEN
+				ASSERT((virtualRegisters.Mapped(irDestinationOperand.register, part) # NIL)
+						OR (irDestinationOperand.register = IntermediateCode.SP)
+						OR (irDestinationOperand.register = IntermediateCode.FP));
+				mappedArmRegister := InstructionSet.NewRegister(PhysicalRegisterNumber(irDestinationOperand.register, part), None, None, 0);
+				MovIfDifferent(mappedArmRegister, register)
+			ELSE
+				HALT(100)
+			END;
+
+			ReleaseHint(register.register)
+		END WriteBack;
+
+		PROCEDURE ZeroExtendOperand(operand: Operand; sizeInBits: LONGINT);
+		BEGIN
+			ASSERT(sizeInBits <= 32);
+			IF operand.mode = InstructionSet.modeRegister THEN
+				IF sizeInBits = 8 THEN
+					Emit3(opAND, operand, operand, InstructionSet.NewImmediate(255)); (* AND reg, reg, 11111111b *)
+				ELSIF sizeInBits = 16 THEN
+					Emit2(opMOV, operand, InstructionSet.NewRegister(operand.register, InstructionSet.shiftLSL, None, 16));
+					Emit2(opMOV, operand, InstructionSet.NewRegister(operand.register, InstructionSet.shiftLSR, None, 16))
+				ELSIF sizeInBits = 32 THEN
+					(* nothing to do *)
+				ELSE
+					HALT(100)
+				END
+			END
+		END ZeroExtendOperand;
+
+		PROCEDURE SignExtendOperand(operand: Operand; sizeInBits: LONGINT);
+		BEGIN
+			ASSERT(sizeInBits <= 32);
+			IF operand.mode = InstructionSet.modeRegister THEN
+				IF sizeInBits < 32 THEN
+					Emit2(opMOV, operand, InstructionSet.NewRegister(operand.register, InstructionSet.shiftLSL, None, 32 - sizeInBits));
+					Emit2(opMOV, operand, InstructionSet.NewRegister(operand.register, InstructionSet.shiftASR, None, 32 - sizeInBits))
+				END
+			END
+		END SignExtendOperand;
+
+		(** sign or zero-extends the content of an operand to 32 bits, depending on the IR type **)
+		PROCEDURE SignOrZeroExtendOperand(operand: Operand; irType: IntermediateCode.Type);
+		BEGIN
+			ASSERT(irType.sizeInBits <= 32);
+			IF irType.form = IntermediateCode.UnsignedInteger THEN
+				ZeroExtendOperand(operand, irType.sizeInBits)
+			ELSE
+				SignExtendOperand(operand, irType.sizeInBits)
+			END
+		END SignOrZeroExtendOperand;
+
+		(* ACTUAL CODE GENERATION *)
+
+		PROCEDURE EmitPush(VAR irOperand: IntermediateCode.Operand; part: LONGINT);
+		VAR
+			register: Operand;
+			partType: IntermediateCode.Type;
+			(*pc: LONGINT;*)
+		BEGIN
+			register := RegisterFromIrOperand(irOperand, part, emptyOperand);
+			IF ~IsRegisterForType(register.register, IntermediateCode.FloatType(32)) THEN
+				Emit2(opSTR, register, InstructionSet.NewImmediateOffsetMemory(InstructionSet.SP, 4, {InstructionSet.Decrement, InstructionSet.PreIndexed}));
+			ELSE
+				partType := PartType(irOperand.type, part);
+				AllocateStack(MAX(4, partType.sizeInBits DIV 8), TRUE,FALSE);
+				Store(register, InstructionSet.NewImmediateOffsetMemory(InstructionSet.SP, 0, {InstructionSet.Increment}), PartType(irOperand.type, part));
+			END;
+
+			(*
+			(* optimization for push chains (THIS DOES NOT WORK IF inEmulation) *)
+			IF pushChainLength = 0 THEN
+				pc := inPC;
+				(* search for consecutive push instructions *)
+				WHILE (pc < in.pc) & (in.instructions[pc].opcode = IntermediateCode.push) DO
+					ASSERT(in.instructions[pc].op1.mode # IntermediateCode.Undefined);
+					INC(pushChainLength, MAX(4, in.instructions[pc].op1.type.sizeInBits DIV 8));
+					INC(pc)
+				END;
+				AllocateStack(pushChainLength, TRUE)
+			END;
+
+			DEC(pushChainLength, 4); (* for 64 bit operands, this procedure is executed twice -> the push chain will be decremented by 8 bytes *)
+			register := RegisterFromIrOperand(irOperand, part, emptyOperand);
+			ASSERT(pushChainLength < InstructionSet.Bits12, 100);
+			ASSERT((pushChainLength MOD 4) = 0);
+			Store(register, InstructionSet.NewImmediateOffsetMemory(InstructionSet.SP, pushChainLength, {InstructionSet.Increment}), PartType(irOperand.type, part))
+			*)
+
+		END EmitPush;
+
+		PROCEDURE EmitPop(VAR irOperand: IntermediateCode.Operand; part: LONGINT);
+		VAR
+			register: Operand; partType: IntermediateCode.Type;
+		BEGIN
+			register := AcquireDestinationRegister(irOperand, part, emptyOperand);
+			IF ~IsRegisterForType(register.register, IntermediateCode.FloatType(32)) THEN
+				(*Emit2(opLDR, register, InstructionSet.NewImmediateOffsetMemory(InstructionSet.SP, 4, {InstructionSet.Increment, InstructionSet.PostIndexed}));*)
+				Load(register, InstructionSet.NewImmediateOffsetMemory(InstructionSet.SP, 4, {InstructionSet.Increment, InstructionSet.PostIndexed}), PartType(irOperand.type, part));
+
+			ELSE
+				Load(register, InstructionSet.NewImmediateOffsetMemory(InstructionSet.SP, 0, {InstructionSet.Increment}), PartType(irOperand.type, part));
+				partType := PartType(irOperand.type, part);
+				AllocateStack(-MAX(4, partType.sizeInBits DIV 8), TRUE,FALSE);
+			END;
+			WriteBack(irOperand, part, register)
+		END EmitPop;
+
+		PROCEDURE Resolve(VAR op: IntermediateCode.Operand);
+		BEGIN
+			IF (op.symbol.name # "") & (op.resolved = NIL) THEN op.resolved := module.allSections.FindByName(op.symbol.name) END
+		END Resolve;
+
+
+		(* call <address>, <parSize> *)
+		PROCEDURE EmitCall(VAR irInstruction: IntermediateCode.Instruction);
+		VAR
+			code: BinaryCode.Section;
+			fixup, newFixup: BinaryCode.Fixup;
+		BEGIN
+			Resolve(irInstruction.op1);
+			IF (irInstruction.op1.resolved # NIL) & (irInstruction.op1.resolved.type = Sections.InlineCodeSection) THEN
+				(* call of an inline procedure: *)
+
+				code := irInstruction.op1.resolved(IntermediateCode.Section).resolved;
+				ASSERT(code # NIL); (* TODO: what if section is not yet resolved, i.e., code has not yet been generated? *)
+
+				IF (out.comments # NIL) THEN
+					out.comments.String("inlined code sequence:");
+					out.comments.Ln;
+					out.comments.Update;
+				END;
+
+				(* emit the generated code of the other section *)
+				out.CopyBits(code.os.bits, 0, code.os.bits.GetSize());
+
+				(* transfer the fixups *)
+				fixup := code.fixupList.firstFixup;
+				WHILE fixup # NIL DO
+					newFixup := BinaryCode.NewFixup(fixup.mode, fixup.offset + code.pc, fixup.symbol, fixup.symbolOffset, fixup.displacement, fixup.scale, fixup.pattern);
+					out.fixupList.AddFixup(newFixup);
+					fixup := fixup.nextFixup
+				END
+			ELSE
+				(* store the address of the procedure in a register and branch and link there *)
+				Emit1(opBLX, RegisterFromIrOperand(irInstruction.op1, Low, emptyOperand));
+
+				(* remove parameters on stack *)
+				AllocateStack(-LONGINT(irInstruction.op2.intValue), TRUE, FALSE)
+			END
+		END EmitCall;
+
+		(* enter <callingConvention>, <pafSize>, <numRegParams> *)
+		PROCEDURE EmitEnter(CONST irInstruction: IntermediateCode.Instruction);
+		VAR allocationSize: LONGINT;
+		BEGIN
+			(* STMFD (Full Descending) aka STMDB (Decrement Before) *)
+
+			IF (irInstruction.op1.intValue = SyntaxTree.InterruptCallingConvention) THEN (* TODO: needed? *)
+				(* push R0-R11, FP and LR *)
+				Emit2WithFlags(opSTM, opSP, InstructionSet.NewRegisterList(0, {0..11, InstructionSet.FP, InstructionSet.LR}), {InstructionSet.flagDB, InstructionSet.flagBaseRegisterUpdate});
+				stackSize := 14*4;
+			ELSE
+				(* push FP and LR *)
+				Emit2WithFlags(opSTM, opSP, InstructionSet.NewRegisterList(0, {InstructionSet.FP, InstructionSet.LR}), {InstructionSet.flagDB, InstructionSet.flagBaseRegisterUpdate});
+				stackSize := 2*4;
+
+				(* altenative:
+				AllocateStack(2 * 4, TRUE);
+				Emit2(opSTR, opFP, InstructionSet.NewImmediateOffsetMemory(InstructionSet.SP, 4, {InstructionSet.Increment}));
+				Emit2(opSTR, opLR, InstructionSet.NewImmediateOffsetMemory(InstructionSet.SP, 0, {InstructionSet.Increment}))
+				*)
+			END;
+
+			Emit2(opMOV, opFP, opSP);
+
+			allocationSize := LONGINT(irInstruction.op2.intValue);
+			Basic.Align(allocationSize, 4); (* 4 byte alignment *)
+			(* allocate space on stack for local variables *)
+			AllocateStack(allocationSize, TRUE, backend.initLocals);
+			
+			
+
+			(* allocate space on stack for register spills *)
+			spillStackStart := stackSize; IF spillStack.MaxSize() > 0 THEN AllocateStack(spillStack.MaxSize(), TRUE, FALSE) END
+		END EmitEnter;
+
+		(* leave <callingConvention> *)
+		PROCEDURE EmitLeave(CONST irInstruction: IntermediateCode.Instruction);
+		BEGIN
+			Emit2(opMOV, opSP, opFP);
+
+			(* LDMFD (Full Descending) aka LDMIA (Increment After) *)
+			IF (irInstruction.op1.intValue = SyntaxTree.InterruptCallingConvention) THEN
+				(* pop R0-R11, FP and LR *)
+				Emit2WithFlags(opLDM, opSP, InstructionSet.NewRegisterList(0, {0..11, InstructionSet.FP, InstructionSet.LR}), {InstructionSet.flagIA, InstructionSet.flagBaseRegisterUpdate})
+			ELSE
+				(* pop FP and LR *)
+				Emit2WithFlags(opLDM, opSP, InstructionSet.NewRegisterList(0, {InstructionSet.FP, InstructionSet.LR}), {InstructionSet.flagIA, InstructionSet.flagBaseRegisterUpdate})
+
+				(* alternative:
+				Emit2(opLDR, opFP, InstructionSet.NewImmediateOffsetMemory(InstructionSet.SP, 4, {InstructionSet.Increment}));
+				Emit2(opLDR, opLR, InstructionSet.NewImmediateOffsetMemory(InstructionSet.SP, 0, {InstructionSet.Increment}));
+				AllocateStack(-2 * 4, TRUE)
+				*)
+			END
+		END EmitLeave;
+
+		(* exit <parSize>, <pcOffset> *)
+		PROCEDURE EmitExit(CONST irInstruction: IntermediateCode.Instruction);
+		BEGIN
+			IF (irInstruction.op1.intValue = 0) & (irInstruction.op2.intValue # SyntaxTree.InterruptCallingConvention) THEN
+				(* Emit2(opMOV, opPC, opLR) *)
+				Emit1(opBX, opLR) (* recommended for better interoperability between ARM and Thumb *)
+			ELSE
+				IF (irInstruction.op2.intValue = SyntaxTree.InterruptCallingConvention) THEN
+					Emit3WithFlags(opSUB, opPC, opLR, InstructionSet.NewImmediate(LONGINT(irInstruction.op1.intValue)),{InstructionSet.flagS})
+				ELSE
+				(* exit from an ARM interrupt procedure that has a PC offset *)
+					Emit3(opSUB, opPC, opLR, InstructionSet.NewImmediate(LONGINT(irInstruction.op1.intValue)))
+				END;
+			END
+		END EmitExit;
+
+		PROCEDURE EmitMov(VAR irInstruction: IntermediateCode.Instruction; part: LONGINT);
+		VAR
+			destinationRegister, sourceOperand: Operand;
+		BEGIN
+			IF irInstruction.op1.mode # IntermediateCode.ModeRegister THEN
+				(* optimization: mov [?], r? it is more optimal to determine the source operand first *)
+				sourceOperand := RegisterOrImmediateFromIrOperand(irInstruction.op2, part, emptyOperand);
+
+				destinationRegister := GetFreeRegisterOrHint(PartType(irInstruction.op2.type, part), sourceOperand) (* note that the source operand (possibly a register) is used as hint *)
+			ELSE
+				PrepareSingleSourceOpWithImmediate(irInstruction, part, destinationRegister, sourceOperand);
+			END;
+
+			MovIfDifferent(destinationRegister, sourceOperand);
+			WriteBack(irInstruction.op1, part, destinationRegister)
+		END EmitMov;
+
+		(* BITWISE LOGICAL OPERATIONS *)
+
+		PROCEDURE EmitNot(VAR irInstruction: IntermediateCode.Instruction; part: LONGINT);
+		VAR
+			destination, source: Operand;
+		BEGIN
+			PrepareSingleSourceOpWithImmediate(irInstruction, part, destination, source);
+			Emit2(opMVN, destination, source); (* invert bits *)
+			WriteBack(irInstruction.op1, part, destination)
+		END EmitNot;
+
+		PROCEDURE EmitAnd(VAR irInstruction: IntermediateCode.Instruction; part: LONGINT);
+		VAR
+			dummy: BOOLEAN;
+			destination, left, right: Operand;
+		BEGIN
+			PrepareDoubleSourceOpWithImmediate(irInstruction, part, destination, left, right, dummy);
+			Emit3(opAND, destination, left, right);
+			WriteBack(irInstruction.op1, part, destination)
+		END EmitAnd;
+
+		PROCEDURE EmitOr(VAR irInstruction: IntermediateCode.Instruction; part: LONGINT);
+		VAR
+			dummy: BOOLEAN;
+			destination, left, right: Operand;
+		BEGIN
+			PrepareDoubleSourceOpWithImmediate(irInstruction, part, destination, left, right, dummy);
+			Emit3(opORR, destination, left, right);
+			WriteBack(irInstruction.op1, part, destination)
+		END EmitOr;
+
+		PROCEDURE EmitXor(VAR irInstruction: IntermediateCode.Instruction; part: LONGINT);
+		VAR
+			dummy: BOOLEAN;
+			destination, left, right: Operand;
+		BEGIN
+			PrepareDoubleSourceOpWithImmediate(irInstruction, part, destination, left, right, dummy);
+			Emit3(opEOR, destination, left, right);
+			WriteBack(irInstruction.op1, part, destination)
+		END EmitXor;
+
+		(* ARITHMETIC OPERATIONS *)
+
+		(*
+			- TODO: double precision floats
+			- note that for operand sizes 8 and 16, the unused bits of the result might be in a unpredictable state (sign/zero-extension is not done on purpose)
+		*)
+		PROCEDURE EmitAddOrSub(VAR irInstruction: IntermediateCode.Instruction);
+		VAR
+			destination, left, right: Operand;
+			(* registerSR0, registerSR1, registerSR2: Operand; *)
+		BEGIN
+			IF IsSinglePrecisionFloat(irInstruction.op1) THEN
+				ASSERT(backend.useFPU);
+				PrepareDoubleSourceOp(irInstruction, Low, destination, left, right);
+				IF irInstruction.opcode = IntermediateCode.add THEN
+					Emit3(opFADDS, destination, left, right)
+				ELSE
+					Emit3(opFSUBS, destination, left, right)
+				END;
+				WriteBack(irInstruction.op1, Low, destination)
+
+			ELSIF IsInteger(irInstruction.op1) THEN
+				IF IsComplex(irInstruction.op1) THEN
+					EmitPartialAddOrSub(irInstruction, Low, TRUE);
+					EmitPartialAddOrSub(irInstruction, High, FALSE)
+				ELSE
+					EmitPartialAddOrSub(irInstruction, Low, FALSE)
+				END
+
+			ELSE
+				HALT(200)
+			END
+		END EmitAddOrSub;
+
+		PROCEDURE EmitPartialAddOrSub(CONST irInstruction: IntermediateCode.Instruction; part: LONGINT; doUpdateFlags: BOOLEAN);
+		VAR
+			destination, left, right, hint: Operand;
+			irDestination, irLeft, irRight: IntermediateCode.Operand;
+			operation: LONGINT;
+			doSwap, doNegateRight: BOOLEAN;
+		BEGIN
+			irDestination := irInstruction.op1; irLeft := irInstruction.op2; irRight := irInstruction.op3;
+
+			doSwap := FALSE; doNegateRight := FALSE; (* defaults *)
+
+			IF irInstruction.opcode = IntermediateCode.add THEN
+				IF IrOperandIsDirectlyEncodable(irRight, part) THEN
+					(* add r0, r1, 16 ~> ADD R0, R1, #16 *)
+					operation := opADD
+				ELSIF IrOperandIsDirectlyEncodable(irLeft, part) THEN
+					(* add r0, 16, r1 ~> ADD R0, R1, #16 *)
+					operation := opADD; doSwap := TRUE
+				ELSIF NegatedIrOperandIsDirectlyEncodable(irRight, part) THEN
+					(* add r0, r1, -16 ~> SUB R0, R1, #16 *)
+					operation := opSUB; doNegateRight := TRUE
+				ELSIF NegatedIrOperandIsDirectlyEncodable(irLeft, part) THEN
+					(* add r0, -16, r1 ~> SUB R0, R1, #16 *)
+					operation := opSUB; doSwap := TRUE; doNegateRight := TRUE
+				ELSE
+					operation := opADD
+				END
+
+			ELSIF irInstruction.opcode = IntermediateCode.sub THEN
+				IF IrOperandIsDirectlyEncodable(irRight, part) THEN
+					(* sub r0, r1, 16 ~> SUB R0, R1, #16 *)
+					operation := opSUB
+				ELSIF IrOperandIsDirectlyEncodable(irLeft, part) THEN
+					(* sub r0, 16, r1 ~> RSB R0, R1, #16 *)
+					operation := opRSB; doSwap := TRUE
+				ELSIF NegatedIrOperandIsDirectlyEncodable(irRight, part) THEN
+					(* sub r0, r1, -16 ~> ADD R0, R1, #16 *)
+					operation := opADD; doNegateRight := TRUE
+				ELSE
+					operation := opSUB
+				END
+
+			ELSE
+				HALT(100)
+			END;
+
+			(* get destination operand *)
+			destination := AcquireDestinationRegister(irDestination, part, emptyOperand);
+
+			(* get source operands *)
+			IF doSwap THEN SwapIrOperands(irLeft, irRight) END; (* if needed, swap operands *)
+
+			(* TODO: revise this! *)
+			IF IsSameRegister(right, destination) THEN hint := destination ELSE hint := emptyOperand END;
+			left := RegisterFromIrOperand(irLeft, part, hint);
+
+			IF doNegateRight THEN
+				ASSERT(NegatedIrOperandIsDirectlyEncodable(irRight, part));
+				right := InstructionSet.NewImmediate(-ValueOfPart(irRight.intValue, part))
+			ELSE
+				right := RegisterOrImmediateFromIrOperand(irRight, part, emptyOperand)
+			END;
+
+			(* if needed, use operation that incorporates carry *)
+			IF part # Low THEN
+				CASE operation OF
+				| opADD: operation := opADC
+				| opSUB: operation := opSBC
+				| opRSB: operation := opRSC
+				ELSE HALT(100)
+				END
+			END;
+
+			IF doUpdateFlags THEN
+				Emit3WithFlags(operation, destination, left, right, {InstructionSet.flagS})
+			ELSE
+				Emit3(operation, destination, left, right)
+			END;
+
+			WriteBack(irDestination, part, destination)
+		END EmitPartialAddOrSub;
+
+		PROCEDURE EmitMul(VAR irInstruction: IntermediateCode.Instruction);
+		VAR
+			destination, left, right: ARRAY 2 OF Operand;
+		BEGIN
+			IF IsSinglePrecisionFloat(irInstruction.op1) THEN
+				ASSERT(backend.useFPU);
+				PrepareDoubleSourceOp(irInstruction, Low, destination[Low], left[Low], right[Low]);
+				Emit3(opFMULS, destination[Low], left[Low], right[Low]);
+				WriteBack(irInstruction.op1, Low, destination[Low])
+
+			ELSIF IsInteger(irInstruction.op1) THEN
+				IF IsComplex(irInstruction.op1) THEN
+					ASSERT(irInstruction.op1.type.form = IntermediateCode.SignedInteger);
+					HALT(200);
+					(* TODO: fix signed 64 bit integer multiplication:
+					PrepareDoubleSourceOp(irInstruction, Low, destination[Low], left[Low], right[Low]);
+					PrepareDoubleSourceOp(irInstruction, High, destination[High], left[High], right[High]);
+
+					Emit4(opSMULL, destination[Low], destination[High], left[Low], right[Low]); (* signed long multiplication *)
+					Emit3(opMLA, destination[High], left[Low], right[High]); (* multiply and accumulate *)
+					Emit3(opMLA, destination[High], left[High], right[Low]);
+
+					WriteBack(irInstruction.op1, Low, destination[Low]);
+					WriteBack(irInstruction.op1, High, destination[High]);
+					*)
+				ELSE
+					(* signed or unsigned integer multiplication: *)
+					PrepareDoubleSourceOp(irInstruction, Low, destination[Low], left[Low], right[Low]);
+					SignOrZeroExtendOperand(left[Low], irInstruction.op2.type);
+					SignOrZeroExtendOperand(right[Low], irInstruction.op3.type);
+					Emit3(opMUL, destination[Low], left[Low], right[Low]); (* note that the sign does not matter for the least 32 significant bits *)
+					WriteBack(irInstruction.op1, Low, destination[Low])
+				END
+			ELSE
+				HALT(200)
+			END
+		END EmitMul;
+
+		PROCEDURE EmitDiv(VAR irInstruction: IntermediateCode.Instruction);
+		VAR
+			destination, left, right: Operand;
+		BEGIN
+			IF IsSinglePrecisionFloat(irInstruction.op1) THEN
+				ASSERT(backend.useFPU);
+				PrepareDoubleSourceOp(irInstruction, Low, destination, left, right);
+				Emit3(opFDIVS, destination, left, right);
+				WriteBack(irInstruction.op1, Low, destination)
+			ELSE
+				HALT(200)
+			END
+		END EmitDiv;
+
+		PROCEDURE EmitMod(CONST irInstruction: IntermediateCode.Instruction);
+		BEGIN HALT(100) (* handled by a runtime call *)
+		END EmitMod;
+
+		PROCEDURE EmitAbs(VAR irInstruction: IntermediateCode.Instruction);
+		VAR
+			destination, source: ARRAY 2 OF Operand;
+			zero: Operand;
+		BEGIN
+			IF IsInteger(irInstruction.op1) THEN
+				zero := InstructionSet.NewImmediate(0);
+				IF IsComplex(irInstruction.op1) THEN
+					PrepareSingleSourceOpWithImmediate(irInstruction, Low, destination[Low], source[Low]);
+					PrepareSingleSourceOpWithImmediate(irInstruction, High, destination[High], source[High]);
+					MovIfDifferent(destination[Low], source[Low]);
+					MovIfDifferent(destination[High], source[High]);
+					(* negate the value if it is negative *)
+					IF irInstruction.op2.type.form = IntermediateCode.SignedInteger THEN
+						Emit2(opCMP, destination[High], zero); (* note that only the high part has to be looked at to determine the sign *)
+						Emit1WithCondition(opB, InstructionSet.NewImmediate(4), InstructionSet.conditionGE); (* BGE #4 = skip the following two instructions if greater or equal *)
+						Emit3WithFlags(opRSB, destination[Low], destination[Low], zero, {InstructionSet.flagS}); (* RSBS *)
+						Emit3(opRSC, destination[High], destination[High], zero); (* RSC - reverse subtraction with carry *)
+					END;
+					WriteBack(irInstruction.op1, Low, destination[Low]);
+					WriteBack(irInstruction.op1, High, destination[High])
+				ELSE
+					PrepareSingleSourceOpWithImmediate(irInstruction, Low, destination[Low], source[Low]);
+					SignOrZeroExtendOperand(source[Low], irInstruction.op2.type);
+					MovIfDifferent(destination[Low], source[Low]);
+					(* negate the value if it is negative *)
+					IF irInstruction.op2.type.form = IntermediateCode.SignedInteger THEN
+						SignExtendOperand(destination[Low], irInstruction.op2.type.sizeInBits);
+						Emit2(opCMP, destination[Low], zero);
+						Emit3WithCondition(opRSB, destination[Low], destination[Low], zero, InstructionSet.conditionLT)
+					END;
+					WriteBack(irInstruction.op1, Low, destination[Low])
+				END
+			ELSIF IsSinglePrecisionFloat(irInstruction.op1) THEN
+				ASSERT(backend.useFPU);
+				PrepareSingleSourceOp(irInstruction, Low, destination[Low], source[Low]);
+				Emit2(opFABSS, destination[Low], source[Low]);
+				WriteBack(irInstruction.op1, Low, destination[Low])
+			ELSE
+				HALT(200)
+			END
+		END EmitAbs;
+
+		(* TODO: floats *)
+		PROCEDURE EmitNeg(VAR irInstruction: IntermediateCode.Instruction);
+		VAR
+			destination, source: ARRAY 2 OF Operand;
+			zero: Operand;
+		BEGIN
+			IF IsInteger(irInstruction.op1) THEN
+				zero := InstructionSet.NewImmediate(0);
+				IF IsComplex(irInstruction.op1) THEN
+					PrepareSingleSourceOpWithImmediate(irInstruction, Low, destination[Low], source[Low]);
+					PrepareSingleSourceOpWithImmediate(irInstruction, High, destination[High], source[High]);
+					Emit3WithFlags(opRSB, destination[Low], source[Low], zero, {InstructionSet.flagS}); (* RSBS *)
+					Emit3(opRSC, destination[High], source[High], zero); (* RSC - reverse subtraction with carry *)
+					WriteBack(irInstruction.op1, Low, destination[Low]);
+					WriteBack(irInstruction.op1, High, destination[High])
+				ELSE
+					PrepareSingleSourceOpWithImmediate(irInstruction, Low, destination[Low], source[Low]);
+					SignOrZeroExtendOperand(source[Low], irInstruction.op2.type);
+					Emit3(opRSB, destination[Low], source[Low], zero); (* reverse subtraction with zero *)
+					WriteBack(irInstruction.op1, Low, destination[Low])
+				END
+			ELSIF IsSinglePrecisionFloat(irInstruction.op1) THEN
+				ASSERT(backend.useFPU);
+				PrepareSingleSourceOp(irInstruction, Low, destination[Low], source[Low]);
+				Emit2(opFNEGS, destination[Low], source[Low]);
+				WriteBack(irInstruction.op1, Low, destination[Low])
+			ELSE
+				HALT(200)
+			END
+		END EmitNeg;
+
+		(*
+		- note that the ARM instructions ASR, LSL, LSR, ROR, etc. are actually aliases for a MOV with a shifted register operand
+		- note that ARM does not support LSL by 32 bits
+		- note that for operand sizes 8 and 16, the unused bits of the result might be in a unpredictable state (sign/zero-extension is not done on purpose)
+		*)
+		PROCEDURE EmitShiftOrRotation(VAR irInstruction: IntermediateCode.Instruction);
+		VAR
+			shiftAmountImmediate, shiftMode: LONGINT;
+			destination, source: ARRAY 2 OF Operand;
+			irShiftOperand: IntermediateCode.Operand;
+			temp, shiftAmountRegister: Operand;
+		BEGIN
+			ASSERT(IsInteger(irInstruction.op1), 100); (* shifts are only allowed on integers *)
+
+			destination[Low] := AcquireDestinationRegister(irInstruction.op1, Low, emptyOperand);
+			source[Low] := RegisterFromIrOperand(irInstruction.op2, Low, emptyOperand); (* note that the destination register cannot be used as hint for the source *)
+			IF IsComplex(irInstruction.op1) THEN
+				destination[High] := AcquireDestinationRegister(irInstruction.op1, High, emptyOperand);
+				source[High] := RegisterFromIrOperand(irInstruction.op2, High, emptyOperand); (* note that the destination register cannot be used as hint for the source *)
+			END;
+
+			irShiftOperand := irInstruction.op3;
+
+			ASSERT((irShiftOperand.type.form = IntermediateCode.UnsignedInteger) & ~IsComplex(irShiftOperand)); (* the shift operand is assumed to be a single part unsigned integer *)
+
+			(* use ARM register or shift immediate to represent IR shift operand *)
+			IF (irShiftOperand.mode = IntermediateCode.ModeImmediate) & (irShiftOperand.symbol.name = "") THEN
+				shiftAmountImmediate := LONGINT(irShiftOperand.intValue); (* note that at this point the shift amount could also be >= 32 *)
+				shiftAmountRegister := emptyOperand;
+				ASSERT(shiftAmountImmediate >= 0);
+			ELSE
+				shiftAmountImmediate := 0;
+				shiftAmountRegister := RegisterFromIrOperand(irShiftOperand, Low, emptyOperand);
+				ZeroExtendOperand(shiftAmountRegister, irShiftOperand.type.sizeInBits)
+			END;
+
+			CASE irInstruction.opcode OF
+			| IntermediateCode.ror, IntermediateCode.rol:
+				(* rotation: *)
+				IF IsComplex(irInstruction.op1) THEN HALT(100) END; (* complex rotations are handled as runtime calls *)
+
+				IF irInstruction.opcode = IntermediateCode.rol THEN
+					(* simple left rotation: rotate right with complementary rotation amount, since ARM does not support left rotations *)
+					IF shiftAmountRegister.register = None THEN
+						shiftAmountImmediate := 32 - shiftAmountImmediate
+					ELSE
+						IF IsSameRegister(destination[Low], source[Low]) THEN temp := GetFreeRegister(IntermediateCode.UnsignedIntegerType(32)) ELSE temp := destination[Low] END;
+						Emit3(opRSB, temp, shiftAmountRegister, InstructionSet.NewImmediate(32));
+						shiftAmountRegister := temp
+					END
+				END;
+
+				shiftAmountImmediate := shiftAmountImmediate MOD 32; (* make sure rotation amount is in range 0..31 *)
+
+				IF (shiftAmountRegister.register = None) & (shiftAmountImmediate = 0) THEN
+					(* simple rotation by 0: *)
+					Emit2(opMOV, destination[Low], source[Low])
+				ELSE
+					IF irInstruction.op1.type.sizeInBits = 8 THEN
+						(* simple 8 bit rotation: *)
+						ZeroExtendOperand(source[Low], 8);
+						IF IsSameRegister(destination[Low], source[Low]) THEN temp := GetFreeRegister(IntermediateCode.UnsignedIntegerType(32)) ELSE temp := destination[Low] END;
+						Emit2(opMOV, temp, InstructionSet.NewRegister(source[Low].register, InstructionSet.shiftROR, shiftAmountRegister.register, shiftAmountImmediate));
+						Emit3(opORR, temp, temp, InstructionSet.NewRegister(temp.register, InstructionSet.shiftLSR, None, 8));
+						Emit3(opORR, temp, temp, InstructionSet.NewRegister(temp.register, InstructionSet.shiftLSR, None, 16));
+						Emit3(opORR, destination[Low], temp, InstructionSet.NewRegister(temp.register, InstructionSet.shiftLSR, None, 24))
+					ELSIF irInstruction.op1.type.sizeInBits = 16 THEN
+						(* simple 16 bit rotation: *)
+						ZeroExtendOperand(source[Low], 16);
+						IF IsSameRegister(destination[Low], source[Low]) THEN temp := GetFreeRegister(IntermediateCode.UnsignedIntegerType(32)) ELSE temp := destination[Low] END;
+						Emit2(opMOV, temp, InstructionSet.NewRegister(source[Low].register, InstructionSet.shiftROR, shiftAmountRegister.register, shiftAmountImmediate));
+						Emit3(opORR, destination[Low], temp, InstructionSet.NewRegister(temp.register, InstructionSet.shiftLSR, None, 16))
+					ELSIF irInstruction.op1.type.sizeInBits = 32 THEN
+						(* simple 32 bit rotation: *)
+						Emit2(opMOV, destination[Low], InstructionSet.NewRegister(source[Low].register, InstructionSet.shiftROR, shiftAmountRegister.register, shiftAmountImmediate))
+					ELSE
+						HALT(100)
+					END
+				END
+
+			| IntermediateCode.shl:
+				(* left shift: *)
+				IF IsComplex(irInstruction.op1) THEN
+					(* complex left shift: *)
+					IF shiftAmountRegister.register = None THEN
+						(* complex left immediate shift: *)
+						IF shiftAmountImmediate = 0 THEN
+							Emit2(opMOV, destination[High], source[High]);
+							Emit2(opMOV, destination[Low], source[Low])
+						ELSIF (shiftAmountImmediate > 0) & (shiftAmountImmediate < 32) THEN
+							IF ~IsSameRegister(destination[High], source[High]) THEN temp := destination[High] ELSE temp := GetFreeRegister(IntermediateCode.UnsignedIntegerType(32)) END;
+							Emit2(opMOV, temp, InstructionSet.NewRegister(source[Low].register, InstructionSet.shiftLSR, None, 32 - shiftAmountImmediate));
+							Emit3(opORR, destination[High], temp, InstructionSet.NewRegister(source[High].register, InstructionSet.shiftLSL, None, shiftAmountImmediate));
+							Emit2(opMOV, destination[Low], InstructionSet.NewRegister(source[Low].register, InstructionSet.shiftLSL, None, shiftAmountImmediate))
+						ELSIF (shiftAmountImmediate >= 32) & (shiftAmountImmediate < 64) THEN
+							Emit2(opMOV, destination[High], InstructionSet.NewRegister(source[Low].register, InstructionSet.shiftLSL, None, shiftAmountImmediate - 32));
+							Emit2(opMOV, destination[Low], InstructionSet.NewImmediate(0))
+						ELSIF shiftAmountImmediate >= 64 THEN
+							Emit2(opMOV, destination[High], InstructionSet.NewImmediate(0));
+							Emit2(opMOV, destination[Low], InstructionSet.NewImmediate(0))
+						ELSE
+							HALT(100)
+						END
+					ELSE
+						(* complex left register shift: *)
+						IF ~IsSameRegister(destination[Low], source[Low]) THEN temp := destination[Low] ELSE temp := GetFreeRegister(IntermediateCode.UnsignedIntegerType(32)) END;
+						Emit2(opCMP, shiftAmountRegister, InstructionSet.NewImmediate(32));
+						(* shiftAmount < 32: *)
+						Emit3WithCondition(opRSB, temp, shiftAmountRegister, InstructionSet.NewImmediate(32), InstructionSet.conditionLT);
+						Emit2WithCondition(opMOV, temp, InstructionSet.NewRegister(source[Low].register, InstructionSet.shiftLSR, temp.register, 0), InstructionSet.conditionLT);
+						Emit3WithCondition(opORR, destination[High], temp, InstructionSet.NewRegister(source[High].register, InstructionSet.shiftLSL, shiftAmountRegister.register, 0), InstructionSet.conditionLT);
+						Emit2WithCondition(opMOV, destination[Low], InstructionSet.NewRegister(source[Low].register, InstructionSet.shiftLSL, shiftAmountRegister.register, 0), InstructionSet.conditionLT);
+						(* shift amount >= 32: *)
+						Emit3WithCondition(opSUB, temp, shiftAmountRegister, InstructionSet.NewImmediate(32), InstructionSet.conditionGE);
+						Emit2WithCondition(opMOV, destination[High], InstructionSet.NewRegister(source[Low].register, InstructionSet.shiftLSL, temp.register, 0), InstructionSet.conditionGE);
+						Emit2WithCondition(opMOV, destination[Low], InstructionSet.NewImmediate(0), InstructionSet.conditionGE)
+					END
+				ELSE
+					(* simple left shift: *)
+					IF shiftAmountRegister.register = None THEN
+						(* simple left immediate shift *)
+						IF (shiftAmountImmediate >= 0) & (shiftAmountImmediate < 32) THEN
+							Emit2(opMOV, destination[Low], InstructionSet.NewRegister(source[Low].register, InstructionSet.shiftLSL, None, shiftAmountImmediate)) (* note: LSL has to be in the range 0..31 *)
+						ELSIF shiftAmountImmediate >= 32 THEN
+							Emit2(opMOV, destination[Low], InstructionSet.NewImmediate(0))
+						ELSE
+							HALT(100)
+						END
+					ELSE
+						(* simple left register shift: *)
+						Emit2(opMOV, destination[Low], InstructionSet.NewRegister(source[Low].register, InstructionSet.shiftLSL, shiftAmountRegister.register, 0))
+					END
+				END
+
+			| IntermediateCode.shr:
+				(* right shift: *)
+
+				(* determine shift mode (depends on if source operand is signed) *)
+				IF irInstruction.op1.type.form = IntermediateCode.UnsignedInteger THEN
+					(* logical right shift: *)
+					shiftMode := InstructionSet.shiftLSR
+				ELSE
+					(* arithmetic right shift: *)
+					shiftMode := InstructionSet.shiftASR
+				END;
+
+				IF IsComplex(irInstruction.op1) THEN
+					(* complex right shift: *)
+
+					IF shiftAmountRegister.register = None THEN
+						(* complex right immediate shift: *)
+						IF shiftAmountImmediate = 0 THEN
+							Emit2(opMOV, destination[High], source[High]);
+							Emit2(opMOV, destination[Low], source[Low])
+						ELSIF (shiftAmountImmediate > 0) & (shiftAmountImmediate < 32) THEN
+							IF ~IsSameRegister(destination[High], source[High]) THEN temp := destination[High] ELSE temp := GetFreeRegister(IntermediateCode.UnsignedIntegerType(32)) END;
+							Emit2(opMOV, temp, InstructionSet.NewRegister(source[High].register, InstructionSet.shiftLSL, None, 32 - shiftAmountImmediate));
+							Emit3(opORR, destination[Low], temp, InstructionSet.NewRegister(source[Low].register, InstructionSet.shiftLSR, None, shiftAmountImmediate));
+							Emit2(opMOV, destination[High], InstructionSet.NewRegister(source[High].register, shiftMode, None, shiftAmountImmediate))
+						ELSIF shiftAmountImmediate >= 32 THEN
+							IF shiftAmountImmediate > 64 THEN shiftAmountImmediate := 64 END;
+							Emit2(opMOV, destination[Low], InstructionSet.NewRegister(source[High].register, shiftMode, None, shiftAmountImmediate - 32));
+							Emit2(opMOV, destination[High], InstructionSet.NewRegister(source[High].register, shiftMode, None, 32))
+						ELSE
+							HALT(100)
+						END
+					ELSE
+						(* complex right register shift: *)
+						IF ~IsSameRegister(destination[High], source[High]) THEN temp := destination[High] ELSE temp := GetFreeRegister(IntermediateCode.UnsignedIntegerType(32)) END;
+						Emit2(opCMP, shiftAmountRegister, InstructionSet.NewImmediate(32));
+						(* shiftAmount < 32: *)
+						Emit3WithCondition(opRSB, temp, shiftAmountRegister, InstructionSet.NewImmediate(32), InstructionSet.conditionLT);
+						Emit2WithCondition(opMOV, temp, InstructionSet.NewRegister(source[High].register, InstructionSet.shiftLSL, temp.register, 0), InstructionSet.conditionLT);
+						Emit3WithCondition(opORR, destination[Low], temp, InstructionSet.NewRegister(source[High].register, InstructionSet.shiftLSR, shiftAmountRegister.register, 0), InstructionSet.conditionLT);
+						Emit2WithCondition(opMOV, destination[High], InstructionSet.NewRegister(source[High].register, shiftMode, shiftAmountRegister.register, 0), InstructionSet.conditionLT);
+						(* shift amount >= 32: *)
+						Emit3WithCondition(opSUB, temp, shiftAmountRegister, InstructionSet.NewImmediate(32), InstructionSet.conditionGE);
+						Emit2WithCondition(opMOV, destination[Low], InstructionSet.NewRegister(source[High].register, shiftMode, temp.register, 0), InstructionSet.conditionGE);
+						Emit2WithCondition(opMOV, destination[High], InstructionSet.NewRegister(source[High].register, shiftMode, shiftAmountRegister.register, 0), InstructionSet.conditionGE)
+					END
+				ELSE
+					(* simple right shift: *)
+					SignOrZeroExtendOperand(source[Low], irInstruction.op1.type);
+
+					IF shiftAmountRegister.register = None THEN
+						(* simple right immediate shift: *)
+						IF shiftAmountImmediate > 32 THEN shiftAmountImmediate := 32 END;
+						Emit2(opMOV, destination[Low], InstructionSet.NewRegister(source[Low].register, shiftMode, None, shiftAmountImmediate))
+					ELSE
+						(* simple right register shift: *)
+						Emit2(opMOV, destination[Low], InstructionSet.NewRegister(source[Low].register, shiftMode, shiftAmountRegister.register, 0))
+					END
+				END
+
+			ELSE
+				HALT(100)
+			END;
+
+			WriteBack(irInstruction.op1, Low, destination[Low]);
+			IF IsComplex(irInstruction.op1) THEN WriteBack(irInstruction.op1, High, destination[High]) END
+		END EmitShiftOrRotation;
+
+		PROCEDURE EmitAsm(CONST irInstruction: IntermediateCode.Instruction);
+		VAR
+			reader: Streams.StringReader;
+			procedure: SyntaxTree.Procedure;
+			scope: SyntaxTree.Scope;
+			symbol: SyntaxTree.Symbol;
+			assembler: Assembler.Assembler;
+			scanner: Scanner.AssemblerScanner;
+			len: LONGINT;
+		BEGIN
+			len := Strings.Length(irInstruction.op1.string^);
+			NEW(reader, len);
+			reader.Set(irInstruction.op1.string^);
+
+			(* determine scope of the section *)
+			symbol := in.symbol;
+			IF symbol = NIL THEN
+				scope := NIL
+			ELSE
+				procedure := symbol(SyntaxTree.Procedure);
+				scope := procedure.procedureScope
+			END;
+
+			NEW(assembler, diagnostics);
+			scanner := Scanner.NewAssemblerScanner(module.moduleName(*module.module.sourceName*), reader, LONGINT(irInstruction.op1.intValue) (* ? *), diagnostics);
+			assembler.InlineAssemble(scanner, in, scope, module);
+			error := error OR assembler.error
+		END EmitAsm;
+
+		PROCEDURE EmitSpecial(VAR instruction: IntermediateCode.Instruction);
+		VAR
+			psrNumber, code, a, b, c, d: LONGINT;
+			register, register2, register3, register4, temp, cpOperand, cpRegister1, cpRegister2, opCode1Operand, opCode2Operand: Operand;
+		BEGIN
+			CASE instruction.subtype OF
+			| GetSP: Emit2(opMOV, opRES, opSP)
+			| SetSP: Emit2(opMOV, opSP, RegisterOrImmediateFromIrOperand(instruction.op1, Low, emptyOperand))
+			| GetFP: Emit2(opMOV, opRES, opFP)
+			| SetFP: Emit2(opMOV, opFP, RegisterOrImmediateFromIrOperand(instruction.op1, Low, emptyOperand))
+			| GetLNK: Emit2(opMOV, opRES, opLR)
+			| SetLNK: Emit2(opMOV, opLR, RegisterOrImmediateFromIrOperand(instruction.op1, Low, emptyOperand))
+			| GetPC: Emit2(opMOV, opRES, opPC)
+			| SetPC: Emit2(opMOV, opPC, RegisterOrImmediateFromIrOperand(instruction.op1, Low, emptyOperand))
+			| LDPSR, STPSR:
+				ASSERT(instruction.op1.type.form IN IntermediateCode.Integer);
+				IF instruction.op1.mode # IntermediateCode.ModeImmediate THEN
+					Error(instruction.textPosition,"first operand must be immediate")
+				ELSIF (instruction.op1.intValue < 0) OR (instruction.op1.intValue > 1) THEN
+					Error(instruction.textPosition,"first operand must be 0 or 1")
+				ELSE
+					IF instruction.op1.intValue = 0 THEN
+						psrNumber := InstructionSet.CPSR
+					ELSE
+						psrNumber := InstructionSet.SPSR
+					END;
+					register := RegisterFromIrOperand(instruction.op2, Low, emptyOperand);
+					IF instruction.subtype = LDPSR THEN
+						Emit2(opMSR, InstructionSet.NewRegisterWithFields(psrNumber, {InstructionSet.fieldF, InstructionSet.fieldC}), register)
+					ELSE
+						temp := GetFreeRegister(IntermediateCode.UnsignedIntegerType(32));
+						Emit2(opMRS, temp, InstructionSet.NewRegister(psrNumber, None, None, 0));
+						Emit2(opSTR, temp, InstructionSet.NewImmediateOffsetMemory(register.register, 0, {InstructionSet.Increment}))
+					END
+				END
+
+			| LDCPR, STCPR:
+				IF instruction.op1.mode # IntermediateCode.ModeImmediate THEN
+					Error(instruction.textPosition,"first operand must be immediate")
+				ELSIF (instruction.op2.mode # IntermediateCode.ModeImmediate) THEN
+					Error(instruction.textPosition,"second operand must be immediate")
+				ELSIF (instruction.op2.intValue < 0) OR (instruction.op2.intValue > 15) THEN
+					Error(instruction.textPosition,"second operand must be between 0 or 15")
+				ELSE
+					code := LONGINT(instruction.op1.intValue); (* code = a00bcdH *)
+					a := (code DIV 100000H) MOD 10H; (* opcode1 * 2 *)
+					b := (code DIV 100H) MOD 10H; (* coprocessor number *)
+					c := (code DIV 10H) MOD 10H; (* opcode2 * 2 *)
+					d := code MOD 10H; (* coprocessor register2 number *)
+
+					InstructionSet.InitCoprocessor(cpOperand, InstructionSet.CP0 + b);
+					InstructionSet.InitOpcode(opCode1Operand, a DIV 2);
+					register := RegisterFromIrOperand(instruction.op3, Low, emptyOperand);
+					InstructionSet.InitRegister(cpRegister1, InstructionSet.CR0 + LONGINT(instruction.op2.intValue), None, None, 0);
+					InstructionSet.InitRegister(cpRegister2, InstructionSet.CR0 + d, None, None, 0);
+					InstructionSet.InitOpcode(opCode2Operand, c DIV 2);
+
+					IF instruction.subtype = LDCPR THEN
+						Emit6(opMCR, cpOperand, opCode1Operand, register, cpRegister1, cpRegister2, opCode2Operand)
+					ELSE
+						temp := GetFreeRegister(IntermediateCode.UnsignedIntegerType(32));
+						Emit6(opMRC, cpOperand, opCode1Operand, temp, cpRegister1, cpRegister2, opCode2Operand);
+						Emit2(opSTR, temp, InstructionSet.NewImmediateOffsetMemory(register.register, 0, {InstructionSet.Increment}))
+					END
+				END
+
+			| FLUSH:
+				IF instruction.op1.mode # IntermediateCode.ModeImmediate THEN
+					Error(instruction.textPosition,"first operand must be immediate")
+				ELSIF (instruction.op1.intValue < 0) OR (instruction.op2.intValue > 0FFH) THEN
+					Error(instruction.textPosition,"first operand must be between 0 and 255")
+				ELSE
+					code := LONGINT(instruction.op1.intValue); (* code = aaa1bbbbB *)
+					a := (code DIV 20H) MOD 8; (* coprocessor opcode 2 *)
+					b := (code MOD 10H); (* coprocessor register2 number  *)
+
+					(* examples:
+					9AH = 10011000B -> MCR p15, 0, R0, c7, c10, 4
+					17H = 00010111B -> MCR p15, 0, R0, c7, c7, 0
+					*)
+
+					InstructionSet.InitCoprocessor(cpOperand, InstructionSet.CP15);
+					InstructionSet.InitOpcode(opCode1Operand, 0);
+					InstructionSet.InitRegister(register, InstructionSet.R0, None, None, 0);
+					InstructionSet.InitRegister(cpRegister1, InstructionSet.CR7, None, None, 0);
+					InstructionSet.InitRegister(cpRegister2, InstructionSet.CR0 + b, None, None, 0);
+					InstructionSet.InitOpcode(opCode2Operand, a);
+
+					Emit6(opMCR, cpOperand, opCode1Operand, register, cpRegister1, cpRegister2, opCode2Operand);
+					Emit2(opMOV, register, register); (* NOP (register = R0) *)
+					Emit2(opMOV, register, register); (* NOP *)
+					Emit2(opMOV, register, register); (* NOP *)
+					Emit2(opMOV, register, register) (* NOP *)
+				END
+
+			| NULL:
+				register := RegisterFromIrOperand(instruction.op1, Low, emptyOperand);
+				Emit3(opBIC, register, register, InstructionSet.NewImmediate(LONGINT(80000000H)));
+				Emit2(opCMP, register, InstructionSet.NewImmediate(0));
+				Emit2WithCondition(opMOV, opRES, InstructionSet.NewImmediate(1), InstructionSet.conditionEQ);
+				Emit2WithCondition(opMOV, opRES, InstructionSet.NewImmediate(0), InstructionSet.conditionNE);
+
+			| XOR:
+				register := RegisterFromIrOperand(instruction.op1, Low, emptyOperand);
+				register2 := RegisterFromIrOperand(instruction.op2, Low, emptyOperand);
+				(*
+				register3 := RegisterFromIrOperand(instruction.op3, Low, emptyOperand);
+				*)
+				Emit3(opEOR, opRES, register, register2);
+
+			| MULD:
+				register := RegisterFromIrOperand(instruction.op1, Low, emptyOperand); (* note that 'register' contains an address *)
+				register2 := RegisterFromIrOperand(instruction.op2, Low, emptyOperand);
+				register3 := RegisterFromIrOperand(instruction.op3, Low, emptyOperand);
+				Emit4(opUMULL, opRES, opRESHI, register2, register3);
+				Emit2(opSTR, opRES, InstructionSet.NewImmediateOffsetMemory(register.register, 0, {InstructionSet.Increment})); (* JCH: 15.05.2012 *)
+				Emit2(opSTR, opRESHI, InstructionSet.NewImmediateOffsetMemory(register.register, 4, {InstructionSet.Increment}))
+
+			| ADDC:
+				register := RegisterFromIrOperand(instruction.op1, Low, emptyOperand);
+				register2 := RegisterFromIrOperand(instruction.op2, Low, emptyOperand);
+				Emit3(opADC, opRES, register, register2)
+
+			| PACK:
+				(* PACK(x, y):
+				add y to the binary exponent of y. PACK(x, y) is equivalent to  x := x * 2^y. *)
+				register := RegisterFromIrOperand(instruction.op1, Low, emptyOperand); (*  register = address of x *)
+				register2 := RegisterFromIrOperand(instruction.op2, Low, emptyOperand); (*  register2 = value of y *)
+
+				register3 := GetFreeRegister(IntermediateCode.UnsignedIntegerType(32)); (* a temporary INTEGER (!) register that is used to store a float *)
+				Emit2(opLDR, register3, InstructionSet.NewImmediateOffsetMemory(register.register, 0, {InstructionSet.Increment})); (* register3 = value of x *)
+				Emit3(opADD, register3, register3, InstructionSet.NewRegister(register2.register, InstructionSet.shiftLSL, None, 23)); (* increase the (biased) exponent of x by y*)
+				Emit2(opSTR, register3, InstructionSet.NewImmediateOffsetMemory(register.register, 0, {InstructionSet.Increment})) (* store new value of x *)
+
+			| UNPK:
+				(* UNPK(x, y):
+				remove the binary exponent on x and put it into y. UNPK is the reverse operation of PACK. The resulting x is normalized, i.e. 1.0 <= x < 2.0.
+				*)
+				register := RegisterFromIrOperand(instruction.op1, Low, emptyOperand); (*  register = address of x *)
+				register2 := RegisterFromIrOperand(instruction.op2, Low, emptyOperand); (*  register2 = address of y *)
+				register3 := GetFreeRegister(IntermediateCode.UnsignedIntegerType(32)); (* a temporary INTEGER (!) register that is used to store a float *)
+				Emit2(opLDR, register3, InstructionSet.NewImmediateOffsetMemory(register.register, 0, {InstructionSet.Increment})); (* register3 = value of x *)
+				register4 := GetFreeRegister(IntermediateCode.UnsignedIntegerType(32));
+				Emit2(opMOV, register4, InstructionSet.NewRegister(register3.register, InstructionSet.shiftLSR, None, 23)); (* register4 = biased exponent (and sign) of x *)
+				Emit3(opSUB, register4, register4, InstructionSet.NewImmediate(127)); (* register4 = exponent of x (biased exponent - 127) *)
+				Emit2(opSTR, register4, InstructionSet.NewImmediateOffsetMemory(register2.register, 0, {InstructionSet.Increment})); (* store exponent of x as value for y *)
+				Emit3(opSUB, register3, register3, InstructionSet.NewRegister(register4.register, InstructionSet.shiftLSL, None, 23)); (* reduce the biased exponent of x by the value of y *)
+				Emit2(opSTR, register3, InstructionSet.NewImmediateOffsetMemory(register.register, 0, {InstructionSet.Increment})) (*  store new value of x *)
+
+			ELSE
+				HALT(100)
+			END
+		END EmitSpecial;
+
+		PROCEDURE EmitBr(VAR irInstruction: IntermediateCode.Instruction);
+		VAR
+			branchDistance: LONGINT;
+			isSwapped: BOOLEAN;
+			left, right: ARRAY 2 OF Operand;
+			temp: Operand;
+			irLeft, irRight: IntermediateCode.Operand;
+			fixup,failFixup: BinaryCode.Fixup;
+			fixupPatternList: ObjectFile.FixupPatterns;
+			identifier: ObjectFile.Identifier;
+			hiHit, hiFail, lowHit, lowFail: LONGINT;
+
+			PROCEDURE JmpDest(branchConditionCode: LONGINT);
+			BEGIN
+				IF (irInstruction.op1.mode = IntermediateCode.ModeImmediate) & (irInstruction.op1.symbol.name = in.name) & (irInstruction.op1.offset = 0) THEN
+				(* branch within same section at a certain IR offset *)
+
+				(* optimization: abort if branch is to the next instruction *)
+				IF irInstruction.op1.symbolOffset = inPC + 1 THEN
+					IF dump # NIL THEN dump.String("branch to next instruction ignored"); dump.Ln END;
+					RETURN
+				END;
+
+				IF irInstruction.op1.symbolOffset <= inPC THEN
+					(* backward branch: calculate the branch distance *)
+					branchDistance := in.instructions[irInstruction.op1.symbolOffset].pc - out.pc - 8;
+					ASSERT((-33554432 <= branchDistance) & (branchDistance <= 0) & ((ABS(branchDistance) MOD 4) = 0), 200);
+				ELSE
+					(* forward branch: the distance is not yet known, use some placeholder and add a relative fixup *)
+					branchDistance := -4;
+
+					(* TODO: what about a branch to the next instruction? this would require the fixup meachnism to patch a negative value! (-> -4) *)
+					NEW(fixupPatternList, 1);
+					fixupPatternList[0].offset := 0;
+					fixupPatternList[0].bits := 24;
+					identifier.name := in.name;
+					identifier.fingerprint := in.fingerprint;
+					fixup := BinaryCode.NewFixup(BinaryCode.Relative, out.pc, identifier, irInstruction.op1.symbolOffset, -8, -2, fixupPatternList);
+					out.fixupList.AddFixup(fixup)
+				END;
+				Emit1WithCondition(opB, InstructionSet.NewImmediate(branchDistance), branchConditionCode)
+
+			ELSE
+				(* any other type of branch -> do register branch *)
+				Emit1WithCondition(opBX, RegisterFromIrOperand(irInstruction.op1, Low, emptyOperand), branchConditionCode)
+
+			END;
+
+			END JmpDest;
+
+			PROCEDURE Cmp(CONST left, right: InstructionSet.Operand; float: BOOLEAN);
+			BEGIN
+				IF float THEN
+					IF ~backend.useFPU OR IsComplex(irLeft) (* 64 bit *) THEN
+						(* floating point comparisons without VFP unit *)
+						temp := GetFreeRegister(IntermediateCode.UnsignedIntegerType(32));
+						Emit3WithFlags(opAND, temp, left, right, {InstructionSet.flagS});
+						Emit2(opCMP, temp, InstructionSet.NewImmediate(0));
+						Emit1WithCondition(opB, InstructionSet.NewImmediate(4), InstructionSet.conditionLT); (* skip two instructions *)
+						Emit2(opCMP, left, right);
+						Emit1(opB, InstructionSet.NewImmediate(0)); (* skip one instructions  *)
+						Emit2(opCMP, right, left);
+					ELSE
+						Emit2(opFCMPS, left, right);
+						Emit0(opFMSTAT); (* transfer the VFP flags to the standard ARM flags *)
+					END
+				ELSE
+					Emit2(opCMP, left, right);
+				END;
+			END Cmp;
+
+		BEGIN
+			hiFail := None;
+			hiHit := None;
+			IF irInstruction.opcode = IntermediateCode.br THEN
+				(* unconditional branch: *)
+				lowHit := InstructionSet.conditionAL
+
+			ELSE
+				(* conditional branch: *)
+				irLeft := irInstruction.op2; irRight := irInstruction.op3;
+				ASSERT((irLeft.type.form = irRight.type.form) & (irLeft.type.sizeInBits = irRight.type.sizeInBits));
+
+				IF IsInteger(irLeft) THEN
+					IF IsComplex(irLeft) THEN
+						CASE irInstruction.opcode OF
+						| IntermediateCode.breq, IntermediateCode.brne: (* left = right, left # right *)
+							lowHit := InstructionSet.conditionEQ;
+
+							left[High] := RegisterFromIrOperand(irLeft, High, emptyOperand);
+							right[High] := RegisterOrImmediateFromIrOperand(irRight, High, emptyOperand);
+							Emit2(opCMP, left[High], right[High]);
+							left[Low] := RegisterFromIrOperand(irLeft, Low, left[High]);
+							right[Low] := RegisterOrImmediateFromIrOperand(irRight, Low, right[High]);
+							Emit2WithCondition(opCMP, left[Low], right[Low], lowHit);
+							
+							IF irInstruction.opcode = IntermediateCode.brne THEN lowHit := InstructionSet.conditionNE END;
+
+						| IntermediateCode.brlt, IntermediateCode.brge: (* left < right, left >= right *)
+							IF irInstruction.opcode = IntermediateCode.brlt THEN lowHit := InstructionSet.conditionLT ELSE lowHit := InstructionSet.conditionGE END;
+
+							ASSERT(irLeft.type.form = IntermediateCode.SignedInteger);
+							left[Low] := RegisterFromIrOperand(irLeft, Low, emptyOperand);
+							right[Low] := RegisterOrImmediateFromIrOperand(irRight, Low, emptyOperand);
+							temp := GetFreeRegister(IntermediateCode.UnsignedIntegerType(32));
+							Emit3WithFlags(opSUB, temp, left[Low], right[Low], {InstructionSet.flagS});
+							left[High] := RegisterFromIrOperand(irLeft, High, left[Low]);
+							right[High] := RegisterOrImmediateFromIrOperand(irRight, High, right[Low]);
+							Emit3WithFlags(opSBC, temp, left[High], right[High], {InstructionSet.flagS}) (* the high part of the subtraction determines the sign *)
+
+						ELSE
+							HALT(100)
+						END
+
+					ELSE
+						ASSERT((irLeft.type.form IN IntermediateCode.Integer) & (irLeft.type.sizeInBits <= 32));
+
+						(* swap operands if beneficial *)
+						IF ~IrOperandIsDirectlyEncodable(irRight, Low) & IrOperandIsDirectlyEncodable(irLeft, Low) THEN
+							isSwapped := TRUE;
+							SwapIrOperands(irLeft, irRight)
+						END;
+
+						left[Low] := RegisterFromIrOperand(irLeft, Low, emptyOperand);
+						right[Low] := RegisterOrImmediateFromIrOperand(irRight, Low, emptyOperand);
+
+						SignOrZeroExtendOperand(left[Low], irLeft.type);
+						SignOrZeroExtendOperand(right[Low], irRight.type);
+
+						Cmp(left[Low], right[Low], FALSE);
+
+						(* determine condition code for the branch (take into consideration that operands could have been swapped) *)
+						CASE irInstruction.opcode OF
+						| IntermediateCode.breq: (* left = right *) lowHit := InstructionSet.conditionEQ
+						| IntermediateCode.brne: (* left # right *) lowHit := InstructionSet.conditionNE
+						| IntermediateCode.brlt: (* left < right *)
+							IF irInstruction.op2.type.form = IntermediateCode.UnsignedInteger THEN
+								IF isSwapped THEN lowHit := InstructionSet.conditionHI ELSE lowHit := InstructionSet.conditionLO END
+							ELSE
+								IF isSwapped THEN lowHit := InstructionSet.conditionGT ELSE lowHit := InstructionSet.conditionLT END
+							END
+						| IntermediateCode.brge: (* left >= right *)
+							IF irInstruction.op2.type.form = IntermediateCode.UnsignedInteger THEN
+								IF isSwapped THEN lowHit := InstructionSet.conditionLS ELSE lowHit := InstructionSet.conditionHS END
+							ELSE
+								IF isSwapped THEN lowHit := InstructionSet.conditionLE ELSE lowHit := InstructionSet.conditionGE END
+							END
+						ELSE HALT(100)
+						END
+					END
+
+				ELSIF IsSinglePrecisionFloat(irLeft) THEN
+					left[Low] := RegisterFromIrOperand(irLeft, Low, emptyOperand);
+					right[Low] := RegisterFromIrOperand(irRight, Low, emptyOperand);
+					Cmp(left[Low], right[Low], TRUE);
+
+					CASE irInstruction.opcode OF
+					| IntermediateCode.breq: (* left = right *) lowHit := InstructionSet.conditionEQ
+					| IntermediateCode.brne: (* left # right *) lowHit := InstructionSet.conditionNE
+					| IntermediateCode.brlt: (* left < right *) lowHit := InstructionSet.conditionLT
+					| IntermediateCode.brge: (* left >= right *) lowHit := InstructionSet.conditionGE
+					ELSE HALT(100)
+					END
+
+				ELSIF IsDoublePrecisionFloat(irLeft) THEN
+					CASE irInstruction.opcode OF
+					IntermediateCode.breq:
+						hiHit := None; hiFail := InstructionSet.conditionNE; lowHit := InstructionSet.conditionEQ
+					|IntermediateCode.brne:
+						hiHit := InstructionSet.conditionNE; hiFail := None; lowHit := InstructionSet.conditionNE
+					|IntermediateCode.brge:
+						IF isSwapped THEN
+							hiHit := InstructionSet.conditionLT; hiFail := InstructionSet.conditionGT; lowHit := InstructionSet.conditionLS
+						ELSE
+							hiHit := InstructionSet.conditionGT; hiFail := InstructionSet.conditionLT; lowHit := InstructionSet.conditionHS
+						END;
+					|IntermediateCode.brlt:
+						IF isSwapped THEN
+							hiHit := InstructionSet.conditionGT; hiFail := InstructionSet.conditionLT; lowHit := InstructionSet.conditionHI
+						ELSE
+							hiHit := InstructionSet.conditionLT; hiFail := InstructionSet.conditionGT; lowHit := InstructionSet.conditionLO
+						END;
+					END;
+
+					(*
+						compare hi part (as float)
+						if hiHit then br dest
+						elsif hiFail then br fail
+						else compare low part (as unsigned int)
+							if lowHit then br dest
+							end
+						end,
+						fail:
+					*)
+
+					(* hi part *)
+					left[High] := RegisterFromIrOperand(irLeft, High, emptyOperand);
+					right[High] := RegisterOrImmediateFromIrOperand(irRight, High, emptyOperand);
+					Cmp(left[High], right[High], TRUE);
+					IF hiHit # None THEN
+						JmpDest(hiHit)
+					END;
+					IF hiFail # None THEN
+						NEW(fixupPatternList, 1);
+						fixupPatternList[0].offset := 0;
+						fixupPatternList[0].bits := 24;
+						identifier.name := in.name;
+						identifier.fingerprint := in.fingerprint;
+						failFixup := BinaryCode.NewFixup(BinaryCode.Relative, out.pc, identifier, irInstruction.op1.symbolOffset, -8, -2, fixupPatternList);
+						out.fixupList.AddFixup(failFixup);
+						Emit1WithCondition(opB, InstructionSet.NewImmediate(branchDistance), hiFail)
+					END;
+
+					(* low part *)
+					left[Low] := RegisterFromIrOperand(irLeft, Low, emptyOperand);
+					right[Low] := RegisterFromIrOperand(irRight, Low, emptyOperand);
+					Cmp(left[Low], right[Low], FALSE);
+				ELSE
+					HALT(200)
+				END
+			END;
+
+			JmpDest(lowHit);
+			IF failFixup # NIL THEN
+				failFixup.SetSymbol(in.name, in.fingerprint, 0, out.pc+failFixup.displacement (* displacement offset computed during operand emission, typically -1 *) );
+				failFixup.resolved := in;
+			END;
+
+
+		END EmitBr;
+
+		(* TODO: floats *)
+		PROCEDURE EmitConv(VAR irInstruction: IntermediateCode.Instruction);
+		VAR
+			irDestination, irSource: IntermediateCode.Operand;
+			destination, source: ARRAY 2 OF Operand;
+			temp: Operand;
+			partType: IntermediateCode.Type;
+		BEGIN
+			irDestination := irInstruction.op1; irSource := irInstruction.op2;
+
+			(* prepare operands *)
+			destination[Low] := AcquireDestinationRegister(irDestination, Low, emptyOperand); (* TODO: find more optimal register allocation *)
+			source[Low] := RegisterOrImmediateFromIrOperand(irSource, Low, destination[Low]);
+			IF IsComplex(irDestination) THEN destination[High]:= AcquireDestinationRegister(irDestination, High, emptyOperand) END;
+			IF IsComplex(irSource) THEN source[High] := RegisterOrImmediateFromIrOperand(irSource, High, destination[High]) END; (* note that the corresponding destination register is used as hint *)
+
+			IF IsInteger(irDestination) THEN
+				(* to integer: *)
+				IF IsComplex(irDestination) THEN
+					(* to complex integer: *)
+					IF IsInteger(irSource) THEN
+						(* integer to complex integer: *)
+						IF IsComplex(irSource) THEN
+							(* complex integer to complex integer: *)
+							MovIfDifferent(destination[Low], source[Low]);
+							MovIfDifferent(destination[High], source[High]);
+						ELSE
+							(* non-complex integer to complex integer: *)
+							SignOrZeroExtendOperand(source[Low], irSource.type);
+							MovIfDifferent(destination[Low], source[Low]);
+
+							IF irDestination.type.form = IntermediateCode.UnsignedInteger THEN
+								Emit2(opMOV, destination[High], InstructionSet.NewImmediate(0));
+							ELSE
+								(* for signed values the high part is set to 0...0 or 1...1, depending on the sign of the low part *)
+								Emit2(opMOV, destination[High], InstructionSet.NewRegister(source[Low].register, InstructionSet.shiftASR, None, 32))
+							END
+						END
+
+					ELSIF IsSinglePrecisionFloat(irSource) THEN
+						ASSERT(backend.useFPU);
+						(* single precision float to complex integer: *)
+						temp := GetFreeRegister(IntermediateCode.FloatType(32));
+						IF irDestination.type.form = IntermediateCode.UnsignedInteger THEN
+							(* single precision float to non-complex unsigned integer: *)
+							Emit2(opFTOUIS, temp, source[Low]);
+						ELSE
+							(* single precision float to non-complex signed integer: *)
+							Emit2(opFTOSIS, temp, source[Low]);
+						END;
+						Emit2(opFMRS, destination[Low], temp);
+						IF irDestination.type.form = IntermediateCode.UnsignedInteger THEN
+							Emit2(opMOV, destination[High], InstructionSet.NewImmediate(0));
+						ELSE
+							(* for signed values the high part is set to 0...0 or 1...1, depending on the sign of the low part *)
+							Emit2(opMOV, destination[High], InstructionSet.NewRegister(destination[Low].register, InstructionSet.shiftASR, None, 32))
+						END
+					ELSE
+						(* anything else to complex-integer: *)
+						HALT(200)
+					END
+				ELSE
+					(* to non-complex integer: *)
+					IF IsInteger(irSource) THEN
+						(* integer to non-complex integer: ignore high part of source *)
+						GetPartType(irSource.type, Low, partType);
+						SignOrZeroExtendOperand(source[Low], partType);
+						MovIfDifferent(destination[Low], source[Low])
+					ELSIF IsSinglePrecisionFloat(irSource) THEN
+						ASSERT(backend.useFPU);
+						(* single precision float to non-complex integer: *)
+						temp := GetFreeRegister(IntermediateCode.FloatType(32));
+						IF irDestination.type.form = IntermediateCode.UnsignedInteger THEN
+							(* single precision float to non-complex unsigned integer: *)
+							Emit2(opFTOUIS, temp, source[Low]);
+						ELSE
+							(* single precision float to non-complex signed integer: *)
+							Emit2(opFTOSIS, temp, source[Low]);
+						END;
+						Emit2(opFMRS, destination[Low], temp)
+					ELSE
+						(* anything to non-complex integer: *)
+						HALT(200)
+					END
+				END
+
+			ELSIF IsSinglePrecisionFloat(irDestination) THEN
+				(* to single precision float: *)
+			 	IF IsInteger(irSource) THEN
+			 		(* integer to single precision float: ignore high part of source *)
+			 		temp := GetFreeRegister(IntermediateCode.FloatType(32));
+			 		Emit2(opFMSR, temp, source[Low]);
+		 			IF irSource.type.form = IntermediateCode.UnsignedInteger THEN
+						(* non-complex unsigned integer to single precision float: *)
+						Emit2(opFUITOS, destination[Low], temp)
+					ELSE
+						(* non-complex signed integer to single precision float: *)
+						Emit2(opFSITOS, destination[Low], temp)
+					END
+			 	ELSIF IsSinglePrecisionFloat(irSource) THEN
+			 		(* single precision float to single precision float: *)
+			 		MovIfDifferent(destination[Low], source[Low])
+			 	ELSE
+			 		(* anything else to single precision float: *)
+			 		HALT(200)
+			 	END
+			ELSE
+				(* to anything else: *)
+				HALT(200)
+			END;
+
+			WriteBack(irDestination, Low, destination[Low]);
+			IF IsComplex(irDestination) THEN WriteBack(irInstruction.op1, High, destination[High]) END
+		END EmitConv;
+
+		(** get the register that is dedicated to store a return value of a function **)
+		PROCEDURE ResultRegister(part: LONGINT; type: IntermediateCode.Type): InstructionSet.Operand;
+		VAR
+			result: Operand;
+		BEGIN
+			IF (type.form IN IntermediateCode.Integer) OR ~(backend.useFPU) THEN
+				IF part = Low THEN result := opRES
+				ELSIF part = High THEN result := opRESHI
+				ELSE HALT(200)
+				END
+			ELSIF type.form = IntermediateCode.Float THEN
+				ASSERT(type.sizeInBits = 32, 200);
+				result := opRESFS
+			END;
+			RETURN result
+		END ResultRegister;
+
+		PROCEDURE EmitReturn(VAR irInstruction: IntermediateCode.Instruction; part: LONGINT);
+		VAR
+			source: Operand;
+		BEGIN
+			source := RegisterOrImmediateFromIrOperand(irInstruction.op1, part, ResultRegister(part, irInstruction.op1.type)); (* note: the result register is given as a hint *)
+			MovIfDifferent(ResultRegister(part, irInstruction.op1.type), source)
+		END EmitReturn;
+
+		PROCEDURE EmitResult(VAR irInstruction: IntermediateCode.Instruction; part: LONGINT);
+		VAR
+			destinationRegister: Operand;
+		BEGIN
+			destinationRegister := AcquireDestinationRegister(irInstruction.op1, part, emptyOperand);
+			MovIfDifferent(destinationRegister, ResultRegister(part, irInstruction.op1.type));
+			WriteBack(irInstruction.op1, part, destinationRegister)
+		END EmitResult;
+
+		PROCEDURE EmitTrap(CONST irInstruction: IntermediateCode.Instruction);
+		BEGIN
+			ASSERT(irInstruction.op1.mode = IntermediateCode.ModeNumber);
+
+			Emit1(opSWI, InstructionSet.NewImmediate(LONGINT(irInstruction.op1.intValue))) (* software interrupt *)
+		END EmitTrap;
+
+		(* possible optimization: use a combination of LDR and LDRB (would be 4x faster on average) *)
+		PROCEDURE EmitCopy(VAR irInstruction: IntermediateCode.Instruction);
+		VAR
+			targetBaseReg, sourceBaseReg, length, lastSourceAddress, currentTargetReg, currentSourceReg, tempReg: Operand;
+		BEGIN
+			ASSERT((irInstruction.op1.type.form = IntermediateCode.UnsignedInteger) & (irInstruction.op1.type.sizeInBits = 32));
+			ASSERT((irInstruction.op2.type.form = IntermediateCode.UnsignedInteger) & (irInstruction.op2.type.sizeInBits = 32));
+			ASSERT((irInstruction.op3.type.form = IntermediateCode.UnsignedInteger) & (irInstruction.op3.type.sizeInBits = 32));
+
+			currentTargetReg := GetFreeRegister(IntermediateCode.UnsignedIntegerType(32));
+			currentSourceReg := GetFreeRegister(IntermediateCode.UnsignedIntegerType(32));
+
+			(* note that the registers that store the current addresses are used as hints: *)
+			targetBaseReg := RegisterFromIrOperand(irInstruction.op1, Low, currentTargetReg);
+			sourceBaseReg := RegisterFromIrOperand(irInstruction.op2, Low, currentSourceReg);
+
+			MovIfDifferent(currentTargetReg, targetBaseReg);
+			MovIfDifferent(currentSourceReg, sourceBaseReg);
+
+			lastSourceAddress := GetFreeRegister(IntermediateCode.UnsignedIntegerType(32));
+			length := RegisterOrImmediateFromIrOperand(irInstruction.op3, Low, lastSourceAddress); (* note that the last source address register is used as hint*)
+
+			Emit3(opADD, lastSourceAddress, sourceBaseReg, length);
+
+			tempReg := GetFreeRegister(IntermediateCode.UnsignedIntegerType(32));
+			Emit2WithFlags(opLDR, tempReg, InstructionSet.NewImmediateOffsetMemory(currentSourceReg.register, 1, {InstructionSet.Increment, InstructionSet.PostIndexed}), {InstructionSet.flagB});
+			Emit2WithFlags(opSTR, tempReg, InstructionSet.NewImmediateOffsetMemory(currentTargetReg.register, 1, {InstructionSet.Increment, InstructionSet.PostIndexed}), {InstructionSet.flagB});
+			Emit2(opCMP, currentSourceReg, lastSourceAddress);
+			Emit1WithCondition(opB, InstructionSet.NewImmediate(-20), InstructionSet.conditionLT)
+		END EmitCopy;
+
+		PROCEDURE EmitFill(CONST irInstruction: IntermediateCode.Instruction; down: BOOLEAN);
+		BEGIN
+			HALT(200) (* note that this instruction is not used at the moment *)
+		END EmitFill;
+
+		(* PREPARATION OF OPERATIONS *)
+
+		(** swap a pair of IR operands **)
+		PROCEDURE SwapIrOperands(VAR left, right: IntermediateCode.Operand);
+		VAR
+			temp: IntermediateCode.Operand;
+		BEGIN
+			temp := left;
+			left := right;
+			right := temp
+		END SwapIrOperands;
+
+		PROCEDURE PrepareSingleSourceOp(VAR irInstruction: IntermediateCode.Instruction; part: LONGINT; VAR destinationRegister, sourceOperand: Operand);
+		BEGIN
+			destinationRegister := AcquireDestinationRegister(irInstruction.op1, part, emptyOperand);
+			sourceOperand := RegisterFromIrOperand(irInstruction.op2, part, destinationRegister); (* note that the destination register is used as hint *)
+		END PrepareSingleSourceOp;
+
+		PROCEDURE PrepareSingleSourceOpWithImmediate(VAR irInstruction: IntermediateCode.Instruction; part: LONGINT; VAR destinationRegister, sourceOperand: Operand);
+		BEGIN
+			destinationRegister := AcquireDestinationRegister(irInstruction.op1, part, emptyOperand);
+			sourceOperand := RegisterOrImmediateFromIrOperand(irInstruction.op2, part, destinationRegister); (* note that the destination register is used as hint *)
+		END PrepareSingleSourceOpWithImmediate;
+
+		PROCEDURE PrepareDoubleSourceOpWithImmediate(CONST irInstruction: IntermediateCode.Instruction; part: LONGINT; VAR destinationRegister, leftSourceOperand, rightSourceOperand: Operand; VAR isSwapped: BOOLEAN);
+		VAR
+			irDestination, irLeft, irRight: IntermediateCode.Operand;
+		BEGIN
+			irDestination := irInstruction.op1;
+			irLeft := irInstruction.op2;
+			irRight := irInstruction.op3;
+
+			destinationRegister:= AcquireDestinationRegister(irDestination, part, emptyOperand);
+
+			(* swap operands such that the right one is an immediate *)
+			IF IrOperandIsDirectlyEncodable(irLeft, part) & ~IrOperandIsDirectlyEncodable(irRight, part) THEN
+				SwapIrOperands(irLeft, irRight);
+				isSwapped := TRUE
+			ELSIF IntermediateCode.OperandEquals(irRight, irDestination) THEN
+				SwapIrOperands(irLeft, irRight);
+				isSwapped := TRUE
+			ELSE
+				isSwapped := FALSE
+			END;
+
+			leftSourceOperand := RegisterFromIrOperand(irLeft, part, destinationRegister); (* the destination register is used as hint *)
+			IF IsSameRegister(leftSourceOperand, destinationRegister) THEN
+				rightSourceOperand := RegisterOrImmediateFromIrOperand(irRight, part, emptyOperand) (* no hint is provided *)
+			ELSE
+				rightSourceOperand := RegisterOrImmediateFromIrOperand(irRight, part, destinationRegister)  (* the destination register is again used as hint *)
+			END
+		END PrepareDoubleSourceOpWithImmediate;
+
+		PROCEDURE PrepareDoubleSourceOp(CONST irInstruction: IntermediateCode.Instruction; part: LONGINT; VAR destinationRegister, leftSourceOperand, rightSourceOperand: Operand);
+		VAR
+			irDestination, irLeft, irRight: IntermediateCode.Operand;
+		BEGIN
+			irDestination := irInstruction.op1;
+			irLeft := irInstruction.op2;
+			irRight := irInstruction.op3;
+
+			destinationRegister:= AcquireDestinationRegister(irDestination, part, emptyOperand);
+
+			IF IntermediateCode.OperandEquals(irRight, irDestination) THEN
+				leftSourceOperand := RegisterFromIrOperand(irLeft, part, emptyOperand); (* do not use destination register as hint *)
+			ELSE
+				leftSourceOperand := RegisterFromIrOperand(irLeft, part, destinationRegister); (* the destination register is used as hint *)
+			END;
+			IF IsSameRegister(leftSourceOperand, destinationRegister) OR IntermediateCode.OperandEquals(irRight, irDestination) THEN
+				rightSourceOperand := RegisterFromIrOperand(irRight, part, emptyOperand) (* no hint is provided *)
+			ELSE
+				rightSourceOperand := RegisterFromIrOperand(irRight, part, destinationRegister)  (* the destination register is again used as hint *)
+			END
+		END PrepareDoubleSourceOp;
+
+	END CodeGeneratorARM;
+
+	BackendARM = OBJECT(IntermediateBackend.IntermediateBackend)
+	VAR
+		cg: CodeGeneratorARM;
+		system: Global.System;
+		useFPU: BOOLEAN;
+		initLocals: BOOLEAN;
+
+		PROCEDURE & InitBackendARM;
+		BEGIN
+			useFPU := FALSE;
+			InitIntermediateBackend;
+			SetRuntimeModuleName(DefaultRuntimeModuleName);
+			SetNewObjectFile(TRUE,FALSE);
+			system := NIL;
+			initLocals := TRUE;
+		END InitBackendARM;
+
+		PROCEDURE Initialize(diagnostics: Diagnostics.Diagnostics; log: Streams.Writer; flags: SET; checker: SemanticChecker.Checker; system: Global.System; activeCellsSpecification: ActiveCells.Specification);
+		BEGIN
+			Initialize^(diagnostics, log, flags, checker, system, activeCellsSpecification);
+			NEW(cg, runtimeModuleName, diagnostics, SELF)
+		END Initialize;
+
+		PROCEDURE EnterCustomBuiltins;
+		VAR
+			procedureType: SyntaxTree.ProcedureType;
+			parameter: SyntaxTree.Parameter;
+
+			PROCEDURE New;
+			BEGIN procedureType := SyntaxTree.NewProcedureType(-1, NIL)
+			END New;
+
+			PROCEDURE BoolRet;
+			BEGIN procedureType.SetReturnType(system.booleanType)
+			END BoolRet;
+
+			PROCEDURE IntRet;
+			BEGIN procedureType.SetReturnType(Global.Integer32)
+			END IntRet;
+
+			PROCEDURE IntPar;
+			BEGIN
+				parameter := SyntaxTree.NewParameter(-1, procedureType, SyntaxTree.NewIdentifier(""), SyntaxTree.ValueParameter);
+				parameter.SetType(Global.Integer32); procedureType.AddParameter(parameter)
+			END IntPar;
+
+			PROCEDURE IntVarPar;
+			BEGIN
+				parameter := SyntaxTree.NewParameter(-1, procedureType, SyntaxTree.NewIdentifier(""), SyntaxTree.VarParameter);
+				parameter.SetType(Global.Integer32); procedureType.AddParameter(parameter)
+			END IntVarPar;
+
+			PROCEDURE RealVarPar;
+			BEGIN
+				parameter := SyntaxTree.NewParameter(-1, procedureType, SyntaxTree.NewIdentifier(""), SyntaxTree.VarParameter);
+				parameter.SetType(Global.Float32); procedureType.AddParameter(parameter)
+			END RealVarPar;
+
+			PROCEDURE Finish(CONST name: ARRAY OF CHAR; number: SHORTINT);
+			BEGIN Global.NewCustomBuiltin(name, system.systemScope, number, procedureType);
+			END Finish;
+
+		BEGIN
+			New; IntRet; Finish("SP", GetSP);
+			New; IntPar; Finish("SetSP", SetSP);
+			New; IntRet; Finish("FP", GetFP);
+			New; IntPar; Finish("SetFP", SetFP);
+			New; IntRet; Finish("PC", GetPC);
+			New; IntPar; Finish("SetPC", SetPC);
+			New; IntRet; Finish("LNK", GetLNK);
+			New; IntPar; Finish("SetLNK", SetLNK);
+			New; IntPar; IntPar;  Finish("LDPSR", LDPSR);
+			New; IntPar; IntVarPar; Finish("STPSR", STPSR);
+			New; IntPar; IntPar; IntPar; Finish("LDCPR", LDCPR);
+			New; IntPar; IntPar; IntVarPar; Finish("STCPR", STCPR);
+			New; IntPar; Finish("FLUSH", FLUSH);
+			New; BoolRet; IntPar; Finish("NULL", NULL);
+			New; IntRet; IntPar; IntPar; Finish("XOR", XOR);
+			New; IntVarPar; IntPar; IntPar; Finish("MULD", MULD);
+			New; IntVarPar; IntPar; IntPar; Finish("ADDC", ADDC);
+			New; RealVarPar; IntPar; Finish("PACK", PACK);
+			New; RealVarPar; IntVarPar; Finish("UNPK", UNPK);
+		END EnterCustomBuiltins;
+
+		PROCEDURE GetSystem(): Global.System;
+		BEGIN
+			(* create system object if not yet existing *)
+			IF system = NIL THEN
+
+				(* used stack frame layout:
+
+								param 1
+								param 2
+								...
+								param n-1
+					FP+8 ->	param n
+					FP+4 ->	old LR
+					FP ->		old FP
+					FP-4 ->	local 1
+								local 2
+								...
+								spill 1
+								spill 2
+								....
+				*)
+
+				(*
+				codeUnit, dataUnit = 8, 8
+				addressSize = 32
+				minVarAlign, maxVarAlign = 32, 32
+				minParAlign, maxParAlign = 8, 32
+				offsetFirstPar = 32 * 2
+				registerParameters = 0
+				*)
+
+				NEW(system, 8, 8, 32, (*32*) 8, 32, 8, 32, 32 * 2, 0, cooperative);
+
+				IF oberon07 THEN
+					IF Trace THEN D.String("Oberon07"); D.Ln END;
+					Global.SetDefaultDeclarations(system, 32) (* each basic type uses at least 32 bits -> INTEGER will be 32 bits long *)
+				ELSE
+					IF Trace THEN D.String("not Oberon07"); D.Ln END;
+					Global.SetDefaultDeclarations(system, 8) (* INTEGER will be 16 bits long *)
+				END;
+
+				Global.SetDefaultOperators(system);
+
+				EnterCustomBuiltins
+			END;
+			RETURN system
+		END GetSystem;
+
+		(** whether the code generator can generate code for a certain IR instruction
+		if not, where to find the runtime procedure that is to be called instead **)
+		PROCEDURE SupportedInstruction(CONST irInstruction: IntermediateCode.Instruction; VAR moduleName, procedureName: ARRAY OF CHAR): BOOLEAN;
+		BEGIN
+			(* only necessary for binary object file format for symbol / module entry in IntermediateBackend *)
+			RETURN cg.Supported(irInstruction, moduleName, procedureName);
+		END SupportedInstruction;
+
+		(** whether a certain intermediate code immediate value can be directly appear in code
+		if not, the value is stored in a const section and loaded from there **)
+		PROCEDURE SupportedImmediate(CONST irImmediateOperand: IntermediateCode.Operand): BOOLEAN;
+		VAR
+			result: BOOLEAN;
+		BEGIN
+			(* TODO: remove this *)
+			RETURN TRUE; (* tentatively generate all immediates, as symbol fixups are not yet implemented *)
+
+			result := FALSE;
+			IF (irImmediateOperand.type.form IN IntermediateCode.Integer) & (irImmediateOperand.type.sizeInBits <= 32) THEN
+				(* 32 bit integers *)
+				IF cg.ValueIsDirectlyEncodable(LONGINT(irImmediateOperand.intValue)) THEN
+					(* the value can be directly encoded as an ARM immediate operand *)
+					result := TRUE
+				ELSIF cg.ValueComposition(LONGINT(irImmediateOperand.intValue), FALSE, emptyOperand) <= 2 THEN (* TODO: find reasonable limit *)
+					(* the value can be generated using a limited amount of intructions *)
+					result := TRUE
+				END
+			END;
+			RETURN result
+		END SupportedImmediate;
+
+		PROCEDURE GenerateBinary(module: Sections.Module; dump: Streams.Writer);
+		VAR
+			in: Sections.Section;
+			out: BinaryCode.Section;
+			name: Basic.SectionName;
+			procedure: SyntaxTree.Procedure;
+			i, j, initialSectionCount: LONGINT;
+
+
+
+
+		 	(* recompute fixup positions and assign binary sections *)
+		 	PROCEDURE PatchFixups(section: BinaryCode.Section);
+			VAR resolved: BinaryCode.Section; fixup: BinaryCode.Fixup; displacement,symbolOffset: LONGINT; in: IntermediateCode.Section;
+				symbol: Sections.Section;
+			BEGIN
+				fixup := section.fixupList.firstFixup;
+				WHILE fixup # NIL DO
+					symbol := module.allSections.FindByName(fixup.symbol.name);
+					IF  (symbol # NIL) & (symbol(IntermediateCode.Section).resolved # NIL) THEN
+						resolved := symbol(IntermediateCode.Section).resolved(BinaryCode.Section);
+						in := symbol(IntermediateCode.Section);
+						symbolOffset := fixup.symbolOffset;
+						IF symbolOffset = in.pc THEN
+							displacement := resolved.pc
+						ELSIF (symbolOffset # 0) THEN
+							ASSERT(in.pc > symbolOffset);
+							displacement := in.instructions[symbolOffset].pc;
+						ELSE
+							displacement := 0;
+						END;
+						fixup.SetSymbol(fixup.symbol.name,fixup.symbol.fingerprint,0,fixup.displacement+displacement);
+					END;
+					fixup := fixup.nextFixup;
+				END;
+			END PatchFixups;
+
+			(*
+			PROCEDURE Resolve(VAR fixup: BinaryCode.Fixup);
+			BEGIN
+				IF (fixup.symbol.name # "") & (fixup.resolved = NIL) THEN fixup.resolved := module.allSections.FindByName(fixup.symbol.name) END;
+			END Resolve;
+			
+		 	(* recompute fixup positions and assign binary sections *)
+		 	PROCEDURE PatchFixups(section: BinaryCode.Section);
+			VAR resolved: BinaryCode.Section; fixup: BinaryCode.Fixup; symbolOffset, offsetWithinSection: LONGINT; in: IntermediateCode.Section;
+			BEGIN
+				fixup := section.fixupList.firstFixup;
+				WHILE fixup # NIL DO
+					Resolve(fixup);
+					IF  (fixup.resolved # NIL) & (fixup.resolved(IntermediateCode.Section).resolved # NIL) THEN
+						resolved := fixup.resolved(IntermediateCode.Section).resolved(BinaryCode.Section);
+						in := fixup.resolved(IntermediateCode.Section);
+
+						(* TODO: is this correct? *)
+						symbolOffset := fixup.symbolOffset;
+						ASSERT(fixup.symbolOffset < in.pc);
+						IF (fixup.symbolOffset # 0) & (symbolOffset < in.pc) THEN
+							offsetWithinSection := in.instructions[fixup.symbolOffset].pc;
+
+							(*
+							(* TENTATIVE *)
+							D.String("FIXUP PATCH:"); D.Ln;
+							D.String("    symbol name: "); fixup.symbol.DumpName(D.Log); D.String("/");
+							D.String("    symbol offset: "); D.Int(fixup.symbolOffset, 0); D.Ln;
+							D.String("    offsetWithinSection"); D.Int(offsetWithinSection, 0); D.Ln;
+							D.String("    fixup.displacement (before)"); D.Int(fixup.displacement, 0); D.Ln; ; D.Ln;
+							D.Update;
+							*)
+
+							(* remove the fixup's symbol offset (in IR units) and change the displacement (in system units) accordingly: *)
+							fixup.SetSymbol(fixup.symbol.name, fixup.symbol.fingerprint, 0, offsetWithinSection + fixup.displacement)
+						END
+					END;
+					fixup := fixup.nextFixup;
+				END;
+			END PatchFixups;
+			*)
+
+		BEGIN
+		 	cg.SetModule(module);
+		 	cg.dump := dump;
+
+		 	FOR i := 0 TO module.allSections.Length() - 1 DO
+			 	in := module.allSections.GetSection(i);
+		 		IF in.type = Sections.InlineCodeSection THEN
+		 			Basic.SegmentedNameToString(in.name, name);
+			 		out := ResolvedSection(in(IntermediateCode.Section));
+			 		cg.dump := out.comments;
+			 		cg.Section(in(IntermediateCode.Section), out);
+			 		IF in.symbol # NIL THEN
+				 		procedure := in.symbol(SyntaxTree.Procedure);
+				 		procedure.procedureScope.body.code.SetBinaryCode(out.os.bits);
+				 	END;
+			 	END
+		 	END;
+
+			initialSectionCount := 0;
+		 	REPEAT
+		 		j := initialSectionCount;
+		 	 	initialSectionCount := module.allSections.Length() ;
+
+			 	FOR i := j TO initialSectionCount - 1 DO
+			 		in := module.allSections.GetSection(i);
+		 			Basic.SegmentedNameToString(in.name, name);
+
+			 		IF (in.type # Sections.InlineCodeSection) & (in(IntermediateCode.Section).resolved = NIL) THEN
+				 		out := ResolvedSection(in(IntermediateCode.Section));
+				 		cg.Section(in(IntermediateCode.Section),out);
+			 		END
+			 	END
+			UNTIL initialSectionCount = module.allSections.Length(); (* process remaining sections that have been added during traversal of sections *)
+
+		 	FOR i := 0 TO module.allSections.Length() - 1 DO
+			 	in := module.allSections.GetSection(i);
+	 			Basic.SegmentedNameToString(in.name, name);
+			 	in := module.allSections.GetSection(i);
+		 		PatchFixups(in(IntermediateCode.Section).resolved)
+		 	END;
+
+			IF cg.error THEN Error("", Diagnostics.Invalid, Diagnostics.Invalid,  "") END
+		END GenerateBinary;
+
+		(** create an ARM code module from an intermediate code module **)
+		PROCEDURE ProcessIntermediateCodeModule*(intermediateCodeModule: Formats.GeneratedModule): Formats.GeneratedModule;
+		VAR
+			result: Formats.GeneratedModule;
+		BEGIN
+			ASSERT(intermediateCodeModule IS Sections.Module);
+			result := ProcessIntermediateCodeModule^(intermediateCodeModule);
+
+			IF ~error THEN
+				GenerateBinary(result(Sections.Module), dump);
+				IF dump # NIL THEN
+					dump.Ln; dump.Ln;
+					dump.String("------------------ binary code -------------------"); dump.Ln;
+					IF (traceString="") OR (traceString="*") THEN
+						result.Dump(dump);
+						dump.Update
+					ELSE
+						Sections.DumpFiltered(dump, result(Sections.Module), traceString);
+						dump.Update;
+					END
+				END;
+			END;
+			RETURN result
+		FINALLY
+			IF dump # NIL THEN
+				dump.Ln; dump.Ln;
+				dump.String("------------------ rescued code (code generation trapped) -------------------"); dump.Ln;
+				IF (traceString="") OR (traceString="*") THEN
+					result.Dump(dump);
+					dump.Update
+				ELSE
+					Sections.DumpFiltered(dump,result(Sections.Module),traceString);
+					dump.Update;
+				END
+			END;
+			RETURN result
+		END ProcessIntermediateCodeModule;
+
+		PROCEDURE DefineOptions(options: Options.Options);
+		BEGIN
+			options.Add(0X, UseFPUFlag, Options.Flag);
+			options.Add(0X, "noInitLocals", Options.Flag);
+			DefineOptions^(options);
+		END DefineOptions;
+
+		PROCEDURE GetOptions(options: Options.Options);
+		BEGIN
+			IF options.GetFlag(UseFPUFlag) THEN useFPU := TRUE END;
+			IF options.GetFlag("noInitLocals") THEN initLocals := FALSE END;
+			GetOptions^(options);
+		END GetOptions;
+
+		PROCEDURE DefaultObjectFileFormat(): Formats.ObjectFileFormat;
+		BEGIN RETURN ObjectFileFormat.Get();
+		END DefaultObjectFileFormat;
+
+		PROCEDURE DefaultSymbolFileFormat(): Formats.SymbolFileFormat;
+		BEGIN RETURN NIL
+		END DefaultSymbolFileFormat;
+
+		(** get the name of the backend **)
+		PROCEDURE GetDescription(VAR instructionSet: ARRAY OF CHAR);
+		BEGIN instructionSet := "ARM"
+		END GetDescription;
+
+		PROCEDURE FindPC(x: SyntaxTree.Module; CONST sectionName: ARRAY OF CHAR; sectionOffset: LONGINT);
+		VAR
+			section: Sections.Section; binarySection: BinaryCode.Section; label: BinaryCode.LabelList; module: Formats.GeneratedModule;
+			i: LONGINT; pooledName: Basic.SegmentedName;
+		BEGIN
+			module := ProcessSyntaxTreeModule(x);
+			Basic.ToSegmentedName(sectionName, pooledName);
+			i := 0;
+			REPEAT
+				section := module(Sections.Module).allSections.GetSection(i);
+				INC(i);
+			UNTIL (i = module(Sections.Module).allSections.Length()) OR (section.name = pooledName);
+
+			IF section.name # pooledName THEN
+				diagnostics.Error(module.module.sourceName,Diagnostics.Invalid,Diagnostics.Invalid," could not locate pc");
+			ELSE
+				binarySection := section(IntermediateCode.Section).resolved;
+				label := binarySection.labels;
+				WHILE (label # NIL) & (label.offset >= sectionOffset) DO
+					label := label.prev;
+				END;
+				IF label # NIL THEN
+					diagnostics.Information(module.module.sourceName,label.position,Diagnostics.Invalid," pc position");
+				ELSE
+					diagnostics.Error(module.module.sourceName,Diagnostics.Invalid,Diagnostics.Invalid," could not locate pc");
+				END;
+			END;
+		END FindPC;
+
+	END BackendARM;
+
+VAR
+	emptyOperand: Operand;
+
+	PROCEDURE Assert(condition: BOOLEAN; CONST message: ARRAY OF CHAR);
+	BEGIN ASSERT(condition, 100)
+	END Assert;
+
+	PROCEDURE Halt(CONST message: ARRAY OF CHAR);
+	BEGIN HALT(100)
+	END Halt;
+
+	PROCEDURE PowerOf2(val: HUGEINT;  VAR exp: LONGINT): BOOLEAN;
+	BEGIN
+		IF val <= 0 THEN RETURN FALSE END;
+		exp := 0;
+		WHILE ~ODD(val) DO
+			val := val DIV 2;
+			INC(exp)
+		END;
+		RETURN val = 1
+	END PowerOf2;
+
+	(** get the ARM code section that corresponds to an intermediate code section **)
+	PROCEDURE ResolvedSection(irSection: IntermediateCode.Section): BinaryCode.Section;
+	VAR
+		result: BinaryCode.Section;
+	BEGIN
+		IF irSection.resolved = NIL THEN
+			NEW(result, irSection.type, irSection.priority, 8, irSection.name, irSection.comments # NIL, FALSE);
+
+			(* set fixed position or alignment
+			(also make sure that any section has an alignment of at least 4 bytes) *)
+			IF ~irSection.fixed & (irSection.positionOrAlignment < 4) THEN
+				result.SetAlignment(FALSE, 4)
+			ELSE
+				result.SetAlignment(irSection.fixed, irSection.positionOrAlignment);
+			END;
+
+			irSection.SetResolved(result)
+		ELSE
+			result := irSection.resolved
+		END;
+		RETURN result
+	END ResolvedSection;
+
+	(** initialize the module **)
+	PROCEDURE Init;
+	BEGIN InstructionSet.InitOperand(emptyOperand)
+	END Init;
+
+	(** get an instance of the ARM backend **)
+	PROCEDURE Get*(): Backend.Backend;
+	VAR
+		result: BackendARM;
+	BEGIN
+		NEW(result);
+		RETURN result
+	END Get;
+
+	(* only for testing purposes *)
+	PROCEDURE Test*;
+	VAR
+		codeGenerator: CodeGeneratorARM;
+		value, count: LONGINT;
+	BEGIN
+		NEW(codeGenerator, "", NIL, NIL);
+		FOR value := 0 TO 300 BY 1 DO
+			count := codeGenerator.ValueComposition(value, FALSE, emptyOperand);
+			D.String("value: "); D.Int(value, 0); D.String(" -> "); D.Int(count, 0); D.String(" instructions"); D.Ln;
+		END;
+		D.Ln; D.Update
+	END Test;
+
+	(* TODO: move this to Debugging.Mod or even Streams.Mod *)
+
+	(** write an integer in binary right-justified in a field of at least ABS(w) characters.
+		If w < 0 THEN ABS(w) least significant hex digits of 'value' are written (potentially including leading zeros or ones)
+	**)
+	PROCEDURE DBin*(value: HUGEINT; numberDigits: LONGINT);
+	CONST
+		MaxBitSize = SIZEOF(HUGEINT) * 8;
+	VAR
+		i, firstRelevantPos: LONGINT;
+		prefixWithSpaces: BOOLEAN;
+		chars: ARRAY MaxBitSize OF CHAR;
+		prefixChar: CHAR;
+	BEGIN
+		prefixWithSpaces := numberDigits >= 0;
+		numberDigits := ABS(numberDigits);
+
+		(*
+		- calculate an array containing the full bitstring
+		- determine the position of the first relevant digit
+		*)
+		firstRelevantPos := 0;
+		FOR i := MaxBitSize - 1 TO 0 BY -1 DO
+			IF ODD(value) THEN
+				chars[i] := '1';
+				firstRelevantPos := i (* occurence of a '1' -> changes the first relevant position *)
+			ELSE
+				chars[i] := '0'
+			END;
+			value := value DIV 2
+		END;
+
+		(* if space prefixing is enabled, limit the number of digits to the relevant digits *)
+		IF prefixWithSpaces THEN numberDigits := MAX(numberDigits, MaxBitSize - firstRelevantPos) END;
+
+		IF numberDigits > MaxBitSize THEN
+			IF prefixWithSpaces THEN prefixChar := ' ' ELSE prefixChar := chars[0] END; (* use spaces or sign bit *)
+			FOR i := 1 TO numberDigits - MaxBitSize DO D.Char(prefixChar) END;
+			numberDigits := MaxBitSize
+		END;
+
+		ASSERT((numberDigits >= 0) & (numberDigits <= MaxBitSize));
+		FOR i := MaxBitSize - numberDigits TO MaxBitSize - 1 DO
+			IF prefixWithSpaces & (i < firstRelevantPos) THEN D.Char(' ') ELSE D.Char(chars[i]) END
+		END;
+		D.Ln;
+	END DBin;
+
+BEGIN
+	Init;
+
+END FoxARMBackend.

+ 2911 - 0
source/FoxARMInstructionSet.Mod

@@ -0,0 +1,2911 @@
+MODULE FoxARMInstructionSet; (** AUTHOR ""; PURPOSE ""; *)
+
+IMPORT D := Debugging, Commands, Streams, Files, BinaryCode := FoxBinaryCode, Disassembler := FoxDisassembler, BitSets, Options, Strings, ObjectFile;
+
+CONST
+	TraceDecode= FALSE;
+
+	None*= -1;
+
+	(* condition codes, values refer to encoding, do not modify *)
+	conditionEQ*= 0;
+	conditionNE*= 1;
+	conditionCS*= 2;
+	conditionHS*= 2;
+	conditionCC*= 3;
+	conditionLO*= 3;
+	conditionMI*= 4;
+	conditionPL*= 5;
+	conditionVS*= 6;
+	conditionVC*= 7;
+	conditionHI*= 8;
+	conditionLS*= 9;
+	conditionGE*= 10;
+	conditionLT*= 11;
+	conditionGT*= 12;
+	conditionLE*= 13;
+	conditionAL*= 14;
+	unconditional*= conditionAL;
+	conditionNV*= 15;
+	NumberConditionEntries*= 18;
+
+	(* indexing flags, values have no meaning in encoding *)
+	Increment*= 0;
+	Decrement*= 1;
+	PostIndexed*= 2;
+	PreIndexed*= 3;
+
+	(* mnemonics, values have no meaning for encoding
+		FoxProgTools.Enum -l=4  opADC opADD opAND opB opBIC opBKPT opBL opBLX opBX opCDP opCDP2
+		opCLZ opCMN opCMP opEOR
+		opFABSD opFABSS opFADDD opFADDS opFCMPD
+		opFCMPED opFCMPES opFCMPEZD opFCMPEZS
+		opFCMPS opFCMPZD opFCMPZS opFCPYD opFCPYS opFCVTDS opFCVTSD
+		opFDIVD opFDIVS opFLDD
+		opFLDMIAD opFLDMIAS opFLDMIAX opFLDMDBD opFLDMDBS opFLDMDBX
+		opFLDS
+		opFMACD opFMACS opFMDHR opFMDLR opFMRDH opFMRDL opFMRS opFMRX
+		opFMSCD opFMSCS opFMSR opFMSTAT opFMULD opFMULS opFMXR opFNEGD opFNEGS
+		opFNMACD opFNMACS opFNMSCD opFNMSCS opFNMULD opFNMULS
+		opFSITOD opFSITOS opFSQRTD opFSQRTS opFSTD opFSTMIAD opFSTMIAS opFSTMIAX opFSTMDBD opFSTMDBS opFSTMDBX
+		opFSTS opFSUBD opFSUBS opFTOSID opFTOSIZD opFTOSIS opFTOSIZS opFTOUID opFTOUIZD opFTOUIS opFTOUIZS opFUITOD
+		opFUITOS
+		opLDC opLDC2 opLDM opLDR opMCR opMCR2
+		opMCRR
+		opMLA opMOV opMRC opMRC2 opMRRC
+		opMRS opMSR opMUL opMVN opORR opPLD opQADD opQDADD opQDSUB opQSUB
+		opRSB opRSC opSBC opSMLABB opSMLABT
+		opSMLAL
+		opSMLATB opSMLATT
+		opSMLALBB opSMLALBT opSMLALTB opSMLALTT
+		opSMLAWB opSMLAWT
+		opSMULBB opSMULBT opSMULTB opSMULTT
+		opSMULWB opSMULWT opSMULL
+		opSTC opSTC2
+		opSTM opSTR opSUB opSWI opSWP opTEQ opTST opUMLAL opUMULL
+
+		NumberMnemonics ~
+	*)
+
+	opADC*= 0; opADD*= 1; opAND*= 2; opB*= 3;
+	opBIC*= 4; opBKPT*= 5; opBL*= 6; opBLX*= 7;
+	opBX*= 8; opCDP*= 9; opCDP2*= 10; opCLZ*= 11;
+	opCMN*= 12; opCMP*= 13; opEOR*= 14; opFABSD*= 15;
+	opFABSS*= 16; opFADDD*= 17; opFADDS*= 18; opFCMPD*= 19;
+	opFCMPED*= 20; opFCMPES*= 21; opFCMPEZD*= 22; opFCMPEZS*= 23;
+	opFCMPS*= 24; opFCMPZD*= 25; opFCMPZS*= 26; opFCPYD*= 27;
+	opFCPYS*= 28; opFCVTDS*= 29; opFCVTSD*= 30; opFDIVD*= 31;
+	opFDIVS*= 32; opFLDD*= 33; opFLDMIAD*= 34; opFLDMIAS*= 35;
+	opFLDMIAX*= 36; opFLDMDBD*= 37; opFLDMDBS*= 38; opFLDMDBX*= 39;
+	opFLDS*= 40; opFMACD*= 41; opFMACS*= 42; opFMDHR*= 43;
+	opFMDLR*= 44; opFMRDH*= 45; opFMRDL*= 46; opFMRS*= 47;
+	opFMRX*= 48; opFMSCD*= 49; opFMSCS*= 50; opFMSR*= 51;
+	opFMSTAT*= 52; opFMULD*= 53; opFMULS*= 54; opFMXR*= 55;
+	opFNEGD*= 56; opFNEGS*= 57; opFNMACD*= 58; opFNMACS*= 59;
+	opFNMSCD*= 60; opFNMSCS*= 61; opFNMULD*= 62; opFNMULS*= 63;
+	opFSITOD*= 64; opFSITOS*= 65; opFSQRTD*= 66; opFSQRTS*= 67;
+	opFSTD*= 68; opFSTMIAD*= 69; opFSTMIAS*= 70; opFSTMIAX*= 71;
+	opFSTMDBD*= 72; opFSTMDBS*= 73; opFSTMDBX*= 74; opFSTS*= 75;
+	opFSUBD*= 76; opFSUBS*= 77; opFTOSID*= 78; opFTOSIZD*= 79;
+	opFTOSIS*= 80; opFTOSIZS*= 81; opFTOUID*= 82; opFTOUIZD*= 83;
+	opFTOUIS*= 84; opFTOUIZS*= 85; opFUITOD*= 86; opFUITOS*= 87;
+	opLDC*= 88; opLDC2*= 89; opLDM*= 90; opLDR*= 91;
+	opMCR*= 92; opMCR2*= 93; opMCRR*= 94; opMLA*= 95;
+	opMOV*= 96; opMRC*= 97; opMRC2*= 98; opMRRC*= 99;
+	opMRS*= 100; opMSR*= 101; opMUL*= 102; opMVN*= 103;
+	opORR*= 104; opPLD*= 105; opQADD*= 106; opQDADD*= 107;
+	opQDSUB*= 108; opQSUB*= 109; opRSB*= 110; opRSC*= 111;
+	opSBC*= 112; opSMLABB*= 113; opSMLABT*= 114; opSMLAL*= 115;
+	opSMLATB*= 116; opSMLATT*= 117; opSMLALBB*= 118; opSMLALBT*= 119;
+	opSMLALTB*= 120; opSMLALTT*= 121; opSMLAWB*= 122; opSMLAWT*= 123;
+	opSMULBB*= 124; opSMULBT*= 125; opSMULTB*= 126; opSMULTT*= 127;
+	opSMULWB*= 128; opSMULWT*= 129; opSMULL*= 130; opSTC*= 131;
+	opSTC2*= 132; opSTM*= 133; opSTR*= 134; opSUB*= 135;
+	opSWI*= 136; opSWP*= 137; opTEQ*= 138; opTST*= 139;
+	opUMLAL*= 140; opUMULL*= 141;
+	(* Usual mnemonics, cont'd *)
+	opISB* = 142;
+
+	(* NEON mnemonics *)
+	opVADD* = 143;
+	opVADDL* = 144;
+	opVADDW* = 145;
+	opVMUL* = 146;
+	opVMULL* = 147;
+	opVMSR* = 148;
+	opVMRS* = 149;
+	opVLDR* = 150;
+	opVSTR* = 151;
+	opVDIV* = 152;
+	opVMLA* = 153;
+	opVMLS* = 154;
+	opVMIN* = 155;
+	opVMAX* = 156;
+	opVSUB* = 157;
+	opVABS* = 158;
+	opVABD* = 159;
+	
+	(* Usual shifts *)
+	opLSL* = 160;
+	opLSR* = 161;
+
+	(* More NEON *)
+	opVLD1* = 162;
+	opVST1* = 163;
+	opVPADD* = 164;
+	opVMOV* = 165;
+	
+	(* Non NEON Instructions *)
+	opSEV* = 166;
+	opDSB* = 167;
+	opLDREX* = 168;
+	opSTREX* = 169;
+	opADR* = 170;
+	opLDREXB* = 171;
+	opSTREXB* = 172;
+	opDMB* = 173;
+	opCLREX* = 174;
+	opREV* = 175;
+	opREV16* = 176;
+	opUXTH* = 177;
+	opWFE* = 178;
+
+	NumberMnemonics*= 179;
+
+	MaxOperands* = 6;
+
+	(* flags values have no meaning for encoding
+	FoxProgTools.Enum -l= 4
+		flagB flagBT flagD flagDA flagDB flagH flagIA flagIB flagL flagS flagSB flagSH flagT
+		NumberFlags flagCondition flagUserMode flagBaseRegisterUpdate ~
+	*)
+	flagB*= 0; flagBT*= 1; flagD*= 2; flagDA*= 3;
+	flagDB*= 4; flagH*= 5; flagIA*= 6; flagIB*= 7;
+	flagL*= 8; flagS*= 9; flagSB*= 10; flagSH*= 11;
+	flagT*= 12;
+	(*NumberFlags*= 13;*)
+	(* NEON data type flags *)
+	flagNEON8bits = 13; flagNEON16bits = 14; flagNEON32bits = 15; flagNEON64bits = 16;
+	flagNEONInt = 17; flagNEONSigned = 18; flagNEONUnsigned = 19;
+	flagNEONFloat = 20; flagNEONPoly = 21; flagNEONUndef = 22;
+
+	NumberFlags* = 23;
+
+	(* Flags not used *)
+	flagCondition*= 23; flagUserMode*= 24;
+	flagBaseRegisterUpdate*= 25;	
+
+	NumberInstructions= NumberMnemonics + 19;
+
+	(* encoding types, values have no meaning for encoding
+		FoxProgTools.Enum -l=4
+			encodingR16 encodingR12 encodingR8 encodingR0
+			encodingAddressingMode1
+			encodingAddressingMode2
+			encodingAddressingMode3
+			encodingAddressingMode5
+			encodingCoprocessor encodingCR0 encodingCR12 encodingCR16
+			encodingOpcode20 encodingOpcode21 encodingOpcode5 encodingOpcode4 encodingSignedImm24 encodingImm24 encodingImm16 encodingRotImm8 encodingRegisterList
+			encodingPSR encodingFields
+			encodingDR0 encodingDR12 encodingDR16
+			encodingFR0 encodingFR12 encodingFR16
+			encodingDRegisterList encodingFRegisterList encodingAddressingMode5V
+			~
+	*)
+	encodingR16= 0; encodingR12= 1; encodingR8= 2; encodingR0= 3;
+	encodingAddressingMode1= 4; encodingAddressingMode2= 5; encodingAddressingMode3= 6; encodingAddressingMode5= 7;
+	encodingCoprocessor= 8; encodingCR0= 9; encodingCR12= 10; encodingCR16= 11;
+	encodingOpcode20= 12; encodingOpcode21= 13; encodingOpcode5= 14; encodingOpcode4= 15;
+	encodingSignedImm24= 16; encodingImm24= 17; encodingImm16= 18; encodingRotImm8= 19;
+	encodingRegisterList= 20; encodingPSR= 21; encodingFields= 22; encodingDR0= 23;
+	encodingDR12= 24; encodingDR16= 25; encodingFR0= 26; encodingFR12= 27;
+	encodingFR16= 28; encodingDRegisterList= 29; encodingFRegisterList= 30; encodingAddressingMode5V= 31;
+
+	(* NEON operand encoding. *)
+	encodingNEONQd = 32; encodingNEONQn = 33; encodingNEONQm = 34;
+	encodingNEONDd = 35; encodingNEONDn = 36; encodingNEONDm = 37;
+	encodingNEONSd = 38; encodingNEONSn = 39; encodingNEONSm = 40;
+	encodingNEONImmAndSize = 41; encodingNEON8bitImm = 42; (*at bit 0 *)
+	encodingNEON3bitImm = 43;
+	encodingNEONQorDd = 44; encodingNEONQorDn = 45; encodingNEONQorDm = 46;
+	encodingNEONDorSd = 47; encodingNEONDorSn = 48; encodingNEONDorSm = 49;
+	encodingNEONDRegList = 50; encodingNEONSysReg = 51; encodingNEONSigned8bitImm = 52;
+	
+	encodingImm7to11 = 53;
+
+	(* NEON instruction mode encoding. These modes are used in EnterNEONInstruction. Values do not matter for encoding.
+		The values mean (see the ARM manual):
+		encodeNEON...
+			...3RegSame: 3 registers of the same type
+			...3RegDiff: 3 registers of different types
+			...2RegScalar: 2 registers and a scalar
+			...2RegShift: 2 registers and a shift
+			...2RegMisc: 2 registers miscellanous
+			...1RegImm: 1 register and an immediate
+			...VFP: VFP operation
+			...LS: load and stores
+			...Tx32: transfers up to 32bits
+			...Tx64: transfers of 64bits
+	encodeNEON3RegSame = 0; encodeNEON3RegLong = 1; encodeNEON3RegNarrow = 2; encodeNEON3RegWide = 3;
+	encodeNEON2RegScalar = 4; encodeNEON2RegScalarLong = 5;
+	encodeNEON2RegShift = 6; encodeNEON2RegShiftNarrow = 7; encodeNEON2RegShiftLong = 8;
+	encodeNEON2RegMisc = 9; encodeNEON2RegMiscNarrow = 10; encodeNEON2RegMiscLong = 11;
+	encodeNEONVFP = 12; encodeNEONVFP2Reg = 13; encodeNEONVFP1Reg = 14;*)
+
+	(* operand modes
+		FoxProgTools.Enum -l= 4 modeImmediate modeRegister modeMemory	modeOpcode modeCoprocessor
+			modeRegisterList modeFields modeOption
+			~
+	*)
+	modeImmediate*= 0; modeRegister*= 1; modeMemory*= 2; modeOpcode*= 3;
+	modeCoprocessor*= 4; modeRegisterList*= 5; modeRegisterWithFields*= 6; modeOption*= 7;
+
+	(* registers, values have no meaning for encoding *)
+
+	(* regular registers: *)
+	R0*= 0; R1*= R0+1; R2*= R0+2; R3*= R0+3; R4*= R0+4; R5*= R0+5; R6*= R0+6; R7*= R0+7;
+	R8*= R0+8; R9*= R0+9; R10*= R0+10; R11*= R0+11; R12*= R0+12; R13*= R0+13; R14*= R0+14; R15*= R0+15;
+
+	(* coprocessor registers: *)
+	CR0*= 16; CR1*= CR0+1; CR2*= CR0+2; CR3*= CR0+3; CR4*= CR0+4; CR5*= CR0+5; CR6*= CR0+6; CR7*= CR0+7;
+	CR8*= CR0+8; CR9*= CR0+9; CR10*= CR0+10; CR11*= CR0+11; CR12*= CR0+12; CR13*= CR0+13; CR14*= CR0+14; CR15*= CR0+15;
+
+	(* VFP double-precision registers: *)
+	DR0*= 32; DR1*= DR0+1; DR2*= DR0+2; DR3*= DR0+3; DR4*= DR0+4; DR5*= DR0+5; DR6*= DR0+6; DR7*= DR0+7;
+	DR8*= DR0+8; DR9*= DR0+9; DR10*= DR0+10; DR11*= DR0+11; DR12*= DR0+12; DR13*= DR0+13; DR14*= DR0+14; DR15*= DR0+15;
+	(* Advanced SIMD doubleword registers *)
+	DR16*= DR0+16; DR17*= DR0+17; DR18*= DR0+18; DR19*= DR0+19; DR20*= DR0+20; DR21*= DR0+21; DR22*= DR0+22;
+	DR23*= DR0+23; DR24*= DR0+24; DR25*= DR0+25; DR26*= DR0+26; DR27*= DR0+27; DR28*= DR0+28; DR29*= DR0+29;
+	DR30*= DR0+30; DR31*= DR0+31;
+
+	(* VFP single-precision registers: *)
+	SR0*= 64; SR1*= SR0+1; SR2*= SR0+2; SR3*= SR0+3; SR4*= SR0+4; SR5*= SR0+5; SR6*= SR0+6; SR7*= SR0+7;
+	SR8*= SR0+8; SR9*= SR0+9; SR10*= SR0+10; SR11*= SR0+11; SR12*= SR0+12; SR13*= SR0+13; SR14*= SR0+14; SR15*= SR0+15;
+
+	(* progran status registers *)
+	CPSR*= 80; SPSR*= 81;
+
+	(* Continue at 82 to skip CPSR and SPSR *)
+	SR16*= 82; SR17*= SR0+17; SR18*= SR0+18; SR19*= SR0+19; SR20*= SR0+20; SR21*= SR0+21; SR22*= SR0+22; SR23*= SR0+23;
+	SR24*= SR0+24; SR25*= SR0+25; SR26*= SR0+26; SR27*= SR0+27; SR28*= SR0+28; SR29*= SR0+29; SR30*= SR0+30; SR31*= SR0+31;
+	
+	(* NEON SIMD quadword registers *)
+	QR0* = 98; QR1* = QR0 + 1; QR2* = QR0 + 2;QR3* = QR0 + 3; QR4* = QR0 + 4; QR5* = QR0 + 5; QR6* = QR0 + 6;
+	QR7* = QR0 + 7; QR8* = QR0 + 8; QR9* = QR0 + 9; QR10* = QR0 + 10; QR11* = QR0 + 11; QR12* = QR0 + 12;
+	QR13* = QR0 + 13; QR14* = QR0 + 14; QR15* = QR0 + 15;
+
+	(* NEON System Registers *)
+	FPSID* = 114; FPSCR* = 115; FPEXC* = 116;
+
+	(* register aliases *)
+	PC*= R15; LR*= R14; SP*= R13; FP*= R12; RESHI *= R1; RES *= R0; RESFS *= SR0;
+
+	NumberRegisters*= 117; (* the number of physical registers *)
+	NumberRegisterEntries*= 123; (* the number of register names *)
+
+	(* shifts, do not modify:  nunbers as in encoding*)
+	shiftLSL*= 0;
+	shiftLSR*= 1;
+	shiftASR*= 2;
+	shiftROR*= 3;
+	shiftRRX*= 4;
+	NumberShifts*= 5;
+
+	(* coprocessors, do not modify: number as in encoding *)
+	CP0* = 0; CP1* = 1; CP2* = 2; CP3* = 3; CP4* = 4; CP5* = 5; CP6* = 6; CP7* = 7;
+	CP8* = 8; CP9* = 9; CP10* = 10; CP11* = 11; CP12* = 12; CP13* = 13; CP14* = 14; CP15* = 15;
+	NumberCoprocessors* = 16;
+
+	(* fields, do not modify: number order as in encoding *)
+	fieldC* = 0;
+	fieldX* = 1;
+	fieldS* = 2;
+	fieldF* = 3;
+
+	Bits12*=4096;
+	Bits10*=1024;
+	Bits7*=128;
+	Bits5*=32;
+
+	(*
+	ArmEncoding *= 0;
+	ThumbEncoding *= 1;
+	*)
+
+	TYPE
+		Name = ARRAY 10 OF CHAR;
+
+		Entry = RECORD
+			name: Name;
+			number: LONGINT
+		END;
+
+		Mnemonic= RECORD
+			name: Name;
+			number: INTEGER;
+			firstInstructionFormat, lastInstructionFormat: LONGINT; (* instructionFormats *)
+		END;
+
+		InstructionFormat= RECORD
+			mnemonic: INTEGER;
+			opcode, mask: SET;
+			flags: SET;
+			operands: ARRAY MaxOperands OF INTEGER;
+			(* NEON specific *)
+			isNEON: BOOLEAN;
+			Unsigned, Quadword, Length, Float, Operation: SET;
+			SizeH: INTEGER; (* The lower bit is SizeH - 1 *)
+		END;
+
+		Instruction*= RECORD
+			format: LONGINT;
+			condition: LONGINT;
+			flags: SET;
+			operands: ARRAY  MaxOperands OF Operand;
+		END;
+
+		Operand*= RECORD
+			mode*: INTEGER;
+			register*: LONGINT;
+			immediate*, shiftImmediate: LONGINT;
+			shiftRegister: LONGINT;
+			shiftMode: INTEGER;
+			offsetImmediate: LONGINT;
+			offsetRegister: LONGINT;
+			indexing: SET; (* Increment, Decrement, PostIndexed PreIndexed*)
+			registerList: SET;
+			coprocessor: INTEGER;
+			opcode: LONGINT;
+			fields: SET;
+			option: LONGINT;
+			fixup-: BinaryCode.Fixup;
+		END;
+
+	VAR
+		mnemonics: ARRAY NumberMnemonics OF Mnemonic;
+		registerEntries: ARRAY NumberRegisterEntries OF Entry;
+		conditionEntries: ARRAY NumberConditionEntries OF Entry;
+		flagNames: ARRAY NumberFlags OF Name;
+		shiftNames: ARRAY NumberShifts OF Name;
+		coprocessorNames: ARRAY NumberCoprocessors OF Name;
+		instructionFormats : ARRAY NumberInstructions OF InstructionFormat;
+
+	(** get the number of a register by its name
+		- 'None' is returned if no such register exists
+	**)
+	PROCEDURE RegisterNumberFromName*(registerName: ARRAY OF CHAR): LONGINT;
+	VAR
+		result, i: LONGINT;
+	BEGIN
+		Strings.UpperCase(registerName);
+		result := None;
+		(* go through all registers *)
+		FOR i := 0 TO NumberRegisterEntries - 1 DO
+			IF registerEntries[i].name = registerName THEN result := registerEntries[i].number END
+		END;
+		RETURN result
+	END RegisterNumberFromName;
+
+	(** get the number of a coprocessor by its name
+		- 'None' is returned if no such mode exists
+	**)
+	PROCEDURE CoprocessorNumberFromName*(coprocessorName: ARRAY OF CHAR): LONGINT;
+	VAR
+		result, i: LONGINT;
+	BEGIN
+		Strings.UpperCase(coprocessorName);
+		result := None;
+		(* go through all coprocessors *)
+		FOR i := 0 TO NumberCoprocessors - 1 DO
+			IF coprocessorNames[i] = coprocessorName THEN result := i END
+		END;
+		RETURN result
+	END CoprocessorNumberFromName;
+
+	(** get the number of a shift mode by its name
+		- 'None' is returned if no such mode exists
+	**)
+	PROCEDURE ShiftModeNumberFromName*(shiftModeName: ARRAY OF CHAR): LONGINT;
+	VAR
+		result, i: LONGINT;
+	BEGIN
+		Strings.UpperCase(shiftModeName);
+		result := None;
+		(* go through all shift modes *)
+		FOR i := 0 TO NumberShifts - 1 DO
+			IF shiftNames[i] = shiftModeName THEN result := i END
+		END;
+		RETURN result
+	END ShiftModeNumberFromName;
+
+	PROCEDURE FindMnemonic*(CONST name: ARRAY OF CHAR; VAR mnemonic, condition: LONGINT; VAR flags: SET): BOOLEAN;
+	VAR i, pos, flagPos, format: LONGINT;
+
+		PROCEDURE Contains(CONST this: ARRAY OF CHAR): BOOLEAN;
+		VAR i: LONGINT;
+		BEGIN
+			ASSERT(this # "");
+			i := 0;
+			WHILE (name[pos+i] # 0X) & (this[i] # 0X) & (CAP(name[i+pos])= this[i]) DO INC(i) END;
+			IF this[i]= 0X THEN
+				INC(pos, i);
+				RETURN TRUE
+			ELSE
+				RETURN FALSE
+			END;
+		END Contains;
+
+	BEGIN
+		ASSERT(name # "");
+		mnemonic := NumberMnemonics;
+		REPEAT
+			DEC(mnemonic);
+			flags := {};
+			pos := 0;
+			(* find matching mnemonic, not necessarily unique! *)
+			WHILE (mnemonic >= 0) & ~Contains(mnemonics[mnemonic].name) DO DEC(mnemonic) END;
+			IF mnemonic >= 0 THEN
+				flagPos := pos;
+				format := mnemonics[mnemonic].firstInstructionFormat;
+				(* found mnemonic, check if flags match with at least one format *)
+				REPEAT
+					ASSERT(instructionFormats[format].mnemonic = mnemonic);
+					pos := flagPos;
+					(* conditions *)
+					IF flagCondition IN instructionFormats[format].flags THEN
+						(* read condition *)
+						i := NumberConditionEntries-1;
+						WHILE (i>= 0) &~Contains(conditionEntries[i].name) DO DEC(i) END;
+						IF i # -1 THEN
+							INCL(flags, flagCondition);
+							condition := conditionEntries[i].number
+						ELSE
+							condition := conditionAL (* no condition: always execute *)
+						END;
+					ELSE
+						condition := conditionAL
+					END;
+					(* read flags, have to go downward because of name inclusions  *)
+					i := NumberFlags-1;
+					WHILE (i>=0) & (name[pos] # 0X) DO
+						IF (i IN instructionFormats[format].flags) & Contains(flagNames[i]) THEN INCL(flags, i) END;
+						DEC(i);
+					END;
+					INC(format);
+				UNTIL (format > mnemonics[mnemonic].lastInstructionFormat) OR (name[pos] = 0X)
+			END;
+		UNTIL (mnemonic = None) OR (name[pos] = 0X);
+
+		RETURN (mnemonic # None)
+	END FindMnemonic;
+
+	PROCEDURE InitInstruction*(VAR instruction: Instruction);
+	VAR i : LONGINT;
+	BEGIN
+		instruction.format := None;
+		instruction.condition := None;
+		instruction.flags := {};
+		FOR i := 0 TO LEN(instruction.operands)-1 DO
+			InitOperand(instruction.operands[i]);
+		END;
+	END InitInstruction;
+	
+	(* generate immediate operand with fixup *)
+	PROCEDURE InitFixup*(VAR operand: Operand; bits: SHORTINT; fixup: BinaryCode.Fixup (*symbol: Sections.Section; offset, displacement: LONGINT *));
+	BEGIN
+		operand.mode := modeImmediate;
+		operand.immediate := 0;
+		operand.fixup := fixup;
+		(*
+		operand.fixup := BinaryCode.NewFixup(BinaryCode.Absolute, 0, symbol, offset, displacement, 0, NIL);
+		*)
+		(*
+		operand.symbol := symbol;
+		operand.symbolOffset := offset;
+		operand.displacement := displacement;
+		*)
+	END InitFixup;
+
+	PROCEDURE AddFixup*(VAR operand: Operand; fixup: BinaryCode.Fixup);
+	BEGIN
+		ASSERT(operand.mode IN {modeImmediate, modeMemory});
+		operand.fixup := fixup
+	END AddFixup;
+
+	PROCEDURE InitOperand*(VAR operand: Operand);
+	BEGIN
+		operand.mode := None;
+		operand.register := None;
+		operand.immediate := 0;
+		operand.shiftImmediate := 0;
+		operand.shiftRegister := None;
+		operand.offsetRegister := None;
+		operand.shiftMode := None;
+		operand.offsetImmediate := 0;
+		operand.indexing := {};
+		operand.registerList := {};
+		operand.coprocessor := None;
+		operand.opcode := 0;
+		operand.fields := {};
+		operand.fixup := NIL;
+	END InitOperand;
+
+	PROCEDURE InitRegister*(VAR operand: Operand; registerNumber: LONGINT; shiftMode: LONGINT; shiftAmountRegisterNumber: LONGINT; shiftImmediate: LONGINT);
+	BEGIN
+		InitOperand(operand);
+		operand.mode := modeRegister;
+		operand.register := SHORT(registerNumber);
+		operand.shiftMode := SHORT(shiftMode);
+		operand.shiftRegister := SHORT(shiftAmountRegisterNumber);
+		operand.shiftImmediate := shiftImmediate
+	END InitRegister;
+
+	PROCEDURE InitImmediate*(VAR operand: Operand; immediate: LONGINT);
+	BEGIN
+		InitOperand(operand); operand.mode := modeImmediate;
+		operand.immediate := immediate;
+	END InitImmediate;
+
+	(* note that this is a memory operand to be used in a load/store instruction *)
+	PROCEDURE InitImmediateOffsetMemory*(VAR operand: Operand; register: LONGINT; offset: LONGINT; indexing: SET);
+	BEGIN
+		InitOperand(operand); operand.mode := modeMemory;
+		operand.register := SHORT(register);
+		operand.offsetImmediate := offset;
+		operand.indexing := indexing;
+	END InitImmediateOffsetMemory;
+
+	(* note that this is a memory operand to be used in a load/store instruction *)
+	PROCEDURE InitRegisterOffsetMemory*(VAR operand: Operand; register, offsetRegister: LONGINT; shiftMode: LONGINT; shiftImmediate: LONGINT; indexing: SET);
+	BEGIN
+		InitOperand(operand); operand.mode := modeMemory;
+		operand.register := SHORT(register);
+		operand.offsetRegister := SHORT(offsetRegister);
+		operand.indexing := indexing;
+		IF operand.shiftImmediate < 0 THEN
+			operand.shiftImmediate := -operand.shiftImmediate;
+			INCL(operand.indexing, Decrement);
+			EXCL(operand.indexing, Increment);
+		END;
+		operand.shiftMode := SHORT(shiftMode);
+		operand.shiftImmediate := shiftImmediate;
+	END InitRegisterOffsetMemory;
+
+	PROCEDURE NewRegister*(registerNumber: LONGINT; shiftMode: LONGINT; shiftAmountRegisterNumber: LONGINT; shiftImmediate: LONGINT): Operand;
+	VAR
+		result: Operand;
+	BEGIN
+		InitRegister(result, registerNumber, shiftMode, shiftAmountRegisterNumber, shiftImmediate);
+		RETURN result
+	END NewRegister;
+
+	PROCEDURE NewImmediate*(immediate: LONGINT): Operand;
+	VAR
+		result: Operand;
+	BEGIN
+		InitImmediate(result, immediate);
+		RETURN result
+	END NewImmediate;
+
+	PROCEDURE NewImmediateOffsetMemory*(register: LONGINT; offset: LONGINT; indexing: SET): Operand;
+	VAR
+		result: Operand;
+	BEGIN
+		InitImmediateOffsetMemory(result, register, offset, indexing);
+		RETURN result
+	END NewImmediateOffsetMemory;
+
+	PROCEDURE NewRegisterOffsetMemory*(register, offsetRegister: LONGINT; shiftMode: LONGINT; shiftImmediate: LONGINT; indexing: SET): Operand;
+	VAR
+		result: Operand;
+	BEGIN
+		InitRegisterOffsetMemory(result, register, offsetRegister, shiftMode, shiftImmediate, indexing);
+		RETURN result
+	END NewRegisterOffsetMemory;
+
+	PROCEDURE NewRegisterList*(registerBase: LONGINT; registerList: SET): Operand;
+	VAR
+		result: Operand;
+	BEGIN
+		InitRegisterList(result, registerBase, registerList);
+		RETURN result
+	END NewRegisterList;
+
+	PROCEDURE InitOption*(VAR operand: Operand; register: LONGINT; option: LONGINT);
+	BEGIN
+		InitOperand(operand); operand.mode := modeOption;
+		operand.register := SHORT(register);
+		operand.option := option;
+	END InitOption;
+
+	PROCEDURE InitCoprocessor*(VAR operand: Operand; coprocessor: LONGINT);
+	BEGIN
+		InitOperand(operand); operand.mode := modeCoprocessor;
+		operand.coprocessor := SHORT(coprocessor);
+	END InitCoprocessor;
+
+	PROCEDURE InitOpcode*(VAR operand: Operand; opcode: LONGINT);
+	BEGIN
+		InitOperand(operand); operand.mode := modeOpcode;
+		operand.opcode := opcode;
+	END InitOpcode;
+
+	PROCEDURE InitRegisterList*(VAR operand: Operand; registerBase: LONGINT; registerList: SET);
+	BEGIN
+		InitOperand(operand); operand.mode := modeRegisterList;
+		operand.register := SHORT(registerBase); operand.registerList := registerList;
+	END InitRegisterList;
+
+	PROCEDURE InitRegisterWithFields*(VAR operand: Operand; registerNumber: LONGINT; fields: SET);
+	BEGIN
+		ASSERT((registerNumber = CPSR) OR (registerNumber = SPSR));
+		InitOperand(operand);
+		operand.mode := modeRegisterWithFields;
+		operand.register := registerNumber;
+		operand.fields := fields
+	END InitRegisterWithFields;
+
+	PROCEDURE NewRegisterWithFields*(registerNumber: LONGINT; fields: SET): Operand;
+	VAR
+		result: Operand;
+	BEGIN
+		InitRegisterWithFields(result, registerNumber, fields);
+		RETURN result
+	END NewRegisterWithFields;
+
+	PROCEDURE MakeInstruction*(VAR instruction: Instruction; mnemonic, condition: LONGINT; flags: SET; CONST operands: ARRAY OF Operand): BOOLEAN;
+	VAR
+		format: InstructionFormat;
+		i, dummyEncoding: LONGINT;
+		result: BOOLEAN;
+
+		PROCEDURE OperandMatches(encoding: LONGINT; CONST operand: Operand): BOOLEAN;
+		BEGIN
+			CASE encoding OF
+				|None: RETURN operand.mode = None
+				|encodingR16, encodingR12, encodingR8, encodingR0:
+					RETURN (operand.mode = modeRegister) & (R0 <= operand.register) & (operand.register <= R15)
+				|encodingFR0, encodingFR12, encodingFR16:
+					RETURN (operand.mode = modeRegister) & (SR0 <= operand.register) & (operand.register <= SR31)
+				|encodingDR0,encodingDR12, encodingDR16:
+					RETURN (operand.mode = modeRegister) & (DR0 <= operand.register) & (operand.register <= DR15)
+				|encodingCR0, encodingCR12, encodingCR16:
+					RETURN (operand.mode = modeRegister) & (CR0 <= operand.register) & (operand.register <= CR15)
+				|encodingAddressingMode1:
+					RETURN (operand.mode = modeRegister) & (R0 <= operand.register) & (operand.register <= R15)
+							OR (operand.mode = modeImmediate)
+				|encodingAddressingMode2:
+					RETURN (operand.mode = modeMemory)
+				|encodingAddressingMode3:
+					RETURN (operand.mode = modeMemory)
+				|encodingAddressingMode5:
+					RETURN (operand.mode = modeMemory) OR (operand.mode = modeOption)
+				|encodingAddressingMode5V:
+					RETURN (operand.mode = modeMemory)
+				|encodingCoprocessor:
+					RETURN (operand.mode = modeCoprocessor)
+				|encodingFields:
+					RETURN operand.mode = modeRegisterWithFields
+				|encodingImm16,encodingSignedImm24,encodingImm24,encodingRotImm8, encodingImm7to11:
+					RETURN operand.mode = modeImmediate
+				|encodingOpcode20,encodingOpcode21,encodingOpcode5,encodingOpcode4:
+					RETURN (operand.mode = modeOpcode) OR (operand.mode = None) (* note: missing opcode operand means opcode = 0 *)
+				|encodingRegisterList, encodingDRegisterList, encodingFRegisterList:
+					RETURN operand.mode = modeRegisterList
+				|encodingPSR:
+					RETURN (operand.mode = modeRegister) & ((operand.register = CPSR) OR (operand.register = SPSR))
+				|encodingNEONQd, encodingNEONQm, encodingNEONQn:
+					RETURN (operand.mode = modeRegister) & (QR0 <= operand.register) & (operand.register <= QR15)
+				|encodingNEONDd, encodingNEONDm, encodingNEONDn:
+					RETURN (operand.mode = modeRegister) & (DR0 <= operand.register) & (operand.register <= DR31)
+				|encodingNEONSd, encodingNEONSm, encodingNEONSn:
+					RETURN (operand.mode = modeRegister) & (SR0 <= operand.register) & (operand.register <= SR31)
+				|encodingNEONQorDd, encodingNEONQorDm, encodingNEONQorDn:
+					RETURN (operand.mode = modeRegister) & (((DR0 <= operand.register) & (operand.register <= DR31))
+						OR ((QR0 <= operand.register) & (operand.register <= QR15)))
+				|encodingNEONDorSd, encodingNEONDorSm, encodingNEONDorSn:
+					RETURN (operand.mode = modeRegister) & (((DR0 <= operand.register) & (operand.register <= DR15))
+						OR ((SR0 <= operand.register) & (operand.register <= SR31)))
+				|encodingNEONImmAndSize, encodingNEON8bitImm, encodingNEON3bitImm, encodingNEONSigned8bitImm:
+					RETURN operand.mode = modeImmediate
+				|encodingNEONDRegList:
+					RETURN operand.mode = modeRegisterList
+				|encodingNEONSysReg:
+					RETURN (operand.mode = modeRegister) & (FPSID <= operand.register) & (operand.register <= FPEXC)
+			ELSE RETURN FALSE
+			END;
+		END OperandMatches;
+
+
+		PROCEDURE Matches(CONST format: InstructionFormat): BOOLEAN;
+		VAR i: LONGINT;
+		BEGIN
+			IF mnemonic # format.mnemonic THEN 	RETURN FALSE END; (* different mnemonic *)
+			IF (instruction.condition # conditionAL) & (instruction.condition # None) & ~(flagCondition IN format.flags) THEN (* not always case for encoding problems of BLX which comes in two forms *)
+				RETURN FALSE END; (* unpermitted condition *)
+			IF instruction.flags * format.flags # instruction.flags THEN
+				RETURN FALSE END; (* unpermitted flags*)
+			i := 0;
+			WHILE (i<MaxOperands) & OperandMatches(format.operands[i], instruction.operands[i]) DO
+				INC(i);
+			END;
+			RETURN i= MaxOperands;
+		END Matches;
+
+	BEGIN
+		instruction.condition := condition;
+		instruction.flags := flags;
+		FOR i := 0 TO LEN(operands)-1 DO
+			instruction.operands[i] := operands[i]
+		END;
+		FOR i := LEN(operands) TO LEN(instruction.operands)-1 DO
+			InitOperand(instruction.operands[i]);
+		END;
+
+		result := FALSE; i := mnemonics[mnemonic].firstInstructionFormat;
+		WHILE (result = FALSE) & (i<=mnemonics[mnemonic].lastInstructionFormat) DO
+			format := instructionFormats[i];
+			IF Matches(format) THEN
+				instruction.format := i;
+				result := TRUE
+			END;
+			INC(i);
+		END;
+
+		(* TODO: remove later on: *)
+		IF ~result THEN
+			D.String("error: did not find matching instruction format for the following mnemonic and operands:"); D.Ln;
+			D.String(mnemonics[mnemonic].name); D.Int(mnemonic, 0); D.Ln;
+			FOR i := 0 TO LEN(operands) - 1 DO
+				D.String("  operand #"); D.Int(i, 0); D.String(":"); D.Ln;
+				D.String("    "); DumpOperand(D.Log, instruction.operands[i]); D.Ln; D.Ln
+			END
+		END;
+
+
+
+		RETURN result
+	END MakeInstruction;
+
+	PROCEDURE NumberToSet(code: LONGINT): SET;
+	VAR i: LONGINT; set: SET;
+	BEGIN
+		ASSERT(MAX(SET) >= 31);
+		set := {};
+		FOR i := 0 TO 31 DO
+			IF ODD(code) THEN INCL(set, i) END;
+			code := code DIV 2;
+		END;
+		RETURN set
+	END NumberToSet;
+
+	PROCEDURE SetToNumber(set: SET): LONGINT;
+	VAR i, num: LONGINT;
+	BEGIN
+		ASSERT(MAX(SET) >= 31);
+		num := 0;
+		FOR i := 0 TO 31 DO
+			IF i IN set THEN INC(num, ASH(1, i)) END;
+		END;
+		RETURN num
+	END SetToNumber;
+
+	PROCEDURE RotateRight(val, rot: LONGINT): LONGINT;
+	VAR set: SET; i, dest: LONGINT;
+	BEGIN
+		set := NumberToSet(val);
+		dest := 0;
+		FOR i := 0 TO 31 DO
+			IF i IN set THEN
+				INC(dest, ASH(1, (i-rot) MOD 32));
+			END;
+		END;
+		RETURN dest
+	END RotateRight;
+
+	PROCEDURE EncodeImmediate*(imm: LONGINT; VAR val,rot: LONGINT): BOOLEAN;
+	VAR immSet: SET; i: LONGINT;
+
+		PROCEDURE RotateLeft(set: SET; rot: LONGINT): SET;
+		VAR i: LONGINT; result: SET;
+		BEGIN
+			result := {};
+			FOR i := 0 TO 31 DO
+				IF i IN set THEN INCL(result,(i+rot) MOD 32) END;
+			END;
+			RETURN result
+		END RotateLeft;
+
+	BEGIN
+		immSet := NumberToSet(imm);
+		rot := 0;
+		WHILE (rot<16) & (immSet*{8..31} # {}) DO
+			INC(rot,1); immSet := RotateLeft(immSet,2);
+		END;
+
+		val := 0;
+		FOR i := 7 TO 0 BY -1 DO
+			val := val*2;
+			IF i IN immSet THEN INC(val) END;
+		END;
+
+		RETURN rot <16
+	END EncodeImmediate;
+
+
+	PROCEDURE Encode(CONST instruction: Instruction; VAR code: LONGINT): BOOLEAN;
+	VAR
+		format: InstructionFormat; codeSet: SET; i: LONGINT; error: BOOLEAN;
+		flags: SET;
+	CONST
+		P=24; U=23; B=22; W=21; L=20; S=6; H=5;
+
+		PROCEDURE Unsigned(val: LONGINT; from,to: LONGINT);
+		VAR i: LONGINT;
+		BEGIN
+			FOR i := from TO to DO
+				IF ODD(val) THEN INCL(codeSet,i) END;
+				val := val DIV 2;
+			END;
+			IF val # 0 THEN error := TRUE END;
+
+			IF error THEN D.TraceBack END
+		END Unsigned;
+
+		(* Encode an unsigned value on a non-contiguous set of bits, specifying the order.
+			val: value to encode
+			positions: array of bit positions, from the lowest to the highest.
+		The end of the encoding region is signaled by:
+			end of the array
+			a position has value 32
+		*)
+		PROCEDURE SplittedUnsigned(val: LONGINT; positions: ARRAY OF INTEGER);
+		VAR
+			i, len: LONGINT;
+		BEGIN
+			ASSERT(LEN(positions) <= 32);
+			FOR i := 0 TO LEN(positions) - 1 DO ASSERT(positions[i] <= 32); ASSERT(positions[i] >= 0) END;
+
+			i := 0;
+			WHILE (i < LEN(positions)) & (positions[i] # 32) DO INC(i) END;
+			len := i;
+			(*D.String("Splitted unsigned length: "); D.Int(i, 0); D.Ln;*)
+			FOR i := 0 TO len - 1 DO
+				IF ODD(val) THEN INCL(codeSet, positions[i]) END;
+				val := val DIV 2;
+			END;
+			IF val # 0 THEN error := TRUE; D.TraceBack END
+		END SplittedUnsigned;
+
+		PROCEDURE Bits(val: SET; from,to: LONGINT);
+		VAR i: LONGINT;
+		BEGIN
+			FOR i := from TO to DO
+				IF i-from IN val THEN INCL(codeSet,i) END;
+			END;
+			IF val * {to-from+1..31} # {} THEN error := TRUE END;
+		END Bits;
+
+		PROCEDURE Signed(val: LONGINT; from,to: LONGINT);
+		VAR i: LONGINT; negate: BOOLEAN;
+		BEGIN
+			negate := val < 0;
+			IF negate THEN val := -val -1 END;
+			FOR i := from TO to-1 DO
+				IF ODD(val) THEN
+					IF  ~negate THEN INCL(codeSet,i) END;
+				ELSIF negate THEN
+					INCL(codeSet,i)
+				END;
+				val := val DIV 2;
+			END;
+			IF negate THEN INCL(codeSet, to) END;
+			IF (val # 0) THEN error := TRUE END;
+		END Signed;
+
+		PROCEDURE Range(set: SET; VAR first, num: LONGINT): BOOLEAN;
+		BEGIN
+			i := 0;
+			WHILE (i<32) & ~(i IN set) DO INC(i) END;
+			first := i;
+			WHILE (i<32) & (i IN set) DO INC(i) END;
+			num := i-first;
+			WHILE (i<32) & ~(i IN set) DO INC(i) END;
+			IF i<32 THEN RETURN FALSE ELSE RETURN TRUE END;
+		END Range;
+
+		(** check immediate shifts and adopt special rules **)
+		PROCEDURE CheckImmediateShifts(VAR shiftMode, shiftImmediate: LONGINT);
+		BEGIN
+			CASE shiftMode OF
+			| None:
+				(* no shift -> encode as LSL 0 *)
+				shiftMode := shiftLSL;
+				shiftImmediate := 0
+			| shiftLSL:
+				IF shiftImmediate < 0 THEN
+					error := TRUE
+				ELSIF shiftImmediate > 31 THEN
+					D.String("LSL with more than 31 bits"); D.Ln; (* note that LSL 32 is disallowed *)
+					error := TRUE
+				END
+			| shiftLSR, shiftASR:
+				IF (shiftImmediate < 0) OR (shiftImmediate > 32) THEN
+					error := TRUE
+				ELSIF shiftImmediate = 0 THEN
+					shiftMode := shiftLSL; (* note that LSR 0, ASR 0 are disallowed -> encode as LSL 0 *)
+					shiftImmediate := 0
+				ELSIF shiftImmediate = 32 THEN
+					shiftImmediate := 0 (* 32 is encoded as 0 in this case! *)
+				END
+			| shiftROR:
+				IF (shiftImmediate < 0) OR (shiftImmediate > 32) THEN
+					error := TRUE
+				ELSIF (shiftImmediate = 0) OR (shiftImmediate = 32) THEN
+					shiftMode := shiftLSL; (* note that ROR 0 has a different meaning -> encode as LSL 0 *)
+					shiftImmediate := 0
+				END
+			| shiftRRX:
+				IF shiftImmediate = 1 THEN
+					(* RRX is encoded as ROR 0 *)
+					shiftMode := shiftROR;
+					shiftImmediate := 0
+				ELSE
+					(* note that only shifts by 1 are allowed *)
+					error := TRUE
+				END
+			ELSE
+				HALT(100)
+			END
+		END CheckImmediateShifts;
+
+
+		PROCEDURE EncodeOperand(operandEncoding: LONGINT; CONST operand: Operand);
+		VAR
+			imm, rot, firstRegister, num, shiftMode, shiftImmediate: LONGINT;
+			NEONRegisterPos: ARRAY 5 OF INTEGER;
+			
+			PROCEDURE Fixup(from, to: LONGINT);
+			VAR patterns: ObjectFile.FixupPatterns; displacement: LONGINT; mode: SHORTINT;
+			BEGIN
+				NEW(patterns, 1);
+				patterns[0].offset := from; patterns[0].bits := to-from+1;
+				
+				(*ASSERT(format.mnemonic = opBL); currently only implemented for this case *)
+				(*
+				IF (opBL <= instructionFormat.mnemonic) & (instructionFormat.mnemonic <= opBF) THEN
+				*)
+				IF (format.mnemonic = opB) OR (format.mnemonic = opBL) THEN
+					mode := BinaryCode.Relative;
+					displacement := operand.fixup.displacement - 8;
+				ELSE
+					mode := BinaryCode.Relative;
+					displacement := operand.fixup.displacement;
+				END;
+					
+				(*
+				ELSE
+					mode := BinaryCode.Absolute;
+					displacement := op.fixup.displacement;
+				END;
+				*)
+				operand.fixup.InitFixup(mode, 0, operand.fixup.symbol, operand.fixup.symbolOffset, displacement, -2, patterns);
+			END Fixup;
+			
+		BEGIN
+			CASE operandEncoding OF
+				|None: error :=  operand.mode # None;
+				|encodingR16, encodingR12,encodingR8, encodingR0:
+					IF operand.mode # modeRegister THEN error := TRUE
+					ELSIF operand.shiftMode # None THEN error := TRUE
+					ELSIF (operand.register < R0) OR (operand.register > R15) THEN error := TRUE
+					END;
+					CASE operandEncoding OF
+					|encodingR16: Unsigned(operand.register-R0,16,19);
+					|encodingR12: Unsigned(operand.register-R0,12,15);
+					|encodingR8: Unsigned(operand.register-R0,8,11);
+					|encodingR0: Unsigned(operand.register-R0,0,3);
+					END;
+				|encodingCR0, encodingCR12, encodingCR16:
+					IF operand.mode # modeRegister THEN error := TRUE
+					ELSIF operand.shiftMode # None THEN error := TRUE
+					ELSIF (operand.register < CR0) OR (operand.register > CR15) THEN error := TRUE
+					END;
+					CASE operandEncoding OF
+					|encodingCR16: Unsigned(operand.register - CR0,16,19);
+					|encodingCR12: Unsigned(operand.register - CR0,12,15);
+					|encodingCR0: Unsigned(operand.register - CR0,0,3);
+					END;
+				|encodingDR0, encodingDR12, encodingDR16:
+					IF operand.mode # modeRegister THEN error := TRUE
+					ELSIF operand.shiftMode # None THEN error := TRUE
+					ELSIF (operand.register < DR0) OR (operand.register > DR15) THEN error := TRUE
+					END;
+					CASE operandEncoding OF
+					|encodingDR16: Unsigned(operand.register-DR0,16,19);
+					|encodingDR12: Unsigned(operand.register-DR0,12,15);
+					|encodingDR0: Unsigned(operand.register-DR0,0,3);
+					END;
+				|encodingFR0, encodingFR12, encodingFR16:
+					IF operand.mode # modeRegister THEN error := TRUE
+					ELSIF operand.shiftMode # None THEN error := TRUE
+					ELSIF (operand.register < SR0) OR (operand.register > SR31) THEN error := TRUE
+					END;
+					CASE operandEncoding OF
+					|encodingFR16: Unsigned((operand.register-SR0) MOD 16,16,19); Unsigned((operand.register-SR0) DIV 16,7,7);
+					|encodingFR12: Unsigned((operand.register-SR0) MOD 16,12,15); Unsigned((operand.register-SR0) DIV 16,22,22);
+					|encodingFR0: Unsigned((operand.register-SR0) MOD 16,0,3);Unsigned((operand.register-SR0) DIV 16,5,5);
+					END;
+				|encodingAddressingMode1:
+					IF operand.mode = modeImmediate THEN
+						INCL(codeSet,25);
+						IF ~EncodeImmediate(operand.immediate, imm,rot) THEN
+							(*HALT(201);*)
+							error := TRUE
+						END;
+						Unsigned(imm,0,7); Unsigned(rot,8,11);
+					ELSIF (operand.mode = modeRegister) & (operand.shiftRegister # None) THEN
+						INCL(codeSet,4);
+						Unsigned(operand.register-R0,0,3);
+						ASSERT(operand.shiftMode # None);
+						Unsigned(operand.shiftMode,5,6);
+						Unsigned(operand.shiftRegister-R0,8,11);
+					ELSIF operand.mode = modeRegister THEN
+						shiftMode := operand.shiftMode;
+						shiftImmediate := operand.shiftImmediate;
+						CheckImmediateShifts(shiftMode, shiftImmediate);
+						Unsigned(shiftMode,5,6);
+						Unsigned(shiftImmediate,7,11);
+						Unsigned(operand.register-R0,0,3)
+					ELSE error := TRUE
+					END;
+
+				|encodingAddressingMode2:
+					IF operand.mode = modeMemory THEN
+						IF Increment IN operand.indexing THEN INCL(codeSet,U) ELSE ASSERT(Decrement IN operand.indexing) END;
+
+						IF PreIndexed IN operand.indexing THEN
+							INCL(codeSet,W); INCL(codeSet,P)
+						ELSIF PostIndexed IN operand.indexing THEN
+							(* P bit remains cleared *)
+						ELSE
+							INCL(codeSet,P)
+						END;
+
+						Unsigned(operand.register-R0,16,19);
+						IF operand.offsetRegister = None THEN
+							Unsigned(operand.offsetImmediate,0,11);
+						ELSE
+							INCL(codeSet,25);
+							shiftMode := operand.shiftMode;
+							shiftImmediate := operand.shiftImmediate;
+							CheckImmediateShifts(shiftMode, shiftImmediate);
+							Unsigned(shiftMode,5,6);
+							Unsigned(shiftImmediate,7,11);
+							Unsigned(operand.offsetRegister-R0,0,3)
+						END;
+					ELSE
+						error := TRUE
+					END;
+
+				|encodingAddressingMode3:
+					IF Increment IN operand.indexing THEN INCL(codeSet,U) ELSE ASSERT(Decrement IN operand.indexing) END;
+					IF PreIndexed IN operand.indexing THEN INCL(codeSet,W); INCL(codeSet,P)
+					ELSIF PostIndexed IN operand.indexing THEN (* P = 0, W= 0 : post indexed addressing *)
+					ELSE INCL(codeSet, P); (* P=1, W= 0: offset addressing *)
+					END;
+					Unsigned(operand.register-R0,16,19);
+					IF operand.offsetRegister = None THEN
+						INCL(codeSet,B);
+						Unsigned(operand.offsetImmediate MOD 16,0,3); Unsigned(operand.offsetImmediate DIV 16,8,11);
+					ELSE
+						Unsigned(operand.offsetRegister-R0,0,3);
+					END
+				|encodingAddressingMode5:
+					IF Increment IN operand.indexing THEN INCL(codeSet,U) ELSE ASSERT(Decrement IN operand.indexing) END;
+					IF PreIndexed IN operand.indexing THEN
+						INCL(codeSet,P); INCL(codeSet,W);
+					ELSIF PostIndexed IN operand.indexing THEN
+						INCL(codeSet,W)
+					END;
+					IF operand.mode = modeMemory THEN
+						IF ~(W IN codeSet) THEN INCL(codeSet,U) END;
+						Unsigned(operand.register-R0,16,19);
+						Unsigned(operand.offsetImmediate DIV 4,0,7);
+					ELSIF operand.mode = modeOption THEN
+						Unsigned(operand.register-R0,16,18);
+						Unsigned(operand.mode,0,7);
+					ELSE error := TRUE
+					END;
+				|encodingAddressingMode5V:
+					IF Increment IN operand.indexing THEN INCL(codeSet,U) ELSE ASSERT(Decrement IN operand.indexing) END;
+					Unsigned(operand.register-R0,16,19);
+					Unsigned(operand.offsetImmediate DIV 4,0,7);
+				|encodingCoprocessor:
+					IF operand.mode # modeCoprocessor THEN
+						error := TRUE
+					END;
+					Unsigned(operand.coprocessor,8,11);
+				|encodingOpcode20, encodingOpcode21, encodingOpcode5, encodingOpcode4:
+					IF operand.mode # modeOpcode THEN
+						IF operand.mode = None THEN (* note: missing opcode operand means opcode = 0 *)
+							ASSERT(operand.opcode = 0)
+						ELSE
+							error := TRUE
+						END
+					END;
+					CASE operandEncoding OF
+						encodingOpcode20: Unsigned(operand.opcode,20,23);
+						|encodingOpcode21: Unsigned(operand.opcode,21,23);
+						|encodingOpcode5: Unsigned(operand.opcode,5,7);
+						|encodingOpcode4: Unsigned(operand.opcode,4,7);
+					END;
+				|encodingSignedImm24:
+					IF operand.mode # modeImmediate THEN
+						error := TRUE
+					END;
+					IF operand.immediate MOD 4 # 0 THEN error := TRUE END;
+					Signed(operand.immediate DIV 4,0,23);
+					IF operand.fixup # NIL THEN Fixup(0, 23) END;
+				|encodingImm24:
+					IF operand.mode # modeImmediate THEN
+						error := TRUE
+					END;
+					Unsigned(operand.immediate,0,23);
+				|encodingImm16:
+					IF operand.mode # modeImmediate THEN
+						error := TRUE
+					END;
+					Unsigned(operand.immediate MOD 16,0,3); Unsigned(operand.immediate DIV 16,9,19);
+				|encodingRotImm8:
+					IF operand.mode # modeImmediate THEN
+						error := TRUE
+					ELSIF ~EncodeImmediate(operand.immediate, imm,rot) THEN
+						error := TRUE
+					END;
+					Unsigned(imm,0,7); Unsigned(rot,8,11);
+				|encodingRegisterList:
+					IF operand.mode # modeRegisterList THEN
+						error := TRUE
+					ELSIF operand.register # R0 THEN error := TRUE
+					END;
+					Bits(operand.registerList,0,15);
+				|encodingPSR:
+					IF (operand.mode # modeRegister) THEN error := TRUE
+					ELSIF ~((operand.mode # CPSR) & (operand.mode # SPSR)) THEN error := TRUE
+					END;
+					IF operand.register = SPSR THEN
+						INCL(codeSet,22);
+					END;
+				|encodingFields:
+					IF operand.mode # modeRegisterWithFields THEN
+						error := TRUE
+					END;
+					IF operand.register = SPSR THEN INCL(codeSet,22)
+					ELSIF operand.register # CPSR THEN error := TRUE
+					END;
+					Bits(operand.fields,16,19);
+				|encodingDRegisterList:
+					IF operand.mode # modeRegisterList THEN error := TRUE
+					ELSIF operand.register # DR0 THEN error := TRUE
+					ELSIF ~Range(operand.registerList,firstRegister, num) THEN error := TRUE
+					END;
+					Unsigned(firstRegister,12,15); Unsigned(num*2,0,7);
+				|encodingFRegisterList:
+					IF operand.mode # modeRegisterList THEN error := TRUE
+					ELSIF operand.register # DR0 THEN error := TRUE
+					ELSIF ~Range(operand.registerList,firstRegister, num) THEN error := TRUE
+					END;
+					Unsigned(firstRegister MOD 2,22,22); Unsigned(num,0,7);
+					Unsigned(firstRegister DIV 2,12,15); Unsigned(num,0,7);
+				|encodingImm7to11:
+					IF operand.mode # modeImmediate THEN
+						error := TRUE
+					END;
+					Unsigned(operand.immediate, 7, 11);
+
+				(* NEON specific operand encodings *)
+				|encodingNEONQd, encodingNEONQm, encodingNEONQn:
+					IF operand.mode # modeRegister THEN error := TRUE
+					ELSIF operand.shiftMode # None THEN error := TRUE
+					(* Check register type for each subcase *)
+					ELSIF (operand.register < QR0) OR (operand.register > QR15) THEN error := TRUE
+					END;
+					CASE operandEncoding OF
+						 encodingNEONQd:
+						 	NEONRegisterPos[0] := 13;
+						 	NEONRegisterPos[1] := 14;
+						 	NEONRegisterPos[2] := 15;
+						 	NEONRegisterPos[3] := 22;
+						|encodingNEONQm:
+						 	NEONRegisterPos[0] := 1;
+						 	NEONRegisterPos[1] := 2;
+						 	NEONRegisterPos[2] := 3;
+						 	NEONRegisterPos[3] := 5;
+						|encodingNEONQn:
+						 	NEONRegisterPos[0] := 17;
+						 	NEONRegisterPos[1] := 18;
+						 	NEONRegisterPos[2] := 19;
+						 	NEONRegisterPos[3] := 7;
+					END;
+				 	NEONRegisterPos[4] := 32; (* end of encoding *)
+				 	SplittedUnsigned(operand.register - QR0, NEONRegisterPos)
+
+				|encodingNEONDd, encodingNEONDm, encodingNEONDn:
+					IF operand.mode # modeRegister THEN error := TRUE
+					ELSIF operand.shiftMode # None THEN error := TRUE
+					ELSIF (operand.register < DR0) OR (operand.register > DR31) THEN error := TRUE
+					END;
+					CASE operandEncoding OF
+						 encodingNEONDd:
+						 	NEONRegisterPos[0] := 12;
+						 	NEONRegisterPos[1] := 13;
+						 	NEONRegisterPos[2] := 14;
+						 	NEONRegisterPos[3] := 15;
+						 	NEONRegisterPos[4] := 22;
+						|encodingNEONDm:
+						 	NEONRegisterPos[0] := 0;
+						 	NEONRegisterPos[1] := 1;
+						 	NEONRegisterPos[2] := 2;
+						 	NEONRegisterPos[3] := 3;
+						 	NEONRegisterPos[4] := 5;
+						|encodingNEONDn:
+						 	NEONRegisterPos[0] := 16;
+						 	NEONRegisterPos[1] := 17;
+						 	NEONRegisterPos[2] := 18;
+						 	NEONRegisterPos[3] := 19;
+						 	NEONRegisterPos[4] := 7;
+					END;
+				 	SplittedUnsigned(operand.register - DR0, NEONRegisterPos)
+
+				|encodingNEONSd, encodingNEONSm, encodingNEONSn:
+					IF operand.mode # modeRegister THEN error := TRUE
+					ELSIF operand.shiftMode # None THEN error := TRUE
+					ELSIF (operand.register < SR0) OR (operand.register > SR31) THEN error := TRUE
+					END;
+					CASE operandEncoding OF
+						 encodingNEONSd:
+						 	NEONRegisterPos[0] := 22;
+						 	NEONRegisterPos[1] := 12;
+						 	NEONRegisterPos[2] := 13;
+						 	NEONRegisterPos[3] := 14;
+						 	NEONRegisterPos[4] := 15;
+						|encodingNEONSm:
+						 	NEONRegisterPos[0] := 5;
+						 	NEONRegisterPos[1] := 0;
+						 	NEONRegisterPos[2] := 1;
+						 	NEONRegisterPos[3] := 2;
+						 	NEONRegisterPos[4] := 3;
+						|encodingNEONSn:
+						 	NEONRegisterPos[0] := 7;
+						 	NEONRegisterPos[1] := 16;
+						 	NEONRegisterPos[2] := 17;
+						 	NEONRegisterPos[3] := 18;
+						 	NEONRegisterPos[4] := 19;
+					END;
+				 	SplittedUnsigned(operand.register - SR0, NEONRegisterPos)
+
+				|encodingNEONQorDd, encodingNEONQorDm, encodingNEONQorDn:
+					IF operand.mode # modeRegister THEN error := TRUE
+					ELSIF operand.shiftMode # None THEN error := TRUE
+					ELSIF (DR0 <= operand.register) & (operand.register <= DR31) THEN
+						CASE operandEncoding OF
+							 encodingNEONQorDd:
+							 	NEONRegisterPos[0] := 12;
+							 	NEONRegisterPos[1] := 13;
+							 	NEONRegisterPos[2] := 14;
+							 	NEONRegisterPos[3] := 15;
+							 	NEONRegisterPos[4] := 22;
+							|encodingNEONQorDm:
+							 	NEONRegisterPos[0] := 0;
+							 	NEONRegisterPos[1] := 1;
+							 	NEONRegisterPos[2] := 2;
+							 	NEONRegisterPos[3] := 3;
+							 	NEONRegisterPos[4] := 5;
+							|encodingNEONQorDn:
+							 	NEONRegisterPos[0] := 16;
+							 	NEONRegisterPos[1] := 17;
+							 	NEONRegisterPos[2] := 18;
+							 	NEONRegisterPos[3] := 19;
+							 	NEONRegisterPos[4] := 7;
+						END;
+						SplittedUnsigned(operand.register - DR0, NEONRegisterPos);
+					ELSIF (QR0 <= operand.register) & (operand.register <= QR15) THEN
+						CASE operandEncoding OF
+							 encodingNEONQorDd:
+							 	NEONRegisterPos[0] := 13;
+							 	NEONRegisterPos[1] := 14;
+							 	NEONRegisterPos[2] := 15;
+							 	NEONRegisterPos[3] := 22;
+							|encodingNEONQorDm:
+							 	NEONRegisterPos[0] := 1;
+							 	NEONRegisterPos[1] := 2;
+							 	NEONRegisterPos[2] := 3;
+							 	NEONRegisterPos[3] := 5;
+							|encodingNEONQorDn:
+							 	NEONRegisterPos[0] := 17;
+							 	NEONRegisterPos[1] := 18;
+							 	NEONRegisterPos[2] := 19;
+							 	NEONRegisterPos[3] := 7;
+						END;
+						NEONRegisterPos[4] := 32;
+						SplittedUnsigned(operand.register - QR0, NEONRegisterPos);
+					ELSE error := TRUE
+					END
+
+				|encodingNEONDorSd, encodingNEONDorSm, encodingNEONDorSn:
+					IF operand.mode # modeRegister THEN error := TRUE
+					ELSIF operand.shiftMode # None THEN error := TRUE
+					ELSIF (DR0 <= operand.register) & (operand.register <= DR15) THEN
+						CASE operandEncoding OF
+							 encodingNEONDorSd:
+							 	NEONRegisterPos[0] := 12;
+							 	NEONRegisterPos[1] := 13;
+							 	NEONRegisterPos[2] := 14;
+							 	NEONRegisterPos[3] := 15;
+							 	NEONRegisterPos[4] := 22;
+							|encodingNEONDorSm:
+							 	NEONRegisterPos[0] := 0;
+							 	NEONRegisterPos[1] := 1;
+							 	NEONRegisterPos[2] := 2;
+							 	NEONRegisterPos[3] := 3;
+							 	NEONRegisterPos[4] := 5;
+							|encodingNEONDorSn:
+							 	NEONRegisterPos[0] := 16;
+							 	NEONRegisterPos[1] := 17;
+							 	NEONRegisterPos[2] := 18;
+							 	NEONRegisterPos[3] := 19;
+							 	NEONRegisterPos[4] := 7;
+						END;
+						SplittedUnsigned(operand.register - DR0, NEONRegisterPos)
+					ELSIF (SR0 <= operand.register) & (operand.register <= SR31) THEN
+						CASE operandEncoding OF
+						 encodingNEONDorSd:
+						 	NEONRegisterPos[0] := 22;
+						 	NEONRegisterPos[1] := 12;
+						 	NEONRegisterPos[2] := 13;
+						 	NEONRegisterPos[3] := 14;
+						 	NEONRegisterPos[4] := 15;
+						|encodingNEONDorSm:
+						 	NEONRegisterPos[0] := 5;
+						 	NEONRegisterPos[1] := 0;
+						 	NEONRegisterPos[2] := 1;
+						 	NEONRegisterPos[3] := 2;
+						 	NEONRegisterPos[4] := 3;
+						|encodingNEONDorSn:
+						 	NEONRegisterPos[0] := 7;
+						 	NEONRegisterPos[1] := 16;
+						 	NEONRegisterPos[2] := 17;
+						 	NEONRegisterPos[3] := 18;
+						 	NEONRegisterPos[4] := 19;
+						END;
+						SplittedUnsigned(operand.register - SR0, NEONRegisterPos)
+					ELSE error := TRUE
+					END
+
+				|encodingNEON8bitImm:
+					IF operand.mode # modeImmediate THEN error := TRUE END;
+					Unsigned(operand.immediate DIV 4, 0, 7);
+
+				|encodingNEONDRegList:
+					IF operand.mode # modeRegisterList THEN error := TRUE
+					ELSIF operand.register # DR0 THEN error := TRUE
+					ELSIF ~Range(operand.registerList, firstRegister, num) THEN error := TRUE
+					END;
+					(* First register is encoded as Dd *)
+					NEONRegisterPos[0] := 12;
+					NEONRegisterPos[1] := 13;
+					NEONRegisterPos[2] := 14;
+					NEONRegisterPos[3] := 15;
+					NEONRegisterPos[4] := 22;
+				 	SplittedUnsigned(DR0 + firstRegister, NEONRegisterPos);
+					(* bits 8 to 11 specify the length of the list *)
+					CASE num OF
+						 1: 	Unsigned(7H, 8, 11)
+						|2: 	Unsigned(0AH, 8, 11)
+						|3: Unsigned(6H, 8, 11)
+						|4: Unsigned(2H, 8, 11)
+					ELSE
+						D.String("Register list too long");
+						error := TRUE
+					END
+				
+				|encodingNEONSysReg:
+					IF operand.mode # modeRegister THEN error := TRUE
+					ELSIF operand.shiftMode # None THEN error := TRUE
+					ELSIF (operand.register < FPSID) & (operand.register > FPEXC) THEN error := TRUE
+					END;
+					
+					CASE operand.register OF
+						(* FPSID - 0b0; FPSCR - 0b1; FPEXC - 0b1000 at bits 19:16 *)
+						 FPSCR: INCL(codeSet, 16)
+						|FPEXC: INCL(codeSet, 19)
+					END
+
+				|encodingNEONSigned8bitImm:
+					IF operand.mode # modeImmediate THEN
+						error := TRUE
+					END;
+					Unsigned(operand.immediate DIV 4,0,7)
+			END;
+
+			(* TODO: remove later on: *)
+			IF error THEN
+				D.String("cannot encode operand:"); D.Ln;
+				DumpOperand(D.Log, operand); D.Ln;
+				D.String("expected operand encoding: "); D.Int(operandEncoding, 0); D.Ln
+			END
+		END EncodeOperand;
+
+		(** Manages the encoding of the types and sizes of a NEON instruction. *)
+		PROCEDURE EncodeNEONTypes(format: InstructionFormat; instruction: Instruction);
+		VAR
+			normalSizeEncoding: BOOLEAN;
+			i, immOperand: INTEGER;
+		BEGIN
+			IF ~format.isNEON THEN RETURN END;
+			(* Set the Unsigned bit *)
+			IF flagNEONUnsigned IN instruction.flags THEN
+				codeSet := codeSet + format.Unsigned
+			END;
+			(* We also set the unsigned bit for positive immediates encoded by encodingNEONSigned8bitimm *)
+			FOR i := 0 TO MaxOperands -1 DO
+				IF (format.operands[i] = encodingNEONSigned8bitImm) & (instruction.operands[i].immediate >= 0) THEN
+					codeSet := codeSet + format.Unsigned
+				END
+			END;
+			
+			(* Set the Float bit *)
+			IF flagNEONFloat IN instruction.flags THEN
+				codeSet := codeSet + format.Float
+			END;
+			
+			(* Set the Q bit *)
+			FOR i := 0 TO MaxOperands - 1 DO
+				CASE format.operands[i] OF
+					 encodingNEONQorDd, encodingNEONQorDm, encodingNEONQorDn:
+					 	IF (QR0 <= instruction.operands[i].register) & (instruction.operands[i].register <= QR15) THEN
+					 		codeSet := codeSet + format.Quadword
+					 	END
+					|encodingNEONDorSd, encodingNEONDorSm, encodingNEONDorSn:
+						IF (DR0 <= instruction.operands[i].register) & (instruction.operands[i].register <= DR15) THEN
+							codeSet := codeSet + format.Quadword
+						END
+				ELSE
+				END
+			END;
+
+			(* Check the type of size encoding *)
+			normalSizeEncoding := TRUE;
+			FOR i := 0 TO MaxOperands - 1 DO
+				IF (format.operands[i] = encodingNEONImmAndSize) THEN
+				immOperand := i;
+				normalSizeEncoding := FALSE
+				END
+			END;
+
+			IF normalSizeEncoding THEN
+				IF format.SizeH # 32 THEN
+					(* Set the size bit for usual encoding *)
+					IF flagNEON16bits IN instruction.flags THEN
+						INCL(codeSet, format.SizeH - 1)
+					ELSIF flagNEON32bits IN instruction.flags THEN
+						INCL(codeSet, format.SizeH)
+					ELSIF flagNEON64bits IN instruction.flags THEN
+						INCL(codeSet, format.SizeH);
+						INCL(codeSet, format.SizeH - 1);
+						(* for floating-points *)
+						codeSet := codeSet + format.Operation;
+					END
+				END
+			ELSE
+				(* size and imm encoding done here *)
+				IF flagNEON8bits IN instruction.flags THEN
+					INCL(codeSet, 19);
+				ELSIF flagNEON16bits IN instruction.flags THEN
+					INCL(codeSet, 18);
+				ELSIF flagNEON32bits IN instruction.flags THEN
+					INCL(codeSet, 17);
+				ELSIF flagNEON64bits IN instruction.flags THEN
+					codeSet := codeSet + format.Length
+				END
+			END;
+		END EncodeNEONTypes;
+
+	BEGIN
+		error := FALSE;
+		IF instruction.format = None THEN error := TRUE
+		ELSE
+			format := instructionFormats[instruction.format];
+			codeSet := format.opcode;
+			(*
+			D.String(mnemonics[format.mnemonic].name); D.Ln;
+			D.String("encoding "); D.Int(instruction.format,1); D.Ln;
+			D.Set(codeSet); D.Ln;
+			*)
+			EncodeNEONTypes(format, instruction);
+
+			IF flagCondition IN format.flags THEN
+				Unsigned(instruction.condition,28,31);
+			END;
+			flags := format.flags * instruction.flags;
+			IF flagB IN flags THEN INCL(codeSet,B) END;
+			IF flagBT IN flags THEN INCL(codeSet,B); INCL(codeSet,W) END;
+			IF flagD IN flags THEN INCL(codeSet,S) END;
+			IF flagDA IN flags THEN END;
+			IF flagDB IN flags THEN INCL(codeSet,P) END;
+			IF flagH IN flags THEN INCL(codeSet,H) END;
+			IF flagIA IN flags THEN INCL(codeSet,U) END;
+			IF flagIB IN flags THEN INCL(codeSet,U); INCL(codeSet,P) END;
+			IF flagL IN flags THEN INCL(codeSet,22) END;
+			IF flagS IN flags THEN INCL(codeSet,20) END;
+			IF flagSB IN flags THEN INCL(codeSet,S) END;
+			IF flagSH IN flags THEN INCL(codeSet,S); INCL(codeSet,H) END;
+			IF flagT IN flags THEN END;
+			IF flagUserMode IN flags THEN INCL(codeSet,22) END;
+			IF flagBaseRegisterUpdate IN flags THEN INCL(codeSet,21) END;
+			i := 0;
+
+			WHILE (i < LEN(instruction.operands)) & ~error DO
+				EncodeOperand(format.operands[i], instruction.operands[i]);
+				(* D.Set(codeSet); D.Ln; *)
+				INC(i);
+			END;
+		END;
+		code := SetToNumber(codeSet);
+
+		IF error THEN D.String("cannot encode instruction"); D.Ln END;
+
+		RETURN ~error
+	END Encode;
+
+	PROCEDURE Decode(code: LONGINT; VAR instruction: Instruction): BOOLEAN;
+	VAR
+		instrNr, operandNr: LONGINT; format: InstructionFormat; codeSet: SET;
+		P, U, B, W, L, S, H: BOOLEAN; done: BOOLEAN;
+
+		PROCEDURE Match(CONST format: InstructionFormat): BOOLEAN;
+		BEGIN
+			RETURN codeSet*format.mask= format.opcode*format.mask
+		END Match;
+
+		PROCEDURE Bits(from, to: LONGINT): SET;
+		VAR i: LONGINT; val: SET;
+		BEGIN
+			val := {};
+			FOR i := from TO to DO
+				IF i IN codeSet THEN INCL(val, i-from) END;
+			END;
+			RETURN val
+		END Bits;
+
+		PROCEDURE Unsigned(from, to: LONGINT): LONGINT;
+		VAR val, i: LONGINT;
+		BEGIN
+			val := 0;
+			FOR i := to TO from BY -1 DO
+				val := val*2;
+				IF i IN codeSet THEN INC(val) END;
+			END;
+			RETURN val
+		END Unsigned;
+
+		PROCEDURE Signed(from, to: LONGINT): LONGINT;
+		VAR val, i: LONGINT; negative:BOOLEAN;
+		BEGIN
+			val := 0;
+			negative := to IN codeSet; (* two's complement negate *)
+			FOR i := to-1 TO from BY -1 DO
+				val := val*2;
+				IF (i IN codeSet) THEN
+					IF ~negative THEN INC(val) END;
+				ELSIF negative THEN
+					INC(val)
+				END;
+			END;
+			IF negative THEN INC(val); val := -val; END;
+			RETURN val
+		END Signed;
+
+		PROCEDURE DecodeOperand(encoding: LONGINT; VAR operand: Operand): BOOLEAN;
+		VAR imm, rot, register, shift, shiftRegister, shiftImm, offsetRegister: LONGINT; indexing: SET; firstRegister, lastRegister: LONGINT;
+		BEGIN
+			CASE encoding OF
+				|None: InitOperand(operand);
+				|encodingR16: InitRegister(operand, R0+Unsigned(16, 19), None, None, 0);
+				|encodingR12: InitRegister(operand, R0+Unsigned(12, 15), None, None, 0);
+				|encodingR8: InitRegister(operand, R0+Unsigned(8, 11), None, None, 0);
+				|encodingR0: InitRegister(operand, R0+Unsigned(0, 3), None, None, 0);
+				|encodingFR0: InitRegister(operand, SR0+Unsigned(0, 3)+16*Unsigned(5,5), None, None, 0);
+				|encodingFR12: InitRegister(operand, SR0+Unsigned(12, 15)+16*Unsigned(22,22), None, None, 0);
+				|encodingFR16: InitRegister(operand, SR0+Unsigned(16, 19)+16*Unsigned(7,7), None, None, 0);
+				|encodingDR0: InitRegister(operand, DR0+Unsigned(0, 3), None, None, 0);
+				|encodingDR12: InitRegister(operand, DR0+Unsigned(12, 15), None, None, 0);
+				|encodingDR16: InitRegister(operand, DR0+Unsigned(16, 19), None, None, 0);
+				|encodingCR0: InitRegister(operand, CR0+Unsigned(0, 3), None, None, 0);
+				|encodingCR12: InitRegister(operand, CR0+Unsigned(12, 15), None, None, 0);
+				|encodingCR16: InitRegister(operand, CR0+Unsigned(16, 19), None, None, 0);
+				|encodingAddressingMode1:
+					IF 25 IN codeSet THEN (* rotate immediate *)
+						imm := Unsigned(0, 7); rot := Unsigned(8, 11); imm := RotateRight(imm, rot*2);
+						InitImmediate(operand, imm);
+					ELSIF 4 IN codeSet THEN (* register register shift *)
+						IF 7 IN codeSet THEN RETURN FALSE END;
+						(* ASSERT(~(7 IN codeSet)); *)
+						shift := Unsigned(5, 6);
+						register := Unsigned(0, 3);
+						shiftRegister := Unsigned(8, 11);
+						imm := 0;
+						InitRegister(operand, register, shift, shiftRegister, imm);
+					ELSE (* register immediate shift *)
+						shiftRegister := None;
+						shift := Unsigned(5, 6);
+						register := Unsigned(0, 3);
+						imm := Unsigned(7, 11);
+						IF (shift= 0) & (imm= 0) THEN shift := None END;
+						IF (shift= 3) & (imm= 0) THEN shift := shiftRRX END;
+						InitRegister(operand, register, shift, shiftRegister, imm);
+					END;
+				|encodingAddressingMode2:
+					IF TraceDecode THEN D.String("AddressMode2"); D.Ln; END;
+					indexing := {};
+					IF U THEN INCL(indexing, Increment) ELSE INCL(indexing, Decrement) END;
+					IF ~P THEN INCL(indexing, PostIndexed)
+					ELSIF W THEN INCL(indexing, PreIndexed)
+					END;
+					register := Unsigned(16, 19);
+					IF ~(25 IN codeSet) THEN (* immediate offset *)
+						IF TraceDecode THEN D.String("ImmediateOffset"); D.Ln; END;
+						imm := Unsigned(0, 11);
+						InitImmediateOffsetMemory(operand, register, imm, indexing);
+					ELSE (* register offset / scaled register offset *)
+						IF (4 IN codeSet)  THEN RETURN FALSE END;
+						IF TraceDecode THEN D.String("RegisterOffset"); D.Ln; END;
+						offsetRegister := Unsigned(0, 3);
+						shift := Unsigned(5, 6);
+						shiftImm := Unsigned(7, 11);
+						IF (shift= 0) & (shiftImm= 0) THEN shift := None END;
+						IF (shiftImm = 0) & (shift IN {shiftLSR, shiftASR}) THEN shiftImm := 32 END;
+						IF (shift= shiftROR) & (shiftImm = 0) THEN shift := shiftRRX; shiftImm := 1 END;
+						InitRegisterOffsetMemory(operand, register, offsetRegister, shift, shiftImm, indexing);
+					END;
+				|encodingAddressingMode3:
+					indexing := {};
+					IF ~S & ~H THEN RETURN FALSE END;
+					IF S & ~L THEN RETURN FALSE END;
+					(* IF ~P & ~W THEN unpredictable instruction  END; *)
+					IF U THEN INCL(indexing, Increment) ELSE INCL(indexing, Decrement) END;
+					IF ~P THEN INCL(indexing, PostIndexed)
+					ELSIF W THEN INCL(indexing, PreIndexed)
+					END;
+
+					register := Unsigned(16, 19);
+					IF B THEN (* immediate offset *)
+						imm := Unsigned(0, 3)+16*Unsigned(8, 11);
+						InitImmediateOffsetMemory(operand, register, imm, indexing);
+					ELSE (* register offset *)
+						offsetRegister := Unsigned(0, 3);
+						InitRegisterOffsetMemory(operand, register, offsetRegister, None, 0, indexing);
+					END;
+				|encodingAddressingMode5:
+					IF U THEN INCL(indexing, Increment) ELSE INCL(indexing, Decrement) END;
+					IF P THEN
+						IF W THEN INCL(indexing, PreIndexed) END;
+					ELSE
+						IF W THEN INCL(indexing, PostIndexed) END;
+					END;
+					IF U OR W THEN
+						InitImmediateOffsetMemory(operand, Unsigned(16, 19), Unsigned(0, 7)*4, indexing);
+					ELSE
+						InitOption(operand, Unsigned(16, 19), Unsigned(0, 7));
+					END;
+				|encodingAddressingMode5V:
+					IF U THEN INCL(indexing,Increment) ELSE INCL(indexing, Decrement) END;
+					InitImmediateOffsetMemory(operand,Unsigned(16,19),Unsigned(0,7)*4,indexing);
+				|encodingCoprocessor: InitCoprocessor(operand, Unsigned(8, 11));
+				|encodingFields: IF 22 IN codeSet THEN register := SPSR ELSE register := CPSR END; InitRegisterWithFields(operand, register,Bits(16, 19));
+				|encodingImm16: imm := Unsigned(0, 3)+Unsigned(8, 19)*16; InitImmediate(operand, imm);
+				|encodingSignedImm24: imm := Signed(0, 23); InitImmediate(operand, imm*4);
+				|encodingImm24: imm := Unsigned(0, 23); InitImmediate(operand, imm);
+				|encodingRotImm8: imm := Unsigned(0, 7); rot := Unsigned(8, 11); imm := RotateRight(imm, rot*2); InitImmediate(operand, imm);
+				|encodingOpcode20: InitOpcode(operand, Unsigned(20, 23));
+				|encodingOpcode21: InitOpcode(operand, Unsigned(21, 23));
+				|encodingOpcode5: InitOpcode(operand, Unsigned(5, 7));
+				|encodingOpcode4: InitOpcode(operand,Unsigned(4,6));
+				|encodingRegisterList: InitRegisterList(operand, R0, Bits(0, 15));
+				|encodingDRegisterList: firstRegister := Unsigned(12,15); lastRegister := firstRegister + Unsigned(0,7) DIV 2 -1;
+					InitRegisterList(operand, DR0, {firstRegister .. lastRegister});
+				|encodingFRegisterList: firstRegister := Unsigned(12,15)*2+Unsigned(22,22); lastRegister := firstRegister + Unsigned(0,7) -1;
+					IF lastRegister >= 32 THEN RETURN FALSE END;
+					InitRegisterList(operand, SR0, {firstRegister .. lastRegister});
+				|encodingPSR: InitRegister(operand, CPSR+Unsigned(22, 22), None, None, 0);
+			ELSE (*! should trap *)
+				RETURN FALSE
+			END;
+			RETURN TRUE
+		END DecodeOperand;
+
+
+	BEGIN
+		codeSet := NumberToSet(code);
+		IF TraceDecode THEN
+			D.String("decoding:"); D.Hex(code, -8); D.String(":"); D.Set(codeSet); D.Ln;
+		END;
+		P := 24 IN codeSet;
+		U := 23 IN codeSet;
+		B := 22 IN codeSet;
+		W := 21 IN codeSet;
+		L := 20 IN codeSet;
+		S := 6 IN codeSet;
+		H := 5 IN codeSet;
+		instrNr := 0; done := FALSE;
+		WHILE (instrNr<NumberInstructions) & ~done DO
+			done := Match(instructionFormats[instrNr]);
+			IF done THEN
+				InitInstruction(instruction);
+				IF TraceDecode THEN
+					D.String("format:"); D.Int(instrNr, 1); D.String(":"); D.String(mnemonics[instructionFormats[instrNr].mnemonic].name); D.Ln;
+				END;
+				instruction.format := instrNr;
+				format := instructionFormats[instrNr];
+
+				IF flagCondition IN format.flags THEN
+					instruction.condition := Unsigned(28, 31);
+					IF TraceDecode THEN
+						D.String("condition: "); D.Int(instruction.condition, 1); D.Ln;
+					END;
+				ELSE
+					instruction.condition := None;
+				END;
+				IF flagB IN format.flags THEN
+					IF B & ~P & ~W THEN INCL(instruction.flags, flagB) END;
+				END;
+				IF flagBT IN format.flags THEN
+					IF B & ~P & W THEN INCL(instruction.flags, flagBT) END;
+				END;
+				IF flagD IN format.flags THEN
+					IF S & ~H THEN INCL(instruction.flags, flagD) END;
+				END;
+				IF flagDA IN format.flags THEN
+					IF ~U & ~P THEN INCL(instruction.flags, flagDA) END;
+				END;
+				IF flagDB IN format.flags THEN
+					IF ~U & P THEN INCL(instruction.flags, flagDB) END;
+				END;
+				IF flagH IN format.flags THEN
+					IF ~S & H THEN INCL(instruction.flags, flagH) END;
+				END;
+				IF flagIA IN format.flags THEN
+					IF U & ~P THEN INCL(instruction.flags, flagIA) END;
+				END;
+				IF flagIB IN format.flags THEN
+					IF U & P THEN INCL(instruction.flags, flagDA) END;
+				END;
+				IF flagL IN format.flags THEN
+					IF 22 IN codeSet THEN INCL(instruction.flags, flagL) END;
+				END;
+				IF flagS IN format.flags THEN
+					IF 20 IN codeSet THEN INCL(instruction.flags, flagS) END;
+				END;
+				IF flagSB IN format.flags THEN
+					IF S & ~H THEN INCL(instruction.flags, flagSB) END;
+				END;
+				IF flagSH IN format.flags THEN
+					IF S & H THEN INCL(instruction.flags, flagSH) END;
+				END;
+				IF flagT IN format.flags THEN
+					IF ~B & ~P & ~W THEN INCL(instruction.flags, flagT) END;
+				END;
+				IF flagUserMode IN format.flags THEN
+					IF 22 IN codeSet THEN INCL(instruction.flags, flagUserMode) END
+				END;
+				IF flagBaseRegisterUpdate IN format.flags THEN
+					IF 21 IN codeSet THEN INCL(instruction.flags, flagBaseRegisterUpdate) END
+				END;
+				operandNr := 0;
+				WHILE (operandNr < MaxOperands) & done DO
+					done := done & DecodeOperand(format.operands[operandNr], instruction.operands[operandNr]);
+					INC(operandNr);
+				END;
+			END;
+			INC(instrNr);
+		END;
+		RETURN done
+	END Decode;
+
+	(* TODO: handle fixups *)
+	PROCEDURE EmitInstruction*(CONST instruction: Instruction; code: BinaryCode.Section): BOOLEAN;
+	VAR
+		encoding: LONGINT;
+		(*decoded: Instruction;*)
+		
+		PROCEDURE PatchFixup(op: Operand);
+		BEGIN
+			IF op.fixup # NIL THEN
+				op.fixup.SetFixupOffset(code.pc);
+				code.fixupList.AddFixup(op.fixup);
+			END;
+		END PatchFixup;
+
+		PROCEDURE PatchFixups;
+		VAR i: LONGINT;
+		BEGIN
+			FOR i := 0 TO LEN(instruction.operands)-1 DO
+				PatchFixup(instruction.operands[i]);				
+			END;
+		END PatchFixups;
+
+	BEGIN
+		IF (code.comments # NIL) THEN
+			DumpInstruction(code.comments, instruction);
+			code.comments.Ln;
+			code.comments.Update;
+		END;
+		IF ~Encode(instruction, encoding) THEN RETURN FALSE END;
+		PatchFixups();
+		
+		(*
+		IF (code.comments # NIL) THEN
+			IF ~Decode(encoding, decoded) THEN HALT(100) END;
+			DumpInstruction(code.comments, decoded);
+			code.comments.String(" (decoding of encoding)");
+			code.comments.Ln;
+			code.comments.Update;
+		END;
+		*)
+
+		(* PatchFixups(); *)
+		code.PutBits(encoding, 32);
+		RETURN TRUE;
+	END EmitInstruction;
+
+	PROCEDURE Emit*(mnemonic, condition: LONGINT; flags: SET; CONST operands: ARRAY OF Operand; code: BinaryCode.Section);
+	VAR instruction: Instruction;
+	BEGIN
+		IF ~MakeInstruction(instruction, mnemonic, condition, flags, operands) THEN HALT(100) END;
+		ASSERT(EmitInstruction(instruction, code));
+	END Emit;
+
+	PROCEDURE Init*;
+	VAR
+		i, instructionCount, conditionEntryCount, registerEntryCount: LONGINT;
+
+		PROCEDURE EnterCoprocessor(number: INTEGER; CONST name: ARRAY OF CHAR);
+		BEGIN
+			COPY(name, coprocessorNames[number])
+		END EnterCoprocessor;
+
+		PROCEDURE EnterShift(number: INTEGER; CONST name: ARRAY OF CHAR);
+		BEGIN
+			COPY(name, shiftNames[number]);
+		END EnterShift;
+
+		PROCEDURE EnterRegister(number: INTEGER; CONST name: ARRAY OF CHAR);
+		VAR
+			registerEntry: Entry;
+		BEGIN
+			COPY(name, registerEntry.name);
+			registerEntry.number := number;
+			registerEntries[registerEntryCount] := registerEntry;
+			INC(registerEntryCount);
+		END EnterRegister;
+
+		PROCEDURE EnterMnemonic(number: INTEGER; CONST name: ARRAY OF CHAR);
+		VAR mnemonic: Mnemonic;
+		BEGIN
+			COPY(name, mnemonic.name);
+			mnemonic.number := number;
+			mnemonic.firstInstructionFormat := MAX(LONGINT); mnemonic.lastInstructionFormat := MIN(LONGINT);
+			mnemonics[number] := mnemonic;
+		END EnterMnemonic;
+
+		PROCEDURE EnterInstruction(mnemonic: INTEGER; opcode, mask: SET; flags: SET; op0, op1, op2, op3, op4, op5: INTEGER);
+		VAR format: InstructionFormat;
+		BEGIN
+			format.mnemonic := mnemonic;
+			format.opcode := opcode;
+			format.mask := mask;
+			format.flags := flags;
+			format.operands[0] := op0;
+			format.operands[1] := op1;
+			format.operands[2] := op2;
+			format.operands[3] := op3;
+			format.operands[4] := op4;
+			format.operands[5] := op5;
+			format.isNEON := FALSE;
+
+			instructionFormats[instructionCount] := format;
+			IF instructionCount < mnemonics[mnemonic].firstInstructionFormat THEN mnemonics[mnemonic].firstInstructionFormat := instructionCount END;
+			IF instructionCount > mnemonics[mnemonic].lastInstructionFormat THEN mnemonics[mnemonic].lastInstructionFormat := instructionCount END;
+
+			INC(instructionCount);
+
+		END EnterInstruction;
+
+		PROCEDURE EnterCondition(number: LONGINT; CONST name: ARRAY OF CHAR);
+		BEGIN
+			COPY(name, conditionEntries[conditionEntryCount].name);
+			conditionEntries[conditionEntryCount].number := number;
+			INC(conditionEntryCount);
+		END EnterCondition;
+
+		PROCEDURE EnterFlag(number: LONGINT; CONST name: ARRAY OF CHAR);
+		BEGIN
+			COPY(name, flagNames[number]);
+		END EnterFlag;
+
+		(** Parse an instruction format string. The format is a string representation of the encoding bits.
+		Each bit can be represented by 0, 1 or X:
+			- 0 means that the bit can be used to identify this instruction and it is not set
+			- 1 means that the bit can be used to identify this instruction and it is set
+			- X means that the bit cannot be used to identify the instruction.
+			- U, S, L, F, o for the corresponding bits
+		Whitespaces are ignored.
+		The output sets are filled according to the parsing results.
+		*)
+		PROCEDURE ParseInstructionFormat(CONST format: ARRAY OF CHAR; VAR setBits, diffBits, U, Q, S, L, F, o: SET);
+		VAR
+			curr, len, count: LONGINT;
+		BEGIN
+			len := Strings.Length(format) - 1;
+			count := 0;
+			setBits := {};
+			diffBits := {};
+			U := {}; Q :={}; S := {}; F := {}; L := {}; F := {}; o := {};
+
+			FOR curr := len TO 0 BY -1 DO
+				ASSERT((format[curr] = " ") OR (format[curr] = "1")
+					 OR (format[curr] = "0") OR (format[curr] = "X")
+					 OR (format[curr] = "U") OR (format[curr] = "Q")
+					 OR (format[curr] = "F") OR (format[curr] = "L")
+					 OR (format[curr] = "S") OR (format[curr] = "o"));
+				CASE format[curr] OF
+					 " ":
+					|"0": INCL(diffBits, count); INC(count)
+					|"1": INCL(setBits, count); INCL(diffBits, count); INC(count)
+					|"X": INC(count)
+					|"U": INCL(U, count); INC(count)
+					|"Q": INCL(Q, count); INC(count)
+					|"S": INCL(S, count); INC(count)
+					|"L": INCL(L, count); INC(count)
+					|"F": INCL(F, count); INC(count)
+					|"o": INCL(o, count); INC(count)
+				END
+			END;
+			(* Only allow 32 elements wide formats *)
+			ASSERT(count = 32);
+			
+			(* DEBUG *)
+			(*D.String("Parsing results:"); D.Ln;
+			D.String("    "); D.String(format); D.Ln;
+			D.String("opcode: "); D.Set(setBits); D.Ln;
+			D.String("mask:    "); D.Set(diffBits); D.Ln;
+			D.String("U:         "); D.Set(U); D.Ln;
+			D.String("Q:         "); D.Set(Q); D.Ln;
+			D.String("S:          "); D.Set(S); D.Ln;
+			D.String("F:          "); D.Set(F); D.Ln;
+			D.String("L:          "); D.Set(L); D.Ln;
+			D.String("o:          "); D.Set(o); D.Ln;*)
+		END ParseInstructionFormat;
+
+		(** Adds the given sets to the corresponding record fields of the last entered instruction. Sets the instruction to be a NEON instruction. *)
+		PROCEDURE UpdateNEONInstruction(U, Q, S, L, F, O: SET);
+		VAR
+			last: LONGINT;
+			i: INTEGER;
+		BEGIN
+			(*ASSERT(instructionCount # 0);*)
+			last := instructionCount - 1;
+			instructionFormats[last].isNEON := TRUE;
+			instructionFormats[last].Unsigned := U;
+			instructionFormats[last].Quadword := Q;
+			(*instructionFormats[last].Size := S;*)
+			instructionFormats[last].Length := L;
+			instructionFormats[last].Float := F;
+			instructionFormats[last].Operation := O;
+
+			FOR i := 0 TO 31 DO
+				IF i IN S THEN
+					instructionFormats[last].SizeH := i
+				END
+			END
+		END UpdateNEONInstruction;
+
+		(** Enter a NEON Instruction, using a string format descriptor.
+		*)
+		PROCEDURE EnterNEONInstruction(mnemonic: INTEGER; CONST format: ARRAY OF CHAR; flags: SET; op0, op1, op2, op3, op4, op5: INTEGER);
+		VAR
+			opcode, mask, U, Q, S, L, F, o: SET;
+			last: LONGINT;
+			i: INTEGER;
+		BEGIN
+			(* Enter instruction *)
+			ParseInstructionFormat(format, opcode, mask, U, Q, S, L, F, o);
+			EnterInstruction(mnemonic, opcode, mask, flags, op0, op1, op2, op3, op4, op5);
+
+			(* Add NEON specific information *)
+			last := instructionCount - 1;
+			instructionFormats[last].isNEON := TRUE;
+			instructionFormats[last].Unsigned := U;
+			instructionFormats[last].Quadword := Q;
+			instructionFormats[last].Length := L;
+			instructionFormats[last].Float := F;
+			instructionFormats[last].Operation := o;
+			instructionFormats[last].SizeH := 32;
+			FOR i := 0 TO 31 DO
+				IF i IN S THEN
+					instructionFormats[last].SizeH := i
+				END
+			END
+		END EnterNEONInstruction;
+
+	BEGIN
+		EnterShift(shiftLSL, "LSL");
+		EnterShift(shiftLSR, "LSR");
+		EnterShift(shiftASR, "ASR");
+		EnterShift(shiftROR, "ROR");
+		EnterShift(shiftRRX, "RRX");
+		FOR i := 0 TO NumberShifts-1 DO ASSERT(shiftNames[i] # "") END;
+
+		EnterCoprocessor(CP0, "P0");
+		EnterCoprocessor(CP1, "P1");
+		EnterCoprocessor(CP2, "P2");
+		EnterCoprocessor(CP3, "P3");
+		EnterCoprocessor(CP4, "P4");
+		EnterCoprocessor(CP5, "P5");
+		EnterCoprocessor(CP6, "P6");
+		EnterCoprocessor(CP7, "P7");
+		EnterCoprocessor(CP8, "P8");
+		EnterCoprocessor(CP9, "P9");
+		EnterCoprocessor(CP10, "P10");
+		EnterCoprocessor(CP11, "P11");
+		EnterCoprocessor(CP12, "P12");
+		EnterCoprocessor(CP13, "P13");
+		EnterCoprocessor(CP14, "P14");
+		EnterCoprocessor(CP15, "P15");
+		FOR i := 0 TO NumberCoprocessors - 1 DO ASSERT(coprocessorNames[i] # "") END;
+
+		(* enter register names (note that the preferred name, i.e. alias, is entered after the other variants) *)
+		registerEntryCount := 0;
+
+		EnterRegister(R0, "RES"); EnterRegister(R0, "R0");
+		EnterRegister(R1, "RESHI"); EnterRegister(R1, "R1");
+		EnterRegister(R2, "R2");
+		EnterRegister(R3, "R3");
+		EnterRegister(R4, "R4");
+		EnterRegister(R5, "R5");
+		EnterRegister(R6, "R6");
+		EnterRegister(R7, "R7");
+		EnterRegister(R8, "R8");
+		EnterRegister(R9, "R9");
+		EnterRegister(R10, "R10");
+		EnterRegister(R11, "R11");
+		EnterRegister(R12, "R12"); EnterRegister(R12, "FP");
+		EnterRegister(R13, "R13"); EnterRegister(R13, "SP");
+		EnterRegister(R14, "R14"); EnterRegister(R14, "LR");
+		EnterRegister(R15, "R15"); EnterRegister(R15, "PC");
+
+		EnterRegister(CR0, "C0");
+		EnterRegister(CR1, "C1");
+		EnterRegister(CR2, "C2");
+		EnterRegister(CR3, "C3");
+		EnterRegister(CR4, "C4");
+		EnterRegister(CR5, "C5");
+		EnterRegister(CR6, "C6");
+		EnterRegister(CR7, "C7");
+		EnterRegister(CR8, "C8");
+		EnterRegister(CR9, "C9");
+		EnterRegister(CR10, "C10");
+		EnterRegister(CR11, "C11");
+		EnterRegister(CR12, "C12");
+		EnterRegister(CR13, "C13");
+		EnterRegister(CR14, "C14");
+		EnterRegister(CR15, "C15");
+
+		EnterRegister(DR0, "D0");
+		EnterRegister(DR1, "D1");
+		EnterRegister(DR2, "D2");
+		EnterRegister(DR3, "D3");
+		EnterRegister(DR4, "D4");
+		EnterRegister(DR5, "D5");
+		EnterRegister(DR6, "D6");
+		EnterRegister(DR7, "D7");
+		EnterRegister(DR8, "D8");
+		EnterRegister(DR9, "D9");
+		EnterRegister(DR10, "D10");
+		EnterRegister(DR11, "D11");
+		EnterRegister(DR12, "D12");
+		EnterRegister(DR13, "D13");
+		EnterRegister(DR14, "D14");
+		EnterRegister(DR15, "D15");
+
+		EnterRegister(SR0, "S0");
+		EnterRegister(SR1, "S1");
+		EnterRegister(SR2, "S2");
+		EnterRegister(SR3, "S3");
+		EnterRegister(SR4, "S4");
+		EnterRegister(SR5, "S5");
+		EnterRegister(SR6, "S6");
+		EnterRegister(SR7, "S7");
+		EnterRegister(SR8, "S8");
+		EnterRegister(SR9, "S9");
+		EnterRegister(SR10, "S10");
+		EnterRegister(SR11, "S11");
+		EnterRegister(SR12, "S12");
+		EnterRegister(SR13, "S13");
+		EnterRegister(SR14, "S14");
+		EnterRegister(SR15, "S15");
+		EnterRegister(SR16, "S16");
+		EnterRegister(SR17, "S17");
+		EnterRegister(SR18, "S18");
+		EnterRegister(SR19, "S19");
+		EnterRegister(SR20, "S20");
+		EnterRegister(SR21, "S21");
+		EnterRegister(SR22, "S22");
+		EnterRegister(SR23, "S23");
+		EnterRegister(SR24, "S24");
+		EnterRegister(SR25, "S25");
+		EnterRegister(SR26, "S26");
+		EnterRegister(SR27, "S27");
+		EnterRegister(SR28, "S28");
+		EnterRegister(SR29, "S29");
+		EnterRegister(SR30, "S30");
+		EnterRegister(SR31, "S31");
+
+		EnterRegister(CPSR, "CPSR");
+		EnterRegister(SPSR, "SPSR");
+
+		(* NEON Registers *)
+		EnterRegister(DR16, "D16");
+		EnterRegister(DR17, "D17");
+		EnterRegister(DR18, "D18");
+		EnterRegister(DR19, "D19");
+		EnterRegister(DR20, "D20");
+		EnterRegister(DR21, "D21");
+		EnterRegister(DR22, "D22");
+		EnterRegister(DR23, "D23");
+		EnterRegister(DR24, "D24");
+		EnterRegister(DR25, "D25");
+		EnterRegister(DR26, "D26");
+		EnterRegister(DR27, "D27");
+		EnterRegister(DR28, "D28");
+		EnterRegister(DR29, "D29");
+		EnterRegister(DR30, "D30");
+		EnterRegister(DR31, "D31");
+
+		EnterRegister(QR0, "Q0");
+		EnterRegister(QR1, "Q1");
+		EnterRegister(QR2, "Q2");
+		EnterRegister(QR3, "Q3");
+		EnterRegister(QR4, "Q4");
+		EnterRegister(QR5, "Q5");
+		EnterRegister(QR6, "Q6");
+		EnterRegister(QR7, "Q7");
+		EnterRegister(QR8, "Q8");
+		EnterRegister(QR9, "Q9");
+		EnterRegister(QR10, "Q10");
+		EnterRegister(QR11, "Q11");
+		EnterRegister(QR12, "Q12");
+		EnterRegister(QR13, "Q13");
+		EnterRegister(QR14, "Q14");
+		EnterRegister(QR15, "Q15");
+
+		EnterRegister(FPSID, "FPSID");
+		EnterRegister(FPSCR, "FPSCR");
+		EnterRegister(FPEXC, "FPEXC");
+
+		FOR i := 0 TO NumberRegisterEntries-1 DO ASSERT(registerEntries[i].name # "") END;
+
+
+		(* enter condition names (note that the preferred name, i.e. alias, is entered after the other variants) *)
+		conditionEntryCount := 0;
+		EnterCondition(conditionEQ, "EQ");
+		EnterCondition(conditionNE, "NE");
+		EnterCondition(conditionCS, "CS"); EnterCondition(conditionHS, "HS");
+		EnterCondition(conditionCC, "CC"); EnterCondition(conditionLO, "LO");
+		EnterCondition(conditionMI, "MI");
+		EnterCondition(conditionPL, "PL");
+		EnterCondition(conditionVS, "VS");
+		EnterCondition(conditionVC, "VC");
+		EnterCondition(conditionHI, "HI");
+		EnterCondition(conditionLS, "LS");
+		EnterCondition(conditionGE, "GE");
+		EnterCondition(conditionLT, "LT");
+		EnterCondition(conditionGT, "GT");
+		EnterCondition(conditionLE, "LE");
+		EnterCondition(conditionAL, "AL");
+		EnterCondition(conditionNV, "NV");
+		FOR i := 0 TO NumberConditionEntries-1 DO ASSERT(conditionEntries[i].name # "") END;
+
+		EnterFlag(flagB, "B");
+		EnterFlag(flagBT, "BT");
+		EnterFlag(flagD, "D");
+		EnterFlag(flagDA, "DA");
+		EnterFlag(flagDB, "DB");
+		EnterFlag(flagH, "H");
+		EnterFlag(flagIA, "IA");
+		EnterFlag(flagIB, "IB");
+		EnterFlag(flagL, "L");
+		EnterFlag(flagS, "S");
+		EnterFlag(flagSB, "SB");
+		EnterFlag(flagSH, "SH");
+		EnterFlag(flagT, "T");
+
+		(* NEON flags *)
+		EnterFlag(flagNEON8bits, "8");
+		EnterFlag(flagNEON16bits, "16");
+		EnterFlag(flagNEON32bits, "32");
+		EnterFlag(flagNEON64bits, "64");
+
+		EnterFlag(flagNEONInt, ".I");
+		EnterFlag(flagNEONSigned, ".S");
+		EnterFlag(flagNEONUnsigned, ".U");
+		EnterFlag(flagNEONFloat, ".F");
+		EnterFlag(flagNEONPoly, ".P");
+		EnterFlag(flagNEONUndef, ".X");
+		FOR i := 0 TO NumberFlags-1 DO ASSERT(flagNames[i] # "") END;
+
+		EnterMnemonic(opADC, "ADC");
+		EnterMnemonic(opADD, "ADD");
+		EnterMnemonic(opAND, "AND");
+		EnterMnemonic(opB, "B");
+		EnterMnemonic(opBIC, "BIC");
+		EnterMnemonic(opBKPT, "BKPT");
+		EnterMnemonic(opBL, "BL");
+		EnterMnemonic(opBLX, "BLX");
+		EnterMnemonic(opBX, "BX");
+		EnterMnemonic(opCDP, "CDP");
+		EnterMnemonic(opCDP2, "CDP2");
+		EnterMnemonic(opCLZ, "CLZ");
+		EnterMnemonic(opCMN, "CMN");
+		EnterMnemonic(opCMP, "CMP");
+		EnterMnemonic(opEOR, "EOR");
+
+		EnterMnemonic(opFABSD, "FABSD");
+		EnterMnemonic(opFABSS, "FABSS");
+		EnterMnemonic(opFADDD, "FADDD");
+		EnterMnemonic(opFADDS, "FADDS");
+		EnterMnemonic(opFCMPD, "FCMPD");
+		EnterMnemonic(opFCMPED, "FCMPED");
+		EnterMnemonic(opFCMPES, "FCMPED");
+		EnterMnemonic(opFCMPEZD, "FCMPEZD");
+		EnterMnemonic(opFCMPEZS, "FCMPEZS");
+		EnterMnemonic(opFCMPS, "FCMPDS");
+		EnterMnemonic(opFCMPZD, "FCMPZED");
+		EnterMnemonic(opFCMPZS, "FCMPZS");
+		EnterMnemonic(opFCPYD, "FCPYD");
+		EnterMnemonic(opFCPYS, "FCPYS");
+		EnterMnemonic(opFCVTDS, "FCVTDS");
+		EnterMnemonic(opFCVTSD, "FCVTSD");
+		EnterMnemonic(opFDIVD, "FDIVD");
+		EnterMnemonic(opFDIVS, "FDIVS");
+		EnterMnemonic(opFLDD, "FLDD");
+		EnterMnemonic(opFLDMIAD, "FLDMIAD");
+		EnterMnemonic(opFLDMIAS, "FLDMIAS");
+		EnterMnemonic(opFLDMIAX, "FLDMIAX");
+		EnterMnemonic(opFLDMDBD, "FLDMIAD");
+		EnterMnemonic(opFLDMDBS, "FLDMIAS");
+		EnterMnemonic(opFLDMDBX, "FLDMIAX");
+		EnterMnemonic(opFLDS, "FLDS");
+		EnterMnemonic(opFMACD, "FMACD");
+		EnterMnemonic(opFMACS, "FMACS");
+		EnterMnemonic(opFMDHR, "FMDHR");
+		EnterMnemonic(opFMDLR, "FMDLR");
+		EnterMnemonic(opFMRDH, "FMRDH");
+		EnterMnemonic(opFMRDL, "FMRDL");
+		EnterMnemonic(opFMRS, "FMRS");
+		EnterMnemonic(opFMRX, "FMRX");
+		EnterMnemonic(opFMSCD, "FMSCD");
+		EnterMnemonic(opFMSCS, "FMSCS");
+		EnterMnemonic(opFMSR, "FMSR");
+		EnterMnemonic(opFMSTAT, "FMSTAT");
+		EnterMnemonic(opFMULD, "FMULD");
+		EnterMnemonic(opFMULS, "FMULS");
+		EnterMnemonic(opFMXR, "FMXR");
+		EnterMnemonic(opFNEGD, "FNEGD");
+		EnterMnemonic(opFNEGS, "FNEGS");
+		EnterMnemonic(opFNMACD, "FNMACD");
+		EnterMnemonic(opFNMACS, "FNMACS");
+		EnterMnemonic(opFNMSCD, "FNMSCD");
+		EnterMnemonic(opFNMSCS, "FNMSCS");
+		EnterMnemonic(opFNMULD, "FNMULD");
+		EnterMnemonic(opFNMULS, "FNMULS");
+		EnterMnemonic(opFSITOD, "FSITOD");
+		EnterMnemonic(opFSITOS, "FSITOS");
+		EnterMnemonic(opFSQRTD, "FSQRTD");
+		EnterMnemonic(opFSQRTS, "FSQRTS");
+		EnterMnemonic(opFSTD, "FSTD");
+		EnterMnemonic(opFSTMIAD, "FSTMIAD");
+		EnterMnemonic(opFSTMIAS, "FSTMIAS");
+		EnterMnemonic(opFSTMIAX, "FSTMIAX");
+		EnterMnemonic(opFSTMDBD, "FSTMDBD");
+		EnterMnemonic(opFSTMDBS, "FSTMDBS");
+		EnterMnemonic(opFSTMDBX, "FSTMDBX");
+		EnterMnemonic(opFSTS, "FSTS");
+		EnterMnemonic(opFSUBD, "FSUBD");
+		EnterMnemonic(opFSUBS, "FSUBS");
+		EnterMnemonic(opFTOSID, "FTOSID");
+		EnterMnemonic(opFTOSIZD, "FTOSIZD");
+		EnterMnemonic(opFTOSIS, "FTOSIS");
+		EnterMnemonic(opFTOSIZS, "FTOSIZS");
+		EnterMnemonic(opFTOUID, "FTOUID");
+		EnterMnemonic(opFTOUIZD, "FTOUIZD");
+		EnterMnemonic(opFTOUIS, "FTOUIS");
+		EnterMnemonic(opFTOUIZS, "FTOUIZS");
+		EnterMnemonic(opFUITOD, "FUITOD");
+		EnterMnemonic(opFUITOS, "FUITOS");
+		EnterMnemonic(opLDC, "LDC");
+		EnterMnemonic(opLDC2, "LDC2");
+		EnterMnemonic(opLDM, "LDM");
+		EnterMnemonic(opLDR, "LDR");
+		EnterMnemonic(opMCR, "MCR");
+		EnterMnemonic(opMCR2, "MCR2");
+		EnterMnemonic(opMCRR, "MCRR");
+		EnterMnemonic(opMLA, "MLA");
+		EnterMnemonic(opMOV, "MOV");
+		EnterMnemonic(opMRC, "MRC");
+		EnterMnemonic(opMRC2, "MRC2");
+		EnterMnemonic(opMRRC, "MRRC");
+		EnterMnemonic(opMRS, "MRS");
+		EnterMnemonic(opMSR, "MSR");
+		EnterMnemonic(opMUL, "MUL");
+		EnterMnemonic(opMVN, "MVN");
+		EnterMnemonic(opORR, "ORR");
+		EnterMnemonic(opPLD, "PLD");
+		EnterMnemonic(opQADD, "QADD");
+		EnterMnemonic(opQDADD, "QDADD");
+		EnterMnemonic(opQDSUB, "QDSUB");
+		EnterMnemonic(opQSUB, "QSUB");
+		EnterMnemonic(opRSB, "RSB");
+		EnterMnemonic(opRSC, "RSC");
+		EnterMnemonic(opSBC, "SBC");
+		EnterMnemonic(opSMLABB, "SMLABB");
+		EnterMnemonic(opSMLABT, "SMLABT");
+		EnterMnemonic(opSMLATB, "SMLATB");
+		EnterMnemonic(opSMLATT, "SMLATT");
+		EnterMnemonic(opSMLAL, "SMLAL");
+		EnterMnemonic(opSMLALBB, "SMLALBB");
+		EnterMnemonic(opSMLALBT, "SMLALBT");
+		EnterMnemonic(opSMLALTB, "SMLALTB");
+		EnterMnemonic(opSMLALTT, "SMLALTT");
+		EnterMnemonic(opSMLAWB, "SMLAWB");
+		EnterMnemonic(opSMLAWT, "SMLAWT");
+		EnterMnemonic(opSMULBB, "SMULBB");
+		EnterMnemonic(opSMULBT, "SMULBT");
+		EnterMnemonic(opSMULTB, "SMULTB");
+		EnterMnemonic(opSMULTT, "SMULTT");
+		EnterMnemonic(opSMULWB, "SMULWB");
+		EnterMnemonic(opSMULWT, "SMULWT");
+		EnterMnemonic(opSMULL, "SMULL");
+		EnterMnemonic(opSTC, "STC");
+		EnterMnemonic(opSTC2, "STC2");
+		EnterMnemonic(opSTM, "STM");
+		EnterMnemonic(opSTR, "STR");
+		EnterMnemonic(opSUB, "SUB");
+		EnterMnemonic(opSWI, "SWI");
+		EnterMnemonic(opSWP, "SWP");
+		EnterMnemonic(opTEQ, "TEQ");
+		EnterMnemonic(opTST, "TST");
+		EnterMnemonic(opUMLAL, "UMLAL");
+		EnterMnemonic(opUMULL, "UMULL");
+
+		EnterMnemonic(opISB, "ISB");
+		EnterMnemonic(opLSL, "LSL");
+		EnterMnemonic(opLSR, "LSR");
+		EnterMnemonic(opSEV, "SEV");
+		EnterMnemonic(opDSB, "DSB");
+		EnterMnemonic(opLDREX, "LDREX");
+		EnterMnemonic(opSTREX, "STREX");
+		EnterMnemonic(opADR, "ADR");
+		EnterMnemonic(opLDREXB, "LDREXB");
+		EnterMnemonic(opSTREXB, "STREXB");
+		EnterMnemonic(opDMB, "DMB");
+		EnterMnemonic(opCLREX, "CLREX");
+		EnterMnemonic(opREV, "REV");
+		EnterMnemonic(opREV16, "REV16");
+		EnterMnemonic(opUXTH, "UXTH");
+		EnterMnemonic(opWFE, "WFE");
+
+		(* NEON mnemonics *)
+		EnterMnemonic(opVADD, "VADD");
+		EnterMnemonic(opVADDL, "VADDL");
+		EnterMnemonic(opVADDW, "VADDW");
+		EnterMnemonic(opVMUL, "VMUL");
+		EnterMnemonic(opVMULL, "VMULL");
+		EnterMnemonic(opVMSR, "VMSR");
+		EnterMnemonic(opVMRS, "VMRS");
+		EnterMnemonic(opVLDR, "VLDR");
+		EnterMnemonic(opVSTR, "VSTR");
+		EnterMnemonic(opVDIV, "VDIV");
+		EnterMnemonic(opVMLA, "VMLA");
+		EnterMnemonic(opVMLS, "VMLS");
+		EnterMnemonic(opVMIN, "VMIN");
+		EnterMnemonic(opVMAX, "VMAX");
+		EnterMnemonic(opVSUB, "VSUB");
+		EnterMnemonic(opVABS, "VABS");
+		EnterMnemonic(opVABD, "VABD");
+		EnterMnemonic(opVLD1, "VLD1");
+		EnterMnemonic(opVST1, "VST1");
+		EnterMnemonic(opVPADD, "VPADD");
+		EnterMnemonic(opVMOV, "VMOV");
+
+		FOR i := 0 TO NumberMnemonics-1 DO ASSERT(mnemonics[i].name # "") END;
+
+		instructionCount := 0;
+		(*! adapt number of instructions if you enter a new instruction here *)
+		FOR i := 0 TO NumberInstructions-1 DO instructionFormats[i].mnemonic := None END;
+		EnterInstruction(opADC, {21, 23}, {21..24, 26, 27}, {flagCondition, flagS}, encodingR12, encodingR16, encodingAddressingMode1, None, None, None);
+		EnterInstruction(opADD, {23}, {21..24, 26, 27}, {flagCondition, flagS}, encodingR12, encodingR16, encodingAddressingMode1, None, None, None);
+		EnterInstruction(opAND, {}, {21..24, 26, 27}, {flagCondition, flagS}, encodingR12, encodingR16, encodingAddressingMode1, None, None, None);
+		EnterInstruction(opB, {25, 27}, {24..27}, {flagCondition}, encodingSignedImm24, None, None, None, None, None);
+		EnterInstruction(opBIC, {22..24}, {21..24, 26, 27}, {flagCondition, flagS}, encodingR12, encodingR16, encodingAddressingMode1, None, None, None);
+		EnterInstruction(opBKPT, {4..6, 21, 24, 29..31}, {4..7, 20..31}, {}, encodingImm16, None, None, None, None, None);
+		EnterInstruction(opBL, {24, 25, 27}, {24..27}, {flagCondition}, encodingSignedImm24, None, None, None, None, None);
+		EnterInstruction(opBLX, {25, 27..31}, {25..31}, {}, encodingSignedImm24, None, None, None, None, None);
+		EnterInstruction(opBLX, {4, 5, 8..19 (* SBO *), 21, 24}, {4..7, 20..27}, {flagCondition}, encodingR0, None, None, None, None, None);
+		EnterInstruction(opBX, {4, 21, 8..19 (* SBO *), 24}, {4..7, 20..27}, {flagCondition}, encodingR0, None, None, None, None, None);
+		EnterInstruction(opCDP, {25..27}, {4, 24..27}, {flagCondition}, encodingCoprocessor, encodingOpcode20, encodingCR12, encodingCR16, encodingCR0, encodingOpcode5);
+		EnterInstruction(opCDP2, {25..27}, {4, 24..31}, {}, encodingCoprocessor, encodingOpcode20, encodingCR12, encodingCR16, encodingCR0, encodingOpcode5);
+		EnterInstruction(opCLZ, {4, 8..11 (* SBO *), 16..19 (* SBO *), 21, 22, 24}, {4..7, 20..27}, {flagCondition}, encodingR12, encodingR0, None, None, None, None);
+		EnterInstruction(opCMN, {20..22, 24}, {20..24, 26, 27}, {flagCondition}, encodingR16, encodingAddressingMode1, None, None, None, None);
+		EnterInstruction(opCMP, {20, 22, 24}, {20..24, 26, 27}, {flagCondition}, encodingR16, encodingAddressingMode1, None, None, None, None);
+		EnterInstruction(opEOR, {21}, {21..24, 26, 27}, {flagCondition, flagS}, encodingR12, encodingR16, encodingAddressingMode1, None, None, None);
+		EnterInstruction(opFABSD, {6,7,8,9,11,20,21,23,25,26,27}, {4..11,16..27}, {flagCondition}, encodingDR12, encodingDR0, None, None, None, None);
+		EnterInstruction(opFABSS, {4,6,7,9,11,20,21,23,25,26,27}, {4,6..11,16..21,23..27}, {flagCondition}, encodingFR12, encodingFR0, None, None, None, None);
+		EnterInstruction(opFADDD, {8,9,11,20,21,25,26,27}, {4..11,20..27}, {flagCondition}, encodingDR12, encodingDR16, encodingDR0, None, None, None);
+		EnterInstruction(opFADDS, {9,11,20,21,25,26,27}, {4,6,8..11,20,21,23..27}, {flagCondition}, encodingFR12, encodingFR16, encodingFR0, None, None, None);
+		EnterInstruction(opFCMPD, {6,8,9,11,18,20,21,23,25,26,27}, {4..11,16..27}, {flagCondition}, encodingDR12, encodingDR0, None, None, None, None);
+		EnterInstruction(opFCMPED, {6,7,8,9,11,18,20,21,23,25,26,27}, {4..11,16..27}, {flagCondition}, encodingDR12, encodingDR0, None, None, None, None);
+		EnterInstruction(opFCMPES, {6,7,9,11,18,20,21,23,25,26,27}, {4,6..11,16..21,23..27}, {flagCondition}, encodingFR12, encodingFR0, None, None, None, None);
+		EnterInstruction(opFCMPEZD,{6,7,8,9,11,16,17,20,21,23,25,26,27},{4..11,16..27}, {flagCondition}, encodingDR12, None, None, None, None, None);
+		EnterInstruction(opFCMPEZS,{6,7,9,11,16,18,20,21,23,25,26,27}, {4..11,16..21,23..27}, {flagCondition}, encodingFR12, None, None, None, None, None);
+		EnterInstruction(opFCMPS, {6,9,11,18,20,21,23,25,26,27}, {4,6..11,16..21,23..27}, {flagCondition}, encodingFR12, encodingFR0, None, None, None, None);
+		EnterInstruction(opFCMPZD, {6,8,9,11,16,18,20,21,23,25,26,27}, {4..11,16..27}, {flagCondition}, encodingDR12, None, None, None, None, None);
+		EnterInstruction(opFCMPZS, {6,9,11,16,18,20,21,23,25,26,27}, {4..11,16..21,23..27}, {flagCondition}, encodingFR12, None, None, None, None, None);
+		EnterInstruction(opFCPYD, {6,8,9,11,20,21,23,25,26,27}, {4..11,16..27}, {flagCondition}, encodingDR12, encodingDR0, None, None, None, None);
+		EnterInstruction(opFCPYS, {6,9,11,20,21,23,25,26,27}, {4,6..11,16..21,23..27}, {flagCondition}, encodingFR12, encodingFR0, None, None, None, None);
+		EnterInstruction(opFCVTDS, {6,7,9,11,16,17,18,20,21,23,25,26,27}, {4,6..11,16..27}, {flagCondition}, encodingDR12, encodingFR0, None, None, None, None);
+		EnterInstruction(opFCVTSD, {6,7,8,9,11,16,17,18,20,21,23,25,26,27}, {4..11,16..21,23..27}, {flagCondition}, encodingFR12, encodingDR0, None, None, None, None);
+		EnterInstruction(opFDIVD, {8,9,11,23,25,26,27}, {4..11,20..27}, {flagCondition}, encodingDR12, encodingDR16, encodingDR0, None, None, None);
+		EnterInstruction(opFDIVS, {9,11,23,25,26,27}, {4,6,8..11,20..21,23..27}, {flagCondition}, encodingFR12, encodingFR16, encodingFR0, None, None, None);
+		EnterInstruction(opFLDD, {8,9,11,20,24,26,27}, {8..11,20..22,24..27}, {flagCondition}, encodingDR12, encodingAddressingMode5V, None, None, None, None);
+		EnterInstruction(opFLDMIAD, {8,9,11,20,23,26,27}, {8..11,20,22..27}, {flagCondition,flagBaseRegisterUpdate}, encodingR16, encodingDRegisterList, None, None, None, None);
+		EnterInstruction(opFLDMIAS, {9,11,20,23,26,27}, {8..11,20,23..27}, {flagCondition,flagBaseRegisterUpdate}, encodingR16, encodingFRegisterList, None, None, None, None);
+		EnterInstruction(opFLDMIAX, {8,9,11,20,23,26,27}, {8..11,20,22..27}, {flagCondition,flagBaseRegisterUpdate}, encodingR16, encodingDRegisterList, None, None, None, None);
+		EnterInstruction(opFLDMDBD, {8,9,11,20,24,26,27}, {8..11,20,22..27}, {flagCondition,flagBaseRegisterUpdate}, encodingR16, encodingDRegisterList, None, None, None, None);
+		EnterInstruction(opFLDMDBS, {9,11,20,24,26,27}, {8..11,20,23..27}, {flagCondition,flagBaseRegisterUpdate}, encodingR16, encodingFRegisterList, None, None, None, None);
+		EnterInstruction(opFLDMDBX, {8,9,11,20,24,26,27}, {8..11,20,22..27}, {flagCondition,flagBaseRegisterUpdate}, encodingR16, encodingDRegisterList, None, None, None, None);
+		EnterInstruction(opFLDS, {9,11,20,24,26,27}, {8..11,20..21,24..27}, {flagCondition}, encodingFR12, encodingAddressingMode5V, None, None, None, None);
+		EnterInstruction(opFMACD, {8,9,11,25,26,27}, {4..11,20..27}, {flagCondition}, encodingDR12, encodingDR16, encodingDR0, None, None, None);
+		EnterInstruction(opFMACS, {9,11,25..27}, {4,6,8..11,20..21,23..27}, {flagCondition}, encodingFR12, encodingFR16, encodingFR0, None, None, None);
+		EnterInstruction(opFMDHR, {4,8,9,11,21,25,26,27}, {4,7..11,20..27}, {flagCondition}, encodingDR16, encodingR12, None, None, None, None);
+		EnterInstruction(opFMDLR, {4,8,9,11,25,26,27}, {4,7..11,20..27}, {flagCondition}, encodingDR16, encodingR12, None, None, None, None);
+		EnterInstruction(opFMRDH, {4,8,9,11,20,21,25,26,27}, {4,7..11,20..27}, {flagCondition}, encodingR12, encodingDR16, None, None, None, None);
+		EnterInstruction(opFMRDL, {4,8,9,11,20,25,26,27}, {4,7..11,20..27}, {flagCondition}, encodingR12, encodingDR16, None, None, None, None);
+		EnterInstruction(opFMRS, {4,9,11,20,25,26,27}, {4,8..11,20..27}, {flagCondition}, encodingR12, encodingFR16, None, None, None, None);
+		EnterInstruction(opFMRX, {4,9,11,20..23,25..27}, {4,7..11,20..27}, {flagCondition}, (*!todo*)None, None, None, None, None, None);
+		EnterInstruction(opFMSCD, {8,9,11,20,25,26,27}, {4..11,20..27}, {flagCondition}, encodingDR12, encodingDR16, encodingDR0, None, None, None);
+		EnterInstruction(opFMSCS, {9,11,20,25,26,27}, {4,6,8..11,20..21,23..27}, {flagCondition}, encodingFR12, encodingFR16, encodingFR0, None, None, None);
+		EnterInstruction(opFMSR, {4,9,11,25,26,27}, {4,8..11,20..27}, {flagCondition}, encodingFR16, encodingR12, None, None, None, None);
+		EnterInstruction(opFMSTAT, {4,9,11,12..16,20..23,25..27}, {4,8..27}, {flagCondition}, None, None, None, None, None, None);
+		EnterInstruction(opFMULD, {8,9,11,21,25,26,27}, {4..11,20..27}, {flagCondition}, encodingDR12, encodingDR16, encodingDR0, None, None, None);
+		EnterInstruction(opFMULS, {9,11,21,25,26,27}, {4,6,8..11,20..21,23..27}, {flagCondition}, encodingFR12, encodingFR16, encodingFR0, None, None, None);
+		EnterInstruction(opFMXR, {4,9,11,21..23,25..27}, {4,7..11,20..27}, {flagCondition}, (*!todo*)None, None, None, None, None, None);
+		EnterInstruction(opFNEGD, {6,8,9,11,16,20,21,23,25,26,27}, {4..11,16..27}, {flagCondition}, encodingDR12, encodingDR0, None, None, None, None);
+		EnterInstruction(opFNEGS	, {6,9,11,16,20,21,23,25,26,27}, {4,6..11,16..21,23..27}, {flagCondition}, encodingFR12, encodingFR0, None, None, None, None);
+		EnterInstruction(opFNMACD, {6,8,9,11,25..27}, {4..11,20..27}, {flagCondition}, encodingDR12, encodingDR16, encodingDR0, None, None, None);
+		EnterInstruction(opFNMACS, {6,9,11,25,26,27}, {4,6,8..11,20,21,23..27}, {flagCondition}, encodingFR12, encodingFR16, encodingFR0, None, None, None);
+		EnterInstruction(opFNMSCD, {6,8,9,11,20,25,26,27}, {4..11,20..27}, {flagCondition}, encodingDR12, encodingDR16, encodingDR0, None, None, None);
+		EnterInstruction(opFNMSCS, {6,9,11,20,25,26,27}, {4..11,20..21,23..27}, {flagCondition}, encodingFR12, encodingFR16, encodingFR0, None, None, None);
+		EnterInstruction(opFNMULD, {6,8,9,11,21,25,26,27}, {4..11,20..27}, {flagCondition}, encodingDR12, encodingDR16, encodingDR0, None, None, None);
+		EnterInstruction(opFNMULS, {6,9,11,21,25,26,27}, {4,6,8..11,20..21,23..27}, {flagCondition}, encodingFR12, encodingFR16, encodingFR0, None, None, None);
+		EnterInstruction(opFSITOD, {6..9,11,19..21,23,25..27}, {4,6..11,16..27}, {flagCondition}, encodingDR12, encodingFR0, None, None, None, None);
+		EnterInstruction(opFSITOS, {6,7,9,11,19..21,23,25..27}, {4,6..11,16..21,23..27}, {flagCondition}, encodingFR12, encodingFR0, None, None, None, None);
+		EnterInstruction(opFSQRTD, {6..9,11,16,20,21,23,25..27}, {4..11,16..27}, {flagCondition}, encodingDR12, encodingDR0, None, None, None, None);
+		EnterInstruction(opFSQRTS, {6,7,9,11,16,20,21,23,25..27}, {4,6..11,16..21,23..27}, {flagCondition}, encodingFR12, encodingFR0, None, None, None, None);
+		EnterInstruction(opFSTD, {8,9,11,24,26,27}, {8..11,20..22,24..27}, {flagCondition}, encodingDR12, encodingAddressingMode5V, None, None, None, None);
+		EnterInstruction(opFSTMIAD, {8,9,11,23,26,27}, {8..11,20,22..27}, {flagCondition,flagBaseRegisterUpdate}, encodingR16, encodingDRegisterList, None, None, None, None);
+		EnterInstruction(opFSTMIAS, {9,11,23,26,27}, {8..11,20,23..27}, {flagCondition,flagBaseRegisterUpdate}, encodingR16, encodingFRegisterList, None, None, None, None);
+		EnterInstruction(opFSTMIAX, {8,9,11,23,26,27}, {8..11,20,22..27}, {flagCondition,flagBaseRegisterUpdate}, encodingR16, encodingDRegisterList, None, None, None, None);
+		EnterInstruction(opFSTMDBD, {8,9,11,24,26,27}, {8..11,20,22..27}, {flagCondition,flagBaseRegisterUpdate},  encodingR16, encodingDRegisterList, None, None, None, None);
+		EnterInstruction(opFSTMDBS, {9,11,24,26,27}, {8..11,20,23..27}, {flagCondition,flagBaseRegisterUpdate}, encodingR16,encodingFRegisterList, None, None, None, None);
+		EnterInstruction(opFSTMDBX, {8,9,11,24,26,27}, {8..11,20,22..27}, {flagCondition,flagBaseRegisterUpdate}, encodingR16, encodingDRegisterList, None, None, None, None);
+		EnterInstruction(opFSTS, {9,11,24,26,27}, {8..11,20,21, 24..27}, {flagCondition}, encodingFR12, encodingAddressingMode5V, None, None, None, None);
+		EnterInstruction(opFSUBD, {6,8,9,11,20,21,25..27}, {4..11,20..27}, {flagCondition}, encodingDR12, encodingDR16, encodingDR0, None, None, None);
+		EnterInstruction(opFSUBS, {6,9,11,20,21,25,26,27}, {4,6,8..11,20..21,23..27}, {flagCondition}, encodingFR12, encodingFR16, encodingFR0, None, None, None);
+		EnterInstruction(opFTOSID, {6,8,9,11,16,18..21,23,25..27}, {4..11,16..21,23..27}, {flagCondition}, encodingFR12, encodingDR0, None, None, None, None);
+		EnterInstruction(opFTOSIZD, {6,7,8,9,11,16,18..21,23,25..27}, {4..11,16..21,23..27}, {flagCondition}, encodingFR12, encodingDR0, None, None, None, None);
+		EnterInstruction(opFTOSIS, {6,9,11,16,18..21,23,25..27}, {4,6..11,16..21,23..27}, {flagCondition}, encodingFR12, encodingFR0, None, None, None, None);
+		EnterInstruction(opFTOSIZS, {6,7,9,11,16,18..21,23,25..27}, {4,6..11,16..21,23..27}, {flagCondition}, encodingFR12, encodingFR0, None, None, None, None);
+		EnterInstruction(opFTOUID, {6,8,9,11,18..21,23,25..27}, {4..11,16..21,23..27}, {flagCondition}, encodingFR12, encodingDR0, None, None, None, None);
+		EnterInstruction(opFTOUIZD, {6,7,8,9,11,18..21,23,25..27}, {4..11,16..21,23..27}, {flagCondition}, encodingFR12, encodingDR0, None, None, None, None);
+		EnterInstruction(opFTOUIS, {6,9,11,18..21,23,25..27}, {4,6..11,16..21,23..27}, {flagCondition}, encodingFR12, encodingFR0, None, None, None, None);
+		EnterInstruction(opFTOUIZS, {6,7,9,11,18..21,23,25..27}, {4,6..11,16..21,23..27}, {flagCondition}, encodingFR12, encodingFR0, None, None, None, None);
+		EnterInstruction(opFUITOD, {6,8,9,11,19..21,23,25..27}, {4,6..11,16..27}, {flagCondition}, encodingDR12, encodingFR0, None, None, None, None);
+		EnterInstruction(opFUITOS, {6,9,11,19..21,23,25..27}, {4,6..11,16..21,23..27}, {flagCondition}, encodingFR12, encodingFR0, None, None, None, None);
+		EnterInstruction(opLDC, {20, 26, 27}, {20, 25..27}, {flagCondition, flagL}, encodingCoprocessor, encodingCR12, encodingAddressingMode5, None, None, None);
+		EnterInstruction(opLDC2, {20, 26, 27, 28..31}, {20, 25..27, 28..31}, {flagL}, encodingCoprocessor, encodingCR12, encodingAddressingMode5, None, None, None);
+		(*EnterInstruction(opLDM, {20, 27}, {20, 25..27}, {flagCondition, flagIA, flagIB, flagDA, flagDB, flagUserMode, flagBaseRegisterUpdate}, encodingR16, encodingRegisterList, None, None, None, None);*)
+		EnterInstruction(opLDM, {20, 27}, {20, 25..27}, {flagCondition, flagIA, flagIB, flagDA, flagDB, flagBaseRegisterUpdate}, encodingR16, encodingRegisterList, None, None, None, None);
+		EnterInstruction(opLDR, {20, 26}, {20, 26..27}, {flagCondition, flagB, flagT, flagBT}, encodingR12, encodingAddressingMode2, None, None, None, None);
+		EnterInstruction(opLDR, {4, 7, 20}, {4, 7, 20, 25..27}, {flagCondition, flagH, flagSH, flagSB, flagD}, encodingR12, encodingAddressingMode3, None, None, None, None);
+		EnterInstruction(opMCR, {4, 25, 26, 27}, {4, 20, 24..27}, {flagCondition}, encodingCoprocessor, encodingOpcode21, encodingR12, encodingCR16, encodingCR0, encodingOpcode5);
+		EnterInstruction(opMCR2, {4, 25, 26, 27, 28..31}, {4, 20, 24..27, 28..31}, {}, encodingCoprocessor, encodingOpcode21, encodingCR12, encodingCR16, encodingCR0, encodingOpcode5);
+		EnterInstruction(opMCRR, {22, 26, 27}, {20..27}, {flagCondition}, encodingCoprocessor, encodingOpcode4, encodingR12, encodingR16, encodingCR0, None);
+		EnterInstruction(opMLA, {4, 7, 21}, {4..7, 21..27}, {flagCondition, flagS}, encodingR12, encodingR0, encodingR8, encodingR16, None, None);
+		EnterInstruction(opMOV, {21, 23, 24}, {21..24, 26, 27}, {flagCondition, flagS}, encodingR12, encodingAddressingMode1, None, None, None, None);
+		EnterInstruction(opMRC, {4, 20, 25, 26, 27}, {4, 20, 24..27}, {flagCondition}, encodingCoprocessor, encodingOpcode21, encodingR12, encodingCR16, encodingCR0, encodingOpcode5);
+		EnterInstruction(opMRC2, {4, 20, 25, 26, 27, 28..31}, {4, 20, 24..27, 28..31}, {}, encodingCoprocessor, encodingOpcode21, encodingR12, encodingCR16, encodingCR0, encodingOpcode5);
+		EnterInstruction(opMRRC, {20, 22, 26, 27}, {20..27}, {flagCondition}, encodingCoprocessor, encodingOpcode4, encodingR12, encodingR16, encodingCR0, None);
+		EnterInstruction(opMRS, {24, 16..19 (* SBO *)}, {20, 21, 23..27}, {flagCondition}, encodingR12, encodingPSR, None, None, None, None);
+		EnterInstruction(opMSR, {21, 24, 25, 12..15 (* SBO *)}, {20, 21, 23..27}, {flagCondition}, encodingFields, encodingRotImm8, None, None, None, None);
+		EnterInstruction(opMSR, {21, 24, 12..15 (* SBO *)}, {4..7, 20, 21, 23..27}, {flagCondition}, encodingFields, encodingR0, None, None, None, None);
+		EnterInstruction(opMUL, {4, 7}, {4..7, 21..27}, {flagCondition, flagS}, encodingR16, encodingR0, encodingR8, None, None, None);
+		EnterInstruction(opMVN, {21..24}, {21..24, 26, 27}, {flagCondition, flagS}, encodingR12, encodingAddressingMode1, None, None, None, None);
+		EnterInstruction(opORR, {23, 24}, {21..24, 26, 27}, {flagCondition, flagS}, encodingR12, encodingR16, encodingAddressingMode1, None, None, None);
+		EnterInstruction(opPLD, {28..31, 26, 24, 22, 20, 12..15}, {26..31, 24, 20..22, 12..15}, {flagCondition}, encodingAddressingMode2, None, None, None, None, None);
+		EnterInstruction(opQADD, {4, 6, 24}, {4..7, 20..27}, {flagCondition}, encodingR12, encodingR0, encodingR16, None, None, None);
+		EnterInstruction(opQDADD, {4, 6, 22, 24}, {4..7, 20..27}, {flagCondition}, encodingR12, encodingR0, encodingR16, None, None, None);
+		EnterInstruction(opQSUB, {4, 6, 21, 24}, {4..7, 20..27}, {flagCondition}, encodingR12, encodingR0, encodingR16, None, None, None);
+		EnterInstruction(opQDSUB, {4, 6, 21, 22, 24}, {4..7, 20..27}, {flagCondition}, encodingR12, encodingR0, encodingR16, None, None, None);
+		EnterInstruction(opRSB, {21, 22}, {21..24, 26, 27}, {flagCondition, flagS}, encodingR12, encodingR16, encodingAddressingMode1, None, None, None);
+		EnterInstruction(opRSC, {21, 22, 23}, {21..24, 26, 27}, {flagCondition, flagS}, encodingR12, encodingR16, encodingAddressingMode1, None, None, None);
+		EnterInstruction(opSBC, {22, 23}, {21..24, 26, 27}, {flagCondition, flagS}, encodingR12, encodingR16, encodingAddressingMode1, None, None, None);
+		EnterInstruction(opSMLAL, {4, 7, 21, 22, 23}, {4..7, 21..27}, {flagCondition, flagS}, encodingR12, encodingR16, encodingR0, encodingR8, None, None);
+		EnterInstruction(opSMULL, {4, 7, 22, 23}, {4..7, 21..27}, {flagCondition, flagS}, encodingR12, encodingR16, encodingR0, encodingR8, None, None);
+		EnterInstruction(opSMLABB, {7, 24}, {4..7, 20..27}, {flagCondition}, encodingR16, encodingR0, encodingR8, encodingR12, None, None);
+		EnterInstruction(opSMLABT, {5, 7, 24}, {4..7, 20..27}, {flagCondition}, encodingR16, encodingR0, encodingR8, encodingR12, None, None);
+		EnterInstruction(opSMLATB, {6, 7, 24}, {4..7, 20..27}, {flagCondition}, encodingR16, encodingR0, encodingR8, encodingR12, None, None);
+		EnterInstruction(opSMLATT, {5, 6, 7, 24}, {4..7, 20..27}, {flagCondition}, encodingR16, encodingR0, encodingR8, encodingR12, None, None);
+		EnterInstruction(opSMLALBB, {7, 22, 24}, {4..7, 20..27}, {flagCondition}, encodingR12, encodingR16, encodingR0, encodingR8, None, None);
+		EnterInstruction(opSMLALBT, {5, 7, 22, 24}, {4..7, 20..27}, {flagCondition}, encodingR12, encodingR16, encodingR0, encodingR8, None, None);
+		EnterInstruction(opSMLALTB, {6, 7, 22, 24}, {4..7, 20..27}, {flagCondition}, encodingR12, encodingR16, encodingR0, encodingR8, None, None);
+		EnterInstruction(opSMLALTT, {5, 6, 7, 22, 24}, {4..7, 20..27}, {flagCondition}, encodingR12, encodingR16, encodingR0, encodingR8, None, None);
+		EnterInstruction(opSMLAWB, {7, 21, 24}, {4..7, 20..27}, {flagCondition}, encodingR16, encodingR0, encodingR8, encodingR12, None, None);
+		EnterInstruction(opSMLAWT, {6, 7, 21, 24}, {4..7, 20..27}, {flagCondition}, encodingR16, encodingR0, encodingR8, encodingR12, None, None);
+		EnterInstruction(opSMULBB, {7, 21, 22, 24}, {4..7, 20..27}, {flagCondition}, encodingR16, encodingR0, encodingR8, None, None, None);
+		EnterInstruction(opSMULBT, {5, 21, 7, 22, 24}, {4..7, 20..27}, {flagCondition}, encodingR16, encodingR0, encodingR8, None, None, None);
+		EnterInstruction(opSMULTB, {6, 7, 21, 22, 24}, {4..7, 20..27}, {flagCondition}, encodingR16, encodingR0, encodingR8, None, None, None);
+		EnterInstruction(opSMULTT, {5, 6, 7, 21, 22, 24}, {4..7, 20..27}, {flagCondition}, encodingR16, encodingR0, encodingR8, None, None, None);
+		EnterInstruction(opSMULWB, {5, 7, 21, 24}, {4..7, 20..27}, {flagCondition}, encodingR16, encodingR0, encodingR8, None, None, None);
+		EnterInstruction(opSMULWT, {5, 6, 7, 21, 24}, {4..7, 20..27}, {flagCondition}, encodingR16, encodingR0, encodingR8, None, None, None);
+		EnterInstruction(opSTC, {26, 27}, {20, 25..27}, {flagCondition, flagL}, encodingCoprocessor, encodingCR12, encodingAddressingMode5, None, None, None);
+		EnterInstruction(opSTC2, {26, 27, 28..31}, {20, 25..27, 28..31}, {flagL}, encodingCoprocessor, encodingCR12, encodingAddressingMode5, None, None, None);
+		(*EnterInstruction(opSTM, {27}, {20, 25..27}, {flagCondition, flagIA, flagIB, flagDA, flagDB, flagUserMode, flagBaseRegisterUpdate}, encodingR16, encodingRegisterList, None, None, None, None);*)
+		EnterInstruction(opSTM, {27}, {20, 25..27}, {flagCondition, flagIA, flagIB, flagDA, flagDB, flagBaseRegisterUpdate}, encodingR16, encodingRegisterList, None, None, None, None);
+		EnterInstruction(opSTR, {26}, {20, 26, 27}, {flagCondition, flagB, flagT, flagBT}, encodingR12, encodingAddressingMode2, None, None, None, None);
+		EnterInstruction(opSTR, {4, 5, 7}, {4, 7, 20, 25..27}, {flagCondition, flagH, flagSH, flagSB, flagD}, encodingR12, encodingAddressingMode3, None, None, None, None);
+		EnterInstruction(opSUB, {22}, {21..24, 26, 27}, {flagCondition, flagS}, encodingR12, encodingR16, encodingAddressingMode1, None, None, None);
+		EnterInstruction(opSWI, {24..27}, {24..27}, {flagCondition}, encodingImm24, None, None, None, None, None);
+		EnterInstruction(opSWP, {4, 7, 24}, {4..7, 20..27}, {flagCondition}, encodingR12, encodingR0, encodingR16 (*! optional *), None, None, None);
+		EnterInstruction(opSWP, {4, 7, 22, 24}, {4..7, 20..27}, {flagCondition, flagB}, encodingR12, encodingR0, encodingR16 (*! optional *), None, None, None);
+		EnterInstruction(opTEQ, {20, 21, 24}, {20..24, 26, 27}, {flagCondition}, encodingR16, encodingAddressingMode1, None, None, None, None);
+		EnterInstruction(opTST, {20, 24}, {20..24, 26, 27}, {flagCondition}, encodingR16, encodingAddressingMode1, None, None, None, None);
+		EnterInstruction(opUMLAL, {4, 7, 21, 23}, {4..7, 21..27}, {flagCondition, flagS}, encodingR12, encodingR16, encodingR0, encodingR8, None, None);
+		EnterInstruction(opUMULL, {4, 7, 23}, {4..7, 21..27}, {flagCondition, flagS}, encodingR12, encodingR16, encodingR0, encodingR8, None, None);
+
+		EnterNEONInstruction(opLDM, "XXXX 100X X101 XXXX 0XXX XXXX XXXX XXXX", {flagCondition, flagIA, flagDA, flagIB, flagDB, flagUserMode}, encodingR16, encodingRegisterList, None, None, None, None);
+		EnterNEONInstruction(opSTM, "XXXX 100X X100 XXXX XXXX XXXX XXXX XXXX", {flagCondition, flagIA, flagDA, flagIB, flagDB, flagUserMode}, encodingR16, encodingRegisterList, None, None, None, None);
+		EnterNEONInstruction(opISB, "1111 0101 0111 1111 1111 0000 0110 1111", {}, None, None, None, None, None, None);
+		EnterNEONInstruction(opLSL, "XXXX 0001 1010 0000 XXXX XXXX X000 XXXX", {flagCondition, flagS}, encodingR12, encodingR0, encodingImm7to11, None, None, None);
+		EnterNEONInstruction(opLSL, "XXXX 0001 1010 0000 XXXX XXXX 0001 XXXX", {flagCondition, flagS}, encodingR12, encodingR0, encodingR8, None, None, None);
+		EnterNEONInstruction(opLSR, "XXXX 0001 1010 0000 XXXX XXXX X010 XXXX", {flagCondition, flagS}, encodingR12, encodingR0, encodingImm7to11, None, None, None);
+		EnterNEONInstruction(opSEV, "XXXX 0011 0010 0000 1111 0000 0000 0100", {flagCondition}, None, None, None, None, None, None);
+		EnterNEONInstruction(opDSB, "1111 0101 0111 1111 1111 0000 0100 1111", {}, None, None, None, None, None, None); (* Full System Reset only *)
+		EnterNEONInstruction(opLDREX, "XXXX 0001 1001 XXXX XXXX 1111 1001 1111", {flagCondition}, encodingR12, encodingR16, None, None, None, None);
+		EnterNEONInstruction(opSTREX, "XXXX  0001 1000 XXXX XXXX 1111 1001 XXXX", {flagCondition}, encodingR12, encodingR0, encodingR16, None, None, None);
+		(* Only for labels after current instruction *)
+		EnterNEONInstruction(opADR, "XXXX 0010 1000 1111 XXXX XXXX XXXX XXXX", {flagCondition}, encodingR12, encodingRotImm8, None, None, None, None);
+		EnterNEONInstruction(opLDREXB, "XXXX 0001 1101 XXXX XXXX 1111 1001 1111", {flagCondition}, encodingR12, encodingR16, None, None, None, None);
+		EnterNEONInstruction(opSTREXB, "XXXX 0001 1100 XXXX XXXX 1111 1001 XXXX", {flagCondition}, encodingR12, encodingR0, encodingR16, None, None, None);
+		EnterNEONInstruction(opDMB, "1111 0101 0111 1111 1111 0000 0101 1111", {}, None, None, None, None, None, None);
+		EnterNEONInstruction(opCLREX, "1111 0101 0111 1111 1111 0000 0001 1111", {}, None, None, None, None, None, None);
+		EnterNEONInstruction(opREV, "XXXX 0110 1011 1111 XXXX 1111 0011 XXXX", {flagCondition}, encodingR12, encodingR0, None, None, None, None);
+		EnterNEONInstruction(opREV16, "XXXX 0110 1011 1111 XXXX 1111 1011 XXXX", {flagCondition}, encodingR12, encodingR0, None, None, None, None);
+		EnterNEONInstruction(opUXTH, "XXXX 0110 1111 1111 XXXX XX00 0111 XXXX", {flagCondition}, encodingR12, encodingR0, None, None, None, None);
+		EnterNEONInstruction(opWFE, "XXXX 0011 0010 0000 1111 0000 0000 0010", {flagCondition}, None, None, None, None, None, None);
+
+		(* NEON instructions *)
+		EnterNEONInstruction(opVADD, "1111 0010 0XSS XXXX XXXX 1000 XQX0 XXXX", {flagNEONInt, flagNEON8bits, flagNEON16bits, flagNEON32bits, flagNEON64bits}, encodingNEONQorDd, encodingNEONQorDn, encodingNEONQorDm, None, None, None);
+		EnterNEONInstruction(opVADD, "1111 0010 0X00 XXXX XXXX 1101 XQX0 XXXX", {flagNEONFloat, flagNEON32bits}, encodingNEONQorDd, encodingNEONQorDn, encodingNEONQorDm, None, None, None);
+		EnterNEONInstruction(opVADD, "XXXX 1110 0X11 XXXX XXXX 101Q X0X0 XXXX", {flagCondition, flagNEONFloat, flagNEON64bits, flagNEON32bits}, encodingNEONDorSd, encodingNEONDorSn, encodingNEONDorSm, None, None, None);
+		EnterNEONInstruction(opVADDL, "1111 001U 1XSS XXXX XXXX 0000 X0X0 XXXX", {flagNEONSigned, flagNEONUnsigned, flagNEON8bits, flagNEON16bits, flagNEON32bits}, encodingNEONQd, encodingNEONDm, encodingNEONDn, None, None, None);
+		EnterNEONInstruction(opVADDW, "1111 001U 1XSS XXXX XXXX 0001 X0X0 XXXX", {flagNEONSigned, flagNEONUnsigned, flagNEON8bits, flagNEON16bits, flagNEON32bits}, encodingNEONQd, encodingNEONQm, encodingNEONDn, None, None, None);
+		EnterNEONInstruction(opVMUL, "1111 0010 0XSS XXXX XXXX 1001 XQX1 XXXX", {flagNEONInt, flagNEONUnsigned, flagNEONSigned, flagNEON8bits, flagNEON16bits, flagNEON32bits}, encodingNEONQorDd, encodingNEONQorDn, encodingNEONQorDm, None, None, None); (*! Does not support polynomail types yet *)
+		EnterNEONInstruction(opVMUL, "1111 0011 0X00 XXXX XXXX 1101 XQX1 XXXX", {flagNEONFloat, flagNEON32bits}, encodingNEONQorDd, encodingNEONQorDn, encodingNEONQorDm, None, None, None);
+		EnterNEONInstruction(opVMUL, "XXXX 1110 0X10 XXXX XXXX 101Q X0X0 XXXX", {flagNEONFloat, flagNEON32bits, flagNEON64bits, flagCondition}, encodingNEONDorSd, encodingNEONDorSm, encodingNEONDorSn, None, None, None);
+		EnterNEONInstruction(opVMULL, "1111 001U 1XSS XXXX XXXX 1010 X1X0 XXXX", {flagNEONSigned, flagNEONUnsigned, flagNEON16bits, flagNEON32bits}, encodingNEONQd, encodingNEONQm, encodingNEONDn, None, None, None);
+		EnterNEONInstruction(opVMSR, "XXXX 1110 1110 XXXX XXXX 1010 0001 0000", {flagCondition}, encodingNEONSysReg, encodingR12, None, None, None, None);
+		EnterNEONInstruction(opVMRS, "XXXX 1110 1111 XXXX XXXX 1010 0001 0000", {flagCondition}, encodingR12, encodingNEONSysReg, None, None, None, None);
+		EnterNEONInstruction(opVLDR, "XXXX 1101 UX01 XXXX XXXX 1011 XXXX XXXX", {flagCondition, flagNEON64bits}, encodingNEONDd, encodingR16, encodingNEONSigned8bitImm, None, None, None);
+		EnterNEONInstruction(opVLDR, "XXXX 1101 UX01 XXXX XXXX 1010 XXXX XXXX", {flagCondition, flagNEON32bits}, encodingNEONSd, encodingR16, encodingNEONSigned8bitImm, None, None, None);
+		EnterNEONInstruction(opVSTR, "XXXX 1101 UX00 XXXX XXXX 1011 XXXX XXXX", {flagCondition, flagNEON64bits}, encodingNEONDd, encodingR16, encodingNEONSigned8bitImm, None, None, None);
+		EnterNEONInstruction(opVSTR, "XXXX 1101 UX00 XXXX XXXX 1010 XXXX XXXX", {flagCondition, flagNEON32bits}, encodingNEONSd, encodingR16, encodingNEONSigned8bitImm, None, None, None);
+		EnterNEONInstruction(opVDIV, "XXXX 1110 1X00 XXXX XXXX 101Q X0X0 XXXX", {flagCondition, flagNEONFloat, flagNEON32bits, flagNEON64bits}, encodingNEONDorSd, encodingNEONDorSn, encodingNEONDorSm, None, None, None);
+		EnterNEONInstruction(opVMLA, "1111 0010 0XSS XXXX XXXX 1001 XQX0 XXXX", {flagNEONInt, flagNEONSigned, flagNEONUnsigned, flagNEON8bits, flagNEON16bits, flagNEON32bits}, encodingNEONQorDd, encodingNEONQorDn, encodingNEONQorDm, None, None, None);
+		EnterNEONInstruction(opVMLA, "1111 0010 0X00 XXXX XXXX 1101 XQX1 XXXX", {flagNEONFloat, flagNEON32bits}, encodingNEONQorDd, encodingNEONQorDn, encodingNEONQorDm, None, None, None);
+		EnterNEONInstruction(opVMLA, "XXXX 1110 0X00 XXXX XXXX 101Q X0X0 XXXX", {flagCondition, flagNEONFloat, flagNEON64bits, flagNEON32bits}, encodingNEONDorSd, encodingNEONDorSn, encodingNEONDorSm, None, None, None);
+		EnterNEONInstruction(opVMLS, "1111 0011 0XSS XXXX XXXX 1001 XQX0 XXXX", {flagNEONInt, flagNEONSigned, flagNEONUnsigned, flagNEON8bits, flagNEON16bits, flagNEON32bits}, encodingNEONQorDd, encodingNEONQorDn, encodingNEONQorDm, None, None, None);
+		EnterNEONInstruction(opVMLS, "1111 0010 0X10 XXXX XXXX 1101 XQX1 XXXX", {flagNEONFloat, flagNEON32bits}, encodingNEONQorDd, encodingNEONQorDn, encodingNEONQorDm, None, None, None);
+		EnterNEONInstruction(opVMLS, "XXXX 1110 0X00 XXXX XXXX 101Q X1X0 XXXX", {flagCondition, flagNEONFloat, flagNEON64bits, flagNEON32bits}, encodingNEONDorSd, encodingNEONDorSn, encodingNEONDorSm, None, None, None);
+		EnterNEONInstruction(opVMIN, "1111 001U 0XSS XXXX XXXX 0110 XQX1 XXXX", {flagNEONSigned, flagNEONUnsigned, flagNEON8bits, flagNEON16bits, flagNEON32bits}, encodingNEONQorDd, encodingNEONQorDn, encodingNEONQorDm, None, None, None);
+		EnterNEONInstruction(opVMAX, "1111 001U 0XSS XXXX XXXX 0110 XQX0 XXXX", {flagNEONSigned, flagNEONUnsigned, flagNEON8bits, flagNEON16bits, flagNEON32bits}, encodingNEONQorDd, encodingNEONQorDn, encodingNEONQorDm, None, None, None);
+		EnterNEONInstruction(opVSUB, "1111 0011 0XSS XXXX XXXX 1000 XQX0 XXXX", {flagNEONInt, flagNEON8bits, flagNEON16bits, flagNEON32bits, flagNEON64bits}, encodingNEONQorDd, encodingNEONQorDn, encodingNEONQorDm, None, None, None);
+		EnterNEONInstruction(opVABS, "1111 0011 1X11 SS01 XXXX 0o11 0QX0 XXXX", {flagNEONSigned, flagNEONFloat,flagNEON8bits, flagNEON16bits, flagNEON32bits}, encodingNEONQorDd, encodingNEONQorDm, None, None, None, None);
+		EnterNEONInstruction(opVABD, "1111 001U 0XSS XXXX XXXX 0111 XQX0 XXXX", {flagNEONSigned, flagNEONUnsigned, flagNEON8bits, flagNEON16bits, flagNEON32bits}, encodingNEONQorDd, encodingNEONQorDn, encodingNEONQorDm, None, None, None);
+		(* Fixed alignment and list size... not using [] or {} syntax *)
+		EnterNEONInstruction(opVLD1, "1111 0100 0X10 XXXX XXXX 1010 SS00 1111", {flagNEON8bits, flagNEON16bits, flagNEON32bits, flagNEON64bits}, encodingNEONDd, encodingR16, None, None, None, None);
+		EnterNEONInstruction(opVST1, "1111 0100 0X00 XXXX XXXX 1010 SS00 1111", {flagNEON8bits, flagNEON16bits, flagNEON32bits, flagNEON64bits}, encodingNEONDd, encodingR16, None, None, None, None);
+
+		EnterNEONInstruction(opVPADD, "1111 0011 0X00 XXXX XXXX 1101 X0X0 XXXX", {flagNEON32bits, flagNEONFloat}, encodingNEONDd, encodingNEONDn, encodingNEONDm, None, None, None);
+		EnterNEONInstruction(opVMOV, "XXXX 1110 0001 XXXX XXXX 1010 X001 0000", {flagCondition}, encodingR12, encodingNEONSn, None, None, None ,None);
+		EnterNEONInstruction(opVMOV, "XXXX 1110 0000 XXXX XXXX 1010 X001 0000", {flagCondition}, encodingNEONSn, encodingR12, None, None, None, None);
+
+		FOR i := 0 TO NumberInstructions-1 DO ASSERT(instructionFormats[i].mnemonic # None)
+		END;
+	END Init;
+
+	(** dump the name of a condition (the last name entered is preferred) **)
+	PROCEDURE DumpConditionName*(w: Streams.Writer; CONST conditionNumber: LONGINT);
+	VAR
+		i, foundIndex: LONGINT;
+	BEGIN
+		(* go through all condition names *)
+		foundIndex := None;
+		FOR i := 0 TO NumberConditionEntries - 1 DO
+			IF conditionEntries[i].number = conditionNumber THEN foundIndex := i END
+		END;
+		ASSERT(foundIndex # None);
+		w.String(conditionEntries[foundIndex].name);
+	END DumpConditionName;
+
+	(** dump the name of a register (the last name entered is preferred) **)
+	PROCEDURE DumpRegisterName*(w: Streams.Writer; registerNumber: LONGINT);
+	VAR
+		i, foundIndex: LONGINT;
+	BEGIN
+		(* go through all register names *)
+		foundIndex := None;
+		FOR i := 0 TO NumberRegisterEntries - 1 DO
+			IF registerEntries[i].number = registerNumber THEN foundIndex := i END
+		END;
+		ASSERT(foundIndex # None);
+		w.String(registerEntries[foundIndex].name);
+	END DumpRegisterName;
+
+	PROCEDURE DumpOperand*(w: Streams.Writer; CONST operand: Operand);
+	VAR i: LONGINT; first: BOOLEAN; mode: LONGINT;
+	BEGIN
+		mode := operand.mode; (* debugging *)
+		CASE operand.mode OF
+			| None: w.String("NONE!")
+			| modeCoprocessor: w.String("P"); w.Int(operand.coprocessor, 1)
+
+			| modeImmediate: w.String("#"); w.Int(operand.immediate, 1)
+
+			| modeMemory: w.String("[");
+				IF operand.register # None THEN
+					DumpRegisterName(w, operand.register)
+				END;
+				IF PostIndexed IN operand.indexing THEN
+					w.String("]")
+				END;
+				w.String(", ");
+				IF operand.offsetRegister= None THEN
+					w.String("#")
+				END;
+				IF Decrement IN operand.indexing THEN w.String("-");
+				ELSIF Increment IN operand.indexing THEN w.String("+");
+				ELSE HALT(100);
+				END;
+				IF operand.offsetRegister= None THEN
+					(*
+					w.Hex(operand.offsetImmediate, 1);w.String("H");
+					*)
+					w.Int(operand.offsetImmediate, 0)
+				ELSE
+					DumpRegisterName(w, operand.offsetRegister)
+				END;
+				IF operand.shiftMode # None THEN
+					w.String(", ");
+					w.String(shiftNames[operand.shiftMode]);
+					IF operand.shiftMode # shiftRRX THEN
+						w.String(" ");
+						IF operand.shiftRegister # None THEN
+							DumpRegisterName(w, operand.shiftRegister)
+						ELSE
+							w.String("#"); w.Int(operand.shiftImmediate, 1);
+						END;
+					END;
+				END;
+				IF ~(PostIndexed IN operand.indexing) THEN
+					w.String("]");
+				END;
+				IF PreIndexed IN operand.indexing  THEN w.String("!") END
+
+			| modeOpcode: w.Int(operand.opcode, 0)
+
+			| modeRegister:
+				DumpRegisterName(w, operand.register);
+				IF (operand.shiftMode # None)  THEN
+					w.String(", ");
+					w.String(shiftNames[operand.shiftMode]);
+					IF operand.shiftMode # shiftRRX THEN
+						w.String(" ");
+						IF operand.shiftRegister # None THEN
+							DumpRegisterName(w, operand.shiftRegister)
+						ELSE
+							w.String("#"); w.Int(operand.shiftImmediate, 1);
+						END;
+					END;
+				END
+
+			| modeRegisterList:
+				w.String("{");first := TRUE;
+				FOR i := 0 TO 31 DO
+					IF i IN operand.registerList THEN
+						IF ~first THEN w.String(", ") ELSE first := FALSE END;
+						DumpRegisterName(w, i + operand.register)
+					END;
+				END;
+				w.String("}");
+
+			| modeOption:
+				w.String("{"); w.Int(operand.option, 1); w.String("}")
+
+			| modeRegisterWithFields:
+				DumpRegisterName(w, operand.register);
+				w.String("_");
+				IF fieldF IN operand.fields THEN w.String("f") END;
+				IF fieldS IN operand.fields THEN w.String("s") END;
+				IF fieldX IN operand.fields THEN w.String("x") END;
+				IF fieldC IN operand.fields THEN w.String("c") END;
+		END;
+	END DumpOperand;
+
+	PROCEDURE DumpInstruction*(w: Streams.Writer; CONST instruction: Instruction);
+	VAR i: LONGINT;
+	BEGIN
+		IF instruction.format= None THEN
+			w.String("undefined format"); w.Ln;
+		ELSE
+			w.String(mnemonics[instructionFormats[instruction.format].mnemonic].name);
+		END;
+		IF (instruction.condition # None) & (instruction.condition # conditionAL) THEN
+			DumpConditionName(w, instruction.condition)
+		END;
+		FOR i := 0 TO NumberFlags-1 DO
+			IF i IN instruction.flags THEN
+				w.String(flagNames[i]);
+			END;
+		END;
+		i := 0;
+		WHILE (i<MaxOperands) & (instruction.operands[i].mode # None) DO
+			IF i > 0 THEN w.String(", ") ELSE w.String(" ") END;
+			DumpOperand(w, instruction.operands[i]);
+			IF (i= 0) & (flagBaseRegisterUpdate IN instruction.flags) THEN w.String("!")
+			ELSIF (i= 1) & (flagUserMode IN instruction.flags) THEN w.String("^");
+			END;
+			INC(i);
+		END;
+	END DumpInstruction;
+
+	PROCEDURE Test*(context: Commands.Context);
+	VAR str: ARRAY 32 OF CHAR; mnemonic: LONGINT; condition: LONGINT; flags: SET; i: LONGINT;
+	BEGIN
+		IF context.arg.GetString(str) THEN
+			IF FindMnemonic(str, mnemonic, condition, flags) THEN
+				context.out.String("found: "); context.out.String(mnemonics[mnemonic].name);
+				IF condition # None THEN
+					context.out.String(":");
+					context.out.String(conditionEntries[condition].name);
+				END;
+				FOR i := 0 TO NumberFlags DO
+					IF i IN flags THEN
+						context.out.String(":");
+						context.out.String(flagNames[i]);
+					END;
+				END;
+			END;
+		END;
+	END Test;
+
+	PROCEDURE Test2*(context: Commands.Context);
+	VAR operands: ARRAY 3 OF Operand; mnemonic: LONGINT; condition: LONGINT; flags: SET; instruction: Instruction;
+	BEGIN
+		InitRegister(operands[0], R0, None, None, 0);
+		InitRegister(operands[1], R1, None, None, 0);
+		InitRegister(operands[2], R1, shiftLSR, None, 8);
+		IF FindMnemonic("ADDEQ", mnemonic, condition, flags) THEN
+			IF MakeInstruction(instruction, mnemonic, condition, flags, operands) THEN
+				DumpInstruction(context.out, instruction);
+			ELSE
+				context.error.String("instruction not found");
+			END
+		ELSE
+			context.error.String("mnemonic not found");
+		END;
+	END Test2;
+
+	PROCEDURE ReadCode(file: Files.File): BitSets.BitSet;
+	VAR r: Files.Reader;  val: LONGINT;bitSet: BitSets.BitSet; adr: LONGINT;
+	BEGIN
+		IF file = NIL THEN RETURN NIL END;
+		adr := 0;
+		NEW(r, file, 0);
+		NEW(bitSet,0);
+		WHILE r.Available()>0 DO
+			r.RawLInt(val);
+			INC(adr);
+			bitSet.Resize(adr*32);
+			bitSet.SetBits((adr-1)*32,32,val);
+		END;
+		RETURN bitSet
+	END ReadCode;
+
+	PROCEDURE Disassemble*(context: Commands.Context);
+	TYPE
+		Disasm = OBJECT (Disassembler.Disassembler)
+
+			PROCEDURE DisassembleInstruction(bitSet: BitSets.BitSet; VAR adr: LONGINT; maxInstructionSize: LONGINT; w:Streams.Writer);
+			VAR instruction: Instruction; value: LONGINT; mnemonic: LONGINT;
+			BEGIN
+				(* maxInstructionSize can be ignored here *)
+				value := bitSet.GetBits(adr*8,32);
+				IF Decode(value, instruction) THEN
+					DumpInstruction(w, instruction);
+					mnemonic := instructionFormats[instruction.format].mnemonic;
+					IF (mnemonic = opBL) OR (mnemonic = opB) THEN
+						WriteReference(instruction.operands[0].immediate+adr+8, TRUE (* points to code section *), w);
+
+					ELSIF (mnemonic = opLDR) OR (mnemonic = opSTR) THEN
+						(* LDR? ..., [PC, #...] or STR? ..., [PC, #...] *)
+						ASSERT(instruction.operands[1].mode = modeMemory);
+						IF (instruction.operands[1].register = PC) & (instruction.operands[1].offsetRegister = None) THEN
+							value := instruction.operands[1].offsetImmediate + adr + 8;
+							WriteReference(value, TRUE, w);
+							IF value * 8 + 32 < bitSet.GetSize() THEN
+								WriteReference(bitSet.GetBits(value * 8, 32) - codeDisplacement, TRUE, w); (* note that data references cannot be resolved like this *)
+							END
+						END
+					END;
+				ELSE
+					w.String("*** COULD NOT DECODE ***");
+				END;
+				INC(adr,4);
+			END DisassembleInstruction;
+
+		END Disasm;
+
+	VAR disassembler: Disasm; codeFileName, dataFileName, logFileName: Files.FileName; codeFile, logFile: Files.File;code: BitSets.BitSet; options: Options.Options;
+		address: LONGINT;
+	BEGIN
+		IF context.arg.GetString(codeFileName) THEN
+			codeFile := Files.Old(codeFileName);
+			IF codeFile = NIL THEN context.out.String("file not found "); context.out.String(codeFileName); RETURN END;
+			NEW(options);
+			options.Add("l","logFile", Options.String);
+			options.Add("a","address",Options.Integer);
+			IF options.Parse(context.arg, context.out) THEN
+				IF ~options.GetInteger("a", address) THEN address := 0 END;
+				NEW(disassembler, context.out);
+				code := ReadCode(codeFile);
+				IF options.GetString("logFile",logFileName) THEN
+					logFile := Files.Old(logFileName);
+				ELSE
+					logFile := disassembler.GetLogFile(codeFileName)
+				END;
+				disassembler.Disassemble(code, code, 8,8 , logFile, address);
+			END;
+		END;
+	END Disassemble;
+
+
+BEGIN Init;
+END FoxARMInstructionSet.
+
+SystemTools.FreeDownTo FoxARMInstructionSet ~
+
+FoxARMInstructionSet.Test BLEQSB  ~
+FoxARMInstructionSet.Test2 ~
+FoxARMInstructionSet.Test3 "E:/Systembau11/WinAos/Work/Minos.boot"  ~

+ 1079 - 0
source/FoxAssembler.Mod

@@ -0,0 +1,1079 @@
+MODULE FoxAssembler;   (**  AUTHOR "fof"; PURPOSE "Oberon Assembler: Generic Part";  **)
+(* (c) fof ETH Zürich, 2009 *)
+
+IMPORT Streams, Strings, Diagnostics,D := Debugging, Commands, BinaryCode := FoxBinaryCode, SyntaxTree := FoxSyntaxTree, Global := FoxGlobal,
+	IntermediateCode := FoxIntermediateCode, Sections := FoxSections, Scanner := FoxScanner, Basic := FoxBasic, SYSTEM, ObjectFile;
+
+CONST
+	Trace* = FALSE;   (* debugging output *)
+
+	MaxOperands* = 3;
+
+	(*
+		currently there is conceptual support for one-pass assembly with a fixup mechanism for section-local references
+		disadvantages of one-pass assembly:
+			- expressions with labels would not work
+			- fixup mechanism complicated and not generic
+
+
+	*)
+	MaxPasses* = 2;
+
+	ConstantInteger* = 0;
+	ConstantFloat* = 1;
+	Fixup* = 2;
+	Offset* = 3;
+	ConstantIntegerOrOffset* = {ConstantInteger, Offset};
+
+TYPE
+	OperandString=ARRAY 256 OF CHAR;
+
+	FixupElement=POINTER TO RECORD
+		fixup: BinaryCode.Fixup; next: FixupElement;
+	END;
+
+	NamedLabel*= OBJECT
+	VAR
+		section: IntermediateCode.Section;
+		offset, displacement: LONGINT; (* in contrast to offset, displacement will be reset each round of assembling. This is to make sure that GetFixup generates the right displacement in the fixup *)
+		name-: Scanner.IdentifierString;
+		nextNamedLabel-: NamedLabel;
+		fixupList: FixupElement;
+
+		PROCEDURE &InitNamedLabel(section: IntermediateCode.Section; CONST name: ARRAY OF CHAR);
+		BEGIN
+			fixupList := NIL;
+			SELF.offset := 0; (* must be zero to be able to track local displacement *)
+			SELF.section := section;
+			COPY(name,SELF.name);
+			nextNamedLabel := NIL;
+		END InitNamedLabel;
+
+		PROCEDURE GetFixup(): BinaryCode.Fixup;
+		VAR fixup: BinaryCode.Fixup; element: FixupElement; identifier: ObjectFile.Identifier;
+		BEGIN
+			identifier.name := section.name;
+			fixup := BinaryCode.NewFixup(BinaryCode.Absolute,0,identifier,0,displacement,0,NIL);
+			NEW(element); element.fixup := fixup; element.next := fixupList; fixupList := element;
+			RETURN fixup;
+		END GetFixup;
+
+		PROCEDURE ResetDisplacements;
+		VAR element: FixupElement;
+		BEGIN
+			displacement := 0;
+			element := fixupList;
+			WHILE element # NIL DO
+				element.fixup.SetSymbol(section.name,0,0,0);
+				element := element.next;
+			END;
+		END ResetDisplacements;
+
+		PROCEDURE SetOffset*(ofs: LONGINT);
+		VAR element: FixupElement;
+		BEGIN
+			SELF.offset := ofs;
+			displacement := ofs;
+			element := fixupList;
+			WHILE element # NIL DO
+				element.fixup.SetSymbol(section.name,0,0,element.fixup.displacement (* must be here to take into account modifications of code emission *) +displacement);
+				element := element.next;
+			END;
+		END SetOffset;
+
+	END NamedLabel;
+
+	NamedLabelList*=OBJECT
+	VAR first-,last-: NamedLabel;
+
+		PROCEDURE & InitNamedLabelList;
+		BEGIN first := NIL; last := NIL
+		END InitNamedLabelList;
+
+		PROCEDURE Add*(n: NamedLabel);
+		BEGIN
+			IF first = NIL THEN first := n ELSE last.nextNamedLabel := n; last.nextNamedLabel := n;  END; last := n;
+		END Add;
+
+		PROCEDURE ResetDisplacements;
+		VAR label: NamedLabel;
+		BEGIN
+			label := first;
+			WHILE label # NIL DO label.ResetDisplacements; label := label.nextNamedLabel END;
+		END ResetDisplacements;
+
+		PROCEDURE Find*(CONST name: ARRAY OF CHAR): NamedLabel;
+		VAR label: NamedLabel;
+		BEGIN
+			label := first;
+			WHILE (label # NIL) & (label.name # name)  DO
+				label := label.nextNamedLabel;
+			END;
+			RETURN label
+		END Find;
+
+	END NamedLabelList;
+
+	Result*= RECORD
+		type*: INTEGER; (* ConstantInteger, ConstantFloat, Fixup, Offset *)
+		sizeInBits*: INTEGER;
+		value*: LONGINT; (*! implementation restriction: operations between hugeints do not yet work *)
+		valueR*: LONGREAL;
+		fixup*: BinaryCode.Fixup;
+	END;
+
+	NamedResult*=POINTER TO RECORD (Result)
+		name: Scanner.IdentifierString;
+		nextResult: NamedResult;
+	END;
+
+	NamedResultList*=OBJECT
+	VAR first, last: NamedResult; number: LONGINT;
+
+		PROCEDURE & InitNamedResultList;
+		BEGIN first := NIL; last := NIL; number := 0;
+		END InitNamedResultList;
+
+		PROCEDURE Add*(n: NamedResult);
+		BEGIN
+			IF first = NIL THEN first := n ELSE last.nextResult := n END; last := n; INC(number);
+		END Add;
+
+		PROCEDURE Find*(CONST name: ARRAY OF CHAR): NamedResult;
+		VAR result: NamedResult;
+		BEGIN
+			result := first;
+			WHILE (result # NIL) & (result.name # name)  DO
+				result := result.nextResult;
+			END;
+			RETURN result
+		END Find;
+
+	END NamedResultList;
+
+	Assembler*= OBJECT
+	VAR
+		diagnostics: Diagnostics.Diagnostics;
+		error-: BOOLEAN;
+		errorPosition-: LONGINT;
+		symbol-: Scanner.Symbol;
+		scanner: Scanner.AssemblerScanner;
+		orgOffset: LONGINT;
+		section-: IntermediateCode.Section;
+		code: BinaryCode.Section;
+		labels: NamedLabelList;
+		results: NamedResultList;
+		scope: SyntaxTree.Scope;
+		module: Sections.Module;
+		pass-: LONGINT;
+
+		PROCEDURE &Init*(diagnostics: Diagnostics.Diagnostics);
+		BEGIN
+			SELF.diagnostics := diagnostics; errorPosition := Diagnostics.Invalid; orgOffset := 0;
+		END Init;
+
+		PROCEDURE SetContext(CONST context: Scanner.Context);
+		BEGIN
+			scanner.SetContext(context); NextSymbol;
+		END SetContext;
+
+		PROCEDURE Error*(pos: LONGINT; CONST msg: ARRAY OF CHAR);
+		BEGIN
+			error := TRUE;
+			IF diagnostics # NIL THEN
+				diagnostics.Error(scanner.source^,pos,Diagnostics.Invalid,msg);
+			END;
+		END Error;
+
+		PROCEDURE ErrorSS*(pos: LONGINT; CONST s1,s2: ARRAY OF CHAR);
+		VAR msg: Basic.MessageString;
+		BEGIN COPY(s1,msg); Strings.Append(msg,s2); Error(pos, msg);
+		END ErrorSS;
+
+		PROCEDURE NextSymbol*;
+		BEGIN error := error OR ~scanner.GetNextSymbol(symbol); errorPosition := symbol.start;
+		END NextSymbol;
+
+		PROCEDURE ThisToken*(x: LONGINT): BOOLEAN;
+		BEGIN
+			IF ~error & (symbol.token = x) THEN NextSymbol; RETURN TRUE ELSE RETURN FALSE END;
+		END ThisToken;
+
+		PROCEDURE GetIdentifier*(VAR pos: LONGINT; VAR identifier: ARRAY OF CHAR): BOOLEAN;
+		BEGIN
+			pos := symbol.start;
+			IF symbol.token # Scanner.Identifier THEN RETURN FALSE
+			ELSE COPY(symbol.identifierString,identifier); NextSymbol; RETURN TRUE
+			END;
+		END GetIdentifier;
+
+		PROCEDURE ThisIdentifier*(CONST this: ARRAY OF CHAR): BOOLEAN;
+		BEGIN
+			IF ~error & (symbol.token = Scanner.Identifier) & (this = symbol.identifierString) THEN NextSymbol; RETURN TRUE ELSE RETURN FALSE END;
+		END ThisIdentifier;
+
+		PROCEDURE ExpectIdentifier*(VAR pos: LONGINT; VAR identifier: ARRAY OF CHAR): BOOLEAN;
+		BEGIN
+			IF ~GetIdentifier(pos,identifier)THEN Error(errorPosition,"identifier expected"); RETURN FALSE
+			ELSE RETURN TRUE
+			END;
+		END ExpectIdentifier;
+
+		PROCEDURE ExpectToken*(x: LONGINT): BOOLEAN;
+		VAR s: Basic.MessageString;
+		BEGIN
+			IF ThisToken(x) THEN RETURN TRUE
+			ELSE
+				s := "expected token "; Strings.Append(s,Scanner.tokens[x]); Strings.Append(s," but got "); Strings.Append(s,Scanner.tokens[symbol.token]);
+				Error(errorPosition,s);RETURN FALSE
+			END;
+		END ExpectToken;
+
+		PROCEDURE ExpectConstantInteger*(VAR x: Result; critical: BOOLEAN): BOOLEAN;
+		VAR result: Result;
+		BEGIN
+			IF ~Expression(result,critical) OR (result.type # ConstantInteger) THEN
+				result.value := 0;
+				IF critical THEN Error(errorPosition,"constant integer expected") END;
+				RETURN ~critical
+			ELSE RETURN TRUE
+			END
+		END ExpectConstantInteger;
+
+		PROCEDURE Section;
+		VAR sectionType: Scanner.IdentifierString; pos: LONGINT;
+		BEGIN
+			IF ExpectToken(Scanner.Period) THEN
+				IF ExpectIdentifier(pos,sectionType) THEN
+					IF sectionType = "data" THEN
+						IF Trace THEN D.String("data section"); D.Ln END;
+						(*! generate section here, if allowed *)
+					ELSIF sectionType = "code" THEN
+						IF Trace THEN D.String("code section"); D.Ln END;
+						(*! generate section here, if allowed *)
+					ELSE Error(pos,"expected data or code");
+					END;
+				END;
+			END;
+		END Section;
+
+		PROCEDURE DefineLabel(pos: LONGINT; CONST name: ARRAY OF CHAR);
+		VAR label: NamedLabel;
+		BEGIN
+			IF Trace THEN D.String("define label: "); D.String(name); D.Ln END;
+			IF labels.Find(name) # NIL THEN
+				Error(pos,"multiply declared identifier")
+			ELSE
+				NEW(label,section,name);
+				labels.Add(label);
+				ASSERT(labels.Find(name) =label);
+			END;
+		END DefineLabel;
+
+		PROCEDURE SetLabel(pos: LONGINT; CONST name: ARRAY OF CHAR);
+		VAR label: NamedLabel;
+		BEGIN
+			IF Trace THEN D.String("set label: "); D.String(name); D.String(" "); D.Int(code.pc,1); D.Ln END;
+			label := labels.Find(name);
+			label.SetOffset(code.pc);
+		END SetLabel;
+
+		PROCEDURE CopyResult(CONST from: Result; VAR to: Result);
+		BEGIN
+			to.type := from.type;
+			to.sizeInBits := from.sizeInBits;
+			to.value := from.value;
+			to.valueR := from.valueR;
+			to.fixup := from.fixup;
+		END CopyResult;
+
+		PROCEDURE DefineResult(pos: LONGINT; CONST name: ARRAY OF CHAR; CONST r: Result);
+		VAR result: NamedResult;
+		BEGIN
+			IF Trace THEN D.String("define result: "); D.String(name); D.Ln END;
+			IF results.Find(name) # NIL THEN
+				Error(pos,"multiply declared identifier")
+			ELSE
+				NEW(result); COPY(name,result.name);
+				CopyResult(r,result^);
+				results.Add(result);
+				ASSERT(results.Find(name) =result);
+			END;
+		END DefineResult;
+
+		PROCEDURE SetResult(CONST name: ARRAY OF CHAR; CONST r: Result);
+		VAR result: NamedResult;
+		BEGIN
+			IF Trace THEN D.String("define result: "); D.String(name); D.Ln END;
+			result := results.Find(name);
+			CopyResult(r,result^);
+		END SetResult;
+
+		PROCEDURE SymbolInScope(CONST ident: ARRAY OF CHAR): SyntaxTree.Symbol;
+		VAR sym: SyntaxTree.Symbol; localScope: SyntaxTree.Scope;  identifier: SyntaxTree.Identifier;
+		CONST Trace=FALSE;
+		BEGIN
+			IF scope = NIL THEN RETURN NIL END;
+			localScope := scope;
+			identifier := SyntaxTree.NewIdentifier(ident);
+			IF Trace THEN D.String("GetScopeSymbol:"); D.String(ident); D.Ln; END;
+			WHILE (sym = NIL) & (localScope # NIL) DO
+				sym := localScope.FindSymbol(identifier);
+				localScope := localScope.outerScope
+			END;
+
+			IF (sym # NIL) & (sym IS SyntaxTree.Import)  THEN
+				NextSymbol;
+				IF ExpectToken(Scanner.Period) & (symbol.token = Scanner.Identifier) THEN
+					identifier := SyntaxTree.NewIdentifier(symbol.identifierString);
+					IF Trace THEN D.String("GetScopeSymbol  :"); D.String(symbol.identifierString); D.Ln; END;
+					localScope := sym(SyntaxTree.Import).module.moduleScope;
+					sym := NIL;
+					WHILE (sym = NIL) & (localScope # NIL) DO
+						sym := localScope.FindSymbol(identifier);
+						IF (sym # NIL) & (sym.access * SyntaxTree.Public = {}) THEN sym := NIL END;
+						localScope := localScope.outerScope
+					END;
+				ELSE RETURN NIL
+				END;
+			END;
+			IF Trace THEN IF sym = NIL THEN D.String("not found") ELSE D.String("found"); END; D.Ln; END;
+			RETURN sym
+		END SymbolInScope;
+
+		PROCEDURE ConstantSymbol(pos: LONGINT; constant: SyntaxTree.Constant; VAR result: Result): BOOLEAN;
+		BEGIN
+			IF constant.type.resolved IS SyntaxTree.CharacterType THEN
+				result.value := ORD(constant.value.resolved(SyntaxTree.CharacterValue).value);
+				result.valueR := result.value;
+				result.type := ConstantInteger;
+			ELSIF constant.type.resolved IS SyntaxTree.IntegerType THEN
+				result.value := constant.value.resolved(SyntaxTree.IntegerValue).value;
+				result.valueR := result.value;
+				result.type := ConstantInteger;
+			ELSIF constant.type.resolved IS SyntaxTree.FloatType THEN
+				result.valueR := constant.value.resolved(SyntaxTree.RealValue).value;
+				result.type := ConstantFloat;
+			ELSE
+				Error(pos,"incompatible constant");
+				RETURN FALSE;
+			END;
+			result.sizeInBits := SHORT(module.system.SizeOf(constant.type));
+			RETURN TRUE
+		END ConstantSymbol;
+
+		PROCEDURE GetFingerprint(symbol: SyntaxTree.Symbol): LONGINT;
+		BEGIN
+			IF (symbol # NIL) THEN RETURN symbol.fingerprint.shallow END;
+		END GetFingerprint;
+
+
+		PROCEDURE NonConstantSymbol(pos: LONGINT; symbol: SyntaxTree.Symbol; VAR result: Result): BOOLEAN;
+		VAR
+			name: Basic.SegmentedName; moduleScope: SyntaxTree.Scope; fixupSection: IntermediateCode.Section;
+			fixupPatternList: ObjectFile.FixupPatterns; identifier: ObjectFile.Identifier;
+		BEGIN
+			IF scope = NIL THEN RETURN FALSE END;
+			moduleScope := scope.ownerModule.moduleScope;
+			Global.GetSymbolSegmentedName(symbol,name);
+			identifier.name := name;
+			identifier.fingerprint := GetFingerprint(symbol);
+
+			IF symbol.scope IS SyntaxTree.ModuleScope THEN (* symbol in module scope *)
+				IF symbol IS SyntaxTree.Variable THEN (* global variable *)
+					result.type := Fixup;
+					result.sizeInBits := SHORT(module.system.SizeOf(symbol.type));
+
+					(* generic fixup pattern list for generic implementation of data instruction etc. -- otherwise replaced during encoding *)
+					NEW(fixupPatternList, 1);
+					fixupPatternList[0].bits := result.sizeInBits;
+					fixupPatternList[0].offset := 0;
+					result.fixup := BinaryCode.NewFixup(BinaryCode.Absolute, 0, identifier, 0, 0, 0, fixupPatternList);
+
+				ELSIF symbol IS SyntaxTree.Procedure THEN (* procedure *)
+					IF symbol(SyntaxTree.Procedure).isInline THEN
+						Error(pos,"forbidden reference to inline procedure"); RETURN FALSE
+					ELSE
+						result.type := Fixup;
+						result.sizeInBits := SHORT(module.system.SizeOf(symbol.type));
+						(* generic fixup pattern list for generic implementation of data instruction etc. -- otherwise replaced during encoding *)
+						NEW(fixupPatternList, 1);
+						fixupPatternList[0].bits := result.sizeInBits;
+						fixupPatternList[0].offset := 0;
+						result.fixup := BinaryCode.NewFixup(BinaryCode.Absolute, 0, identifier, 0, 0, 0, fixupPatternList);
+					END;
+				ELSE HALT(100);
+				END;
+			ELSIF symbol.scope IS SyntaxTree.ProcedureScope THEN (* symbol in procedure (local) scope *)
+				IF symbol.scope # scope THEN
+					Error(pos,"local symbol not in current scope");
+				ELSE
+					RETURN FALSE;
+					IF (symbol IS SyntaxTree.Variable) OR (symbol IS SyntaxTree.Parameter) THEN
+						result.type := Offset;
+						result.value := symbol.offsetInBits DIV module.system.dataUnit;
+						ASSERT(symbol.offsetInBits MOD module.system.dataUnit = 0);
+						result.sizeInBits := SHORT(module.system.SizeOf(symbol.type));
+					ELSE Error(pos,"forbidden symbol in local scope");
+					END;
+				END
+			ELSIF symbol.scope IS SyntaxTree.RecordScope THEN (* symbol in record scope *)
+			ELSE Error(pos,"symbol in forbidden scope"); RETURN FALSE
+			END;
+			RETURN TRUE
+		END NonConstantSymbol;
+
+		PROCEDURE GetNonConstant*(pos: LONGINT; CONST ident: ARRAY OF CHAR; VAR result: Result): BOOLEAN;
+		VAR symbol: SyntaxTree.Symbol; namedLabel: NamedLabel;
+			name: Basic.SegmentedName;fixupPatternList: ObjectFile.FixupPatterns;
+			string: ARRAY 256 OF CHAR;
+			identifier: ObjectFile.Identifier;
+		BEGIN
+			namedLabel := labels.Find(ident);
+			IF (namedLabel # NIL) THEN
+				result.type := Fixup;
+				result.fixup := namedLabel.GetFixup();
+				RETURN TRUE
+			END;
+			IF ident[0] = "@" THEN
+				result.type := Fixup;
+				COPY(ident, string);
+				Strings.Delete(string,0,1);
+				Basic.ToSegmentedName(string, name);
+				result.sizeInBits := 32;
+				NEW(fixupPatternList, 1);
+				fixupPatternList[0].bits := result.sizeInBits;
+				fixupPatternList[0].offset := 0;
+				identifier.name := name;
+				identifier.fingerprint := 0;
+				result.fixup := BinaryCode.NewFixup(BinaryCode.Absolute, 0, identifier, 0, 0, 0, fixupPatternList);
+				RETURN TRUE
+			END;
+			symbol := SymbolInScope(ident);
+			IF symbol = NIL THEN RETURN FALSE
+			ELSIF symbol IS SyntaxTree.Constant THEN RETURN FALSE
+			ELSE RETURN NonConstantSymbol(pos,symbol,result)
+			END;
+		END GetNonConstant;
+
+		PROCEDURE LocalOffset(pos: LONGINT; symbol: SyntaxTree.Symbol; VAR result: Result): BOOLEAN;
+		BEGIN
+			IF symbol.scope IS SyntaxTree.ProcedureScope THEN (* symbol in procedure (local) scope *)
+				IF symbol.scope = scope THEN
+					IF (symbol IS SyntaxTree.Variable) OR (symbol IS SyntaxTree.Parameter) THEN
+						result.type := ConstantInteger;
+						result.value := symbol.offsetInBits DIV module.system.dataUnit;
+						ASSERT(symbol.offsetInBits MOD module.system.dataUnit = 0);
+						result.sizeInBits := SHORT(module.system.SizeOf(symbol.type));
+						RETURN TRUE
+					END;
+				END;
+			END;
+			RETURN FALSE
+		END LocalOffset;
+
+		PROCEDURE GetConstant*(pos: LONGINT; CONST ident: ARRAY OF CHAR; VAR result: Result): BOOLEAN;
+		VAR symbol: SyntaxTree.Symbol; namedResult: NamedResult;
+		BEGIN
+			namedResult := results.Find(ident);
+			IF namedResult # NIL THEN CopyResult(namedResult^,result); RETURN TRUE END;
+			symbol := SymbolInScope(ident);
+			IF symbol = NIL THEN RETURN FALSE
+			ELSIF symbol IS SyntaxTree.Constant THEN RETURN ConstantSymbol(pos,symbol(SyntaxTree.Constant),result)
+			ELSIF LocalOffset(pos,symbol,result) THEN RETURN TRUE
+			ELSE RETURN FALSE
+			END;
+		END GetConstant;
+
+		PROCEDURE Factor (VAR x: Result; critical: BOOLEAN): BOOLEAN;
+		VAR label: NamedLabel; identifier: Scanner.IdentifierString; pos: LONGINT;
+		BEGIN
+			IF ThisToken(Scanner.Number) THEN
+				(* ASSERT(symbol.numberType = Scanner.Integer); *)
+				IF symbol.numberType = Scanner.Integer THEN
+					x.value := symbol.integer
+				ELSIF symbol.numberType = Scanner.Hugeint THEN
+					(* note that 64 bit integer constants are not (yet) supported in expressions by the assembler.
+					however, the scanner interprets large 32 bit integers as hugeints (because integers are always assumed to be signed). *)
+					x.value := SYSTEM.VAL(LONGINT, symbol.hugeint); (* TODO: how to do that? *)
+					ASSERT(x.value < 0); (* the resulting 32 bit integer must be negative when interpreted as a signed value *)
+				END;
+				x.type := ConstantInteger;
+				RETURN TRUE;
+			ELSIF ThisToken(Scanner.PC) THEN (* pc IN units ! *)
+				x.value := code.pc;
+				x.type := ConstantInteger; (* TODO: should it be 'x.type := Offset'? *)
+				RETURN TRUE;
+			ELSIF ThisToken(Scanner.PCOffset) THEN
+				x.value := code.pc-orgOffset;
+				x.type := ConstantInteger; (* TODO: should it be 'x.type := Offset'? *)
+				RETURN TRUE;
+			ELSIF GetIdentifier(pos,identifier) THEN
+				label := labels.Find (identifier);
+				IF label # NIL THEN
+					x.value := label.offset;
+					x.type := Offset;
+					(*! deal with fixups ? / enter fixup ? *)
+					RETURN TRUE;
+				ELSIF GetConstant(errorPosition, identifier,x) THEN RETURN TRUE
+				ELSIF ~critical & (pass # MaxPasses) THEN
+					x.value := 0; x.type := ConstantInteger; RETURN TRUE
+				ELSE Error(pos,"undefined symbol"); RETURN FALSE
+				END;
+			ELSIF ThisToken(Scanner.LeftParenthesis)  THEN
+				RETURN Expression (x, critical) & ExpectToken(Scanner.RightParenthesis);
+			END;
+			RETURN FALSE
+		END Factor;
+
+		(* term = Factor { ( "*" | "/" | "%" ) Factor } *)
+		PROCEDURE Term (VAR x: Result; critical: BOOLEAN): BOOLEAN;
+		VAR y: Result; op : LONGINT;
+		BEGIN
+			IF Factor (x, critical) THEN
+				WHILE (symbol.token = Scanner.Times) OR (symbol.token = Scanner.Div) OR (symbol.token = Scanner.Mod) DO
+					op := symbol.token; NextSymbol;
+					IF Factor (y, critical) THEN
+						IF (x.type IN ConstantIntegerOrOffset) & (y.type IN ConstantIntegerOrOffset) THEN
+							IF op = Scanner.Times THEN x.value := x.value * y.value
+							ELSIF op = Scanner.Div THEN x.value := x.value DIV y.value
+							ELSE x.value := x.value MOD y.value
+							END;
+						ELSIF (x.type = ConstantFloat) OR (y.type = ConstantFloat) THEN
+							IF op = Scanner.Times THEN x.valueR := x.valueR * y.valueR
+							ELSIF op = Scanner.Div THEN x.valueR := x.valueR / y.valueR
+							ELSE RETURN FALSE
+							END;
+						ELSE RETURN FALSE
+						END;
+					ELSE
+						RETURN FALSE;
+					END;
+				END;
+				RETURN TRUE;
+			ELSE
+				RETURN FALSE;
+			END;
+		END Term;
+
+		(* Expression = [ "-" | "+" | "~" ] Term { ( "+" | "-" ) Term } *)
+		PROCEDURE Expression*(VAR x: Result; critical: BOOLEAN): BOOLEAN;
+		VAR y: Result; op : LONGINT;
+		BEGIN
+			op := symbol.token;
+			IF ThisToken(Scanner.Minus) THEN
+				IF Term (x, critical) THEN
+					IF x.type IN ConstantIntegerOrOffset THEN
+						x.value := -x.value; x.valueR := x.value
+					ELSIF x.type = ConstantFloat THEN
+						x.valueR := -x.valueR
+					ELSE
+						RETURN FALSE
+					END;
+				ELSE
+					RETURN FALSE;
+				END;
+			ELSIF ThisToken(Scanner.Plus) THEN
+				IF ~Term (x, critical) THEN RETURN FALSE
+				ELSE
+					RETURN (x.type IN ConstantIntegerOrOffset) OR (x.type = ConstantFloat)
+				END;
+			ELSIF ThisToken(Scanner.Not) THEN
+				IF Term (x, critical) THEN
+					IF x.type IN ConstantIntegerOrOffset THEN
+						x.value := -x.value-1; x.valueR := x.value
+					ELSE
+						RETURN FALSE
+					END
+				END;
+			ELSIF ~Term (x, critical) THEN RETURN FALSE
+			END;
+			WHILE (symbol.token = Scanner.Plus) OR (symbol.token = Scanner.Minus) DO
+				op := symbol.token; NextSymbol;
+				IF Term (y, critical) THEN
+					IF op = Scanner.Plus THEN
+						IF (x.type IN ConstantIntegerOrOffset) & (y.type IN ConstantIntegerOrOffset) THEN
+							x.value := x.value+y.value; x.valueR := x.value;
+						ELSIF (x.type = ConstantFloat) & (y.type = ConstantFloat) THEN
+							x.valueR := x.valueR + y.valueR;
+						ELSE RETURN FALSE
+						END;
+					ELSE
+						IF (x.type IN ConstantIntegerOrOffset) & (y.type IN ConstantIntegerOrOffset) THEN
+							x.value := x.value-y.value; x.valueR := x.value;
+						ELSIF (x.type = ConstantFloat) & (y.type = ConstantFloat) THEN
+							x.valueR := x.valueR - y.valueR;
+						ELSE RETURN FALSE
+						END;
+					END;
+				ELSE
+					RETURN FALSE;
+				END;
+			END;
+			RETURN TRUE;
+		END Expression;
+
+		PROCEDURE Data(CONST ident: ARRAY OF CHAR): BOOLEAN;
+		VAR size,i,nr: LONGINT; x: Result; pos: LONGINT; result: Result; patterns: ObjectFile.FixupPatterns;
+			PROCEDURE Number(ch: CHAR; VAR nr: LONGINT): BOOLEAN;
+			BEGIN
+				IF (ch >= "0") & (ch <="9") THEN
+					nr := ORD(ch)-ORD("0");
+					RETURN TRUE
+				ELSE
+					RETURN FALSE
+				END;
+			END Number;
+
+		BEGIN
+			size := -1;
+			IF (ident = "DB") OR (ident = "db") THEN size := 8
+			ELSIF (ident="DW") OR (ident = "dw") THEN size := 16
+			ELSIF (ident="DD") OR (ident = "dd") THEN size := 32
+			ELSIF (ident="DQ") OR (ident = "dq") THEN size := 64
+			ELSIF (CAP(ident[0]) ="D") THEN
+				size := 0;i := 1;
+				WHILE Number(ident[i],nr) DO
+					size := size*10+nr; INC(i);
+				END;
+				IF ident[i] # 0X THEN size := -1 END;
+			END;
+			IF size = -1 THEN RETURN FALSE
+			ELSE
+				IF Trace THEN D.String("Data"); D.Ln; END;
+				REPEAT
+					pos := errorPosition;
+					IF symbol.token = Scanner.String THEN
+						IF (pass = MaxPasses) & (code.comments # NIL) THEN
+							code.comments.String(ident); section.comments.String(' "');
+							code.comments.String(symbol.string^);
+							code.comments.String('"');
+							code.comments.Ln;
+							code.comments.Update
+						END;
+						i := 0;
+						WHILE symbol.string[i] # 0X DO
+							PutBitsIfLastPass(ORD(symbol.string[i]),size);
+							INC(i);
+						END;
+						NextSymbol;
+
+					ELSIF (symbol.token = Scanner.Identifier) & GetNonConstant(errorPosition,symbol.identifierString,result) THEN
+						IF (pass = MaxPasses) & (code.comments # NIL) THEN
+							code.comments.String(ident);
+							code.comments.String(" ");
+							code.comments.String(symbol.identifierString);
+							code.comments.Ln;
+							code.comments.Update
+						END;
+
+						(* if this is the last pass then enter the fixup to the generated code section *)
+						IF pass = MaxPasses THEN
+							result.fixup.SetFixupOffset(code.pc);
+							code.fixupList.AddFixup(result.fixup);
+							(* set fixup width *)
+							NEW(patterns, 1);
+							patterns[0].offset := 0; patterns[0].bits := size;
+							result.fixup.InitFixup(result.fixup.mode, result.fixup.offset, result.fixup.symbol, result.fixup.symbolOffset, result.fixup.displacement, 0, patterns);
+						END;
+
+						PutBitsIfLastPass(0,size);
+						NextSymbol;
+
+					ELSIF Expression(x,FALSE) THEN
+						IF x.type # ConstantInteger THEN Error(pos,"forbidden non-constant value") END;
+						IF (pass = MaxPasses) & (code.comments # NIL) THEN
+							code.comments.String(ident);
+							code.comments.String(" ");
+							(* code.comments.Int(x.value,1); *)
+
+							(* print number in hexadecimal form *)
+							code.comments.String("0");
+							code.comments.Hex(x.value, -size DIV 4);
+							code.comments.String("H");
+
+							code.comments.Ln;
+							code.comments.Update
+						END;
+
+						PutBitsIfLastPass(x.value,size);
+
+					ELSE Error(pos,"expected string or expression");
+					END;
+				UNTIL error OR ~ThisToken(Scanner.Comma);
+			END;
+			RETURN TRUE
+		END Data;
+
+		PROCEDURE Reserve(CONST ident: ARRAY OF CHAR): BOOLEAN;
+		BEGIN RETURN FALSE
+		END Reserve;
+
+		(** if the assembler is at the last pass: put bits into the binary code section, otherwise only increment the PC **)
+		PROCEDURE PutBitsIfLastPass(data: LONGINT; size: BinaryCode.Bits);
+		VAR
+			oldPC: LONGINT;
+		BEGIN
+			IF pass = MaxPasses THEN
+				code.PutBits(data, size)
+			ELSE
+				oldPC := code.pc;
+				ASSERT(size MOD code.os.unit = 0);
+				code.SetPC(oldPC + size DIV code.os.unit)
+			END
+		END PutBitsIfLastPass;
+
+		PROCEDURE Instruction*(CONST mnemonic: ARRAY OF CHAR);
+		VAR numberOperands: LONGINT;
+
+			PROCEDURE ParseOperand(pos: LONGINT; numberOperand: LONGINT);
+			(* stub, must be overwritten by implementation *)
+			VAR operand: OperandString;
+				result: Result; first: BOOLEAN; str: ARRAY 256 OF CHAR;
+			BEGIN
+				first := TRUE;
+				WHILE ~error & (symbol.token # Scanner.Ln) & (symbol.token # Scanner.Comma) DO
+					IF (symbol.token = Scanner.Identifier) & GetNonConstant(errorPosition,symbol.identifierString,result) THEN
+							D.String("(* non constant ");
+							D.String(symbol.identifierString); D.String("="); DumpResult(D.Log,result);
+							D.String("*)");
+					ELSIF (symbol.token = Scanner.Identifier) & GetConstant(errorPosition,symbol.identifierString,result) THEN
+							D.String("(* constant ");
+							DumpResult(D.Log,result);
+							D.String("*)");
+					END;
+					IF first THEN first := FALSE ELSE Strings.Append(operand," ") END;
+					Scanner.SymbolToString(symbol, scanner.case, str);
+					Strings.Append(operand, str);
+					NextSymbol;
+				END;
+				IF Trace THEN
+					D.String("operand= ");
+					D.String(operand); IF symbol.token = Scanner.Comma THEN D.String(" , ") END;
+				END;
+			END ParseOperand;
+		BEGIN
+			IF Trace THEN
+				D.String("Instruction= "); D.String(mnemonic);  D.String(" ");
+			END;
+			numberOperands := 0;
+			IF ~ThisToken(Scanner.Ln) THEN
+				REPEAT
+					ParseOperand(errorPosition,numberOperands);
+					INC(numberOperands);
+				UNTIL error OR ~ThisToken(Scanner.Comma);
+				IF ~error & ExpectToken(Scanner.Ln) THEN END;
+			END;
+			IF Trace THEN D.Ln END
+		END Instruction;
+
+		PROCEDURE IgnoreNewLines;
+		BEGIN
+			WHILE ThisToken(Scanner.Ln) DO END;
+		END IgnoreNewLines;
+
+		PROCEDURE DoAssemble();
+		VAR result: Result; line,pos, orgCodePos: LONGINT; identifier: Scanner.IdentifierString; context: Scanner.Context;
+		BEGIN
+			IF Trace THEN
+				D.Str("DoAssemble: ");
+				IF section # NIL THEN Basic.WriteSegmentedName(D.Log,section.name); D.Ln END;
+			END;
+
+			NEW(labels);
+			NEW(results);
+
+			scanner.GetContext(context);
+			NextSymbol;
+			IgnoreNewLines;
+			WHILE ~error & (symbol.token # Scanner.Period) & (symbol.token # Scanner.EndOfText) DO
+				IF ThisToken(Scanner.Number) THEN
+					line := symbol.integer;
+					IF ThisToken(Scanner.Colon) THEN (* line number *)
+					ELSE Error(symbol.start,"Identifier expected");
+					END;
+				END;
+				IF ExpectIdentifier(pos,identifier) THEN
+					IF ThisToken(Scanner.Colon) THEN (* label *)
+						DefineLabel(pos,identifier)
+					ELSIF ThisIdentifier("equ") OR ThisToken(Scanner.Equal) THEN
+						IF Expression(result,FALSE) THEN DefineResult(pos,identifier,result) END;
+					ELSE scanner.SkipToEndOfLine; NextSymbol;
+					END;
+				END;
+				IgnoreNewLines;
+			END;
+
+			orgCodePos := code.pc;
+			FOR pass := 1 TO MaxPasses DO
+				labels.ResetDisplacements; (* this is important as the displacement is corrected by code emission in a cummulative way *)
+				code.SetPC(orgCodePos);
+				SetContext(context);
+				IgnoreNewLines;
+				WHILE ~error & (symbol.token # Scanner.EndOfText) & (symbol.token # Scanner.Period) DO
+					IF ThisToken(Scanner.Number) THEN
+						line := symbol.integer;
+						IF ThisToken(Scanner.Colon) THEN (* line number *)
+						ELSE Error(symbol.start,"Identifier expected");
+						END;
+					END;
+					IF ExpectIdentifier(pos,identifier) THEN
+						IF ThisToken(Scanner.Colon) THEN (* label *)
+							SetLabel(pos,identifier);
+						ELSIF ThisIdentifier("equ") OR ThisToken(Scanner.Equal) THEN (* constant definition *)
+							IF Expression(result,FALSE) THEN SetResult(identifier,result) END;
+						ELSE
+							IF identifier = "section" THEN
+								Section()
+							ELSIF Data(identifier) THEN
+							ELSIF Reserve(identifier) THEN
+							ELSIF identifier = "fixed" THEN
+								IF ExpectConstantInteger(result,TRUE) THEN
+									code.SetAlignment(TRUE,result.value)
+								END;
+							ELSIF ~error THEN
+								errorPosition := pos;
+								Instruction(identifier);
+								(*
+								IF ~error & ExpectToken(Scanner.Ln) THEN END;
+								*)
+							END;
+						END;
+					END;
+					IgnoreNewLines;
+				END;
+			END;
+			IF Trace THEN
+				D.Str("END Assemble"); D.Ln;
+			END
+		END DoAssemble;
+
+		PROCEDURE InlineAssemble*(scanner: Scanner.AssemblerScanner; section: IntermediateCode.Section; scope: SyntaxTree.Scope; module: Sections.Module);
+		BEGIN
+			ASSERT(module # NIL); ASSERT(scanner # NIL); ASSERT(section # NIL);
+			ASSERT(section.resolved # NIL);
+			SELF.scope := scope;
+			SELF.module := module;
+			SELF.scanner := scanner;
+			SELF.section := section;
+			SELF.code := section.resolved;
+			DoAssemble;
+		END InlineAssemble;
+
+		PROCEDURE Assemble*(scanner: Scanner.AssemblerScanner);
+		BEGIN
+			ASSERT(scanner # NIL);
+			SELF.scanner := scanner;
+			module := NIL; section := NIL; scope := NIL;
+			scanner.SetContext(scanner.startContext);
+			DoAssemble;
+		END Assemble;
+
+		PROCEDURE AllSections*;
+		VAR pos: LONGINT; sectionType, sectionName: Scanner.IdentifierString;
+		BEGIN
+			IF Trace THEN D.String("AllSections"); D.Ln END;
+			SetContext(scanner.startContext);
+			IgnoreNewLines;
+			WHILE ThisToken(Scanner.Period) & ExpectIdentifier(pos,sectionType) & ExpectIdentifier(pos,sectionName) DO
+				D.String("section "); D.String(sectionType); D.String(" "); D.String(sectionName); D.Ln;
+				DoAssemble;
+			END;
+		END AllSections;
+
+		PROCEDURE Text*(scanner: Scanner.AssemblerScanner);
+		BEGIN
+			ASSERT(scanner # NIL);
+			SELF.scanner := scanner;
+			module := NIL; section := NIL; scope := NIL;
+			AllSections;
+		END Text;
+
+
+	END Assembler;
+
+	PROCEDURE DumpResult*(w: Streams.Writer; result: Result);
+	BEGIN
+		CASE result.type OF
+			ConstantInteger: w.String("i"); w.Int(result.sizeInBits,1);w.String(" ");w.Int(result.value,1);
+			|ConstantFloat: w.String("f");w.Int(result.sizeInBits,1);w.String(" ");w.Float(result.value,20);
+			|Offset: w.String("ofs "); w.Int(result.value,1);
+			|Fixup: w.String("i"); w.Int(result.sizeInBits,1);w.String(" "); w.String("fixup ");
+				result.fixup.Dump(w);
+		END;
+	END DumpResult;
+
+	PROCEDURE Test*(context: Commands.Context);
+	VAR scanner: Scanner.AssemblerScanner;  diagnostics: Diagnostics.StreamDiagnostics; assembler: Assembler;
+	BEGIN
+		NEW(diagnostics,context.out);
+		NEW(scanner,"command",context.arg,0,diagnostics);
+		NEW(assembler,diagnostics);
+		assembler.Text(scanner);
+		(*
+		assembler.Assemble(scanner);
+		*)
+	END Test;
+
+	PROCEDURE TestScanner*(context: Commands.Context);
+	VAR scanner: Scanner.AssemblerScanner;  diagnostics: Diagnostics.StreamDiagnostics; symbol: Scanner.Symbol;
+	BEGIN
+		NEW(diagnostics,context.out);
+		NEW(scanner,"command",context.arg,0,diagnostics);
+		WHILE scanner.GetNextSymbol(symbol) & (symbol.token # Scanner.EndOfText)  DO
+			Scanner.OutSymbol(context.out, symbol); context.out.Ln;
+		END;
+	END TestScanner;
+
+END FoxAssembler.
+
+SystemTools.Free FoxAssembler ~
+
+
+FoxAssembler.Test
+;---------------- intermediate code -----------------
+.module BitSets
+
+.imports SYSTEM
+
+.const BitSets.@moduleSelf offset=0
+	 0: data u32 0
+
+.const BitSets.BitSet offset=0
+	 0: data u32 0
+
+.code BitSets.BitSet.InitBitSet offset=0
+	 0: enter  0,  0
+	 1: mov u32 r1, u32 [fp+8]
+	 2: mov s32 [r1], s32 [fp+12]
+	 3: push s32 [fp+12]
+	 4: mov u32 r2, u32 [fp+8]
+	 5: add u32 r3, u32 [r2-4], u32 -88
+	 6: push u32 r2
+	 7: call u32 [r3],  8
+	 8: leave  0
+	 9: exit  8
+
+.code BitSets.BitSet.Zero offset=0
+	 0: enter  0,  8
+	 1: mov s32 [fp-4], s32 0
+	 2: mov u32 r1, u32 [fp+8]
+	 3: mov u32 r2, u32 [r1+4]
+	 4: conv s32 r3, u32 [r2+12]
+	 5: sub s32 r3, s32 r3, s32 1
+	 6: mov s32 [fp-8], s32 r3
+	 7: brlt u32 BitSets.BitSet.Zero:21, s32 [fp-8], s32 [fp-4]
+	 8: br u32 BitSets.BitSet.Zero:9
+	 9: conv u32 r4, s32 [fp-4]
+	10: mov u32 r5, u32 r4
+	11: mov u32 r6, u32 [fp+8]
+	12: mov u32 r7, u32 [r6+4]
+	13: brlt u32 BitSets.BitSet.Zero:15, u32 r4, u32 [r7+12]
+	14: trap  7
+	15: mul u32 r5, u32 r5, u32 4
+	16: add u32 r5, u32 r5, u32 r7+16
+	17: mov u32 [r5], u32 0
+	18: add s32 r8, s32 [fp-4], s32 1
+	19: mov s32 [fp-4], s32 r8
+	20: br u32 BitSets.BitSet.Zero:7
+	21: leave  0
+	22: exit  4
+
+.code BitSets.BitSet.Resize offset=0
+	 0: enter  0,  12
+	 1: brlt u32 BitSets.BitSet.Resize:3, s32 [fp+12], s32 0
+	 2: br u32 BitSets.BitSet.Resize:4
+	 3: trap  8
+	 4: mov u32 r1, u32 [fp+8]
+	 5: mov s32 [r1], s32 [fp+12]
+	 6: sub s32 r2, s32 [fp+12], s32 1
+	 7: brlt u32 BitSets.BitSet.Resize:10, s32 r2, s32 0
+	 8: mov s32 r2, s32 r2
+	 9: br u32 BitSets.BitSet.Resize:11
+	10: mov s32 r2, s32 0, s32 r2
+	11: shr s32 r2, s32 r2, s32 5
+	12: add s32 r2, s32 r2, s32 1
+	13: mov s32 [fp+12], s32 r2
+	14: mov u32 r3, u32 [fp+8]
+	15: breq u32 BitSets.BitSet.Resize:35, u32 [r3+4], u32 0
+	16: br u32 BitSets.BitSet.Resize:17
+	17: mov u32 r4, u32 [fp+8]
+	18: mov u32 r5, u32 [r4+4]
+	19: conv s32 r6, u32 [r5+12]
+	20: brlt u32 BitSets.BitSet.Resize:25, s32 r6, s32 [fp+12]
+	21: br u32 BitSets.BitSet.Resize:22
+	22: leave  0
+	23: exit  8
+	24: br u32 BitSets.BitSet.Resize:25
+	25: mov u32 r7, u32 [fp+8]
+	26: mov u32 r8, u32 [r7+4]
+	27: conv s32 r9, u32 [r8+12]
+	28: shl s32 r9, s32 r9, s32 1
+	29: brlt u32 BitSets.BitSet.Resize:32, s32 [fp+12], s32 r9
+	30: mov s32 r9, s32 [fp+12]
+	31: br u32 BitSets.BitSet.Resize:33
+	32: mov s32 r9, s32 r9, s32 r9
+	33: mov s32 [fp+12], s32 r9
+	34: br u32 BitSets.BitSet.Resize:35
+	35: brge u32 BitSets.BitSet.Resize:37, s32 [fp+12], s32 0
+	36: trap  9
+	37: push s32 [fp+12]
+	38: mov s32 r10, s32 [fp+12]
+	39: conv u32 r10, s32 r10
+	40: mul u32 r10, u32 r10, u32 4
+	41: add u32 r10, u32 r10, u32 16
+	42: push u32 fp-4
+	43: push u32 fp-4
+	44: push u32 r10
+	45: push u8 0
+	46: call u32 $SystemCall2:0,  0
+	47: pop u32 r11
+	48: mov u32 r12, u32 [r11]
+	49: breq u32 BitSets.BitSet.Resize:53, u32 r12, u32 0
+	50: pop u32 r13
+	51: mov u32 [r12+12], u32 r13
+	52: br u32 BitSets.BitSet.Resize:54
+	53: add u32 sp, u32 sp, u32 4
+	54: mov u32 r14, u32 [fp+8]
+	55: breq u32 BitSets.BitSet.Resize:85, u32 [r14+4], u32 0
+	56: br u32 BitSets.BitSet.Resize:57
+	57: mov s32 [fp-8], s32 0
+	58: mov u32 r15, u32 [fp+8]
+	59: mov u32 r16, u32 [r15+4]
+	60: conv s32 r17, u32 [r16+12]
+	61: sub s32 r17, s32 r17, s32 1
+	62: mov s32 [fp-12], s32 r17
+	63: brlt u32 BitSets.BitSet.Resize:84, s32 [fp-12], s32 [fp-8]
+	64: br u32 BitSets.BitSet.Resize:65
+	65: conv u32 r18, s32 [fp-8]
+	66: mov u32 r19, u32 r18
+	67: mov u32 r20, u32 [fp+8]
+	68: mov u32 r21, u32 [r20+4]
+	69: brlt u32 BitSets.BitSet.Resize:71, u32 r18, u32 [r21+12]
+	70: trap  7
+	71: mul u32 r19, u32 r19, u32 4
+	72: add u32 r19, u32 r19, u32 r21+16
+	73: conv u32 r22, s32 [fp-8]
+	74: mov u32 r23, u32 r22
+	75: mov u32 r24, u32 [fp-4]
+	76: brlt u32 BitSets.BitSet.Resize:78, u32 r22, u32 [r24+12]
+	77: trap  7
+	78: mul u32 r23, u32 r23, u32 4
+	79: add u32 r23, u32 r23, u32 r24+16
+	80: mov u32 [r23], u32 [r19]
+	81: add s32 r25, s32 [fp-8], s32 1
+	82: mov s32 [fp-8], s32 r25
+	83: br u32 BitSets.BitSet.Resize:63
+	84: br u32 BitSets.BitSet.Resize:85
+	85: mov u32 r26, u32 [fp+8]
+	86: mov u32 [r26+4], u32 [fp-4]
+	87: leave  0
+	88: exit  8
+
+.code BitSets.BitSet.GetSize offset=0
+	 0: enter  0,  0
+	 1: mov u32 r1, u32 [fp+8]
+	 2: return s32 [r1]
+	 3: leave  0
+	 4: exit  4
+	 5: trap  3

+ 269 - 0
source/FoxDisassembler.Mod

@@ -0,0 +1,269 @@
+MODULE FoxDisassembler; (** AUTHOR ""; PURPOSE ""; *)
+
+IMPORT Streams, ObjectFile, Scanner := FoxScanner, Basic := FoxBasic, BitSets, D := Debugging, Files, Commands;
+
+
+CONST Trace = TRUE;
+TYPE
+	Block*= POINTER TO RECORD (ObjectFile.Section)
+		to-: LONGINT;
+		next-: Block;
+	END;
+
+	Disassembler* = OBJECT
+	VAR
+		first, block: Block; w: Streams.Writer; stringWriter: Streams.StringWriter; code,data: BitSets.BitSet; codeUnit, dataUnit: LONGINT;
+		codeDisplacement-, dataDisplacement: LONGINT;
+
+		PROCEDURE & Init*(w: Streams.Writer);
+		BEGIN SELF.w := w; NEW(stringWriter, 256);
+		END Init;
+
+		PROCEDURE GetLogFile*(CONST binaryFileName: ARRAY OF CHAR): Files.File;
+		VAR fileName,extension: Files.FileName;
+		BEGIN
+			Files.SplitExtension(binaryFileName, fileName, extension);
+			Files.JoinExtension(fileName, "log", fileName);
+			RETURN Files.Old(fileName)
+		END GetLogFile;
+
+		PROCEDURE BlockHeader(block: Block);
+		VAR name: ObjectFile.SectionName; offset: LONGINT;
+		BEGIN
+			Basic.SegmentedNameToString(block.identifier.name, name);
+			w.String("-------- "); w.Char(Scanner.TAB);
+			IF  ObjectFile.IsCode(block.type) THEN w.String("code");
+			ELSE w.String("data");
+			END;
+			w.Char(Scanner.TAB);
+			w.String(name);
+			w.String(" @");
+			w.Hex(block.alignment,-8); (*w.String("-"); w.Hex(block.to+offset,-8);*)
+			w.String(" ---");
+			w.Ln;
+		END BlockHeader;
+
+		PROCEDURE WriteReference*(adr: LONGINT; isCode: BOOLEAN; w: Streams.Writer);
+		VAR b: Block; name: ObjectFile.SectionName; offset: LONGINT;
+		BEGIN
+			w.String(" --> "); w.Hex(adr+codeDisplacement,-8);
+			b := first; IF b = NIL THEN RETURN END;
+			IF isCode THEN offset := codeDisplacement ELSE offset := dataDisplacement END;
+			WHILE (b # NIL) & ((adr < b.alignment-offset) OR (adr > b.to - offset) OR (ObjectFile.IsCode(b.type)#isCode)) DO
+				b := b.next;
+			END;
+			IF (b # NIL) & (b # block) THEN
+				Basic.SegmentedNameToString(b.identifier.name, name);
+				w.String(" [");
+				w.String(name);
+				IF adr # b.alignment THEN
+					w.String("+"); w.Int(adr-b.alignment,1)
+				END;
+				w.String("]");
+			END;
+		END WriteReference;
+
+		PROCEDURE DisassembleInstruction*(bitset: BitSets.BitSet; VAR adrInUnits: LONGINT; maxInstructionSize: LONGINT; w: Streams.Writer);
+		BEGIN
+		END DisassembleInstruction;
+
+		PROCEDURE DisassembleBlock(from, to: LONGINT);
+		VAR adr, prevadr, max, digit,value: LONGINT; string: ARRAY 256 OF CHAR;
+		BEGIN
+			IF code = NIL THEN RETURN END;
+			adr := from;
+			max := MIN(to+1, code.GetSize() DIV codeUnit);
+			WHILE adr < max DO
+				(* adr *)
+				w.Hex(adr+codeDisplacement,-8); w.String(": ");
+				prevadr := adr;
+				DisassembleInstruction(code, adr, max-adr, stringWriter);
+				IF prevadr = adr THEN w.String("decoder error: address must increase"); w.Ln; RETURN END;
+				stringWriter.Update;
+				stringWriter.Get(string);
+				(* value *)
+				WHILE prevadr < adr DO
+					value := code.GetBits(prevadr*codeUnit, codeUnit);
+					w.Hex(value,-((codeUnit-1) DIV 4 +1)); w.String(" ");
+					INC(prevadr);
+				END;
+				(* instruction string *)
+				w.Char(Scanner.TAB); w.String(string); w.Ln;
+			END;
+		END DisassembleBlock;
+
+		PROCEDURE DataBlock(from, to: LONGINT);
+		VAR adr,width,max,value: LONGINT;
+		BEGIN
+			IF data = NIL THEN RETURN END;
+			adr := from;
+			max := MIN(to+1, data.GetSize() DIV dataUnit);
+			WHILE adr < max DO
+				w.Hex(adr+dataDisplacement,-8); w.String(": ");
+				width := 8;
+				WHILE (adr < max) & (width > 0) DO
+					value := data.GetBits(adr*dataUnit, dataUnit);
+					w.Hex(value,-((dataUnit-1) DIV 4 +1)); w.String(" ");
+					INC(adr); DEC(width);
+				END;
+				w.Ln;
+			END;
+		END DataBlock;
+
+		PROCEDURE ParseLogFile*(file: Files.File): Block;
+		VAR reader: Files.Reader; newline: BOOLEAN; sectionName: ObjectFile.SectionName; scanner: Scanner.AssemblerScanner;
+			symbol: Scanner.Symbol; b: BOOLEAN; block: Block; first: Block; last: Block;
+
+			PROCEDURE GetNextSymbol;
+			VAR b: BOOLEAN;
+			BEGIN b := scanner.GetNextSymbol(symbol)
+			END GetNextSymbol;
+
+			PROCEDURE ExpectToken(token: LONGINT): BOOLEAN;
+			BEGIN IF symbol.token = token THEN GetNextSymbol; RETURN TRUE ELSE RETURN FALSE END;
+			END ExpectToken;
+
+			PROCEDURE ExpectIdentifier(VAR name: ARRAY OF CHAR): BOOLEAN;
+			BEGIN
+				IF (symbol.token = Scanner.Identifier) THEN COPY(symbol.identifierString,name); GetNextSymbol; RETURN TRUE
+				ELSE RETURN FALSE
+				END;
+			END ExpectIdentifier;
+
+			PROCEDURE ExpectThisIdentifier(CONST name: ARRAY OF CHAR): BOOLEAN;
+			BEGIN
+				IF (symbol.token = Scanner.Identifier) & (symbol.identifierString = name) THEN GetNextSymbol; RETURN TRUE
+				ELSE RETURN FALSE
+				END;
+			END ExpectThisIdentifier;
+
+			PROCEDURE ExpectNumber(VAR int: LONGINT):BOOLEAN;
+			BEGIN
+				IF (symbol.token = Scanner.Number) THEN
+					int := SHORT(symbol.hugeint); GetNextSymbol; RETURN TRUE
+				ELSE RETURN FALSE
+				END;
+			END ExpectNumber;
+
+			PROCEDURE ParseLine(): Block;
+			VAR from,to: LONGINT; block: Block; displacement: LONGINT;
+			BEGIN
+				block := NIL;
+				IF ExpectNumber(from) & ExpectToken(Scanner.Colon) THEN
+					IF ExpectThisIdentifier("code") & ExpectIdentifier(sectionName) &  ExpectThisIdentifier("to") & ExpectNumber(to) THEN
+						NEW(block); block.type := ObjectFile.Code; Basic.ToSegmentedName(sectionName, block.identifier.name); block.alignment := from; block.fixed := TRUE; block.to := to;
+					ELSIF ExpectThisIdentifier("data") & ExpectIdentifier(sectionName) &  ExpectThisIdentifier("to") & ExpectNumber(to) THEN
+						NEW(block); block.type := ObjectFile.Data; Basic.ToSegmentedName(sectionName, block.identifier.name); block.alignment := from; block.fixed := TRUE; block.to := to;
+					END;
+				ELSIF ExpectThisIdentifier("code") & ExpectThisIdentifier("displacement") & ExpectNumber(displacement) THEN
+					codeDisplacement := displacement; dataDisplacement := displacement
+				ELSIF ExpectThisIdentifier("data") & ExpectThisIdentifier("displacement") & ExpectNumber(displacement) THEN
+					dataDisplacement := displacement;
+				ELSE scanner.SkipToEndOfLine; GetNextSymbol;	scanner.ResetError;
+				END;
+				IF (block # NIL) & Trace THEN
+					D.String("found section ");
+					D.String(sectionName);
+					IF  ObjectFile.IsCode(block.type) THEN D.String(" (code) ") ELSE D.String(" (data) ") END;
+					D.Int(block.alignment,1); D.String(" "); D.Int(block.to,1);
+					D.Ln;
+				END;
+				RETURN block
+			END ParseLine;
+
+		BEGIN
+			first := NIL; last := NIL;
+			IF file = NIL THEN RETURN NIL END;
+			NEW(reader, file, 0);
+			NEW(scanner, "", reader,0,  NIL);
+			b := scanner.GetNextSymbol(symbol);
+			REPEAT
+				block := ParseLine();
+				IF block # NIL THEN
+					IF first = NIL THEN first := block; last := block ELSE last.next := block; last := block END;
+				END;
+				GetNextSymbol
+			UNTIL symbol.token = Scanner.EndOfText;
+			RETURN first;
+		END ParseLogFile;
+
+		PROCEDURE Disassemble*(code, data: BitSets.BitSet; codeUnit, dataUnit: LONGINT; logFile: Files.File; address: ADDRESS);
+		BEGIN
+			SELF.code := code;SELF.data := data; SELF.codeUnit := codeUnit; SELF.dataUnit := dataUnit;
+			codeDisplacement := 0; dataDisplacement := 0;
+			first := ParseLogFile(logFile); block := first;
+			IF block = NIL THEN
+				w.String("------ code ------"); w.Ln;
+				DisassembleBlock(0, MAX(LONGINT)-1);
+				IF code # data THEN
+					w.String("------ data ------"); w.Ln;
+					DataBlock(0, MAX(LONGINT)-1);
+				END;
+			ELSE
+				WHILE block # NIL DO
+					IF (address = 0) OR (block.alignment <= address) & (block.to > address) THEN
+						BlockHeader(block);
+						IF ObjectFile.IsCode(block.type) & (code # NIL) THEN
+							DisassembleBlock(block.alignment-codeDisplacement, block.to-codeDisplacement);
+						ELSE
+							DataBlock(block.alignment-dataDisplacement, block.to-dataDisplacement);
+						END;
+					END;
+					block := block.next
+				END;
+			END;
+		END Disassemble;
+
+	END Disassembler;
+
+	PROCEDURE FindPC*(context: Commands.Context);
+	VAR file: Files.File; logFile: Files.FileName; adr,maxadr: LONGINT; disassembler: Disassembler; block, found: Block; name: ObjectFile.SectionName;
+	BEGIN
+		IF context.arg.GetString(logFile) & context.arg.GetInteger(adr, TRUE) THEN
+			file := Files.Old(logFile);
+			IF file = NIL THEN Files.JoinExtension(logFile,".log",logFile); file := Files.Old(logFile) END;
+			IF file = NIL THEN
+				context.error.String("file not found "); context.error.String(logFile); context.error.Ln
+			ELSE
+				NEW(disassembler, context.out);
+				maxadr := 0;
+				block := disassembler.ParseLogFile(file);
+				WHILE (block # NIL ) DO
+					IF (block.alignment < adr) & (block.alignment > maxadr) & ObjectFile.IsCode(block.type) THEN
+						found := block;
+						maxadr := block.alignment
+					END;
+					block := block.next
+				END;
+				Basic.SegmentedNameToString(found.identifier.name, name);
+				context.out.String(name); context.out.String(":"); context.out.Int(adr-found.alignment,1); context.out.Ln;
+			END;
+		END;
+	END FindPC;
+
+
+
+
+
+	(*
+	PROCEDURE Test*(context: Commands.Context);
+	VAR filename: Files.FileName; name: ObjectFile.SectionName; block: Block;
+	BEGIN
+		IF context.arg.GetString(filename) THEN
+		file := Files.Old(filename);
+		IF file = NIL THEN (* error *) HALT(100) END;
+		 block := ParseLogFile(filename);
+			WHILE block # NIL DO
+				Basic.SegmentedNameToString(block.identifier.name, name);
+				context.out.String(name); context.out.String(" at "); context.out.Int(block.alignment,1); context.out.Ln;
+				block := block.next;
+			END;
+		END;
+	END Test;
+	*)
+
+END FoxDisassembler.
+
+SystemTools.FreeDownTo FoxDisassembler ~
+FoxDisassembler.Test ins.log ~
+

+ 111 - 0
source/FoxIntermediateAssembler.Mod

@@ -0,0 +1,111 @@
+MODULE FoxIntermediateAssembler; (** AUTHOR ""; PURPOSE ""; *)
+
+IMPORT IntermediateCode := FoxIntermediateCode, FoxAssembler, D := Debugging, Scanner := FoxScanner;
+
+CONST Trace=FoxAssembler.Trace;
+TYPE
+	Register* = LONGINT; (* index for InstructionSet.registers *)
+	Operand* = IntermediateCode.Operand;
+
+	TYPE
+	Assembler*= OBJECT (FoxAssembler.Assembler)
+
+		PROCEDURE Instruction*(CONST mnemonic: ARRAY OF CHAR);
+		VAR i,numberOperands,mnem,pos: LONGINT; VAR operands: ARRAY 3 OF Operand; instruction: IntermediateCode.Instruction;
+
+			PROCEDURE ParseOperand;
+			(* stub, must be overwritten by implementation *)
+			VAR operand: IntermediateCode.Operand;
+				result: FoxAssembler.Result;
+				register1,register2:LONGINT;
+				class1, class2: IntermediateCode.RegisterClass;
+				stop,memory: BOOLEAN;
+			BEGIN
+				stop := FALSE;
+				register1 := IntermediateCode.None;
+				register2 := IntermediateCode.None;
+				result.type := -1;
+				result.value := 0;
+
+				IF numberOperands >= 3 THEN Error(errorPosition,"too many operands")
+				ELSE
+					memory := ThisToken(Scanner.LeftBracket);
+					IF (symbol.token = Scanner.Identifier) & IntermediateCode.DenotesRegister(symbol.identifierString,class1,register1) THEN
+						NextSymbol;
+						stop := ~ThisToken(Scanner.Plus);
+					END;
+					IF ~stop THEN
+						IF (symbol.token = Scanner.Identifier) THEN
+							IF IntermediateCode.DenotesRegister(symbol.identifierString,class2,register2) THEN
+								NextSymbol;
+							ELSIF GetNonConstant(errorPosition,symbol.identifierString,result) THEN
+								NextSymbol;
+							ELSIF Expression(result,FALSE) THEN
+							END;
+						ELSIF Expression(result,FALSE) THEN
+						END;
+					END;
+					IF memory & ExpectToken(Scanner.RightBracket) THEN
+						(*
+						IntermediateCode.InitMemory(operand,register1,register2,result.value);
+						*)
+					ELSIF register1 # -1 THEN
+						(*
+						IntermediateCode.InitRegister(operand,0,register1);
+						*)
+					ELSE
+						(*
+						IntermediateCode.InitImmediate(operand,result.sizeInBits,result.value);
+						*)
+					END;
+					(*
+					IF result.fixup # NIL THEN
+						IntermediateCode.AddFixup(operand,result.fixup);
+					END;
+					*)
+					operands[numberOperands] := operand;
+				END;
+			END ParseOperand;
+
+		BEGIN
+			IF Trace THEN
+				D.String("Instruction: "); D.String(mnemonic);  D.String(" "); D.Ln;
+			END;
+			pos := errorPosition;
+			mnem := IntermediateCode.FindMnemonic(mnemonic);
+			IF mnem >= 0 THEN
+				FOR i := 0 TO 2 DO IntermediateCode.InitOperand(operands[i]) END;
+				numberOperands := 0;
+				IF symbol.token # Scanner.Ln THEN
+					REPEAT
+						ParseOperand;
+						INC(numberOperands);
+					UNTIL error OR ~ThisToken(Scanner.Comma);
+				END;
+
+				IF ~error THEN
+					IntermediateCode.InitInstruction(instruction, 0, SHORTINT(mnem), operands[0], operands[1], operands[2]);
+					section.Emit(instruction);
+					(*
+
+					mnem,operands[0],operands[1],operands[2],section.resolved);
+					*)
+				END
+
+			ELSE
+				ErrorSS(pos,"unknown instruction ",mnemonic)
+			END
+		END Instruction;
+
+	END Assembler;
+
+
+
+END FoxIntermediateAssembler.
+
+SystemTools.Free FoxInlineAssembler FoxInlineInstructionSet ~
+
+
+
+
+

+ 561 - 0
source/FoxMinosObjectFile.Mod

@@ -0,0 +1,561 @@
+MODULE FoxMinosObjectFile; (** AUTHOR "fof"; PURPOSE "Oberon Compiler Minos Object File Writer"; *)
+
+IMPORT
+	Scanner := FoxScanner, Basic := FoxBasic, SyntaxTree := FoxSyntaxTree, Global := FoxGlobal, SemanticChecker := FoxSemanticChecker, FingerPrinter := FoxFingerPrinter, Sections := FoxSections,
+	Streams, D := Debugging, Files, SYSTEM,Strings, BinaryCode := FoxBinaryCode, KernelLog, Diagnostics, SymbolFileFormat := FoxTextualSymbolFile, Options,
+	Formats := FoxFormats, IntermediateCode := FoxIntermediateCode, Machine
+	;
+
+
+CONST
+	Trace=FALSE;
+
+
+TYPE Name=ARRAY 256 OF CHAR;
+	ByteArray = POINTER TO ARRAY OF CHAR;
+
+TYPE
+
+	Fixup = OBJECT
+	VAR
+		nextFixup: Fixup;
+		fixup: BinaryCode.Fixup;
+		fixupSection: Sections.Section;
+	END Fixup;
+
+	ObjectFileFormat*= OBJECT (Formats.ObjectFileFormat)
+	VAR extension,prefix: Basic.FileName;
+
+		PROCEDURE Export*(module: Formats.GeneratedModule; symbolFileFormat: Formats.SymbolFileFormat): BOOLEAN;
+		VAR symbolFile: Files.File; moduleName: SyntaxTree.IdentifierString; fileName: Files.FileName; f: Files.File; w: Files.Writer;
+		VAR varSize, codeSize: LONGINT; VAR code: ByteArray; bodyOffset: LONGINT; error: BOOLEAN;
+		BEGIN
+			Global.ModuleFileName(module.module.name,module.module.context,moduleName);
+			Basic.Concat(fileName,prefix,moduleName,extension);
+			IF Trace THEN D.Str("FoxBinaryObjectFile.ObjectFileFormat.Export "); D.Str(moduleName); D.Ln; END;
+
+			IF ~(module IS Sections.Module) THEN
+				diagnostics.Error(module.module.sourceName,Diagnostics.Invalid,Diagnostics.Invalid,"generated module format does not match object file format");
+				RETURN FALSE;
+			ELSIF module.findPC # MAX(LONGINT) THEN
+				MakeSectionOffsets(module(Sections.Module),varSize, codeSize, bodyOffset, code);
+				RETURN FindPC(module.findPC,module(Sections.Module),diagnostics);
+			ELSE
+				WITH module: Sections.Module DO
+					f := Files.New(fileName);
+					ASSERT(f # NIL);
+					NEW(w,f,0);
+
+					error := ~WriteObjectFile(w,module,symbolFile, diagnostics);
+					w.Update;
+					Files.Register(f);
+					RETURN ~error
+				END;
+			END;
+		END Export;
+
+		PROCEDURE DefineOptions*(options: Options.Options);
+		BEGIN
+			options.Add(0X,"objectFileExtension",Options.String);
+			options.Add(0X,"objectFilePrefix",Options.String);
+		END DefineOptions;
+
+		PROCEDURE GetOptions*(options: Options.Options);
+		BEGIN
+			IF ~options.GetString("objectFileExtension",extension) THEN
+				extension := ".arm"
+			END;
+			IF ~options.GetString("objectFilePrefix",prefix) THEN prefix := "" END
+		END GetOptions;
+
+		PROCEDURE DefaultSymbolFileFormat(): Formats.SymbolFileFormat;
+		BEGIN RETURN SymbolFileFormat.Get();
+		END DefaultSymbolFileFormat;
+
+		PROCEDURE ForceModuleBodies(): BOOLEAN; (* necessary in binary object file format as bodies not recognizable later on *)
+		BEGIN RETURN TRUE
+		END ForceModuleBodies;
+
+		PROCEDURE GetExtension(VAR ext: ARRAY OF CHAR);
+		BEGIN COPY(extension, ext)
+		END GetExtension;
+
+	END ObjectFileFormat;
+
+	(*
+		this procedure converts the section-based representation of fixups into a symbol based representation
+	*)
+	PROCEDURE GetFixups(module: Sections.Module; symbol: Sections.Section; VAR first: Fixup): LONGINT;
+	VAR temp: Fixup; fixup: BinaryCode.Fixup; nr :LONGINT; i: LONGINT; section: Sections.Section; sectionList: Sections.SectionList;
+
+		PROCEDURE Do;
+		BEGIN
+			FOR i := 0 TO sectionList.Length() - 1 DO
+				section := sectionList.GetSection(i);
+				IF (section.type # Sections.InlineCodeSection) & (section.type # Sections.InitCodeSection) THEN
+					IF section(IntermediateCode.Section).resolved # NIL THEN
+					fixup := section(IntermediateCode.Section).resolved.fixupList.firstFixup;
+					WHILE (fixup # NIL) DO
+						IF (fixup.symbol.name = symbol.name) THEN
+							INC(nr);
+							NEW(temp);
+							temp.fixup := fixup;
+							temp.fixupSection := section;
+							temp.nextFixup := first;
+							first := temp;
+						END;
+						fixup := fixup.nextFixup;
+					END;
+					END
+				END
+			END;
+		END Do;
+
+	BEGIN
+		first := NIL; nr := 0;
+		sectionList := module.allSections; Do;
+		sectionList := module.importedSections; Do;
+		RETURN nr
+	END GetFixups;
+
+	PROCEDURE FindPC(pc: LONGINT; module: Sections.Module; diagnostics: Diagnostics.Diagnostics): BOOLEAN;
+	VAR
+		section:Sections.Section; binarySection: BinaryCode.Section; label: BinaryCode.LabelList;
+		i: LONGINT;
+	BEGIN
+		FOR i := 0 TO module.allSections.Length() - 1 DO
+			section := module.allSections.GetSection(i);
+			binarySection := section(IntermediateCode.Section).resolved;
+			IF ((section.offset ) <= pc) & (pc < (section.offset +binarySection.pc )) THEN
+				label := binarySection.labels;
+				WHILE (label # NIL) & ((label.offset  + section.offset ) > pc) DO
+					label := label.prev;
+				END;
+				IF label # NIL THEN
+					diagnostics.Information(module.module.sourceName,label.position,Diagnostics.Invalid," pc position");
+					RETURN TRUE
+				END;
+			END
+		END;
+		diagnostics.Error(module.module.sourceName,Diagnostics.Invalid,Diagnostics.Invalid," could not locate pc");
+		RETURN FALSE
+	END FindPC;
+
+	PROCEDURE MakeSectionOffsets(module: Sections.Module; VAR varSize, codeSize, bodyOffset: LONGINT; VAR code: ByteArray);
+	VAR symbolName: SyntaxTree.IdentifierString; symbol: SyntaxTree.Symbol; binarySection: BinaryCode.Section;
+
+		PROCEDURE Copy(section: BinaryCode.Section; to: ByteArray; offset: LONGINT);
+		VAR i,ofs: LONGINT;
+		BEGIN
+			ofs := (offset );
+			FOR i := 0 TO ((section.pc-1) ) DO
+				to[i+ofs] := CHR(section.os.bits.GetBits(i*8,8));
+			END;
+		END Copy;
+
+		(*
+		PROCEDURE ReportSection(section: Sections.Section);
+		BEGIN
+			D.String("Section ");  Basic.WriteSegmentedName(D.Log, section.name); D.String(" allocated at "); D.Int(section.offset,1); D.Ln;
+		END ReportSection;
+		*)
+
+		(*
+		not necessary
+		*)
+
+		(* link body as first section: entry[0] = 0 *)
+		PROCEDURE FirstOffsets(sectionList: Sections.SectionList);
+		VAR
+			section: Sections.Section;
+			i: LONGINT;
+		BEGIN
+			FOR i := 0 TO sectionList.Length() - 1 DO
+				section := sectionList.GetSection(i);
+				binarySection := section(IntermediateCode.Section).resolved;
+				symbol := section.symbol;
+				IF symbol # NIL THEN
+					symbol.GetName(symbolName);
+					IF section.symbol = module.module.moduleScope.bodyProcedure THEN
+						section.SetOffset(0); INC(codeSize,binarySection.pc);
+						(*ReportSection(section)*)
+					END;
+				END
+			END;
+		END FirstOffsets;
+
+
+		(* note: if 'caseSections' is TRUE, only case table sections are processed, otherwise only regular sections (imported symbol/system call sections are never processed) *)
+		PROCEDURE SetOffsets(sectionList: Sections.SectionList);
+		VAR
+			section: Sections.Section;
+			i: LONGINT;
+
+
+		BEGIN
+			FOR i := 0 TO sectionList.Length() - 1 DO
+				section := sectionList.GetSection(i);
+
+				binarySection := section(IntermediateCode.Section).resolved;
+				symbol := section.symbol;
+				IF symbol # NIL THEN
+					symbol.GetName(symbolName);
+				ELSE symbolName := "";
+				END;
+
+				IF section.symbol = module.module.moduleScope.bodyProcedure THEN
+				ELSIF symbolName = "@moduleSelf" THEN
+				ELSIF section.type = Sections.ConstSection THEN
+					 IF binarySection.os.alignment # 0 THEN
+					 	INC(codeSize,(-codeSize) MOD binarySection.os.alignment);
+					 END;
+					  section.SetOffset(codeSize); INC(codeSize,binarySection.pc); (* global constants: in code *)
+					  Basic.Align(codeSize, 4); (* word alignment *)
+						(*ReportSection(section)*)
+				ELSIF (section.type = Sections.CodeSection) OR (section.type = Sections.BodyCodeSection) THEN
+					(*IF section.symbol = module.module.moduleScope.bodyProcedure THEN
+						bodyOffset := codeSize
+					END;
+					*)
+					section.SetOffset(codeSize); INC(codeSize, binarySection.pc);
+					Basic.Align(codeSize, 4); (* word alignment *)
+					(*ReportSection(section)*)
+ 				ELSIF section.type = Sections.VarSection THEN
+					INC(varSize, binarySection.pc);
+					IF binarySection.os.alignment # 0 THEN
+					 	INC(varSize,(-varSize) MOD binarySection.os.alignment);
+					END;
+					section.SetOffset(-varSize); (* global variables: negative offset *)
+					(*ReportSection(section)*)
+				END
+			END;
+		END SetOffsets;
+
+		(* note: if 'caseSections' is TRUE, only case table sections are processed, otherwise only regular sections (imported symbol/system call sections are never processed) *)
+		PROCEDURE CopySections(sectionList: Sections.SectionList);
+		VAR
+			section: Sections.Section;
+			i: LONGINT;
+		BEGIN
+			FOR i := 0 TO sectionList.Length() - 1 DO
+				section := sectionList.GetSection(i);
+				binarySection := section(IntermediateCode.Section).resolved;
+				IF (section.type = Sections.CodeSection) OR (section.type = Sections.BodyCodeSection) OR (section.type = Sections.ConstSection) THEN
+					Copy(binarySection,code,section.offset);
+				END
+			END;
+		END CopySections;
+
+	BEGIN
+
+		FirstOffsets(module.allSections); (* regular sections *)
+
+		SetOffsets(module.allSections); (* regular sections and case table sections -- a case table is a special case of a constant section *)
+		NEW(code,codeSize );
+		CopySections(module.allSections); (* regular sections *)
+	END MakeSectionOffsets;
+
+	PROCEDURE WriteObjectFile*(w:Streams.Writer; module: Sections.Module; symbolFile: Files.File; diagnostics: Diagnostics.Diagnostics): BOOLEAN;
+	VAR codeSize, dataSize, bodyOffset: LONGINT;
+		moduleScope: SyntaxTree.ModuleScope; fingerprinter: FingerPrinter.FingerPrinter;
+		code: ByteArray;
+		fp: SyntaxTree.FingerPrint;
+		error : BOOLEAN;
+		(** helper procedures *)
+		PROCEDURE GetEntries(moduleScope: SyntaxTree.ModuleScope; VAR numberEntries: LONGINT; VAR entries: ARRAY 256 OF IntermediateCode.Section);
+		VAR symbol: SyntaxTree.Symbol; p: Sections.Section;
+
+			PROCEDURE ConstantNeedsSection(constant: SyntaxTree.Constant): BOOLEAN;
+			BEGIN
+				RETURN constant.type.resolved IS SyntaxTree.StringType
+			END ConstantNeedsSection;
+
+			PROCEDURE TypeNeedsSection(type: SyntaxTree.TypeDeclaration): BOOLEAN;
+			BEGIN
+				RETURN (type.declaredType.resolved IS SyntaxTree.RecordType)
+			END TypeNeedsSection;
+
+
+		BEGIN
+			numberEntries := 0;
+			symbol := moduleScope.firstSymbol;
+			WHILE symbol # NIL DO
+				IF (symbol.access * SyntaxTree.Public # {}) THEN
+					IF (symbol IS SyntaxTree.Procedure) & ~(symbol(SyntaxTree.Procedure).isInline)
+							OR (symbol IS SyntaxTree.Variable)
+							OR (symbol IS SyntaxTree.TypeDeclaration) & TypeNeedsSection(symbol(SyntaxTree.TypeDeclaration))
+							OR (symbol IS SyntaxTree.Constant) & (ConstantNeedsSection(symbol(SyntaxTree.Constant))) THEN
+						INC(numberEntries); (* start at 1 !! *)
+						p := module.allSections.FindBySymbol(symbol);
+						IF p = NIL THEN
+							p := module.importedSections.FindBySymbol(symbol);
+						END;
+						IF p # NIL THEN
+							entries[numberEntries] := p(IntermediateCode.Section);
+							IF Trace THEN
+								IF moduleScope = module.module.moduleScope (* self *) THEN
+									D.String("Entry "); D.Int(numberEntries,1); D.String(": "); D.Str0(symbol.name); D.String(" @"); D.Int(p.offset,1); D.Ln;
+								END;
+							END;
+						ELSE
+							IF Trace THEN
+								IF moduleScope = module.module.moduleScope (* self *) THEN
+									D.String("did not find entry for "); D.Str0(symbol.name); D.Ln;
+								END
+							END;
+							entries[numberEntries] := NIL;
+						END;
+					END;
+				END;
+				symbol := symbol.nextSymbol;
+			END;
+		END GetEntries;
+
+		PROCEDURE Put32(offset: LONGINT; number: LONGINT);
+		BEGIN
+			IF Trace THEN
+				D.String("put32 at offset "); D.Int(offset,1);D.String(" : "); D.Hex(number,-8); D.Ln;
+			END;
+			code[offset] := CHR(number MOD 100H);
+			INC(offset); number := number DIV 100H;
+			code[offset] := CHR(number MOD 100H);
+			INC(offset); number := number DIV 100H;
+			code[offset] := CHR(number MOD 100H);
+			INC(offset); number := number DIV 100H;
+			code[offset] := CHR(number MOD 100H);
+		END Put32;
+
+		PROCEDURE Get32(offset: LONGINT): LONGINT;
+		BEGIN
+			RETURN ORD(code[offset]) + 100H*ORD(code[offset+1]) + 10000H * ORD(code[offset+2]) + 1000000H*ORD(code[offset+3]);
+		END Get32;
+
+		(* ObjectFile = name:String key:Int fixSelf:Int Imports Commands Entries Data Code *)
+		PROCEDURE ObjectFile(bodyOffset: LONGINT);
+		VAR moduleName: Name;
+
+			PROCEDURE Resolve(fixup: BinaryCode.Fixup);
+			BEGIN
+				IF fixup.resolved = NIL THEN fixup.resolved := module.allSections.FindByName(fixup.symbol.name) END;
+				IF fixup.resolved = NIL THEN fixup.resolved := module.importedSections.FindByName(fixup.symbol.name) END;
+			END Resolve;
+
+			PROCEDURE InModule(s: Basic.SegmentedName):BOOLEAN;
+			VAR
+				section: Sections.Section;
+				i: LONGINT;
+			BEGIN
+				FOR i := 0 TO module.allSections.Length() - 1 DO
+					section := module.allSections.GetSection(i);
+					IF section.name = s THEN RETURN TRUE END
+				END;
+				RETURN FALSE
+			END InModule;
+
+			(* go through list of all sections and all fixups in sections and if it is a self fixup, chain it *)
+			PROCEDURE FixSelf(): LONGINT;
+			VAR prev,this,patch: LONGINT; section: Sections.Section;
+				binarySection: BinaryCode.Section; fixup: BinaryCode.Fixup; i,patchOffset: LONGINT;
+				msg, name: ARRAY  256 OF CHAR;
+			BEGIN
+				prev := 0;
+				FOR i := 0 TO module.allSections.Length() - 1 DO
+					section := module.allSections.GetSection(i);
+					IF  (section.type # Sections.InitCodeSection) THEN
+						binarySection := section(IntermediateCode.Section).resolved;
+						fixup := binarySection.fixupList.firstFixup;
+						WHILE fixup # NIL DO
+							IF (fixup.mode = BinaryCode.Relative)  & InModule(fixup.symbol.name) THEN
+								HALT(100); (* should already be resolved *)
+							ELSIF (fixup.mode = BinaryCode.Absolute) & InModule(fixup.symbol.name) THEN
+								this := section.offset  + fixup.offset; (* location of the fixup *)
+								(*
+								ASSERT(this < 8000H);
+								ASSERT(this >= -8000H);
+								*)
+								Resolve(fixup);
+								patchOffset := (fixup.resolved.offset + fixup.displacement);
+								IF (patchOffset DIV 4 >= 8000H) OR (patchOffset DIV 4< -8000H)
+									OR (patchOffset MOD 4 # 0)
+								 THEN
+									msg := "fixup problem: ";
+									Basic.SegmentedNameToString(fixup.symbol.name, name);
+									Strings.Append(msg, name);
+									Strings.Append(msg," : ");
+									Strings.AppendInt(msg, patchOffset);
+
+									diagnostics.Error(module.moduleName,Diagnostics.Invalid, Diagnostics.Invalid,msg);
+
+									error := TRUE
+								END;
+								patch := prev DIV 4 + 10000H * (patchOffset DIV 4);
+								IF Trace THEN
+									D.String("fix self "); Basic.WriteSegmentedName(D.Log, section.name); D.String("+"); D.Int(fixup.offset,1);
+									D.String(" -> ");
+									Basic.WriteSegmentedName(D.Log, fixup.symbol.name); D.String("+"); D.Int(fixup.displacement,1) ;
+									D.Ln;
+								END;
+								Put32(this, patch);
+								prev := this;
+							ELSE (* external fixup, handled in imports *)
+							END;
+							fixup := fixup.nextFixup;
+						END
+					END
+				END;
+
+				RETURN prev DIV 4
+			END FixSelf;
+
+		BEGIN
+			Global.ModuleFileName(module.module.name,module.module.context,moduleName);
+			fp := fingerprinter.SymbolFP(module.module);
+			w.RawString(moduleName); w.RawLInt(fp.public);
+			w.RawLInt(FixSelf());
+			Imports;
+			Commands;
+			Entries(bodyOffset);
+			Data;
+			Code;
+		END ObjectFile;
+
+		(* Imports = {name:String key:Int fix:Int} 0X:Char *)
+		PROCEDURE Imports;
+		VAR name: Name; import: SyntaxTree.Import; number: LONGINT; numberEntries: LONGINT; entries: ARRAY 256 OF IntermediateCode.Section;
+
+			PROCEDURE IsFirstOccurence(import: SyntaxTree.Import): BOOLEAN; (*! inefficient *)
+			VAR i: SyntaxTree.Import;
+			BEGIN
+				i := moduleScope.firstImport;
+				WHILE (i # NIL) & (i.module # import.module) DO
+					i := i.nextImport;
+				END;
+				RETURN i = import
+			END IsFirstOccurence;
+
+			PROCEDURE MakeFixups(): LONGINT;
+			VAR prev,this,instr,i: LONGINT; section: Sections.Section; first: Fixup; numberFixups: LONGINT;
+			BEGIN
+				prev := 0;
+				FOR i := 1 TO numberEntries DO
+					section := entries[i];
+					IF section # NIL THEN
+						numberFixups := GetFixups(module, section, first);
+						IF Trace THEN
+							D.Int(numberFixups,1); D.String(" fixups "); Basic.WriteSegmentedName(D.Log, section.name); D.Ln;
+						END;
+						WHILE first # NIL DO
+							this := first.fixupSection.offset  + first.fixup.offset;
+							instr := Get32(this);
+							ASSERT(prev < 10000H); ASSERT(i < 100H);
+							(*
+								31 ... 24 | 23 .. 16 | 16 .. 0
+								opCode | pno | next
+							*)
+							instr := instr MOD 1000000H + i * 10000H + prev DIV 4;
+							Put32(this, instr);
+							prev := this;
+							first := first.nextFixup;
+						END;
+					END;
+				END;
+				IF Trace THEN D.String(" fixup chain starting at "); D.Int(prev,1); D.Ln END;
+				RETURN prev DIV 4
+			END MakeFixups;
+
+		BEGIN
+			import := moduleScope.firstImport;
+			WHILE(import # NIL) DO
+				IF ~Global.IsSystemModule(import.module) & IsFirstOccurence(import) THEN
+					Global.ModuleFileName(import.module.name,import.module.context,name);
+					IF Trace THEN
+						D.Str("Import module : "); D.Str(name); D.Ln;
+					END;
+					w.RawString(name);
+					fp := fingerprinter.SymbolFP(import.module);
+					w.RawLInt(fp.public);
+
+					(* get all imported entries of imported module *)
+					GetEntries(import.module.moduleScope, numberEntries, entries);
+					(* generate fixups to all non-zero entries *)
+					w.RawLInt(MakeFixups());
+				END;
+				import := import.nextImport;
+			END;
+			w.Char(0X);
+		END Imports;
+
+		(* Commands = {name:String offset:Int} 0X:Char *)
+		PROCEDURE Commands;
+		VAR
+			procedure : SyntaxTree.Procedure; procedureType: SyntaxTree.ProcedureType;
+			p: Sections.Section; name: Name; numberParameters, i: LONGINT;
+		BEGIN
+			FOR i := 0 TO module.allSections.Length() - 1 DO
+				p := module.allSections.GetSection(i);
+				IF (p.type # Sections.InitCodeSection) & (p.symbol # NIL) & (p.symbol IS SyntaxTree.Procedure)  THEN
+					procedure := p.symbol(SyntaxTree.Procedure);
+					procedureType := procedure.type(SyntaxTree.ProcedureType);
+					IF (SyntaxTree.PublicRead IN procedure.access) & ~(procedure.isInline) & ~(procedureType.isDelegate) & (procedureType.numberParameters = 0) THEN
+						procedure.GetName(name);
+						IF Trace THEN
+							D.Str("Command : "); D.Str(name); D.Str(" @ "); D.Int(p.offset ,1);
+						END;
+						w.RawString(name);
+						w.RawLInt(p.offset DIV 4);
+						IF Trace THEN D.Ln END
+					END
+				END
+			END;
+			w.Char(0X);
+		END Commands;
+
+		(* noEntries:Int BodyEntry {entry:Int32}:noEntries *)
+		PROCEDURE Entries(bodyOffset: LONGINT);
+		VAR
+			i,numberEntries: LONGINT; entry: ARRAY 256 OF IntermediateCode.Section; (* more is not allowed anyway in the runtime system *)
+		BEGIN
+			GetEntries(moduleScope, numberEntries, entry);
+			w.RawLInt(numberEntries);
+			w.RawLInt(0); (* body entry: body is fixed at position 0, cf. MakeSectionOffsets *)
+			FOR i := 1 TO numberEntries DO
+				ASSERT(entry[i].offset MOD 4 = 0);
+				w.RawLInt(entry[i].offset DIV 4); (* entries here must be byte wise because jumps take place with absolute fixup - I cannot distinguish here *)
+			END;
+		END Entries;
+
+		(* dataSize:Int32 *)
+		PROCEDURE Data;
+		BEGIN
+			w.RawLInt(dataSize);
+		END Data;
+
+		(* codeLen:Int32 {code:Int32}:codeLen *)
+		PROCEDURE Code;
+		VAR i: LONGINT;
+		BEGIN
+			ASSERT(codeSize MOD 4 = 0);
+			w.RawLInt(codeSize DIV 4);
+			FOR i := 0 TO codeSize-1 DO
+				w.Char(code[i]);
+			END;
+		END Code;
+
+	BEGIN
+		error := FALSE;
+		moduleScope := module.module.moduleScope;
+		NEW(fingerprinter,module.system);
+		MakeSectionOffsets(module,dataSize,codeSize,bodyOffset,code); (* --> all sections are now assembled as one piece in code *)
+		ObjectFile(bodyOffset);
+		w.Update;
+		RETURN ~error
+	END WriteObjectFile;
+
+	PROCEDURE Get*(): Formats.ObjectFileFormat;
+	VAR objectFileFormat: ObjectFileFormat;
+	BEGIN NEW(objectFileFormat); RETURN objectFileFormat
+	END Get;
+
+END FoxMinosObjectFile.
+
+SystemTools.Free FoxMinosObjectFile ~