// File: ksherlock[P]wdc-utils/link.cpp
// Function: to_omf(expression const&, omf::segment&)
void to_omf(const expression &e, omf::segment &seg) {
	if (e.stack.empty() || e.size == 0) {
		expr_error(false, e, "Expression empty");
		return;
	}

	if (e.size < 1 || e.size > 4) {
		expr_error(true, e, "Expression size must be 1-4 bytes");
	}


	if (e.stack.size() == 1) {
		auto &a = e.stack[0];

		uint32_t value = a.value;

		if (e.relative) {
			if (a.tag == OP_VAL || a.tag == OP_LOC) {

				int tmp = (int)value - (int)e.offset - (int)e.size;
				bool ok = false;

				if (e.size >= 2 && in_range(tmp, -32768, 32767)) ok = true;
				if (e.size == 1 && in_range(tmp, -128, 127)) ok = true;

				if (!ok) {
					expr_error(true, e, "Relative branch out of range");
					return;					
				}

				for (int i = 0; i < e.size; ++i, tmp >>= 8)
					seg.data[e.offset + i] = tmp & 0xff;

				return;
			}

			expr_error(true, e, "Relative expression too complex");
			return;
		}


		if (a.tag == OP_VAL) {
			for (int i = 0; i < e.size; ++i, value >>= 8)
				seg.data[e.offset + i] = value & 0xff;
			return;
		}

		if (a.tag == OP_LOC) {
			auto &loc = a;

			if (loc.section == 0) {
				expr_error(true, e, "Invalid segment");
				return;
			}

			if (loc.section == seg.segnum) {
				omf::reloc r;
				r.size = e.size;
				r.offset = e.offset;
				r.value = value;

				seg.relocs.emplace_back(r);
			} else {
				omf::interseg r;
				r.size = e.size;
				r.offset = e.offset;
				r.segment = loc.section;
				r.segment_offset = loc.value;

				seg.intersegs.emplace_back(r);

				// if generating super, store 
			}
			return;
		}

		// error handled below.
	}



	if (e.stack.size() == 3) {
		auto &loc = e.stack[0];
		auto &shift = e.stack[1];
		auto &op = e.stack[2];

		if (loc.tag == OP_LOC && shift.tag == OP_VAL && (op.tag == OP_SHL || op.tag == OP_SHR)) {


			if (shift.value > 24) {
				expr_error(false, e, "Shift too large");
				// data is already pre-zeroed.
				return;
			}

			if (loc.section == 0) {
				expr_error(true, e, "Invalid segment");
				return;
			}

			uint32_t value = loc.value;
			uint8_t shift_value = shift.value;
			if (op.tag == OP_SHR) {
				value >>= shift_value;
				shift_value = -shift_value;
			} else {
				value <<= shift_value;
			}

			if (loc.section == seg.segnum) {
				omf::reloc r;
				r.size = e.size;
				r.offset = e.offset;
				r.value = loc.value;
				r.shift = shift_value;

				#if 0
				// also store value in data
				for (int i = 0; i < e.size; ++i, value >>= 8)
					seg.data[e.offset + i] = value & 0xff;
				#endif

				seg.relocs.emplace_back(r);
			} else {
				omf::interseg r;
				r.size = e.size;
				r.offset = e.offset;
				r.segment = loc.section;
				r.segment_offset = loc.value;
				r.shift = shift_value;

				seg.intersegs.emplace_back(r);
			}
			return;
		}
	}


	expr_error(true, e, "Expression too complex");
	// should also pretty-print the expression.
	return;

}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0xa0, %rsp
movq %rdi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
movq -0x8(%rbp), %rdi
addq $0x10, %rdi
callq 0xc1a0
testb $0x1, %al
jne 0x60c1
movq -0x8(%rbp), %rax
movzbl 0x8(%rax), %eax
cmpl $0x0, %eax
jne 0x60d8
movq -0x8(%rbp), %rsi
xorl %edi, %edi
leaq 0x25403(%rip), %rdx     # 0x2b4d1
callq 0xac10
jmp 0x6586
movq -0x8(%rbp), %rax
movzbl 0x8(%rax), %eax
cmpl $0x1, %eax
jl 0x60f2
movq -0x8(%rbp), %rax
movzbl 0x8(%rax), %eax
cmpl $0x4, %eax
jle 0x6107
movq -0x8(%rbp), %rsi
movl $0x1, %edi
leaq 0x253e0(%rip), %rdx     # 0x2b4e2
callq 0xac10
movq -0x8(%rbp), %rdi
addq $0x10, %rdi
callq 0xa020
cmpq $0x1, %rax
jne 0x639f
movq -0x8(%rbp), %rdi
addq $0x10, %rdi
xorl %eax, %eax
movl %eax, %esi
callq 0xc1e0
movq %rax, -0x18(%rbp)
movq -0x18(%rbp), %rax
movl 0x4(%rax), %eax
movl %eax, -0x1c(%rbp)
movq -0x8(%rbp), %rax
cmpb $0x0, 0x9(%rax)
je 0x6268
movq -0x18(%rbp), %rax
cmpl $0x2, (%rax)
je 0x6161
movq -0x18(%rbp), %rax
cmpl $0x3, (%rax)
jne 0x624e
movl -0x1c(%rbp), %eax
movq -0x8(%rbp), %rcx
subl 0x4(%rcx), %eax
movq -0x8(%rbp), %rcx
movzbl 0x8(%rcx), %ecx
subl %ecx, %eax
movl %eax, -0x20(%rbp)
movb $0x0, -0x21(%rbp)
movq -0x8(%rbp), %rax
movzbl 0x8(%rax), %eax
cmpl $0x2, %eax
jl 0x61a5
movl -0x20(%rbp), %edi
movl $0xffff8000, %esi       # imm = 0xFFFF8000
movl $0x7fff, %edx           # imm = 0x7FFF
callq 0xc200
testb $0x1, %al
jne 0x61a1
jmp 0x61a5
movb $0x1, -0x21(%rbp)
movq -0x8(%rbp), %rax
movzbl 0x8(%rax), %eax
cmpl $0x1, %eax
jne 0x61ce
movl -0x20(%rbp), %edi
movl $0xffffff80, %esi       # imm = 0xFFFFFF80
movl $0x7f, %edx
callq 0xc200
testb $0x1, %al
jne 0x61ca
jmp 0x61ce
movb $0x1, -0x21(%rbp)
testb $0x1, -0x21(%rbp)
jne 0x61ee
movq -0x8(%rbp), %rsi
movl $0x1, %edi
leaq 0x25320(%rip), %rdx     # 0x2b504
callq 0xac10
jmp 0x6586
movl $0x0, -0x28(%rbp)
movl -0x28(%rbp), %eax
movq -0x8(%rbp), %rcx
movzbl 0x8(%rcx), %ecx
cmpl %ecx, %eax
jge 0x6249
movl -0x20(%rbp), %eax
andl $0xff, %eax
movb %al, -0x95(%rbp)
movq -0x10(%rbp), %rdi
addq $0x50, %rdi
movq -0x8(%rbp), %rax
movl 0x4(%rax), %eax
addl -0x28(%rbp), %eax
movl %eax, %eax
movl %eax, %esi
callq 0xc230
movb -0x95(%rbp), %cl
movb %cl, (%rax)
movl -0x28(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x28(%rbp)
movl -0x20(%rbp), %eax
sarl $0x8, %eax
movl %eax, -0x20(%rbp)
jmp 0x61f5
jmp 0x6586
movq -0x8(%rbp), %rsi
movl $0x1, %edi
leaq 0x252c3(%rip), %rdx     # 0x2b521
callq 0xac10
jmp 0x6586
movq -0x18(%rbp), %rax
cmpl $0x2, (%rax)
jne 0x62d1
movl $0x0, -0x2c(%rbp)
movl -0x2c(%rbp), %eax
movq -0x8(%rbp), %rcx
movzbl 0x8(%rcx), %ecx
cmpl %ecx, %eax
jge 0x62cc
movl -0x1c(%rbp), %eax
andl $0xff, %eax
movb %al, -0x96(%rbp)
movq -0x10(%rbp), %rdi
addq $0x50, %rdi
movq -0x8(%rbp), %rax
movl 0x4(%rax), %eax
addl -0x2c(%rbp), %eax
movl %eax, %eax
movl %eax, %esi
callq 0xc230
movb -0x96(%rbp), %cl
movb %cl, (%rax)
movl -0x2c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x2c(%rbp)
movl -0x1c(%rbp), %eax
shrl $0x8, %eax
movl %eax, -0x1c(%rbp)
jmp 0x6278
jmp 0x6586
movq -0x18(%rbp), %rax
cmpl $0x3, (%rax)
jne 0x639d
movq -0x18(%rbp), %rax
movq %rax, -0x38(%rbp)
movq -0x38(%rbp), %rax
cmpl $0x0, 0x8(%rax)
jne 0x630a
movq -0x8(%rbp), %rsi
movl $0x1, %edi
leaq 0x25241(%rip), %rdx     # 0x2b541
callq 0xac10
jmp 0x6586
movq -0x38(%rbp), %rax
movl 0x8(%rax), %eax
movq -0x10(%rbp), %rcx
movzwl (%rcx), %ecx
cmpl %ecx, %eax
jne 0x6355
leaq -0x44(%rbp), %rdi
callq 0xc250
movq -0x8(%rbp), %rax
movb 0x8(%rax), %al
movb %al, -0x44(%rbp)
movq -0x8(%rbp), %rax
movl 0x4(%rax), %eax
movl %eax, -0x40(%rbp)
movl -0x1c(%rbp), %eax
movl %eax, -0x3c(%rbp)
movq -0x10(%rbp), %rdi
addq $0x80, %rdi
leaq -0x44(%rbp), %rsi
callq 0xc280
jmp 0x6398
leaq -0x54(%rbp), %rdi
callq 0xc300
movq -0x8(%rbp), %rax
movb 0x8(%rax), %al
movb %al, -0x54(%rbp)
movq -0x8(%rbp), %rax
movl 0x4(%rax), %eax
movl %eax, -0x50(%rbp)
movq -0x38(%rbp), %rax
movl 0x8(%rax), %eax
movw %ax, -0x4a(%rbp)
movq -0x38(%rbp), %rax
movl 0x4(%rax), %eax
movl %eax, -0x48(%rbp)
movq -0x10(%rbp), %rdi
addq $0x68, %rdi
leaq -0x54(%rbp), %rsi
callq 0xc330
jmp 0x6586
jmp 0x639f
movq -0x8(%rbp), %rdi
addq $0x10, %rdi
callq 0xa020
cmpq $0x3, %rax
jne 0x6571
movq -0x8(%rbp), %rdi
addq $0x10, %rdi
xorl %eax, %eax
movl %eax, %esi
callq 0xc1e0
movq %rax, -0x60(%rbp)
movq -0x8(%rbp), %rdi
addq $0x10, %rdi
movl $0x1, %esi
callq 0xc1e0
movq %rax, -0x68(%rbp)
movq -0x8(%rbp), %rdi
addq $0x10, %rdi
movl $0x2, %esi
callq 0xc1e0
movq %rax, -0x70(%rbp)
movq -0x60(%rbp), %rax
cmpl $0x3, (%rax)
jne 0x656f
movq -0x68(%rbp), %rax
cmpl $0x2, (%rax)
jne 0x656f
movq -0x70(%rbp), %rax
cmpl $0x19, (%rax)
je 0x6427
movq -0x70(%rbp), %rax
cmpl $0x18, (%rax)
jne 0x656f
movq -0x68(%rbp), %rax
cmpl $0x18, 0x4(%rax)
jbe 0x6448
movq -0x8(%rbp), %rsi
xorl %edi, %edi
leaq 0x25113(%rip), %rdx     # 0x2b551
callq 0xac10
jmp 0x6586
movq -0x60(%rbp), %rax
cmpl $0x0, 0x8(%rax)
jne 0x646c
movq -0x8(%rbp), %rsi
movl $0x1, %edi
leaq 0x250df(%rip), %rdx     # 0x2b541
callq 0xac10
jmp 0x6586
movq -0x60(%rbp), %rax
movl 0x4(%rax), %eax
movl %eax, -0x74(%rbp)
movq -0x68(%rbp), %rax
movl 0x4(%rax), %eax
movb %al, -0x75(%rbp)
movq -0x70(%rbp), %rax
cmpl $0x18, (%rax)
jne 0x64a2
movzbl -0x75(%rbp), %ecx
movl -0x74(%rbp), %eax
shrl %cl, %eax
movl %eax, -0x74(%rbp)
movzbl -0x75(%rbp), %ecx
xorl %eax, %eax
subl %ecx, %eax
movb %al, -0x75(%rbp)
jmp 0x64ae
movzbl -0x75(%rbp), %ecx
movl -0x74(%rbp), %eax
shll %cl, %eax
movl %eax, -0x74(%rbp)
movq -0x60(%rbp), %rax
movl 0x8(%rax), %eax
movq -0x10(%rbp), %rcx
movzwl (%rcx), %ecx
cmpl %ecx, %eax
jne 0x650f
leaq -0x84(%rbp), %rdi
callq 0xc250
movq -0x8(%rbp), %rax
movb 0x8(%rax), %al
movb %al, -0x84(%rbp)
movq -0x8(%rbp), %rax
movl 0x4(%rax), %eax
movl %eax, -0x80(%rbp)
movq -0x60(%rbp), %rax
movl 0x4(%rax), %eax
movl %eax, -0x7c(%rbp)
movb -0x75(%rbp), %al
movb %al, -0x83(%rbp)
movq -0x10(%rbp), %rdi
addq $0x80, %rdi
leaq -0x84(%rbp), %rsi
callq 0xc280
jmp 0x656d
leaq -0x94(%rbp), %rdi
callq 0xc300
movq -0x8(%rbp), %rax
movb 0x8(%rax), %al
movb %al, -0x94(%rbp)
movq -0x8(%rbp), %rax
movl 0x4(%rax), %eax
movl %eax, -0x90(%rbp)
movq -0x60(%rbp), %rax
movl 0x8(%rax), %eax
movw %ax, -0x8a(%rbp)
movq -0x60(%rbp), %rax
movl 0x4(%rax), %eax
movl %eax, -0x88(%rbp)
movb -0x75(%rbp), %al
movb %al, -0x93(%rbp)
movq -0x10(%rbp), %rdi
addq $0x68, %rdi
leaq -0x94(%rbp), %rsi
callq 0xc330
jmp 0x6586
jmp 0x6571
movq -0x8(%rbp), %rsi
movl $0x1, %edi
leaq 0x24fe0(%rip), %rdx     # 0x2b561
callq 0xac10
addq $0xa0, %rsp
popq %rbp
retq
nop