package hal const PageSize = 1024 var defaultRegister uint16 = 0 type Page struct { Referenced bool RegisterAddress *uint16 PageNumber uint16 } // PageTable uses key as page number type PageTable map[uint16]*Page type Address uint16 type PageErrorHandler interface { handlePageError(address Address, write bool) } type MMU struct { module *Module PageTable PageTable LoadedPages []*Page // pseudo hard drive which holds all pages drive map[uint16][]float64 pageErrorHandler PageErrorHandler } func (address Address) PageNumber() uint16 { return uint16(address) >> 10 } func (address Address) Offset() uint16 { return uint16(address) & 0x03FF } func NewMMU(module *Module, pageErrorHandler PageErrorHandler) *MMU { pageCount := uint16(PageSize / 16) drive := make(map[uint16][]float64) pageTable := make(PageTable) for i := uint16(0); i < pageCount; i++ { drive[i] = make([]float64, PageSize, PageSize) pageTable[i] = &Page{ Referenced: false, RegisterAddress: nil, PageNumber: i, } } return &MMU{ module: module, PageTable: pageTable, LoadedPages: make([]*Page, 0, 4), drive: drive, pageErrorHandler: pageErrorHandler, } } func (mmu *MMU) Read(address Address) float64 { // 10 bit for one register in a page // 6 bit to address the page // 000010 | 0000000011 // -> Page 2, offset 3 // Find the page with that number page := mmu.PageTable[address.PageNumber()] // Page not loaded in "physical" register if page.RegisterAddress == nil { // Page Error mmu.pageErrorHandler.handlePageError(address, false) mmu.Load(page) } page.Referenced = true return mmu.module.register[page.RegisterOffset()+int(address.Offset())] } func (mmu *MMU) Write(address Address, content float64) { // 10 bit for one register in a page // 6 bit to address the page // 000010 | 0000000011 // -> Page 2, offset 3 // Find the page with that number page := mmu.PageTable[address.PageNumber()] // Page not loaded in "physical" register if page.RegisterAddress == nil { // Page Error mmu.pageErrorHandler.handlePageError(address, true) mmu.Load(page) } page.Referenced = true mmu.module.register[page.RegisterOffset()+int(address.Offset())] = content } // func (mmu *MMU) Load(page *Page) { if len(mmu.LoadedPages) >= 4 { for index, loadedPage := range mmu.LoadedPages { if loadedPage.Referenced { loadedPage.Referenced = false continue } mmu.movePageToDrive(loadedPage) mmu.LoadedPages = append(mmu.LoadedPages[:index], mmu.LoadedPages[index+1:]...) break } // Everything is referenced, we need to remove one if len(mmu.LoadedPages) == 4 { mmu.movePageToDrive(mmu.LoadedPages[3]) mmu.LoadedPages = mmu.LoadedPages[:3] } } mmu.loadPageFromDrive(page) mmu.LoadedPages = append(mmu.LoadedPages, page) } func (mmu *MMU) findEmptyRegisterAddress() *uint16 { var i uint16 for i = 0; i < uint16(len(mmu.module.register)/PageSize); i++ { var isTaken bool for _, loadedPage := range mmu.LoadedPages { if *loadedPage.RegisterAddress == i { isTaken = true break } } if isTaken { continue } return &i } return &defaultRegister } func (mmu *MMU) movePageToDrive(page *Page) { for i := 0; i < PageSize-1; i++ { mmu.drive[page.PageNumber][i] = mmu.module.register[i+page.RegisterOffset()] } page.Referenced = false page.RegisterAddress = nil } func (mmu *MMU) loadPageFromDrive(page *Page) { page.RegisterAddress = mmu.findEmptyRegisterAddress() for i := 0; i < PageSize-1; i++ { mmu.module.register[i+page.RegisterOffset()] = mmu.drive[page.PageNumber][i] } } func (page *Page) RegisterOffset() int { return int(*page.RegisterAddress) * PageSize }