// This file is part of www.nand2tetris.org // and the book "The Elements of Computing Systems" // by Nisan and Schocken, MIT Press. // File name: projects/12/Memory.jack /** * This library provides two services: direct access to the computer's main * memory (RAM), and allocation and recycling of memory blocks. The Hack RAM * consists of 32,768 words, each holding a 16-bit binary number. */ class Memory { static Array ram; static Array heap; static Array freeList; /** Initializes the class. */ function void init() { let ram = 0; let heap = 2048; let freeList = heap; let freeList[0] = 0; // next let freeList[1] = 14334; // length return; } /** Returns the RAM value at the given address. */ function int peek(int address) { return ram[address]; } /** Sets the RAM value at the given address to the given value. */ function void poke(int address, int value) { let ram[address] = value; return; } /** Finds an available RAM block of the given size and returns * a reference to its base address. */ function int alloc(int size) { var Array previousBlock; var Array currentBlock; var Array resultBlock; var int currentBlockSize; var int remainingSize; var int currentBlockNext; if (freeList = 0) { return 0; } // XXX: I am not happy with having the whole code duplicated just because // we have the "special case" that we select the first block. // Using the first block is a special case, because we have to // update freeList instead of simply taking the block out of the list // by doing `let previousBlock.next = currentBlock.next`. let currentBlockSize = freeList[1]; if (currentBlockSize > size) { let remainingSize = currentBlockSize - size; let resultBlock = freeList; if (remainingSize < 4) { // If remainingSize is smaller than 4 it does not make sense to split // the block. Just return it as a whole. let freeList = freeList[0]; // point to freeList.next } else { // If remainingSize is big enough we split the block. In that case // we have to update the size of the block we return as well as // the block (aka the remainder of the current block). let resultBlock[1] = size; let freeList = resultBlock + 2 + size; let freeList[0] = resultBlock[0]; let freeList[1] = currentBlockSize - size - 2; } return resultBlock + 2; } let previousBlock = freeList; // The following excludes the first block which is what we want // because we handled it separately before. while (~(previousBlock[0] = 0)) { let currentBlock = previousBlock[0]; let currentBlockSize = currentBlock[1]; if (currentBlockSize > size) { let resultBlock = currentBlock; let remainingSize = currentBlockSize - size; if (remainingSize < 4) { let previousBlock[0] = resultBlock[0]; } else { let resultBlock[1] = size; let currentBlock = resultBlock + 2 + size; let currentBlock[0] = resultBlock[0]; let currentBlock[1] = currentBlockSize - size - 2; let previousBlock[0] = currentBlock; } return resultBlock; } else { let previousBlock = currentBlock; } } // Not enough heap available. Call defragment and try again. return 0; } /** De-allocates the given object (cast as an array) by making * it available for future allocations. */ function void deAlloc(Array o) { var Array currentBlock; if (freeList = 0) { let freeList = o - 2; } let currentBlock = freeList; while (~(currentBlock[0] = 0)) { let currentBlock = currentBlock[0]; } let currentBlock[0] = o - 2; return; } }