//
// Created by bruno on 2.2.2025.
//

#ifndef RISCB_CORE_H
#define RISCB_CORE_H

#include <stdint.h>
#include "stdio.h"

#define MEM_SIZE 65535
// Register count (register names R0 to R7)
#define REG_COUNT 64
#define STACK_SIZE 255

#define CPU_FLAG_ZERO (1 << 0)
#define CPU_FLAG_NEGATIVE (1 << 1)

#define CPU_MODE_PAUSED (1 << 0)
#define CPU_MODE_HALTED (1 << 1)
#define CPU_MODE_ERROR (1 << 2)
#define CPU_MODE_LOOP (1 << 3)
#define CPU_MODE_STEP (1 << 4)
#define CPU_MODE_SECOND (1 << 5)
#define CPU_MODE_FRAME (1 << 6)

#define CPU_INSTRUCTION_SIZE 8 //Biggest instruction


// CPU state
typedef struct {
    uint8_t regs[REG_COUNT];
    uint8_t memory[MEM_SIZE];
    uint32_t stack[STACK_SIZE]; // Stack pointer
    uint32_t addrToLineMapper[MEM_SIZE / CPU_INSTRUCTION_SIZE];
    uint32_t pc; // Program counter
    uint32_t stack_ptr; // Stack pointer
    uint8_t flags; // Status flags
    uint32_t programEnd;
    uint8_t mode;
    uint32_t cycle;
} CPU;

void step(CPU *cpu);

void init_cpu(CPU *cpu);

// Helper function for setting flags in the CPU (here we assume bit0 is the Zero flag,
// and bit1 is the Negative flag).
static inline void set_flags(CPU *cpu, int32_t result);

// Opcode definitions (should match the CPU’s enum)
typedef enum {
    NOP,        // NOP - No operation (does nothing, advances to next instruction)

    BRK,        // BRK - Pause CPU (halts execution until resumed)

    HLT,

    MOV_IMM_RN,   // MOV Rn, Imm - Move immediate to register (Rn = Imm)
    MOV_RN_RM,    // MOV Rn, Rm - Move value from one register to another (Rn = Rm)
    MOV_RN_ADDR,  // MOV Rn, [Addr] - Load value from memory address into register (Rn = [Addr])
    MOV_ADDR_RN,  // MOV [Addr], Rn - Store register value into memory address ([Addr] = Rn)

    SWAP,         // SWAP Rn, Rm - Swap values between two registers (Rn <-> Rm)

    SWAPN,        // SWAPN Rn - Swap nibbles within a register (Rn high/low nibbles swapped)

    ADD_RN_RM,    // ADD Rn, Rm - Add values of two registers (Rn = Rn + Rm)
    ADD_RN_IMM,   // ADD Rn, Imm - Add immediate value to register (Rn = Rn + Imm)

    SUB_RN_RM,    // SUB Rn, Rm - Subtract one register from another (Rn = Rn - Rm)
    SUB_RN_IMM,   // SUB Rn, Imm - Subtract immediate value from register (Rn = Rn - Imm)

    MUL_RN_RM,    // MUL Rn, Rm - Multiply two registers (Rn = Rn * Rm)
    MUL_RN_IMM,   // MUL Rn, Imm - Multiply register by immediate (Rn = Rn * Imm)

    DIV_RN_RM,    // DIV Rn, Rm - Divide one register by another (Rn = Rn / Rm)
    DIV_RN_IMM,   // DIV Rn, Imm - Divide register by immediate (Rn = Rn / Imm)

    MOD_RN_RM,    // MOD Rn, Rm - Compute remainder of division (Rn = Rn % Rm)
    MOD_RN_IMM,   // MOD Rn, Imm - Compute remainder using immediate (Rn = Rn % Imm)

    NEG_RN,       // NEG Rn - Negate register value (Rn = -Rn)

    AND_RN_RM,    // AND Rn, Rm - Bitwise AND two registers (Rn = Rn & Rm)
    AND_RN_IMM,   // AND Rn, Imm - Bitwise AND register with immediate (Rn = Rn & Imm)

    OR_RN_RM,     // OR Rn, Rm - Bitwise OR two registers (Rn = Rn | Rm)
    OR_RN_IMM,    // OR Rn, Imm - Bitwise OR register with immediate (Rn = Rn | Imm)

    XOR_RN_RM,    // XOR Rn, Rm - Bitwise XOR two registers (Rn = Rn ^ Rm)
    XOR_RN_IMM,   // XOR Rn, Imm - Bitwise XOR register with immediate (Rn = Rn ^ Imm)

    NOT_RN,       // NOT Rn - Bitwise NOT (Rn = ~Rn)

    SHL_RN_IMM,   // SHL Rn, Imm - Logical shift left (Rn = Rn << Imm)

    SHR_RN_IMM,   // SHR Rn, Imm - Logical shift right (Rn = Rn >> Imm)

    SAR_RN_IMM,   // SAR Rn, Imm - Arithmetic shift right (Rn = Rn >> Imm with sign extension)

    JMP,        // JMP Addr - Jump to address (PC = Addr)

    JMP_REL,    // JMP Offset - relative jump (offset is signed)

    INC_RN,        //INC Rn - Increment register
    INC_ADDR,        //INC [Addr] - Increment address

    DEC_RN,        //DEC Rn - Increment register
    DEC_ADDR,        //DEC [Addr] - Increment address

    CMP,        // CMP Rn, Rm - Compare two registers (sets flags based on Rn - Rm)

    JE,         // JE Addr - Jump if equal (if zero flag set, PC = Addr)
    JNE,        // JNE Addr - Jump if not equal (if zero flag not set, PC = Addr)

    BIT_TS_RN,     // jump relative if a given bit in a register is set
    BIT_TS_ADDR,   // jump relative if a given bit in memory is set

    BIT_TC_RN,     // jump relative if a given bit in a register is not set
    BIT_TC_ADDR,   // jump relative if a given bit in memory is not set

    BITS_RN,     // jump relative if a given bit in a register is set
    BITS_ADDR,   // jump relative if a given bit in memory is set

    BITC_RN,     // jump relative if a given bit in a register is not set
    BITC_ADDR,   // jump relative if a given bit in memory is not set

    JG,         // JG Addr - Jump if greater (if greater flag set, PC = Addr)

    JL,         // JL Addr - Jump if less (if less flag set, PC = Addr)

    JGE,        // JGE Addr - Jump if greater or equal (if greater or zero flag set, PC = Addr)

    JLE,        // JLE Addr - Jump if less or equal (if less or zero flag set, PC = Addr)

    CALL,         // CALL Addr - Call subroutine (push PC, flags and registers to stack, PC = Addr)

    RET,        // RET - Return from subroutine (pop PC, flags and registers from stack)
} Opcode;

#endif //RISCB_CORE_H
