mirror of
https://github.com/wavemotion-dave/GimliDS.git
synced 2025-06-18 22:05:33 -04:00
347 lines
8.6 KiB
C++
347 lines
8.6 KiB
C++
// =====================================================================================
|
|
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
|
|
//
|
|
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
|
|
// any copying or distribution of this emulator, its source code and associated
|
|
// readme files, with or without modification, are permitted per the original
|
|
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
|
|
// efforts to provide a clean open-source emulation base for the C64.
|
|
//
|
|
// Numerous hacks and 'unsafe' optimizations have been performed on the original
|
|
// Frodo emulator codebase to get it running on the small handheld system. You
|
|
// are strongly encouraged to seek out the official Frodo sources if you're at
|
|
// all interested in this emulator code.
|
|
//
|
|
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
|
|
// =====================================================================================
|
|
|
|
/*
|
|
* REU.cpp - 17xx REU emulation
|
|
*
|
|
* Frodo Copyright (C) Christian Bauer
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/*
|
|
* Incompatibilities:
|
|
* ------------------
|
|
*
|
|
* - REU interrupts are not emulated.
|
|
* - Transfer time is not accounted for, all transfers are done in 0 cycles.
|
|
*/
|
|
|
|
#include "sysdeps.h"
|
|
|
|
#include "REU.h"
|
|
#include "CPUC64.h"
|
|
#include "Prefs.h"
|
|
|
|
extern u8 myRAM[];
|
|
|
|
// We are emulating the REU-1764 (256K) only
|
|
u8 REU_RAM[256 * 1024];
|
|
|
|
#define RAM_MASK 0x3FFFF
|
|
|
|
/*
|
|
* REU constructor
|
|
*/
|
|
|
|
REU::REU(MOS6510 * cpu) : the_cpu(cpu)
|
|
{
|
|
// Allocate expansion RAM
|
|
ram_size = 0x40000; // 256K only
|
|
|
|
ex_ram = REU_RAM;
|
|
|
|
// Clear expansion RAM
|
|
memset(ex_ram, 0, ram_size);
|
|
|
|
// Reset registers
|
|
Reset();
|
|
}
|
|
|
|
|
|
/*
|
|
* REU destructor
|
|
*/
|
|
|
|
REU::~REU()
|
|
{
|
|
}
|
|
|
|
|
|
/*
|
|
* Reset the REU
|
|
*/
|
|
|
|
void REU::Reset()
|
|
{
|
|
// Set size bit in status register
|
|
if (ram_size > 0x20000)
|
|
{
|
|
regs[0] = 0x10;
|
|
}
|
|
else
|
|
{
|
|
regs[0] = 0x00;
|
|
}
|
|
|
|
// FF00 disabled in command register
|
|
regs[1] = 0x10;
|
|
|
|
for (unsigned i = 2; i < 10; ++i) {
|
|
regs[i] = 0;
|
|
}
|
|
|
|
// Transfer length = $ffff
|
|
regs[7] = regs[8] = 0xff;
|
|
|
|
// Unconnected registers
|
|
for (unsigned i = 11; i < 16; ++i) {
|
|
regs[i] = 0xff;
|
|
}
|
|
|
|
// Autoload registers
|
|
autoload_c64_adr_lo = 0;
|
|
autoload_c64_adr_hi = 0;
|
|
autoload_reu_adr_lo = 0;
|
|
autoload_reu_adr_hi = 0;
|
|
autoload_reu_adr_bank = 0;
|
|
autoload_length_lo = 0xff;
|
|
autoload_length_hi = 0xff;
|
|
}
|
|
|
|
|
|
/*
|
|
* Read from REU register
|
|
*/
|
|
|
|
uint8_t REU::ReadIO2(uint16_t adr, uint8_t bus_byte)
|
|
{
|
|
if (ex_ram == nullptr)
|
|
return bus_byte;
|
|
if ((adr & 0x1f) >= 0x10)
|
|
return 0xff;
|
|
|
|
switch (adr & 0xf)
|
|
{
|
|
case 0:
|
|
{
|
|
uint8_t ret = regs[0];
|
|
regs[0] &= 0x1f; // Clear status bits
|
|
return ret;
|
|
}
|
|
case 6:
|
|
return regs[6] | 0xf8;
|
|
case 9:
|
|
return regs[9] | 0x1f;
|
|
case 10:
|
|
return regs[10] | 0x3f;
|
|
default:
|
|
return regs[adr & 0xf];
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Write to REU register
|
|
*/
|
|
|
|
void REU::WriteIO2(uint16_t adr, uint8_t byte)
|
|
{
|
|
if (ex_ram == nullptr)
|
|
return;
|
|
if ((adr & 0x1f) >= 0x10)
|
|
return;
|
|
|
|
switch (adr & 0xf)
|
|
{
|
|
case 0: // Status register is read-only
|
|
case 2:
|
|
regs[2] = autoload_c64_adr_lo = byte;
|
|
regs[3] = autoload_c64_adr_hi;
|
|
break;
|
|
case 3:
|
|
regs[2] = autoload_c64_adr_lo;
|
|
regs[3] = autoload_c64_adr_hi = byte;
|
|
break;
|
|
case 4:
|
|
regs[4] = autoload_reu_adr_lo = byte;
|
|
regs[5] = autoload_reu_adr_hi;
|
|
break;
|
|
case 5:
|
|
regs[4] = autoload_reu_adr_lo;
|
|
regs[5] = autoload_reu_adr_hi = byte;
|
|
break;
|
|
case 6:
|
|
regs[6] = autoload_reu_adr_bank = byte;
|
|
break;
|
|
case 7:
|
|
regs[7] = autoload_length_lo = byte;
|
|
regs[8] = autoload_length_hi;
|
|
break;
|
|
case 8:
|
|
regs[7] = autoload_length_lo;
|
|
regs[8] = autoload_length_hi = byte;
|
|
break;
|
|
case 11: // Unconnected registers
|
|
case 12:
|
|
case 13:
|
|
case 14:
|
|
case 15:
|
|
break;
|
|
case 1: // Command register
|
|
regs[1] = byte;
|
|
if ((byte & 0x90) == 0x90)
|
|
{
|
|
execute_dma();
|
|
}
|
|
if ((byte & 0x90) == 0x80)
|
|
{
|
|
// This would normally be the 'delayed' write... not fully handled by GimliDS as we can't afford the trigger check on FF00
|
|
execute_dma();
|
|
}
|
|
break;
|
|
default:
|
|
regs[adr & 0xf] = byte;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Execute REU DMA transfer
|
|
*/
|
|
|
|
void REU::execute_dma()
|
|
{
|
|
// Clear execute bit in command register
|
|
regs[1] &= 0x7f;
|
|
|
|
// Set FF00 disable bit in command register
|
|
regs[1] |= 0x10;
|
|
|
|
// Get C64 and REU transfer base addresses
|
|
uint16_t c64_adr = regs[2] | (regs[3] << 8);
|
|
uint32_t reu_adr = regs[4] | (regs[5] << 8) | (regs[6] << 16);
|
|
|
|
// Get transfer length
|
|
uint16_t length = regs[7] | (regs[8] << 8);
|
|
|
|
// We are skipping the memory increment of regs[10] as there
|
|
// are no known practical uses of it... and we need the speed.
|
|
|
|
if ((regs[1] & 3) == 0)
|
|
{
|
|
while (length--)
|
|
{
|
|
REU_RAM[reu_adr++ & RAM_MASK] = the_cpu->REUReadByte(c64_adr++);
|
|
}
|
|
}
|
|
else if ((regs[1] & 3) == 1)
|
|
{
|
|
while (length--)
|
|
{
|
|
the_cpu->REUWriteByte(c64_adr++, REU_RAM[reu_adr++ & RAM_MASK]);
|
|
}
|
|
}
|
|
else if ((regs[1] & 3) == 2)
|
|
{
|
|
while (length--)
|
|
{
|
|
uint8_t tmp = the_cpu->REUReadByte(c64_adr);
|
|
the_cpu->REUWriteByte(c64_adr++, REU_RAM[reu_adr & RAM_MASK]);
|
|
REU_RAM[reu_adr++ & RAM_MASK] = tmp;
|
|
}
|
|
}
|
|
else // Compare
|
|
{
|
|
while (length--)
|
|
{
|
|
if (REU_RAM[reu_adr++ & RAM_MASK] != the_cpu->REUReadByte(c64_adr++))
|
|
{
|
|
regs[0] |= 0x20; // Verify error
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
regs[0] |= 0x40; // Transfer finished
|
|
|
|
// Update address and length registers
|
|
if (regs[1] & 0x20)
|
|
{
|
|
regs[2] = autoload_c64_adr_lo;
|
|
regs[3] = autoload_c64_adr_hi;
|
|
regs[4] = autoload_reu_adr_lo;
|
|
regs[5] = autoload_reu_adr_hi;
|
|
regs[6] = autoload_reu_adr_bank;
|
|
regs[7] = autoload_length_lo;
|
|
regs[8] = autoload_length_hi;
|
|
}
|
|
else
|
|
{
|
|
reu_adr &= RAM_MASK;
|
|
regs[2] = c64_adr & 0xff;
|
|
regs[3] = c64_adr >> 8;
|
|
regs[4] = reu_adr & 0xff;
|
|
regs[5] = (reu_adr >> 8) & 0xff;
|
|
regs[6] = (reu_adr >> 16) & 0xff;
|
|
regs[7] = length & 0xff;
|
|
regs[8] = (length >> 8) & 0xff;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Ret REU state
|
|
*/
|
|
|
|
void REU::GetState(REUState *rs)
|
|
{
|
|
rs->ram_size = ram_size;
|
|
rs->ram_mask = RAM_MASK;
|
|
rs->autoload_c64_adr_lo = autoload_c64_adr_lo;
|
|
rs->autoload_c64_adr_hi = autoload_c64_adr_hi;
|
|
rs->autoload_reu_adr_lo = autoload_reu_adr_lo;
|
|
rs->autoload_reu_adr_hi = autoload_reu_adr_hi;
|
|
rs->autoload_reu_adr_bank = autoload_reu_adr_bank;
|
|
rs->autoload_length_lo = autoload_length_lo;
|
|
rs->autoload_length_hi = autoload_length_hi;
|
|
memcpy(rs->regs, regs, sizeof(regs));
|
|
}
|
|
|
|
|
|
/*
|
|
* Set REU state
|
|
*/
|
|
|
|
void REU::SetState(REUState *rs)
|
|
{
|
|
ram_size = rs->ram_size;
|
|
ram_mask = RAM_MASK;
|
|
autoload_c64_adr_lo = rs->autoload_c64_adr_lo;
|
|
autoload_c64_adr_hi = rs->autoload_c64_adr_hi;
|
|
autoload_reu_adr_lo = rs->autoload_reu_adr_lo;
|
|
autoload_reu_adr_hi = rs->autoload_reu_adr_hi;
|
|
autoload_reu_adr_bank = rs->autoload_reu_adr_bank;
|
|
autoload_length_lo = rs->autoload_length_lo;
|
|
autoload_length_hi = rs->autoload_length_hi;
|
|
memcpy(regs, rs->regs, sizeof(regs));
|
|
}
|
|
|