Initial release of CAR
This commit is contained in:
33
helper.py
Normal file
33
helper.py
Normal file
@ -0,0 +1,33 @@
|
||||
try:
|
||||
from colorama import Fore, Style
|
||||
HAS_COLORAMA = True
|
||||
except ImportError:
|
||||
HAS_COLORAMA = False
|
||||
|
||||
if not HAS_COLORAMA:
|
||||
print("Warning: colorama is not installed.")
|
||||
print("Install it with: pip install colorama or sudo apt install python3-colorama or sudo pacman -S python-colorama")
|
||||
exit(-1);
|
||||
|
||||
|
||||
def is_number(s):
|
||||
try:
|
||||
if s.lower().startswith(("0x", "0b", "0o")):
|
||||
int(s, 0)
|
||||
else:
|
||||
int(s)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
REG_OPCODE_MASK = 0x3f800000
|
||||
REG_DEST_MASK = 0x001f0000
|
||||
REG_SRC2_MASK = 0x0000f800
|
||||
REG_SRC1_MASK = 0x000007f0
|
||||
|
||||
LS_OPCODE_MASK = 0x3c000000
|
||||
LS_IMM_MASK = 0x03ffffff
|
||||
|
||||
|
||||
print("this helper function is not ready yet!")
|
||||
58
helpers.c
Normal file
58
helpers.c
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Source file for helper functions.
|
||||
*
|
||||
* CAR (Cool ARM Ripoff) copyright (c) 2026 David J Goeke. All rights reserved.
|
||||
* Unauthorized (re)distribution is prohibited.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
|
||||
#include "include/defs.h"
|
||||
|
||||
void dump_regs(pstate_t* s)
|
||||
{
|
||||
printf("r0: 0x%08x r1: 0x%08x r2: 0x%08x, r3: 0x%08x\n", s->gprs[0], s->gprs[1], s->gprs[2], s->gprs[3]);
|
||||
printf("r4: 0x%08x r5: 0x%08x r6: 0x%08x, r7: 0x%08x\n", s->gprs[4], s->gprs[5], s->gprs[6], s->gprs[7]);
|
||||
printf("r8: 0x%08x r9: 0x%08x r10: 0x%08x, r11: 0x%08x\n", s->gprs[8], s->gprs[9], s->gprs[10], s->gprs[11]);
|
||||
printf("r12: 0x%08x r13: 0x%08x r14: 0x%08x, r15: 0x%08x\n", s->gprs[12], s->gprs[13], s->gprs[14], s->gprs[15]);
|
||||
printf("sp: 0x%04x pc: 0x%04x\n", s->sp, s->pc);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
misc_instruction_t convert_raw2misc(raw_instruction_t* r)
|
||||
{
|
||||
misc_instruction_t i;
|
||||
uint32_t inst = (uint32_t) *r;
|
||||
inst = __builtin_bswap32(inst);
|
||||
i.opcode = (inst >> 23) & 0x3f;
|
||||
i.imm = 0xffffff;
|
||||
return i;
|
||||
}
|
||||
register_instruction_t convert_raw2register(raw_instruction_t* r)
|
||||
{
|
||||
register_instruction_t i;
|
||||
uint32_t inst = (uint32_t) *r;
|
||||
i.opcode = (inst >> 23) & 0x7f;
|
||||
i.dest = (inst >> 18) & 0x1f;
|
||||
i.src1 = (inst >> 13) & 0x1f;
|
||||
i.src2 = (inst >> 8) & 0x1f;
|
||||
|
||||
#ifdef _DEBUG
|
||||
printf("convert_raw2register: opcode: 0b%04b, dest: 0b%05b, src1: 0b%021b, src2: 0b%021b\n", i.opcode, i.dest, i.src1, i.src2);
|
||||
#endif
|
||||
|
||||
return i;
|
||||
}
|
||||
ls_instruction_t convert_raw2ls(raw_instruction_t* r)
|
||||
{
|
||||
ls_instruction_t i;
|
||||
uint32_t inst = (uint32_t) *r;
|
||||
i.opcode = (inst >> 26) & 0xf;
|
||||
i.reg = (inst >> 21) & 0x1f;
|
||||
i.imm = inst & 0x1FFFFF;
|
||||
|
||||
#ifdef _DEBUG
|
||||
printf("convert_raw2ls: opcode: 0b%04b, reg: 0b%05b, imm: 0b%021b\n", i.opcode, i.reg, i.imm);
|
||||
#endif
|
||||
|
||||
return i;
|
||||
}
|
||||
209
include/defs.h
Normal file
209
include/defs.h
Normal file
@ -0,0 +1,209 @@
|
||||
#ifndef _CAR_INCLUDE_DEFS_H
|
||||
#define _CAR_INCLUDE_DEFS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
//#define _DEBUG
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t gprs[32]; // register 31 is the control register (change maybe?)
|
||||
|
||||
uint32_t flags;
|
||||
|
||||
uint32_t pc;
|
||||
uint32_t sp;
|
||||
|
||||
uint32_t* memory;
|
||||
size_t memory_size; // Never let addresses go past this, or we wil get a segsev
|
||||
} pstate_t;
|
||||
|
||||
int processor_start(pstate_t* state);
|
||||
|
||||
void dump_regs(pstate_t* s);
|
||||
|
||||
#define FLAG_CARRY (1 << 0)
|
||||
#define FLAG_SIGN (1 << 1)
|
||||
#define FLAG_OVERFLOW (1 << 2)
|
||||
#define FLAG_ZERO (1 << 3)
|
||||
#define FLAG_INTERRUPTS (1 << 4)
|
||||
#define FLAG_ERROR (1 << 5)
|
||||
#define FLAG_DIRECTION_ERROR (1 << 6)
|
||||
#define FLAG_DIVISION_ERROR (1 << 7)
|
||||
|
||||
typedef uint32_t raw_instruction_t;
|
||||
|
||||
/*
|
||||
Instruction encoding for misc operations:
|
||||
instruction group: bits 0 to 1 (2 bits) {NOTE: for misc operations, should be set to 0b00}
|
||||
opcode: bits 2 to 7 (6 bits)
|
||||
imm: bits 8 to 31 {NOTE: depending on the opcode, could be an address, or a register indirection}
|
||||
|
||||
|
||||
Instruction encoding for register operations:
|
||||
instruction group: bits 0 to 1 (2 bits) {NOTE: for register operations, should be set to 0b01}
|
||||
opcode: bits 2 to 8 (7 bits)
|
||||
dest: bits 9 to 14 (5 bits)
|
||||
src1 bits 15 to 20 (5 bits)
|
||||
src2 bits 21 to 26 (5 bits)
|
||||
all other bits unused, however should be set to zero
|
||||
|
||||
|
||||
Instruction encoding for load/store operations:
|
||||
instruction group: bits 0 to 1 (2 bits) {NOTE: for load/store operations, should be set to 0b10}
|
||||
opcode: bits 2 to 5 (4 bits)
|
||||
register: bits 6 to 10 (5 bits)
|
||||
imm: bits 11 to 31 {NOTE: depending on the opcode, could be an address, or register indirect, or an immediate}
|
||||
is be one of:
|
||||
A) signed 21-bit offset from PC
|
||||
B) signed 21-bit offset from SP
|
||||
C) base register (5 bits) and a 16-bit signed offset from PC
|
||||
D) signed 21 bit immediate
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t opcode;
|
||||
|
||||
uint32_t imm;
|
||||
} misc_instruction_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t opcode;
|
||||
|
||||
uint8_t dest;
|
||||
uint8_t src1;
|
||||
uint8_t src2;
|
||||
} register_instruction_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t opcode;
|
||||
|
||||
uint8_t reg;
|
||||
|
||||
uint32_t imm;
|
||||
} ls_instruction_t;
|
||||
|
||||
|
||||
misc_instruction_t convert_raw2misc(raw_instruction_t* r);
|
||||
register_instruction_t convert_raw2register(raw_instruction_t* r);
|
||||
ls_instruction_t convert_raw2ls(raw_instruction_t* r);
|
||||
|
||||
static inline uint8_t get_instruction_group(raw_instruction_t* r)
|
||||
{
|
||||
return ((uint32_t) (*r)) >> 30;
|
||||
}
|
||||
|
||||
|
||||
#define get_reg_from_imm(x) ((x & 0x1F0000) >> 16)
|
||||
#define get_imm_from_imm(x) (x & ~0x1F0000)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool do_inc_pc;
|
||||
bool exiting_error;
|
||||
} op_return_t;
|
||||
|
||||
#define DEFINE_OP(name) \
|
||||
op_return_t op_##name(raw_instruction_t* ri, \
|
||||
pstate_t* state)
|
||||
|
||||
static const op_return_t _OP_RETV_NORMAL = { .do_inc_pc = true, .exiting_error = false };
|
||||
static const op_return_t _OP_RETV_EXITING_ERROR = { .do_inc_pc = true, .exiting_error = true };
|
||||
|
||||
#define OP_RETURN_NORMAL return _OP_RETV_NORMAL;
|
||||
#define OP_RETURN_EXITING_ERROR return _OP_RETV_EXITING_ERROR;
|
||||
|
||||
typedef op_return_t (*op)(raw_instruction_t*, pstate_t*);
|
||||
|
||||
static DEFINE_OP(nop)
|
||||
{
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
static const op misc_ops[] =
|
||||
{
|
||||
op_nop,
|
||||
};
|
||||
static const size_t NUM_MISC_OPCODES = sizeof(misc_ops) / sizeof(misc_ops[0]);
|
||||
|
||||
DEFINE_OP(add);
|
||||
DEFINE_OP(sub);
|
||||
DEFINE_OP(mul);
|
||||
DEFINE_OP(divide);
|
||||
DEFINE_OP(modulus);
|
||||
|
||||
DEFINE_OP(shl);
|
||||
DEFINE_OP(shr);
|
||||
DEFINE_OP(ahr);
|
||||
DEFINE_OP(ror);
|
||||
|
||||
DEFINE_OP(band);
|
||||
DEFINE_OP(bor);
|
||||
DEFINE_OP(bxor);
|
||||
DEFINE_OP(bnot);
|
||||
|
||||
DEFINE_OP(xchg);
|
||||
|
||||
static const op reg_ops[] =
|
||||
{
|
||||
op_add,
|
||||
op_sub,
|
||||
op_mul,
|
||||
op_divide,
|
||||
op_modulus,
|
||||
|
||||
op_shl,
|
||||
op_shr,
|
||||
op_ahr, // Arithmetic sHift Right
|
||||
op_ror,
|
||||
|
||||
op_band,
|
||||
op_bor,
|
||||
op_bxor,
|
||||
op_bnot,
|
||||
|
||||
op_xchg,
|
||||
};
|
||||
static const size_t NUM_REG_OPCODES = sizeof(reg_ops) / sizeof(reg_ops[0]);
|
||||
|
||||
DEFINE_OP(ldr);
|
||||
DEFINE_OP(lds);
|
||||
DEFINE_OP(ldb);
|
||||
DEFINE_OP(ldi);
|
||||
|
||||
DEFINE_OP(str);
|
||||
DEFINE_OP(sts);
|
||||
DEFINE_OP(stb);
|
||||
|
||||
DEFINE_OP(ldh);
|
||||
|
||||
DEFINE_OP(sti);
|
||||
DEFINE_OP(stj);
|
||||
|
||||
static const op ls_ops[] =
|
||||
{
|
||||
op_ldr,
|
||||
op_lds,
|
||||
op_ldb,
|
||||
op_ldi,
|
||||
|
||||
op_str,
|
||||
op_sts,
|
||||
op_stb,
|
||||
|
||||
op_ldh,
|
||||
|
||||
op_sti,
|
||||
op_stj,
|
||||
};
|
||||
static const size_t NUM_LS_OPCODES = sizeof(ls_ops) / sizeof(ls_ops[0]);
|
||||
|
||||
#endif // _CAR_INCLUDE_DEFS_H
|
||||
69
instruction_format.txt
Normal file
69
instruction_format.txt
Normal file
@ -0,0 +1,69 @@
|
||||
|
||||
|
||||
Instruction encoding for misc operations:
|
||||
instruction group: bits 0 to 1 (2 bits) {NOTE: for misc operations, should be set to 0b00}
|
||||
opcode: bits 2 to 7 (6 bits)
|
||||
imm: bits 8 to 31 {NOTE: depending on the opcode, could be an address, or a register indirection}
|
||||
|
||||
|
||||
Instruction encoding for register operations:
|
||||
instruction group: bits 0 to 1 (2 bits) {NOTE: for register operations, should be set to 0b01}
|
||||
opcode: bits 2 to 8 (7 bits)
|
||||
dest: bits 9 to 13 (5 bits)
|
||||
src1 bits 14 to 18 (5 bits)
|
||||
src2 bits 19 to 23 (5 bits)
|
||||
all other bits unused, however should be set to zero
|
||||
|
||||
|
||||
Instruction encoding for load/store operations:
|
||||
instruction group: bits 0 to 1 (2 bits) {NOTE: for load/store operations, should be set to 0b10}
|
||||
opcode: bits 2 to 5 (4 bits)
|
||||
register: bits 6 to 10 (5 bits)
|
||||
imm: bits 11 to 31 {NOTE: depending on the opcode, could be an address, or register indirect, or an immediate}
|
||||
is be one of:
|
||||
A) signed 21-bit offset from PC
|
||||
B) signed 21-bit offset from SP
|
||||
C) base register (5 bits) and a 16-bit signed offset from PC
|
||||
D) signed 21 bit immediate
|
||||
|
||||
|
||||
The difference between 'arithmetic' and 'logical' shifts/rotates is that in arithmetic shifts/rotates
|
||||
the sign bit is untouched, while logical shifts/rotates don't care about the sign bit.
|
||||
Also, we only need arithmetic shift right because arithmetic shift left is typically the same as a logical shift left.
|
||||
Another instruction we really don't 'need' is a rotate left instrction, because you can do the same thing with a rotate right
|
||||
instruction, and rotate right instructions are more common than rotate left instructions.
|
||||
|
||||
opcodes for register operations (in order of opcode numbers):
|
||||
add; add src1 and src2 and store the result in dest.
|
||||
sub; subtract src1 and src2 and store the result in dest.
|
||||
mul; multiply src1 and src2 and store the result in dest.
|
||||
div; divide src1 and src2 and store the result in dest. If src1 or src2 is zero, dest is untouched and a exception is raised.
|
||||
mod; divide src1 and src2 and store the remainder in dest. If src1 or src2 is zero, dest is untouched and a exception is raised.
|
||||
|
||||
shl; logically shift src1 left by src2 and store the result in dest.
|
||||
shr; logically shift src1 right by src2 and store the result in dest.
|
||||
ahr; arithmetic shift src1 right by src2 and store the result in dest.
|
||||
ror; logically rotate src1 right by src2 and store the result in dest.
|
||||
|
||||
and; AND src1 by src2 and store the result in dest.
|
||||
or; OR src1 by src2 and store the result in dest.
|
||||
xor; XOR src1 by src2 and store the result in dest.
|
||||
not; NOT src1 and store the result in dest, ignore src2.
|
||||
|
||||
|
||||
|
||||
xchg; exchange src1 and src2, ingore dest.
|
||||
|
||||
opcodes for load/store operations: (in order of opcode numbers)
|
||||
ldr; load register from value at PC + imm
|
||||
lds; load register from value at SP + imm
|
||||
ldb; load register from value base_reg + imm
|
||||
ldi; load low 21 bits of register with value imm
|
||||
|
||||
str; store register at address PC + imm
|
||||
sts; store register at address SP + imm
|
||||
stb; store register at address base_reg + imm
|
||||
ldh; load high 11 bits of register with value imm
|
||||
|
||||
sti; store immediate at address PC + register
|
||||
stj; store immediate at address SP + register
|
||||
162
load_store.c
Normal file
162
load_store.c
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Source file for load/store operations.
|
||||
*
|
||||
* CAR (Cool ARM Ripoff) copyright (c) 2026 David J Goeke. All rights reserved.
|
||||
* Unauthorized (re)distribution is prohibited.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "include/defs.h"
|
||||
|
||||
|
||||
DEFINE_OP(ldr)
|
||||
{
|
||||
ls_instruction_t i = convert_raw2ls(ri);
|
||||
|
||||
uint8_t reg = i.reg;
|
||||
|
||||
uint32_t dest;
|
||||
uint32_t addr = state->pc + i.imm;
|
||||
|
||||
//assert(addr < state->memory_size);
|
||||
if (addr >= state->memory_size)
|
||||
{
|
||||
printf("addr >= state->memory_size!\n");
|
||||
//OP_RETURN_EXITING_ERROR
|
||||
}
|
||||
|
||||
printf("loading register %i with value from address 0x%04x, imm: 0x%04x\n", reg, addr, i.imm);
|
||||
|
||||
memcpy(&dest, state->memory + addr, sizeof(dest));
|
||||
|
||||
state->gprs[reg] = dest;
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(lds)
|
||||
{
|
||||
ls_instruction_t i = convert_raw2ls(ri);
|
||||
|
||||
uint8_t reg = i.reg;
|
||||
|
||||
uint32_t dest;
|
||||
uint32_t addr = state->sp + i.imm;
|
||||
|
||||
memcpy(&dest, state->memory + addr, sizeof(dest));
|
||||
|
||||
state->gprs[reg] = dest;
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(ldb)
|
||||
{
|
||||
ls_instruction_t i = convert_raw2ls(ri);
|
||||
|
||||
uint8_t reg = i.reg;
|
||||
|
||||
uint32_t dest;
|
||||
uint32_t base_value = state->gprs[get_reg_from_imm(i.imm)];
|
||||
uint32_t addr = base_value + (uint32_t) get_imm_from_imm(i.imm);
|
||||
|
||||
memcpy(&dest, state->memory + addr, sizeof(dest));
|
||||
|
||||
state->gprs[reg] = dest;
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(ldi)
|
||||
{
|
||||
ls_instruction_t i = convert_raw2ls(ri);
|
||||
|
||||
uint8_t reg = i.reg;
|
||||
|
||||
//uint32_t value = (i.imm) & 0b111111111111111111111; // should convert this to hex
|
||||
uint32_t value = i.imm;
|
||||
|
||||
#ifdef _DEBUG
|
||||
printf("In ldi, reg: 0b%05b, imm: 0x%x\n", reg, value);
|
||||
#endif
|
||||
|
||||
state->gprs[reg] |= value;
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(str)
|
||||
{
|
||||
ls_instruction_t i = convert_raw2ls(ri);
|
||||
|
||||
uint32_t value = state->gprs[i.reg];
|
||||
uint32_t addr = state->pc + i.imm;
|
||||
|
||||
memcpy(state->memory + addr, &value, sizeof(value));
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(sts)
|
||||
{
|
||||
ls_instruction_t i = convert_raw2ls(ri);
|
||||
|
||||
uint32_t value = state->gprs[i.reg];
|
||||
uint32_t addr = state->sp + i.imm;
|
||||
|
||||
memcpy(state->memory + addr, &value, sizeof(value));
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(stb)
|
||||
{
|
||||
ls_instruction_t i = convert_raw2ls(ri);
|
||||
|
||||
uint32_t value = state->gprs[i.reg];
|
||||
uint32_t base_value = state->gprs[get_reg_from_imm(i.imm)];
|
||||
uint32_t addr = base_value + (uint32_t) get_imm_from_imm(i.imm);
|
||||
|
||||
memcpy(state->memory + addr, &value, sizeof(value));
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(ldh)
|
||||
{
|
||||
ls_instruction_t i = convert_raw2ls(ri);
|
||||
|
||||
uint8_t reg = i.reg;
|
||||
|
||||
uint32_t value = (i.imm) & 0b11111111111; // should convert this to hex
|
||||
|
||||
state->gprs[reg] = (state->gprs[reg] & 0x001FFFFF) | ((value & 0x7FF) << 21);
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(sti)
|
||||
{
|
||||
ls_instruction_t i = convert_raw2ls(ri);
|
||||
|
||||
uint32_t addr = state->pc + state->gprs[i.reg];
|
||||
uint32_t value = i.imm;
|
||||
|
||||
memcpy(state->memory + addr, &value, sizeof(value));
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(stj)
|
||||
{
|
||||
ls_instruction_t i = convert_raw2ls(ri);
|
||||
|
||||
uint32_t addr = state->sp + state->gprs[i.reg];
|
||||
uint32_t value = i.imm;
|
||||
|
||||
memcpy(state->memory + addr, &value, sizeof(value));
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
97
main.c
Normal file
97
main.c
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Source file for entry point and initialization.
|
||||
*
|
||||
* CAR (Cool ARM Ripoff) copyright (c) 2026 David J Goeke. All rights reserved.
|
||||
* Unauthorized (re)distribution is prohibited.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "include/defs.h"
|
||||
|
||||
static long get_file_size(FILE *fp)
|
||||
{
|
||||
long current = ftell(fp);
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long size = ftell(fp);
|
||||
|
||||
fseek(fp, current, SEEK_SET);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
printf("CAR (Cool ARM Ripoff) copyright (c) 2026 David J Goeke. All rights reserved.\n\n");
|
||||
|
||||
pstate_t* state = malloc(sizeof(pstate_t));
|
||||
|
||||
if (!state)
|
||||
{
|
||||
printf("Error: malloc failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argc < 1)
|
||||
{
|
||||
printf("Error: no input file specified.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE* f = fopen(argv[1], "rb");
|
||||
|
||||
if (!f)
|
||||
{
|
||||
printf("Error: failed to open file %s.\n", argv[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t* data = malloc(get_file_size(f));
|
||||
|
||||
if (!data)
|
||||
{
|
||||
printf("Error: malloc failed.\n");
|
||||
free(state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t read = fread(data, 1, get_file_size(f), f);
|
||||
|
||||
size_t words = read / sizeof(uint32_t);
|
||||
|
||||
if (read % sizeof(uint32_t) != 0)
|
||||
{
|
||||
fprintf(stderr, "Error: file size (%zu bytes) is not a multiple of 4.\n", read);
|
||||
free(data);
|
||||
free(state);
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
state->memory_size = words; // Just to be safe, use the number of bytes read instead of what get_file_size told us
|
||||
state->memory = (uint32_t*) data;
|
||||
|
||||
state->flags = 0;
|
||||
state->pc = 0; // TODO: we should load ELF files or some other format so we can start somewhere other than address 0 (and so we can have bss)
|
||||
// We could set sp, but let's let software do that
|
||||
|
||||
for (int k = 0; k < words; k++)
|
||||
{
|
||||
state->memory[k] = __builtin_bswap32(state->memory[k]);
|
||||
}
|
||||
|
||||
int j = processor_start(state);
|
||||
|
||||
if (j != 0)
|
||||
{
|
||||
// An error occured, though a message already has been printed, so for now do nothing.
|
||||
}
|
||||
|
||||
free(data); // or would could do free(state->memory), shouldn't matter
|
||||
free(state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
216
operations.c
Normal file
216
operations.c
Normal file
@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Source file for register operations.
|
||||
*
|
||||
* CAR (Cool ARM Ripoff) copyright (c) 2026 David J Goeke. All rights reserved.
|
||||
* Unauthorized (re)distribution is prohibited.
|
||||
*/
|
||||
|
||||
|
||||
#include "include/defs.h"
|
||||
|
||||
|
||||
|
||||
|
||||
DEFINE_OP(add)
|
||||
{
|
||||
register_instruction_t i = convert_raw2register(ri);
|
||||
|
||||
uint8_t dest = i.dest;
|
||||
uint8_t src1 = i.src1;
|
||||
uint8_t src2 = i.src2;
|
||||
|
||||
state->gprs[dest] = state->gprs[src1] + state->gprs[src2];
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(sub)
|
||||
{
|
||||
register_instruction_t i = convert_raw2register(ri);
|
||||
|
||||
uint8_t dest = i.dest;
|
||||
uint8_t src1 = i.src1;
|
||||
uint8_t src2 = i.src2;
|
||||
|
||||
state->gprs[dest] = state->gprs[src1] - state->gprs[src2];
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(mul)
|
||||
{
|
||||
register_instruction_t i = convert_raw2register(ri);
|
||||
|
||||
uint8_t dest = i.dest;
|
||||
uint8_t src1 = i.src1;
|
||||
uint8_t src2 = i.src2;
|
||||
|
||||
state->gprs[dest] = state->gprs[src1] * state->gprs[src2];
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(divide)
|
||||
{
|
||||
register_instruction_t i = convert_raw2register(ri);
|
||||
|
||||
uint8_t dest = i.dest;
|
||||
uint8_t src1 = i.src1;
|
||||
uint8_t src2 = i.src2;
|
||||
|
||||
if (src1 == 0 || src2 == 0)
|
||||
{
|
||||
state->flags |= FLAG_DIVISION_ERROR;
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
state->gprs[dest] = state->gprs[src1] / state->gprs[src2];
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(modulus)
|
||||
{
|
||||
register_instruction_t i = convert_raw2register(ri);
|
||||
|
||||
uint8_t dest = i.dest;
|
||||
uint8_t src1 = i.src1;
|
||||
uint8_t src2 = i.src2;
|
||||
|
||||
if (src1 == 0 || src2 == 0)
|
||||
{
|
||||
state->flags |= FLAG_DIVISION_ERROR;
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
state->gprs[dest] = state->gprs[src1] % state->gprs[src2];
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(shl)
|
||||
{
|
||||
register_instruction_t i = convert_raw2register(ri);
|
||||
|
||||
uint8_t dest = i.dest;
|
||||
uint8_t src1 = i.src1;
|
||||
uint8_t src2 = i.src2;
|
||||
|
||||
state->gprs[dest] = state->gprs[src1] << state->gprs[src2];
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(shr)
|
||||
{
|
||||
register_instruction_t i = convert_raw2register(ri);
|
||||
|
||||
uint8_t dest = i.dest;
|
||||
uint8_t src1 = i.src1;
|
||||
uint8_t src2 = i.src2;
|
||||
|
||||
state->gprs[dest] = state->gprs[src1] >> state->gprs[src2];
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(ahr)
|
||||
{
|
||||
register_instruction_t i = convert_raw2register(ri);
|
||||
|
||||
uint8_t dest = i.dest;
|
||||
uint8_t src1 = i.src1;
|
||||
uint8_t src2 = i.src2;
|
||||
|
||||
state->gprs[dest] = (uint32_t) ((int32_t) state->gprs[src1]) >> state->gprs[src2];
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
static uint32_t rotr32(uint32_t x, uint32_t n)
|
||||
{
|
||||
n &= 31; // Ensure 0 <= n < 32
|
||||
return (x >> n) | (x << ((32 - n) & 31));
|
||||
}
|
||||
|
||||
DEFINE_OP(ror)
|
||||
{
|
||||
register_instruction_t i = convert_raw2register(ri);
|
||||
|
||||
uint8_t dest = i.dest;
|
||||
uint8_t src1 = i.src1;
|
||||
uint8_t src2 = i.src2;
|
||||
|
||||
state->gprs[dest] = rotr32(state->gprs[src1], state->gprs[src2]);
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(band)
|
||||
{
|
||||
register_instruction_t i = convert_raw2register(ri);
|
||||
|
||||
uint8_t dest = i.dest;
|
||||
uint8_t src1 = i.src1;
|
||||
uint8_t src2 = i.src2;
|
||||
|
||||
state->gprs[dest] = state->gprs[src1] & state->gprs[src2];
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(bor)
|
||||
{
|
||||
register_instruction_t i = convert_raw2register(ri);
|
||||
|
||||
uint8_t dest = i.dest;
|
||||
uint8_t src1 = i.src1;
|
||||
uint8_t src2 = i.src2;
|
||||
|
||||
state->gprs[dest] = state->gprs[src1] | state->gprs[src2];
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(bxor)
|
||||
{
|
||||
register_instruction_t i = convert_raw2register(ri);
|
||||
|
||||
uint8_t dest = i.dest;
|
||||
uint8_t src1 = i.src1;
|
||||
uint8_t src2 = i.src2;
|
||||
|
||||
state->gprs[dest] = state->gprs[src1] ^ state->gprs[src2];
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(bnot)
|
||||
{
|
||||
register_instruction_t i = convert_raw2register(ri);
|
||||
|
||||
uint8_t dest = i.dest;
|
||||
uint8_t src1 = i.src1;
|
||||
|
||||
state->gprs[dest] = ~(state->gprs[src1]);
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
|
||||
DEFINE_OP(xchg)
|
||||
{
|
||||
register_instruction_t i = convert_raw2register(ri);
|
||||
|
||||
uint8_t src1 = i.src1;
|
||||
uint8_t src2 = i.src2;
|
||||
|
||||
// Truthfully we could use XORs here, but it's not really faster, so there is no real point,
|
||||
// along with the fact that if both values are the same the result is zeroed out.
|
||||
|
||||
uint8_t temp = src1;
|
||||
|
||||
state->gprs[src1] = state->gprs[src2];
|
||||
state->gprs[src2] = state->gprs[temp];
|
||||
|
||||
OP_RETURN_NORMAL
|
||||
}
|
||||
67
processor.c
Normal file
67
processor.c
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Source file for core processor functions.
|
||||
*
|
||||
* CAR (Cool ARM Ripoff) copyright (c) 2026 David J Goeke. All rights reserved.
|
||||
* Unauthorized (re)distribution is prohibited.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "include/defs.h"
|
||||
|
||||
op_return_t dispatch(raw_instruction_t* r, pstate_t* state)
|
||||
{
|
||||
uint8_t instruction_group = get_instruction_group(r);
|
||||
|
||||
uint8_t opcode;
|
||||
|
||||
switch (instruction_group)
|
||||
{
|
||||
case 0:
|
||||
opcode = convert_raw2misc(r).opcode;
|
||||
//assert(opcode < NUM_MISC_OPCODES);
|
||||
return misc_ops[opcode](r, state);
|
||||
case 1:
|
||||
opcode = convert_raw2register(r).opcode;
|
||||
return reg_ops[opcode](r, state);
|
||||
case 2:
|
||||
opcode = convert_raw2ls(r).opcode;
|
||||
return ls_ops[opcode](r, state);
|
||||
default:
|
||||
printf("\n[ERROR] instruction group is invalid\n");
|
||||
OP_RETURN_EXITING_ERROR
|
||||
}
|
||||
|
||||
OP_RETURN_EXITING_ERROR
|
||||
}
|
||||
|
||||
int processor_start(pstate_t* state)
|
||||
{
|
||||
while (state->pc < state->memory_size)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
printf("state->memory[pc] -> 0x%x, 0b%032b\n", state->memory[state->pc], state->memory[state->pc]);
|
||||
#endif
|
||||
|
||||
op_return_t rv = dispatch((raw_instruction_t*) (state->memory + state->pc), state);
|
||||
|
||||
if (rv.do_inc_pc)
|
||||
{
|
||||
state->pc++;
|
||||
}
|
||||
else if (rv.exiting_error)
|
||||
{
|
||||
printf("An error was occured\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dump_regs(state);
|
||||
|
||||
usleep(300000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
program.bin
Normal file
BIN
program.bin
Normal file
Binary file not shown.
12
program.txt
Normal file
12
program.txt
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
What program.bin does is this:
|
||||
|
||||
; Move the value 8 into r0 and r1
|
||||
mov r0, #0x8
|
||||
mov r1, #0x8
|
||||
|
||||
; multiply r0 by r1 and store the result in r2
|
||||
mul r2, r0, r1
|
||||
|
||||
; do nothing
|
||||
nop
|
||||
19
run
Executable file
19
run
Executable file
@ -0,0 +1,19 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Run script.
|
||||
# CAR (Cool ARM Ripoff) copyright (c) 2026 David J Goeke. All rights reserved.
|
||||
# Unauthorized (re)distribution is prohibited.
|
||||
|
||||
FILES="processor.c load_store.c operations.c helpers.c main.c"
|
||||
CFLAGS="" #"-g -O0 -fsanitize=address"
|
||||
TARGET="vm"
|
||||
|
||||
|
||||
gcc $CFLAGS $FILES -o $TARGET
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo
|
||||
./$TARGET $@
|
||||
else
|
||||
echo "Error: gcc failed with error code" $?
|
||||
fi
|
||||
Reference in New Issue
Block a user