496 lines
17 KiB
C++
496 lines
17 KiB
C++
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string>
|
|
#include <algorithm>
|
|
|
|
#include "memory.h"
|
|
|
|
|
|
namespace Memory
|
|
{
|
|
int _sizeRAM = RAM_SIZE_LO;
|
|
int _baseFreeRAM = _sizeRAM - RAM_USED_DEFAULT;
|
|
int _sizeFreeRAM = _baseFreeRAM;
|
|
|
|
std::vector<RamEntry> _freeRam;
|
|
std::vector<RamEntry> _videoRam;
|
|
|
|
|
|
int getSizeRAM(void) {return _sizeRAM;}
|
|
int getBaseFreeRAM(void) {return _baseFreeRAM;}
|
|
int getSizeFreeRAM(void) {return _sizeFreeRAM;}
|
|
int getFreeGtbRAM(int numLines)
|
|
{
|
|
int free = ((0x80 - HI_BYTE(GTB_LINE0_ADDRESS))*NUM_GTB_LINES_PER_ROW - numLines)*MAX_GTB_LINE_SIZE - MAX_GTB_LINE_SIZE;
|
|
if(_sizeRAM == RAM_SIZE_HI) free += RAM_UPPER_SIZE;
|
|
return free;
|
|
}
|
|
|
|
void setSizeRAM(int sizeRAM) {_sizeRAM = sizeRAM; initialise();}
|
|
void setSizeFreeRAM(int freeRAM) {_sizeFreeRAM = (freeRAM >= 0) ? freeRAM : 0;}
|
|
|
|
|
|
void initialise(void)
|
|
{
|
|
_freeRam.clear();
|
|
_videoRam.clear();
|
|
|
|
// 0x0200 <-> 0x0400
|
|
_freeRam.push_back({RAM_PAGE_START_0, RAM_PAGE_SIZE_0});
|
|
_freeRam.push_back({RAM_PAGE_START_1, RAM_PAGE_SIZE_1});
|
|
_freeRam.push_back({RAM_PAGE_START_2, RAM_PAGE_SIZE_2});
|
|
|
|
// 0x0500 <-> 0x0600
|
|
_freeRam.push_back({RAM_PAGE_START_3, RAM_PAGE_SIZE_3*2});
|
|
|
|
// 0x08A0 <-> 0x7FA0
|
|
for(uint16_t i=RAM_SEGMENTS_START; i<=RAM_SEGMENTS_END; i+=RAM_SEGMENTS_OFS) _freeRam.push_back({i, RAM_SEGMENTS_SIZE});
|
|
|
|
// 0x8000 <-> 0xFF00
|
|
if(_sizeRAM == RAM_SIZE_HI) _freeRam.push_back({RAM_UPPER_START, RAM_UPPER_SIZE});
|
|
|
|
// VRAM 0x0800 <-> 0x7F00, 160x120 pixels, offscreen areas start at 0xXXA0 and end at 0xXXFF, (can be used for horizontal scrolling or code/data storage)
|
|
for(uint16_t i=RAM_VIDEO_START; i<=RAM_VIDEO_END; i+=RAM_VIDEO_OFS) _videoRam.push_back({i, RAM_SCANLINE_SIZE});
|
|
|
|
_baseFreeRAM = _sizeRAM - RAM_USED_DEFAULT;
|
|
_sizeFreeRAM = _baseFreeRAM;
|
|
}
|
|
|
|
void invertFreeRAM(void)
|
|
{
|
|
_freeRam.clear();
|
|
_freeRam = _videoRam;
|
|
}
|
|
|
|
void updateFreeRAM(void)
|
|
{
|
|
_sizeFreeRAM = 0;
|
|
|
|
// Check to see if any chunks can be merged
|
|
for(auto it=_freeRam.begin(); it!=_freeRam.end()-1;)
|
|
{
|
|
uint16_t addr0 = it->_address;
|
|
uint16_t size0 = uint16_t(it->_size);
|
|
|
|
uint16_t addr1 = (it + 1)->_address;
|
|
|
|
// Merge
|
|
if(addr0 + size0 == addr1)
|
|
{
|
|
(it + 1)->_size += size0;
|
|
(it + 1)->_address = addr0;
|
|
it = _freeRam.erase(it);
|
|
}
|
|
else
|
|
{
|
|
it++;
|
|
}
|
|
}
|
|
|
|
// Sort by address in ascending order
|
|
std::sort(_freeRam.begin(), _freeRam.end(), [](const RamEntry& ramEntryA, const RamEntry& ramEntryB)
|
|
{
|
|
uint16_t addressA = ramEntryA._address;
|
|
uint16_t addressB = ramEntryB._address;
|
|
return (addressA < addressB);
|
|
});
|
|
|
|
for(int i=0; i<int(_freeRam.size()); i++)
|
|
{
|
|
_sizeFreeRAM += _freeRam[i]._size;
|
|
}
|
|
}
|
|
|
|
bool updateFreeRamList(int index, uint16_t address, int size)
|
|
{
|
|
if(index >= 0 && size >= 0)
|
|
{
|
|
_freeRam.erase(_freeRam.begin() + index);
|
|
if(size) _freeRam.push_back({uint16_t(address), size});
|
|
updateFreeRAM();
|
|
|
|
//fprintf(stderr, "1 RAM segment: %3d : %04x %3d : %04x %3d\n", _sizeFreeRAM, address, size, uint16_t(address + size), newSize);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool updateFreeRamList(int index, uint16_t address0, int size0, uint16_t address1, int size1)
|
|
{
|
|
if(index >= 0)
|
|
{
|
|
_freeRam.erase(_freeRam.begin() + index);
|
|
if(size0) _freeRam.push_back({uint16_t(address0), size0});
|
|
if(size1) _freeRam.push_back({uint16_t(address1), size1});
|
|
updateFreeRAM();
|
|
|
|
//fprintf(stderr, "2 RAM segments: %3d : %04x %3d : %04x %3d\n", _sizeFreeRAM, address0, size0, address1, size1);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool isFreeRAM(uint16_t address, int size)
|
|
{
|
|
if(address > _sizeRAM - 1)
|
|
{
|
|
fprintf(stderr, "Memory::isFreeRAM() : Memory at 0x%04x does not exist on this %d byte system : your request : 0x%04x %d\n", address, _sizeRAM, address, size);
|
|
return false;
|
|
}
|
|
|
|
for(int i=0; i<int(_freeRam.size()); i++)
|
|
{
|
|
// Free RAM segment
|
|
if(address == _freeRam[i]._address && size <= _freeRam[i]._size)
|
|
{
|
|
return true;
|
|
}
|
|
// Free RAM within segment
|
|
else if(address > _freeRam[i]._address && (address + size <= _freeRam[i]._address + _freeRam[i]._size))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool isVideoRAM(uint16_t address)
|
|
{
|
|
for(int i=0; i<int(_videoRam.size()); i++)
|
|
{
|
|
// Address within Video RAM segment
|
|
if(address >= _videoRam[i]._address && (address < _videoRam[i]._address + _videoRam[i]._size))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool getNextCodeAddress(FitType fitType, uint16_t start, int size, uint16_t& address)
|
|
{
|
|
switch(fitType)
|
|
{
|
|
case FitAscending:
|
|
{
|
|
for(int j=0; j<int(_freeRam.size()); j++)
|
|
{
|
|
for(int i=0; i<_freeRam[j]._size; i++)
|
|
{
|
|
uint16_t left = uint16_t(_freeRam[j]._size - i);
|
|
uint16_t addr = uint16_t(_freeRam[j]._address + i);
|
|
if(addr >= start && size <= left && HI_BYTE(addr) == HI_BYTE(addr + size))
|
|
{
|
|
address = addr;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FitDescending:
|
|
{
|
|
for(int j=int(_freeRam.size())-1; j>=0; j--)
|
|
{
|
|
for(int i=_freeRam[j]._size-1; i>=0; i--)
|
|
{
|
|
uint16_t left = uint16_t(_freeRam[j]._size - i);
|
|
uint16_t addr = uint16_t(_freeRam[j]._address + i);
|
|
if(addr < start && size <= left && HI_BYTE(addr) == HI_BYTE(addr + size))
|
|
{
|
|
address = addr;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
fprintf(stderr, "Memory::getNextCodeAddress() : Couldn't find free code space in RAM of size %d bytes\n", size);
|
|
return false;
|
|
}
|
|
|
|
bool giveFreeRAM(uint16_t address, int size)
|
|
{
|
|
// Check to see if new chunk can be merged with any other free chunks
|
|
for(int i=0; i<int(_freeRam.size()); i++)
|
|
{
|
|
uint16_t siz = uint16_t(_freeRam[i]._size);
|
|
uint16_t addr = _freeRam[i]._address;
|
|
|
|
// RAM is already free
|
|
if(address >= addr && address + size <= addr + siz) return false;
|
|
|
|
// Insert
|
|
if(address + size == addr)
|
|
{
|
|
_freeRam[i]._size += size;
|
|
_freeRam[i]._address = address;
|
|
updateFreeRAM();
|
|
return true;
|
|
}
|
|
|
|
// Append
|
|
if(address == addr + siz)
|
|
{
|
|
_freeRam[i]._size += size;
|
|
updateFreeRAM();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Add new chunk
|
|
_freeRam.push_back({address, size});
|
|
updateFreeRAM();
|
|
return true;
|
|
}
|
|
|
|
bool takeFreeRAM(uint16_t address, int size, bool printError)
|
|
{
|
|
if(address > _sizeRAM - 1)
|
|
{
|
|
if(printError) fprintf(stderr, "Memory::takeFreeRAM() : Memory at 0x%04x does not exist on this %d byte system : your request : 0x%04x %d\n", address, _sizeRAM, address, size);
|
|
return false;
|
|
}
|
|
|
|
for(int i=0; i<int(_freeRam.size()); i++)
|
|
{
|
|
// RAM segment becomes smaller
|
|
if(address == _freeRam[i]._address && size <= _freeRam[i]._size)
|
|
{
|
|
if(!updateFreeRamList(i, address + uint16_t(size), _freeRam[i]._size - size)) break;
|
|
return true;
|
|
}
|
|
// RAM segment gets split into 2 smaller segments
|
|
else if(address > _freeRam[i]._address && (address + size <= _freeRam[i]._address + _freeRam[i]._size))
|
|
{
|
|
uint16_t address0 = _freeRam[i]._address;
|
|
uint16_t address1 = uint16_t(address + size);
|
|
int size0 = address - _freeRam[i]._address;
|
|
int size1 = (_freeRam[i]._address + _freeRam[i]._size) - (address + size);
|
|
if(!updateFreeRamList(i, address0, size0, address1, size1)) break;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if(printError) fprintf(stderr, "Memory::takeFreeRAM() : Memory at 0x%04x already in use : your request : 0x%04x %d\n", address, address, size);
|
|
return false;
|
|
}
|
|
|
|
bool getFreeRAMLargest(uint16_t& address, int& size)
|
|
{
|
|
// Sort entries from highest size to lowest size
|
|
std::sort(_freeRam.begin(), _freeRam.end(), [](const RamEntry& ramEntryA, const RamEntry& ramEntryB)
|
|
{
|
|
int sizeA = ramEntryA._size;
|
|
int sizeB = ramEntryB._size;
|
|
return (sizeA > sizeB);
|
|
});
|
|
|
|
if(_freeRam.size() == 0) return false;
|
|
|
|
address = _freeRam[0]._address;
|
|
size = _freeRam[0]._size;
|
|
|
|
updateFreeRAM();
|
|
|
|
return true;
|
|
}
|
|
|
|
// Attempts to returns RAM request of a given size : withinPage specifiec no page boundary crossings : oddeven specifies type of address returned, 0=don't care, 1=even, 2=odd
|
|
bool getFreeRAM(FitType fitType, int size, uint16_t min, uint16_t max, uint16_t& address, bool withinPage, ParityType oddEven)
|
|
{
|
|
switch(fitType)
|
|
{
|
|
case FitAscending:
|
|
{
|
|
for(int j=0; j<int(_freeRam.size()); j++)
|
|
{
|
|
for(int i=0; i<_freeRam[j]._size; i++)
|
|
{
|
|
uint16_t left = uint16_t(_freeRam[j]._size - i);
|
|
uint16_t addr = uint16_t(_freeRam[j]._address + i);
|
|
|
|
if(addr >= min && addr + size-1 <= max && size <= left)
|
|
{
|
|
// Skip if request must be within a page and it isn't
|
|
if(withinPage && HI_BYTE(addr) != HI_BYTE(addr + size-1)) continue;
|
|
|
|
// Skip if address doesn't meet odd/even requirements
|
|
if(oddEven)
|
|
{
|
|
if((addr & 1) != oddEven - ParityEven) continue;
|
|
}
|
|
|
|
address = addr;
|
|
return takeFreeRAM(addr, size);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FitDescending:
|
|
{
|
|
for(int j=int(_freeRam.size())-1; j>=0; j--)
|
|
{
|
|
for(int i=_freeRam[j]._size-1; i>=0; i--)
|
|
{
|
|
uint16_t left = uint16_t(_freeRam[j]._size - i);
|
|
uint16_t addr = uint16_t(_freeRam[j]._address + i);
|
|
|
|
if(addr >= min && addr + size-1 <= max && size <= left)
|
|
{
|
|
// Skip if request must be within a page and it isn't
|
|
if(withinPage && HI_BYTE(addr) != HI_BYTE(addr + size-1)) continue;
|
|
|
|
// Skip if address doesn't meet odd/even requirements
|
|
if(oddEven)
|
|
{
|
|
if((addr & 1) != oddEven - ParityEven) continue;
|
|
}
|
|
|
|
address = addr;
|
|
return takeFreeRAM(addr, size);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
fprintf(stderr, "Memory::getFreeRAM() : No free RAM found of size %d bytes\n", size);
|
|
return false;
|
|
}
|
|
|
|
// Return free RAM chunks in order of request : withinPage specifiec no page boundary crossings
|
|
bool getFreeRAM(FitType fitType, uint16_t addrMin, uint16_t addrMax, uint16_t sizeMin, uint16_t& address, uint16_t inSize, uint16_t& outSize, bool withinPage)
|
|
{
|
|
switch(fitType)
|
|
{
|
|
case FitAscending:
|
|
{
|
|
for(int i=0; i<int(_freeRam.size()); i++)
|
|
{
|
|
outSize = uint16_t(_freeRam[i]._size);
|
|
address = uint16_t(_freeRam[i]._address);
|
|
|
|
// Skip if size is too small or request crosses a page boundary
|
|
if((outSize < sizeMin) || (withinPage && HI_BYTE(address) != HI_BYTE(address + outSize-1))) continue;
|
|
|
|
if(address >= addrMin && address + outSize-1 <= addrMax)
|
|
{
|
|
if(inSize < outSize) outSize = inSize;
|
|
return takeFreeRAM(address, outSize);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FitDescending:
|
|
{
|
|
for(int i=int(_freeRam.size())-1; i>=0; i--)
|
|
{
|
|
outSize = uint16_t(_freeRam[i]._size);
|
|
address = uint16_t(_freeRam[i]._address);
|
|
|
|
// Skip if size is too small or request crosses a page boundary
|
|
if((outSize < sizeMin) || (withinPage && HI_BYTE(address) != HI_BYTE(address + outSize-1))) continue;
|
|
|
|
if(address >= addrMin && address + outSize-1 <= addrMax)
|
|
{
|
|
if(inSize < outSize) outSize = inSize;
|
|
return takeFreeRAM(address, outSize);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
fprintf(stderr, "Memory::getFreeRAM() : No free RAM found within 0x%04x and 0x%04x\n", addrMin, addrMax);
|
|
return false;
|
|
}
|
|
|
|
void printFreeRamList(SortType sortType)
|
|
{
|
|
// Make a local copy so that we don't change the sort on the real free RAM list
|
|
std::vector<RamEntry> freeRam = _freeRam;
|
|
|
|
switch(sortType)
|
|
{
|
|
// Sort entries from lowest address to highest address
|
|
case AddressAscending:
|
|
{
|
|
std::sort(freeRam.begin(), freeRam.end(), [](const RamEntry& ramEntryA, const RamEntry& ramEntryB)
|
|
{
|
|
uint16_t addressA = ramEntryA._address;
|
|
uint16_t addressB = ramEntryB._address;
|
|
return (addressA < addressB);
|
|
});
|
|
}
|
|
break;
|
|
|
|
// Sort entries from highest address to lowest address
|
|
case AddressDescending:
|
|
{
|
|
std::sort(freeRam.begin(), freeRam.end(), [](const RamEntry& ramEntryA, const RamEntry& ramEntryB)
|
|
{
|
|
uint16_t addressA = ramEntryA._address;
|
|
uint16_t addressB = ramEntryB._address;
|
|
return (addressA > addressB);
|
|
});
|
|
}
|
|
break;
|
|
|
|
// Sort entries from lowest size to highest size
|
|
case SizeAscending:
|
|
{
|
|
std::sort(freeRam.begin(), freeRam.end(), [](const RamEntry& ramEntryA, const RamEntry& ramEntryB)
|
|
{
|
|
int sizeA = ramEntryA._size;
|
|
int sizeB = ramEntryB._size;
|
|
return (sizeA < sizeB);
|
|
});
|
|
}
|
|
break;
|
|
|
|
// Sort entries from highest size to lowest size
|
|
case SizeDescending:
|
|
{
|
|
std::sort(freeRam.begin(), freeRam.end(), [](const RamEntry& ramEntryA, const RamEntry& ramEntryB)
|
|
{
|
|
int sizeA = ramEntryA._size;
|
|
int sizeB = ramEntryB._size;
|
|
return (sizeA > sizeB);
|
|
});
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
int totalFree = 0;
|
|
fprintf(stderr, "\n");
|
|
for(int i=0; i<int(freeRam.size()); i++)
|
|
{
|
|
totalFree += freeRam[i]._size;
|
|
fprintf(stderr, "Memory::printFreeRamList() : %3d : 0x%04x %3d\n", i, freeRam[i]._address, freeRam[i]._size);
|
|
}
|
|
fprintf(stderr, "Memory::printFreeRamList() : Expected %5d : Found %5d\n", _sizeFreeRAM, totalFree);
|
|
}
|
|
}
|