diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4350edd --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ + +*.o + +*.helper + +!.gitignore diff --git a/README.md b/README.md index a04cbb7..768febb 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,19 @@ -#Cool ARM Ripoff +# Cool ARM Ripoff *A simple (and ridiculous) virtual machine/CPU made for no real reason/purpose.* ***Written in C, made by David Goeke.*** -###Current features: +### Current features: - few to none. - 32 general purpose registers - 32-bit addresses -###Needed features: -- push/pop -- call/ret -- jmp/conditional jmps -- store high 11 bits of an immediate in memory +### Needed features: +- conditional jmps -###instuctions (arithmetic): +### Instuctions (arithmetic): - add - sub (subtract) - mul (multiply) @@ -31,8 +28,13 @@ - xor (logical XOR) - not (logical NOT/invertion) - xchg (exchange registers) +- fp_add (add floating point values in registers) +- fp_sub (subtract floating point values in registers) +- fp_mul (multiply floating point values in registers) +- fp_div (divide floating point values in registers) +- fp_mod (modulus (division remainder) floating point values in registers) -###Insutrctions (load/store): +### Insutrctions (load/store): - ldr (load register with value from memory, using the program counter as the base) - lds (load register with value from memory, using the stack pointer as the base) - ldb (load register with value from memory, using a specified register as the base) @@ -43,6 +45,14 @@ - ldh (load high 11 bits of register with immediate) - sti (store a 21-bit immediate at memory, using the program counter as the base) - stj (store a 21-bit immediate at memory, using the stack pointer as the base) +- push (push register onto stack) +- pushi (push 21-bit immediate onto stack) +- pop (pop value from stack into register) +- sth (stroe high 11 bits of register to memory, using pc as the base) +- stk (stroe high 11 bits of register to memory, using sp as the base) -###Instructions (misc): +### Instructions (misc): - nop (do nothing) +- call (call subroutine/function) +- ret (return from subroutine/function) +- jmp (jump to memory address) diff --git a/fpu.c b/fpu.c new file mode 100644 index 0000000..e39b67e --- /dev/null +++ b/fpu.c @@ -0,0 +1,88 @@ +/* + * Source file for floating point operations. + * + * CAR (Cool ARM Ripoff) copyright (c) 2026 David J Goeke. All rights reserved. + * Unauthorized (re)distribution is prohibited. +*/ + +// This is mostly just wrappers around the hardware/system FPU. +// We don't do any calculations ourselves. + +#include "include/defs.h" + +DEFINE_OP(fp_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] = (uint32_t) ((float) state->gprs[src1] + (float) state->gprs[src2]); + + OP_RETURN_NORMAL +} + +DEFINE_OP(fp_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] = (uint32_t) ((float) state->gprs[src1] - (float) state->gprs[src2]); + + OP_RETURN_NORMAL +} + +DEFINE_OP(fp_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] = (uint32_t) ((float) state->gprs[src1] * (float) state->gprs[src2]); + + OP_RETURN_NORMAL +} + +DEFINE_OP(fp_div) +{ + register_instruction_t i = convert_raw2register(ri); + + uint8_t dest = i.dest; + uint32_t src1 = state->gprs[i.src1]; + uint32_t src2 = state->gprs[i.src2]; + + if (src1 != 0 && src2 != 0) + { + state->gprs[dest] = (uint32_t) ((float) src1 / (float) src2); + OP_RETURN_NORMAL + } + + state->gprs[dest] = 0xffffffff; + + OP_RETURN_NORMAL +} + +DEFINE_OP(fp_mod) +{ + register_instruction_t i = convert_raw2register(ri); + + uint8_t dest = i.dest; + uint32_t src1 = state->gprs[i.src1]; + uint32_t src2 = state->gprs[i.src2]; + + if (src1 != 0 && src2 != 0) + { + state->gprs[dest] = (uint32_t) ((float) src1 / (float) src2); + OP_RETURN_NORMAL + } + + state->gprs[dest] = 0xffffffff; + + OP_RETURN_NORMAL +} diff --git a/helpers.c b/helpers.c index 1160a2d..c2e7525 100644 --- a/helpers.c +++ b/helpers.c @@ -4,10 +4,23 @@ * CAR (Cool ARM Ripoff) copyright (c) 2026 David J Goeke. All rights reserved. * Unauthorized (re)distribution is prohibited. */ +#include #include #include "include/defs.h" +void _push(uint32_t v, pstate_t* state) +{ + state->sp--; + memcpy(state->memory + state->sp, &v, sizeof(v)); +} + +void _pop(uint32_t* a, pstate_t* state) +{ + memcpy(a, state->memory + state->sp, sizeof(*a)); + state->sp++; +} + 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]); diff --git a/include/defs.h b/include/defs.h index 3b7d8ea..25ce167 100644 --- a/include/defs.h +++ b/include/defs.h @@ -5,7 +5,8 @@ #include #include -#include +#include + //#define _DEBUG @@ -27,6 +28,22 @@ int processor_start(pstate_t* state); void dump_regs(pstate_t* s); +void _push(uint32_t v, pstate_t* state); +void _pop(uint32_t* a, pstate_t* state); + + +// returns !error (true on no error, false on error) { true on safe address, false on invalid address } +static inline bool check_addr(uint32_t addr, pstate_t* state) +{ + if (addr < state->memory_size) + { + return true; + } + + fprintf(stderr, "[ERROR]: memory address out of range: address 0x%x with memory size 0x%x\n", addr, (uint32_t) state->memory_size); + return false; +} + #define FLAG_CARRY (1 << 0) #define FLAG_SIGN (1 << 1) #define FLAG_OVERFLOW (1 << 2) @@ -116,21 +133,31 @@ typedef struct 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 }; +static const op_return_t _OP_RETV_EXITING_ERROR = { .do_inc_pc = true, .exiting_error = true }; +static const op_return_t _OP_RETV_NO_INC_PC = { .do_inc_pc = false, .exiting_error = false }; #define OP_RETURN_NORMAL return _OP_RETV_NORMAL; +#define OP_RETURN_NO_INC_PC return _OP_RETV_NO_INC_PC; #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 -} +DEFINE_OP(nop); + +DEFINE_OP(call); +DEFINE_OP(ret); + +DEFINE_OP(jmp); static const op misc_ops[] = { op_nop, + + op_call, + op_ret, + + op_jmp, + op_jb, }; static const size_t NUM_MISC_OPCODES = sizeof(misc_ops) / sizeof(misc_ops[0]); @@ -152,6 +179,12 @@ DEFINE_OP(bnot); DEFINE_OP(xchg); +DEFINE_OP(fp_add); +DEFINE_OP(fp_sub); +DEFINE_OP(fp_mul); +DEFINE_OP(fp_div); +DEFINE_OP(fp_mod); + static const op reg_ops[] = { op_add, @@ -171,6 +204,12 @@ static const op reg_ops[] = op_bnot, op_xchg, + + op_fp_add, + op_fp_sub, + op_fp_mul, + op_fp_div, + op_fp_mod, }; static const size_t NUM_REG_OPCODES = sizeof(reg_ops) / sizeof(reg_ops[0]); @@ -188,6 +227,13 @@ DEFINE_OP(ldh); DEFINE_OP(sti); DEFINE_OP(stj); +DEFINE_OP(push); +DEFINE_OP(pushi); // push immediate +DEFINE_OP(pop); + +DEFINE_OP(sth); +DEFINE_OP(stk); + static const op ls_ops[] = { op_ldr, @@ -203,6 +249,15 @@ static const op ls_ops[] = op_sti, op_stj, + + op_push, + op_pushi, + op_pop, + + op_sth, // store high 11 bits of immediate at address with base of pc + op_stk, // store high 11 bits of immediate at address with base of sp + + // 1 more }; static const size_t NUM_LS_OPCODES = sizeof(ls_ops) / sizeof(ls_ops[0]); diff --git a/load_store.c b/load_store.c index b0376bf..ed26775 100644 --- a/load_store.c +++ b/load_store.c @@ -160,3 +160,58 @@ DEFINE_OP(stj) OP_RETURN_NORMAL } + +DEFINE_OP(push) +{ + ls_instruction_t i = convert_raw2ls(ri); + + _push(state->gprs[i.reg], state); + + OP_RETURN_NORMAL +} + +DEFINE_OP(pushi) +{ + ls_instruction_t i = convert_raw2ls(ri); + + _push(i.imm, state); + + OP_RETURN_NORMAL +} + +DEFINE_OP(pop) +{ + ls_instruction_t i = convert_raw2ls(ri); + + uint32_t v; + + _pop(&v, state); + + state->gprs[i.reg] = v; + + OP_RETURN_NORMAL +} + +DEFINE_OP(sth) +{ + ls_instruction_t i = convert_raw2ls(ri); + + uint32_t addr = state->pc + state->gprs[i.reg]; + uint32_t v = (i.imm >> 21) & 0x7ff; + + memcpy(state->memory + addr, &v, sizeof(v)); + + OP_RETURN_NORMAL +} + +DEFINE_OP(stk) +{ + ls_instruction_t i = convert_raw2ls(ri); + + uint32_t addr = state->sp + state->gprs[i.reg]; + uint32_t v = (i.imm >> 21) & 0x7ff; + + memcpy(state->memory + addr, &v, sizeof(v)); + + OP_RETURN_NORMAL +} diff --git a/misc.c b/misc.c new file mode 100644 index 0000000..117411f --- /dev/null +++ b/misc.c @@ -0,0 +1,49 @@ +/* + * Source file for miscellaneous 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(nop) +{ + OP_RETURN_NORMAL +} + +DEFINE_OP(call) +{ + misc_instruction_t i = convert_raw2misc(ri); + + _push(state->pc + 1, state); + + uint32_t addr = (uint32_t) (state->pc + (int32_t) i.imm); + + state->pc = addr; + + OP_RETURN_NO_INC_PC +} + +DEFINE_OP(ret) +{ + uint32_t r_addr; + + _pop(&r_addr, state); + + state->pc = r_addr; + + OP_RETURN_NO_INC_PC +} + +DEFINE_OP(jmp) +{ + misc_instruction_t i = convert_raw2misc(ri); + + uint32_t addr = (uint32_t) (state->pc + (int32_t) i.imm); + + state->pc = addr; + + OP_RETURN_NO_INC_PC +} diff --git a/operations.c b/operations.c index 4a3308e..18a5c27 100644 --- a/operations.c +++ b/operations.c @@ -9,8 +9,6 @@ #include "include/defs.h" - - DEFINE_OP(add) { register_instruction_t i = convert_raw2register(ri); diff --git a/run b/run index d6bb6ad..e7b8c9b 100755 --- a/run +++ b/run @@ -4,8 +4,8 @@ # 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" +FILES="processor.c fpu.c misc.c load_store.c operations.c helpers.c main.c" +CFLAGS="-O2 -Wall" #"-g -O0 -fsanitize=address" TARGET="vm" diff --git a/vm b/vm new file mode 100755 index 0000000..2d90529 Binary files /dev/null and b/vm differ