/*
 * Copyright (c) 2021-2022 Rupert Carmichael
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

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

#include "cega.h"
#include "cega_m68k.h"
#include "cega_md.h"

#include "m68k.h"

static size_t delaycycs = 0;
static uint64_t totalcycs = 0;

unsigned int m68k_read_memory_8(unsigned int address) {
    address &= 0xffffff;
    return cega_md_m68k_rd08(address);
}

unsigned int m68k_read_memory_16(unsigned int address) {
    address &= 0xffffff;
    return cega_md_m68k_rd16(address);
}

unsigned int m68k_read_memory_32(unsigned int address) {
    address &= 0xffffff;
    return (cega_md_m68k_rd16(address) << 16) | cega_md_m68k_rd16(address + 2);
}

void m68k_write_memory_8(unsigned int address, unsigned int value) {
    address &= 0xffffff;
    cega_md_m68k_wr08(address, (uint8_t)(value & 0xff));
}

void m68k_write_memory_16(unsigned int address, unsigned int value) {
    address &= 0xffffff;
    cega_md_m68k_wr16(address, (uint16_t)(value & 0xffff));
}

void m68k_write_memory_32(unsigned int address, unsigned int value) {
    address &= 0xffffff;
    cega_md_m68k_wr16(address, (uint16_t)(value >> 16));
    cega_md_m68k_wr16(address + 2, (uint16_t)(value & 0xffff));
}

uint64_t cega_m68k_cyc_total(void) {
    return totalcycs;
}

// Delay the M68K's execution by a requested number of cycles
void cega_m68k_delay(size_t delay) {
    delaycycs += delay;
}

void cega_m68k_irq(int level) {
    m68k_set_irq(level);
}

void cega_m68k_init(void) {
    m68k_init();
    m68k_set_cpu_type(M68K_CPU_TYPE_68000);
}

void cega_m68k_reset(void) {
    m68k_pulse_reset();
}

// Run a single instruction
uint32_t cega_m68k_exec(void) {
    uint32_t retcyc = m68k_execute(1);

    if (delaycycs) {
        retcyc += delaycycs;
        delaycycs = 0;
    }

    totalcycs += retcyc;
    return retcyc;
}

// Run a number of instruction
uint32_t cega_m68k_run(int cycles) {
    uint32_t retcyc = m68k_execute(cycles);

    if (delaycycs) {
        retcyc += delaycycs;
        delaycycs = 0;
    }

    totalcycs += retcyc;
    return retcyc;
}
