BMOW 1

From Esolang
Jump to navigation Jump to search

BMOW 1 (standing for Big Mess o’ Wires 1) is a computer with custom CPU made of discrete logic chips, mostly 7400-series ones, wire-wrap and a microcode ROM. It is the first (but not only) custom CPU hardware that BMOW created. BMOW started to design the computer in 2007, started to build the hardware in 2008, and the hardware was complete in 2009.

CPU

BMOW 1 is an 1980s style CISC 8-bit CPU. It uses 24-bit memory addresses to individual bytes, thus in theory could address 16 megabytes of memory. In this address space, 512 kilobytes of ROM and 512 kilobytes of RAM plus some IO devices are mapped.

Instructions have an 8-bit opcode and sometimes an 8-bit or 16-bit operand. The CPU has four 8-bit registers for arithmetic (A, X, Y). There are also two 24-bit registers: the program counter (PC), the stack pointer (SP). There is an 8-byte bank register (ARbank) that is used as the highest byte of all memory access that does not use PC or SP, except for a few that use a long (three-byte) address. In such cases, the base address can be a 16-bit address operand, 8-bit address operand for zero-page address, or 16-bit indirect. The indirect address itself can be read using a 16-bit address operand or from the zero page using an 8-bit address operand, or from the stack with an 8-bit offset from SP. Many instructions use an 8-bit immediate or the X or Y register as an index: adding such an index carries to the middle byte but not to the highest (bank) byte, except that zero-page addressing doesn't even carry to the middle byte. Branch instructions that use an 8-bit relative code address also don't carry to the highest byte. The highest (bank) byte is thus never specified an address calculations, it's just taken from one of the three bank registers as is. There are instructions that add X or Y as an index to the 16-bit indirect address read from memory.

There are three arithmetic flags: Carry, Overflow, Zero, Negative. Zero and Negative are updated during most arithmetic instructions, including increment and decrement instructions, shift, bitwise, compare instructions. Carry and Overflow are updated during add and subtract instructions, but not during increment or decrement instructions. For subtract instructions, the Carry flag stores the borrow like in x86. Carry is also updated from compare, shift and rotate instructions. The bit test instructions take the bitwise and of two values, do not store it, but update the Zero and Negative flags from it, as well update the Overflow flag from the second highest bit of the result. A few instructions treat the flags as a single one-byte Flags register whose bits from lowest to highest are Overflow Zero Negative Carry 0 0 0 0.

Instructions


00: software interrupt
01:
02:
03: A |= mem8[SP + imm8]
04:
05: A |= mem8[imm8]
06: mem8[imm8] <<= 1
07: A = A + X + Carry
08: mem8[SP--] = Flags
09: A |= imm8
0A: A <<= 1
0B: X = SP>>16; Y = SP>>8; A = SP // move stack pointer to XYA treated as a single three-byte register
0C:
0D: A |= mem8[imm16]
0E: mem8[imm16] <<= 1
0F:
10: if (!Carry) PC += signed_imm8
11:
12:
13:
14:
15:
16: mem8[imm8 + X & 0xFF] <<= 1
17: A = A + Y + Carry
18: Carry = 0
19:
1A: A++
1B: SP = (X<<16) + (Y<<8) + A // move XYA treated as single three-byte register into stack pointer
1C:
1D: A |= mem8[imm16 + X]
1E: mem8[imm16 + X] <<= 1
1F:
20: mem16[SP] = PC; SP -= 2; PC = imm16 // subroutine call, pushes the PC that points in the middle of this instruction after the opcode but before the code address opernad
21:
22: mem24[SP] = PC24; SP -= 2; PC = imm24 // subroutine call, pushes the full three-byte PC that points in the middle of this instruction after the opcode but before the code address opernad
23: A &= mem8[SP + imm8]
24: A & mem8[imm8] // bit test and update flags
25: A &= mem8[imm8]
26: Addr = imm8; mem8[Addr] = (mem8[Addr] << 1) + Carry // rotate left through carry
27: A = A - X - Carry
28: Flags = mem8[++SP]
29: A &= imm8
2A: A = (A << 1) + Carry // rotate left through carry
2B:
2C: A & mem8[imm16] // bit test and update flags
2D: A &= mem8[imm16]
2E: Addr = imm16; mem8[Addr] = (mem8[Addr] << 1) + Carry // rotate left through carry
2F:
30: if (Negative) PC += signed_imm8
31:
32:
33:
34:
35:
36:
37: A = A - Y - Carry
38: Carry = 1
39:
3A: A--
3B:
3C:
3D: A &= mem8[imm16 + X]
3E: Addr = imm16 + X; mem8[Addr] = (mem8[Addr] << 1) + Carry // rotate left through carry
3F:
40: return from interrupt
41: A ^= mem8[mem16[imm8] + X]
42:
43: A ^= mem8[SP + imm8]
44:
45: A ^= mem8[imm8]
46: mem8[imm8] >>= 1
47:
48: mem8[SP--] = A
49: A ^= imm8
4A: A >>= 1 // unsigned right shift
4B:
4C: PC = imm16
4D: A ^= mem8[imm16]
4E: mem8[imm16] >>= 1
4F:
50: if (!Overflow) PC += signed_imm8
51: A ^= mem8[mem16[imm8] + Y]
52:
53:
54:
55:
56: mem8[imm8 + X & 0xFF] >>= 1
57:
58: enable interrupts
59:
5A: mem8[SP--] = Y
5B:
5C: PC = imm24
5D: A ^= mem8[imm16 + X]
5E: mem8[imm16 + X] >>= 1
5F:
60: PC = mem16[++SP]; SP++ // return from subroutine
61:
62:
63: A = A + mem16[SP + imm8] + Carry
64:
65: A = A + mem16[imm8] + Carry
66: Addr = imm8; mem8[Addr] = (mem8[Addr] >> 1) + Carry // rotate right through carry
67:
68: A = mem8[++SP]
69: A = A + imm8 + Carry
6A: A = (A >> 1) + Carry // rotate right through carry
6B: PC24 = mem24[++SP]; SP+=2 // long return from subroutine
6C: PC = mem16[imm16]
6D: A = A + mem16[imm16] + Carry
6E: Addr = imm16; mem8[Addr] = (mem8[Addr] >> 1) + Carry // rotate right through carry
6F:
70: if (Overflow) PC += signed_imm8
71:
72:
73:
74:
75:
76: Addr = imm8 + X & 0xFF; mem8[Addr] = (mem8[Addr] >> 1) + Carry // rotate right through carry
77:
78: disable interrupts
79: A = A + mem16[imm16 + Y] + Carry
7A: Y = mem8[++SP]
7B:
7C:
7D: A = A + mem16[imm16 + X] + Carry
7E: Addr = imm16 + X; mem8[Addr] = (mem8[Addr] >> 1) + Carry // rotate right through carry
7F:
80: PC += signed_imm8
81: mem8[mem16[imm8] + X & 0xFF] = A
82:
83: mem8[SP + imm8] + A
84: mem8[imm8] = Y
85: mem8[imm8] = A
86: mem8[imm8] = X
87:
88: Y--
89:
8A: A = X
8B: mem8[SP--] = ARbank
8C: mem8[imm16] = Y
8D: mem8[imm16] = A
8E: mem8[imm16] = X
8F: mem8[imm24] = A // long absolute address operand
90: if (Carry) PC += signed_imm8
91: mem8[mem16[imm16] + Y] = A
92: mem8[mem16[imm16] + X] = A
93:
94: mem8[imm8 + X & 0xFF] = Y
95: mem8[imm8 + X & 0xFF] = A
96:
97:
98: A = Y
99: mem8[imm16 + Y] = A
9A: SP = (SP & 0xFFFF00) + A // move X to low byte of stack pointer
9B:
9C: mem8[imm16] = 0
9D: mem8[imm16 + X] = A
9E: mem8[imm16 + X] = 0
9F:
A0: Y = imm8
A1: A = mem8[mem16[imm8] + X]
A2: X = imm8
A3: A = mem8[SP + X]
A4: Y = mem8[imm8]
A5: A = mem8[imm8]
A6: X = mem8[imm8]
A7:
A8: Y = A
A9: A = imm8
AA: X = A
AB: ARbank = mem[++SP]
AC: Y = mem8[imm16]
AD: A = mem8[imm16]
AE: X = mem8[imm16]
AF: A = mem8[imm24] // long absolute address operand
B0: if (Carry) PC += signed_imm8
B1: A = mem8[mem16[imm16] + Y]
B2: A = mem8[mem16[imm16] + X]
B3: AR = SP + imm8
B4: Y = mem8[imm8 + X & 0xFF]
B5: A = mem8[imm8 + X & 0xFF]
B6:
B7:
B8: Overflow = 0
B9: A = mem8[imm16 + Y]
BA: X = SP // move low byte of stack pointer to X
BB:
BC: Y = mem8[mem16[imm16] + X]
BD: A = mem8[imm16 + X]
BE: X = mem8[imm16 + Y]
BF:
C0: Y - imm8 // compare to update flags
C1:
C2: A = mem8[SP++] // does not align with normal stack operations
C3: A - mem8[SP + imm8] // compare to update flags
C4: Y - mem8[imm8] // compare to update flags
C5: A - mem8[imm8] // compare to update flags
C6: mem8[imm8]--
C7:
C8: Y++
C9: A - imm8 // compare to update flags
CA: X--
CB: Y = mem8[mem16[imm16] + X]
CC: Y - mem8[imm16] // compare to update flags
CD: A - mem8[imm16] // compare to update flags
CE: mem8[imm16]--
CF:
D0: if (!Zero) PC += signed_imm8
D1: A - mem8[mem16[imm8] + Y] // compare to update flags
D2:
D3:
D4:
D5:
D6:
D7:
D8:
D9: A - mem8[imm16 + Y] // compare to update flags
DA: mem8[SP--] = X
DB: mem8[mem16[imm16] + X] = Y
DC:
DD: A - mem8[imm16 + X] // compare to update flags
DE: mem8[imm16 + X]--
DF:
E0: X - imm8 // compare to update flags
E1:
E2: mem8[SP++] = A // does not align with normal stack operations
E3: A = A - mem16[SP + imm8] - Carry
E4: X - mem8[imm8] // compare to update flags
E5: A = A - mem16[imm8] - Carry
E6: mem8[imm8]++
E7:
E8: X++
E9: A = A - imm8 - Carry
EA:
EB: ARbank = imm
EC: mem8[imm16] - X // compare to update flags
ED: A = A - mem16[imm16] - Carry
EE: mem8[imm16]++
EF: swap(X, Flags)
F0: if (Zero) PC += signed_imm8
F1: A = A - mem16[mem16[imm8] + Y] - Carry
F2:
F3:
F4:
F5: A = A - mem16[imm8 + X & 0xFF] - Carry
F6: mem8[imm8 + X & 0xFF]++
F7:
F8:
F9: A = A - mem16[imm16 + Y] - Carry
FA: X = mem8[++SP]
FB: ARbank = A
FC:
FD: A = A - mem16[imm16 + X] - Carry
FE: mem8[imm16 + X]++
FF: halt

Software

BMOW 1 has custom software including pre-emptive multitasking, an integer BASIC interpreter and editor,

Peripherials

Main video output is a custom graphics card that outputs VGA and can show either bitmap graphics or 8×16 pixel character cells, at 512×480 pixels black and white or 180×240 pixels with a 256 color palette. There's a three-voice sound generator for audio output. There's also debug output on a small character LCD, PS/2 keyboard input, and a commercial USB controller to communicate with a PC.

External links

BMOW 1 webpage