package okean240 import ( "os" log "github.com/sirupsen/logrus" ) const RamBlockSize = 16 * 1024 // 16Kb const RamSize = 512 * 1024 // 512kb (16xRU7) or 128k (16xRU5) const RamBlocks = RamSize / RamBlockSize // 32 Ram blocks for 512k, 8 for 128k const RamDefaultInitPattern = 0x3f const RamWindows = 4 const RSTBit = 0x20 const ROMDisBit = 0x10 const AccessHiBit = 0x01 const ExtRamAddrBits = 0x0E const ( WindowNo0 = iota WindowNo1 WindowNo2 WindowNo3 ) type RamBlock struct { id byte memory [RamBlockSize]byte } type Memory struct { allMemory [RamBlocks]*RamBlock memoryWindow [RamWindows]*RamBlock rom0 RamBlock // monitor + monitor rom1 RamBlock // cpm + monitor config byte } type MemoryInterface interface { // Init - Initialize memory at "computer started" Init(rom0 string, rom1 string) // Configure - Set memory configuration Configure(value byte) // M1MemRead Read byte from memoryWindow for specified address M1MemRead(addr uint16) byte // MemRead Read byte from memoryWindow for specified address MemRead(addr uint16) byte // MemWrite Write byte to memoryWindow to specified address MemWrite(addr uint16, val byte) } func (m *Memory) Init(monFile string, cmpFile string) { // empty RAM var id byte = 0 for block := range m.allMemory { rb := RamBlock{} rb.id = id id++ for addr := 0; addr < RamBlockSize; addr++ { rb.memory[addr] = RamDefaultInitPattern } m.allMemory[block] = &rb } // Command ROM files and init ROM0,1 // Read the entire file into a byte slice rom0bin, err := os.ReadFile(monFile) if err != nil { log.Fatal(err) } rom1bin, err := os.ReadFile(cmpFile) if err != nil { log.Fatal(err) } m.rom0 = RamBlock{} m.rom0.id = 100 m.rom1 = RamBlock{} m.rom0.id = 101 half := RamBlockSize / 2 for i := 0; i < half; i++ { // mon+mon m.rom0.memory[i] = rom0bin[i] m.rom0.memory[i+half] = rom0bin[i] // cp/m + mon m.rom1.memory[i] = rom1bin[i] m.rom1.memory[i+half] = rom0bin[i] } // Config mem with RST pin Hi m.Configure(RSTBit) } // Configure - Configure memoryWindow windows func (m *Memory) Configure(value byte) { m.config = value if m.config&RSTBit != 0 { // RST bit set just after System RESET // All memoryWindow windows points to ROM0 (monitor) for i := 0; i < RamWindows; i++ { m.memoryWindow[i] = &m.rom0 } } else { // Map RAM blocks to windows sp := (m.config & ExtRamAddrBits) << 1 // 0,4,8,12 for i := byte(0); i < RamWindows; i++ { m.memoryWindow[i] = m.allMemory[sp+i] } // Map two hi windows to low windows in 32k flag set if m.config&AccessHiBit == 1 { m.memoryWindow[WindowNo0] = m.memoryWindow[WindowNo2] m.memoryWindow[WindowNo1] = m.memoryWindow[WindowNo3] } // If ROM enabled, map ROM to last window if m.config&ROMDisBit == 0 { // If ROM enabled, CP/M + Mon at window 3 [0xC000:0xFFFF] m.memoryWindow[WindowNo3] = &m.rom1 } } } func (m *Memory) M1MemRead(addr uint16) byte { return m.memoryWindow[addr>>14].memory[addr&0x3fff] } func (m *Memory) MemRead(addr uint16) byte { return m.memoryWindow[addr>>14].memory[addr&0x3fff] } func (m *Memory) MemWrite(addr uint16, val byte) { window := addr >> 14 offset := addr & 0x3fff if window == 1 { //log.Debugf("at vram [%2x][%4x]", window, offset) } m.memoryWindow[window].memory[offset] = val }