boot‐room - retrotruestory/M1DEV GitHub Wiki
From the documentation, I now understand that:
-
Boot Process:
- Starts at 0x0000 in supervisor mode with paging disabled
- Memory map with paging disabled: 64K device space with overlapping ROM/RAM
- First 32K (0x0000-0x3FFF): Overlapping EPROM and SRAM (selectable via switch)
- 0x4000-0x7FFF: Always SRAM
- 0xFF80-0xFFFF: Memory-mapped I/O devices
-
Memory-Mapped I/O:
- 0xFFF0-0xFFFF: UART #0
- 0xFFE0-0xFFEF: UART #1
- 0xFFD0-0xFFDF: Real-time clock
- 0xFFC0-0xFFCF: 2-digit hex display
- 0xFFB0-0xFFBF: IDE interface
- 0xFFA0-0xFFAF: Front panel switch block
-
Booting Sequence:
- Bootloader loads and initializes paging
- Then loads "Milo" (Minix Loader) or monitor
- Milo loads Minix from disk
Let's now implement the boot ROM to correctly initialize the system and start the boot process:
#ifndef BOOT_ROM_H
#define BOOT_ROM_H
#include <cstdint>
#include <string>
#include <vector>
#include <memory>
class Memory;
class CPU;
class BootROM {
public:
BootROM(std::shared_ptr<Memory> memory, std::shared_ptr<CPU> cpu);
~BootROM();
// Load the boot ROM into memory
bool loadROM(const std::string& filename);
// Generate a simple boot ROM for testing
bool generateTestROM();
private:
std::shared_ptr<Memory> memory;
std::shared_ptr<CPU> cpu;
std::vector<uint8_t> romData;
// Internal helper methods
bool loadROMFromFile(const std::string& filename);
void createSimpleBootLoader();
};
#endif // BOOT_ROM_H
#include "boot_rom.h"
#include "memory.h"
#include "cpu.h"
#include <fstream>
#include <iostream>
BootROM::BootROM(std::shared_ptr<Memory> memory, std::shared_ptr<CPU> cpu)
: memory(memory), cpu(cpu) {
}
BootROM::~BootROM() {
}
bool BootROM::loadROM(const std::string& filename) {
return loadROMFromFile(filename);
}
bool BootROM::generateTestROM() {
createSimpleBootLoader();
// Load the test ROM into memory at address 0x0000
if (memory) {
for (size_t i = 0; i < romData.size(); ++i) {
memory->write8(static_cast<uint16_t>(i), romData[i]);
}
return true;
}
return false;
}
bool BootROM::loadROMFromFile(const std::string& filename) {
std::ifstream file(filename, std::ios::binary);
if (!file) {
std::cerr << "Failed to open ROM file: " << filename << std::endl;
return false;
}
// Get the file size
file.seekg(0, std::ios::end);
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
// Resize the ROM data buffer
romData.resize(size);
// Read the file
if (!file.read(reinterpret_cast<char*>(romData.data()), size)) {
std::cerr << "Failed to read ROM file: " << filename << std::endl;
return false;
}
// Load the ROM into memory at address 0x0000
if (memory) {
for (size_t i = 0; i < romData.size(); ++i) {
if (i < 0x4000) { // Only load into ROM area (0x0000-0x3FFF)
memory->write8(static_cast<uint16_t>(i), romData[i]);
}
}
return true;
}
return false;
}
void BootROM::createSimpleBootLoader() {
// Create a minimal boot sequence that initializes the hardware
// and reads sector 2 from the disk to find the image table
// Based on the documentation, replicating basic bootloader behavior
romData = {
// Interrupt vector table
0x8B, 0x20, // sbr +32 ; Branch over interrupt vector
0x00, 0x00, // defw 0x0000 ; IRQ5 (RTC)
0x00, 0x00, // defw 0x0000 ; IRQ4 (unassigned)
0x00, 0x00, // defw 0x0000 ; IRQ3 (UART0)
0x00, 0x00, // defw 0x0000 ; IRQ2 (UART1)
0x00, 0x00, // defw 0x0000 ; IRQ1 (IDE)
0x00, 0x00, // defw 0x0000 ; IRQ0 (unassigned)
0x00, 0x00, // defw 0x0000 ; DMA req
0x00, 0x00, // defw 0x0000 ; SYSCALL
0x00, 0x00, // defw 0x0000 ; 0x9
0x00, 0x00, // defw 0x0000 ; Overflow trap
0x00, 0x00, // defw 0x0000 ; Privilege violation
0x00, 0x00, // defw 0x0000 ; Breakpoint
0x00, 0x00, // defw 0x0000 ; Non-writeable page
0x00, 0x00, // defw 0x0000 ; Page not present
0x00, 0x00, // defw 0x0000 ; 0xF
// Initialization code
0x90, 0x00, 0x00, // ld.16 a,0x0000 ; Clear A
0xD0, // copy msw,a ; Set MSW (interrupts off, system mode)
0x90, 0x80, 0x00, // ld.16 a,0x8000 ; Stack address
0xD1, // copy sp,a ; Set stack pointer
0x90, 0x00, 0x00, // ld.16 a,0x0000 ; Data pointer base
0xD2, // copy dp,a ; Set data pointer
// Initialize UART
0x90, 0xFF, 0xF0, // ld.16 a,0xFFF0 ; UART0 base address
0x91, 0x80, // ld.8 a,0x80 ; DLAB=1
0xD7, 0x03, // st.8 a,(0x03) ; Set DLAB in LCR
0x91, 0x0C, // ld.8 a,0x0C ; Divisor for 9600 baud
0xD7, 0x00, // st.8 a,(0x00) ; Set divisor LSB
0x91, 0x00, // ld.8 a,0x00
0xD7, 0x01, // st.8 a,(0x01) ; Set divisor MSB
0x91, 0x03, // ld.8 a,0x03 ; 8N1, DLAB=0
0xD7, 0x03, // st.8 a,(0x03) ; Set LCR
// Initialize IDE controller
0x90, 0xFF, 0xB0, // ld.16 a,0xFFB0 ; IDE base address
0x91, 0x04, // ld.8 a,0x04 ; Reset command
0xD7, 0x07, // st.8 a,(0x07) ; Send reset command
// Wait loop (simple delay)
0x90, 0x10, 0x00, // ld.16 a,0x1000 ; Delay count
0x41, // dec a ; Decrement
0x22, 0xFC, // brnz -4 ; Loop until zero
// Select master drive
0x91, 0xE0, // ld.8 a,0xE0 ; LBA mode, master drive
0xD7, 0x06, // st.8 a,(0x06) ; Write to drive select register
// Read sector 2 (image table)
0x91, 0x01, // ld.8 a,0x01 ; Read 1 sector
0xD7, 0x02, // st.8 a,(0x02) ; Set sector count
0x91, 0x02, // ld.8 a,0x02 ; LBA low (sector 2)
0xD7, 0x03, // st.8 a,(0x03) ; Set LBA low
0x91, 0x00, // ld.8 a,0x00 ; LBA mid
0xD7, 0x04, // st.8 a,(0x04) ; Set LBA mid
0x91, 0x00, // ld.8 a,0x00 ; LBA high
0xD7, 0x05, // st.8 a,(0x05) ; Set LBA high
0x91, 0x20, // ld.8 a,0x20 ; READ SECTORS command
0xD7, 0x07, // st.8 a,(0x07) ; Execute command
// Display "Magic-1" on UART
0x90, 0x00, 0x80, // ld.16 a,0x0080 ; Address of message
0x91, 0x4D, // ld.8 a,'M'
0xD7, 0xF0, // st.8 a,(0xFFF0) ; Output to UART
0x91, 0x61, // ld.8 a,'a'
0xD7, 0xF0, // st.8 a,(0xFFF0) ; Output to UART
0x91, 0x67, // ld.8 a,'g'
0xD7, 0xF0, // st.8 a,(0xFFF0) ; Output to UART
0x91, 0x69, // ld.8 a,'i'
0xD7, 0xF0, // st.8 a,(0xFFF0) ; Output to UART
0x91, 0x63, // ld.8 a,'c'
0xD7, 0xF0, // st.8 a,(0xFFF0) ; Output to UART
0x91, 0x2D, // ld.8 a,'-'
0xD7, 0xF0, // st.8 a,(0xFFF0) ; Output to UART
0x91, 0x31, // ld.8 a,'1'
0xD7, 0xF0, // st.8 a,(0xFFF0) ; Output to UART
0x91, 0x0D, // ld.8 a,'\r'
0xD7, 0xF0, // st.8 a,(0xFFF0) ; Output to UART
0x91, 0x0A, // ld.8 a,'\n'
0xD7, 0xF0, // st.8 a,(0xFFF0) ; Output to UART
// Stop execution
0x00 // halt ; Stop execution
};
}