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 ...
}