|
|
nono 1.7.0
//
// nono
// Copyright (C) 2022 nono project
// Licensed under nono-license.txt
//
//
// NEWS の ROM エミュレーション (-X のみ対応)
//
// IODevice
// |
// +- ROMDevice (LoadROM()、ウェイト、マスク等を持つ)
// | +- PROMDevice (LUNA* の PROM)
// | +- IPLROM1Device (X680x0 の IPLROM 後半)
// | +- IPLROM2Device (X680x0 の IPLROM 前半)
// | +- CGROMDevice (X680x0 の CGROM)
// | |
// | +- ROMEmuDevice
// | +- LunaPROMEmuDevice (LUNA PROM エミュレーションの共通部分)
// | | +- Luna1PROMEmuDevice (LUNA-I の PROM エミュレーション)
// | | +- Luna88kPROMEmuDevice (LUNA-88K の PROM エミュレーション)
// | |
// | | +------------------+
// | +-| NewsROMEmuDevice | (NEWS の ROM エミュレーション)
// | | +------------------+
// | |
// | +- ROM30EmuDevice (X68030 の ROM30 エミュレーション)
// | +- Virt68kROMEmuDevice (virt-m68k の IPLROM 相当の何か)
// |
// +- PROM0Device (LUNA* のブートページ切り替え用プロキシ)
// +- IPLROM0Device (X680x0 のブートページ切り替え用プロキシ)
#include "romemu_news.h"
#include "mainapp.h"
#include "mainbus.h"
#include "mainram.h"
#include "memorystream.h"
#include "mpu680x0.h"
#include "newsfb.h"
#include "power.h"
// コンストラクタ
NewsROMEmuDevice::NewsROMEmuDevice()
: inherited(OBJ_PROM)
{
}
// デストラクタ
NewsROMEmuDevice::~NewsROMEmuDevice()
{
}
// 初期化
bool
NewsROMEmuDevice::Init()
{
// ROM 領域を用意。サイズは適当。
if (AllocROM(2048, 0) == false) {
return false;
}
mainram = GetMainRAMDevice();
MemoryStreamBE roms(imagebuf.get());
roms.Write4(0x00000400); // +00: リセットベクタ SP
roms.Write4(0xf8000400); // +04: リセットベクタ PC
// ベクタ
for (int i = 2; i < 256; i++) {
roms.Write4(0);
}
roms.SetOffset(32 * 4);
roms.Write4(0xf8000500); // Trap#0 で ROM のサービスコール
// Trap#15 は ROM モニタらしい。今の所停止するだけ。
// とりあえず Trap#1..#14 も全部ここへ向けておく。
for (int i = 1; i < 16; i++) {
roms.Write4(0xf8000600);
}
// リセット時に実行する命令
roms.SetOffset(0x400);
roms.Write2(0x2039); // move.l (ROMIO_INIT),d0
roms.Write4(ROMIO_INIT);
roms.Write2(0x2040); // move.l d0,a0 ; ジャンプ先を a0 にセット
roms.Write2(0x4ed0); // jmp (a0) ; ジャンプ
// ROM サービスコール
roms.SetOffset(0x500);
roms.Write2(0x0c80); // cmpi.l #3,d0
roms.Write4(0x00000003); //
roms.Write2(0x6708); // beq @f
roms.Write2(0x2039); // move.l (ROMIO_SYS),d0
roms.Write4(ROMIO_SYS); //
roms.Write2(0x4e73); // rte
//@@: ; Read は対応してないので
roms.Write4(0x4e722500); // stop #0x2500 ; STOP しつつ
roms.Write2(0x60fa); // bra @b ; 無限ループ。
roms.Write2(0x4e73); // rte
// ROM モニタの TRAP#n。はないのでここに来たらとりあえず停止する?
roms.SetOffset(0x600);
//@@:
roms.Write4(0x4e722700); // stop #0x2700 ; STOP しつつ
roms.Write2(0x60fa); // bra @b ; 無限ループ。
return true;
}
void
NewsROMEmuDevice::ResetHard(bool poweron)
{
// 起動時に RAM にリセットベクタを書き込んでおく。(手抜き)
mainram->WriteMem(0, &imagebuf[0], 8);
}
// ROMIO から読み出す。
// ROMIO が応答すべきでなければ (uint64)-1 を返す。
uint64
NewsROMEmuDevice::ReadROMIO(busaddr addr)
{
if (addr.GetSize() == 4) {
switch (addr.Addr()) {
case ROMIO_INIT:
return ROM_Init();
case ROMIO_SYS:
return ROM_Sys();
default:
break;
}
}
return (uint64)-1;
}
uint32
NewsROMEmuDevice::ROM_Init()
{
// -X file で指定されたホストファイルを読み込んでエントリポイント
// アドレスをこの Read アクセスの読み出し値として返す。
LoadInfo info(gMainApp.exec_file);
if (BootLoader::LoadExec(mainram, &info) == false) {
// エラーメッセージは表示済み。
auto power = GetPowerDevice();
power->PushPowerButton();
return -1;
}
putmsg(1, "Host program loaded. Entry point = $%08x", info.entry);
// ROMCONS のために VBR を設定
m68kreg& reg = GetMPU680x0Device(mpu)->reg;
reg.vbr = 0xf8000000;
// %d2 には MARK_END (たぶん最後の番地)
// %d4 にメモリサイズ。
// %d5 には読み込んだファイル名の先頭番地?
// %d6 に bootdev、NetBSD/news68k の場合は
// bootdev = $MCxUHPTT
// || |||++ Type? (SD=0)
// || ||+-- Partition?
// || |+--- Host: 機種? (NEWS1700 は 0)
// || +---- Unit?
// |+------ Controller?
// +------- magic ($5)
// 下位28ビット(7桁) は ROM から渡されるようだ。
// %d7 は boothowto。
reg.D[2] = info.sym;
reg.D[4] = (uint32)mainram->GetSize();
reg.D[6] = 0x50000000;
reg.D[7] = 0;
return info.entry;
}
uint32
NewsROMEmuDevice::ROM_Sys()
{
auto mainbus = GetMainbusDevice();
auto mpu680x0 = GetMPU680x0Device(mpu);
m68kreg& reg = mpu680x0->reg;
auto newsfb = GetNewsfbDevice();
if (reg.D[0] == SYS_write) {
uint32 a6 = reg.A[6];
uint32 fd = mainbus->HVRead4(a6 + 8);
uint32 buf = mainbus->HVRead4(a6 + 12);
uint32 len = mainbus->HVRead4(a6 + 16);
for (int i = 0; i < len; i++) {
busdata bd = mainbus->HVRead1(buf++);
if (bd.IsBusErr()) {
// XXX ?
return 0;
}
uint8 ch = bd.Data();
if (ch == '\r') {
continue;
}
newsfb->Putc(ch);
}
(void)fd;
return len;
}
VMPANIC("Unknown service call $%02x", reg.D[0]);
return 0;
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.