// File: /workspace/github_cpp_makefile_star2_O2/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 0x3db6
movb 0x8(%r14), %dl
testb %dl, %dl
je 0x3db6
movq %rsi, %rbx
cmpb $0x5, %dl
jb 0x3d65
leaq 0x9690(%rip), %rdx      # 0xd3e2
pushq $0x1
popq %rdi
movq %r14, %rsi
callq 0x55ec
movq 0x10(%r14), %rcx
movq 0x18(%r14), %rax
subq %rcx, %rax
cmpq $0x24, %rax
je 0x3dc1
cmpq $0xc, %rax
jne 0x3e1f
movl (%rcx), %edx
movl 0x4(%rcx), %eax
cmpb $0x0, 0x9(%r14)
je 0x3dee
andl $-0x2, %edx
cmpl $0x2, %edx
jne 0x3e38
movzbl 0x8(%r14), %ecx
movl 0x4(%r14), %edx
addl %ecx, %edx
subl %edx, %eax
cmpl $0x1, %ecx
jbe 0x3e41
movswl %ax, %edx
cmpl %eax, %edx
je 0x3e54
jmp 0x3e79
leaq 0x9614(%rip), %rdx      # 0xd3d1
xorl %edi, %edi
jmp 0x3e29
cmpl $0x3, (%rcx)
jne 0x3e1f
cmpl $0x2, 0xc(%rcx)
jne 0x3e1f
movl 0x18(%rcx), %eax
movl %eax, %edx
andl $-0x2, %edx
cmpl $0x18, %edx
jne 0x3e1f
movl 0x10(%rcx), %esi
cmpl $0x19, %esi
jb 0x3eab
leaq 0x9665(%rip), %rdx      # 0xd451
jmp 0x3dbd
cmpl $0x3, %edx
je 0x3e82
cmpl $0x2, %edx
jne 0x3e1f
xorl %ecx, %ecx
movzbl 0x8(%r14), %edx
cmpl %edx, %ecx
jae 0x3f4f
movl 0x4(%r14), %edx
addl %ecx, %edx
movq 0x50(%rbx), %rsi
movb %al, (%rsi,%rdx)
incl %ecx
shrl $0x8, %eax
jmp 0x3dfe
leaq 0x963b(%rip), %rdx      # 0xd461
pushq $0x1
popq %rdi
movq %r14, %rsi
addq $0x18, %rsp
popq %rbx
popq %r14
jmp 0x55ec
leaq 0x95e2(%rip), %rdx      # 0xd421
jmp 0x3e26
cmpb $0x1, %cl
jne 0x3e79
leal 0x80(%rax), %edx
cmpl $0x100, %edx            # imm = 0x100
jae 0x3e79
xorl %edx, %edx
movzbl %cl, %ecx
cmpl %ecx, %edx
jae 0x3f4f
movl 0x4(%r14), %ecx
addl %edx, %ecx
movq 0x50(%rbx), %rsi
movb %al, (%rsi,%rcx)
incl %edx
sarl $0x8, %eax
movb 0x8(%r14), %cl
jmp 0x3e56
leaq 0x9584(%rip), %rdx      # 0xd404
jmp 0x3e26
movl 0x8(%rcx), %ecx
testl %ecx, %ecx
je 0x3ef1
movzwl (%rbx), %edx
leaq 0x8(%rsp), %rsi
cmpl %edx, %ecx
jne 0x3efd
movb $0x0, 0x1(%rsi)
movb 0x8(%r14), %cl
movb %cl, (%rsi)
movl 0x4(%r14), %ecx
movl %ecx, 0x4(%rsi)
movl %eax, 0x8(%rsi)
jmp 0x3ee3
movl 0x8(%rcx), %edx
testl %edx, %edx
je 0x3ef1
movl %esi, %edi
negb %dil
cmpl $0x18, %eax
movzbl %dil, %eax
cmovnel %esi, %eax
movzwl (%rbx), %esi
cmpl %esi, %edx
jne 0x3f1d
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 0x606e
jmp 0x3f4f
leaq 0x9549(%rip), %rdx      # 0xd441
jmp 0x3e26
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 0x3f43
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 0x60ac
addq $0x18, %rsp
popq %rbx
popq %r14
retq