This is an implementation of 32 bit Intel x86 in an FPGA. The board being used here is an iceFUN with a Lattice iCE40 HX8K FPGA.
https://www.mikekohn.net/micro/micro_x86_68000_fpga.php
This only implements the important instructions, but does include all the strange addressing modes. Example of some instructions:
- 32, 16, 8 bit register access.
- All the standard ALU and mov instructions with their addressing modes.
- The push and pop only work with 32 bit registers.
The memory model is 8 bit, so reading and writing 32 bits takes 4 memory cycles. Kind of slow, but easier to implement.
Here's a list of instructions that are implemented. There are possibly others missing here.
adc
add
and
call
clc
cbw
cmc
cmp
cwde
dec (reg only, only 16 and 32 bit)
inc (reg only, only 16 and 32 bit)
jcc
jmp
lea
mov
neg
or
pop
push
pushf
ret
sbb
sha
shl
shr
stc
sub
xor
test
Shifts only work directly on registers.
This implementation of the Intel x86 has 4 banks of memory.
- Bank 0: RAM (4096 bytes)
- Bank 1: ROM (4096 bytes loaded from rom.txt)
- Bank 2: Peripherals
- Bank 3: RAM (4096 bytes)
On start-up by default, the chip will load a program from a AT93C86A 2kB EEPROM with a 3-Wire (SPI-like) interface but wll run the code from the ROM. To start the program loaded to RAM, the program select button needs to be held down while the chip is resetting.
The peripherals area contain the following:
- 0x8000: input from push button
- 0x8001: SPI TX
- 0x8002: SPI RX
- 0x8003: SPI CTRL
- 0x8008: ioport0 output (in my test case only 1 pin is connected)
- 0x8009: MIDI note value (60-96) to play a tone on the speaker or 0 to stop
- 0x800a: iport1
NUM 32 16 8
------------------
000 eax / ax / al
001 ecx / cx / cl
010 edx / dx / dl
011 ebx / bx / bl
100 esp / sp / ah
101 ebp / bp / ch
110 esi / si / dh
111 edi / di / bh
A typical x86 instruction has an 8 bit opcode followed by an 8 bit mode byte possibly followed by data or SIB byte. If the destination register is 16 bit, it is prefixed by 0x66.
PREFIX | MOD R/M | SIB | DISPLACEMENT | IMMEDIATE
Displacement and immediate can be 1, 2, or 4 bytes.
Typical opcode encoding:
7 6 5 4 3 2 | 1 | 0
OPCODE | d | s
The bit in position 1 is the direction bit (which of the operands is the source / destination) and the bottom bit tells the size (32 or 8 bit). Again, if the instruction should run in 16 bit mode, a 0x66 prefix comes first. Not all instructions actually use this encoding.
The MOD R/M encoding is:
7 6 | 5 4 3 | 2 1 0
MOD REG R/M
The upper 2 bits (the MOD bits) tells the addressing mode of the instruction:
00: Register Indirect unless: R/M=101 Displacement only.
01: 1 byte signed displacement (-128 to 127)
10: 4 byte signed displacement
11: Register to register.
For all modes but 11, R/M=100 (ESP) is special.
MOD 11 example is:
OP DS MOD REG R/M
mov ebx, edx 89 d3 100010 01 11 010 011
MOD 01 example is:
OP DS MOD REG R/M offset
mov ebx,[edx+0x4] 8B 5a 04 100110 11 01 011 010 0000 0100
mov [edx+0x4],ebx 89 5a 04 100110 01 01 011 010 0000 0100
MOD 01 R/M=100 example is:
mov edx,[ebx+ecx*4+0x64] 8b 54 8b 64
mov [ebx+ecx*4+0x64],edx 89 54 8b 64
| SIB |
DS MOD REG R/M SCALE INDEX BASE OFFSET
100010 11 01 010 100 10 001 011 0x64
100010 01 01 010 100 10 001 011 0x64
MOD 10 example is:
OP DS MOD REG R/M
mov ebx,[edx+0x100] 8b 9a 00 01 00 00 100010 11 10 011 010 [4 byte off]
mov [edx+0x100],ebx 89 9a 00 01 00 00 100010 01 10 011 010 [4 byte off]
MOD 10 R/M=100 example is:
mov edx,[ebx+ecx*4+0x3e8] 8b 94 8b e8 03 00 00
mov [ebx+ecx*4+0x3e8],edx 89 94 8b e8 03 00 00
| SIB |
DS MOD REG R/M SCALE INDEX BASE OFFSET
100010 11 10 010 100 10 001 011 0xe8 0x03 0x00 0x00
100010 01 10 010 100 10 001 011 0xe8 0x03 0x00 0x00
MOD 00 R/M=000 example:
OP DS MOD REG R/M
mov ebx,[edx] 8b 1a 100010 11 00 011 010
mov [edx],ebx 89 1a 100010 01 00 011 010
MOD 00 R/M=100 example:
| SIB |
OP DS MOD REG R/M SCALE INDEX BASE
mov ebx,[esp]: 8b 1c 24 100010 11 00 011 100 00 100 100
mov [esp],ebx: 89 1c 24 100010 01 00 011 100 00 100 100
| SIB |
OP DS MOD REG R/M SCALE INDEX BASE
mov edx,[ebx+edx] 8b 14 13 100010 11 00 010 100 00 010 011
mov [ebx+edx],edx 89 14 13 100010 01 00 010 100 00 010 011
| SIB |
OP DS MOD REG R/M SCALE INDEX BASE
mov edx,[ebx+ecx*4] 8b 14 8b 100010 11 00 010 100 10 001 011
mov [ebx+ecx*4],edx 89 14 8b 100010 01 00 010 100 10 001 011
MOD 00 R/M=101 example:
mov [dword 0x80],edx 89 15 80 00 00 00
mov [dword 0x1000],edx 89 15 00 10 00 00
OP DS MOD REG R/M
100010 01 00 010 101 0xe8 0x03 0x00 0x00
100010 01 00 010 101 0xe8 0x03 0x00 0x00
Other examples:
// add edx, [esi]: 0x03,0x16
// add dx, [esi]: 0x66,0x03,0x16
// add dl, [esi]: 0x02,0x16
As bits:
prefix dw MD REG R/M
- 0000 0011 00 010 110
0110 0110 0000 0011 00 010 110
- 0000 0010 00 010 110
mov eax, [ebx+0x4000]
mov ebx, 0xf1
mov [0x0004], ebx
mov eax, [0x0004]
lea eax, [ebx+eax*2]
call eax
test ebx, 1
jz skip_led
call led_on
hlt
ret
push ebx
rol eax, cl
shr eax, 5