133 lines
3.0 KiB
C
133 lines
3.0 KiB
C
// assembly.c
|
|
// These are functions useful for translating assembly mnemonics to hex and vice-versa for the SplitBit CPU.
|
|
// Written by Anachronaut
|
|
// 10/18/2024
|
|
|
|
#include "assembly.h"
|
|
#include <string.h>
|
|
|
|
typedef struct {
|
|
uint8_t opcode;
|
|
const char* mnemonic;
|
|
} Instruction;
|
|
|
|
Instruction instruction_set[] = {
|
|
// Arithmetic and Logic Operations:
|
|
{0x00, "ADD"},
|
|
{0x01, "SUB"},
|
|
{0x02, "AND"},
|
|
{0x03, "OR"},
|
|
{0x04, "XOR"},
|
|
{0x05, "NOTA"},
|
|
{0x06, "NOTB"},
|
|
{0x07, "SHL"},
|
|
{0x08, "SHR"},
|
|
// Branch Operations:
|
|
{0x10, "BRI"},
|
|
{0x11, "BRQ"},
|
|
{0x12, "BRA"},
|
|
{0x13, "BRB"},
|
|
{0x14, "BRC"},
|
|
{0x15, "BRD"},
|
|
{0x17, "CALL"},
|
|
{0x18, "SWI"},
|
|
{0x19, "RETI"},
|
|
{0x1F, "RET"},
|
|
// Register Operations:
|
|
{0x20, "RSTA"},
|
|
{0x21, "RSTB"},
|
|
{0x22, "INCA"},
|
|
{0x23, "INCB"},
|
|
{0x24, "DECA"},
|
|
{0x25, "DECB"},
|
|
{0x26, "INIA"},
|
|
{0x27, "INIB"},
|
|
{0x28, "CCF"},
|
|
{0x29, "MVQA"},
|
|
{0x2A, "MVQB"},
|
|
{0x2B, "SIF"},
|
|
{0x2C, "CIF"},
|
|
// Stack Operations:
|
|
{0x30, "PSHQ"},
|
|
{0x31, "PSHA"},
|
|
{0x32, "PSHB"},
|
|
{0x33, "PSHD"},
|
|
{0x34, "POPA"},
|
|
{0x35, "POPB"},
|
|
{0x36, "POPD"},
|
|
// Data Operations:
|
|
{0x40, "INCD"},
|
|
{0x41, "DECD"},
|
|
{0x42, "LDA"},
|
|
{0x43, "LDB"},
|
|
{0x44, "STQ"},
|
|
{0x45, "STA"},
|
|
{0x46, "STB"},
|
|
{0x47, "SETD"},
|
|
{0x48, "DPUP"},
|
|
{0x49, "DPDN"},
|
|
{0x4A, "LDD"},
|
|
{0x4B, "STD"},
|
|
{0x4C, "MVSD"},
|
|
// Output Operations:
|
|
{0xD0, "OUTQ"},
|
|
{0xD1, "OUTA"},
|
|
{0xD2, "OUTB"},
|
|
// Input Operations:
|
|
{0xE0, "INA"},
|
|
{0xE1, "INB"},
|
|
// Special Operations:
|
|
{0xF0, "NOP"},
|
|
{0xFF, "HALT"}
|
|
};
|
|
|
|
int num_instructions = sizeof(instruction_set) / sizeof(Instruction);
|
|
|
|
const char* getMnemonic(uint8_t opcode) {
|
|
for (int i = 0; i < num_instructions; i++) {
|
|
if (instruction_set[i].opcode == opcode) {
|
|
return instruction_set[i].mnemonic;
|
|
}
|
|
}
|
|
return "---";
|
|
}
|
|
|
|
int dataPointerOperands(uint8_t opcode) {
|
|
// How many Data Pointer selector bytes follow this opcode. Most instructions
|
|
// have none. The ones that work through a pointer have one naming which pointer.
|
|
// LDD and STD move a pointer through a pointer, so they name two.
|
|
switch (opcode) {
|
|
case 0x4A: // LDD
|
|
case 0x4B: // STD
|
|
return 2;
|
|
case 0x15: // BRD
|
|
case 0x33: // PSHD
|
|
case 0x36: // POPD
|
|
case 0x40: // INCD
|
|
case 0x41: // DECD
|
|
case 0x42: // LDA
|
|
case 0x43: // LDB
|
|
case 0x44: // STQ
|
|
case 0x45: // STA
|
|
case 0x46: // STB
|
|
case 0x47: // SETD
|
|
case 0x48: // DPUP
|
|
case 0x49: // DPDN
|
|
case 0x4C: // MVSD
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
uint8_t getOpcode(char* mnemonic) {
|
|
for (int i = 0; i < num_instructions; i++) {
|
|
if (strcmp(instruction_set[i].mnemonic, mnemonic) == 0) {
|
|
return instruction_set[i].opcode;
|
|
}
|
|
}
|
|
return 0xFE; // FE is an unused instruction, we'll use it to indicate an error.
|
|
}
|
|
|
|
|