You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
162 lines
3.7 KiB
162 lines
3.7 KiB
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
|
|
}
|
|
|