!-------------------------------------------------------------------
!	This is a code generator definition file for
!	TXCGG, the T3X Code Generator Generator.
!	-- GAS-386 version --
!	Copyright (C) 1998 Nils M Holm.
!	See LICENSE for conditions of use.
!	For documentation, see 'cgdesc.doc'.
!-------------------------------------------------------------------

!-------------------- GENERAL TARGET DESCRIPTION --------------------!

! STRING TEMPLATE: The first line generated, usually a comment
HEADER		"/* Code generated by TXCG386, GAS-386 version */"

! Set to TRUE, if the assembler which code is generated for expects
! arguments to instructions in SRC,DEST order (like AT&Ts AS, GAS, etc).
! Set to FALSE for Intel-style assemblers (DEST,SRC order) like TASM,
! MASM, S86, ...
SRC_DEST	TRUE

! Set to TRUE, if conditional branches may have displacements of
! (at least) 16 bits or go to absolute locations. Setting LONG_BRANCHES
! to FALSE will result in the generation of jump-around-jumps to
! get around conditional branch instructions with limited ranges.
LONG_BRANCHES	FALSE

! DECIMAL CONSTANT: Bytes per data word
BPW		4

! LIST OF VECTORS CONTAINING ONE STRING EACH.
! Available general purpose registers. These will be allocated in a cycle
! beginning at the first entry. Therefore, if an accumulator exists, it
! should be placed in the first slot.
REGISTERS {
		[ "%eax" ],
		[ "%esi" ],
		[ "%edi" ],
		[ "%ebx" ],
}

! STRING TEMPLATES: Text/Data/BSS segment headers.
! Used to emit subsequently generated code to a specific segment.
! Instructions go to the TEXT segment, initialized data go to the
! DATA segment, and uninitialized data go to the BSS segment.
SET_TEXT	".text"
SET_DATA	".data"
SET_BSS		".bss"

! STRING TEMPLATE: Pseudo-instruction used to define a data word.
! Should be separated from the data using a space or TAB.
! Currently, this instruction must accept a comma-separated
! list of numeric values.
WORDDATA	".long\t"

! STRING TEMPLATE: Pseudo-instruction used to define a data byte.
! Should be separated from the data using a space or TAB.
! Currently, this instruction must accept a comma-separated
! list of numeric values.
BYTEDATA	".byte\t"


!-------------------- SETUP AND CLEANUP ROUTINES --------------------!

! This code will be generated at the beginning of each target program
! (for example, library initialization, RT variable setup, assembler
! prologue, etc...).
INIT_SEQ {
		dest(TEXT);
		genraw("call\t_TXINIT\n");
}

! This code will be generated at the end of each target program
! (cleanup routines, assembler epilogue, etc...).
END_SEQ {
}


!-------------------- SETUP AND CLEANUP ROUTINES --------------------!

! Routines which are required but not provided by the TXCG framework
! may be defined here...
LOCAL_ROUTINES {
}


!-------------------- DATA REFERENCES --------------------!


! STRING TEMPLATE:
! How to reference a global variable. Should deliver the content
! of a global memory location.
! '%D' will be replaced with a unique label ID.
GLOB_REF	"A_%D"

! STRING TEMPLATE:
! How to reference a global vector. Should deliver the address
! of a global memory location.
! '%D' will be replaced with a unique label ID.
GLOB_VEC_REF	"$A_%D"

! STRING TEMPLATE:
! How to reference a local variable. Should deliver the content
! of a local memory location.
! '%D' will be replaced with the offset (relative to the frame pointer)
! of the local symbol in bytes.
! Local variables have descending offsets: -1*BPW, -2*BPW, ...
! Parameters have ascending offsets: 2*BPW, 3*BPW, ...
LOC_REF		"%D(%%ebp)"

! STRING TEMPLATE:
! How to reference a local vector. Should deliver the address
! of a local memory location (or at least its offset relative
! to the frame pointer -- see LOC_VEC_ADJUST below).
! %D will be replaced with the frame pointer relative offset.
LOC_VEC_REF	"$%D"

! STRING TEMPLATE:
! Code required to adjust a local vector address. If LOC_VEC_REF
! delivers the absolute address of a local vector, this should be
! a null operation.
! If LOC_VEC_REF delivers an offset relative to the frame pointer,
! the frame pointer value should be added at this point.
! %S will be replaced with the register loaded in LOC_VEC_REF.
LOC_VEC_ADJUST	"addl\t%%ebp,%S"

! STRING TEMPLATE:
! How to reference a label. Shoule deliver the address of the label.
! '%D' will be replaced with a unique label ID.
LAB_REF		"$A_%D"

! STRING TEMPLATE:
! How to referebce a numeric literal. Shoule deliver the value of
! the literal.
! %D will be replaced with the literal (in decimal notation).
CONST_REF	"$%D"


!-------------------- REGISTER SHUFFLING --------------------!

! Push the content of a register onto the stack.
! VA0 holds the register.
PUSH_REG_SEQ	gen("pushl\t%S", va);

! Pop the content of a register from the stack.
! VA0 holds the register.
POP_REG_SEQ	gen("popl\t%S", va);

! Load a register with any value. (BINARY)
LOAD_REG_SEQ	gen("movl\t%S,%S", va);

! Move the content of a register to another register. (BINARY)
MOVE_REG_SEQ	gen("movl\t%S,%S", va);

! Swap the contents of two registers. (BINARY)
SWAP_REGS_SEQ	gen("xchgl\t%S,%S", va);

! Load a register with the content of a location pointed to by
! that register (indirect word access). (BINARY)
IND_REG_SEQ	gen("movl\t(%S),%S", va);

! Load a register with the content of a byte pointed to by
! that register (indirect byte access). (BINARY)
IND_REG_BYTE_SEQ {
		gen("xorl\t%%ecx,%%ecx", 0);
		gen("movb\t(%S),%%cl", va);
		gen("movl\t%%ecx,%S", va);
}


!-------------------- MEMORY OPERATIONS --------------------!

! Store the content of a register in a memory location. (BINARY)
STORE_REG_SEQ	gen("movl\t%S,%S", va);

! Store the content of a register in a location pointed to by
! another register. (BINARY)
STORE_IND_REG_SEQ {
		gen("movl\t%S,(%S)", va);
}

! Store the content of the least significant 8 bits of a register in a
! byte pointed to by another register. (BINARY)
STORE_BYTE_SEQ {
		gen("movl\t%S,%%ecx", va);
		gen("movb\t%%cl,(%S)", @va[1]);
}

! Compute the address of a location denoted by a[b] where a=SRC, b=DEST.
! Usually, this is done by scaling B by BPW (b := b << log2(BPW)) and
! then adding it to A. (BINARY)
DEREF_SEQ {
		gen("shll\t$2,%S", va);
		gen("addl\t%S,%S", va);
}

! Compute the address of a location denoted by a::b where a=SRC, b=DEST.
! Since B does not have to be scaled, it can be simply added to A. (BINARY)
DEREF_BYTE_SEQ {
		gen("addl\t%S,%S", va);
}

! Increment a global memory location by a contant value. (BINARY)
! The constant value is passed in numeric format (%D).
INC_GLOBAL_SEQ	gen("addl\t$%D,A_%D", va);

! Increment a local memory location by a contant value. (BINARY)
! The constant value is passed in numeric format (%D).
INC_LOCAL_SEQ	gen("addl\t$%D,%D(%%ebp)", va);


!-------------------- UNARY OPERATIONS --------------------!

! Negate the content of a register. (UNARY)
NEG_REG_SEQ	gen("negl\t%S", va);

! Invert the content of a register (toggle all bits). (UNARY)
NOT_REG_SEQ	gen("notl\t%S", va);

! Perform a logical NOT on a register R:   R := R=0 -> -1 : 0;
! (UNARY, but VA0 and VA1 both hold the operand)
LOGICAL_NOT_SEQ {
		gen("negl\t%S", va);
		gen("sbbl\t%S,%S", va);
		gen("notl\t%S", va);
}

! Test a register for being zero. VA0 and VA1 both point to the same
! register so that code like
!				gen("OR %S,%S", va);
! may be used to achieve this.
CMP_ZERO_REG_SEQ {
		gen("orl\t%S,%S", va);
}


!-------------------- BINARY OPERATIONS --------------------!

! Multiply DEST := DEST * SRC (BINARY)
MUL_REG_SEQ	gen("imull\t%S,%S", va);

! Divide DEST := DEST / SRC (BINARY)
DIV_REG_SEQ {
		var     save;
		if (Qe-Qs /\ LdQueue[Qs] = INUM) do
			gen("movl\t%S,%%ecx", va);
			va[0] := "%ecx";
		end
		save := 0;
		if (va[1][0] \= '%' \/ va[1][2] \= 'a') do
			gen("pushl\t%%eax", 0);
			gen("movl\t%S,%%eax", @va[1]);
			save := 1;
		end
		gen("cdq", 0); gen("idivl\t%S,%%eax", va);
		if (save) do
			gen("movl\t%%eax,%S", @va[1]);
			gen("popl\t%%eax", 0);
		end
}

! Compute the division remainder:  DEST := DEST MOD SRC (BINARY)
MOD_REG_SEQ {
		var     save;
		if (Qe-Qs /\ LdQueue[Qs] = INUM) do
			gen("movl\t%S,%%ecx", va);
			va[0] := "%ecx";
		end
		save := 0;
		if (va[1][0] \= '%' \/ va[1][2] \= 'a') do
			gen("pushl\t%%eax", 0);
			gen("movl\t%S,%%eax", @va[1]);
			save := 1;
		end
		gen("cdq", 0); gen("idivl\t%S,%%eax", va);
		if (save) do
			gen("movl\t%%eax,%S", @va[1]);
			gen("popl\t%%eax", 0);
		end
		va[0] := va[1];
		gen("movl\t%%edx,%S", va);
}

! Multiply unsigned DEST := DEST .* SRC (BINARY)
UMUL_REG_SEQ {
		var	save;
		if (Qe-Qs /\ LdQueue[Qs] = INUM) do
			gen("movl\t%S,%%ecx", va);
			va[0] := "%ecx";
		end
		save := 0;
		if (va[1][0] \= '%' \/ va[1][2] \= 'a') do
			gen("pushl\t%%eax", 0);
			gen("movl\t%S,%%eax", @va[1]);
			save := 1;
		end
		gen("mull\t%S", va);
		if (save) do
			gen("movl\t%%eax,%S", @va[1]);
			gen("popl\t%%eax", 0);
		end
}

! Divide unsigned DEST := DEST ./ SRC (BINARY)
UDIV_REG_SEQ {
		var	save;
		if (Qe-Qs /\ LdQueue[Qs] = INUM) do
			gen("movl\t%S,%%ecx", va);
			va[0] := "%ecx";
		end
		save := 0;
		if (va[1][0] \= '%' \/ va[1][2] \= 'a') do
			gen("pushl\t%%eax", 0);
			gen("movl\t%S,%%eax", @va[1]);
			save := 1;
		end
		gen("xorl\t%%edx,%%edx", 0); gen("divl\t%S", va);
		if (save) do
			gen("movl\t%%eax,%S", @va[1]);
			gen("popl\t%%eax", 0);
		end
}

! Add DEST := DEST + SRC (BINARY)
ADD_REG_SEQ	gen("addl\t%S,%S", va);

! Subtract DEST := DEST - SRC (BINARY)
SUB_REG_SEQ	gen("subl\t%S,%S", va);

! Perform bitwise AND: DEST := DEST & SRC (BINARY)
BIN_AND_REG_SEQ	gen("andl\t%S,%S", va);

! Perform bitwise OR: DEST := DEST | SRC (BINARY)
BIN_OR_REG_SEQ	gen("orl\t%S,%S", va);

! Perform bitwise Exclusive OR: DEST := DEST ^ SRC (BINARY)
BIN_XOR_REG_SEQ	gen("xorl\t%S,%S", va);

! Perform bitwise Shift Left: DEST := DEST << SRC (BINARY)
BIN_SHL_REG_SEQ {
		gen("movl\t%S,%%ecx", va);
		gen("shll\t%%cl,%S", @va[1]);
}

! Perform bitwise Shift Right: DEST := DEST >> SRC (BINARY)
BIN_SHR_REG_SEQ {
		gen("movl\t%S,%%ecx", va);
		gen("shrl\t%%cl,%S", @va[1]);
}

! Compare two registers. (BINARY)
COMP_REG_SEQ	gen("cmpl\t%S,%S", va);


!-------------------- BRANCHES AND CONDITIONS --------------------!

! STRING TEMPLATE: Branch On Equal
BE		"je"

! STRING TEMPLATE: Branch On Not Equal
BNE		"jne"

! STRING TEMPLATE: Branch On Less
BL		"jl"

! STRING TEMPLATE: Branch On Greater
BG		"jg"

! STRING TEMPLATE: Branch On Less Or Equal
BLE		"jle"

! STRING TEMPLATE: Branch On Greater Or Equal
BGE		"jge"

! STRING TEMPLATE: Branch On Unsigned Less
BUL		"jb"

! STRING TEMPLATE: Branch On Unsigned Greater
BUG		"ja"

! STRING TEMPLATE: Branch On Unsigned Less Or Equal
! temporary label.
BULE		"jbe"

! STRING TEMPLATE: Branch On Unsigned Greater Or Equal
! temporary label.
BUGE		"jae"

! STRING TEMPLATE: The destination of a branch to a temporary label
! (a temporary label)
BRANCH_DEST	"T_%D"

! STRING TEMPLATE: The destination of a jump to a global label
! (a global label)
JUMP_DEST	"A_%D"

! STRING TEMPLATE: Branch unconditionally to a global label.
JUMP	"jmp\tA_%D"

! STRING TEMPLATE: code to set a global label.
LABEL		"A_%D:"

! STRING TEMPLATE: code to set a global label in the data segment
! -- in most cases the same as LABEL.
DATALABEL	"A_%D:"

! STRING TEMPLATE:  Code to generate an alias for a data label
! -- in most cases the same as DATALABEL.
DATALABEL_ALIAS	"A_%D:"

! STRING TEMPLATE: Branch unconditionally to a temporary label.
BRANCH		"jmp\tT_%D"

! STRING TEMPLATE: temporary label.
TMPLAB		"T_%D:"

! STRING TEMPLATE: jump to a label (only the instruction, the separator
! 	and the label itself will be appended).
JMP_TO_LABEL	"jmp"

! STRING TEMPLATE: set a register to TRUE (-1).
TRUE_REG	"movl\t$-1,%S"

! STRING TEMPLATE: set a register to FALSE (0).
FALSE_REG	"xorl\t%S,%S"


!-------------------- PROCEDURES AND PROCEDURE CALLS --------------------!

! Code generated at the beginning of each procedure. This fragment
! should save the current procedure context (caller context) and then
! create a new one. Generally, this means to save the frame pointer
! on the stack and then load it with the stack pointer. No additional
! registers have to be saved. VA1 and VA0 are undefined.
PROC_PROLOGUE {
		gen("pushl\t%%ebp", 0);
		gen("movl\t%%esp,%%ebp", 0);
}

! Code generated at the end of each procedure. This fragment should
! restore the context saved by PROC_PROLOGUE and then return to the
! calling procedure. VA1 and VA0 are undefined.
PROC_EPILOGUE {
		gen("popl\t%%ebp", 0);
		gen("ret", 0);
}

! Call a procedure pointed to by a register.
! VA0 holds the register name.
CALL_REG_SEQ	gen("call\t%S", va);

! Call a procedure tagged by a label.
! (This is the common way of calling a procedure.)
! VA0 holds the label name.
CALL_PROC_SEQ	gen("call\t%S", va);

! Call a procedure whose address is stored at a given offset
! in the Extension Vector. The extension vector is itself named _GV_
! (including the underscores). Each member of _GV_ allocates one
! machine word.
! VA0 holds the Extension Vector index in numeric format (%D).
CALL_GVEC_PROC_SEQ {
		gen("movl\t$%D,%%eax", va);
		genraw("movl\t_GV_(,%eax,4),%ebx\n");
		va[0] := "*%ebx";
		gen("call\t%S", va);
}

! Adjust the stack pointer by subtracting a constant value from it.
! Since T3X currently requires a push-down stack, this fragment is
! used to allocate memory on the runtime stack.
! VA0 holds the number of bytes to allocate in numeric format (%D).
ADJUST_STACK_SEQ {
		gen("subl\t$%D,%%esp", va);
}

! Remove arguments from the stack after a procedure call. Normally
! achieved by adding the space allocated by the arguments to the
! stack pointer.
! VA0 holds the number of bytes to remove in numeric format (%D).
CLEAN_STACK_SEQ	gen("addl\t$%D,%%esp", va);

! Code used to halt program execution and return to the calling program
! (probably the Operating System). If a return value is generated,
! it should be zero.
! VA0 and VA1 are undefined.
HALT_SEQ {
		gen("xorl\t%%eax,%%eax", 0);
		gen("pushl\t%%eax", 0);
		gen("call\t__exit", 0);
}


!-------------------- DATA DECLARATIONS --------------------!

! Declare a global uninitialized variable with the name 'A_%D' where
! %D will be replaced with a unique label ID.
! VA1 contains the size of the variable in machine words.
! When space is allocated in words, VA1 should be scaled up before
! generating code (by multiplying it with BPW).
DECL_VAR_SEQ {
	va[1] := va[1] * BPW;
	gen(".comm\tA_%D,%U", va);
}

! Declare a global, initialized, word-size variable with the name
! 'A_%D' where %D will be replaced with a unique ID.
! The object will be initialized with a numeric value if OP=IDATA
! and with the address of a label otherwise.
! VA0 holds the unique ID for 'A_%D' and VA1 holds the initializer
! (also in numeric format -- %D).
! (either a constant or the ID of a label, depending on OP).
DECL_NAMED_DATA_SEQ {
		gen(op = IDATA->
			"A_%D:\t.long\t%D":
			"A_%D:\t.long\tA_%D", va);
}

! Declare an anonymous initialized data object.
! Like DECL_NAMED_DATA, but no name is supplied and the initializer
! is supplied in VA0 in numeric format (%D).
DECL_DATA_SEQ {
		gen(op = IDATA->
			".long\t%D":
			".long\tA_%D", va);
}

! STRING TEMPLATE:
! The keyword used to declare (but not to define) a global symbol name
! followed by a space or a TAB. The name itself will be appended.
DECL_GLOBAL	".globl\t"

! STRING TEMPLATE:
! The keyword used to define an external symbol followed by a space or
! a TAB character. The name itself will be appended.
DECL_EXTERN	".extrn\t"

