What's new

Instruction set design for a virtual 16-bit computer

lincore

New member
Hello all, I hope this is not the completely wrong place to talk about ISA design. It's the best I could find.


I'm writing an emulator for a fictional 16-bit computer architecture which I'd like to use as the basis for a game some day. And if I happen to learn a lot on the way, that'd be great, too.
Implementing the emulator itself has been straightforward so far, but coming up with a decent instruction set is far from trivial. I find it difficult to decide which instructions are worth the opcode and which are so rarely used that I can ignore them.
Assuming you guys have a lot more experience with assembly/machine language code than I do, I would greatly appreciate any feedback you have.

My design goals are:
  • Make it look like an early eighties computer (a few quirks and limitations)
  • Keep it accessible for the uninitiated (orthogonal design)
  • When in doubt, keep it simple (more RISC than CISC, but keep it reasonable)
  • Where useful try to support byte, word and dword operands


TX-1000 features:
  • 16-bit address-, data-, and IO-busses
  • lower 32k memory is built in
  • upper 32k can be dynamically mapped to 4 8k ROM/RAM banks (of up to 256, in theory)
  • load/store architecture
  • 15 16-bit registers (implemented as byte[])
  • the lower 8 are general purpose
  • they can be used as 16 8-bit or 4 32-bit registers
  • dedicated accumulator (4*8|2*16|1*32 bit)
  • instructions are 1-3 words long
Instruction format:
Code:
opcode(6) reg_a(5) reg_b(5)         (16 bit)
opcode(6) reg_a(5) address(21)      (32 bit)
opcode(6) reg_a(5) immediate(16|32) (32-48 bit)
+a few instructions use reg_a or reg_b for immediate values/flags

Address modes:
Code:
1) Address[R_BASE(5) %0 unused(10) R_OFFSET(5)]    (register indirect + offset)
2) Address[R_BASE(5) %1 I_OFFSET(15)]              (register indirect + immediate offset)
3) Address[%11111 I_ABS(16)]                       (absolute)

General Purpose Registers:
Code:
     0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15
=====================================================================
 8   |A0 |A1 |B0 |B1 |C0 |C1 |D0 |D1 |E0 |E1 |F0 |F1 |G0 |G1 |H0 |H1 |
16   |-- A --|-- B --|-- C --|-- D --|-- E --|-- F --|-- G --|-- H --|
32   |------ AB -----|------ CD -----|------ EF -----|------ GH -----|
Special Purpose Registers:
Code:
   16   17   18   19   20   21   22   23   24   25   26   27   28   29   
=========================================================================
 8  ACC0 ACC1 ACC2 ACC3 |
16  |- ACCP --|- ACCQ --|-- PC ---|-- SP ---|-- FL ---|
32  |------ ACC --------|
Flag Register (FL):
  • $0-$8: Zero, Negative, Parity, Carry, Half Carry, Overflow, Int Enabled, Supervisor, Debug(step)
  • $9-$F: undefined


Instruction Set
All of the following instructions have three variants for byte, word and dword operands:
  • load, store
  • copy register->register or immediate->register
  • add, add with carry, sub, sub with carry, inc, dec
  • and, or, xor, arithmetic negation,
  • bit-wise left/right shift with 2 mode-bits (arithmetic, logic, rotate, rotate with carry)
  • push, pop
These use 16-bit operands, if any:
  • bit check, bit set
  • io in, out
  • halt
  • conditional branch (1 bit set/clear, 4 bit flag, branch if FL_register&flag set/clear)
  • conditional call (see above)
I have omitted unconditional branch and return instructions because they can be expressed as single load/copy and pop instructions respectively. There's also no mul/div instructions nor anything fp related, which seems reasonable for the time.

What I am not entirely happy with is the way shift instructions are currently handled. Instructions should be simple, so I'd prefer to have dedicated instructions for logical, arithmetic, circular and circular shifts with carry. However, 4 modes times 3 operand sizes times 2 directions equals 24 opcodes I don't have, so I use two bits to determine the mode and pretend that the ALU can do that ;~). I know I could get away with far less. 6502 only four, RISC I only three. Which ones do you think I should drop?

Also, how practical is it really to have most operations support byte, word and dword operands? I like it, but that's probably why I stick to high-level languages. Currently I have four opcodes left, not much for future expansions. I also have not decided how to handle interrupts, but I have 32-bits left for registers.

Attached is the more detailed instruction set draft. if you don't have the time to take a look I understand. If you do, I'd greatly appreciate your feedback. Thanks!
 

Attachments

  • tx1000.pdf
    249.9 KB · Views: 485
Last edited:

Top