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.
hal/hal/paging.go

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
}