File:  [Isaki's NoNo m68k/m88k emulator] / nono / hd64180 / hd647180dev.cpp
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:06:01 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, HEAD
nono 1.7.0

//
// nono
// Copyright (C) 2023 nono project
// Licensed under nono-license.txt
//

//
// HD647180 I/O デバイス
//

#include "mpu64180.h"
#include "event.h"
#include "hd647180.h"
#include "scheduler.h"

//
// タイマー (Programmable Reload Timer)
//

// タイマーを開始する時 つまり TDE{0,1} の立ち上がりで呼ぶ。
// 該当チャンネルの running をセットしてから呼ぶこと。
void
MPU64180Device::EnableTimer()
{
	// すでに他方が開始しているなら、もう何もしなくていい
	if (timer[0].running && timer[1].running) {
		return;
	} else if (timer[0].running == false && timer[1].running == false) {
		PANIC("must be called when running");
	}

	// TDE を立ててから1回目の TDR の減算が始まるまでの時間は
	// 0 < t < 20φ と書いてあるので (HD647180.pdf p.112, Figure 13-3)、
	// タイマーの tick はタイマー開始時点からカウントし始める方式ではなく、
	// フリーランしている tick が来た時に処理をする方式のようだ。
	// これの開始点はおそらくリセット時なので、
	// 停止していたのを開始する時だけ次の tick までの時間を求める。

	uint64 now = scheduler->GetVirtTime() - timer_epoch;
	uint64 tick = now / (clock_tsec * 20);
	uint64 next = (tick + 1) * (clock_tsec * 20);

	timer_event->time = next - now;
	scheduler->StartEvent(timer_event);
}

// タイマー系の割り込み線の状態を変更する。
// HD647180.pdf p.112, Figure 13-5。
// TIEn, TIFn, IEF1 の変更でコールすること。
void
MPU64180Device::ChangeTimerInterrupt()
{
	for (int ch = 0; ch < timer.size(); ch++) {
		auto& t = timer[ch];
		if (t.tif != 0 && t.intr_enable && GetIEF1()) {
			AssertIntmap(HD647180::IntmapTimer(ch));
		} else {
			NegateIntmap(HD647180::IntmapTimer(ch));
		}
	}
}

// タイマーイベント
void
MPU64180Device::TimerCallback(Event *ev)
{
	// アクティブがなければ停止
	if (active_timer.empty()) {
		return;
	}

	// 少なくとも片方は有効
	for (auto tp : active_timer) {
		auto& t = *tp;
		if (__predict_false(t.count == 0)) {
			// 0 になった次の tick でリロード
			t.count = t.reload;
		} else {
			t.count--;
			// 0 になったら割り込みを上げる
			if (t.count == 0) {
				t.tif = 0x03;
				ChangeTimerInterrupt();
			}
		}
	}

	// タイマーへの入力は原クロックの20倍なので、
	// 厳密には 162.7604… * 20 = 3.255… [nsec] だが、
	// ここでは clock_tsec * 20 = 3.260   [nsec] とする。
	// この代入は本当は1回目だけでいいのだが。
	timer_event->time = clock_tsec * 20;
	scheduler->RestartEvent(ev);
}

// アクティブタイマーリストを(再)構成する。
// 数が少ないので毎回作り直す。
// running を変更するたびに呼ぶこと。
void
MPU64180Device::MakeActiveTimer()
{
	active_timer.clear();

	for (int ch = 0; ch < timer.size(); ch++) {
		auto& t = timer[ch];
		if (t.running) {
			active_timer.push_back(&t);
		}
	}
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.