#include "mm.h" #include "toolbox.h" #include #include #include #include #include #include #include #include using ToolBox::Log; using namespace ToolBox::Errors; namespace { mplite_t pool; uint8_t *Memory; uint32_t MemorySize; // queue of free Handles std::deque HandleQueue; // map of ptr -> size std::map PtrMap; struct HandleInfo { uint32_t address; uint32_t size; bool locked; bool purgeable; HandleInfo(uint32_t a = 0, uint32_t s = 0) : address(a), size(s), locked(false), purgeable(false) {} }; // map of handle -> size [? just use Ptr map?] std::map HandleMap; inline uint16_t SetMemError(uint16_t error) { memoryWriteWord(error, 0x0220); return error; } bool alloc_handle_block() { const unsigned HandleCount = 128; // 512 bytes of handle blocks. uint8_t *block = (uint8_t *)mplite_malloc(&pool, sizeof(uint32_t) * HandleCount); if (!block) return false; uint32_t hh = block - Memory; uint32_t end = hh + 128 * sizeof(uint32_t); for ( ; hh < end; hh += sizeof(uint32_t)) { HandleQueue.push_back(hh); } return true; } } namespace MM { bool Init(uint8_t *memory, uint32_t memorySize, uint32_t reserved) { int ok; Memory = memory; MemorySize = memorySize; ok = mplite_init(&pool, memory + reserved, memorySize - reserved, 32, NULL); if (ok != MPLITE_OK) return false; // allocate a handle master block... if (!alloc_handle_block()) return false; return true; } namespace Native { void PrintMemoryStats() { mplite_print_stats(&pool, std::puts); } uint16_t NewPtr(uint32_t size, bool clear, uint32_t &mcptr) { // native pointers. mcptr = 0; if (size == 0) return 0; uint8_t *ptr = nullptr; ptr = (uint8_t *)mplite_malloc(&pool, size); if (!ptr) { return SetMemError(memFullErr); } if (clear) std::memset(ptr, 0, size); mcptr = ptr - Memory; PtrMap.emplace(std::make_pair(mcptr, size)); return SetMemError(0); } uint16_t DisposePtr(uint32_t mcptr) { auto iter = PtrMap.find(mcptr); if (iter == PtrMap.end()) return SetMemError(memWZErr); PtrMap.erase(iter); uint8_t *ptr = mcptr + Memory; mplite_free(&pool, ptr); return SetMemError(0); } uint16_t NewHandle(uint32_t size, bool clear, uint32_t &handle, uint32_t &mcptr) { uint8_t *ptr; uint32_t hh; handle = 0; mcptr = 0; if (!HandleQueue.size()) { if (!alloc_handle_block()) { return SetMemError(memFullErr); } } hh = HandleQueue.front(); HandleQueue.pop_front(); ptr = nullptr; if (size) { ptr = (uint8_t *)mplite_malloc(&pool, size); if (!ptr) { HandleQueue.push_back(hh); return SetMemError(memFullErr); } mcptr = ptr - Memory; if (clear) std::memset(ptr, 0, size); } // need a handle -> ptr map? HandleMap.emplace(std::make_pair(hh, HandleInfo(mcptr, size))); memoryWriteLong(mcptr, hh); handle = hh; return SetMemError(0); } uint16_t NewHandle(uint32_t size, bool clear, uint32_t &handle) { uint32_t ptr; return NewHandle(size, clear, handle, ptr); } uint16_t DisposeHandle(uint32_t handle) { auto iter = HandleMap.find(handle); if (iter == HandleMap.end()) return SetMemError(memWZErr); HandleInfo info = iter->second; HandleMap.erase(iter); uint8_t *ptr = info.address + Memory; mplite_free(&pool, ptr); HandleQueue.push_back(handle); return SetMemError(0); } } uint16_t BlockMove(uint16_t trap) { /* * on entry: * A0 Pointer to source * A1 Pointer to destination * D0 Number of bytes to copy * * on exit: * A0 Address of the new block or NIL * D0 Result code * */ uint32_t source = cpuGetAReg(0); uint32_t dest = cpuGetAReg(1); uint32_t count = cpuGetDReg(0); Log("%04x BlockMove(%08x, %08x, %08x)\n", trap, source, dest, count); // TODO -- 32-bit clean? // TODO -- verify within MemorySize? #if 0 if (source == 0 || dest == 0 || count == 0) return 0; #endif std::memmove(Memory + dest, Memory + source, count); return 0; } uint32_t CompactMem(uint16_t trap) { // todo -- add function to check pool for largest block? /* * on entry: * D0: cbNeeded (long word) * * on exit: * D0: function result (long word) * */ uint32_t cbNeeded = cpuGetDReg(0); Log("%04x CompactMem(%08x)\n", trap, cbNeeded); // todo -- add mplite call to return total free mem, largest block. return 0x0f0000; } uint16_t MoveHHi(uint16_t trap) { /* * on entry: * A0: Handle to move * * on exit: * D0: Result code. * */ uint32_t theHandle = cpuGetAReg(0); Log("%04x MoveHHi(%08x)\n", trap, theHandle); // check if it's valid. auto iter = HandleMap.find(theHandle); if (iter == HandleMap.end()) return SetMemError(memWZErr); return SetMemError(0); } #pragma mark Pointers uint16_t NewPtr(uint16_t trap) { /* * on entry: * D0 Number of logical bytes requested * * on exit: * A0 Address of the new block or NIL * D0 Result code * */ bool clear = trap & (1 << 9); //bool sys = trap & (1 << 10); uint32_t size = cpuGetDReg(0); Log("%04x NewPtr(%08x)\n", trap, size); // todo -- separate pools for sys vs non-sys? // todo -- NewPtr(0) -- null or empty ptr? uint32_t mcptr; uint16_t error; error = Native::NewPtr(size, clear, mcptr); cpuSetAReg(0, mcptr); return error; //SetMemError(error); } uint16_t DisposePtr(uint16_t trap) { /* * on entry: * A0 Pointer to the nonrelocatable block to be disposed of * * on exit: * D0 Result code * */ uint32_t mcptr = cpuGetAReg(0); Log("%04x DisposePtr(%08x)\n", trap, mcptr); uint16_t error; error = Native::DisposePtr(mcptr); return error; //SetMemError(error); } uint32_t GetPtrSize(uint16_t trap) { /* * on entry: * A0 pointer * * on exit: * D0 size (32-bit) or error code * */ uint32_t mcptr = cpuGetAReg(0); Log("%08x GetPtrSize(%08x)\n", trap, mcptr); auto iter = PtrMap.find(mcptr); if (iter == PtrMap.end()) return SetMemError(memWZErr); return iter->second; } uint16_t SetPtrSize(uint16_t trap) { /* * on entry: * A0 pointer * D0 new size * * on exit: * D0 Result code * */ uint32_t mcptr = cpuGetAReg(0); uint32_t newSize = cpuGetDReg(0); Log("%08x SetPtrSize(%08x, %08x)\n", trap, mcptr, newSize); auto iter = PtrMap.find(mcptr); if (iter == PtrMap.end()) return SetMemError(memWZErr); uint8_t *ptr = mcptr + Memory; if (mplite_resize(&pool, ptr, newSize) < 0) { return SetMemError(memFullErr); } // update the size. iter->second = newSize; return SetMemError(0); } #pragma mark Handles uint16_t NewHandle(uint16_t trap) { /* * on entry: * D0 Number of logical bytes requested * * on exit: * A0 Address of the new handle or NIL * D0 Result code * */ uint32_t hh = 0; uint16_t error; bool clear = trap & (1 << 9); //bool sys = trap & (1 << 10); uint32_t size = cpuGetDReg(0); Log("%04x NewHandle(%08x)\n", trap, size); error = Native::NewHandle(size, clear, hh); cpuSetAReg(0, hh); return error; } uint16_t DisposeHandle(uint16_t trap) { /* * on entry: * A0 Handle to be disposed of * * on exit: * D0 Result code * */ uint32_t hh = cpuGetAReg(0); Log("%04x DisposeHandle(%08x)\n", trap, hh); return Native::DisposeHandle(hh); } uint32_t GetHandleSize(uint16_t trap) { /* * on entry: * A0 handle * * on exit: * D0 size (32-bit) or error code * */ uint32_t hh = cpuGetAReg(0); Log("%08x GetHandleSize(%08x)\n", trap, hh); auto iter = HandleMap.find(hh); if (iter == HandleMap.end()) return SetMemError(memWZErr); return iter->second.size; } uint16_t SetHandleSize(uint16_t trap) { /* * on entry: * A0 pointer * D0 new size * * on exit: * D0 Result code * */ uint32_t hh = cpuGetAReg(0); uint32_t newSize = cpuGetDReg(0); Log("%04x SetHandleSize(%08x, %08x)\n", hh, newSize); auto iter = HandleMap.find(hh); if (iter == HandleMap.end()) return SetMemError(memWZErr); // todo -- if handle ptr is null, other logic? // todo -- if locked, can't move. auto &info = iter->second; // 0 - no change in size. if (info.size == newSize) return SetMemError(0); uint32_t mcptr = info.address; uint8_t *ptr = mcptr + Memory; // 1. - resizing to 0. if (!newSize) { if (info.locked) return SetMemError(memLockedErr); mplite_free(&pool, ptr); info.address = 0; info.size = 0; return SetMemError(0); } // 2. - resizing from 0. if (!ptr) { ptr = (uint8_t *)mplite_malloc(&pool, newSize); if (!ptr) return SetMemError(memFullErr); mcptr = ptr - Memory; info.address = mcptr; info.size = newSize; return SetMemError(0); } // 3. - locked if (info.locked) { if (mplite_resize(&pool, ptr, newSize) == MPLITE_OK) { info.size = newSize; return SetMemError(0); } return SetMemError(memFullErr); } // 4. - resize. ptr = (uint8_t *)mplite_realloc(&pool, ptr, newSize); if (ptr) { mcptr = ptr - Memory; info.address = mcptr; info.size = newSize; return SetMemError(0); } else { return SetMemError(memFullErr); } } #pragma mark Handle attributes // these are all nops for now. uint16_t HPurge(uint16_t trap) { /* * on entry: * A0 Handle * * on exit: * D0 Result code * */ uint32_t hh = cpuGetAReg(0); Log("%04x HPurge(%08x)\n", trap, hh); auto iter = HandleMap.find(hh); if (iter == HandleMap.end()) return SetMemError(memWZErr); iter->second.purgeable = true; return 0; } uint16_t HLock(uint16_t trap) { /* * on entry: * A0 Handle * * on exit: * D0 Result code * */ uint32_t hh = cpuGetAReg(0); Log("%04x HLock(%08x)\n", trap, hh); auto iter = HandleMap.find(hh); if (iter == HandleMap.end()) return SetMemError(memWZErr); iter->second.locked = true; return 0; } uint16_t HUnlock(uint16_t trap) { /* * on entry: * A0 Handle * * on exit: * D0 Result code * */ uint32_t hh = cpuGetAReg(0); Log("%04x HUnlock(%08x)\n", trap, hh); auto iter = HandleMap.find(hh); if (iter == HandleMap.end()) return SetMemError(memWZErr); iter->second.locked = false; return 0; } #pragma mark - OS Utility Routines uint16_t PtrToHand(uint16_t trap) { /* * on entry: * A0 source Pointer * D0 size * * on exit: * A0 destination pointer * D0 Result code * */ uint32_t mcptr = cpuGetAReg(0); uint32_t size = cpuGetDReg(0); Log("%04x PtrToHand(%08x, %08x)\n", trap, mcptr, size); uint32_t destHandle; uint32_t destPtr; uint32_t d0 = Native::NewHandle(size, false, destHandle, destPtr); if (d0 == 0) { std::memmove(memoryPointer(destPtr), memoryPointer(mcptr), size); } cpuSetAReg(0, destHandle); return d0; } #pragma mark - uint32_t StripAddress(uint16_t trap) { /* * on entry: * d0 Address to strip * * on exit: * D0 Address that has been stripped. * */ // TODO -- in 32-bit mode, this is a nop. // have a --24 / --32 flag? uint32_t address = cpuGetDReg(0); Log("%04x StripAddress(%08x)\n", trap, address); address &= 0x00ffffff; return address; } }