// ============================================================================
// face_c64_dialect.c — the C64 dialect: fills FaceBasis by wrapping the real
// 6502 core (fake6502) + real KERNAL/BASIC ROMs + emudore-derived Memory/IO.
//
// REFERENCE COPY. This file depends on the OS64-master kernel's own headers
// (c64/memory.h, c64/io.h) and fake6502 bridge -- it will not compile
// standalone outside that tree. The live, buildable, hardware-verified
// original is:
//   Commadore64 on Substrate/OS64-master/src/c64/face_node.cpp
// Treat that copy as canonical; this one is kept here so the C64 dialect's
// shape is visible alongside the universal core without needing to open the
// full kernel tree. If they ever disagree, the OS64-master copy wins.
// ============================================================================
#include "../../core/face_node.h"
#include <c64/memory.h>
#include <c64/io.h>

// bridge symbols from fake6502_bridge.cpp
extern "C" {
  void     f6502_bind(Memory* mem);
  void     f6502_reset();
  void     f6502_step();
  void     f6502_irq();
  uint16_t f6502_pc();
}

// the C64 face's private machine: just its Memory (the abstraction the guest
// binds to) and IO (mouse -> joystick-port translation lives there). The real
// 6502 core is the shared fake6502 bridge.
struct C64Self { Memory* mem; IO* io; unsigned long tick; };

static void c64_init(void* self)
{
  C64Self* s = (C64Self*)self;
  f6502_bind(s->mem);
  f6502_reset();                 // real KERNAL reset — boots for real
  s->tick = 0;
}

static int c64_step(void* self, unsigned int quanta)
{
  C64Self* s = (C64Self*)self;
  for(unsigned int i=0;i<quanta;i++){
    f6502_step();
    if((++s->tick % 16667) == 0) f6502_irq();  // ~60Hz for kbd/cursor
  }
  return 0;
}

static void c64_observe(void* self, unsigned char* surface, int cols, int rows)
{
  C64Self* s = (C64Self*)self;
  // OBSERVE real screen RAM ($0400) -> ASCII projection surface (read-only)
  for(int i=0;i<cols*rows && i<FACE_SURFACE_SIZE;i++){
    uint8_t sc = s->mem->read_byte(0x0400 + i);   // C64 screen code
    uint8_t a;
    if(sc == 0x00)       a = '@';
    else if(sc <= 0x1A) a = 'A' + (sc - 1);
    else if(sc <= 0x3F) a = sc;
    else                a = ' ';
    surface[i] = a;
  }
}

static void c64_input(void* self, uint8_t key)
{
  C64Self* s = (C64Self*)self;
  // deliver into the real KERNAL keyboard buffer (KEYD $0277 / NDX $C6),
  // the authentic path — BASIC reads and echoes it itself.
  uint8_t petscii;
  if(key >= 'a' && key <= 'z') petscii = key - 0x20; else petscii = key;
  uint8_t ndx = s->mem->read_byte(0x00C6);
  if(ndx < 10){
    s->mem->write_byte(0x0277 + ndx, petscii);
    s->mem->write_byte(0x00C6, ndx + 1);
  }
  // We inject into the buffer directly rather than via the hardware matrix, so
  // the KERNAL must NOT think a physical key is being held (or it auto-repeats,
  // producing doubled characters like HEELLLOO). Tell it: no key in the matrix
  // ($CB=0x40), and last-key = none ($C5=0x40), and repeat off ($028A=0x40).
  s->mem->write_byte(0x00CB, 0x40);   // no key currently down in matrix
  s->mem->write_byte(0x00C5, 0x40);   // last key = none
  s->mem->write_byte(0x028A, 0x40);   // RPTFLG: no repeat
}

static uint32_t c64_coord(void* self){ (void)self; return f6502_pc(); }

// TRANSLATE mouse: a real C64 mouse (1350-style) reports relative motion as
// direction pulses on joystick port 1's own lines -- no SID/POT plumbing
// needed, it's the same port lines the KERNAL already reads for a joystick.
static void c64_mouse(void* self, int8_t dx, int8_t dy, uint8_t buttons)
{
  C64Self* s = (C64Self*)self;
  if(s->io) s->io->mouseEvent(dx, dy, (buttons & 0x1) != 0);
}

// build a C64 face bound to a given Memory (the abstraction).
static C64Self g_c64_self;
void face_bind_c64(struct FaceNode* f, Memory* mem, IO* io)
{
  g_c64_self.mem = mem; g_c64_self.io = io; g_c64_self.tick = 0;
  f->name = "c64";
  f->self = &g_c64_self;
  f->cols = FACE_SURFACE_COLS; f->rows = FACE_SURFACE_ROWS;
  f->basis.init    = c64_init;
  f->basis.step    = c64_step;
  f->basis.observe = c64_observe;
  f->basis.input   = c64_input;
  f->basis.mouse   = c64_mouse;
  f->basis.coord   = c64_coord;
}
