type registers = {
  mutable a : int;
  mutable x : int;
  mutable l : int;
  mutable b : int;
  mutable s : int;
  mutable t : int;
  mutable f : float;
  mutable pc : int;
  mutable sw : int
}
(*tip state predstavlja stanje SIC/XE, z pomnilnikom in registri*)
type state = {
  regs : registers;
  memory : Bytes.t
}

(*beri 1 byte in povisaj PC*)
let fetch (state : state) : char = 
  let byte = Bytes.get state.memory state.regs.pc in
  state.regs.pc <- state.regs.pc + 1;
  byte
  
 (*beri 1 byte za offset in ne povecaj PC*) 
let readMem (state : state) (offset : int) : char = 
  Bytes.get state.memory (state.regs.pc + offset)


(*beri 3 byte iz address*)  
let readMemAddr (state : state) (address : int) : int = 
  let byte1 = Char.code (Bytes.get state.memory address) in
  let byte2 = Char.code (Bytes.get state.memory (address + 1)) in
  let byte3 = Char.code (Bytes.get state.memory (address + 2)) in
  let value = (byte1 lsl 16) lor (byte2 lsl 8) lor byte3 in
  value


(*napiši 3 byte inta na address*)
let writeMemAddr (state : state) (address : int) (value : int) : unit =
  (* Mask and shift each byte *)
  let byte1 = Char.chr ((value lsr 16) land 0xFF) in
  let byte2 = Char.chr ((value lsr 8)  land 0xFF) in
  let byte3 = Char.chr (value land 0xFF) in

  (* Write bytes into memory *)
  Bytes.set state.memory address     byte1;
  Bytes.set state.memory (address+1) byte2;
  Bytes.set state.memory (address+2) byte3

(*popoč za pisanje instrukcij*)
let write_instruction3 (state : state) (address : int) (byte1 : char) (byte2 : char) (byte3 : char) : unit = 
  Bytes.set state.memory address byte1;
  Bytes.set state.memory (address+1) byte2;
  Bytes.set state.memory (address+2) byte3

(*povečaj pc za i*)
let pcIncrement (state : state) (i : int) : unit =
  state.regs.pc <- state.regs.pc + i;
  ()

(*beri register glede na reg_code*)

let read_register_int (state: state) (reg_code : int) : int =
  match reg_code with
  | 0 -> state.regs.a
  | 1 -> state.regs.x
  | 2 -> state.regs.l
  | 3 -> state.regs.b
  | 4 -> state.regs.s
  | 5 -> state.regs.t
  | 6 -> int_of_float state.regs.f
  | 8 -> state.regs.pc
  | 9 -> state.regs.sw
  | _ -> failwith "Invalid register code"

let write_register_int (state : state) (reg_code : int) (value : int) : unit=
  match reg_code with
  | 0 -> state.regs.a <- value
  | 1 -> state.regs.x <- value
  | 2 -> state.regs.l <- value
  | 3 -> state.regs.b <- value
  | 4 -> state.regs.s <- value
  | 5 -> state.regs.t <- value
  | 6 -> state.regs.f <- float_of_int value
  | 8 -> state.regs.pc <- value
  | 9 -> state.regs.sw <- value
  | _ -> failwith "Invalid register code"

let print_memory (state : state) (n : int) : unit =
  let mem = state.memory in
  let len = Bytes.length mem in
  let limit = min n len in
  Printf.printf "Memory dump (first %d bytes):\n" limit;
  for i = 0 to limit - 1 do
    let byte = Char.code (Bytes.get mem i) in
    Printf.printf "%02X " byte;
    if (i + 1) mod 16 = 0 then Printf.printf "\n";
  done;
  Printf.printf "\n"

let print_memory_pretty (state : state) (n : int) : unit =
  let mem = state.memory in
  let len = Bytes.length mem in
  let limit = min n len in
  Printf.printf "Memory dump (first %d bytes):\n" limit;

  for i = 0 to limit - 1 do
    (* Print address at the start of each row *)
    if i mod 16 = 0 then
      Printf.printf "%04X: " i;

    let byte = Char.code (Bytes.get mem i) in
    Printf.printf "%02X " byte;

    (* Newline after 16 bytes *)
    if (i + 1) mod 16 = 0 then Printf.printf "\n";
  done;

  (* Final newline if last line was incomplete *)
  if limit mod 16 <> 0 then Printf.printf "\n"

let print_regs state : unit =
  let regs = state.regs in
  Printf.printf "A: %06X\n" regs.a;
  Printf.printf "B: %06X\n" regs.b;
  Printf.printf "X: %06X\n" regs.x;
  Printf.printf "L: %06X\n" regs.l;
  Printf.printf "S: %06X\n" regs.s;
  Printf.printf "T: %06X\n" regs.t;
  Printf.printf "PC: %06X\n" regs.pc;
  Printf.printf "SW: %06X\n" regs.sw;
  Printf.printf "\n"