diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index b12cbd8090..164627f03a 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -709,7 +709,6 @@ - diff --git a/BizHawk.Emulation.Cores/CPUs/Intel8048/Execute.cs b/BizHawk.Emulation.Cores/CPUs/Intel8048/Execute.cs index 340c3c5a71..512addb1e1 100644 --- a/BizHawk.Emulation.Cores/CPUs/Intel8048/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/Intel8048/Execute.cs @@ -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 diff --git a/BizHawk.Emulation.Cores/CPUs/Intel8048/I8048.cs b/BizHawk.Emulation.Cores/CPUs/Intel8048/I8048.cs index 61691764bb..6af3e975f3 100644 --- a/BizHawk.Emulation.Cores/CPUs/Intel8048/I8048.cs +++ b/BizHawk.Emulation.Cores/CPUs/Intel8048/I8048.cs @@ -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", diff --git a/BizHawk.Emulation.Cores/CPUs/Intel8048/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/Intel8048/Interrupts.cs index f346b0ccad..fe10cf9cea 100644 --- a/BizHawk.Emulation.Cores/CPUs/Intel8048/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/Intel8048/Interrupts.cs @@ -26,7 +26,8 @@ namespace BizHawk.Emulation.Common.Components.I8048 private void ResetInterrupts() { - IntEn = true; + IntEn = false; + TimIntEn = false; } } } \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/Intel8048/OP_Tables.cs b/BizHawk.Emulation.Cores/CPUs/Intel8048/OP_Tables.cs index ce84b8efe8..d5fd15ed0f 100644 --- a/BizHawk.Emulation.Cores/CPUs/Intel8048/OP_Tables.cs +++ b/BizHawk.Emulation.Cores/CPUs/Intel8048/OP_Tables.cs @@ -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; } diff --git a/BizHawk.Emulation.Cores/CPUs/Intel8048/Registers.cs b/BizHawk.Emulation.Cores/CPUs/Intel8048/Registers.cs index fea2ad0fb9..a2038ba039 100644 --- a/BizHawk.Emulation.Cores/CPUs/Intel8048/Registers.cs +++ b/BizHawk.Emulation.Cores/CPUs/Intel8048/Registers.cs @@ -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; diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/Audio.cs deleted file mode 100644 index b2edc7104c..0000000000 --- a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/Audio.cs +++ /dev/null @@ -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 - } -} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/MemoryMap.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/MemoryMap.cs index 6921955868..161f17cf7b 100644 --- a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/MemoryMap.cs +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/MemoryMap.cs @@ -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 { diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IEmulator.cs index 9dc8cb7f31..ebea0ab315 100644 --- a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IEmulator.cs @@ -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 diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IStatable.cs index 3030adff46..ff886bc27e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.IStatable.cs @@ -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); diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.cs index 3db97d260d..f76f4872f2 100644 --- a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/O2Hawk.cs @@ -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(this); - ser.Register(audio); + ser.Register(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); diff --git a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/PPU.cs index 8589d23246..7471900a39 100644 --- a/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Magnavox/Odyssey2/PPU.cs @@ -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 } }