I read “First Time Reading the 486,” so I’ll note down what caught my attention. It’s helpful since I’m making an emulator. Throughout the book, the diagrams are very clear, so just looking at them is educational.

Segments

  • When reading instructions from EIP, the segment pointed to by the CS register is used, and when accessing memory during instruction execution, the segment pointed to by the DS register is used. Also, for stack-related operations like PUSH, POP, CALL, RET, the segment pointed to by the SS register is used. Since the stack grows toward lower addresses, the stack segment’s limit exceptionally specifies the lower bound.
  • Referencing the segment descriptor every time memory is accessed is wasteful, so the CPU has a cache of segment descriptors themselves (segment descriptor cache) internally.
  • In protected mode, if the CS register points to a 16-bit segment, the operand size and address size are 16 bits. Similarly, in a 32-bit segment, those sizes are 32 bits.
  • Each segment specifies a privilege level with DPL (Descriptor Privilege Level). The DPL of the currently executing segment determines the CPL (Current Privilege Level). CPL is the lower 2 bits of the CS register, and DPL is set in the segment descriptor. If the DPL of the jump destination segment is higher than the CPL, you cannot jump directly and must go through a call gate.

Descriptors

  • In addition to segment descriptors, the GDT stores multiple system objects such as TSS, call gates, task gates, trap gates, and interrupt gates.
  • Various gate descriptors also have DPL set, and when calling a gate, the CPL and DPL are compared. If the CPL is equal to or higher than the gate’s DPL, you can jump to that segment regardless of the segment’s DPL.
  • When using LDT, create a descriptor pointing to the LDT in the GDT and set that selector value to LDTR.

Interrupts

  • Interrupt numbers range from 0x00 to 0xFF, a total of 256 types.
  • The Interrupt Descriptor Table (IDT) contains gate descriptors arranged in order. The interrupt number becomes the offset within the IDT. There are three types of gates: interrupt gate, trap gate, and task gate.
  • Normally, the RET instruction simply POPs CS and EIP, but if the DPL of the popped CS is lower than the CPL, it also POPs SS and ESP.
  • When calling an OS function from a low privilege level through a call gate, the arguments only exist on the low privilege level’s stack, so the OS can’t easily access them. As a countermeasure, the copy count parameter of the call gate is set to the number of argument bytes / 4, and the arguments are automatically copied to the OS-side stack. When returning, use the RET instruction with an operand to increment the OS stack.

I/O

  • IO ports don’t have descriptors, so they don’t have DPL, but instead there’s a dedicated privilege level for IO ports called IOPRL (I/O Privilege Level). IOPRL is set in the EFLAGS register. It can also be controlled with the IO permission map in the TSS.

Other

  • There was a technology called clock doubler that doubled the clock frequency. It was used in the 486DX2 and ODP (Over Drive Processor).
  • System registers used by the OS are GDTR, IDTR, LDTR, TR, CR0-CR3.
  • By manipulating DR0-DR7, you can set breakpoints and debug.
  • The address bus and data bus are 32 bits, and the IO address bus is 16 bits.
  • Operand size and address size are interpreted as either 32 bits or 16 bits. Both can be changed in size by adding a prefix (0x66 or 0x67).
  • The select value of the TSS used by the current task is held in TR (Task Register).
  • For a CALL instruction at the same privilege level, it just PUSHes CS and EIP and jumps. For a CALL instruction to a call gate to move to a different privilege level, it switches stacks first, then PUSHes SS, ESP, CS, and EIP.