for(;;){
	run next instruction
}
#define DATA_PORT    0x378
#define STATUS_PORT  0x379
#define   BUSY 0x80
#define CONTROL_PORT 0x37A
#define   STROBE 0x01
void
lpt_putc(int c)
{
  /* wait for printer to consume previous byte */
  while((inb(STATUS_PORT) & BUSY) == 0)
    ;
  /* put the byte on the parallel lines */
  outb(DATA_PORT, c);
  /* tell the printer to look at the data */
  outb(CONTROL_PORT, STROBE);
  outb(CONTROL_PORT, 0);
}
		
+------------------+ <- 0xFFFFFFFF (4GB) | 32-bit | | memory mapped | | devices | | | /\/\/\/\/\/\/\/\/\/\ /\/\/\/\/\/\/\/\/\/\ | | | Unused | | | +------------------+ <- depends on amount of RAM | | | | | Extended Memory | | | | | +------------------+ <- 0x00100000 (1MB) | BIOS ROM | +------------------+ <- 0x000F0000 (960KB) | 16-bit devices, | | expansion ROMs | +------------------+ <- 0x000C0000 (768KB) | VGA Display | +------------------+ <- 0x000A0000 (640KB) | | | Low Memory | | | +------------------+ <- 0x00000000
| AT&T syntax | "C"-ish equivalent | |
| movl %eax, %edx | edx = eax; | register mode | 
| movl $0x123, %edx | edx = 0x123; | immediate | 
| movl 0x123, %edx | edx = *(int32_t*)0x123; | direct | 
| movl (%ebx), %edx | edx = *(int32_t*)ebx; | indirect | 
| movl 4(%ebx), %edx | edx = *(int32_t*)(ebx+4); | displaced | 
| Example instruction | What it does | 
| pushl %eax | subl $4, %esp movl %eax, (%esp) | 
| popl %eax | movl (%esp), %eax addl $4, %esp | 
| call $0x12345 | pushl %eip (*) movl $0x12345, %eip (*) | 
| ret | popl %eip (*) | 
+------------+ | | arg 2 | \ +------------+ >- previous function's stack frame | arg 1 | / +------------+ | | ret %eip | / +============+ | saved %ebp | \ %ebp-> +------------+ | | | | | local | \ | variables, | >- current function's stack frame | etc. | / | | | | | | %esp-> +------------+ /
pushl %ebp movl %esp, %ebp
movl %ebp, %esp popl %ebpor
leave
		int main(void) { return f(8)+1; }
		int f(int x) { return g(x); }
		int g(int x) { return x+3; }
		
	_main: prologue pushl %ebp movl %esp, %ebp body pushl $8 call _f addl $1, %eax epilogue movl %ebp, %esp popl %ebp ret _f: prologue pushl %ebp movl %esp, %ebp body pushl 8(%esp) call _g epilogue movl %ebp, %esp popl %ebp ret _g: prologue pushl %ebp movl %esp, %ebp save %ebx pushl %ebx body movl 8(%ebp), %ebx addl $3, %ebx movl %ebx, %eax restore %ebx popl %ebx epilogue movl %ebp, %esp popl %ebp ret
_g: movl 4(%esp), %eax addl $3, %eax ret
int32_t regs[8]; #define REG_EAX 1; #define REG_EBX 2; #define REG_ECX 3; ... int32_t eip; int16_t segregs[4]; ...
	for (;;) {
		read_instruction();
		switch (decode_instruction_opcode()) {
		case OPCODE_ADD:
			int src = decode_src_reg();
			int dst = decode_dst_reg();
			regs[dst] = regs[dst] + regs[src];
			break;
		case OPCODE_SUB:
			int src = decode_src_reg();
			int dst = decode_dst_reg();
			regs[dst] = regs[dst] - regs[src];
			break;
		...
		}
		eip += instruction_length;
	}
	
	#define KB		1024
	#define MB		1024*1024
	#define LOW_MEMORY	640*KB
	#define EXT_MEMORY	10*MB
	uint8_t low_mem[LOW_MEMORY];
	uint8_t ext_mem[EXT_MEMORY];
	uint8_t bios_rom[64*KB];
	uint8_t read_byte(uint32_t phys_addr) {
		if (phys_addr < LOW_MEMORY)
			return low_mem[phys_addr];
		else if (phys_addr >= 960*KB && phys_addr < 1*MB)
			return rom_bios[phys_addr - 960*KB];
		else if (phys_addr >= 1*MB && phys_addr < 1*MB+EXT_MEMORY) {
			return ext_mem[phys_addr-1*MB];
		else ...
	}
	void write_byte(uint32_t phys_addr, uint8_t val) {
		if (phys_addr < LOW_MEMORY)
			low_mem[phys_addr] = val;
		else if (phys_addr >= 960*KB && phys_addr < 1*MB)
			; /* ignore attempted write to ROM! */
		else if (phys_addr >= 1*MB && phys_addr < 1*MB+EXT_MEMORY) {
			ext_mem[phys_addr-1*MB] = val;
		else ...
	}