|
|
nono 1.7.0
//
// nono
// Copyright (C) 2024 nono project
// Licensed under nono-license.txt
//
//
// SSG (YM2149)
//
#include "ssg.h"
#include "monitor.h"
// コンストラクタ
SSGDevice::SSGDevice()
: inherited(OBJ_SSG)
{
monitor = gMonitorManager->Regist(ID_MONITOR_SSG, this);
monitor->SetCallback(&SSGDevice::MonitorScreen);
monitor->SetSize(45, 12);
}
// デストラクタ
SSGDevice::~SSGDevice()
{
}
// リセット
void
SSGDevice::ResetHard(bool poweron)
{
}
// A0 でアドレスポートとデータポートを分けるみたいなのではなく、
// 謎の BDIR と BC1 を R/W と A0 につないであるっぽい感じなので、
// トラップみたいな配置になっている。(紙資料も間違えている…)
//
// BDIR BC1 -> XP I/O
// 0 0 Inactive
// 0 1 Read 0x83 R
// 1 0 Write 0x82 W
// 1 1 Address 0x83 W
busdata
SSGDevice::Read1(uint32 addr)
{
uint32 offset = addr & 1;
uint32 data;
if (offset == 0) {
data = 0xff; // ?
} else {
// 読み込み
data = reg.Get(regno);
putlog(3, "R%x -> $%02x", regno, data);
}
return data;
}
busdata
SSGDevice::Write1(uint32 addr, uint32 data)
{
uint32 offset = addr & 1;
if (offset == 0) {
// データ書き込み
putlog(2, "R%x <- $%02x", regno, data);
switch (regno) {
case 0x0: // Frequency of channel A (Low)
reg.freq[0] &= 0xf00;
reg.freq[0] |= data;
break;
case 0x1: // Frequency of channel A (High)
reg.freq[0] &= 0x0ff;
reg.freq[0] |= data << 8;
break;
case 0x2: // Frequency of channel B (Low)
reg.freq[1] &= 0xf00;
reg.freq[1] |= data;
break;
case 0x3: // Frequency of channel B (High)
reg.freq[1] &= 0x0ff;
reg.freq[1] |= data << 8;
break;
case 0x4: // Frequency of channel C (Low)
reg.freq[2] &= 0xf00;
reg.freq[2] |= data;
break;
case 0x5: // Frequency of channel C (High)
reg.freq[2] &= 0x0ff;
reg.freq[2] |= data << 8;
break;
case 0x6: // Frequency of noise
reg.noise_freq = data & 0x1f;
break;
case 0x7: // I/O port and mixer settings
reg.settings = data;
break;
case 0x8: // Level of channel A
reg.ampmode[0] = (data & 0x10);
reg.amplevel[0] = data & 0x0f;
break;
case 0x9: // Level of channel B
reg.ampmode[1] = (data & 0x10);
reg.amplevel[1] = data & 0x0f;
break;
case 0xa: // Level of channel C
reg.ampmode[2] = (data & 0x10);
reg.amplevel[2] = data & 0x0f;
break;
case 0xb: // Frequency of envelope (Low)
reg.envelope_freq &= 0xff00;
reg.envelope_freq |= data;
break;
case 0xc: // Frequency of envelope (Hight)
reg.envelope_freq &= 0x00ff;
reg.envelope_freq |= data << 8;
break;
case 0xd: // Shape of envelope
reg.envelope_shape = data & 0x0f;
break;
case 0xe: // Data of I/O port A
reg.dataA = data;
break;
case 0xf: // Data of I/O port B
reg.dataB = data;
break;
default:
assertmsg(false, "regno=$%x", regno);
break;
}
} else {
// アドレス書き込み
regno = data & 0x0f;
putlog(4, "reg <- $%x", regno);
}
return 0;
}
busdata
SSGDevice::Peek1(uint32 addr)
{
return reg.Get(regno);
}
void
SSGDevice::MonitorScreen(Monitor *, TextScreen& screen)
{
// 012345678901234567890
// R1/R0 Freq ChA $xxx f=
// R3/R2 Freq ChB $xxx
// R5/R4 Freq ChC $xxx
// R6 FreqNoise $xx
// R7 Settings $xx(IB IA NC NB NA TC TB TA)
// R8 Level A $xx
// R9 Level B $xx
// Ra Level C $xx
// Rc/Rb EnvFreq $xxxx
// Rd EnvShape $xx
// Re Data A $xx
// Rf Data A $xx
uint f;
int y = 0;
screen.Clear();
SSG tmp = reg;
for (int i = 0; i < 3; i++) {
if (__predict_false(tmp.freq[i] == 0)) {
f = 0;
} else {
f = fMaster / (16 * tmp.freq[i]);
}
screen.Print(0, y, "R%u/R%u Freq Ch%c $%03x f=%u",
i * 2 + 1, i * 2, 'A' + i, tmp.freq[i], f);
y++;
}
if (__predict_false(tmp.noise_freq == 0)) {
f = 0;
} else {
f = fMaster / (16 * tmp.noise_freq);
}
screen.Print(0, y++, "R6 FreqNoise $%02x f=%u", tmp.noise_freq, f);
screen.Print(0, y, "R7 Settings $%02x(", tmp.settings);
screen.Puts(21, y, TA::OnOff(tmp.settings & 0x80), "IB");
screen.Puts(24, y, TA::OnOff(tmp.settings & 0x40), "IA");
screen.Puts(27, y, TA::OnOff(tmp.settings & 0x20), "NC");
screen.Puts(30, y, TA::OnOff(tmp.settings & 0x10), "NB");
screen.Puts(33, y, TA::OnOff(tmp.settings & 0x08), "NA");
screen.Puts(36, y, TA::OnOff(tmp.settings & 0x04), "TA");
screen.Puts(39, y, TA::OnOff(tmp.settings & 0x02), "TA");
screen.Puts(42, y, TA::OnOff(tmp.settings & 0x01), "TA");
screen.Putc(44, y, ')');
y++;
for (int i = 0; i < 3; i++) {
screen.Print(0, y++, "R%x Level %c $%02x",
8 + i, 'A' + i, tmp.GetLevelReg(i));
}
if (__predict_false(tmp.envelope_freq == 0)) {
f = 0;
} else {
f = fMaster / (256 * tmp.envelope_freq);
}
screen.Print(0, y++, "Rc/Rb EnvFreq $%04x f=%u", tmp.envelope_freq, f);
// モニタはソースコードのマルチバイト文字(UTF-8) を SJIS に変換する所は
// (まだ?)用意してないので、こっちで SJIS 文字列を用意する必要がある。
#define H "\x81\x50"
#define L "\x81\x51"
#define U "\x81\x5e"
#define D "\x81\x5f"
static const char *shape8[8] = {
D D D D D, // "\\\\\"
D L L L L, // "\____"
D U D U D, // "\/\/\"
D H H H H, // "\ ̄ ̄ ̄ ̄"
U U U U U, // "/////"
U H H H H, // "/ ̄ ̄ ̄ ̄"
U D U D U, // "/\/\/"
U L L L L, // "/____"
};
const char *shape;
uint es = tmp.envelope_shape;
if (es >= 8) {
shape = shape8[es - 8];
} else if (es >= 4) {
shape = shape8[7];
} else {
shape = shape8[1];
}
screen.Print(0, y++, "Rd EnvShape $%02x %s",
tmp.envelope_shape, shape);
screen.Print(0, y++, "Re Data A $%02x", tmp.dataA);
screen.Print(0, y++, "Rf Data B $%02x", tmp.dataB);
}
// レジスタの内容を副作用なく返す。
uint32
SSG::Get(uint n) const
{
switch (n) {
case 0x0:
return freq[0] & 0xff;
case 0x1:
return freq[0] >> 8;
case 0x2:
return freq[1] & 0xff;
case 0x3:
return freq[1] >> 8;
case 0x4:
return freq[2] & 0xff;
case 0x5:
return freq[2] >> 8;
case 0x6:
return noise_freq;
case 0x7:
return settings;
case 0x8:
return GetLevelReg(0);
case 0x9:
return GetLevelReg(1);
case 0xa:
return GetLevelReg(2);
case 0xb:
return envelope_freq & 0xff;
case 0xc:
return envelope_freq >> 8;
case 0xd:
return envelope_shape;
case 0xe:
return dataA;
case 0xf:
return dataB;
default:
__unreachable();
}
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.