4 Commits

Author SHA1 Message Date
1f11242762 CAR: added stack operations, call, ret, jmp and some other random stuff 2026-07-18 19:28:11 -05:00
5902eae153 Update README.md 2026-07-16 14:03:58 -05:00
24f5bff78d Update README.md 2026-07-16 14:00:39 -05:00
ffef8b3743 Update README.md 2026-07-16 13:59:26 -05:00
10 changed files with 332 additions and 10 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
*.o
*.helper
!.gitignore

58
README.md Normal file
View File

@ -0,0 +1,58 @@
# 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:
- few to none.
- 32 general purpose registers
- 32-bit addresses
### Needed features:
- conditional jmps
### Instuctions (arithmetic):
- add
- sub (subtract)
- mul (multiply)
- div (divide)
- mod (modulus, division remainder)
- shl (logical shift left)
- shr (logical shift right)
- ahr (arithmetic shift right)
- ror (logical rotate right)
- and (logical AND)
- or (logical OR)
- 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):
- 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)
- ldi (load register with and immediate value, only low 21 bits)
- str (store register at memory, using the program counter as the base)
- sts (store register at memory, using the stack pointer as the base)
- stb (store register at memory, using the specified register as the base)
- 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):
- nop (do nothing)
- call (call subroutine/function)
- ret (return from subroutine/function)
- jmp (jump to memory address)

88
fpu.c Normal file
View File

@ -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
}

View File

@ -4,10 +4,23 @@
* CAR (Cool ARM Ripoff) copyright (c) 2026 David J Goeke. All rights reserved. * CAR (Cool ARM Ripoff) copyright (c) 2026 David J Goeke. All rights reserved.
* Unauthorized (re)distribution is prohibited. * Unauthorized (re)distribution is prohibited.
*/ */
#include <string.h>
#include <stdio.h> #include <stdio.h>
#include "include/defs.h" #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) 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("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]);

View File

@ -5,7 +5,8 @@
#include <stddef.h> #include <stddef.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdio.h>
//#define _DEBUG //#define _DEBUG
@ -27,6 +28,22 @@ int processor_start(pstate_t* state);
void dump_regs(pstate_t* s); 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_CARRY (1 << 0)
#define FLAG_SIGN (1 << 1) #define FLAG_SIGN (1 << 1)
#define FLAG_OVERFLOW (1 << 2) #define FLAG_OVERFLOW (1 << 2)
@ -117,20 +134,30 @@ typedef struct
static const op_return_t _OP_RETV_NORMAL = { .do_inc_pc = true, .exiting_error = false }; 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_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; #define OP_RETURN_EXITING_ERROR return _OP_RETV_EXITING_ERROR;
typedef op_return_t (*op)(raw_instruction_t*, pstate_t*); typedef op_return_t (*op)(raw_instruction_t*, pstate_t*);
static DEFINE_OP(nop) DEFINE_OP(nop);
{
OP_RETURN_NORMAL DEFINE_OP(call);
} DEFINE_OP(ret);
DEFINE_OP(jmp);
static const op misc_ops[] = static const op misc_ops[] =
{ {
op_nop, op_nop,
op_call,
op_ret,
op_jmp,
op_jb,
}; };
static const size_t NUM_MISC_OPCODES = sizeof(misc_ops) / sizeof(misc_ops[0]); 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(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[] = static const op reg_ops[] =
{ {
op_add, op_add,
@ -171,6 +204,12 @@ static const op reg_ops[] =
op_bnot, op_bnot,
op_xchg, 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]); 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(sti);
DEFINE_OP(stj); 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[] = static const op ls_ops[] =
{ {
op_ldr, op_ldr,
@ -203,6 +249,15 @@ static const op ls_ops[] =
op_sti, op_sti,
op_stj, 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]); static const size_t NUM_LS_OPCODES = sizeof(ls_ops) / sizeof(ls_ops[0]);

View File

@ -160,3 +160,58 @@ DEFINE_OP(stj)
OP_RETURN_NORMAL 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
}

49
misc.c Normal file
View File

@ -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
}

View File

@ -9,8 +9,6 @@
#include "include/defs.h" #include "include/defs.h"
DEFINE_OP(add) DEFINE_OP(add)
{ {
register_instruction_t i = convert_raw2register(ri); register_instruction_t i = convert_raw2register(ri);

4
run
View File

@ -4,8 +4,8 @@
# CAR (Cool ARM Ripoff) copyright (c) 2026 David J Goeke. All rights reserved. # CAR (Cool ARM Ripoff) copyright (c) 2026 David J Goeke. All rights reserved.
# Unauthorized (re)distribution is prohibited. # Unauthorized (re)distribution is prohibited.
FILES="processor.c load_store.c operations.c helpers.c main.c" FILES="processor.c fpu.c misc.c load_store.c operations.c helpers.c main.c"
CFLAGS="" #"-g -O0 -fsanitize=address" CFLAGS="-O2 -Wall" #"-g -O0 -fsanitize=address"
TARGET="vm" TARGET="vm"

BIN
vm Executable file

Binary file not shown.