// File: /workspace/github_cpp_makefile_star2_O3/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 %r14
pushq %rbx
subq $0x18, %rsp
movq %rdi, %r14
movq 0x10(%rdi), %rcx
movq 0x18(%rdi), %rax
cmpq %rax, %rcx
je 0x4488
movb 0x8(%r14), %dl
testb %dl, %dl
je 0x4488
movq %rsi, %rbx
cmpb $0x5, %dl
jb 0x4437
leaq 0xaf08(%rip), %rdx      # 0xf32a
movl $0x1, %edi
movq %r14, %rsi
callq 0x6099
movq 0x10(%r14), %rcx
movq 0x18(%r14), %rax
subq %rcx, %rax
cmpq $0x24, %rax
je 0x4493
cmpq $0xc, %rax
jne 0x44fb
movl (%rcx), %edx
movl 0x4(%rcx), %eax
cmpb $0x0, 0x9(%r14)
je 0x44c0
andl $-0x2, %edx
cmpl $0x2, %edx
jne 0x4516
movzbl 0x8(%r14), %ecx
movl 0x4(%r14), %edx
addl %ecx, %edx
subl %edx, %eax
cmpl $0x1, %ecx
jbe 0x451f
movswl %ax, %ecx
cmpl %eax, %ecx
je 0x4532
jmp 0x4554
leaq 0xae8a(%rip), %rdx      # 0xf319
xorl %edi, %edi
jmp 0x4507
cmpl $0x3, (%rcx)
jne 0x44fb
cmpl $0x2, 0xc(%rcx)
jne 0x44fb
movl 0x18(%rcx), %eax
movl %eax, %edx
andl $-0x2, %edx
cmpl $0x18, %edx
jne 0x44fb
movl 0x10(%rcx), %esi
cmpl $0x19, %esi
jb 0x4586
leaq 0xaedb(%rip), %rdx      # 0xf399
jmp 0x448f
cmpl $0x3, %edx
je 0x455d
cmpl $0x2, %edx
jne 0x44fb
cmpb $0x0, 0x8(%r14)
je 0x462a
xorl %ecx, %ecx
movl 0x4(%r14), %edx
addl %ecx, %edx
movq 0x50(%rbx), %rsi
movb %al, (%rsi,%rdx)
incl %ecx
shrl $0x8, %eax
movzbl 0x8(%r14), %edx
cmpl %edx, %ecx
jb 0x44db
jmp 0x462a
leaq 0xaea7(%rip), %rdx      # 0xf3a9
movl $0x1, %edi
movq %r14, %rsi
addq $0x18, %rsp
popq %rbx
popq %r14
jmp 0x6099
leaq 0xae4c(%rip), %rdx      # 0xf369
jmp 0x4502
cmpb $0x1, %cl
jne 0x4554
leal 0x80(%rax), %ecx
cmpl $0x100, %ecx            # imm = 0x100
jae 0x4554
xorl %ecx, %ecx
movl 0x4(%r14), %edx
addl %ecx, %edx
movq 0x50(%rbx), %rsi
movb %al, (%rsi,%rdx)
incl %ecx
sarl $0x8, %eax
movzbl 0x8(%r14), %edx
cmpl %edx, %ecx
jb 0x4534
jmp 0x462a
leaq 0xadf1(%rip), %rdx      # 0xf34c
jmp 0x4502
movl 0x8(%rcx), %ecx
testl %ecx, %ecx
je 0x45cc
movzwl (%rbx), %edx
leaq 0x8(%rsp), %rsi
cmpl %edx, %ecx
jne 0x45d8
movb $0x0, 0x1(%rsi)
movb 0x8(%r14), %cl
movb %cl, (%rsi)
movl 0x4(%r14), %ecx
movl %ecx, 0x4(%rsi)
movl %eax, 0x8(%rsi)
jmp 0x45be
movl 0x8(%rcx), %edx
testl %edx, %edx
je 0x45cc
movl %esi, %edi
negb %dil
cmpl $0x18, %eax
movzbl %dil, %eax
cmovnel %esi, %eax
movzwl (%rbx), %esi
cmpl %esi, %edx
jne 0x45f8
movb 0x8(%r14), %dl
leaq 0x8(%rsp), %rsi
movb %dl, (%rsi)
movl 0x4(%r14), %edx
movl %edx, 0x4(%rsi)
movl 0x4(%rcx), %ecx
movl %ecx, 0x8(%rsi)
movb %al, 0x1(%rsi)
subq $-0x80, %rbx
movq %rbx, %rdi
callq 0x6e86
jmp 0x462a
leaq 0xadb6(%rip), %rdx      # 0xf389
jmp 0x4502
movb $0x0, 0x1(%rsi)
movw $0x1, 0x8(%rsi)
movb 0x8(%r14), %dl
movb %dl, (%rsi)
movl 0x4(%r14), %edx
movl %edx, 0x4(%rsi)
movw %cx, 0xa(%rsi)
movl %eax, 0xc(%rsi)
jmp 0x461e
leaq 0x8(%rsp), %rsi
movw $0x1, 0x8(%rsi)
movb 0x8(%r14), %dil
movb %dil, (%rsi)
movl 0x4(%r14), %edi
movl %edi, 0x4(%rsi)
movw %dx, 0xa(%rsi)
movl 0x4(%rcx), %ecx
movl %ecx, 0xc(%rsi)
movb %al, 0x1(%rsi)
addq $0x68, %rbx
movq %rbx, %rdi
callq 0x6ec4
addq $0x18, %rsp
popq %rbx
popq %r14
retq