O2Hawk: begin ppu work

This commit is contained in:
alyosha-tas 2019-11-30 09:28:53 -05:00
parent 8ce403dab2
commit ecc9899f92
12 changed files with 289 additions and 236 deletions

View File

@ -709,7 +709,6 @@
</Compile>
<Compile Include="Consoles\Intellivision\PSG.cs" />
<Compile Include="Consoles\Intellivision\STIC.cs" />
<Compile Include="Consoles\Magnavox\Odyssey2\Audio.cs" />
<Compile Include="Consoles\Magnavox\Odyssey2\O2Hawk.cs" />
<Compile Include="Consoles\Magnavox\Odyssey2\O2Hawk.ICodeDataLog.cs" />
<Compile Include="Consoles\Magnavox\Odyssey2\O2Hawk.IDebuggable.cs">

View File

@ -159,9 +159,9 @@ namespace BizHawk.Emulation.Common.Components.I8048
case 0x85: OP_IMP(CL0); break; // CLR F0
case 0x86: JP_COND(!IRQPending, IDLE); break; // JP !IRQ
case 0x87: ILLEGAL(); break; // ILLEGAL
case 0x88: OP_PB_DIR(OR8, BUS); break; // OR BUS,#
case 0x89: OP_PB_DIR(OR8, P1); break; // OR P1,#
case 0x8A: OP_PB_DIR(OR8, P2); break; // OR P2,#
case 0x88: OP_PB_DIR(OR8, 0); break; // OR BUS,#
case 0x89: OP_PB_DIR(OR8, 1); break; // OR P1,#
case 0x8A: OP_PB_DIR(OR8, 2); break; // OR P2,#
case 0x8B: ILLEGAL(); break; // ILLEGAL
case 0x8C: OP_EXP_A(OR8, P4); break; // OR P4,A
case 0x8D: OP_EXP_A(OR8, P5); break; // OR P5,A
@ -175,9 +175,9 @@ namespace BizHawk.Emulation.Common.Components.I8048
case 0x95: OP_IMP(CM0); break; // COM F0
case 0x96: JP_COND(Regs[A] != 0, IDLE); break; // JP (A != 0)
case 0x97: OP_IMP(CLC); break; // CLR C
case 0x98: OP_PB_DIR(AND8, BUS); break; // AND BUS,#
case 0x99: OP_PB_DIR(AND8, P1); break; // AND P1,#
case 0x9A: OP_PB_DIR(AND8, P2); break; // AND P2,#
case 0x98: OP_PB_DIR(AND8, 0); break; // AND BUS,#
case 0x99: OP_PB_DIR(AND8, 1); break; // AND P1,#
case 0x9A: OP_PB_DIR(AND8, 2); break; // AND P2,#
case 0x9B: ILLEGAL(); break; // ILLEGAL
case 0x9C: OP_EXP_A(AND8, P4); break; // AND P4,A
case 0x9D: OP_EXP_A(AND8, P5); break; // AND P5,A

View File

@ -418,10 +418,18 @@ namespace BizHawk.Emulation.Common.Components.I8048
EA = false;
break;
case RD_P:
EA = false;
reg_d_ad = cur_instr[instr_pntr++];
reg_l_ad = cur_instr[instr_pntr++];
Regs[reg_d_ad] = ReadPort(reg_l_ad);
Regs[PX + reg_l_ad] = Regs[reg_d_ad];
break;
case WR_P:
WritePort(cur_instr[instr_pntr++], (byte)Regs[cur_instr[instr_pntr++]]);
reg_d_ad = cur_instr[instr_pntr++];
reg_l_ad = cur_instr[instr_pntr++];
WritePort(reg_d_ad, (byte)Regs[reg_l_ad]);
Regs[PX + reg_d_ad] = Regs[reg_l_ad];
break;
}
@ -492,7 +500,7 @@ namespace BizHawk.Emulation.Common.Components.I8048
public string TraceHeader
{
get { return "MC6809: PC, machine code, mnemonic, operands, registers (A, B, X, Y, US, SP, DP, CC), Cy, flags (EFHINZVC)"; }
get { return "MC6809: PC, machine code, mnemonic, operands, registers (A, B, X, Y, US, SP, DP, CC), Cy, flags (CAFBIFTTR)"; }
}
public TraceInfo State(bool disassemble = true)
@ -503,7 +511,7 @@ namespace BizHawk.Emulation.Common.Components.I8048
{
Disassembly = $"{(disassemble ? Disassemble(Regs[PC], ReadMemory, out notused) : "---")} ".PadRight(50),
RegisterInfo = string.Format(
"A:{0:X2} R0:{1:X2} R1:{2:X2} R2:{3:X2} R3:{4:X2} R4:{5:X2} R5:{6:X2} R6:{7:X2} R7:{8:X2} PSW:{9:X4} Cy:{10} {11}{12}{13}{14} {15}{16}{17}{18}{19}",
"A:{0:X2} R0:{1:X2} R1:{2:X2} R2:{3:X2} R3:{4:X2} R4:{5:X2} R5:{6:X2} R6:{7:X2} R7:{8:X2} PSW:{9:X4} Cy:{10} {11}{12}{13}{14}{15}{16}{17}{18}{19}{20}",
Regs[A],
Regs[(ushort)(R0 + RB)],
Regs[(ushort)(R1 + RB)],
@ -520,6 +528,7 @@ namespace BizHawk.Emulation.Common.Components.I8048
FlagF0 ? "F" : "f",
FlagBS ? "B" : "b",
IntEn ? "I" : "i",
TimIntEn ? "N" : "n",
F1 ? "F" : "f",
T0 ? "T" : "t",
T1 ? "T" : "t",

View File

@ -26,7 +26,8 @@ namespace BizHawk.Emulation.Common.Components.I8048
private void ResetInterrupts()
{
IntEn = true;
IntEn = false;
TimIntEn = false;
}
}
}

View File

@ -212,13 +212,13 @@ namespace BizHawk.Emulation.Common.Components.I8048
{
PopulateCURINSTR(IDLE,
IDLE,
EEA,
WR_P, 0, (ushort)(reg + RB),
DEA,
IDLE,
IDLE,
IDLE,
IDLE,
IDLE,
IDLE,
IDLE);
RD_P, A, 0);
IRQS = 9;
}
@ -268,18 +268,35 @@ namespace BizHawk.Emulation.Common.Components.I8048
IRQS = 9;
}
// TODO: This should only write back to the port destination if directly wired, otherwise we should wait for a write pulse
// TODO: for O2, P1 is tied direct to CTRL outputs so this is ok, BUS and P2 should do something else though
public void OP_PB_DIR(ushort oper, ushort reg)
{
PopulateCURINSTR(IDLE,
IDLE,
IDLE,
RD, ALU, PC,
INC11, PC,
IDLE,
IDLE,
IDLE,
oper, reg, ALU);
if (reg == 1)
{
PopulateCURINSTR(IDLE,
IDLE,
IDLE,
RD, ALU, PC,
INC11, PC,
oper, (ushort)(reg + PX), ALU,
IDLE,
IDLE,
WR_P, reg, (ushort)(reg + PX));
}
else
{
PopulateCURINSTR(IDLE,
IDLE,
IDLE,
RD, ALU, PC,
INC11, PC,
oper, (ushort)(reg + PX), ALU,
IDLE,
IDLE,
IDLE);
}
IRQS = 9;
}

View File

@ -33,6 +33,9 @@ namespace BizHawk.Emulation.Common.Components.I8048
public const ushort R6 = 6;
public const ushort R7 = 7;
// offset for port regs
public const ushort PX = 70;
// the location pointed to by the registers is controlled by the RAM bank
public ushort RB = 0;
public ushort RAM_ptr = 0;
@ -44,11 +47,11 @@ namespace BizHawk.Emulation.Common.Components.I8048
//RAM occupies registers 0-63
public const ushort PC = 64;
public const ushort PSW = 65;
public const ushort BUS = 66;
public const ushort A = 67;
public const ushort ADDR = 68; // internal
public const ushort ALU = 69; // internal
public const ushort ALU2 = 70; // internal
public const ushort A = 66;
public const ushort ADDR = 67; // internal
public const ushort ALU = 68; // internal
public const ushort ALU2 = 69; // internal
public const ushort BUS = 70;
public const ushort P1 = 71;
public const ushort P2 = 72;
public const ushort P4 = 73;

View File

@ -1,180 +0,0 @@
using System;
using BizHawk.Common;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Common.NumberExtensions;
namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
{
// Audio Emulation (a 24 bit shift register plus a control register)
public class Audio : ISoundProvider
{
public O2Hawk Core { get; set; }
private BlipBuffer _blip_C = new BlipBuffer(15000);
public byte sample;
public byte shift_0, shift_1, shift_2, aud_ctrl;
public uint master_audio_clock;
public int tick_cnt, output_bit;
public int latched_sample_C;
public byte ReadReg(int addr)
{
byte ret = 0;
switch (addr)
{
case 0xA7: ret = shift_0; break;
case 0xA8: ret = shift_1; break;
case 0xA9: ret = shift_2; break;
case 0xAA: ret = aud_ctrl; break;
}
return ret;
}
public void WriteReg(int addr, byte value)
{
switch (addr)
{
case 0xA7: shift_0 = value; break;
case 0xA8: shift_1 = value; break;
case 0xA9: shift_2 = value; break;
case 0xAA: aud_ctrl = value; break;
}
}
public void tick()
{
int C_final = 0;
if (aud_ctrl.Bit(7))
{
tick_cnt++;
if (tick_cnt > (aud_ctrl.Bit(5) ? 455 : 1820))
{
tick_cnt = 0;
output_bit = (shift_0 >> 1) & 1;
shift_0 = (byte)((shift_0 >> 1) | ((shift_1 & 1) << 3));
shift_1 = (byte)((shift_1 >> 1) | ((shift_2 & 1) << 3));
if (aud_ctrl.Bit(6))
{
shift_2 = (byte)((shift_2 >> 1) | ((output_bit) << 3));
}
else
{
shift_0 = (byte)(shift_2 >> 1);
}
}
C_final = output_bit;
C_final *= ((aud_ctrl & 0xF) + 1) * 40;
}
if (C_final != latched_sample_C)
{
_blip_C.AddDelta(master_audio_clock, C_final - latched_sample_C);
latched_sample_C = C_final;
}
master_audio_clock++;
}
public void power_off()
{
for (int i = 0; i < 0x16; i++)
{
WriteReg(0xFF10 + i, 0);
}
}
public void Reset()
{
master_audio_clock = 0;
sample = 0;
_blip_C.SetRates(4194304, 44100);
}
public void SyncState(Serializer ser)
{
ser.Sync(nameof(master_audio_clock), ref master_audio_clock);
ser.Sync(nameof(sample), ref sample);
ser.Sync(nameof(latched_sample_C), ref latched_sample_C);
ser.Sync(nameof(aud_ctrl), ref aud_ctrl);
ser.Sync(nameof(shift_0), ref shift_0);
ser.Sync(nameof(shift_1), ref shift_1);
ser.Sync(nameof(shift_2), ref shift_2);
ser.Sync(nameof(tick_cnt), ref tick_cnt);
ser.Sync(nameof(output_bit), ref output_bit);
}
#region audio
public bool CanProvideAsync => false;
public void SetSyncMode(SyncSoundMode mode)
{
if (mode != SyncSoundMode.Sync)
{
throw new InvalidOperationException("Only Sync mode is supported_");
}
}
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
public void GetSamplesSync(out short[] samples, out int nsamp)
{
_blip_C.EndFrame(master_audio_clock);
nsamp = _blip_C.SamplesAvailable();
samples = new short[nsamp * 2];
if (nsamp != 0)
{
_blip_C.ReadSamples(samples, nsamp, false);
}
master_audio_clock = 0;
}
public void GetSamplesAsync(short[] samples)
{
throw new NotSupportedException("Async is not available");
}
public void DiscardSamples()
{
_blip_C.Clear();
master_audio_clock = 0;
}
private void GetSamples(short[] samples)
{
}
public void DisposeSound()
{
_blip_C.Clear();
_blip_C.Dispose();
_blip_C = null;
}
#endregion
}
}

View File

@ -80,10 +80,6 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
}
if (ppu_en && !copy_en)
{
if ((addr_latch >= 0xA7) || (addr_latch <= 0xAA))
{
return audio.ReadReg(addr_latch);
}
return ppu.ReadReg(addr_latch);
}
@ -134,14 +130,8 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
}
if (ppu_en)
{
if ((addr_latch >= 0xA7) || (addr_latch <= 0xAA))
{
audio.WriteReg(addr_latch, value);
}
else
{
ppu.WriteReg(addr_latch, value);
}
ppu.WriteReg(addr_latch, value);
//Console.WriteLine((addr_latch) + " " + value);
}
}
}
@ -155,6 +145,8 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
kybrd_en = !value.Bit(2);
cart_b1 = value.Bit(1);
cart_b0 = value.Bit(0);
//Console.WriteLine("main ctrl: " + value + " " + ppu_en + " " + RAM_en);
}
else
{

View File

@ -52,10 +52,11 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
{
for (int i = 0; i < 10000; i++)
{
audio.tick();
ppu.tick();
ppu.tick();
ppu.DMA_tick();
serialport.serial_transfer_tick();
ppu.Audio_tick();
cpu.ExecuteOne();
if (in_vblank && !in_vblank_old)
@ -82,10 +83,11 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
public void do_single_step()
{
audio.tick();
ppu.tick();
ppu.tick();
ppu.DMA_tick();
serialport.serial_transfer_tick();
ppu.Audio_tick();
cpu.ExecuteOne();
}
@ -138,7 +140,7 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
public void Dispose()
{
audio.DisposeSound();
ppu.DisposeSound();
}
#region Video provider

View File

@ -51,7 +51,6 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
mapper.SyncState(ser);
ppu.SyncState(ser);
serialport.SyncState(ser);
audio.SyncState(ser);
ser.BeginSection("Odyssey2");
ser.Sync(nameof(core), ref core, false);

View File

@ -35,7 +35,7 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
public byte[] cart_RAM;
public bool has_bat;
private int _frame = 0;
public int _frame = 0;
public MapperBase mapper;
@ -43,7 +43,6 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
public I8048 cpu;
public PPU ppu;
public Audio audio;
public SerialPort serialport;
[CoreConstructor("O2")]
@ -62,7 +61,6 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
OnExecFetch = ExecFetch,
};
audio = new Audio();
serialport = new SerialPort();
CoreComm = comm;
@ -92,12 +90,11 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
_frameHz = 60;
audio.Core = this;
ppu.Core = this;
serialport.Core = this;
ser.Register<IVideoProvider>(this);
ser.Register<ISoundProvider>(audio);
ser.Register<ISoundProvider>(ppu);
ServiceProvider = ser;
_settings = (O2Settings)settings ?? new O2Settings();
@ -118,6 +115,11 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
{
RAM[j] = (byte)j;
}
for (int k = 0; k < 0x100; k++)
{
ppu.WriteReg(k, (byte)k);
}
}
public DisplayType Region => DisplayType.NTSC;
@ -132,7 +134,6 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
RAM_Bank = 1; // RAM bank always starts as 1 (even writing zero still sets 1)
ppu.Reset();
audio.Reset();
serialport.Reset();
cpu.SetCallbacks(ReadMemory, PeekMemory, PeekMemory, WriteMemory);

View File

@ -1,9 +1,13 @@
using System;
using BizHawk.Common;
using BizHawk.Common.NumberExtensions;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
{
public class PPU
public class PPU : ISoundProvider
{
public O2Hawk Core { get; set; }
@ -25,7 +29,7 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
public byte STAT;
public byte scroll_y;
public byte scroll_x;
public byte LY;
public int LY;
public byte LY_actual;
public byte LY_inc;
public byte LYC;
@ -39,6 +43,9 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
public int DMA_clock;
public int DMA_inc;
public byte DMA_byte;
public int cycle;
public bool VBL;
public bool HBL;
public byte ReadReg(int addr)
{
@ -76,6 +83,10 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
{
ret = VDC_color;
}
else if (addr <= 0xA7)
{
ret = AudioReadReg(addr);
}
return ret;
}
@ -114,11 +125,47 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
{
VDC_color = value;
}
else if (addr <= 0xA7)
{
AudioWriteReg(addr, value);
}
}
public void tick()
{
cycle++;
// drawing cycles
if ((cycle >= 43) && !VBL)
{
if (cycle == 43)
{
HBL = false;
// trigger timer tick if enabled
if (Core.cpu.counter_en) { Core.cpu.T1 = false; }
}
}
// end of scanline
if (cycle == 228)
{
cycle = 0;
HBL = true;
if (VDC_ctrl.Bit(0)) { Core.cpu.IRQPending = true;}
// trigger timer tick if enabled
if (Core.cpu.counter_en) { Core.cpu.T1 = true; }
LY++;
if (LY == 262)
{
LY = 0;
HBL = false;
VBL = true;
}
if (LY == 22) { VBL = false; }
}
}
// might be needed, not sure yet
@ -151,7 +198,7 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
public void Reset()
{
AudioReset();
}
public static readonly byte[] Internal_Graphics = { 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, // 0 0x00
@ -220,6 +267,7 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
0x00, 0x00, 0x00, 0x54, 0x54, 0xFF, 0x7E, // (boat 3 unk) 0x3F
};
public void SyncState(Serializer ser)
{
ser.Sync(nameof(Sprites), ref Sprites, false);
@ -255,6 +303,168 @@ namespace BizHawk.Emulation.Cores.Consoles.O2Hawk
ser.Sync(nameof(DMA_clock), ref DMA_clock);
ser.Sync(nameof(DMA_inc), ref DMA_inc);
ser.Sync(nameof(DMA_byte), ref DMA_byte);
ser.Sync(nameof(cycle), ref cycle);
ser.Sync(nameof(VBL), ref VBL);
ser.Sync(nameof(HBL), ref HBL);
AudioSyncState(ser);
}
private BlipBuffer _blip_C = new BlipBuffer(15000);
public byte sample;
public byte shift_0, shift_1, shift_2, aud_ctrl;
public uint master_audio_clock;
public int tick_cnt, output_bit;
public int latched_sample_C;
public byte AudioReadReg(int addr)
{
byte ret = 0;
switch (addr)
{
case 0xA7: ret = shift_0; break;
case 0xA8: ret = shift_1; break;
case 0xA9: ret = shift_2; break;
case 0xAA: ret = aud_ctrl; break;
}
return ret;
}
public void AudioWriteReg(int addr, byte value)
{
switch (addr)
{
case 0xA7: shift_0 = value; break;
case 0xA8: shift_1 = value; break;
case 0xA9: shift_2 = value; break;
case 0xAA: aud_ctrl = value; break;
}
}
public void Audio_tick()
{
int C_final = 0;
if (aud_ctrl.Bit(7))
{
tick_cnt++;
if (tick_cnt > (aud_ctrl.Bit(5) ? 455 : 1820))
{
tick_cnt = 0;
output_bit = (shift_0 >> 1) & 1;
shift_0 = (byte)((shift_0 >> 1) | ((shift_1 & 1) << 3));
shift_1 = (byte)((shift_1 >> 1) | ((shift_2 & 1) << 3));
if (aud_ctrl.Bit(6))
{
shift_2 = (byte)((shift_2 >> 1) | ((output_bit) << 3));
}
else
{
shift_0 = (byte)(shift_2 >> 1);
}
}
C_final = output_bit;
C_final *= ((aud_ctrl & 0xF) + 1) * 40;
}
if (C_final != latched_sample_C)
{
_blip_C.AddDelta(master_audio_clock, C_final - latched_sample_C);
latched_sample_C = C_final;
}
master_audio_clock++;
}
public void AudioReset()
{
master_audio_clock = 0;
sample = 0;
_blip_C.SetRates(4194304, 44100);
}
public void AudioSyncState(Serializer ser)
{
ser.Sync(nameof(master_audio_clock), ref master_audio_clock);
ser.Sync(nameof(sample), ref sample);
ser.Sync(nameof(latched_sample_C), ref latched_sample_C);
ser.Sync(nameof(aud_ctrl), ref aud_ctrl);
ser.Sync(nameof(shift_0), ref shift_0);
ser.Sync(nameof(shift_1), ref shift_1);
ser.Sync(nameof(shift_2), ref shift_2);
ser.Sync(nameof(tick_cnt), ref tick_cnt);
ser.Sync(nameof(output_bit), ref output_bit);
}
#region audio
public bool CanProvideAsync => false;
public void SetSyncMode(SyncSoundMode mode)
{
if (mode != SyncSoundMode.Sync)
{
throw new InvalidOperationException("Only Sync mode is supported_");
}
}
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
public void GetSamplesSync(out short[] samples, out int nsamp)
{
_blip_C.EndFrame(master_audio_clock);
nsamp = _blip_C.SamplesAvailable();
samples = new short[nsamp * 2];
if (nsamp != 0)
{
_blip_C.ReadSamples(samples, nsamp, false);
}
master_audio_clock = 0;
}
public void GetSamplesAsync(short[] samples)
{
throw new NotSupportedException("Async is not available");
}
public void DiscardSamples()
{
_blip_C.Clear();
master_audio_clock = 0;
}
private void GetSamples(short[] samples)
{
}
public void DisposeSound()
{
_blip_C.Clear();
_blip_C.Dispose();
_blip_C = null;
}
#endregion
}
}