commodorejohn
Veteran Member
This might sorta fit better under "Vintage Computer Programming," but it concerns a machine that A. doesn't exist yet and B. isn't technically vintage, so I'll just throw it here instead.
Anyway, this was inspired by a comment Chuck made a while back about the lack of a good starter system for people to learn machine-language programming on - in that most 8-bit CPU architectures require too many steps to do anything complex, and most modern 32-bit microcontrollers require too much busywork and boilerplate just to get things up and running. I had made the offhand suggestion that someone should look at doing a sort of retro design in an FPGA or something based on a simple yet comfortable 16-bit architecture like the PDP-11.
Well, I don't have any knowledge of FPGA development, but what I do have is a Parallax Propeller chip sitting around not doing anything at the moment. So I've begun to brainstorm ideas for developing such a system to be emulated on the Propeller. Specifics are sketchy at the moment, but I've started off with trying to work out a nice, hobbyist-friendly CPU architecture that looks like it could potentially be emulated in a single Propeller cog (since I want the entire 32KB of hub RAM for the emulated machine's address space.) Ideally it should achieve something in the neighborhood of 150-300 KIPS - comparable to a 1MHz 6502, but capable of doing a good deal more per instruction. As suggested, it's heavily derived from the PDP-11, though it's not actually binary-compatible. Before I get too much into the grunt work, I thought it might be nice to get some comments on the proposed architecture and a few little details I'm still trying to make up my mind on.
- - -
So the CPU is a 16-bit design with eight main registers and eight addressing modes. Six of the registers are purely general-purpose, but R6 is the system stack pointer and R7 is the instruction pointer. This is all lifted directly from the PDP-11, as are the addressing modes (register, register indirect pre-decrement, register indirect post-increment, and register indirect indexed, optionally with an additional level of indirection.) The instruction set is very similar, although I've stripped out the more advanced functionality like memory mapping/protection and the like, as well as floating-point math (not gonna fit that in and still run in a single cog - although it might be possible to add a separate "FPU" in another cog.) I've also tweaked things a bit so that everything fits neatly into octal-based instruction formats (the PDP-11 was mostly this way, but broke its own rule with 8-bit branch offsets.) The instruction set, at present, is as follows:
So I'm looking for feedback - how does this look as an instruction set? Anything obvious missing? Anything here seem unnecessary? And I'm specifically looking for input on a couple little implementation details:
Curious to get some feedback on this!
Anyway, this was inspired by a comment Chuck made a while back about the lack of a good starter system for people to learn machine-language programming on - in that most 8-bit CPU architectures require too many steps to do anything complex, and most modern 32-bit microcontrollers require too much busywork and boilerplate just to get things up and running. I had made the offhand suggestion that someone should look at doing a sort of retro design in an FPGA or something based on a simple yet comfortable 16-bit architecture like the PDP-11.
Well, I don't have any knowledge of FPGA development, but what I do have is a Parallax Propeller chip sitting around not doing anything at the moment. So I've begun to brainstorm ideas for developing such a system to be emulated on the Propeller. Specifics are sketchy at the moment, but I've started off with trying to work out a nice, hobbyist-friendly CPU architecture that looks like it could potentially be emulated in a single Propeller cog (since I want the entire 32KB of hub RAM for the emulated machine's address space.) Ideally it should achieve something in the neighborhood of 150-300 KIPS - comparable to a 1MHz 6502, but capable of doing a good deal more per instruction. As suggested, it's heavily derived from the PDP-11, though it's not actually binary-compatible. Before I get too much into the grunt work, I thought it might be nice to get some comments on the proposed architecture and a few little details I'm still trying to make up my mind on.
- - -
So the CPU is a 16-bit design with eight main registers and eight addressing modes. Six of the registers are purely general-purpose, but R6 is the system stack pointer and R7 is the instruction pointer. This is all lifted directly from the PDP-11, as are the addressing modes (register, register indirect pre-decrement, register indirect post-increment, and register indirect indexed, optionally with an additional level of indirection.) The instruction set is very similar, although I've stripped out the more advanced functionality like memory mapping/protection and the like, as well as floating-point math (not gonna fit that in and still run in a single cog - although it might be possible to add a separate "FPU" in another cog.) I've also tweaked things a bit so that everything fits neatly into octal-based instruction formats (the PDP-11 was mostly this way, but broke its own rule with 8-bit branch offsets.) The instruction set, at present, is as follows:
Code:
Legend: m = addressing mode, d = destination register, s = source register, r = other register, o = branch offset
Opcodes are presented in octal.
[u]Two-address group B:[/u]
[i]Opcode Mnem. Description[/i]
17mdms MOVB Move byte source to destination
16mdms CMPB Compare byte source to destination (subtract, no result)
15mdms SBCB Subtract byte source from destination with borrow
14mdms ADCB Add byte source to destination with carry
13mdms SUBB Subtract byte source from destination
12mdms ADDB Add byte source to destination
11mdms TSTB Test byte source against destination (AND, no result)
[u]Branch group:[/u]
[i]Opcode Mnem. Description[/i]
107ooo BCC Branch if carry clear
106ooo BCS Branch if carry set
105ooo BMI Branch if negative
104ooo BPL Branch if positive
103ooo BNE Branch if not equal to zero
102ooo BEQ Branch if equal to zero
101ooo BRA Branch always
100roo DBZ Decrement Rr and branch until zero
[u]Two-address group A:[/u]
[i]Opcode Mnem. Description[/i]
07mdms MOV Move source to destination
06mdms CMP Compare source to destination (subtract, no result)
05mdms SBC Subtract source from destination with borrow
04mdms ADC Add source to destination with carry
03mdms SUB Subtract source from destination
02mdms ADD Add source to destination
01mdms TST Test source against destination (AND, no result)
[u]Address-and-register group:[/u]
[i]Opcode Mnem. Description[/i]
007rms ASH Arithmetic shift Rr by source
006rms ROT Rotate Rr by source
005rms DIV Divide Rr by source
004rms MUL Multiply Rr by source
003rms XOR XOR Rr with source
002rms OR OR Rr with source
001rms AND AND Rr with source
[u]One-address group:[/u]
[i]Opcode Mnem. Description[/i]
0007ms DECB Decrement byte in source
0006ms INCB Increment byte in source
0005ms DEC Decrement source
0004ms INC Increment source
0003ms JSR Jump to subroutine
0002ms JMP Jump to source
0001ms NOT Ones-complement source
[u]Register group:[/u]
[i]Opcode Mnem. Description[/i]
00007r DUB Duplicate bye in Rr to MSB
00006r SXB Sign-extend byte in Rr
00005r RCR Rotate Rr right with carry
00004r RCL Rotate Rr left with carry
00003r LSR Logical shift Rr right
00002r LSL Logical shift Rr left
00001r ASR Arithmetic shift Rr right
[u]Parameterless group:[/u]
[i]Opcode Mnem. Description[/i]
000007 SEC Set carry flag
000006 CLC Clear carry flag
000005 PLS Pull status register from stack
000004 PHS Push status register to stack
000003 RTI Return from interrupt
000002 WAI Wait for external interrupt
000001 RST Reset system (CPU & peripherals)
000000 BRK Break (software interrupt)
So I'm looking for feedback - how does this look as an instruction set? Anything obvious missing? Anything here seem unnecessary? And I'm specifically looking for input on a couple little implementation details:
- Should byte moves to a register zero-extend the value, or leave the MSB of the destination untouched? What about byte arithmetic operations?
- I want to make the order of operations in instruction decoding explicit, so that there's no ambiguity about, say, MOV R4, R4+ as there was across different PDP-11 models. I'm looking at a scenario where the source operand is decoded first, and both pre-decrement and post-increment write back to the register at decode time - so in the hypothetical most-complicated scenario of, say, a loop of ADDB R0+, R0+ the effect would be to add, say, address 0 to address 1, then address 2 to address 3, then address 4 to address 5, etc. Does this seem like an intuitive way to do things?
- General policy is for word operations on odd addresses to silently AND out the low bit, dropping back to the next-lowest word-aligned address. This seems potentially unwise with the instruction pointer, though - should instruction fetches from odd addresses do a proper trap? What about the stack pointer?
Curious to get some feedback on this!
Last edited: