mirror of https://github.com/makarcz/vm6502.git
650 lines
17 KiB
C++
650 lines
17 KiB
C++
|
|
#include "Memory.h"
|
|
#include "MKGenException.h"
|
|
|
|
//#define DBG 1
|
|
#if defined (DBG)
|
|
#include <iostream>
|
|
using namespace std;
|
|
#endif
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method:
|
|
* Purpose:
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
|
|
namespace MKBasic {
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method:
|
|
* Purpose:
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
Memory::Memory()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method:
|
|
* Purpose:
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
Memory::~Memory()
|
|
{
|
|
if (NULL != mpMemMapDev) delete mpMemMapDev;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method:
|
|
* Purpose:
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void Memory::Initialize()
|
|
{
|
|
unsigned short addr = 0;
|
|
for (int i=0; i < 0xFFFF; i++) {
|
|
m8bitMem[addr++] = 0;
|
|
}
|
|
for (int i=0; i < MEM_PAGE_SIZE; i++) {
|
|
mMemPageDev[i] = -1;
|
|
}
|
|
mCharIOAddr = CHARIO_ADDR;
|
|
mCharIOActive = false;
|
|
mIOEcho = false;
|
|
mROMBegin = ROM_BEGIN;
|
|
mROMEnd = ROM_END;
|
|
mROMActive = false;
|
|
mpMemMapDev = new MemMapDev(this);
|
|
mGraphDispActive = false;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method:
|
|
* Purpose:
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void Memory::EnableROM()
|
|
{
|
|
mROMActive = true;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method:
|
|
* Purpose:
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void Memory::DisableROM()
|
|
{
|
|
mROMActive = false;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method:
|
|
* Purpose:
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void Memory::SetROM(unsigned short start, unsigned short end)
|
|
{
|
|
if (mROMEnd > mROMBegin) {
|
|
mROMBegin = start;
|
|
mROMEnd = end;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method:
|
|
* Purpose:
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void Memory::EnableROM(unsigned short start, unsigned short end)
|
|
{
|
|
SetROM(start,end);
|
|
EnableROM();
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: Peek8bit()
|
|
* Purpose: Get/read 8-bit value from memory. If the memory address
|
|
* is within the range of any active devices, call
|
|
* handling method for this device.
|
|
* Arguments: addr - memory address
|
|
* Returns: unsigned char value read from specified memory address
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
unsigned char Memory::Peek8bit(unsigned short addr)
|
|
{
|
|
// if memory address is in range of any active memory mapped
|
|
// devices, call corresponding device handling function
|
|
int mempg = addr / MEM_PAGE_SIZE;
|
|
if (mMemPageDev[mempg] >= 0) {
|
|
bool cont_loop = true;
|
|
for (vector<Device>::iterator devit = mActiveDeviceVec.begin();
|
|
devit != mActiveDeviceVec.end() && cont_loop;
|
|
++devit
|
|
) {
|
|
|
|
if (devit->num >= 0) {
|
|
for (MemAddrRanges::iterator memrangeit = devit->addr_ranges.begin();
|
|
memrangeit != devit->addr_ranges.end();
|
|
++memrangeit
|
|
) {
|
|
if (addr >= memrangeit->start_addr && addr <= memrangeit->end_addr) {
|
|
ReadFunPtr pfun = devit->read_fun_ptr;
|
|
if (pfun != NULL) {
|
|
cont_loop = false;
|
|
(mpMemMapDev->*pfun)((int)addr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
mDispOp = (DEVNUM_GRDISP == devit->num);
|
|
}
|
|
}
|
|
}
|
|
|
|
return m8bitMem[addr];
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: Peek8bitImg()
|
|
* Purpose: Get/read 8-bit value from memory image only.
|
|
* Memory mapped devices are not affected.
|
|
* Arguments: addr - memory address
|
|
* Returns: unsigned char value read from specified memory address
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
unsigned char Memory::Peek8bitImg(unsigned short addr)
|
|
{
|
|
return m8bitMem[addr];
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: Peek16bit()
|
|
* Purpose: Get/read 16-bit value from memory. If the memory address
|
|
* is within the range of any active devices, call
|
|
* handling method for this device.
|
|
* Arguments: addr - memory address
|
|
* Returns: unsigned short value read secified from memory address
|
|
* and next memory address (16-bit, little endian)
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
unsigned short Memory::Peek16bit(unsigned short addr)
|
|
{
|
|
unsigned short ret = 0;
|
|
|
|
// if memory address is in range of any active memory mapped
|
|
// devices, call corresponding device handling function
|
|
int mempg = addr / MEM_PAGE_SIZE;
|
|
if (mMemPageDev[mempg] >= 0) {
|
|
bool cont_loop = true;
|
|
for (vector<Device>::iterator devit = mActiveDeviceVec.begin();
|
|
devit != mActiveDeviceVec.end() && cont_loop;
|
|
++devit
|
|
) {
|
|
if (devit->num >= 0) {
|
|
for (MemAddrRanges::iterator memrangeit = devit->addr_ranges.begin();
|
|
memrangeit != devit->addr_ranges.end();
|
|
++memrangeit
|
|
) {
|
|
if (addr >= memrangeit->start_addr && addr <= memrangeit->end_addr) {
|
|
ReadFunPtr pfun = devit->read_fun_ptr;
|
|
if (pfun != NULL) {
|
|
cont_loop = false;
|
|
(mpMemMapDev->*pfun)((int)addr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
mDispOp = (DEVNUM_GRDISP == devit->num);
|
|
}
|
|
}
|
|
}
|
|
|
|
ret = m8bitMem[addr++];
|
|
ret += m8bitMem[addr] * 256;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: Poke8bit()
|
|
* Purpose: Write byte to specified memory location.
|
|
* If the memory location is mapped to an active device,
|
|
* call corresponding handling function.
|
|
* If the memory location is protected (ROM), do not
|
|
* write the value.
|
|
* Arguments: addr - (0x0000..0xffff) memory address,
|
|
* val - value (0x00..0xff)
|
|
* Returns: n/a
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void Memory::Poke8bit(unsigned short addr, unsigned char val)
|
|
{
|
|
// if memory address is in range of any active memory mapped
|
|
// devices, call corresponding device handling function
|
|
int mempg = addr / MEM_PAGE_SIZE;
|
|
if (mMemPageDev[mempg] >= 0) {
|
|
bool cont_loop = true;
|
|
for (vector<Device>::iterator devit = mActiveDeviceVec.begin();
|
|
devit != mActiveDeviceVec.end() && cont_loop;
|
|
++devit
|
|
) {
|
|
if (devit->num >= 0) {
|
|
for (MemAddrRanges::iterator memrangeit = devit->addr_ranges.begin();
|
|
memrangeit != devit->addr_ranges.end();
|
|
++memrangeit
|
|
) {
|
|
if (addr >= memrangeit->start_addr && addr <= memrangeit->end_addr) {
|
|
WriteFunPtr pfun = devit->write_fun_ptr;
|
|
if (pfun != NULL) {
|
|
cont_loop = false;
|
|
(mpMemMapDev->*pfun)((int)addr,(int)val);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
mDispOp = (DEVNUM_GRDISP == devit->num);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!mROMActive || (addr < mROMBegin || addr > mROMEnd)) {
|
|
m8bitMem[addr] = val;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: Poke8bitImg()
|
|
* Purpose: Write byte to specified memory location.
|
|
* Arguments: addr - (0x0000..0xffff) memory address,
|
|
* val - value (0x00..0xff)
|
|
* Returns: n/a
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void Memory::Poke8bitImg(unsigned short addr, unsigned char val)
|
|
{
|
|
m8bitMem[addr] = val;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: SetCharIO()
|
|
* Purpose: Activates and sets an address of basic character I/O
|
|
* emulation (console).
|
|
* Arguments: addr - address of I/O area (0x0000..0xFFFF)
|
|
* Returns: n/a
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void Memory::SetCharIO(unsigned short addr, bool echo)
|
|
{
|
|
mCharIOAddr = addr;
|
|
mIOEcho = echo;
|
|
|
|
AddrRange addr_range(addr, addr+1);
|
|
MemAddrRanges memaddr_ranges;
|
|
DevPar dev_par("echo",(echo?"true":"false"));
|
|
DevParams dev_params;
|
|
|
|
dev_params.push_back(dev_par);
|
|
memaddr_ranges.push_back(addr_range);
|
|
|
|
SetupDevice(DEVNUM_CHARIO, memaddr_ranges, dev_params);
|
|
if (false == mCharIOActive) AddDevice(DEVNUM_CHARIO);
|
|
mCharIOActive = true;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: DisableCharIO()
|
|
* Purpose: Deactivates basic character I/O emulation (console).
|
|
* Arguments: n/a
|
|
* Returns: n/a
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void Memory::DisableCharIO()
|
|
{
|
|
mCharIOActive = false;
|
|
DeleteDevice(DEVNUM_CHARIO);
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: GetCharIOAddr()
|
|
* Purpose: Returns current address of basic character I/O area.
|
|
* Arguments: n/a
|
|
* Returns: address of I/O area
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
unsigned short Memory::GetCharIOAddr()
|
|
{
|
|
return mCharIOAddr;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: SetGraphDisp()
|
|
* Purpose: Setup and activate graphics display device.
|
|
* Arguments: addr - base address of display device
|
|
* Returns: n/a
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void Memory::SetGraphDisp(unsigned short addr)
|
|
{
|
|
AddrRange addr_range(addr, addr + GRAPHDEVREG_END - 1);
|
|
MemAddrRanges memaddr_ranges;
|
|
DevPar dev_par("nil","nil");
|
|
DevParams dev_params;
|
|
|
|
dev_params.push_back(dev_par);
|
|
memaddr_ranges.push_back(addr_range);
|
|
|
|
SetupDevice(DEVNUM_GRDISP, memaddr_ranges, dev_params);
|
|
if (false == mGraphDispActive) AddDevice(DEVNUM_GRDISP);
|
|
mGraphDispActive = true;
|
|
mpMemMapDev->ActivateGraphDisp();
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: DisableGraphDisp()
|
|
* Purpose: Inactivate graphics display device.
|
|
* Arguments: n/a
|
|
* Returns: n/a
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void Memory::DisableGraphDisp()
|
|
{
|
|
mGraphDispActive = false;
|
|
mpMemMapDev->DeactivateGraphDisp();
|
|
DeleteDevice(DEVNUM_GRDISP);
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: GetGraphDispAddr()
|
|
* Purpose: Return base address of graphics display device.
|
|
* Arguments: n/a
|
|
* Returns: unsigned short - address ($0000 - $FFFF)
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
unsigned short Memory::GetGraphDispAddr()
|
|
{
|
|
return mpMemMapDev->GetGraphDispAddrBase();
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method:
|
|
* Purpose:
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
unsigned short Memory::GetROMBegin()
|
|
{
|
|
return mROMBegin;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method:
|
|
* Purpose:
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
unsigned short Memory::GetROMEnd()
|
|
{
|
|
return mROMEnd;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method:
|
|
* Purpose:
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
bool Memory::IsROMEnabled()
|
|
{
|
|
return mROMActive;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: AddDevice()
|
|
* Purpose: Add device number to active devices list.
|
|
* Arguments: devnum - device number
|
|
* Returns: -1 if device is not supported OR already cached
|
|
* devnum - device number if it was found
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
int Memory::AddDevice(int devnum)
|
|
{
|
|
int ret = -1;
|
|
bool found = false;
|
|
Device dev = mpMemMapDev->GetDevice(devnum);
|
|
if (dev.num >= 0) {
|
|
for (vector<Device>::iterator devit = mActiveDeviceVec.begin();
|
|
devit != mActiveDeviceVec.end();
|
|
++devit
|
|
) {
|
|
|
|
if (devit->num == devnum) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
// if device not found in local cache, add it
|
|
if (!found) {
|
|
mActiveDeviceVec.push_back(dev);
|
|
ret = devnum;
|
|
|
|
// update the device usage flag in memory pages devices array mMemPageDev
|
|
for (MemAddrRanges::iterator memrangeit = dev.addr_ranges.begin();
|
|
memrangeit != dev.addr_ranges.end();
|
|
++memrangeit
|
|
) {
|
|
|
|
int pgnum = memrangeit->start_addr / MEM_PAGE_SIZE;
|
|
while (pgnum < MEM_PAGE_SIZE) {
|
|
mMemPageDev[pgnum] = devnum;
|
|
pgnum++;
|
|
if (pgnum * MEM_PAGE_SIZE > memrangeit->end_addr) break;
|
|
}
|
|
}
|
|
}
|
|
} // END if (dev.num >= 0)
|
|
// else device with wuch number is not supported
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: DeleteDevice()
|
|
* Purpose: Delete device number from active devices list.
|
|
* Arguments: devnum - device number
|
|
* Returns: >=0 if device was found in local cache and deleted
|
|
* -1 if device was not found
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
int Memory::DeleteDevice(int devnum)
|
|
{
|
|
vector<Device> actdev_new;
|
|
int ret = -1;
|
|
|
|
for (int i=0; i < MEM_PAGE_SIZE; i++) {
|
|
mMemPageDev[i] = -1;
|
|
}
|
|
// device is deleted by refreshing local active devices cache
|
|
// the device to be deleted is skipped and not re-added to refreshed
|
|
// cache vector
|
|
for (vector<Device>::iterator devit = mActiveDeviceVec.begin();
|
|
devit != mActiveDeviceVec.end();
|
|
++devit
|
|
) {
|
|
|
|
if (devit->num != devnum) {
|
|
|
|
Device dev = mpMemMapDev->GetDevice(devit->num);
|
|
|
|
if (dev.num < 0)
|
|
throw MKGenException("Unsupported device in local cache");
|
|
|
|
actdev_new.push_back(mpMemMapDev->GetDevice(devit->num));
|
|
|
|
// update the device number in memory pages devices array mMemPageDev
|
|
for (MemAddrRanges::iterator memrangeit = devit->addr_ranges.begin();
|
|
memrangeit != devit->addr_ranges.end();
|
|
++memrangeit
|
|
) {
|
|
|
|
int pgnum = memrangeit->start_addr / MEM_PAGE_SIZE;
|
|
while (pgnum < MEM_PAGE_SIZE) {
|
|
mMemPageDev[pgnum] = devit->num;
|
|
pgnum++;
|
|
if (pgnum * MEM_PAGE_SIZE > memrangeit->end_addr) break;
|
|
}
|
|
}
|
|
|
|
} else ret++; // indicating that the device was found in cache
|
|
}
|
|
// refresh local active devices cache
|
|
mActiveDeviceVec.clear();
|
|
mActiveDeviceVec = actdev_new;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: SetupDevice()
|
|
* Purpose: Configure device address ranges and parameters.
|
|
* Arguments: devnum - device number, must be on active dev. list
|
|
* memranges - memory address ranges vector
|
|
* params - parameters vector
|
|
* Returns: n/a
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void Memory::SetupDevice(int devnum,
|
|
MemAddrRanges memranges,
|
|
DevParams params)
|
|
{
|
|
Device dev = mpMemMapDev->GetDevice(devnum);
|
|
if (dev.num >= 0) {
|
|
mpMemMapDev->SetupDevice(devnum, memranges, params);
|
|
// reload device to local vector because its setup has changed
|
|
// but only if device was cached locally already (active)
|
|
if (0 <= DeleteDevice(devnum)) AddDevice(devnum);
|
|
}
|
|
if (DEVNUM_CHARIO == devnum) {
|
|
mCharIOAddr = mpMemMapDev->GetCharIOAddr();
|
|
mIOEcho = mpMemMapDev->GetCharIOEchoOn();
|
|
}
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: GetCharIn()
|
|
* Purpose: Return character from the emulated character I/O FIFO
|
|
* input buffer or -1 if FIFO empty or char I/O not active.
|
|
* Arguments: n/a
|
|
* Returns: character
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
char Memory::GetCharIn()
|
|
{
|
|
return mpMemMapDev->GetCharIn();
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: GetCharOut()
|
|
* Purpose: Return character from the emulated character I/O FIFO
|
|
* output buffer or -1 if FIFO empty or char I/O not
|
|
* active.
|
|
* Arguments: n/a
|
|
* Returns: character
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
char Memory::GetCharOut()
|
|
{
|
|
return mpMemMapDev->GetCharOut();
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method:
|
|
* Purpose:
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void Memory::GraphDisp_ReadEvents()
|
|
{
|
|
mpMemMapDev->GraphDisp_ReadEvents();
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method:
|
|
* Purpose:
|
|
* Arguments:
|
|
* Returns:
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
void Memory::GraphDisp_Update()
|
|
{
|
|
mpMemMapDev->GraphDisp_Update();
|
|
}
|
|
|
|
/*
|
|
*--------------------------------------------------------------------
|
|
* Method: GraphDispOp()
|
|
* Purpose: Status of last operation being perf. on Graphics Display
|
|
* or not.
|
|
* Arguments: n/a
|
|
* Returns: bool, true if last operation was performed on Graphics
|
|
* Display device.
|
|
*--------------------------------------------------------------------
|
|
*/
|
|
bool Memory::GraphDispOp()
|
|
{
|
|
return mDispOp;
|
|
}
|
|
|
|
} // namespace MKBasic
|