
macro struct? name
	macro end?.struct?!
			end namespace
		esc end struc
		virtual at 0
			name name
			sizeof.name = $
		end virtual
		purge end?.struct?
	end macro
	esc struc name
		label . : sizeof.name
		namespace .
end macro

struct SCNHDR
	s_name		db 8 dup ?
	s_paddr 	dd ?
	s_vaddr 	dd ?
	s_size		dd ?
	s_scnptr	dd ?
	s_relptr	dd ?
	s_lnnoptr	dd ?
	s_nreloc	dw ?
	s_nlnno 	dw ?
	s_flags 	dd ?
end struct

SCNHSZ = sizeof SCNHDR

struct RELOC
	r_vaddr 	dd ?
	r_symndx	dd ?
	r_type		dw ?
end struct

RELSZ = sizeof RELOC

struct SYMENT
	e_name		db 8 dup ?
    virtual at e_name
	e_zeroes	dd ?
	e_offset	dd ?
    end virtual
	e_value 	dd ?
	e_scnum 	dw ?
	e_type		dw ?
	e_sclass	db ?
	e_numaux	db ?
end struct

SYMESZ = sizeof SYMENT

I386MAGIC = 0x14c

F_RELFLG = 0x0001
F_EXEC	 = 0x0002
F_LNNO	 = 0x0004
F_LSYMS  = 0x0008
F_AR32WR = 0x0100

STYP_TEXT = 0x0020
STYP_DATA = 0x0040
STYP_BSS  = 0x0080

N_UNDEF = 0
N_ABS	= -1
N_DEBUG = -2

T_NULL	 = 0000b
T_VOID	 = 0001b
T_CHAR	 = 0010b
T_SHORT  = 0011b
T_INT	 = 0100b
T_LONG	 = 0101b
T_FLOAT  = 0110b
T_DOUBLE = 0111b
T_STRUCT = 1000b
T_UNION  = 1001b
T_ENUM	 = 1010b
T_MOE	 = 1011b
T_UCHAR  = 1100b
T_USHORT = 1101b
T_UINT	 = 1110b
T_ULONG  = 1111b
T_LNGDBL = 01_0000b

DT_NON	 = 00b
DT_PTR	 = 01b
DT_FCN	 = 10b
DT_ARY	 = 11b

C_NULL	  = 0
C_AUTO	  = 1
C_EXT	  = 2
C_STAT	  = 3
C_REG	  = 4
C_EXTDEF  = 5
C_LABEL   = 6
C_ULABEL  = 7
C_MOS	  = 8
C_ARG	  = 9
C_STRTAG  = 10
C_MOU	  = 11
C_UNTAG   = 12
C_TPDEF   = 13
C_USTATIC = 14
C_ENTAG   = 15
C_MOE	  = 16
C_REGPARM = 17
C_FIELD   = 18
C_AUTOARG = 19
C_LASTENT = 20
C_BLOCK   = 100
C_FCN	  = 101
C_EOS	  = 102
C_FILE	  = 103
C_LINE	  = 104
C_ALIAS   = 105
C_HIDDEN  = 106
C_EFCN	  = 255

RELOC_ADDR32 = 6
RELOC_REL32  = 20

COFF::

namespace COFF

	Header:

	f_magic 	dw I386MAGIC
	f_nscns 	dw NUMBER_OF_SECTIONS
	f_timdat	dd __TIME__
	f_symptr	dd SYMBOL_TABLE_OFFSET
	f_nsyms 	dd NUMBER_OF_SYMBOLS
	f_opthdr	dw 0
	f_flags 	dw F_AR32WR + F_LNNO

	Sections:	db NUMBER_OF_SECTIONS * SCNHSZ dup 0

	virtual at 0
		symbol_table:: rb NUMBER_OF_SYMBOLS * SYMESZ
	end virtual

	virtual at 0
		string_table:: dd STRING_TABLE_SIZE
		STRING_POSITION = $
		rb STRING_TABLE_SIZE - $
	end virtual

	virtual at 0
		relocations:: rb NUMBER_OF_RELOCATIONS * RELSZ
	end virtual

	element relocatable?

	macro section_start
		local sym
		element sym : relocatable * (1+SECTION_INDEX) + SYMBOL_INDEX
		SECTION_BASE = sym
		org sym
		if DEFINED_SECTION | defined DEFAULT_SECTION
			store SECTION_NAME : 8 at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_name
			store C_STAT at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_sclass
			store 1+SECTION_INDEX at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_scnum
			SYMBOL_INDEX = SYMBOL_INDEX + 1
		end if
	end macro

	RELOCATION_INDEX = 0
	SECTION_INDEX = 0
	SECTION_RELOCATION_INDEX = RELOCATION_INDEX
	SYMBOL_INDEX = 0

	SECTION_OFFSET = $%
	SECTION_ALIGN = 4
	SECTION_NAME = '.flat'
	SECTION_FLAGS = STYP_TEXT + STYP_DATA
	DEFINED_SECTION = 0
	section_start

end namespace

macro section?
	namespace COFF

		SECTION_SIZE = $% - SECTION_OFFSET

		if DEFINED_SECTION | SECTION_SIZE > 0

			if ~ DEFINED_SECTION
				DEFAULT_SECTION := 1
			end if

			if $%% = SECTION_OFFSET
				SECTION_FLAGS = SECTION_FLAGS or STYP_BSS
				SECTION_OFFSET = 0
				section $
			else
				UNINITIALIZED_LENGTH = $% - $%%
				section $
				db UNINITIALIZED_LENGTH dup 0
			end if

			store SECTION_NAME : 8 at COFF:Sections + SECTION_INDEX * SCNHSZ + SCNHDR.s_name
			store SECTION_OFFSET at COFF:Sections + SECTION_INDEX * SCNHSZ + SCNHDR.s_scnptr
			store SECTION_SIZE at COFF:Sections + SECTION_INDEX * SCNHSZ + SCNHDR.s_size
			store SECTION_FLAGS at COFF:Sections + SECTION_INDEX * SCNHSZ + SCNHDR.s_flags

			if RELOCATION_INDEX > SECTION_RELOCATION_INDEX
				store RELOCATION_INDEX - SECTION_RELOCATION_INDEX at COFF:Sections + SECTION_INDEX * SCNHSZ + SCNHDR.s_nreloc
				store RELOCATIONS_OFFSET + SECTION_RELOCATION_INDEX * RELSZ at COFF:Sections + SECTION_INDEX * SCNHSZ + SCNHDR.s_relptr
			end if

			SECTION_INDEX = SECTION_INDEX + 1

		end if

	end namespace
end macro

macro section? declaration*
	namespace COFF

		section

		DEFINED_SECTION = 1
		SECTION_FLAGS = 0
		SECTION_OFFSET = $%
		SECTION_ALIGN = 4

		match name attributes, declaration

			SECTION_NAME = name

			local seq,list
			define seq attributes
			while 1
				match car cdr, seq
					define list car
					define seq cdr
				else
					match any, seq
						define list any
					end match
					break
				end match
			end while
			irpv attribute, list
				match =code?, attribute
					SECTION_FLAGS = SECTION_FLAGS or STYP_TEXT
				else match =data?, attribute
					SECTION_FLAGS = SECTION_FLAGS or STYP_DATA
				else
					err 'unknown attribute "',`attribute,'"'
				end match
			end irpv

		else

			SECTION_NAME = declaration

		end match

		section_start

		SECTION_RELOCATION_INDEX = RELOCATION_INDEX

	end namespace
end macro

calminstruction align? boundary,value:?
	check	COFF.SECTION_ALIGN mod (boundary) = 0
	jyes	allowed
	err	'section not aligned enough'
	exit
    allowed:
	compute boundary, (boundary-1)-($-COFF.SECTION_BASE+boundary-1) mod boundary
	arrange value, =db boundary =dup value
	assemble value
end calminstruction

macro public? declaration*
	namespace COFF
		match =static? value =as? str, declaration
			SYMBOL_VALUE = value
			SYMBOL_NAME = string str
			SYMBOL_CLASS = C_STAT
		else match value =as? str, declaration
			SYMBOL_VALUE = value
			SYMBOL_NAME = string str
			SYMBOL_CLASS = C_EXT
		else match =static? value, declaration
			SYMBOL_VALUE = value
			SYMBOL_NAME = `value
			SYMBOL_CLASS = C_STAT
		else
			SYMBOL_VALUE = declaration
			SYMBOL_NAME = `declaration
			SYMBOL_CLASS = C_EXT
		end match
		if SYMBOL_VALUE relativeto 1 elementof SYMBOL_VALUE & 1 elementof (1 metadataof SYMBOL_VALUE) relativeto relocatable & 1 scaleof (1 metadataof SYMBOL_VALUE) > 0
			SYMBOL_SECTION_INDEX = 1 scaleof (1 metadataof SYMBOL_VALUE)
			SYMBOL_VALUE = SYMBOL_VALUE - 1 elementof SYMBOL_VALUE
		else
			SYMBOL_SECTION_INDEX = N_ABS
		end if
		if lengthof SYMBOL_NAME > 8
			store 0 at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_zeroes
			store STRING_POSITION at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_offset
			store SYMBOL_NAME : lengthof SYMBOL_NAME at string_table:STRING_POSITION
			STRING_POSITION = STRING_POSITION + lengthof SYMBOL_NAME + 1
		else
			store SYMBOL_NAME : 8 at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_name
		end if
		store SYMBOL_VALUE at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_value
		store SYMBOL_SECTION_INDEX at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_scnum
		store SYMBOL_CLASS at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_sclass
		SYMBOL_INDEX = SYMBOL_INDEX + 1
	end namespace
end macro

macro extrn? declaration*
	namespace COFF
		local sym,psym
		element sym : relocatable * (-1) + SYMBOL_INDEX
		match str =as? name:size, declaration
			label name:size at sym
			SYMBOL_NAME = string str
		else match name:size, declaration
			label name:size at sym
			SYMBOL_NAME = `name
		else match str =as? name, declaration
			label name at sym
			SYMBOL_NAME = string str
		else
			label declaration at sym
			SYMBOL_NAME = `declaration
		end match
		if lengthof SYMBOL_NAME > 8
			store 0 at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_zeroes
			store STRING_POSITION at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_offset
			store SYMBOL_NAME : lengthof SYMBOL_NAME at string_table:STRING_POSITION
			STRING_POSITION = STRING_POSITION + lengthof SYMBOL_NAME + 1
		else
			store SYMBOL_NAME : 8 at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_name
		end if
		store C_EXT at symbol_table : SYMBOL_INDEX * SYMESZ + SYMENT.e_sclass
		SYMBOL_INDEX = SYMBOL_INDEX + 1
	end namespace
end macro

calminstruction calminstruction?.initsym? var*, val&
	publish var, val
end calminstruction

calminstruction calminstruction?.asm? line&
	local	name, i
	initsym name, name.0
	match	name.i, name
	compute i, i+1
	arrange name, name.i
	publish name, line
	arrange line, =assemble name
	assemble line
end calminstruction

calminstruction dword? value
	compute value, value
	check	~ value relativeto 0 & value relativeto 1 elementof value & 1 elementof (1 metadataof value) relativeto COFF.relocatable
	jyes	addr32
	check	~ value relativeto 0 & (value + COFF.SECTION_BASE) relativeto 1 elementof (value + COFF.SECTION_BASE)
	jno	plain
	check	1 elementof (1 metadataof (value + COFF.SECTION_BASE)) relativeto COFF.relocatable
	jyes	rel32
    plain:
	emit	4, value
	exit
	local	offset, symndx, type
    addr32:
	compute symndx, 0 scaleof (1 metadataof value)
	compute type, RELOC_ADDR32
	jump	add_relocation
    rel32:
	compute value, value + COFF.SECTION_BASE
	compute symndx, 0 scaleof (1 metadataof value)
	compute type, RELOC_REL32
	jump	add_relocation
    add_relocation:
	compute offset, $%
	emit	4, 0 scaleof value
	check	$% > offset
	jno	done
	compute offset, offset - COFF.SECTION_OFFSET
	local	reloc
	compute reloc, COFF.RELOCATION_INDEX * RELSZ
	asm	store offset at COFF.relocations : reloc + RELOC.r_vaddr
	asm	store symndx at COFF.relocations : reloc + RELOC.r_symndx
	asm	store type at COFF.relocations : reloc + RELOC.r_type
	compute COFF.RELOCATION_INDEX, COFF.RELOCATION_INDEX + 1
    done:
end calminstruction

calminstruction dd? definitions&
	local	value, n
    start:
	match	value=,definitions, definitions, ()
	jyes	recognize
	match	value, definitions
	arrange definitions,
    recognize:
	match	n =dup? value, value, ()
	jyes	duplicate
	match	?, value
	jyes	reserve
	call	dword, value
    next:
	match	, definitions
	jno	start
	take	, definitions
	take	definitions, definitions
	jyes	next
	exit
    reserve:
	emit	dword
	jump	next
    duplicate:
	match	(value), value
    stack:
	check	n
	jno	next
	take	definitions, value
	arrange value, definitions
	compute n, n - 1
	jump	stack
end calminstruction

calminstruction (label) dd? definitions&
	local	cmd
	arrange cmd, =label label : =dword
	assemble cmd
	arrange cmd, =dd definitions
	assemble cmd
end calminstruction

postpone
	purge section?
	section
	namespace COFF

		NUMBER_OF_SECTIONS := SECTION_INDEX
		STRING_TABLE_SIZE := STRING_POSITION
		NUMBER_OF_SYMBOLS := SYMBOL_INDEX
		NUMBER_OF_RELOCATIONS := RELOCATION_INDEX

		RELOCATIONS_OFFSET = $%
		load byte_sequence : NUMBER_OF_RELOCATIONS * RELSZ from relocations:0
		db byte_sequence

		SYMBOL_TABLE_OFFSET = $%
		load byte_sequence : NUMBER_OF_SYMBOLS * SYMESZ from symbol_table:0
		db byte_sequence

		load byte_sequence : STRING_TABLE_SIZE from string_table:0
		db byte_sequence

	end namespace
end postpone
