/**
 * This is an example for EhBasic
 * based on the 6502 CPU emulator
 * by Mike Chambers ([email protected])
 * https://jeelabs.org/book/1549b/
 */
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdint.h>

// Function declarations
uint16_t getpc();
uint8_t getop();
void exec6502(int32_t tickcount);
void reset6502();
void serout(uint8_t val);
uint8_t getkey();
void clearkey();
void print_state();
void load_program(const uint8_t *program, uint16_t size, uint16_t address);

// Global variables
volatile uint8_t curkey = 0;

// Serial output function optimized using registers directly
void serout(uint8_t val)
{
    while (!(UCSR0A & (1 << UDRE0)))
        ;       // Wait until buffer is ready
    UDR0 = val; // Send data
}

// Get key input
uint8_t getkey()
{
    return curkey;
}

// Clear key input
void clearkey()
{
    curkey = 0;
}

// Optimized hexadecimal print function
void printhex(uint16_t val)
{
    char buffer[5];

    for (int i = 3; i >= 0; i--)
    {
        uint8_t digit = (val >> (i * 4)) & 0xF;
        buffer[3 - i] = digit < 10 ? '0' + digit : 'A' + (digit - 10);
    }

    buffer[4] = '\0'; // Terminate string

    for (int i = 0; i < 4; i++)
    {
        serout(buffer[i]); // Send each character via serial
    }

    serout('\n'); // Newline
}

// Function to print CPU state for debugging (optional)
void print_state()
{
    uint16_t pc = getpc();
    uint8_t opcode = getop();
    // Implement function to print registers and status flags if needed
}

// Function to load a program into memory
void load_program(const uint8_t *program, uint16_t size, uint16_t address)
{
    extern uint8_t RAM[]; // Access RAM from cpu.c

    for (uint16_t i = 0; i < size; i++)
    {
        RAM[address + i] = program[i];
    }
}

// Main loop
int main(void)
{
    // Set up serial communication at 9600 bps using registers directly
    UBRR0H = 0;
    UBRR0L = 103; // Baud rate 9600 with 16 MHz clock

    UCSR0A = 0;
    UCSR0B = (1 << TXEN0) | (1 << RXEN0); // Enable TX and RX
    UCSR0C =
        (1 << UCSZ01) |
        (1 << UCSZ00); // Configure 8N1 (8 data bits, no parity, 1 stop bit)

    // Initialize the 6502 system
    reset6502();

    // Optionally load a program into memory
    // const uint8_t program[] = { ... };
    // load_program(program, sizeof(program), 0x8000);

    while (1)
    {
        exec6502(1000); // Execute 1000 clock cycles

        // Check if data is received via serial
        if (UCSR0A & (1 << RXC0))
        {
            curkey = UDR0 & 0x7F; // Read received data and mask the parity bit
        }

        // Optional: Print CPU state for debugging
        print_state();

        // Small delay to prevent CPU hogging
        _delay_ms(1);
    }

    return 0;
}