├── .gitignore
├── Assets
└── famicon.ico
├── CS64.Core
├── Audio
│ ├── BlipBuffer.cs
│ └── SID.cs
├── CPU
│ ├── AddressingModes.cs
│ ├── AudioBuffer.cs
│ ├── CpuExecution.cs
│ ├── InitInstructionSet.cs
│ ├── MC6502InstructionSet.cs
│ ├── MC6502State.cs
│ └── OpCodeDelegate.cs
├── CS64.Core.csproj
├── Fonts
│ └── Modeseven.ttf
├── Interface
│ ├── AudioProvider.cs
│ ├── DisplayMode.cs
│ ├── IMainInterface.cs
│ ├── Input
│ │ ├── AnsiKeyboard.cs
│ │ ├── C64Keyboard.cs
│ │ ├── Controller.cs
│ │ ├── InputEvent.cs
│ │ ├── InputEventArgs.cs
│ │ ├── InputEventType.cs
│ │ ├── InputKeyEnum.cs
│ │ ├── InputProvider.cs
│ │ ├── KeyBinding.cs
│ │ └── Keyboard.cs
│ ├── KeyboardMatrix.cs
│ ├── Main.Keyboard.cs
│ ├── Main.cs
│ ├── Playback.cs
│ ├── RowCol.cs
│ └── VideoProvider.cs
├── SDL2_ttf.dll
├── UnsupportedMapperException.cs
├── Utility
│ └── BinaryWriterExtensions.cs
├── Video
│ ├── CIA.cs
│ ├── StateEnum.cs
│ └── VICII.cs
├── blip_buf.dll
├── libfreetype-6.dll
└── zlib1.dll
├── CS64.UI
├── CS64.UI.csproj
├── Configuration.cs
├── ControlExtensions.cs
├── FormExtensions.cs
├── KeyCodeMappings.cs
├── MainForm.Designer.cs
├── MainForm.cs
├── MainForm.resx
├── MapWaitForm.Designer.cs
├── MapWaitForm.cs
├── MapWaitForm.resx
├── MappingForm.Designer.cs
├── MappingForm.cs
├── MappingForm.resx
└── famicon.ico
├── CS64.sln
├── CS64
├── CS64.csproj
├── Program.cs
├── Properties
│ └── launchSettings.json
├── publish.cmd
└── rom
│ ├── basic.bin
│ ├── characters.bin
│ └── kernal.bin
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | #Ignore thumbnails created by Windows
3 | Thumbs.db
4 | #Ignore files built by Visual Studio
5 | *.obj
6 | *.exe
7 | *.pdb
8 | *.user
9 | *.aps
10 | *.pch
11 | *.vspscc
12 | *_i.c
13 | *_p.c
14 | *.ncb
15 | *.suo
16 | *.tlb
17 | *.tlh
18 | *.bak
19 | *.cache
20 | *.ilk
21 | *.log
22 | [Bb]in
23 | [Dd]ebug*/
24 | *.lib
25 | *.sbr
26 | obj/
27 | [Rr]elease*/
28 | _ReSharper*/
29 | [Tt]est[Rr]esult*
30 | .vs/
31 | #Nuget packages folder
32 | packages/
33 |
--------------------------------------------------------------------------------
/Assets/famicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RupertAvery/CS64/eb089999233654cc494235ad89267d8ae60d38de/Assets/famicon.ico
--------------------------------------------------------------------------------
/CS64.Core/Audio/BlipBuffer.cs:
--------------------------------------------------------------------------------
1 | #nullable disable
2 |
3 | using System;
4 | using System.Runtime.InteropServices;
5 |
6 | // ReSharper disable StyleCop.SA1300
7 | // ReSharper disable InconsistentNaming
8 | namespace BizHawk.Emulation.Common
9 | {
10 | ///
11 | /// wrapper around blargg's unmanaged blip_buf
12 | ///
13 | public sealed class BlipBuffer : IDisposable
14 | {
15 | // this is transitional only. if the band-limited synthesis idea works out, i'll
16 | // make a managed MIT implementation
17 | private static class BlipBufDll
18 | {
19 | /** Creates new buffer that can hold at most sample_count samples. Sets rates
20 | so that there are blip_max_ratio clocks per sample. Returns pointer to new
21 | buffer, or NULL if insufficient memory. */
22 | [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
23 | public static extern IntPtr blip_new(int sample_count);
24 |
25 | /** Sets approximate input clock rate and output sample rate. For every
26 | clock_rate input clocks, approximately sample_rate samples are generated. */
27 | [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
28 | public static extern void blip_set_rates(IntPtr context, double clock_rate, double sample_rate);
29 |
30 | /** Maximum clock_rate/sample_rate ratio. For a given sample_rate,
31 | clock_rate must not be greater than sample_rate*blip_max_ratio. */
32 | public const int BlipMaxRatio = 1 << 20;
33 |
34 | /** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */
35 | [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
36 | public static extern void blip_clear(IntPtr context);
37 |
38 | /** Adds positive/negative delta into buffer at specified clock time. */
39 | [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
40 | public static extern void blip_add_delta(IntPtr context, uint clock_time, int delta);
41 |
42 | /** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */
43 | [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
44 | public static extern void blip_add_delta_fast(IntPtr context, uint clock_time, int delta);
45 |
46 | /** Length of time frame, in clocks, needed to make sample_count additional
47 | samples available. */
48 | [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
49 | public static extern int blip_clocks_needed(IntPtr context, int sample_count);
50 |
51 | /** Maximum number of samples that can be generated from one time frame. */
52 | public const int BlipMaxFrame = 4000;
53 |
54 | /** Makes input clocks before clock_duration available for reading as output
55 | samples. Also begins new time frame at clock_duration, so that clock time 0 in
56 | the new time frame specifies the same clock as clock_duration in the old time
57 | frame specified. Deltas can have been added slightly past clock_duration (up to
58 | however many clocks there are in two output samples). */
59 | [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
60 | public static extern void blip_end_frame(IntPtr context, uint clock_duration);
61 |
62 | /** Number of buffered samples available for reading. */
63 | [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
64 | public static extern int blip_samples_avail(IntPtr context);
65 |
66 | /** Reads and removes at most 'count' samples and writes them to 'out'. If
67 | 'stereo' is true, writes output to every other element of 'out', allowing easy
68 | interleaving of two buffers into a stereo sample stream. Outputs 16-bit signed
69 | samples. Returns number of samples actually read. */
70 | [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
71 | public static extern int blip_read_samples(IntPtr context, short[] @out, int count, int stereo);
72 | [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
73 | public static extern int blip_read_samples(IntPtr context, IntPtr @out, int count, int stereo);
74 |
75 | /** Frees buffer. No effect if NULL is passed. */
76 | [DllImport("blip_buf", CallingConvention = CallingConvention.Cdecl)]
77 | public static extern void blip_delete(IntPtr context);
78 | }
79 |
80 | private IntPtr _context;
81 |
82 | /// unmanaged call failed
83 | public BlipBuffer(int sampleCount)
84 | {
85 | _context = BlipBufDll.blip_new(sampleCount);
86 | if (_context == IntPtr.Zero)
87 | {
88 | throw new Exception("blip_new returned NULL!");
89 | }
90 | }
91 |
92 | ~BlipBuffer()
93 | {
94 | Dispose();
95 | }
96 |
97 | public void Dispose()
98 | {
99 | if (_context != IntPtr.Zero)
100 | {
101 | BlipBufDll.blip_delete(_context);
102 | _context = IntPtr.Zero;
103 | GC.SuppressFinalize(this);
104 | }
105 | }
106 |
107 | public void SetRates(double clockRate, double sampleRate)
108 | {
109 | BlipBufDll.blip_set_rates(_context, clockRate, sampleRate);
110 | }
111 |
112 | public const int MaxRatio = BlipBufDll.BlipMaxRatio;
113 |
114 | public void Clear()
115 | {
116 | BlipBufDll.blip_clear(_context);
117 | }
118 |
119 | public void AddDelta(uint clockTime, int delta)
120 | {
121 | BlipBufDll.blip_add_delta(_context, clockTime, delta);
122 | }
123 |
124 | public void AddDeltaFast(uint clockTime, int delta)
125 | {
126 | BlipBufDll.blip_add_delta_fast(_context, clockTime, delta);
127 | }
128 |
129 | public int ClocksNeeded(int sampleCount)
130 | {
131 | return BlipBufDll.blip_clocks_needed(_context, sampleCount);
132 | }
133 |
134 | public const int MaxFrame = BlipBufDll.BlipMaxFrame;
135 |
136 | public void EndFrame(uint clockDuration)
137 | {
138 | BlipBufDll.blip_end_frame(_context, clockDuration);
139 | }
140 |
141 | public int SamplesAvailable()
142 | {
143 | return BlipBufDll.blip_samples_avail(_context);
144 | }
145 |
146 | /// can't hold samples (or twice that if is )
147 | public int ReadSamples(short[] output, int count, bool stereo)
148 | {
149 | if (output.Length < count * (stereo ? 2 : 1))
150 | {
151 | throw new ArgumentOutOfRangeException();
152 | }
153 |
154 | return BlipBufDll.blip_read_samples(_context, output, count, stereo ? 1 : 0);
155 | }
156 |
157 | /// can't hold 2 * samples
158 | public int ReadSamplesLeft(short[] output, int count)
159 | {
160 | if (output.Length < count * 2)
161 | {
162 | throw new ArgumentOutOfRangeException();
163 | }
164 |
165 | return BlipBufDll.blip_read_samples(_context, output, count, 1);
166 | }
167 |
168 | /// can't hold 2 * samples
169 | public int ReadSamplesRight(short[] output, int count)
170 | {
171 | if (output.Length < count * 2)
172 | {
173 | throw new ArgumentOutOfRangeException();
174 | }
175 |
176 | unsafe
177 | {
178 | fixed (short* s = &output[1])
179 | return BlipBufDll.blip_read_samples(_context, new IntPtr(s), count, 1);
180 | }
181 | }
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/CS64.Core/Audio/SID.cs:
--------------------------------------------------------------------------------
1 | using CS64.Core.CPU;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace CS64.Core.Audio
7 | {
8 | public class SID
9 | {
10 | public uint sampleclock;
11 | private MC6502State mC6502State;
12 |
13 | public SID(MC6502State mC6502State)
14 | {
15 | this.mC6502State = mC6502State;
16 | }
17 |
18 | public int EmitSample()
19 | {
20 | return 0;
21 | }
22 |
23 | public uint Read(uint address)
24 | {
25 | return 0;
26 | }
27 |
28 | public void Write(uint address, uint value)
29 | {
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/CS64.Core/CPU/AddressingModes.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | namespace CS64.Core.CPU
4 | {
5 | public partial class MC6502State
6 | {
7 |
8 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
9 | private void AddrModeImmediate()
10 | {
11 | EffectiveAddr = PC + 1;
12 | }
13 |
14 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
15 | private void AddrModeZeroPage()
16 | {
17 | EffectiveAddr = BusRead(PC + 1);
18 | }
19 |
20 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
21 | private void AddrModeZeroPageX()
22 | {
23 | EffectiveAddr = (BusRead(PC + 1) + X) % 0x100;
24 | }
25 |
26 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
27 | private void AddrModeZeroPageY()
28 | {
29 | EffectiveAddr = (BusRead(PC + 1) + Y) % 0x100;
30 | }
31 |
32 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
33 | private void AddrModeAbsolute()
34 | {
35 | EffectiveAddr = BusRead(PC + 1) + BusRead(PC + 2) * 0x100;
36 | }
37 |
38 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
39 | private void AddrModeAbsoluteX()
40 | {
41 | var basemem = BusRead(PC + 1) + BusRead(PC + 2) * 0x100;
42 | var basememPage = (basemem >> 8) & 0xff;
43 | EffectiveAddr = basemem + X;
44 | var effAddPage = (EffectiveAddr >> 8) & 0xff;
45 |
46 | //var x = PC >> 8;
47 | //var y = EffectiveAddr >> 8;
48 |
49 | if (basememPage != effAddPage)
50 | {
51 | PageBoundsCrossed = true;
52 | }
53 | }
54 |
55 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
56 | private void AddrModeAbsoluteY()
57 | {
58 | var basemem = BusRead(PC + 1) + BusRead(PC + 2) * 0x100;
59 | var basememPage = (basemem >> 8) & 0xFF;
60 | EffectiveAddr = (basemem + Y) & 0xFFFF;
61 | var effAddPage = (EffectiveAddr >> 8) & 0xFF;
62 |
63 | //var x = PC >> 8;
64 | //var y = EffectiveAddr >> 8;
65 |
66 | if (basememPage != effAddPage)
67 | {
68 | PageBoundsCrossed = true;
69 | }
70 | }
71 |
72 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
73 | private void AddrModeRelative()
74 | {
75 | EffectiveAddr = PC + 1;
76 | }
77 |
78 |
79 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
80 | private void AddrModeIndirect()
81 | {
82 | var arg = BusRead(PC + 1) + BusRead(PC + 2) * 0x100;
83 | EffectiveAddr = BusRead(arg) + BusRead(arg + 1) * 0x100;
84 | }
85 |
86 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
87 | private void AddrModeIndirect_JMP()
88 | {
89 | var arg = BusRead(PC + 1);
90 | arg += BusRead(PC + 2) * 0x100;
91 | if ((arg & 0xFF) == 0xFF)
92 | {
93 | EffectiveAddr = BusRead(arg) + BusRead(arg + 1 - 0x100) * 0x100;
94 | }
95 | else
96 | {
97 | EffectiveAddr = BusRead(arg) + BusRead(arg + 1) * 0x100;
98 | }
99 | }
100 |
101 |
102 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
103 | private void AddrModeIndirectX()
104 | {
105 | var arg = BusRead(PC + 1);
106 | var ix = arg + X;
107 | EffectiveAddr = BusRead(ix % 0x100) + BusRead((ix + 1) % 0x100) * 0x100;
108 | }
109 |
110 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
111 | private void AddrModeIndirectY()
112 | {
113 | var arg = BusRead(PC + 1);
114 | var basemem = BusRead(arg) + BusRead((arg + 1) % 0x100) * 0x100;
115 | var basememPage = (basemem >> 8) & 0xFF;
116 | EffectiveAddr = (basemem + Y) & 0xFFFF;
117 | var effAddPage = (EffectiveAddr >> 8) & 0xff;
118 |
119 | if (basememPage != effAddPage)
120 | {
121 | PageBoundsCrossed = true;
122 | }
123 | }
124 |
125 | }
126 | }
--------------------------------------------------------------------------------
/CS64.Core/CPU/AudioBuffer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using BizHawk.Emulation.Common;
3 | using CS64.Core.Audio;
4 | using CS64.Core.Video;
5 |
6 | namespace CS64.Core.CPU
7 | {
8 | public partial class MC6502State
9 | {
10 | private BlipBuffer blip = new BlipBuffer(4096);
11 | private const int blipbuffsize = 4096;
12 | private const int cpuclockrate = 1789773; // NTSC
13 | private int old_s;
14 |
15 | public SID Sid;
16 |
17 | public void GetSamplesSync(out short[] samples, out int nsamp)
18 | {
19 | if (Sid == null || blip == null)
20 | {
21 | nsamp = 0;
22 | samples = new short[nsamp * 2];
23 | return;
24 | }
25 |
26 | blip.EndFrame(Sid.sampleclock);
27 | Sid.sampleclock = 0;
28 |
29 | nsamp = blip.SamplesAvailable();
30 | samples = new short[nsamp * 2];
31 |
32 |
33 | blip.ReadSamples(samples, nsamp, false);
34 | //HighPassFilter(samples, nsamp, 90.0, 0.25);
35 | //HighPassFilter(samples, nsamp, 440.0, 0.25);
36 | //LowPassFilter(samples, nsamp, 5000.0, 1.0);
37 |
38 | for (int i = nsamp - 1; i >= 0; i--)
39 | {
40 | samples[i * 2] = samples[i];
41 | samples[i * 2 + 1] = samples[i];
42 | }
43 | }
44 |
45 | static void LowPassFilter(short[] sample, int samples, double frequency, double q)
46 | {
47 | double O = 2.0 * Math.PI * frequency / 44100.0;
48 | double C = q / O;
49 | double L = 1 / q / O;
50 | for (int c = 0; c < 1; c++)
51 | {
52 | double V = 0, I = 0, T;
53 | for (int s = 0; s < samples; s++)
54 | {
55 | T = (I - V) / C;
56 | I += (sample[s] * O - V) / L;
57 | V += T;
58 | sample[s] = (short)(V / O);
59 | }
60 | }
61 | }
62 |
63 | static void HighPassFilter(short[] sample, int samples, double Frequency, double Q)
64 | {
65 | double O = 2.0 * Math.PI * Frequency / 44100;
66 | double C = Q / O;
67 | double L = 1 / Q / O;
68 | for (int c = 0; c < 1; c++)
69 | {
70 | double V = 0, I = 0, T;
71 | for (int s = 0; s < samples; s++)
72 | {
73 | T = sample[s] * O - V;
74 | V += (I + T) / C;
75 | I += T / L;
76 | sample[s] -= (short)(V / O);
77 | }
78 | }
79 | }
80 |
81 | public void DiscardSamples()
82 | {
83 | blip.Clear();
84 | Sid.sampleclock = 0;
85 | }
86 |
87 |
88 | }
89 | }
--------------------------------------------------------------------------------
/CS64.Core/CPU/CpuExecution.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using CS64.Core.Audio;
5 | using CS64.Core.Video;
6 |
7 | namespace CS64.Core.CPU
8 | {
9 | public partial class MC6502State
10 | {
11 | //private bool running;
12 | private StringBuilder log;
13 | private int _instructionCyclesLeft;
14 | public bool Debug { get; set; }
15 | public VICII Vic;
16 | public CIA Cia1;
17 | public CIA Cia2;
18 |
19 | public MC6502State()
20 | {
21 | log = new StringBuilder();
22 | Vic = new VICII(this);
23 | Sid = new SID(this);
24 | Cia1 = new CIA();
25 |
26 | Cia1.RequestInterrupt = () => TriggerInterrupt(InterruptTypeEnum.IRQ);
27 |
28 | Cia2 = new CIA();
29 |
30 | // https://retrocomputing.stackexchange.com/questions/7791/6510-i-o-port-initialisation
31 |
32 | //I/O lines with programmable direction are set to input on reset. The 6510 is no exception,
33 | //its data direction register at address 0 is set to 0 on reset, all 6 I/O lines become inputs.
34 | //The circuitry outside the processor includes a pullup resistor on the I/O lines controlling
35 | //banking, so that they are in a known stable state even if the data direction is set to input.
36 |
37 | // Set the IO Data direction register to input on startup, else I/O will be be inaccessible.
38 | //IO_Port_DDR = 0xFF;
39 | IO_Port_DR = 0x1F;
40 | }
41 |
42 |
43 | public void Init()
44 | {
45 | MC6502InstructionSet.InitOpcodeTable();
46 | blip.SetRates((uint)cpuclockrate, 44100);
47 | }
48 |
49 | private int divider;
50 |
51 | public uint Step()
52 | {
53 | Vic.Clock();
54 | if (BusAvailable)
55 | {
56 | ClockCpu();
57 | }
58 |
59 | divider++;
60 | // TODO: is the counter driven by the CPU clock?
61 | if (divider == 8)
62 | {
63 | divider = 0;
64 | Cia1.Clock();
65 | Cia2.Clock();
66 | }
67 |
68 | int s = Sid.EmitSample();
69 |
70 | if (s != old_s)
71 | {
72 | blip.AddDelta(Sid.sampleclock, s - old_s);
73 | old_s = s;
74 | }
75 |
76 | Sid.sampleclock++;
77 |
78 | return 1;
79 | }
80 |
81 | private void ClockCpu()
82 | {
83 | // TODO: is the counter driven by the CPU clock?
84 | Cia1.Count();
85 | Cia2.Count();
86 |
87 | if (_instructionCyclesLeft-- > 0)
88 | {
89 | return;
90 | }
91 |
92 | _instructionCyclesLeft += (int)ExecuteInstruction();
93 | Cycles += (uint)_instructionCyclesLeft;
94 | Instructions++;
95 | }
96 |
97 | public void Execute()
98 | {
99 | int ctr = 0;
100 | try
101 | {
102 | Debug = true;
103 | var running = true;
104 | while (running)
105 | {
106 | Step();
107 | if (ctr > 10000)
108 | {
109 | running = false;
110 | }
111 | }
112 |
113 | File.WriteAllText("c64.log", log.ToString());
114 | }
115 | catch (Exception e)
116 | {
117 | Console.WriteLine(e);
118 | File.WriteAllText("c64.log", log.ToString());
119 | }
120 | }
121 |
122 | ///
123 | /// Executes one instruction and return the number of cycles consumed
124 | ///
125 | ///
126 | public uint ExecuteInstruction()
127 | {
128 | for (var i = 0; i < _interrupts.Length; i++)
129 | {
130 | if (_interrupts[i])
131 | {
132 | _interrupts[i] = false;
133 | switch ((InterruptTypeEnum)i)
134 | {
135 | case InterruptTypeEnum.NMI:
136 | return NonMaskableInterrupt();
137 | case InterruptTypeEnum.IRQ:
138 | return InterruptRequest();
139 | }
140 | }
141 | }
142 |
143 | //switch (PC)
144 | //{
145 |
146 | // case 0xFDCD: // Setup CIA
147 | // //case 0xEA8E: // keyboard routine
148 | // {
149 | // var x = 1;
150 | // break;
151 | // }
152 | //}
153 |
154 | var ins = BusRead(PC);
155 | var bytes = MC6502InstructionSet.bytes[ins];
156 |
157 | if (Debug)
158 | {
159 | Log(bytes);
160 | }
161 |
162 | // This could be moved into each instruction, but we would need to implement all 255 instructions separately
163 | switch (MC6502InstructionSet.addrmodes[ins])
164 | {
165 | case MC6502InstructionSet.ACC:
166 | case MC6502InstructionSet.IMP:
167 | break;
168 | case MC6502InstructionSet.IMM:
169 | AddrModeImmediate();
170 | break;
171 | case MC6502InstructionSet.DP_:
172 | AddrModeZeroPage();
173 | break;
174 | case MC6502InstructionSet.DPX:
175 | AddrModeZeroPageX();
176 | break;
177 | case MC6502InstructionSet.DPY:
178 | AddrModeZeroPageY();
179 | break;
180 | case MC6502InstructionSet.IND:
181 | if (ins == 0x6c)
182 | {
183 | AddrModeIndirect_JMP();
184 | }
185 | else
186 | {
187 | AddrModeIndirect();
188 | }
189 | break;
190 | case MC6502InstructionSet.IDX:
191 | AddrModeIndirectX();
192 | break;
193 | case MC6502InstructionSet.IDY:
194 | AddrModeIndirectY();
195 | break;
196 | case MC6502InstructionSet.ABS:
197 | AddrModeAbsolute();
198 | break;
199 | case MC6502InstructionSet.ABX:
200 | AddrModeAbsoluteX();
201 | break;
202 | case MC6502InstructionSet.ABY:
203 | AddrModeAbsoluteY();
204 | break;
205 | case MC6502InstructionSet.REL:
206 | AddrModeRelative();
207 | break;
208 | default:
209 | //File.WriteAllText("mario.log", log.ToString());
210 |
211 | throw new NotImplementedException();
212 | }
213 |
214 | PC += bytes;
215 |
216 | var pcycles = MC6502InstructionSet.cycles[ins];
217 |
218 | pcycles += MC6502InstructionSet.OpCodes[ins](this);
219 |
220 | if (PageBoundsCrossed)
221 | {
222 | //switch (ins)
223 | //{
224 | // /*
225 | // According to documentation, these modes are affected
226 | // ADC
227 | // AND
228 | // CMP
229 | // EOR
230 | // LAX
231 | // LDA
232 | // LDX
233 | // LDY
234 | // NOP
235 | // ORA
236 | // SBC
237 | // (indirect),Y
238 | // absolute,X
239 | // absolute,Y
240 | // */
241 | // case 0x71:
242 | // case 0x7D:
243 | // case 0x79:
244 | // case 0x31:
245 | // case 0x3D:
246 | // case 0x39:
247 | // case 0xD1:
248 | // case 0xDD:
249 | // case 0xD9:
250 | // case 0x51:
251 | // case 0x5D:
252 | // case 0x59:
253 | // case 0xB3:
254 | // case 0xBF:
255 | // case 0xB1:
256 | // case 0xBD:
257 | // case 0xB9:
258 | // case 0xBE:
259 | // case 0xBC:
260 | // case 0x1C:
261 | // case 0x3C:
262 | // case 0x5C:
263 | // case 0x7C:
264 | // case 0xDC:
265 | // case 0xFC:
266 | // case 0x11:
267 | // case 0x1D:
268 | // case 0x19:
269 | // case 0xF1:
270 | // case 0xFD:
271 | // case 0xF9:
272 | // case 0xF0:
273 | // pcycles++;
274 | // break;
275 | //}
276 | pcycles++;
277 | PageBoundsCrossed = false;
278 | }
279 |
280 | switch (ins)
281 | {
282 | // Just to align with nestest.log
283 | case 0xce:
284 | pcycles += 3;
285 | break;
286 | case 0xf3:
287 | pcycles += 4;
288 | break;
289 | }
290 |
291 | return pcycles;
292 | }
293 |
294 | private void Log(uint bytes)
295 | {
296 | var sb = new StringBuilder(256);
297 | for (var i = 0u; i < bytes; i++)
298 | {
299 | sb.Append($"{BusRead(PC + i):X2} ");
300 | }
301 |
302 | for (var i = 0; i < 3 - bytes; i++)
303 | {
304 | sb.Append($" ");
305 | }
306 |
307 | var logMessage =
308 | $"{PC:X4} {sb} A:{A:X2} X:{X:X2} Y:{Y:X2} P:{P:X2} SP:{S:X2} CYC:{Cycles}";
309 |
310 | Console.WriteLine(logMessage);
311 |
312 | //log.AppendLine(logMessage);
313 |
314 |
315 | }
316 |
317 | }
318 | }
--------------------------------------------------------------------------------
/CS64.Core/CPU/InitInstructionSet.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CS64.Core.CPU
4 | {
5 | public static partial class MC6502InstructionSet
6 | {
7 | public static OpCodeDelegate[] OpCodes;
8 |
9 | public static void InitOpcodeTable()
10 | {
11 | OpCodes = new OpCodeDelegate[256];
12 |
13 | for (var opCode = 0; opCode < 256; opCode++)
14 | {
15 | OpCodes[opCode] = GetOpcodeDelegate(opCode);
16 | }
17 | }
18 |
19 | private static OpCodeDelegate GetOpcodeDelegate(int i)
20 | {
21 | return i switch
22 | {
23 | 0x00 => BRK,
24 | 0x01 => ORA,
25 | 0x02 => HLT,
26 | 0x03 => SLO,
27 | 0x04 => NOP,
28 | 0x05 => ORA,
29 | 0x06 => ASL_Mem,
30 | 0x07 => SLO,
31 | 0x08 => PHP,
32 | 0x09 => ORA,
33 | 0x0A => ASL,
34 | 0x0B => ANC,
35 | 0x0C => NOP,
36 | 0x0D => ORA,
37 | 0x0E => ASL_Mem,
38 | 0x0F => SLO,
39 | 0x10 => BPL,
40 | 0x11 => ORA,
41 | 0x12 => HLT,
42 | 0x13 => SLO,
43 | 0x14 => NOP,
44 | 0x15 => ORA,
45 | 0x16 => ASL_Mem,
46 | 0x17 => SLO,
47 | 0x18 => CLC,
48 | 0x19 => ORA,
49 | 0x1A => NOP,
50 | 0x1B => SLO,
51 | 0x1C => NOP,
52 | 0x1D => ORA,
53 | 0x1E => ASL_Mem,
54 | 0x1F => SLO,
55 | 0x20 => JSR,
56 | 0x21 => AND,
57 | 0x22 => HLT,
58 | 0x23 => RLA,
59 | 0x24 => BIT,
60 | 0x25 => AND,
61 | 0x26 => ROL_Mem,
62 | 0x27 => RLA,
63 | 0x28 => PLP,
64 | 0x29 => AND,
65 | 0x2A => ROL,
66 | 0x2B => ANC,
67 | 0x2C => BIT,
68 | 0x2D => AND,
69 | 0x2E => ROL_Mem,
70 | 0x2F => RLA,
71 | 0x30 => BMI,
72 | 0x31 => AND,
73 | 0x32 => HLT,
74 | 0x33 => RLA,
75 | 0x34 => NOP,
76 | 0x35 => AND,
77 | 0x36 => ROL_Mem,
78 | 0x37 => RLA,
79 | 0x38 => SEC,
80 | 0x39 => AND,
81 | 0x3A => NOP,
82 | 0x3B => RLA,
83 | 0x3C => NOP,
84 | 0x3D => AND,
85 | 0x3E => ROL_Mem,
86 | 0x3F => RLA,
87 | 0x40 => RTI,
88 | 0x41 => EOR,
89 | 0x42 => HLT,
90 | 0x43 => SRE,
91 | 0x44 => NOP,
92 | 0x45 => EOR,
93 | 0x46 => LSR_Mem,
94 | 0x47 => SRE,
95 | 0x48 => PHA,
96 | 0x49 => EOR,
97 | 0x4A => LSR,
98 | 0x4B => ALR,
99 | 0x4C => JMP,
100 | 0x4D => EOR,
101 | 0x4E => LSR_Mem,
102 | 0x4F => SRE,
103 | 0x50 => BVC,
104 | 0x51 => EOR,
105 | 0x52 => HLT,
106 | 0x53 => SRE,
107 | 0x54 => NOP,
108 | 0x55 => EOR,
109 | 0x56 => LSR_Mem,
110 | 0x57 => SRE,
111 | 0x58 => CLI,
112 | 0x59 => EOR,
113 | 0x5A => NOP,
114 | 0x5B => SRE,
115 | 0x5C => NOP,
116 | 0x5D => EOR,
117 | 0x5E => LSR_Mem,
118 | 0x5F => SRE,
119 | 0x60 => RTS,
120 | 0x61 => ADC,
121 | 0x62 => HLT,
122 | 0x63 => RRA,
123 | 0x64 => NOP,
124 | 0x65 => ADC,
125 | 0x66 => ROR_Mem,
126 | 0x67 => RRA,
127 | 0x68 => PLA,
128 | 0x69 => ADC,
129 | 0x6A => ROR,
130 | 0x6B => ARR,
131 | 0x6C => JMP,
132 | 0x6D => ADC,
133 | 0x6E => ROR_Mem,
134 | 0x6F => RRA,
135 | 0x70 => BVS,
136 | 0x71 => ADC,
137 | 0x72 => HLT,
138 | 0x73 => RRA,
139 | 0x74 => NOP,
140 | 0x75 => ADC,
141 | 0x76 => ROR_Mem,
142 | 0x77 => RRA,
143 | 0x78 => SEI,
144 | 0x79 => ADC,
145 | 0x7A => NOP,
146 | 0x7B => RRA,
147 | 0x7C => NOP,
148 | 0x7D => ADC,
149 | 0x7E => ROR_Mem,
150 | 0x7F => RRA,
151 | 0x80 => NOP,
152 | 0x81 => STA,
153 | 0x82 => NOP,
154 | 0x83 => SAX,
155 | 0x84 => STY,
156 | 0x85 => STA,
157 | 0x86 => STX,
158 | 0x87 => SAX,
159 | 0x88 => DEY,
160 | 0x89 => NOP,
161 | 0x8A => TXA,
162 | 0x8B => ANE,
163 | 0x8C => STY,
164 | 0x8D => STA,
165 | 0x8E => STX,
166 | 0x8F => SAX,
167 | 0x90 => BCC,
168 | 0x91 => STA,
169 | 0x92 => HLT,
170 | 0x93 => SHA,
171 | 0x94 => STY,
172 | 0x95 => STA,
173 | 0x96 => STX,
174 | 0x97 => SAX,
175 | 0x98 => TYA,
176 | 0x99 => STA,
177 | 0x9A => TXS,
178 | 0x9B => TAS,
179 | 0x9C => SHY,
180 | 0x9D => STA,
181 | 0x9E => SHX,
182 | 0x9F => SHA,
183 | 0xA0 => LDY,
184 | 0xA1 => LDA,
185 | 0xA2 => LDX,
186 | 0xA3 => LAX,
187 | 0xA4 => LDY,
188 | 0xA5 => LDA,
189 | 0xA6 => LDX,
190 | 0xA7 => LAX,
191 | 0xA8 => TAY,
192 | 0xA9 => LDA,
193 | 0xAA => TAX,
194 | 0xAB => LXA,
195 | 0xAC => LDY,
196 | 0xAD => LDA,
197 | 0xAE => LDX,
198 | 0xAF => LAX,
199 | 0xB0 => BCS,
200 | 0xB1 => LDA,
201 | 0xB2 => HLT,
202 | 0xB3 => LAX,
203 | 0xB4 => LDY,
204 | 0xB5 => LDA,
205 | 0xB6 => LDX,
206 | 0xB7 => LAX,
207 | 0xB8 => CLV,
208 | 0xB9 => LDA,
209 | 0xBA => TSX,
210 | 0xBB => LAS,
211 | 0xBC => LDY,
212 | 0xBD => LDA,
213 | 0xBE => LDX,
214 | 0xBF => LAX,
215 | 0xC0 => CPY,
216 | 0xC1 => CMP,
217 | 0xC2 => NOP,
218 | 0xC3 => DCP,
219 | 0xC4 => CPY,
220 | 0xC5 => CMP,
221 | 0xC6 => DEC,
222 | 0xC7 => DCP,
223 | 0xC8 => INY,
224 | 0xC9 => CMP,
225 | 0xCA => DEX,
226 | 0xCB => SBX,
227 | 0xCC => CPY,
228 | 0xCD => CMP,
229 | 0xCE => DEC,
230 | 0xCF => DCP,
231 | 0xD0 => BNE,
232 | 0xD1 => CMP,
233 | 0xD2 => HLT,
234 | 0xD3 => DCP,
235 | 0xD4 => NOP,
236 | 0xD5 => CMP,
237 | 0xD6 => DEC,
238 | 0xD7 => DCP,
239 | 0xD8 => CLD,
240 | 0xD9 => CMP,
241 | 0xDA => NOP,
242 | 0xDB => DCP,
243 | 0xDC => NOP,
244 | 0xDD => CMP,
245 | 0xDE => DEC,
246 | 0xDF => DCP,
247 | 0xE0 => CPX,
248 | 0xE1 => SBC,
249 | 0xE2 => NOP,
250 | 0xE3 => ISC,
251 | 0xE4 => CPX,
252 | 0xE5 => SBC,
253 | 0xE6 => INC,
254 | 0xE7 => ISC,
255 | 0xE8 => INX,
256 | 0xE9 => SBC,
257 | 0xEA => NOP,
258 | 0xEB => USB,
259 | 0xEC => CPX,
260 | 0xED => SBC,
261 | 0xEE => INC,
262 | 0xEF => ISC,
263 | 0xF0 => BEQ,
264 | 0xF1 => SBC,
265 | 0xF2 => HLT,
266 | 0xF3 => ISC,
267 | 0xF4 => NOP,
268 | 0xF5 => SBC,
269 | 0xF6 => INC,
270 | 0xF7 => ISC,
271 | 0xF8 => SED,
272 | 0xF9 => SBC,
273 | 0xFA => NOP,
274 | 0xFB => ISC,
275 | 0xFC => NOP,
276 | 0xFD => SBC,
277 | 0xFE => INC,
278 | 0xFF => ISC,
279 |
280 | _ => throw new ArgumentOutOfRangeException()
281 | };
282 | }
283 | }
284 | }
--------------------------------------------------------------------------------
/CS64.Core/CPU/MC6502State.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.Design;
3 | using System.Drawing;
4 | using System.IO;
5 | using System.Runtime.CompilerServices;
6 |
7 | namespace CS64.Core.CPU
8 | {
9 | public enum InterruptTypeEnum
10 | {
11 | NMI,
12 | IRQ,
13 | BRK
14 | }
15 |
16 | public partial class MC6502State
17 | {
18 | public byte[] RAM = new byte[0x10000];
19 | public byte[] KERNAL = new byte[0x2000];
20 | public byte[] BASIC = new byte[0x2000];
21 | public byte[] CHAROM = new byte[0x1000];
22 | public byte[] COLOR = new byte[0x400];
23 |
24 | public uint A;
25 | public uint X;
26 | public uint Y;
27 | public uint S;
28 |
29 | public uint PC;
30 | public uint N; // bit 7
31 | public uint V; // bit 6
32 | public uint U; // bit 5
33 | public uint B; // bit 4
34 | public uint D; // bit 3
35 | public uint I { get; set; } // bit 2
36 | public uint Z; // bit 1
37 | public uint C; // bit 0
38 |
39 | public uint Cycles;
40 | public uint Instructions;
41 |
42 | public bool BusAvailable = true;
43 |
44 | public uint dma_page;
45 | public uint dma_address;
46 | public byte dma_data;
47 |
48 | public bool dma_transfer;
49 | public bool dma_dummy;
50 |
51 |
52 | public uint P
53 | {
54 | get =>
55 | (uint)(
56 | (N << 7) +
57 | (V << 6) +
58 | (U << 5) +
59 | (B << 4) +
60 | (D << 3) +
61 | (I << 2) +
62 | (Z << 1) +
63 | (C << 0)
64 | );
65 | set
66 | {
67 | N = (value >> 7 & 1);
68 | V = (value >> 6 & 1);
69 | U = (value >> 5 & 1);
70 | B = (value >> 4 & 1);
71 | D = (value >> 3 & 1);
72 | I = (value >> 2 & 1);
73 | Z = (value >> 1 & 1);
74 | C = (value >> 0 & 1);
75 | }
76 | }
77 |
78 |
79 | public uint EffectiveAddr;
80 | public bool PageBoundsCrossed;
81 |
82 |
83 | public bool[] _interrupts = new bool[3];
84 |
85 | public void TriggerInterrupt(InterruptTypeEnum type)
86 | {
87 | if (I != 1 || type == InterruptTypeEnum.NMI)
88 | {
89 | _interrupts[(int)type] = true;
90 | }
91 | }
92 |
93 | public void WriteState(Stream stream)
94 | {
95 | var w = new BinaryWriter(stream);
96 | w.Write(RAM, 0, RAM.Length);
97 | w.Write((byte)A);
98 | w.Write((byte)X);
99 | w.Write((byte)Y);
100 | w.Write((byte)S);
101 | w.Write((byte)P);
102 | w.Write((ushort)PC);
103 | w.Write(Cycles);
104 | }
105 |
106 | public void ReadState(Stream stream)
107 | {
108 | var w = new BinaryReader(stream);
109 | w.Read(RAM, 0, RAM.Length);
110 | A = w.ReadByte();
111 | X = w.ReadByte();
112 | Y = w.ReadByte();
113 | S = w.ReadByte();
114 | P = w.ReadByte();
115 | PC = w.ReadUInt16();
116 | Cycles = w.ReadUInt32();
117 | }
118 |
119 | public void Reset()
120 | {
121 | // https://www.pagetable.com/?p=410
122 | S = 0xFD; // Actually 0xFF, but 3 Stack Pushes are done with writes supressed,
123 | P = 0x24; // Just to align with nestest.log: I is set, U shouldn't exist, but...
124 | PC = BusRead(0xFFFC) + BusRead(0xFFFD) * 0x100; // Fetch the reset vector
125 | I = 1;
126 | Cycles = 7; // takes 7 cycles to reset
127 |
128 | //IO_Port_DR = 0x1F; // enable KERNAL, I/O, BASIC
129 |
130 | _instructionCyclesLeft = 0;
131 | dma_page = 0;
132 | dma_address = 0x00;
133 | dma_transfer = false;
134 | for (var i = 0; i < _interrupts.Length; i++)
135 | {
136 | _interrupts[i] = false;
137 | }
138 | }
139 |
140 | public uint BusRead(uint address)
141 | {
142 | uint data = address switch
143 | {
144 | >= 0xE000 and <= 0xFFFF => KERNAL[address - 0xE000],
145 | >= 0xA000 and <= 0xBFFF => BASIC[address - 0xA000],
146 | >= 0xD000 and <= 0xDFFF => CharROMRead(address),
147 | 0x0001 => IO_Port_DR,
148 | 0x0000 => IO_Port_DDR,
149 | _ => RAM[address]
150 | };
151 |
152 | return data;
153 | }
154 |
155 | public void BusWrite(uint address, uint data)
156 | {
157 | switch (address)
158 | {
159 | case >= 0xD000 and <= 0xDFFF:
160 | CharROMWrite(address, data);
161 | break;
162 | case 0x0001:
163 | _io_port = data;
164 | UpdateIOPort();
165 | break;
166 | case 0x0000:
167 | IO_Port_DDR = data;
168 | UpdateIOPort();
169 | break;
170 | case >= 0 and <= 0xFFFF:
171 | // fall through to RAM
172 | RAM[address] = (byte)data;
173 | break;
174 | }
175 | }
176 |
177 | private void UpdateIOPort()
178 | {
179 | var output = _io_port;
180 | output &= IO_Port_DDR;
181 | IO_Port_DR &= (IO_Port_DDR ^ 0xFF);
182 | IO_Port_DR |= output;
183 | }
184 |
185 | // https://www.c64-wiki.com/wiki/Bank_Switching
186 |
187 | private uint _io_port;
188 | //data direction register
189 | public uint IO_Port_DDR;
190 | //data register
191 | public uint IO_Port_DR;
192 |
193 | public uint CharROMRead(uint address)
194 | {
195 | uint data = 0;
196 | // CHAREN = bit 2 of I/O
197 | if ((IO_Port_DR & 0x04) == 0x04)
198 | {
199 | data = address switch
200 | {
201 | >= 0xD000 and <= 0xD3FF => Vic.Read(address),
202 | >= 0xD400 and <= 0xD7FF => Sid.Read(address),
203 | // maybe OR with the upper nybble of previous contents of the data bus - for accuracy
204 | >= 0xD800 and <= 0xDBFF => (COLOR[address - 0xD800] & 0xFU),
205 | >= 0xDC00 and <= 0xDCFF => Cia1.Read(address), // Keyboard
206 | >= 0xDD00 and <= 0xDDFF => Cia2.Read(address), // VIC bankswitch
207 | >= 0xDE00 and <= 0xDEFF => 0, // I/O 1
208 | >= 0xDF00 and <= 0xDFFF => 0, // I/O 2
209 | };
210 | }
211 | else
212 | {
213 | data = CHAROM[address - 0xD000];
214 | }
215 | return data;
216 | }
217 |
218 | public void CharROMWrite(uint address, uint value)
219 | {
220 | // CHAREN = bit 2 of I/O port
221 | if ((IO_Port_DR & 0x04) == 0x04)
222 | {
223 | if (address >= 0xD000 && address <= 0xD3FF)
224 | {
225 | Vic.Write(address, value);
226 | }
227 | else if (address >= 0xD400 && address <= 0xD7FF)
228 | {
229 | Sid.Write(address, value);
230 | }
231 | else if (address >= 0xD800 && address <= 0xDBFF)
232 | {
233 | // Technically, only the lower 4 bits are connected
234 | COLOR[address - 0xD800] = (byte)(COLOR[address - 0xD800] | (value & 0xFU));
235 | }
236 | else if (address >= 0xDC00 && address <= 0xDCFF)
237 | {
238 | Cia1.Write(address, value);
239 | }
240 | else if (address >= 0xDD00 && address <= 0xDDFF)
241 | {
242 | Cia2.Write(address, value); // VIC bankswitch
243 | }
244 | }
245 | else
246 | {
247 | // fall through to RAM
248 | RAM[address] = (byte)value;
249 | }
250 | }
251 |
252 |
253 |
254 | public uint NonMaskableInterrupt()
255 | {
256 | Push((PC >> 8) & 0xFF); // Push the high byte of the PC
257 | Push((PC & 0xFF)); // Push the low byte of the PC
258 | B = 0;
259 | U = 1;
260 | Push(P);
261 | I = 1;
262 | PC = BusRead(0xFFFA) + BusRead(0xFFFB) * 0x100; // Jump to NMI handler
263 | return 7;
264 | }
265 |
266 |
267 | public uint InterruptRequest()
268 | {
269 | Push((PC >> 8) & 0xFF); // Push the high byte of the PC
270 | Push((PC & 0xFF)); // Push the low byte of the PC
271 | B = 0;
272 | U = 1;
273 | Push(P);
274 | I = 1;
275 | PC = BusRead(0xFFFE) + BusRead(0xFFFF) * 0x100; // Jump to IRQ handler
276 | return 7;
277 | }
278 |
279 |
280 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
281 | public uint Push(uint value)
282 | {
283 | BusWrite(S + 0x100, value);
284 | S -= 1;
285 | return 0;
286 | }
287 |
288 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
289 | public uint Pop()
290 | {
291 | S += 1;
292 | return BusRead(S + 0x100);
293 | }
294 |
295 | public void LoadMemory(uint address, byte[] data)
296 | {
297 | if (address == 0xE000)
298 | {
299 | Array.Copy(data, 0, KERNAL, 0, data.Length);
300 | }
301 | else if (address == 0xA000)
302 | {
303 | Array.Copy(data, 0, BASIC, 0, data.Length);
304 | }
305 | else if (address == 0xD000)
306 | {
307 | Array.Copy(data, 0, CHAROM, 0, data.Length);
308 | }
309 | }
310 | }
311 | }
312 |
--------------------------------------------------------------------------------
/CS64.Core/CPU/OpCodeDelegate.cs:
--------------------------------------------------------------------------------
1 | namespace CS64.Core.CPU
2 | {
3 | public delegate uint OpCodeDelegate(MC6502State cpu);
4 | }
--------------------------------------------------------------------------------
/CS64.Core/CS64.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | CS64.Core
6 | CS64.Core
7 | latest
8 |
9 |
10 |
11 | true
12 |
13 |
14 |
15 | true
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | PreserveNewest
27 |
28 |
29 | PreserveNewest
30 |
31 |
32 | PreserveNewest
33 |
34 |
35 | PreserveNewest
36 |
37 |
38 | PreserveNewest
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/CS64.Core/Fonts/Modeseven.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RupertAvery/CS64/eb089999233654cc494235ad89267d8ae60d38de/CS64.Core/Fonts/Modeseven.ttf
--------------------------------------------------------------------------------
/CS64.Core/Interface/AudioProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 | using static SDL2.SDL;
4 |
5 | namespace CS64.Core.Interface
6 | {
7 | public class AudioProvider : IDisposable
8 | {
9 | private const uint AUDIO_SAMPLE_FULL_THRESHOLD = 2048;
10 | private const int SAMPLES_PER_CALLBACK = 32; // 735 = 44100 samples / 60 fps // 367.5? 1470
11 |
12 | private readonly IntPtr _audioTempBufPtr = Marshal.AllocHGlobal(16384);
13 | private SDL_AudioSpec _want, _have;
14 | private uint _audioDevice;
15 |
16 | public void Initialize()
17 | {
18 | _want.channels = 2;
19 | _want.freq = 44100;
20 | _want.samples = SAMPLES_PER_CALLBACK;
21 | _want.format = AUDIO_S16LSB;
22 | _audioDevice = SDL_OpenAudioDevice(null, 0, ref _want, out _have, (int)SDL_AUDIO_ALLOW_FORMAT_CHANGE);
23 | SDL_PauseAudioDevice(_audioDevice, 0);
24 | }
25 |
26 | public void AudioReady(short[] data)
27 | {
28 | // Don't queue audio if too much is in buffer
29 | if (GetAudioSamplesInQueue() < AUDIO_SAMPLE_FULL_THRESHOLD)
30 | {
31 | int bytes = sizeof(short) * data.Length;
32 |
33 | Marshal.Copy(data, 0, _audioTempBufPtr, data.Length);
34 |
35 | // Console.WriteLine("Outputting samples to SDL");
36 |
37 | SDL_QueueAudio(_audioDevice, _audioTempBufPtr, (uint)bytes);
38 | }
39 | }
40 |
41 | public uint GetAudioSamplesInQueue()
42 | {
43 | return SDL_GetQueuedAudioSize(_audioDevice) / sizeof(short);
44 | }
45 |
46 | public void Dispose()
47 | {
48 | Marshal.FreeHGlobal(_audioTempBufPtr);
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/CS64.Core/Interface/DisplayMode.cs:
--------------------------------------------------------------------------------
1 | namespace CS64.Core.Interface
2 | {
3 | public enum DisplayMode
4 | {
5 | Ratio1x1,
6 | Ratio4x3,
7 | Ratio8x7,
8 | Stretched,
9 | }
10 | }
--------------------------------------------------------------------------------
/CS64.Core/Interface/IMainInterface.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CS64.Core.Interface.Input;
3 |
4 | namespace CS64.Core.Interface
5 | {
6 | public interface IMainInterface
7 | {
8 |
9 | IntPtr Handle { get; }
10 | Action OnHostResize { get; set; }
11 | Func LoadRom { get; set; }
12 | Action ChangeSlot { get; set; }
13 | Action LoadState { get; set; }
14 | Action SaveState { get; set; }
15 | Action ResizeWindow { get; set; }
16 | Action SetMapping { get; set; }
17 | }
18 | }
--------------------------------------------------------------------------------
/CS64.Core/Interface/Input/AnsiKeyboard.cs:
--------------------------------------------------------------------------------
1 | using static SDL2.SDL;
2 |
3 | namespace CS64.Core.Interface.Input
4 | {
5 | public class AnsiKeyboard : Keyboard
6 | {
7 | public override void SetupBinding()
8 | {
9 | //TODO: Bug - when holding SHIFT and alternating between characters rapidly,
10 | // you may end up with unshifted characters
11 | Bind(SDL_Keycode.SDLK_UP, new[] { InputKeyEnum.LSHIFT, InputKeyEnum.CURSOR_DOWN });
12 | Bind(SDL_Keycode.SDLK_DOWN, new[] { InputKeyEnum.CURSOR_DOWN });
13 | Bind(SDL_Keycode.SDLK_LEFT, new[] { InputKeyEnum.LSHIFT, InputKeyEnum.CURSOR_RIGHT });
14 | Bind(SDL_Keycode.SDLK_RIGHT, new[] { InputKeyEnum.CURSOR_RIGHT });
15 |
16 | BindWithShift(SDL_Keycode.SDLK_BACKSPACE, InputKeyEnum.DELETE);
17 | Bind(SDL_Keycode.SDLK_SPACE, new[] { InputKeyEnum.SPACE });
18 | Bind(SDL_Keycode.SDLK_RETURN, new[] { InputKeyEnum.RETURN });
19 |
20 | Bind(SDL_Keycode.SDLK_LSHIFT, SDL_Keymod.KMOD_LSHIFT, InputKeyEnum.LSHIFT);
21 | // When releasing shift, the modifer is gone!
22 | // Need to bind to this as well or we won't detect when SHIFT was released
23 | Bind(SDL_Keycode.SDLK_LSHIFT, InputKeyEnum.LSHIFT);
24 | Bind(SDL_Keycode.SDLK_RSHIFT, SDL_Keymod.KMOD_RSHIFT, InputKeyEnum.RSHIFT);
25 | Bind(SDL_Keycode.SDLK_RSHIFT, InputKeyEnum.RSHIFT);
26 |
27 | Bind(SDL_Keycode.SDLK_COLON, new[] { InputKeyEnum.COLON });
28 | Bind(SDL_Keycode.SDLK_SEMICOLON, new[] { InputKeyEnum.SEMICOLON });
29 | Bind(SDL_Keycode.SDLK_QUOTE, new[] { InputKeyEnum.LSHIFT, InputKeyEnum.D7 });
30 | Bind(SDL_Keycode.SDLK_QUOTE, SDL_Keymod.KMOD_LSHIFT, new[] { InputKeyEnum.LSHIFT, InputKeyEnum.D2 });
31 | Bind(SDL_Keycode.SDLK_QUOTE, SDL_Keymod.KMOD_RSHIFT, new[] { InputKeyEnum.RSHIFT, InputKeyEnum.D2 });
32 |
33 | Bind(SDL_Keycode.SDLK_COMMA, new[] { InputKeyEnum.COMMA });
34 | Bind(SDL_Keycode.SDLK_PERIOD, new[] { InputKeyEnum.PERIOD });
35 |
36 | BindWithShift(SDL_Keycode.SDLK_HOME, InputKeyEnum.HOME);
37 | BindWithShift(SDL_Keycode.SDLK_ESCAPE, InputKeyEnum.STOP);
38 | //_keyMapping.Add(SDL_Keycode.SDLK_ESCAPE, new[] { InputKeyEnum.RESTORE });
39 | Bind(SDL_Keycode.SDLK_LALT, new[] { InputKeyEnum.CTRL });
40 | Bind(SDL_Keycode.SDLK_RALT, new[] { InputKeyEnum.CTRL });
41 |
42 | Bind(SDL_Keycode.SDLK_SLASH, new[] { InputKeyEnum.SLASH });
43 | Bind(SDL_Keycode.SDLK_LEFTBRACKET, new[] { InputKeyEnum.LSHIFT, InputKeyEnum.COLON });
44 | Bind(SDL_Keycode.SDLK_RIGHTBRACKET, new[] { InputKeyEnum.LSHIFT, InputKeyEnum.SEMICOLON });
45 |
46 | Bind(SDL_Keycode.SDLK_EQUALS, SDL_Keymod.KMOD_LSHIFT, new[] { InputKeyEnum.PLUS });
47 | Bind(SDL_Keycode.SDLK_EQUALS, SDL_Keymod.KMOD_RSHIFT, new[] { InputKeyEnum.PLUS });
48 |
49 | Bind(SDL_Keycode.SDLK_2, SDL_Keymod.KMOD_LSHIFT, new[] { InputKeyEnum.AT });
50 | Bind(SDL_Keycode.SDLK_2, SDL_Keymod.KMOD_RSHIFT, new[] { InputKeyEnum.AT });
51 | Bind(SDL_Keycode.SDLK_6, SDL_Keymod.KMOD_LSHIFT, new[] { InputKeyEnum.CARET });
52 | Bind(SDL_Keycode.SDLK_6, SDL_Keymod.KMOD_RSHIFT, new[] { InputKeyEnum.CARET });
53 | Bind(SDL_Keycode.SDLK_7, SDL_Keymod.KMOD_LSHIFT, new[] { InputKeyEnum.POUND });
54 | Bind(SDL_Keycode.SDLK_7, SDL_Keymod.KMOD_RSHIFT, new[] { InputKeyEnum.POUND });
55 | Bind(SDL_Keycode.SDLK_8, SDL_Keymod.KMOD_LSHIFT, new[] { InputKeyEnum.ASTERISK });
56 | Bind(SDL_Keycode.SDLK_8, SDL_Keymod.KMOD_RSHIFT, new[] { InputKeyEnum.ASTERISK });
57 | Bind(SDL_Keycode.SDLK_9, SDL_Keymod.KMOD_LSHIFT, new[] { InputKeyEnum.LSHIFT, InputKeyEnum.D8 });
58 | Bind(SDL_Keycode.SDLK_9, SDL_Keymod.KMOD_RSHIFT, new[] { InputKeyEnum.RSHIFT, InputKeyEnum.D8 });
59 | Bind(SDL_Keycode.SDLK_0, SDL_Keymod.KMOD_LSHIFT, new[] { InputKeyEnum.LSHIFT, InputKeyEnum.D9 });
60 | Bind(SDL_Keycode.SDLK_0, SDL_Keymod.KMOD_RSHIFT, new[] { InputKeyEnum.RSHIFT, InputKeyEnum.D9 });
61 |
62 | Bind(SDL_Keycode.SDLK_EQUALS, new[] { InputKeyEnum.EQUALS });
63 | Bind(SDL_Keycode.SDLK_MINUS, new[] { InputKeyEnum.MINUS });
64 | Bind(SDL_Keycode.SDLK_BACKSLASH, new[] { InputKeyEnum.EQUALS });
65 |
66 | BindWithShift(SDL_Keycode.SDLK_1, InputKeyEnum.D1);
67 | Bind(SDL_Keycode.SDLK_2, InputKeyEnum.D2);
68 | BindWithShift(SDL_Keycode.SDLK_3, InputKeyEnum.D3);
69 | BindWithShift(SDL_Keycode.SDLK_4, InputKeyEnum.D4);
70 | BindWithShift(SDL_Keycode.SDLK_5, InputKeyEnum.D5);
71 | Bind(SDL_Keycode.SDLK_6, InputKeyEnum.D6);
72 | Bind(SDL_Keycode.SDLK_7, InputKeyEnum.D7);
73 | Bind(SDL_Keycode.SDLK_8, InputKeyEnum.D8);
74 | Bind(SDL_Keycode.SDLK_9, InputKeyEnum.D9);
75 | Bind(SDL_Keycode.SDLK_0, InputKeyEnum.D0);
76 |
77 | BindWithShift(SDL_Keycode.SDLK_a, InputKeyEnum.A);
78 | BindWithShift(SDL_Keycode.SDLK_b, InputKeyEnum.B);
79 | BindWithShift(SDL_Keycode.SDLK_c, InputKeyEnum.C);
80 | BindWithShift(SDL_Keycode.SDLK_d, InputKeyEnum.D);
81 | BindWithShift(SDL_Keycode.SDLK_e, InputKeyEnum.E);
82 | BindWithShift(SDL_Keycode.SDLK_f, InputKeyEnum.F);
83 | BindWithShift(SDL_Keycode.SDLK_g, InputKeyEnum.G);
84 | BindWithShift(SDL_Keycode.SDLK_h, InputKeyEnum.H);
85 | BindWithShift(SDL_Keycode.SDLK_i, InputKeyEnum.I);
86 | BindWithShift(SDL_Keycode.SDLK_j, InputKeyEnum.J);
87 | BindWithShift(SDL_Keycode.SDLK_k, InputKeyEnum.K);
88 | BindWithShift(SDL_Keycode.SDLK_l, InputKeyEnum.L);
89 | BindWithShift(SDL_Keycode.SDLK_m, InputKeyEnum.M);
90 | BindWithShift(SDL_Keycode.SDLK_n, InputKeyEnum.N);
91 | BindWithShift(SDL_Keycode.SDLK_o, InputKeyEnum.O);
92 | BindWithShift(SDL_Keycode.SDLK_p, InputKeyEnum.P);
93 | BindWithShift(SDL_Keycode.SDLK_q, InputKeyEnum.Q);
94 | BindWithShift(SDL_Keycode.SDLK_r, InputKeyEnum.R);
95 | BindWithShift(SDL_Keycode.SDLK_s, InputKeyEnum.S);
96 | BindWithShift(SDL_Keycode.SDLK_t, InputKeyEnum.T);
97 | BindWithShift(SDL_Keycode.SDLK_u, InputKeyEnum.U);
98 | BindWithShift(SDL_Keycode.SDLK_v, InputKeyEnum.V);
99 | BindWithShift(SDL_Keycode.SDLK_w, InputKeyEnum.W);
100 | BindWithShift(SDL_Keycode.SDLK_x, InputKeyEnum.X);
101 | BindWithShift(SDL_Keycode.SDLK_y, InputKeyEnum.Y);
102 | BindWithShift(SDL_Keycode.SDLK_z, InputKeyEnum.Z);
103 | }
104 | }
105 | }
--------------------------------------------------------------------------------
/CS64.Core/Interface/Input/C64Keyboard.cs:
--------------------------------------------------------------------------------
1 | using static SDL2.SDL;
2 |
3 | namespace CS64.Core.Interface.Input
4 | {
5 | public class C64Keyboard : Keyboard
6 | {
7 | public override void SetupBinding()
8 | {
9 | BindWithShift(SDL_Keycode.SDLK_UP, InputKeyEnum.CURSOR_DOWN);
10 | BindWithShift(SDL_Keycode.SDLK_DOWN, InputKeyEnum.CURSOR_DOWN);
11 | BindWithShift(SDL_Keycode.SDLK_LEFT, InputKeyEnum.CURSOR_RIGHT);
12 | BindWithShift(SDL_Keycode.SDLK_RIGHT, InputKeyEnum.CURSOR_RIGHT);
13 |
14 | BindWithShift(SDL_Keycode.SDLK_DELETE, InputKeyEnum.DELETE);
15 | BindWithShift(SDL_Keycode.SDLK_BACKSPACE, InputKeyEnum.BACKSPACE);
16 | BindWithShift(SDL_Keycode.SDLK_SPACE, InputKeyEnum.SPACE);
17 | BindWithShift(SDL_Keycode.SDLK_RETURN, InputKeyEnum.RETURN);
18 |
19 | Bind(SDL_Keycode.SDLK_LSHIFT, SDL_Keymod.KMOD_LSHIFT, InputKeyEnum.LSHIFT);
20 | // When releasing shift, the modifer is gone!
21 | // Need to bind to this as well or we won't detect when SHIFT was released
22 | Bind(SDL_Keycode.SDLK_LSHIFT, InputKeyEnum.LSHIFT);
23 |
24 | Bind(SDL_Keycode.SDLK_RSHIFT, SDL_Keymod.KMOD_RSHIFT, InputKeyEnum.RSHIFT);
25 | Bind(SDL_Keycode.SDLK_RSHIFT, InputKeyEnum.RSHIFT);
26 |
27 | BindWithShift(SDL_Keycode.SDLK_SEMICOLON, InputKeyEnum.COLON);
28 | BindWithShift(SDL_Keycode.SDLK_QUOTE, InputKeyEnum.SEMICOLON);
29 |
30 | BindWithShift(SDL_Keycode.SDLK_COMMA, InputKeyEnum.COMMA);
31 | BindWithShift(SDL_Keycode.SDLK_PERIOD, InputKeyEnum.PERIOD);
32 |
33 | BindWithShift(SDL_Keycode.SDLK_HOME, InputKeyEnum.HOME);
34 | BindWithShift(SDL_Keycode.SDLK_END, InputKeyEnum.STOP);
35 | BindWithShift(SDL_Keycode.SDLK_ESCAPE, InputKeyEnum.RESTORE);
36 | BindWithShift(SDL_Keycode.SDLK_LALT, InputKeyEnum.CTRL);
37 | BindWithShift(SDL_Keycode.SDLK_RALT, InputKeyEnum.CTRL);
38 |
39 | BindWithShift(SDL_Keycode.SDLK_SLASH, InputKeyEnum.SLASH);
40 | BindWithShift(SDL_Keycode.SDLK_LEFTBRACKET, InputKeyEnum.AT);
41 | BindWithShift(SDL_Keycode.SDLK_RIGHTBRACKET, InputKeyEnum.ASTERISK);
42 |
43 | //TODO: CARET (uparrow)
44 | //_keyMapping.Add(SDL_Keycode.SDLK_BACKSLASH, InputKeyEnum.CARET);
45 | BindWithShift(SDL_Keycode.SDLK_EQUALS, InputKeyEnum.PLUS);
46 | BindWithShift(SDL_Keycode.SDLK_MINUS, InputKeyEnum.MINUS);
47 | BindWithShift(SDL_Keycode.SDLK_BACKSLASH, InputKeyEnum.EQUALS);
48 |
49 | BindWithShift(SDL_Keycode.SDLK_0, InputKeyEnum.D0);
50 | BindWithShift(SDL_Keycode.SDLK_1, InputKeyEnum.D1);
51 | BindWithShift(SDL_Keycode.SDLK_2, InputKeyEnum.D2);
52 | BindWithShift(SDL_Keycode.SDLK_3, InputKeyEnum.D3);
53 | BindWithShift(SDL_Keycode.SDLK_4, InputKeyEnum.D4);
54 | BindWithShift(SDL_Keycode.SDLK_5, InputKeyEnum.D5);
55 | BindWithShift(SDL_Keycode.SDLK_6, InputKeyEnum.D6);
56 | BindWithShift(SDL_Keycode.SDLK_7, InputKeyEnum.D7);
57 | BindWithShift(SDL_Keycode.SDLK_8, InputKeyEnum.D8);
58 | BindWithShift(SDL_Keycode.SDLK_9, InputKeyEnum.D9);
59 |
60 | BindWithShift(SDL_Keycode.SDLK_a, InputKeyEnum.A);
61 | BindWithShift(SDL_Keycode.SDLK_b, InputKeyEnum.B);
62 | BindWithShift(SDL_Keycode.SDLK_c, InputKeyEnum.C);
63 | BindWithShift(SDL_Keycode.SDLK_d, InputKeyEnum.D);
64 | BindWithShift(SDL_Keycode.SDLK_e, InputKeyEnum.E);
65 | BindWithShift(SDL_Keycode.SDLK_f, InputKeyEnum.F);
66 | BindWithShift(SDL_Keycode.SDLK_g, InputKeyEnum.G);
67 | BindWithShift(SDL_Keycode.SDLK_h, InputKeyEnum.H);
68 | BindWithShift(SDL_Keycode.SDLK_i, InputKeyEnum.I);
69 | BindWithShift(SDL_Keycode.SDLK_j, InputKeyEnum.J);
70 | BindWithShift(SDL_Keycode.SDLK_k, InputKeyEnum.K);
71 | BindWithShift(SDL_Keycode.SDLK_l, InputKeyEnum.L);
72 | BindWithShift(SDL_Keycode.SDLK_m, InputKeyEnum.M);
73 | BindWithShift(SDL_Keycode.SDLK_n, InputKeyEnum.N);
74 | BindWithShift(SDL_Keycode.SDLK_o, InputKeyEnum.O);
75 | BindWithShift(SDL_Keycode.SDLK_p, InputKeyEnum.P);
76 | BindWithShift(SDL_Keycode.SDLK_q, InputKeyEnum.Q);
77 | BindWithShift(SDL_Keycode.SDLK_r, InputKeyEnum.R);
78 | BindWithShift(SDL_Keycode.SDLK_s, InputKeyEnum.S);
79 | BindWithShift(SDL_Keycode.SDLK_t, InputKeyEnum.T);
80 | BindWithShift(SDL_Keycode.SDLK_u, InputKeyEnum.U);
81 | BindWithShift(SDL_Keycode.SDLK_v, InputKeyEnum.V);
82 | BindWithShift(SDL_Keycode.SDLK_w, InputKeyEnum.W);
83 | BindWithShift(SDL_Keycode.SDLK_x, InputKeyEnum.X);
84 | BindWithShift(SDL_Keycode.SDLK_y, InputKeyEnum.Y);
85 | BindWithShift(SDL_Keycode.SDLK_z, InputKeyEnum.Z);
86 | }
87 | }
88 | }
--------------------------------------------------------------------------------
/CS64.Core/Interface/Input/Controller.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using static SDL2.SDL;
4 |
5 | namespace CS64.Core.Interface.Input
6 | {
7 | public class Controller
8 | {
9 | private IntPtr gamepad;
10 | private IntPtr joystick;
11 | private int instanceId;
12 |
13 | private readonly Dictionary _buttonMapping = new Dictionary();
14 |
15 | public int ControllerIndex { get; set; }
16 |
17 | public Controller()
18 | {
19 | //_buttonMapping.Add(11, InputKeyEnum.Up);
20 | //_buttonMapping.Add(12, InputKeyEnum.Down);
21 | //_buttonMapping.Add(13, InputKeyEnum.Left);
22 | //_buttonMapping.Add(14, InputKeyEnum.Right);
23 | //_buttonMapping.Add(3, InputKeyEnum.B);
24 | //_buttonMapping.Add(2, InputKeyEnum.A);
25 | //_buttonMapping.Add(1, InputKeyEnum.B);
26 | //_buttonMapping.Add(0, InputKeyEnum.A);
27 | //_buttonMapping.Add(4, InputKeyEnum.Select);
28 | //_buttonMapping.Add(6, InputKeyEnum.Start);
29 | }
30 |
31 | public bool TryMap(int button, out InputKeyEnum[] mappedInput)
32 | {
33 | return _buttonMapping.TryGetValue(button, out mappedInput);
34 | }
35 |
36 | public static bool TryOpen(int deviceId, out Controller controller)
37 | {
38 | controller = new Controller();
39 | controller.gamepad = SDL_GameControllerOpen(deviceId);
40 | controller.joystick = SDL_GameControllerGetJoystick(controller.gamepad);
41 | controller.instanceId = SDL_JoystickInstanceID(controller.joystick);
42 | return true;
43 | }
44 |
45 | public void Close()
46 | {
47 | SDL_GameControllerClose(gamepad);
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/CS64.Core/Interface/Input/InputEvent.cs:
--------------------------------------------------------------------------------
1 | namespace CS64.Core.Interface.Input
2 | {
3 | public delegate void InputEvent(object sender, InputEventArgs args);
4 | }
--------------------------------------------------------------------------------
/CS64.Core/Interface/Input/InputEventArgs.cs:
--------------------------------------------------------------------------------
1 | namespace CS64.Core.Interface.Input
2 | {
3 | public class InputEventArgs
4 | {
5 | public int Player { get; set; }
6 | public InputEventType EventType { get; set; }
7 | public InputKeyEnum[] Keys { get; set; }
8 | public int LightPenX { get; set; }
9 | public int LightPenY { get; set; }
10 | public bool LigntPenTrigger { get; set; }
11 | }
12 | }
--------------------------------------------------------------------------------
/CS64.Core/Interface/Input/InputEventType.cs:
--------------------------------------------------------------------------------
1 | namespace CS64.Core.Interface.Input
2 | {
3 | public enum InputEventType
4 | {
5 | BUTTON_UP,
6 | BUTTON_DOWN,
7 | POINT,
8 | TRIGGER
9 | }
10 | }
--------------------------------------------------------------------------------
/CS64.Core/Interface/Input/InputKeyEnum.cs:
--------------------------------------------------------------------------------
1 | namespace CS64.Core.Interface.Input
2 | {
3 | public enum InputKeyEnum
4 | {
5 | // Add offset for controller buttons to make things easier
6 | BACKSPACE,
7 | D1,
8 | D2,
9 | D3,
10 | D4,
11 | D5,
12 | D6,
13 | D7,
14 | D8,
15 | D9,
16 | D0,
17 | PLUS,
18 | MINUS,
19 | POUND,
20 | HOME,
21 | DELETE,
22 | CTRL,
23 | AT,
24 | ASTERISK,
25 | CARET,
26 | RESTORE,
27 | STOP,
28 | COLON,
29 | SEMICOLON,
30 | EQUALS,
31 | RETURN,
32 | COMMODORE,
33 | LSHIFT,
34 | COMMA,
35 | PERIOD,
36 | SLASH,
37 | RSHIFT,
38 | CURSOR_DOWN,
39 | CURSOR_RIGHT,
40 | F1,
41 | F3,
42 | F5,
43 | F7,
44 | A,
45 | B,
46 | C,
47 | D,
48 | E,
49 | F,
50 | G,
51 | H,
52 | I,
53 | J,
54 | K,
55 | L,
56 | M,
57 | N,
58 | O,
59 | P,
60 | Q,
61 | R,
62 | S,
63 | T,
64 | U,
65 | V,
66 | W,
67 | X,
68 | Y,
69 | Z,
70 | SPACE,
71 | // Standard buttons go here
72 | Rewind = 0x1001,
73 | FastForward = 0x1002,
74 | SaveState = 0x1003,
75 | LoadState = 0x1004,
76 | NextState = 0x1005,
77 | PreviousState = 0x1006,
78 | }
79 | }
--------------------------------------------------------------------------------
/CS64.Core/Interface/Input/InputProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using static SDL2.SDL;
4 |
5 | namespace CS64.Core.Interface.Input
6 | {
7 | public class InputProvider
8 | {
9 | private readonly Dictionary _deviceControllerMapping = new Dictionary();
10 |
11 | private readonly Keyboard _keyboard = new AnsiKeyboard();
12 |
13 | public InputEvent InputEvent { get; set; }
14 |
15 | public InputProvider(InputEvent inputEvent)
16 | {
17 | InputEvent = inputEvent;
18 | }
19 |
20 | public bool HandleEvent(SDL_MouseMotionEvent motionEvent)
21 | {
22 | // Get the position on screen where the Zapper is pointed at
23 | InputEvent?.Invoke(null, new InputEventArgs() { EventType = InputEventType.POINT, LightPenX = motionEvent.x, LightPenY = motionEvent.y });
24 | return true;
25 | }
26 |
27 | public bool HandleEvent(SDL_MouseButtonEvent buttonEvent)
28 | {
29 | InputEvent?.Invoke(null, new InputEventArgs() { EventType = InputEventType.TRIGGER });
30 | return true;
31 | }
32 |
33 | public bool HandleEvent(SDL_KeyboardEvent keyboardEvent)
34 | {
35 | var handled = false;
36 |
37 | switch (keyboardEvent.type)
38 | {
39 | case SDL_EventType.SDL_KEYUP:
40 | {
41 | if (_keyboard.TryMap(keyboardEvent.keysym.sym, keyboardEvent.keysym.mod, out InputKeyEnum[] mappedInput))
42 | {
43 | InputEvent?.Invoke(null, new InputEventArgs() { EventType = InputEventType.BUTTON_UP, Player = 0, Keys = mappedInput });
44 | handled = true;
45 | }
46 |
47 | break;
48 | }
49 | case SDL_EventType.SDL_KEYDOWN:
50 | {
51 | if (_mappingMode)
52 | {
53 | if (_keyboard.TrySetMap(keyboardEvent.keysym.sym, keyboardEvent.keysym.mod, _mappingTargets))
54 | {
55 | _mappingMode = false;
56 | handled = true;
57 | }
58 | }
59 | else
60 | {
61 | if (_keyboard.TryMap(keyboardEvent.keysym.sym, keyboardEvent.keysym.mod, out InputKeyEnum[] mappedInput))
62 | {
63 | InputEvent?.Invoke(null, new InputEventArgs() { EventType = InputEventType.BUTTON_DOWN, Player = 0, Keys = mappedInput });
64 | handled = true;
65 | }
66 | }
67 |
68 | break;
69 | }
70 | }
71 | return handled;
72 | }
73 |
74 | public bool HandleControllerEvent(SDL_ControllerButtonEvent buttonEvent)
75 | {
76 | var handled = false;
77 | switch (buttonEvent.type)
78 | {
79 | case SDL_EventType.SDL_CONTROLLERBUTTONUP:
80 | Console.WriteLine($"{buttonEvent.type} {buttonEvent.button} {buttonEvent.which}");
81 | {
82 | if (_deviceControllerMapping.TryGetValue(buttonEvent.which, out var controller))
83 | {
84 | if (controller.TryMap(buttonEvent.button, out InputKeyEnum[] mappedInput))
85 | {
86 | InputEvent?.Invoke(null, new InputEventArgs() { EventType = InputEventType.BUTTON_UP, Player = controller.ControllerIndex, Keys = mappedInput });
87 | handled = true;
88 | }
89 | }
90 | }
91 | break;
92 |
93 | case SDL_EventType.SDL_CONTROLLERBUTTONDOWN:
94 | Console.WriteLine($"{buttonEvent.type} {buttonEvent.button} {buttonEvent.which}");
95 | {
96 | if (_deviceControllerMapping.TryGetValue(buttonEvent.which, out var controller))
97 | {
98 | if (controller.TryMap(buttonEvent.button, out InputKeyEnum[] mappedInput))
99 | {
100 | InputEvent?.Invoke(null, new InputEventArgs() { EventType = InputEventType.BUTTON_DOWN, Player = controller.ControllerIndex, Keys = mappedInput });
101 | handled = true;
102 | }
103 | }
104 | }
105 | break;
106 |
107 | }
108 |
109 | return handled;
110 | }
111 |
112 | public void HandleDeviceEvent(SDL_ControllerDeviceEvent deviceEvent)
113 | {
114 | //Console.WriteLine($"{deviceEvent.type} {deviceEvent.which}");
115 |
116 | switch (deviceEvent.type)
117 | {
118 | case SDL_EventType.SDL_CONTROLLERDEVICEADDED:
119 | {
120 | if (Controller.TryOpen(deviceEvent.which, out var controller))
121 | {
122 | _deviceControllerMapping.Add(deviceEvent.which, controller);
123 | }
124 |
125 | }
126 | break;
127 | case SDL_EventType.SDL_CONTROLLERDEVICEREMOVED:
128 | {
129 | if (_deviceControllerMapping.TryGetValue(deviceEvent.which, out var controller))
130 | {
131 | controller.Close();
132 | _deviceControllerMapping.Remove(deviceEvent.which);
133 | }
134 | }
135 | break;
136 | }
137 | }
138 |
139 |
140 | private bool _mappingMode = false;
141 | private InputKeyEnum[] _mappingTargets;
142 |
143 | public void SetMapping(InputKeyEnum[] key)
144 | {
145 | _mappingMode = true;
146 | _mappingTargets = key;
147 | }
148 |
149 | public void ClearMapping(InputKeyEnum key)
150 | {
151 | //_mappingTarget = button;
152 | }
153 |
154 | public string GetMapping(InputKeyEnum[] key)
155 | {
156 | if (_keyboard.TryGetMap(key, out var mappedKey))
157 | {
158 | return mappedKey.ToString();
159 | }
160 |
161 | return "";
162 | }
163 | }
164 | }
--------------------------------------------------------------------------------
/CS64.Core/Interface/Input/KeyBinding.cs:
--------------------------------------------------------------------------------
1 | using static SDL2.SDL;
2 |
3 | namespace CS64.Core.Interface.Input
4 | {
5 | public struct KeyBinding
6 | {
7 | public KeyBinding(SDL_Keycode keyCode)
8 | {
9 | KeyCode = keyCode;
10 | Modifier = SDL_Keymod.KMOD_NONE;
11 | }
12 |
13 | public KeyBinding(SDL_Keycode keyCode, SDL_Keymod modifier)
14 | {
15 | KeyCode = keyCode;
16 | // My keyboard is stuck on NUM
17 | modifier &= (SDL_Keymod)((int)(SDL_Keymod.KMOD_NUM) ^ 0xFFFF);
18 | modifier &= (SDL_Keymod)((int)(SDL_Keymod.KMOD_CAPS) ^ 0xFFFF);
19 | Modifier = modifier;
20 | }
21 |
22 | public SDL_Keycode KeyCode { get; set; }
23 | public SDL_Keymod Modifier { get; set; }
24 |
25 | public override bool Equals(object obj)
26 | {
27 | return obj != null &&
28 | KeyCode == ((KeyBinding)obj).KeyCode &&
29 | Modifier == ((KeyBinding)obj).Modifier;
30 | }
31 |
32 | public override int GetHashCode()
33 | {
34 | unchecked // Overflow is fine, just wrap
35 | {
36 | int hash = 7199369;
37 | // Suitable nullity checks etc, of course :)
38 | hash = hash * 486187739 + KeyCode.GetHashCode();
39 | hash = hash * 486187739 + Modifier.GetHashCode();
40 | return hash;
41 | }
42 |
43 | //return HashCode.Combine(KeyCode, Modifier);
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/CS64.Core/Interface/Input/Keyboard.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using static SDL2.SDL;
4 |
5 | namespace CS64.Core.Interface.Input
6 | {
7 | public abstract class Keyboard
8 | {
9 |
10 | protected readonly Dictionary _keyMapping = new Dictionary();
11 |
12 | protected Keyboard()
13 | {
14 | SetupBinding();
15 | }
16 |
17 | public abstract void SetupBinding();
18 |
19 | public bool TryMap(SDL_Keycode keyCode, SDL_Keymod modifier, out InputKeyEnum[] mappedInputs)
20 | {
21 | return _keyMapping.TryGetValue(new KeyBinding(keyCode, modifier), out mappedInputs);
22 | }
23 |
24 | public bool TrySetMap(SDL_Keycode keyCode, SDL_Keymod modifier, InputKeyEnum[] target)
25 | {
26 | if (TryGetMap(target, out var oldkey))
27 | {
28 | _keyMapping.Remove(oldkey.Value);
29 | }
30 | _keyMapping[new KeyBinding(keyCode, modifier)] = target;
31 | return true;
32 | }
33 |
34 | public bool TryGetMap(InputKeyEnum[] key, out KeyBinding? mappedKey)
35 | {
36 | mappedKey = null;
37 |
38 | if (_keyMapping.ContainsValue(key))
39 | {
40 | var mapping = _keyMapping.First(m => m.Value == key);
41 | mappedKey = mapping.Key;
42 | return true;
43 | }
44 |
45 | return false;
46 | }
47 |
48 | protected void BindWithShift(SDL_Keycode keyCode, InputKeyEnum key)
49 | {
50 | _keyMapping.Add(new KeyBinding(keyCode), new[] { key });
51 | _keyMapping.Add(new KeyBinding(keyCode, SDL_Keymod.KMOD_LSHIFT), new[] { InputKeyEnum.LSHIFT, key });
52 | _keyMapping.Add(new KeyBinding(keyCode, SDL_Keymod.KMOD_RSHIFT), new[] { InputKeyEnum.RSHIFT, key });
53 | _keyMapping.Add(new KeyBinding(keyCode, SDL_Keymod.KMOD_ALT), new[] { InputKeyEnum.CTRL, key });
54 | }
55 |
56 | protected void Bind(SDL_Keycode keyCode, InputKeyEnum key)
57 | {
58 | _keyMapping.Add(new KeyBinding(keyCode), new[] { key });
59 | }
60 |
61 | protected void Bind(SDL_Keycode keyCode, InputKeyEnum[] keys)
62 | {
63 | _keyMapping.Add(new KeyBinding(keyCode), keys);
64 | }
65 |
66 | protected void Bind(SDL_Keycode keyCode, SDL_Keymod modifer, InputKeyEnum[] keys)
67 | {
68 | _keyMapping.Add(new KeyBinding(keyCode, modifer), keys);
69 | }
70 |
71 | protected void Bind(SDL_Keycode keyCode, SDL_Keymod modifer, InputKeyEnum key)
72 | {
73 | _keyMapping.Add(new KeyBinding(keyCode, modifer), new []{key});
74 | }
75 |
76 | }
77 | }
--------------------------------------------------------------------------------
/CS64.Core/Interface/KeyboardMatrix.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using CS64.Core.Interface.Input;
3 |
4 | namespace CS64.Core.Interface
5 | {
6 | public class KeyboardMatrix
7 | {
8 | // The COLUMN is stored in the first dimension (vertically here)
9 | // The ROW is stored in the second dimension (horizontally here)
10 | // COLUMN is the value strobed in CIA2 Port A
11 | // ROW is the value read in CIA2 Port B
12 |
13 | private static InputKeyEnum[,] _lookup = new InputKeyEnum[,]
14 | {
15 | {
16 | InputKeyEnum.STOP, InputKeyEnum.Q, InputKeyEnum.COMMODORE, InputKeyEnum.SPACE, InputKeyEnum.D2,
17 | InputKeyEnum.CTRL, InputKeyEnum.BACKSPACE, InputKeyEnum.D1
18 | },
19 | {
20 | InputKeyEnum.SLASH, InputKeyEnum.CARET, InputKeyEnum.EQUALS, InputKeyEnum.RSHIFT, InputKeyEnum.HOME,
21 | InputKeyEnum.SEMICOLON, InputKeyEnum.ASTERISK, InputKeyEnum.POUND
22 | },
23 | {
24 | InputKeyEnum.COMMA, InputKeyEnum.AT, InputKeyEnum.COLON, InputKeyEnum.PERIOD, InputKeyEnum.MINUS,
25 | InputKeyEnum.L, InputKeyEnum.P, InputKeyEnum.PLUS
26 | },
27 | {
28 | InputKeyEnum.N, InputKeyEnum.O, InputKeyEnum.K, InputKeyEnum.M, InputKeyEnum.D0, InputKeyEnum.J,
29 | InputKeyEnum.I, InputKeyEnum.D9
30 | },
31 | {
32 | InputKeyEnum.V, InputKeyEnum.U, InputKeyEnum.H, InputKeyEnum.B, InputKeyEnum.D8,
33 | InputKeyEnum.G, InputKeyEnum.Y, InputKeyEnum.D7
34 | },
35 | {
36 | InputKeyEnum.X, InputKeyEnum.T, InputKeyEnum.F, InputKeyEnum.C, InputKeyEnum.D6,
37 | InputKeyEnum.D, InputKeyEnum.R, InputKeyEnum.D5
38 | },
39 | {
40 | InputKeyEnum.LSHIFT, InputKeyEnum.E, InputKeyEnum.S, InputKeyEnum.Z, InputKeyEnum.D4,
41 | InputKeyEnum.A, InputKeyEnum.W, InputKeyEnum.D3
42 | },
43 | {
44 | InputKeyEnum.CURSOR_DOWN, InputKeyEnum.F5, InputKeyEnum.F3, InputKeyEnum.F1, InputKeyEnum.F7,
45 | InputKeyEnum.CURSOR_RIGHT, InputKeyEnum.RETURN, InputKeyEnum.DELETE
46 | },
47 | };
48 |
49 | public Dictionary _matrix = new Dictionary();
50 |
51 | public uint[] ColumnIndex = new uint[129];
52 |
53 | public KeyboardMatrix()
54 | {
55 | // Produce the COL,ROW values for each key in the lookup
56 | // We COULD have just hardcoded this, but I was feeling lazy
57 |
58 | for (int col = 0; col < 8; col++)
59 | {
60 | for (int row = 0; row < 8; row++)
61 | {
62 | var key = _lookup[col, row];
63 |
64 | _matrix.Add(key, new RowCol((uint)(1 << (7 - col)), (uint)(1 << (7 - row))));
65 | }
66 | }
67 |
68 | // This is for quick lookup from the inverse of strobe position (0x01 to 0x80) to a column index lookup (0-7)
69 | // Note that we create a 129-byte array, and most of it will be empty
70 |
71 | for (var i = 0; i < 8; i++)
72 | {
73 | ColumnIndex[1 << i] = (uint)i;
74 | }
75 | }
76 |
77 | public bool TryEncode(InputKeyEnum key, out RowCol rowCol)
78 | {
79 | return _matrix.TryGetValue(key, out rowCol);
80 | }
81 |
82 | }
83 | }
--------------------------------------------------------------------------------
/CS64.Core/Interface/Main.Keyboard.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CS64.Core.Interface.Input;
3 |
4 | namespace CS64.Core.Interface
5 | {
6 | public partial class Main
7 | {
8 | private readonly KeyboardMatrix _keyboardMatrix = new KeyboardMatrix();
9 | private bool DeletePressed = false;
10 |
11 | public uint key_col;
12 | private uint[] rowCols = new uint[8];
13 |
14 | private void SetupKeyboardScanRoutines()
15 | {
16 | _c64.Cia1.PortAWrite = PortAWrite;
17 | _c64.Cia1.PortBRead = PortBRead;
18 | }
19 |
20 | private uint PortBRead()
21 | {
22 | // Initially scan routine pulls all outputs low
23 | // (now high since we flipped it)
24 | if (key_col == 0xFF &&
25 | // check if anything pressed
26 | ((rowCols[0] |
27 | rowCols[1] |
28 | rowCols[2] |
29 | rowCols[3] |
30 | rowCols[4] |
31 | rowCols[5] |
32 | rowCols[6] |
33 | rowCols[7]) > 0)
34 |
35 | | DeletePressed
36 | )
37 | {
38 | // send any non-zero value to trigger scan routine
39 | return 0x01;
40 | }
41 |
42 | if (DeletePressed & key_col == 1)
43 | {
44 | return 0xFE;
45 | }
46 |
47 | // The scan routine will now have a shifting 1 bit (never 0xFF)
48 | // So we can compare each column test
49 | if (key_col != 0xFF)
50 | {
51 | // convert the column bit into an index
52 | var index = _keyboardMatrix.ColumnIndex[key_col];
53 | // invert the row bit because CIA expects the line to be pulled to 0
54 | return rowCols[index] ^ 0xFF;
55 | }
56 |
57 | return 0xFF;
58 | }
59 |
60 | private void PortAWrite(uint value)
61 | {
62 | // flip the output so we get a shifting 1 bit instead of 0
63 | key_col = value ^ 0xFF;
64 | }
65 |
66 | private void InputEvent(object sender, InputEventArgs args)
67 | {
68 | switch (args.EventType)
69 | {
70 | case InputEventType.BUTTON_UP:
71 | foreach (var key in args.Keys)
72 | {
73 | Console.Write(key);
74 | if (((uint)key & 0x1000) == 0x1000)
75 | {
76 | switch (key)
77 | {
78 | case InputKeyEnum.Rewind:
79 | _rewind = false;
80 | break;
81 | case InputKeyEnum.FastForward:
82 | _fastForward = false;
83 | break;
84 | }
85 | }
86 | else
87 | {
88 | if (key == InputKeyEnum.DELETE)
89 | {
90 | DeletePressed = false;
91 | }
92 | else
93 | {
94 | _keyboardMatrix.TryEncode(key, out var rowCol);
95 | var index = _keyboardMatrix.ColumnIndex[rowCol.Col];
96 | // Clear specific bit only
97 | rowCols[index] &= rowCol.Row ^ 0xFF;
98 | }
99 | }
100 | }
101 | Console.WriteLine();
102 |
103 | break;
104 | case InputEventType.BUTTON_DOWN:
105 |
106 | foreach (var key in args.Keys)
107 | {
108 | if (((uint)key & 0x1000) == 0x1000)
109 | {
110 | switch (key)
111 | {
112 | case InputKeyEnum.Rewind:
113 | _rewind = true;
114 | break;
115 | case InputKeyEnum.FastForward:
116 | _fastForward = true;
117 | break;
118 | case InputKeyEnum.SaveState:
119 | _saveStatePending = true;
120 | break;
121 | case InputKeyEnum.LoadState:
122 | _loadStatePending = true;
123 | break;
124 | case InputKeyEnum.NextState:
125 | break;
126 | case InputKeyEnum.PreviousState:
127 | break;
128 | }
129 | }
130 | else
131 | {
132 | // For some reason DELETE key doesn't work properly
133 | // so, force it to work
134 | if (key == InputKeyEnum.DELETE)
135 | {
136 | DeletePressed = true;
137 | }
138 | else
139 | {
140 | _keyboardMatrix.TryEncode(key, out var rowCol);
141 | var index = _keyboardMatrix.ColumnIndex[rowCol.Col];
142 | // Setr specific bit without erasing any other bits
143 | rowCols[index] |= rowCol.Row;
144 | }
145 | }
146 | }
147 |
148 | break;
149 | }
150 | }
151 | }
152 | }
--------------------------------------------------------------------------------
/CS64.Core/Interface/Main.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.IO.Compression;
4 | using System.Linq;
5 | using System.Runtime.CompilerServices;
6 | using System.Runtime.InteropServices;
7 | using System.Threading;
8 | using CS64.Core.CPU;
9 | using CS64.Core.Interface.Input;
10 | using static SDL2.SDL;
11 |
12 | namespace CS64.Core.Interface
13 | {
14 | public partial class Main : IDisposable
15 | {
16 | //PAL C64 master clock: 17.734475 MHz / 18
17 | //NTSC C64 master clock: 14.31818 MHz / 14
18 | //
19 | //CLOCK_PAL = 985248 Hz * 8
20 | //CLOCK_NTSC = 1022727 Hz * 8
21 |
22 |
23 | //CLOCK_VICII_PAL = 7881984 Hz / 60 = 131366 cycles / frame
24 | //CLOCK_VICII_NTSC = 8181816 Hz / 60 = 136363 cycles / frame
25 |
26 |
27 | public const int CYCLES_PER_FRAME = 131366;
28 |
29 | //private const double SECONDS_PER_FRAME = 1 / 60D;
30 | private const double SECONDS_PER_FRAME = 1 / 50D;
31 |
32 | private double _fps;
33 | private int _cyclesLeft;
34 | private long _cyclesRan;
35 | private Thread _emulationThread;
36 | private readonly AutoResetEvent _threadSync = new AutoResetEvent(false);
37 | private bool _saveStatePending;
38 | private bool _loadStatePending;
39 |
40 | private bool _running;
41 | private string _romPath;
42 | private bool _resetPending;
43 |
44 | private bool _paused;
45 | private bool _frameAdvance;
46 | private bool _rewind;
47 | private bool _fastForward;
48 | private int _stateSlot = 1;
49 |
50 | private MC6502State _c64;
51 |
52 | private uint _frames;
53 | private bool _hasState;
54 |
55 | public Action StateChanged { get; set; }
56 | private AudioProvider _audioProvider;
57 | private VideoProvider _videoProvider;
58 |
59 | private IntPtr Window;
60 |
61 | public InputProvider InputProvider { get; private set; }
62 | public int Width => _c64.Vic.Width;
63 | public int Height => _c64.Vic.Height;
64 |
65 | private Playback _playback;
66 |
67 |
68 |
69 | private void SaveState(Stream stream)
70 | {
71 | _c64.WriteState(stream);
72 | //_c64.Ppu.WriteState(stream);
73 | //_c64.Cartridge.WriteState(stream);
74 | }
75 |
76 | private void LoadState(Stream stream)
77 | {
78 | _c64.ReadState(stream);
79 | //_c64.Ppu.ReadState(stream);
80 | //_c64.Cartridge.ReadState(stream);
81 | }
82 |
83 | private string GetStateSavePath()
84 | {
85 | return Path.Combine(_romDirectory, $"{_romFilename}.s{_stateSlot:00}");
86 | }
87 |
88 | public void SaveState()
89 | {
90 | using (var file = new FileStream(GetStateSavePath(), FileMode.Create, FileAccess.Write))
91 | {
92 | SaveState(file);
93 | _videoProvider.SetMessage($"Saved State #{_stateSlot}");
94 | }
95 | _hasState = true;
96 | }
97 |
98 | public void LoadState()
99 | {
100 | var path = GetStateSavePath();
101 | if (File.Exists(path))
102 | {
103 | using (var file = new FileStream(path, FileMode.Open, FileAccess.Read))
104 | {
105 | LoadState(file);
106 | _videoProvider.SetMessage($"Loaded State #{_stateSlot}");
107 | }
108 | }
109 | }
110 |
111 | private int _scale = 4;
112 |
113 | public void SetMapping(InputKeyEnum o)
114 | {
115 | InputProvider.SetMapping(new[] { o });
116 | }
117 |
118 | public void Initialize(IMainInterface form)
119 | {
120 |
121 | form.OnHostResize = () =>
122 | {
123 | _videoProvider.Destroy();
124 | _videoProvider.Initialize();
125 | _videoProvider.Clear();
126 | };
127 |
128 | form.ChangeSlot = i =>
129 | {
130 | _stateSlot = i;
131 | _videoProvider.SetMessage($"Slot #{_stateSlot}");
132 | };
133 |
134 | form.SaveState = SaveState;
135 | form.LoadState = LoadState;
136 | //form.ResizeWindow = (width, height) =>
137 | //{
138 | // _paused = true;
139 | // Thread.Sleep(200);
140 | // //_videoProvider.Resize(width, height);
141 | // _paused = false;
142 | //};
143 | form.SetMapping = o =>
144 | {
145 | InputProvider.SetMapping(new[] { o });
146 | };
147 |
148 | form.LoadRom = s =>
149 | {
150 | try
151 | {
152 | LoadInternal(s);
153 | return true;
154 | }
155 | catch (Exception ex)
156 | {
157 | return false;
158 | }
159 | };
160 |
161 | _playback = new Playback()
162 | {
163 | LoadState = LoadState,
164 | SaveState = SaveState
165 | };
166 |
167 |
168 |
169 | _c64.Init();
170 | SetupKeyboardScanRoutines();
171 |
172 |
173 | SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK);
174 |
175 | SDL2.SDL_ttf.TTF_Init();
176 |
177 | Window = SDL_CreateWindowFrom(form.Handle);
178 |
179 | //Window = SDL_CreateWindow("Fami", 0, 0, WIDTH * _scale, HEIGHT * _scale,
180 | // SDL_WindowFlags.SDL_WINDOW_SHOWN | SDL_WindowFlags.SDL_WINDOW_RESIZABLE );
181 |
182 | _videoProvider = new VideoProvider(Window, _c64.Vic.Width, _c64.Vic.Height);
183 | _audioProvider = new AudioProvider();
184 | InputProvider = new InputProvider(InputEvent);
185 |
186 | _videoProvider.Initialize();
187 | _videoProvider.Clear();
188 | _audioProvider.Initialize();
189 |
190 | form.ChangeSlot(1);
191 |
192 |
193 |
194 | }
195 |
196 | private bool _justLoaded;
197 |
198 | private void LoadInternal(string path)
199 | {
200 | _paused = true;
201 | // Ensure frame rendering has completed so we don't overwrite anything while the emulation thread is busy
202 | Thread.Sleep(200);
203 | Load(path);
204 | _justLoaded = true;
205 | // prevent the first click fron firing when loading light gun games
206 | _paused = false;
207 | }
208 |
209 | public Main()
210 | {
211 | _c64 = new MC6502State();
212 | }
213 |
214 |
215 | public void EmulationThreadHandler()
216 | {
217 | void Render()
218 | {
219 | _videoProvider.Render(_c64.Vic.buffer);
220 |
221 | _c64.GetSamplesSync(out var lsamples, out int lnsamp);
222 |
223 | _audioProvider.AudioReady(lsamples);
224 | }
225 |
226 | while (_running)
227 | {
228 | try
229 | {
230 | _threadSync.WaitOne();
231 |
232 | if (_resetPending)
233 | {
234 | _c64.Reset();
235 | _resetPending = false;
236 | }
237 |
238 | RunFrame();
239 |
240 | while (_fastForward)
241 | {
242 | RunFrame();
243 | Render();
244 | }
245 |
246 | _playback.PerFrame(ref _rewind);
247 |
248 | if (_saveStatePending)
249 | {
250 | SaveState();
251 | _saveStatePending = false;
252 | }
253 |
254 | if (_loadStatePending)
255 | {
256 | LoadState();
257 | _loadStatePending = false;
258 | }
259 |
260 | Render();
261 |
262 | _c64.Cia1.TimeOfDay();
263 | _c64.Cia1.TimeOfDay();
264 |
265 | }
266 | catch (Exception e)
267 | {
268 | Console.WriteLine(e.ToString());
269 | //Excepted = true;
270 | }
271 | }
272 | }
273 |
274 | public void RunFrame()
275 | {
276 | _cyclesRan += CYCLES_PER_FRAME;
277 | _cyclesLeft += CYCLES_PER_FRAME;
278 |
279 | while (_cyclesLeft > 0)
280 | {
281 | _cyclesLeft -= (int)_c64.Step();
282 | }
283 |
284 |
285 | _frames++;
286 | }
287 |
288 | public void Test()
289 | {
290 | _c64.Init();
291 | _c64.Reset();
292 | _c64.Execute();
293 | }
294 |
295 | private string _romDirectory;
296 | private string _romFilename;
297 |
298 | public void LoadROM(string path, uint address, int size)
299 | {
300 | byte[] data = new byte[size];
301 |
302 | using (var f = File.Open(path, FileMode.Open, FileAccess.Read))
303 | {
304 | f.Read(data, 0, size);
305 | }
306 |
307 | _c64.LoadMemory(address, data);
308 | }
309 |
310 |
311 |
312 | public void Load(string rompath)
313 | {
314 | _romDirectory = "";
315 | _romFilename = "";
316 |
317 |
318 | if (Path.GetExtension(rompath).ToLower() == ".zip")
319 | {
320 | using var zipFile = ZipFile.Open(rompath, ZipArchiveMode.Read);
321 | var nesFile = zipFile.Entries.FirstOrDefault(z => Path.GetExtension(z.Name).ToLower() == ".nes");
322 | if (nesFile == default)
323 | {
324 | throw new Exception("No ROM found in archive!");
325 | }
326 | else
327 | {
328 | using var s = nesFile.Open();
329 | //cart = Cartridge.Load(s, _c64);
330 | }
331 | }
332 | else
333 | {
334 | //cart = Cartridge.Load(rompath, _c64);
335 | }
336 |
337 | _romDirectory = Path.GetDirectoryName(rompath);
338 | _romFilename = Path.GetFileNameWithoutExtension(rompath);
339 |
340 | //_c64.LoadCartridge(cart);
341 | _c64.Reset();
342 |
343 | _videoProvider.SetMessage($"Loaded {_romFilename}");
344 |
345 | if (_emulationThread == null)
346 | {
347 | _emulationThread = new Thread(EmulationThreadHandler);
348 | _emulationThread.Name = "Emulation Core";
349 | _emulationThread.Start();
350 | }
351 | }
352 |
353 |
354 | public void Run()
355 | {
356 | if (_emulationThread == null)
357 | {
358 | _emulationThread = new Thread(EmulationThreadHandler);
359 | _emulationThread.Name = "Emulation Core";
360 | _emulationThread.Start();
361 | }
362 |
363 | _running = true;
364 |
365 | var nextFrameAt = GetTime();
366 | double fpsEvalTimer = 0;
367 |
368 | void ResetTimers()
369 | {
370 | var time = GetTime();
371 | nextFrameAt = time;
372 | fpsEvalTimer = time;
373 | }
374 |
375 | ResetTimers();
376 |
377 | while (_running)
378 | {
379 | try
380 | {
381 | SDL_Event evt;
382 | while (SDL_PollEvent(out evt) != 0)
383 | {
384 | switch (evt.type)
385 | {
386 | case SDL_EventType.SDL_QUIT:
387 | _running = false;
388 | break;
389 | case SDL_EventType.SDL_KEYUP:
390 | case SDL_EventType.SDL_KEYDOWN:
391 | KeyEvent(evt.key);
392 | break;
393 | case SDL_EventType.SDL_CONTROLLERDEVICEADDED:
394 | case SDL_EventType.SDL_CONTROLLERDEVICEREMOVED:
395 | InputProvider.HandleDeviceEvent(evt.cdevice);
396 | break;
397 | case SDL_EventType.SDL_CONTROLLERBUTTONDOWN:
398 | case SDL_EventType.SDL_CONTROLLERBUTTONUP:
399 | InputProvider.HandleControllerEvent(evt.cbutton);
400 | break;
401 | case SDL_EventType.SDL_MOUSEMOTION:
402 | // _videoProvider.ToScreenCoordinates(evt.motion.x, evt.motion.y);
403 | break;
404 | case SDL_EventType.SDL_MOUSEBUTTONDOWN:
405 | // prevent the first click fron firing when loading light gun games
406 | if (_justLoaded)
407 | {
408 | _justLoaded = false;
409 | break;
410 | }
411 |
412 | break;
413 |
414 | case SDL_EventType.SDL_DROPFILE:
415 | var filename = Marshal.PtrToStringAnsi(evt.drop.file);
416 | LoadInternal(filename);
417 |
418 | break;
419 | default:
420 | //Console.WriteLine(evt.type);
421 | break;
422 | }
423 | }
424 |
425 |
426 | if (_paused)
427 | {
428 | if (_frameAdvance)
429 | {
430 | _frameAdvance = false;
431 | _threadSync.Set();
432 | }
433 | }
434 | else
435 | {
436 | double currentSec = GetTime();
437 |
438 | // Reset time if behind schedule
439 | if (currentSec - nextFrameAt >= SECONDS_PER_FRAME)
440 | {
441 | double diff = currentSec - nextFrameAt;
442 | Console.WriteLine("Can't keep up! Skipping " + (int)(diff * 1000) + " milliseconds");
443 | nextFrameAt = currentSec;
444 | }
445 |
446 | if (currentSec >= nextFrameAt)
447 | {
448 | nextFrameAt += SECONDS_PER_FRAME;
449 |
450 | _threadSync.Set();
451 | }
452 |
453 | if (currentSec >= fpsEvalTimer)
454 | {
455 | double diff = currentSec - fpsEvalTimer + 1;
456 | double frames = _cyclesRan / CYCLES_PER_FRAME;
457 | _cyclesRan = 0;
458 |
459 | //double mips = (double)Gba.Cpu.InstructionsRan / 1000000D;
460 | //Gba.Cpu.InstructionsRan = 0;
461 |
462 | // Use Math.Floor to truncate to 2 decimal places
463 | _fps = Math.Floor((frames / diff) * 100) / 100;
464 | //Mips = Math.Floor((mips / diff) * 100) / 100;
465 | UpdateTitle();
466 | //Seconds++;
467 |
468 | fpsEvalTimer += 1;
469 | }
470 | }
471 |
472 | }
473 | catch (Exception e)
474 | {
475 | Console.WriteLine(e);
476 | }
477 |
478 |
479 |
480 | //Draw();
481 | }
482 |
483 | _threadSync.Close();
484 |
485 | }
486 |
487 | private void UpdateTitle()
488 | {
489 | SDL_SetWindowTitle(
490 | _videoProvider.Window,
491 | "CS64 - " + _fps + " fps" + _audioProvider.GetAudioSamplesInQueue() + " samples queued"
492 | );
493 | }
494 |
495 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
496 | private bool HasModifier(SDL_KeyboardEvent evtKey, SDL_Keymod modifier)
497 | {
498 | return (evtKey.keysym.mod & modifier) == modifier;
499 | }
500 |
501 | private void KeyEvent(SDL_KeyboardEvent evtKey)
502 | {
503 | if (evtKey.type == SDL_EventType.SDL_KEYDOWN)
504 | {
505 | if (!InputProvider.HandleEvent(evtKey))
506 | {
507 | if (HasModifier(evtKey, SDL_Keymod.KMOD_LCTRL) || HasModifier(evtKey, SDL_Keymod.KMOD_RCTRL))
508 | {
509 | switch (evtKey.keysym.sym)
510 | {
511 |
512 | case SDL_Keycode.SDLK_BACKSLASH:
513 | _fastForward = true;
514 | _videoProvider.SetMessage("Fast Forward");
515 | break;
516 | case SDL_Keycode.SDLK_p:
517 | _paused = !_paused;
518 | _videoProvider.SetMessage(_paused ? "Paused" : "Resumed");
519 | break;
520 | case SDL_Keycode.SDLK_f:
521 | if (_paused)
522 | {
523 | _videoProvider.SetMessage("Frame Advanced");
524 | _frameAdvance = true;
525 | }
526 | break;
527 | case SDL_Keycode.SDLK_BACKSPACE:
528 | if (!_rewind)
529 | {
530 | _videoProvider.SetMessage("Rewinding...");
531 | _rewind = true;
532 | if (_paused)
533 | {
534 | _frameAdvance = true;
535 | }
536 | }
537 | break;
538 | }
539 | }
540 |
541 |
542 | switch (evtKey.keysym.sym)
543 | {
544 | case SDL_Keycode.SDLK_F10:
545 | _resetPending = true;
546 | _videoProvider.SetMessage("Reset");
547 | break;
548 |
549 | case SDL_Keycode.SDLK_F2:
550 | _saveStatePending = true;
551 | break;
552 |
553 | case SDL_Keycode.SDLK_F4:
554 | _loadStatePending = true;
555 | break;
556 |
557 | case { } keyCode when keyCode >= SDL_Keycode.SDLK_0 && keyCode <= SDL_Keycode.SDLK_9:
558 | if (keyCode == SDL_Keycode.SDLK_0)
559 | {
560 | _stateSlot = 10;
561 | }
562 | else
563 | {
564 | _stateSlot = keyCode - SDL_Keycode.SDLK_0;
565 | }
566 | _videoProvider.SetMessage($"Slot #{_stateSlot}");
567 | StateChanged?.Invoke(_stateSlot);
568 | break;
569 |
570 | }
571 | }
572 | }
573 |
574 | if (evtKey.type == SDL_EventType.SDL_KEYUP)
575 | {
576 | if (!InputProvider.HandleEvent(evtKey))
577 | {
578 | if (HasModifier(evtKey, SDL_Keymod.KMOD_LCTRL) || HasModifier(evtKey, SDL_Keymod.KMOD_RCTRL))
579 | {
580 | switch (evtKey.keysym.sym)
581 | {
582 | case SDL_Keycode.SDLK_BACKSLASH:
583 | _fastForward = false;
584 | break;
585 | case SDL_Keycode.SDLK_BACKSPACE:
586 | _rewind = false;
587 | break;
588 | }
589 | }
590 | }
591 | }
592 | }
593 |
594 | private static double GetTime()
595 | {
596 | return (double)SDL_GetPerformanceCounter() / (double)SDL_GetPerformanceFrequency();
597 | }
598 |
599 | public void Dispose()
600 | {
601 | // Wait for renderers to finish what they're doing
602 | // Not guaranteed, but it works
603 | Thread.Sleep(500);
604 | _threadSync.Dispose();
605 | _audioProvider.Dispose();
606 | _videoProvider.Dispose();
607 | SDL2.SDL_ttf.TTF_Quit();
608 | SDL_AudioQuit();
609 | SDL_VideoQuit();
610 | SDL_DestroyWindow(Window);
611 | SDL_Quit();
612 | }
613 |
614 | public void Redraw()
615 | {
616 | if (_emulationThread == null)
617 | {
618 | _videoProvider.Clear();
619 | }
620 | }
621 |
622 | public void Reset()
623 | {
624 | _c64.Reset();
625 | }
626 | }
627 | }
--------------------------------------------------------------------------------
/CS64.Core/Interface/Playback.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace CS64.Core.Interface
5 | {
6 | public class Playback
7 | {
8 | private const int MAX_REWIND_BUFFER = 512;
9 | private readonly MemoryStream[] _rewindStateBuffer = new MemoryStream[MAX_REWIND_BUFFER];
10 |
11 | private int _rewindStateHead = 0;
12 | private int _rewindStateTail = 0;
13 |
14 | public Action SaveState { get; set; }
15 | public Action LoadState { get; set; }
16 |
17 | public Playback()
18 | {
19 | for (var i = 0; i < MAX_REWIND_BUFFER; i++)
20 | {
21 | _rewindStateBuffer[i] = new MemoryStream();
22 | }
23 | }
24 |
25 | public void PerFrame(ref bool rewind)
26 | {
27 | switch (rewind)
28 | {
29 | case true:
30 | {
31 | _rewindStateHead--;
32 | if (_rewindStateHead < 0)
33 | {
34 | _rewindStateHead = MAX_REWIND_BUFFER - 1;
35 | }
36 | if (_rewindStateHead == _rewindStateTail)
37 | {
38 | rewind = false;
39 | }
40 | _rewindStateBuffer[_rewindStateHead].Position = 0;
41 | LoadState(_rewindStateBuffer[_rewindStateHead]);
42 | break;
43 | }
44 | case false:
45 | {
46 | _rewindStateBuffer[_rewindStateHead].Position = 0;
47 | SaveState(_rewindStateBuffer[_rewindStateHead]);
48 |
49 | _rewindStateHead++;
50 |
51 | if (_rewindStateHead >= MAX_REWIND_BUFFER)
52 | {
53 | _rewindStateHead = 0;
54 | }
55 | else if (_rewindStateHead == _rewindStateTail)
56 | {
57 | _rewindStateTail = _rewindStateHead + 1;
58 | if (_rewindStateTail >= MAX_REWIND_BUFFER)
59 | {
60 | _rewindStateTail = 0;
61 | }
62 | }
63 |
64 | break;
65 | }
66 | }
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/CS64.Core/Interface/RowCol.cs:
--------------------------------------------------------------------------------
1 | namespace CS64.Core.Interface
2 | {
3 | public struct RowCol
4 | {
5 | public RowCol(uint col, uint row)
6 | {
7 | Row = row;
8 | Col = col;
9 | }
10 |
11 | public uint Row;
12 | public uint Col;
13 | }
14 | }
--------------------------------------------------------------------------------
/CS64.Core/Interface/VideoProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using static SDL2.SDL_ttf;
4 | using static SDL2.SDL;
5 |
6 | namespace CS64.Core.Interface
7 | {
8 | public class VideoProvider : IDisposable
9 | {
10 | private int VicWidth;
11 | private int VicHeight;
12 |
13 |
14 | private const double RATIO_4x3 = 4 / 3d;
15 | private const double RATIO_1x1 = 1;
16 | private const double RATIO_8x7 = 8 / 7d;
17 |
18 | private uint[] _displayBuf;
19 | public IntPtr Window;
20 | private IntPtr _renderer;
21 | private IntPtr _texture;
22 | private IntPtr _font;
23 |
24 | public VideoProvider(IntPtr window, int width, int height)
25 | {
26 | Window = window;
27 |
28 | VicWidth = width;
29 | VicHeight = height;
30 |
31 | _displayBuf = new uint[VicWidth * VicHeight];
32 | _font = TTF_OpenFont(Path.Combine("Fonts", "Modeseven.ttf"), 24);
33 |
34 | //string fontsfolder = Environment.GetFolderPath(Environment.SpecialFolder.Fonts);
35 | //_font = TTF_OpenFont(Path.Combine(fontsfolder, "LUCON.TTF"), 24);
36 |
37 | }
38 |
39 | public DisplayMode DisplayMode { get; set; }
40 | public bool IntegerScaling { get; set; }
41 |
42 | public int X { get; private set; }
43 | public int Y { get; private set; }
44 | public int Width { get; private set; }
45 | public int Height { get; private set; }
46 |
47 | public int TextWidth { get; private set; }
48 | public int TextHeight { get; private set; }
49 |
50 | public int CanvasWidth { get; private set; }
51 | public int CanvasHeight { get; private set; }
52 |
53 | public double Ratio { get; private set; }
54 |
55 | public (int X, int Y) ToScreenCoordinates(int x, int y)
56 | {
57 | var sx = (x - (CanvasWidth - Width) / 2) / Ratio;
58 | var sy = y / Ratio;
59 |
60 | return ((int)sx, (int)sy);
61 | }
62 |
63 | public void Initialize()
64 | {
65 | //SDL_SetWindowPosition(Window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
66 | _renderer = SDL_CreateRenderer(Window, -1,
67 | SDL_RendererFlags.SDL_RENDERER_ACCELERATED | SDL_RendererFlags.SDL_RENDERER_PRESENTVSYNC);
68 |
69 | _texture = SDL_CreateTexture(_renderer, SDL_PIXELFORMAT_ABGR8888,
70 | (int)SDL_TextureAccess.SDL_TEXTUREACCESS_STREAMING, VicWidth, VicHeight);
71 |
72 | SDL_GetWindowSize(Window, out int w, out int h);
73 |
74 | CanvasWidth = w;
75 | CanvasHeight = h;
76 |
77 | Ratio = Math.Min((double)h / (double)VicHeight, (double)w / (double)VicWidth);
78 |
79 | double aspect_ratio = RATIO_1x1;
80 |
81 | switch (DisplayMode)
82 | {
83 | case DisplayMode.Ratio1x1:
84 | {
85 | int fillWidth;
86 | int fillHeight;
87 | if (IntegerScaling)
88 | {
89 | fillWidth = ((int)(Ratio * VicWidth) / VicWidth) * VicWidth;
90 | fillHeight = ((int)(Ratio * VicHeight) / VicHeight) * VicHeight;
91 | }
92 | else
93 | {
94 | fillWidth = (int)(Ratio * VicWidth);
95 | fillHeight = (int)(Ratio * VicHeight);
96 | }
97 |
98 | fillWidth = (int)(fillWidth * aspect_ratio);
99 |
100 | Width = fillWidth;
101 | Height = fillHeight;
102 | X = (int)((w - fillWidth) / 2);
103 | Y = (int)((h - fillHeight) / 2);
104 | break;
105 | }
106 | case DisplayMode.Stretched:
107 | Width = w;
108 | Height = h;
109 | X = 0;
110 | Y = 0;
111 | break;
112 | }
113 |
114 | }
115 |
116 | public void Clear()
117 | {
118 | SDL_RenderClear(_renderer);
119 | SDL_RenderPresent(_renderer);
120 | SDL_UpdateWindowSurface(Window);
121 | }
122 |
123 | public unsafe void Render(uint[] buffer)
124 | {
125 | for (var y = 0; y < VicHeight; y++)
126 | {
127 | for (var x = 0; x < VicWidth; x++)
128 | {
129 | var p = buffer[x + y * VicWidth];
130 | //var r = p >> 16 & 0xFF;
131 | //var g = p >> 8 & 0xFF;
132 | //var b = p & 0xFF;
133 | //var q = DisplayBuf[x + y * WIDTH];
134 | //var i = q >> 16 & 0xFF;
135 | //var j = q >> 8 & 0xFF;
136 | //var k = q & 0xFF;
137 |
138 | //if (r < 150 & g < 150 & b < 150)
139 | //{
140 | // r = (uint)(i * 0.99);
141 | // g = (uint)(j * 0.99);
142 | // b = (uint)(k * 0.99);
143 | //}
144 |
145 | //p = 0xFF000000 | (r << 16) | (g << 8) | b;
146 | _displayBuf[x + y * VicWidth] = p;
147 | }
148 | }
149 |
150 | //CopyPixels(Gba.Ppu.Renderer.ScreenFront, DisplayBuf, WIDTH * HEIGHT, ColorCorrection);
151 | fixed (void* ptr = _displayBuf)
152 | SDL_UpdateTexture(_texture, IntPtr.Zero, (IntPtr)ptr, VicWidth * 4);
153 |
154 | SDL_Rect dest = new();
155 |
156 | switch (DisplayMode)
157 | {
158 | case DisplayMode.Ratio1x1:
159 | dest.w = Width;
160 | dest.h = Height;
161 | dest.x = X;
162 | dest.y = Y;
163 | break;
164 | case DisplayMode.Stretched:
165 | dest.w = Width;
166 | dest.h = Height;
167 | dest.x = 0;
168 | dest.y = 0;
169 | break;
170 | }
171 |
172 | SDL_RenderClear(_renderer);
173 | SDL_RenderCopy(_renderer, _texture, IntPtr.Zero, ref dest);
174 |
175 | if (_messageTimeout > 0)
176 | {
177 | SDL_Rect textLocation = new SDL_Rect() { x = 0, y = dest.h - TextHeight, h = TextHeight, w = TextWidth };
178 | if (_messageTimeout < 50)
179 | {
180 | SDL_SetTextureAlphaMod(textTexture, (byte)(((_messageTimeout) / 50f) * 255));
181 | }
182 |
183 | var result = SDL_RenderCopy(_renderer, textTexture, IntPtr.Zero, ref textLocation);
184 | //var result = SDL_BlitSurface(pTexture, IntPtr.Zero, _texture, ref textLocation);
185 | if (result != 0)
186 | {
187 | Console.WriteLine(SDL_GetError());
188 | }
189 |
190 | _messageTimeout--;
191 | }
192 | else
193 | {
194 | CleanMessage();
195 | }
196 |
197 | SDL_RenderPresent(_renderer);
198 | SDL_UpdateWindowSurface(Window);
199 | }
200 |
201 | private string _message;
202 | private int _messageTimeout;
203 | private IntPtr textSurface = IntPtr.Zero;
204 | private IntPtr textTexture = IntPtr.Zero;
205 |
206 | private void CleanMessage()
207 | {
208 | if (textSurface != IntPtr.Zero)
209 | {
210 | SDL_FreeSurface(textSurface);
211 | SDL_DestroyTexture(textTexture);
212 | textSurface = IntPtr.Zero;
213 | textTexture = IntPtr.Zero;
214 | }
215 | }
216 |
217 | public void SetMessage(string message)
218 | {
219 | if (textSurface == IntPtr.Zero)
220 | {
221 | _message = message;
222 | _messageTimeout = 200;
223 |
224 | SDL_Color foregroundColor = new SDL_Color() {a = 0xFF, r = 0xFF, g = 0xFF, b = 0xFF};
225 | SDL_Color backgroundColor = new SDL_Color() {a = 0x00, r = 0x00, g = 0x00, b = 0x00};
226 |
227 | textSurface = TTF_RenderText_Blended(_font, _message, foregroundColor);
228 | textTexture = SDL_CreateTextureFromSurface(_renderer, textSurface);
229 | SDL_QueryTexture(textTexture, out uint format, out int access, out int width, out int height);
230 | TextHeight = height;
231 | TextWidth = width;
232 | SDL_SetTextureAlphaMod(textTexture, 255);
233 | }
234 | }
235 |
236 |
237 | public void Destroy()
238 | {
239 | SDL_DestroyTexture(_texture);
240 | SDL_DestroyRenderer(_renderer);
241 | }
242 |
243 | public void Dispose()
244 | {
245 | TTF_CloseFont(_font);
246 | Destroy();
247 | }
248 |
249 | public void Resize(int width, int height)
250 | {
251 | //Destroy();
252 | //SDL_SetWindowSize(Window, width, height);
253 | //Initialize();
254 | }
255 | }
256 | }
--------------------------------------------------------------------------------
/CS64.Core/SDL2_ttf.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RupertAvery/CS64/eb089999233654cc494235ad89267d8ae60d38de/CS64.Core/SDL2_ttf.dll
--------------------------------------------------------------------------------
/CS64.Core/UnsupportedMapperException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CS64.Core
4 | {
5 | public class UnsupportedMapperException : Exception
6 | {
7 | private readonly int _mapperId;
8 |
9 | public UnsupportedMapperException(int mapperId) : base($"Unsupported Mapper {mapperId}")
10 | {
11 | _mapperId = mapperId;
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/CS64.Core/Utility/BinaryWriterExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace CS64.Core.Utility
5 | {
6 | public static class BinaryWriterExtensions
7 | {
8 | public static void Write(this BinaryWriter writer, uint[] values, int offset, int length)
9 | {
10 | for (var i = offset; i < length; i++)
11 | {
12 | writer.Write(values[i]);
13 | }
14 | }
15 |
16 | public static uint[] ReadUInt32Array(this BinaryReader writer, int length)
17 | {
18 | var result = new uint[length];
19 | for (var i = 0; i < length; i++)
20 | {
21 | result[i] = writer.ReadUInt32();
22 | }
23 | return result;
24 | }
25 |
26 | public static void Write2DArray(this BinaryWriter writer, byte[,] array, int outer, int inner)
27 | {
28 | var buffer = new byte[outer * inner];
29 | Buffer.BlockCopy(array, 0, buffer, 0, outer * inner);
30 | writer.Write(buffer, 0, buffer.Length);
31 | }
32 |
33 | public static void Write2DArraySlow(this BinaryWriter writer, byte[,] array, int outer, int inner)
34 | {
35 | for (var i = 0; i < outer; i++)
36 | {
37 | for (var j = 0; j < inner; j++)
38 | {
39 | writer.Write(array[i, j]);
40 | }
41 | }
42 | }
43 |
44 | public static void Read2DArray(this BinaryReader writer, byte[,] array, int outer, int inner)
45 | {
46 | var buffer = new byte[outer * inner];
47 | writer.Read(buffer, 0, buffer.Length);
48 | Buffer.BlockCopy(buffer, 0, array, 0, outer * inner);
49 | }
50 |
51 | public static byte[,] Read2DArray(this BinaryReader writer, int outer, int inner)
52 | {
53 | var result = new byte[outer, inner];
54 | var buffer = new byte[outer * inner];
55 | writer.Read(buffer, 0, buffer.Length);
56 | Buffer.BlockCopy(buffer, 0, result, 0, outer * inner);
57 | return result;
58 | }
59 |
60 | public static byte[,] Read2DArraySlow(this BinaryReader reader, int outer, int inner)
61 | {
62 | var result = new byte[outer, inner];
63 | for (var i = 0; i < outer; i++)
64 | {
65 | for (var j = 0; j < inner; j++)
66 | {
67 | result[i, j] = reader.ReadByte();
68 | }
69 | }
70 | return result;
71 | }
72 |
73 | }
74 | }
--------------------------------------------------------------------------------
/CS64.Core/Video/CIA.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 |
4 | namespace CS64.Core.Video
5 | {
6 | //https://www.c64-wiki.com/wiki/CIA
7 |
8 | // TODO: Interrupts, timers, everything
9 |
10 | public class CIA
11 | {
12 | public uint PortA { get; private set; }
13 | public uint PortB { get; private set; }
14 |
15 | // Bit X: 0=Input (read only), 1=Output (read and write)
16 | private uint port_a;
17 | private uint port_b;
18 | private uint readwrite_mask_a;
19 | private uint readwrite_mask_b;
20 |
21 | public Func PortARead { get; set; }
22 | public Action PortAWrite { get; set; }
23 |
24 | public Func PortBRead { get; set; }
25 | public Action PortBWrite { get; set; }
26 |
27 | public Action RequestInterrupt { get; set; }
28 |
29 | public CIA()
30 | {
31 | }
32 |
33 | public uint Read(uint address)
34 | {
35 | uint data = 0;
36 | address &= 0x0F; // Mirror every 16 address bytes
37 | data = address switch
38 | {
39 | 0x0 => ReadPortA(),
40 | 0x1 => ReadPortB(),
41 | 0x2 => readwrite_mask_a,
42 | 0x3 => readwrite_mask_b,
43 | 0x4 => (uint)timer_A & 0xFF,
44 | 0x5 => (uint)(timer_A >> 8) & 0xFF,
45 | 0x6 => (uint)timer_B & 0xFF,
46 | 0x7 => (uint)(timer_B >> 8) & 0xFF,
47 | 0x8 => rtc_tenths,
48 | 0x9 => rtc_seconds | (rtc_tenseconds << 4),
49 | 0xA => rtc_minutes | (rtc_tenminutes << 4),
50 | 0xB => rtc_hours | (rtc_tenhours << 4) | (rtc_am_pm << 7),
51 | 0xC => shift_register,
52 | 0xD => ReadInterrupts(),
53 | _ => data
54 | };
55 | return data;
56 | }
57 |
58 | private uint int_mask;
59 | private uint int_data;
60 |
61 | private uint ReadPortA()
62 | {
63 | port_a = PortARead?.Invoke() ?? PortA;
64 | return port_a;
65 | }
66 |
67 | private uint ReadPortB()
68 | {
69 | port_b = PortBRead?.Invoke() ?? PortB;
70 | return port_b;
71 | }
72 |
73 | private uint ReadInterrupts()
74 | {
75 | var temp = int_data;
76 | int_data = 0; //Flags will be cleared after reading the register!
77 | return temp;
78 | }
79 |
80 | private void UpdatePortA()
81 | {
82 | //var output = port_b |= (readwrite_mask_b ^ 0xFF);
83 | var output = port_a;
84 | output &= readwrite_mask_a;
85 |
86 | if (PortAWrite != null)
87 | {
88 | PortAWrite(output);
89 | }
90 | else
91 | {
92 | PortA &= readwrite_mask_a ^ 0xFF;
93 | PortA |= output;
94 | }
95 |
96 | //var output = port_a |= (readwrite_mask_a ^ 0xFF);
97 | //if (PortAWrite != null)
98 | //{
99 | // PortAWrite(output);
100 | //}
101 | //else
102 | //{
103 | // PortA = output;
104 | //}
105 | }
106 |
107 | private void UpdatePortB()
108 | {
109 | var output = port_b;
110 | port_b &= readwrite_mask_b;
111 |
112 | if (PortBWrite != null)
113 | {
114 | PortBWrite(output);
115 | }
116 | else
117 | {
118 | PortB &= readwrite_mask_b ^ 0xFF;
119 | PortB |= output;
120 | }
121 |
122 | //var output = port_b |= (readwrite_mask_b ^ 0xFF);
123 | //if (PortBWrite != null)
124 | //{
125 | // PortBWrite(output);
126 | //}
127 | //else
128 | //{
129 | // PortB = output;
130 | //}
131 | }
132 |
133 | public void Write(uint address, uint data)
134 | {
135 | address &= 0x0F; // Mirror every 16 address bytes
136 |
137 | switch (address)
138 | {
139 | case 0x0:
140 | port_a = data;
141 | UpdatePortA();
142 |
143 | break;
144 | case 0x1:
145 | port_b = data;
146 | UpdatePortB();
147 |
148 | break;
149 | case 0x2:
150 | readwrite_mask_a = data;
151 | UpdatePortA();
152 | break;
153 | case 0x3:
154 | readwrite_mask_b = data;
155 | UpdatePortB();
156 | break;
157 | case 0x4:
158 | timer_A_latch |= (int)data;
159 | break;
160 | case 0x5:
161 | timer_A_latch |= (int)(data << 8);
162 | break;
163 | case 0x6:
164 | timer_B_latch |= (int)data;
165 | break;
166 | case 0x7:
167 | timer_B_latch |= (int)(data << 8);
168 | break;
169 | case 0x8:
170 | if (tod_set_mode == 1)
171 | {
172 | alarm_tenths = data & 0xF;
173 | }
174 | else
175 | {
176 | rtc_tenths = data & 0xF;
177 | }
178 | tod_stopped = false;
179 | break;
180 | case 0x9:
181 | if (tod_set_mode == 1)
182 | {
183 | alarm_seconds = data & 0xF;
184 | alarm_tenseconds = (data >> 4) & 0x7;
185 | }
186 | else
187 | {
188 | rtc_seconds = data & 0xF;
189 | rtc_tenseconds = (data >> 4) & 0x7;
190 | }
191 | break;
192 | case 0xA:
193 | if (tod_set_mode == 1)
194 | {
195 | alarm_minutes = data & 0xF;
196 | alarm_tenminutes = (data >> 4) & 0x7;
197 | }
198 | else
199 | {
200 | rtc_minutes = data & 0xF;
201 | rtc_tenminutes = (data >> 4) & 0x7;
202 | }
203 | break;
204 | case 0xB:
205 | if (tod_set_mode == 1)
206 | {
207 | alarm_hours = data & 0xF;
208 | alarm_tenhours = (data >> 4) & 0x7;
209 | alarm_am_pm = (data >> 7) & 1;
210 | }
211 | else
212 | {
213 | rtc_hours = data & 0xF;
214 | rtc_tenhours = (data >> 8) & 0x7;
215 | rtc_am_pm = (data >> 7) & 1;
216 | }
217 | // TODO: Writing into this register stops TOD, until register 8(TOD 10THS) will be read.
218 | tod_stopped = true;
219 | break;
220 | case 0xC:
221 | shift_register = data;
222 | break;
223 | case 0xD:
224 | // Bit 7: Source bit.
225 | // 0 = set bits 0..4 are clearing the according mask bit.
226 | // 1 = set bits 0..4 are setting the according mask bit.
227 | // If all bits 0..4 are cleared, there will be no change to the mask.
228 | if (((data >> 7) & 1) == 1)
229 | {
230 | // set bits
231 | int_mask |= (data & 0x1F);
232 | }
233 | else
234 | {
235 | int_mask &= (data & 0x1F) ^ 0xFF;
236 | }
237 |
238 | break;
239 | case 0xE:
240 | timer_A_start = (byte)(data & 1);
241 | timer_A_underflow_stop = (byte)((data >> 3) & 1);
242 | timer_A_load = (byte)((data >> 4) & 1);
243 | // should this be done here?
244 | if (timer_A_load == 1)
245 | {
246 | timer_A = timer_A_latch;
247 | }
248 | timer_A_count_mode = (byte)((data >> 5) & 1);
249 | shift_register_direction = (byte)((data >> 6) & 1);
250 | tod_frame_divider = 5 + ((data >> 7) & 1); // value will be 5 or 6
251 | // tod_frame_divider = (((data >> 7) & 1) == 0 ? 5U : 6U); // divide frame rate by this value
252 | break;
253 | case 0xF:
254 | timer_B_start = (byte)(data & 1);
255 | timer_B_underflow_stop = (byte)((data >> 3) & 1);
256 | timer_B_load = (byte)((data >> 4) & 1);
257 | // should this be done here?
258 | if (timer_B_load == 1)
259 | {
260 | timer_B = timer_B_latch;
261 | }
262 | timer_B_count_mode = (byte)((data >> 5) & 3);
263 | tod_set_mode = (data >> 7) & 1;
264 | break;
265 | }
266 | return;
267 | }
268 |
269 | private int timer_A;
270 | private int timer_B;
271 | private int timer_A_latch;
272 | private int timer_B_latch;
273 | private uint rtc_tenths;
274 | private uint rtc_seconds;
275 | private uint rtc_tenseconds;
276 | private uint rtc_minutes;
277 | private uint rtc_tenminutes;
278 | private uint rtc_hours;
279 | private uint rtc_tenhours;
280 | private uint rtc_am_pm;
281 |
282 | private uint alarm_tenths;
283 | private uint alarm_seconds;
284 | private uint alarm_tenseconds;
285 | private uint alarm_minutes;
286 | private uint alarm_tenminutes;
287 | private uint alarm_hours;
288 | private uint alarm_tenhours;
289 | private uint alarm_am_pm;
290 |
291 | private uint tod_set_mode; // 0 = clock, 1 = alarm
292 | private bool tod_stopped;
293 |
294 | private uint shift_register_bit;
295 | private uint shift_register_direction; // 0 = input, 1 = output
296 | private uint shift_register;
297 |
298 | private byte int_underflow_A;
299 | private byte int_underflow_B;
300 | private byte int_alarm;
301 |
302 |
303 | private byte timer_A_start;
304 | private byte timer_A_underflow_portB6;
305 | private byte timer_A_underflow_portB6_type;
306 | private byte timer_A_underflow_stop;
307 | private byte timer_A_load;
308 | private byte timer_A_count_mode;
309 |
310 | private byte timer_B_start;
311 | private byte timer_B_underflow_portB6;
312 | private byte timer_B_underflow_portB6_type;
313 | private byte timer_B_underflow_stop;
314 | private byte timer_B_load;
315 | private byte timer_B_count_mode;
316 |
317 | private uint tod_frame_divider; // 5 = 50Hz, 6 = 60hz;
318 | private uint tod_frame_counter;
319 | public void TimeOfDay()
320 | {
321 | if (tod_stopped) return;
322 |
323 | tod_frame_counter++;
324 | if (tod_frame_counter > tod_frame_divider)
325 | {
326 | tod_frame_counter = 0;
327 | rtc_tenths++;
328 | if (rtc_tenths > 9)
329 | {
330 | rtc_tenths = 0;
331 | rtc_seconds++;
332 | if (rtc_seconds > 9)
333 | {
334 | rtc_seconds = 0;
335 | rtc_tenseconds++;
336 | if (rtc_tenseconds > 5)
337 | {
338 | rtc_tenseconds = 0;
339 | rtc_minutes++;
340 | if (rtc_minutes > 9)
341 | {
342 | rtc_minutes = 0;
343 | rtc_tenminutes++;
344 | if (rtc_tenminutes > 5)
345 | {
346 | rtc_tenminutes = 0;
347 | rtc_hours++;
348 | if (rtc_tenhours < 2 && rtc_hours > 9)
349 | {
350 | rtc_hours = 0;
351 | rtc_tenhours++;
352 | }
353 | else if (rtc_tenhours == 2 && rtc_hours > 3)
354 | {
355 | rtc_tenths = 0;
356 | rtc_seconds = 0;
357 | rtc_tenseconds = 0;
358 | rtc_minutes = 0;
359 | rtc_tenminutes = 0;
360 | rtc_hours = 0;
361 | rtc_tenhours = 0;
362 | }
363 | }
364 | }
365 | }
366 | }
367 | //Console.WriteLine($"{rtc_tenhours}{rtc_hours}:{rtc_tenminutes}{rtc_minutes}:{rtc_tenseconds}{rtc_seconds}");
368 | }
369 | }
370 |
371 | }
372 |
373 | public void Count()
374 | {
375 | // TODO: shift shift_register into shift_register_bit
376 |
377 | if (timer_A_count_mode == 1)
378 | {
379 | UpdateTimerA();
380 | }
381 | if (timer_B_count_mode == 1)
382 | {
383 | UpdateTimerB();
384 | }
385 | }
386 |
387 |
388 | private void UpdateTimerA()
389 | {
390 | if (timer_A_start == 1)
391 | {
392 | timer_A--;
393 | if (timer_A < 0)
394 | {
395 | if ((int_mask & 1) == 1)
396 | {
397 | int_data |= 1;
398 | RequestInterrupt?.Invoke();
399 | }
400 | if (timer_A_underflow_stop == 0)
401 | {
402 | timer_A = timer_A_latch;
403 | if (timer_B_count_mode == 2)
404 | {
405 | //0b10 = Timer counts underflow of timer A
406 | //TODO: 0b11 = Timer counts underflow of timer A if the CNT - pin is high
407 | UpdateTimerB();
408 | }
409 | }
410 | else
411 | {
412 | timer_A_start = 0;
413 | }
414 | }
415 | }
416 | }
417 |
418 |
419 |
420 | private void UpdateTimerB()
421 | {
422 | if (timer_B_start == 1)
423 | {
424 | timer_B--;
425 | if (timer_B < 0)
426 | {
427 | if ((int_mask & 2) == 2)
428 | {
429 | int_data |= 2;
430 | RequestInterrupt?.Invoke();
431 | }
432 | if (timer_B_underflow_stop == 0)
433 | {
434 | timer_B = timer_B_latch;
435 | }
436 | }
437 | }
438 | }
439 |
440 | public void Clock()
441 | {
442 | if (timer_A_count_mode == 0)
443 | {
444 | UpdateTimerA();
445 | }
446 | if (timer_B_count_mode == 0)
447 | {
448 | UpdateTimerB();
449 | }
450 | }
451 | }
452 | }
--------------------------------------------------------------------------------
/CS64.Core/Video/StateEnum.cs:
--------------------------------------------------------------------------------
1 | namespace CS64.Core.Video
2 | {
3 | public enum StateEnum
4 | {
5 | IdleState,
6 | DisplayState
7 | }
8 | }
--------------------------------------------------------------------------------
/CS64.Core/blip_buf.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RupertAvery/CS64/eb089999233654cc494235ad89267d8ae60d38de/CS64.Core/blip_buf.dll
--------------------------------------------------------------------------------
/CS64.Core/libfreetype-6.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RupertAvery/CS64/eb089999233654cc494235ad89267d8ae60d38de/CS64.Core/libfreetype-6.dll
--------------------------------------------------------------------------------
/CS64.Core/zlib1.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RupertAvery/CS64/eb089999233654cc494235ad89267d8ae60d38de/CS64.Core/zlib1.dll
--------------------------------------------------------------------------------
/CS64.UI/CS64.UI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Library
5 | net5.0-windows
6 | true
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/CS64.UI/Configuration.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace CS64.UI
4 | {
5 | public class Configuration
6 | {
7 | public Configuration()
8 | {
9 | RecentItems = new List();
10 | }
11 |
12 | public List RecentItems { get; set; }
13 | }
14 | }
--------------------------------------------------------------------------------
/CS64.UI/ControlExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Forms;
2 |
3 | namespace CS64.UI
4 | {
5 | public static class ControlExtensions
6 | {
7 | public static void InvokeIfRequired(this Control control, MethodInvoker action)
8 | {
9 | // See Update 2 for edits Mike de Klerk suggests to insert here.
10 |
11 | if (control.InvokeRequired)
12 | {
13 | control.Invoke(action);
14 | }
15 | else
16 | {
17 | action();
18 | }
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/CS64.UI/FormExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Forms;
2 |
3 | namespace CS64.UI
4 | {
5 | public static class FormExtensions
6 | {
7 | public static void InvokeIfRequired(this Form control, MethodInvoker action)
8 | {
9 | // See Update 2 for edits Mike de Klerk suggests to insert here.
10 |
11 | if (control.InvokeRequired)
12 | {
13 | control.Invoke(action);
14 | }
15 | else
16 | {
17 | action();
18 | }
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/CS64.UI/KeyCodeMappings.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Windows.Forms;
3 | using SDL2;
4 |
5 | namespace CS64.UI
6 | {
7 | public static class KeyCodeMappings
8 | {
9 | private static Dictionary mappings;
10 |
11 | public static bool TryGetKeyCode(Keys key, out SDL.SDL_Keycode code)
12 | {
13 | return mappings.TryGetValue(key, out code);
14 | }
15 |
16 | static KeyCodeMappings()
17 | {
18 | mappings = new Dictionary()
19 | {
20 | {Keys.A, SDL.SDL_Keycode.SDLK_a},
21 | {Keys.B, SDL.SDL_Keycode.SDLK_b},
22 | {Keys.C, SDL.SDL_Keycode.SDLK_c},
23 | {Keys.D, SDL.SDL_Keycode.SDLK_d},
24 | {Keys.E, SDL.SDL_Keycode.SDLK_e},
25 | {Keys.F, SDL.SDL_Keycode.SDLK_f},
26 | {Keys.G, SDL.SDL_Keycode.SDLK_g},
27 | {Keys.H, SDL.SDL_Keycode.SDLK_h},
28 | {Keys.I, SDL.SDL_Keycode.SDLK_i},
29 | {Keys.J, SDL.SDL_Keycode.SDLK_j},
30 | {Keys.K, SDL.SDL_Keycode.SDLK_k},
31 | {Keys.L, SDL.SDL_Keycode.SDLK_l},
32 | {Keys.M, SDL.SDL_Keycode.SDLK_m},
33 | {Keys.N, SDL.SDL_Keycode.SDLK_n},
34 | {Keys.O, SDL.SDL_Keycode.SDLK_o},
35 | {Keys.P, SDL.SDL_Keycode.SDLK_p},
36 | {Keys.Q, SDL.SDL_Keycode.SDLK_q},
37 | {Keys.R, SDL.SDL_Keycode.SDLK_r},
38 | {Keys.S, SDL.SDL_Keycode.SDLK_s},
39 | {Keys.T, SDL.SDL_Keycode.SDLK_t},
40 | {Keys.U, SDL.SDL_Keycode.SDLK_u},
41 | {Keys.V, SDL.SDL_Keycode.SDLK_v},
42 | {Keys.W, SDL.SDL_Keycode.SDLK_w},
43 | {Keys.X, SDL.SDL_Keycode.SDLK_x},
44 | {Keys.Y, SDL.SDL_Keycode.SDLK_y},
45 | {Keys.Z, SDL.SDL_Keycode.SDLK_z},
46 | {Keys.Up, SDL.SDL_Keycode.SDLK_UP},
47 | {Keys.Down, SDL.SDL_Keycode.SDLK_DOWN},
48 | {Keys.Left, SDL.SDL_Keycode.SDLK_LEFT},
49 | {Keys.Right, SDL.SDL_Keycode.SDLK_RIGHT},
50 | {Keys.LShiftKey, SDL.SDL_Keycode.SDLK_LSHIFT},
51 | {Keys.RShiftKey, SDL.SDL_Keycode.SDLK_RSHIFT},
52 | {Keys.LControlKey, SDL.SDL_Keycode.SDLK_LCTRL},
53 | {Keys.RControlKey, SDL.SDL_Keycode.SDLK_RCTRL},
54 | {Keys.Add, SDL.SDL_Keycode.SDLK_PLUS},
55 | {Keys.Subtract, SDL.SDL_Keycode.SDLK_MINUS},
56 | {Keys.NumPad0, SDL.SDL_Keycode.SDLK_KP_0},
57 | {Keys.NumPad1, SDL.SDL_Keycode.SDLK_KP_1},
58 | {Keys.NumPad2, SDL.SDL_Keycode.SDLK_KP_2},
59 | {Keys.NumPad3, SDL.SDL_Keycode.SDLK_KP_3},
60 | {Keys.NumPad4, SDL.SDL_Keycode.SDLK_KP_4},
61 | {Keys.NumPad5, SDL.SDL_Keycode.SDLK_KP_5},
62 | {Keys.NumPad6, SDL.SDL_Keycode.SDLK_KP_6},
63 | {Keys.NumPad7, SDL.SDL_Keycode.SDLK_KP_7},
64 | {Keys.NumPad8, SDL.SDL_Keycode.SDLK_KP_8},
65 | {Keys.NumPad9, SDL.SDL_Keycode.SDLK_KP_9},
66 | {Keys.D0, SDL.SDL_Keycode.SDLK_0},
67 | {Keys.D1, SDL.SDL_Keycode.SDLK_1},
68 | {Keys.D2, SDL.SDL_Keycode.SDLK_2},
69 | {Keys.D3, SDL.SDL_Keycode.SDLK_3},
70 | {Keys.D4, SDL.SDL_Keycode.SDLK_4},
71 | {Keys.D5, SDL.SDL_Keycode.SDLK_5},
72 | {Keys.D6, SDL.SDL_Keycode.SDLK_6},
73 | {Keys.D7, SDL.SDL_Keycode.SDLK_7},
74 | {Keys.D8, SDL.SDL_Keycode.SDLK_8},
75 | {Keys.D9, SDL.SDL_Keycode.SDLK_9},
76 | {Keys.Tab, SDL.SDL_Keycode.SDLK_TAB},
77 | {Keys.Escape, SDL.SDL_Keycode.SDLK_ESCAPE},
78 | {Keys.Back, SDL.SDL_Keycode.SDLK_BACKSPACE},
79 | {Keys.Return, SDL.SDL_Keycode.SDLK_RETURN},
80 | {Keys.PageUp, SDL.SDL_Keycode.SDLK_PAGEUP},
81 | {Keys.PageDown, SDL.SDL_Keycode.SDLK_PAGEDOWN},
82 | {Keys.End, SDL.SDL_Keycode.SDLK_END},
83 | {Keys.Delete, SDL.SDL_Keycode.SDLK_DELETE},
84 | };
85 | }
86 | }
87 | }
--------------------------------------------------------------------------------
/CS64.UI/MainForm.Designer.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace CS64.UI
3 | {
4 | partial class MainForm
5 | {
6 | ///
7 | /// Required designer variable.
8 | ///
9 | private System.ComponentModel.IContainer components = null;
10 |
11 | ///
12 | /// Clean up any resources being used.
13 | ///
14 | /// true if managed resources should be disposed; otherwise, false.
15 | protected override void Dispose(bool disposing)
16 | {
17 | if (disposing && (components != null))
18 | {
19 | components.Dispose();
20 | }
21 | base.Dispose(disposing);
22 | }
23 |
24 | #region Windows Form Designer generated code
25 |
26 | ///
27 | /// Required method for Designer support - do not modify
28 | /// the contents of this method with the code editor.
29 | ///
30 | private void InitializeComponent()
31 | {
32 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
33 | this.menuStrip1 = new System.Windows.Forms.MenuStrip();
34 | this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
35 | this.loadROMToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
36 | this.recentToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
37 | this.clearItemsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
38 | this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
39 | this.changeSlotToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
40 | this.slot1ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
41 | this.slot2ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
42 | this.slot3ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
43 | this.slot4ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
44 | this.slot5ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
45 | this.slot6ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
46 | this.slot7ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
47 | this.slot8ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
48 | this.slot9ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
49 | this.slot10ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
50 | this.loadStateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
51 | this.saveStateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
52 | this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator();
53 | this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
54 | this.optionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
55 | this.controllersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
56 | this.windowSizeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
57 | this.x1ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
58 | this.x2ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
59 | this.x3ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
60 | this.x4ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
61 | this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog();
62 | this.menuStrip1.SuspendLayout();
63 | this.SuspendLayout();
64 | //
65 | // menuStrip1
66 | //
67 | this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
68 | this.fileToolStripMenuItem,
69 | this.optionsToolStripMenuItem});
70 | this.menuStrip1.Location = new System.Drawing.Point(0, 0);
71 | this.menuStrip1.Name = "menuStrip1";
72 | this.menuStrip1.Size = new System.Drawing.Size(575, 24);
73 | this.menuStrip1.TabIndex = 0;
74 | this.menuStrip1.Text = "menuStrip1";
75 | //
76 | // fileToolStripMenuItem
77 | //
78 | this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
79 | this.loadROMToolStripMenuItem,
80 | this.recentToolStripMenuItem,
81 | this.toolStripMenuItem1,
82 | this.changeSlotToolStripMenuItem,
83 | this.loadStateToolStripMenuItem,
84 | this.saveStateToolStripMenuItem,
85 | this.toolStripMenuItem2,
86 | this.exitToolStripMenuItem});
87 | this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
88 | this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
89 | this.fileToolStripMenuItem.Text = "&File";
90 | //
91 | // loadROMToolStripMenuItem
92 | //
93 | this.loadROMToolStripMenuItem.Name = "loadROMToolStripMenuItem";
94 | this.loadROMToolStripMenuItem.Size = new System.Drawing.Size(138, 22);
95 | this.loadROMToolStripMenuItem.Text = "&Load ROM";
96 | this.loadROMToolStripMenuItem.Click += new System.EventHandler(this.openToolStripMenuItem_Click);
97 | //
98 | // recentToolStripMenuItem
99 | //
100 | this.recentToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
101 | this.clearItemsToolStripMenuItem});
102 | this.recentToolStripMenuItem.Name = "recentToolStripMenuItem";
103 | this.recentToolStripMenuItem.Size = new System.Drawing.Size(138, 22);
104 | this.recentToolStripMenuItem.Text = "Recent";
105 | //
106 | // clearItemsToolStripMenuItem
107 | //
108 | this.clearItemsToolStripMenuItem.Name = "clearItemsToolStripMenuItem";
109 | this.clearItemsToolStripMenuItem.Size = new System.Drawing.Size(133, 22);
110 | this.clearItemsToolStripMenuItem.Text = "Clear Items";
111 | this.clearItemsToolStripMenuItem.Click += new System.EventHandler(this.clearItemsToolStripMenuItem_Click);
112 | //
113 | // toolStripMenuItem1
114 | //
115 | this.toolStripMenuItem1.Name = "toolStripMenuItem1";
116 | this.toolStripMenuItem1.Size = new System.Drawing.Size(135, 6);
117 | //
118 | // changeSlotToolStripMenuItem
119 | //
120 | this.changeSlotToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
121 | this.slot1ToolStripMenuItem,
122 | this.slot2ToolStripMenuItem,
123 | this.slot3ToolStripMenuItem,
124 | this.slot4ToolStripMenuItem,
125 | this.slot5ToolStripMenuItem,
126 | this.slot6ToolStripMenuItem,
127 | this.slot7ToolStripMenuItem,
128 | this.slot8ToolStripMenuItem,
129 | this.slot9ToolStripMenuItem,
130 | this.slot10ToolStripMenuItem});
131 | this.changeSlotToolStripMenuItem.Enabled = false;
132 | this.changeSlotToolStripMenuItem.Name = "changeSlotToolStripMenuItem";
133 | this.changeSlotToolStripMenuItem.Size = new System.Drawing.Size(138, 22);
134 | this.changeSlotToolStripMenuItem.Text = "Change Slot";
135 | //
136 | // slot1ToolStripMenuItem
137 | //
138 | this.slot1ToolStripMenuItem.Name = "slot1ToolStripMenuItem";
139 | this.slot1ToolStripMenuItem.Size = new System.Drawing.Size(116, 22);
140 | this.slot1ToolStripMenuItem.Text = "Slot #1";
141 | //
142 | // slot2ToolStripMenuItem
143 | //
144 | this.slot2ToolStripMenuItem.Name = "slot2ToolStripMenuItem";
145 | this.slot2ToolStripMenuItem.Size = new System.Drawing.Size(116, 22);
146 | this.slot2ToolStripMenuItem.Text = "Slot #2";
147 | //
148 | // slot3ToolStripMenuItem
149 | //
150 | this.slot3ToolStripMenuItem.Name = "slot3ToolStripMenuItem";
151 | this.slot3ToolStripMenuItem.Size = new System.Drawing.Size(116, 22);
152 | this.slot3ToolStripMenuItem.Text = "Slot #3";
153 | //
154 | // slot4ToolStripMenuItem
155 | //
156 | this.slot4ToolStripMenuItem.Name = "slot4ToolStripMenuItem";
157 | this.slot4ToolStripMenuItem.Size = new System.Drawing.Size(116, 22);
158 | this.slot4ToolStripMenuItem.Text = "Slot #4";
159 | //
160 | // slot5ToolStripMenuItem
161 | //
162 | this.slot5ToolStripMenuItem.Name = "slot5ToolStripMenuItem";
163 | this.slot5ToolStripMenuItem.Size = new System.Drawing.Size(116, 22);
164 | this.slot5ToolStripMenuItem.Text = "Slot #5";
165 | //
166 | // slot6ToolStripMenuItem
167 | //
168 | this.slot6ToolStripMenuItem.Name = "slot6ToolStripMenuItem";
169 | this.slot6ToolStripMenuItem.Size = new System.Drawing.Size(116, 22);
170 | this.slot6ToolStripMenuItem.Text = "Slot #6";
171 | //
172 | // slot7ToolStripMenuItem
173 | //
174 | this.slot7ToolStripMenuItem.Name = "slot7ToolStripMenuItem";
175 | this.slot7ToolStripMenuItem.Size = new System.Drawing.Size(116, 22);
176 | this.slot7ToolStripMenuItem.Text = "Slot #7";
177 | //
178 | // slot8ToolStripMenuItem
179 | //
180 | this.slot8ToolStripMenuItem.Name = "slot8ToolStripMenuItem";
181 | this.slot8ToolStripMenuItem.Size = new System.Drawing.Size(116, 22);
182 | this.slot8ToolStripMenuItem.Text = "Slot #8";
183 | //
184 | // slot9ToolStripMenuItem
185 | //
186 | this.slot9ToolStripMenuItem.Name = "slot9ToolStripMenuItem";
187 | this.slot9ToolStripMenuItem.Size = new System.Drawing.Size(116, 22);
188 | this.slot9ToolStripMenuItem.Text = "Slot #9";
189 | //
190 | // slot10ToolStripMenuItem
191 | //
192 | this.slot10ToolStripMenuItem.Name = "slot10ToolStripMenuItem";
193 | this.slot10ToolStripMenuItem.Size = new System.Drawing.Size(116, 22);
194 | this.slot10ToolStripMenuItem.Text = "Slot #10";
195 | //
196 | // loadStateToolStripMenuItem
197 | //
198 | this.loadStateToolStripMenuItem.Enabled = false;
199 | this.loadStateToolStripMenuItem.Name = "loadStateToolStripMenuItem";
200 | this.loadStateToolStripMenuItem.Size = new System.Drawing.Size(138, 22);
201 | this.loadStateToolStripMenuItem.Text = "Load State";
202 | //
203 | // saveStateToolStripMenuItem
204 | //
205 | this.saveStateToolStripMenuItem.Enabled = false;
206 | this.saveStateToolStripMenuItem.Name = "saveStateToolStripMenuItem";
207 | this.saveStateToolStripMenuItem.Size = new System.Drawing.Size(138, 22);
208 | this.saveStateToolStripMenuItem.Text = "Save State";
209 | //
210 | // toolStripMenuItem2
211 | //
212 | this.toolStripMenuItem2.Name = "toolStripMenuItem2";
213 | this.toolStripMenuItem2.Size = new System.Drawing.Size(135, 6);
214 | //
215 | // exitToolStripMenuItem
216 | //
217 | this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
218 | this.exitToolStripMenuItem.Size = new System.Drawing.Size(138, 22);
219 | this.exitToolStripMenuItem.Text = "E&xit";
220 | this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
221 | //
222 | // optionsToolStripMenuItem
223 | //
224 | this.optionsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
225 | this.controllersToolStripMenuItem,
226 | this.windowSizeToolStripMenuItem});
227 | this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem";
228 | this.optionsToolStripMenuItem.Size = new System.Drawing.Size(61, 20);
229 | this.optionsToolStripMenuItem.Text = "Options";
230 | //
231 | // controllersToolStripMenuItem
232 | //
233 | this.controllersToolStripMenuItem.Name = "controllersToolStripMenuItem";
234 | this.controllersToolStripMenuItem.Size = new System.Drawing.Size(141, 22);
235 | this.controllersToolStripMenuItem.Text = "Controllers";
236 | this.controllersToolStripMenuItem.Click += new System.EventHandler(this.controllersToolStripMenuItem_Click);
237 | //
238 | // windowSizeToolStripMenuItem
239 | //
240 | this.windowSizeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
241 | this.x1ToolStripMenuItem,
242 | this.x2ToolStripMenuItem,
243 | this.x3ToolStripMenuItem,
244 | this.x4ToolStripMenuItem});
245 | this.windowSizeToolStripMenuItem.Name = "windowSizeToolStripMenuItem";
246 | this.windowSizeToolStripMenuItem.Size = new System.Drawing.Size(141, 22);
247 | this.windowSizeToolStripMenuItem.Text = "Window Size";
248 | //
249 | // x1ToolStripMenuItem
250 | //
251 | this.x1ToolStripMenuItem.Name = "x1ToolStripMenuItem";
252 | this.x1ToolStripMenuItem.Size = new System.Drawing.Size(86, 22);
253 | this.x1ToolStripMenuItem.Text = "x1";
254 | //
255 | // x2ToolStripMenuItem
256 | //
257 | this.x2ToolStripMenuItem.Name = "x2ToolStripMenuItem";
258 | this.x2ToolStripMenuItem.Size = new System.Drawing.Size(86, 22);
259 | this.x2ToolStripMenuItem.Text = "x2";
260 | //
261 | // x3ToolStripMenuItem
262 | //
263 | this.x3ToolStripMenuItem.Name = "x3ToolStripMenuItem";
264 | this.x3ToolStripMenuItem.Size = new System.Drawing.Size(86, 22);
265 | this.x3ToolStripMenuItem.Text = "x3";
266 | //
267 | // x4ToolStripMenuItem
268 | //
269 | this.x4ToolStripMenuItem.Name = "x4ToolStripMenuItem";
270 | this.x4ToolStripMenuItem.Size = new System.Drawing.Size(86, 22);
271 | this.x4ToolStripMenuItem.Text = "x4";
272 | //
273 | // MainForm
274 | //
275 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
276 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
277 | this.BackColor = System.Drawing.SystemColors.ControlDark;
278 | this.ClientSize = new System.Drawing.Size(575, 535);
279 | this.Controls.Add(this.menuStrip1);
280 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
281 | this.MainMenuStrip = this.menuStrip1;
282 | this.Name = "MainForm";
283 | this.Text = "CS64";
284 | this.Load += new System.EventHandler(this.MainForm_Load);
285 | this.Paint += new System.Windows.Forms.PaintEventHandler(this.MainForm_Paint);
286 | this.menuStrip1.ResumeLayout(false);
287 | this.menuStrip1.PerformLayout();
288 | this.ResumeLayout(false);
289 | this.PerformLayout();
290 |
291 | }
292 |
293 | #endregion
294 |
295 | private System.Windows.Forms.MenuStrip menuStrip1;
296 | private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
297 | private System.Windows.Forms.ToolStripMenuItem loadROMToolStripMenuItem;
298 | private System.Windows.Forms.OpenFileDialog openFileDialog1;
299 | private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1;
300 | private System.Windows.Forms.ToolStripMenuItem changeSlotToolStripMenuItem;
301 | private System.Windows.Forms.ToolStripMenuItem slot1ToolStripMenuItem;
302 | private System.Windows.Forms.ToolStripMenuItem loadStateToolStripMenuItem;
303 | private System.Windows.Forms.ToolStripMenuItem saveStateToolStripMenuItem;
304 | private System.Windows.Forms.ToolStripMenuItem slot2ToolStripMenuItem;
305 | private System.Windows.Forms.ToolStripMenuItem slot3ToolStripMenuItem;
306 | private System.Windows.Forms.ToolStripMenuItem slot4ToolStripMenuItem;
307 | private System.Windows.Forms.ToolStripMenuItem slot5ToolStripMenuItem;
308 | private System.Windows.Forms.ToolStripMenuItem slot6ToolStripMenuItem;
309 | private System.Windows.Forms.ToolStripMenuItem slot7ToolStripMenuItem;
310 | private System.Windows.Forms.ToolStripMenuItem slot8ToolStripMenuItem;
311 | private System.Windows.Forms.ToolStripMenuItem slot9ToolStripMenuItem;
312 | private System.Windows.Forms.ToolStripMenuItem slot10ToolStripMenuItem;
313 | private System.Windows.Forms.ToolStripMenuItem recentToolStripMenuItem;
314 | private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2;
315 | private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
316 | private System.Windows.Forms.ToolStripMenuItem optionsToolStripMenuItem;
317 | private System.Windows.Forms.ToolStripMenuItem controllersToolStripMenuItem;
318 | private System.Windows.Forms.ToolStripMenuItem windowSizeToolStripMenuItem;
319 | private System.Windows.Forms.ToolStripMenuItem x1ToolStripMenuItem;
320 | private System.Windows.Forms.ToolStripMenuItem x2ToolStripMenuItem;
321 | private System.Windows.Forms.ToolStripMenuItem x3ToolStripMenuItem;
322 | private System.Windows.Forms.ToolStripMenuItem x4ToolStripMenuItem;
323 | private System.Windows.Forms.ToolStripMenuItem clearItemsToolStripMenuItem;
324 | }
325 | }
326 |
327 |
--------------------------------------------------------------------------------
/CS64.UI/MainForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Drawing;
3 | using System.IO;
4 | using System.Windows.Forms;
5 | using CS64.Core.Interface;
6 | using CS64.Core.Interface.Input;
7 |
8 | namespace CS64.UI
9 | {
10 | public partial class MainForm : Form, IMainInterface
11 | {
12 | private readonly Main _main;
13 | public Action OnHostResize { get; set; }
14 | public Func LoadRom { get; set; }
15 | public Action ChangeSlot { get; set; }
16 | public Action LoadState { get; set; }
17 | public Action SaveState { get; set; }
18 | public Action ResizeWindow { get; set; }
19 | public Action SetMapping { get; set; }
20 | public Configuration Configuration { get;set;}
21 |
22 | public MainForm(Main main)
23 | {
24 | _main = main;
25 | _main.StateChanged = StateChanged;
26 | SizeChanged += (sender, args) => OnHostResize?.Invoke();
27 | InitializeComponent();
28 | Configuration = new Configuration();
29 | }
30 |
31 | private void StateChanged(int slot)
32 | {
33 | UncheckSlots();
34 | switch (slot)
35 | {
36 | case 1: slot1ToolStripMenuItem.Checked = true; break;
37 | case 2: slot2ToolStripMenuItem.Checked = true; break;
38 | case 3: slot3ToolStripMenuItem.Checked = true; break;
39 | case 4: slot4ToolStripMenuItem.Checked = true; break;
40 | case 5: slot5ToolStripMenuItem.Checked = true; break;
41 | case 6: slot6ToolStripMenuItem.Checked = true; break;
42 | case 7: slot7ToolStripMenuItem.Checked = true; break;
43 | case 8: slot8ToolStripMenuItem.Checked = true; break;
44 | case 9: slot9ToolStripMenuItem.Checked = true; break;
45 | case 10: slot10ToolStripMenuItem.Checked = true; break;
46 |
47 | }
48 | }
49 |
50 | private void MainForm_Load(object sender, EventArgs e)
51 | {
52 | SetSize(3);
53 |
54 | slot1ToolStripMenuItem.Click += (o, args) => { UncheckSlots(); ChangeSlot?.Invoke(1); slot1ToolStripMenuItem.Checked = true; };
55 | slot2ToolStripMenuItem.Click += (o, args) => { UncheckSlots(); ChangeSlot?.Invoke(2); slot2ToolStripMenuItem.Checked = true; };
56 | slot3ToolStripMenuItem.Click += (o, args) => { UncheckSlots(); ChangeSlot?.Invoke(3); slot3ToolStripMenuItem.Checked = true; };
57 | slot4ToolStripMenuItem.Click += (o, args) => { UncheckSlots(); ChangeSlot?.Invoke(4); slot4ToolStripMenuItem.Checked = true; };
58 | slot5ToolStripMenuItem.Click += (o, args) => { UncheckSlots(); ChangeSlot?.Invoke(5); slot5ToolStripMenuItem.Checked = true; };
59 | slot6ToolStripMenuItem.Click += (o, args) => { UncheckSlots(); ChangeSlot?.Invoke(6); slot6ToolStripMenuItem.Checked = true; };
60 | slot7ToolStripMenuItem.Click += (o, args) => { UncheckSlots(); ChangeSlot?.Invoke(7); slot7ToolStripMenuItem.Checked = true; };
61 | slot8ToolStripMenuItem.Click += (o, args) => { UncheckSlots(); ChangeSlot?.Invoke(8); slot8ToolStripMenuItem.Checked = true; };
62 | slot9ToolStripMenuItem.Click += (o, args) => { UncheckSlots(); ChangeSlot?.Invoke(9); slot9ToolStripMenuItem.Checked = true; };
63 | slot10ToolStripMenuItem.Click += (o, args) => { UncheckSlots(); ChangeSlot?.Invoke(10); slot10ToolStripMenuItem.Checked = true; };
64 |
65 | loadStateToolStripMenuItem.Click += (o, args) => LoadState?.Invoke();
66 | saveStateToolStripMenuItem.Click += (o, args) => SaveState?.Invoke();
67 |
68 | x1ToolStripMenuItem.Click += (o, args) => { UncheckSizes(); SetSize(1); x1ToolStripMenuItem.Checked = true; };
69 | x2ToolStripMenuItem.Click += (o, args) => { UncheckSizes(); SetSize(2); x2ToolStripMenuItem.Checked = true; };
70 | x3ToolStripMenuItem.Click += (o, args) => { UncheckSizes(); SetSize(3); x3ToolStripMenuItem.Checked = true; };
71 | x4ToolStripMenuItem.Click += (o, args) => { UncheckSizes(); SetSize(4); x4ToolStripMenuItem.Checked = true; };
72 | }
73 |
74 | private void SetSize(int scale)
75 | {
76 | //ResizeWindow?.Invoke(256 * scale, 240 * scale + menuStrip1.Height);
77 | Size = new Size((int)_main.Width * scale, (int)_main.Height * scale + menuStrip1.Height);
78 | // This resizes the window *twice*
79 | //Width = 256 * scale;
80 | //Height = 240 * scale + menuStrip1.Height;
81 | }
82 |
83 | private void exitToolStripMenuItem_Click(object sender, EventArgs e)
84 | {
85 | Close();
86 | }
87 |
88 | private const int MaxRecentItems = 10;
89 |
90 | private void AddRecentItem(string item)
91 | {
92 | if (!Configuration.RecentItems.Contains(item))
93 | {
94 | Configuration.RecentItems.Add(item);
95 | var recentItem = new ToolStripMenuItem(Path.GetFileName(item));
96 | recentItem.Click += (sender, args) =>
97 | {
98 | LoadRom.Invoke(item);
99 | };
100 | recentToolStripMenuItem.DropDownItems.Insert(0, recentItem);
101 |
102 | if (recentToolStripMenuItem.DropDownItems.Count > MaxRecentItems + 1)
103 | {
104 | recentToolStripMenuItem.DropDownItems.RemoveAt(MaxRecentItems);
105 | }
106 | }
107 | }
108 |
109 | private void openToolStripMenuItem_Click(object sender, EventArgs e)
110 | {
111 | openFileDialog1.Filter = "All supported files|*.t64;*.d64;*.prg;*.zip|All files|*.*";
112 | var result = openFileDialog1.ShowDialog();
113 | if (result == DialogResult.OK)
114 | {
115 | if (LoadRom != null)
116 | {
117 | if (LoadRom.Invoke(openFileDialog1.FileName))
118 | {
119 | AddRecentItem(openFileDialog1.FileName);
120 | SetMenus(true);
121 | }
122 | }
123 | }
124 | }
125 |
126 | private void UncheckSizes()
127 | {
128 | x1ToolStripMenuItem.Checked = false;
129 | x2ToolStripMenuItem.Checked = false;
130 | x3ToolStripMenuItem.Checked = false;
131 | x4ToolStripMenuItem.Checked = false;
132 | }
133 |
134 | private void UncheckSlots()
135 | {
136 | slot1ToolStripMenuItem.Checked = false;
137 | slot2ToolStripMenuItem.Checked = false;
138 | slot3ToolStripMenuItem.Checked = false;
139 | slot4ToolStripMenuItem.Checked = false;
140 | slot5ToolStripMenuItem.Checked = false;
141 | slot6ToolStripMenuItem.Checked = false;
142 | slot7ToolStripMenuItem.Checked = false;
143 | slot8ToolStripMenuItem.Checked = false;
144 | slot9ToolStripMenuItem.Checked = false;
145 | slot10ToolStripMenuItem.Checked = false;
146 | }
147 |
148 | private void SetMenus(bool enabled)
149 | {
150 | changeSlotToolStripMenuItem.Enabled = enabled;
151 | loadStateToolStripMenuItem.Enabled = enabled;
152 | saveStateToolStripMenuItem.Enabled = enabled;
153 | }
154 |
155 | private void controllersToolStripMenuItem_Click(object sender, EventArgs e)
156 | {
157 | var mapper = new MappingForm(_main);
158 | mapper.Show(this);
159 | }
160 |
161 | private void clearItemsToolStripMenuItem_Click(object sender, EventArgs e)
162 | {
163 | Configuration.RecentItems.Clear();
164 | while (recentToolStripMenuItem.DropDownItems.Count > 1)
165 | {
166 | recentToolStripMenuItem.DropDownItems.RemoveAt(recentToolStripMenuItem.DropDownItems.Count - 2);
167 | }
168 | }
169 |
170 | private void MainForm_Paint(object sender, PaintEventArgs e)
171 | {
172 | _main.Redraw();
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/CS64.UI/MapWaitForm.Designer.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace CS64.UI
3 | {
4 | partial class MapWaitForm
5 | {
6 | ///
7 | /// Required designer variable.
8 | ///
9 | private System.ComponentModel.IContainer components = null;
10 |
11 | ///
12 | /// Clean up any resources being used.
13 | ///
14 | /// true if managed resources should be disposed; otherwise, false.
15 | protected override void Dispose(bool disposing)
16 | {
17 | if (disposing && (components != null))
18 | {
19 | components.Dispose();
20 | }
21 | base.Dispose(disposing);
22 | }
23 |
24 | #region Windows Form Designer generated code
25 |
26 | ///
27 | /// Required method for Designer support - do not modify
28 | /// the contents of this method with the code editor.
29 | ///
30 | private void InitializeComponent()
31 | {
32 | this.label1 = new System.Windows.Forms.Label();
33 | this.SuspendLayout();
34 | //
35 | // label1
36 | //
37 | this.label1.AutoSize = true;
38 | this.label1.Location = new System.Drawing.Point(74, 36);
39 | this.label1.Name = "label1";
40 | this.label1.Size = new System.Drawing.Size(120, 15);
41 | this.label1.TabIndex = 0;
42 | this.label1.Text = "Press a button or key";
43 | //
44 | // MapWaitForm
45 | //
46 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
47 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
48 | this.ClientSize = new System.Drawing.Size(268, 93);
49 | this.Controls.Add(this.label1);
50 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
51 | this.Name = "MapWaitForm";
52 | this.ShowInTaskbar = false;
53 | this.Text = "Press a key";
54 | this.Load += new System.EventHandler(this.MapWaitForm_Load);
55 | this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.MapWaitForm_KeyDown);
56 | this.ResumeLayout(false);
57 | this.PerformLayout();
58 |
59 | }
60 |
61 | #endregion
62 |
63 | private System.Windows.Forms.Label label1;
64 | }
65 | }
--------------------------------------------------------------------------------
/CS64.UI/MapWaitForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Windows.Forms;
4 | using CS64.Core.Interface.Input;
5 | using SDL2;
6 |
7 | namespace CS64.UI
8 | {
9 | public partial class MapWaitForm : Form
10 | {
11 | private readonly InputProvider _inputProvider;
12 | private readonly InputKeyEnum _key;
13 | private System.Threading.Timer timer;
14 | private int countDown;
15 | public bool Success { get; set; }
16 |
17 | public MapWaitForm(InputProvider inputProvider, InputKeyEnum key)
18 | {
19 | _inputProvider = inputProvider;
20 | _key = key;
21 | Closing += (sender, args) =>
22 | {
23 | timer.Change(Timeout.Infinite, Timeout.Infinite);
24 | };
25 |
26 | InitializeComponent();
27 | }
28 |
29 | private void MapWaitForm_Load(object sender, EventArgs e)
30 | {
31 | timer = new System.Threading.Timer(Callback, null, 0, 1000);
32 | countDown = 5;
33 | }
34 |
35 |
36 |
37 | private void UpdateText()
38 | {
39 | label1.Text = $"Press a button or key ({countDown})";
40 | }
41 |
42 | private void Callback(object? state)
43 | {
44 | if (countDown == 0)
45 | {
46 | timer.Change(Timeout.Infinite, Timeout.Infinite);
47 | this.InvokeIfRequired(Close);
48 | }
49 | label1.InvokeIfRequired(UpdateText);
50 | countDown--;
51 | }
52 |
53 | private void MapWaitForm_KeyDown(object sender, KeyEventArgs e)
54 | {
55 | if(!KeyCodeMappings.TryGetKeyCode(e.KeyCode, out var code))
56 | {
57 | code = (SDL.SDL_Keycode) e.KeyCode;
58 | }
59 | var keyEvent = new SDL.SDL_KeyboardEvent()
60 | {
61 | type = SDL.SDL_EventType.SDL_KEYDOWN,
62 | keysym = new SDL.SDL_Keysym()
63 | {
64 | sym = code
65 | }
66 | };
67 | _inputProvider.HandleEvent(keyEvent);
68 | Success = true;
69 | timer.Change(Timeout.Infinite, Timeout.Infinite);
70 | Close();
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/CS64.UI/MapWaitForm.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | text/microsoft-resx
50 |
51 |
52 | 2.0
53 |
54 |
55 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
56 |
57 |
58 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
59 |
60 |
--------------------------------------------------------------------------------
/CS64.UI/MappingForm.Designer.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace CS64.UI
3 | {
4 | partial class MappingForm
5 | {
6 | ///
7 | /// Required designer variable.
8 | ///
9 | private System.ComponentModel.IContainer components = null;
10 |
11 | ///
12 | /// Clean up any resources being used.
13 | ///
14 | /// true if managed resources should be disposed; otherwise, false.
15 | protected override void Dispose(bool disposing)
16 | {
17 | if (disposing && (components != null))
18 | {
19 | components.Dispose();
20 | }
21 | base.Dispose(disposing);
22 | }
23 |
24 | #region Windows Form Designer generated code
25 |
26 | ///
27 | /// Required method for Designer support - do not modify
28 | /// the contents of this method with the code editor.
29 | ///
30 | private void InitializeComponent()
31 | {
32 | this.buttonUp = new System.Windows.Forms.Button();
33 | this.buttonDown = new System.Windows.Forms.Button();
34 | this.buttonLeft = new System.Windows.Forms.Button();
35 | this.buttonRight = new System.Windows.Forms.Button();
36 | this.buttonSelect = new System.Windows.Forms.Button();
37 | this.buttonStart = new System.Windows.Forms.Button();
38 | this.buttonB = new System.Windows.Forms.Button();
39 | this.buttonA = new System.Windows.Forms.Button();
40 | this.buttonTurboB = new System.Windows.Forms.Button();
41 | this.buttonTurboA = new System.Windows.Forms.Button();
42 | this.textBoxUp = new System.Windows.Forms.TextBox();
43 | this.textBoxDown = new System.Windows.Forms.TextBox();
44 | this.textBoxRight = new System.Windows.Forms.TextBox();
45 | this.textBoxLeft = new System.Windows.Forms.TextBox();
46 | this.textBoxA = new System.Windows.Forms.TextBox();
47 | this.textBoxB = new System.Windows.Forms.TextBox();
48 | this.textBoxTurboA = new System.Windows.Forms.TextBox();
49 | this.textBoxTurboB = new System.Windows.Forms.TextBox();
50 | this.textBoxStart = new System.Windows.Forms.TextBox();
51 | this.textBoxSelect = new System.Windows.Forms.TextBox();
52 | this.SuspendLayout();
53 | //
54 | // buttonUp
55 | //
56 | this.buttonUp.Location = new System.Drawing.Point(319, 53);
57 | this.buttonUp.Name = "buttonUp";
58 | this.buttonUp.Size = new System.Drawing.Size(65, 23);
59 | this.buttonUp.TabIndex = 0;
60 | this.buttonUp.Text = "Up";
61 | this.buttonUp.UseVisualStyleBackColor = true;
62 | //
63 | // buttonDown
64 | //
65 | this.buttonDown.Location = new System.Drawing.Point(319, 82);
66 | this.buttonDown.Name = "buttonDown";
67 | this.buttonDown.Size = new System.Drawing.Size(65, 23);
68 | this.buttonDown.TabIndex = 1;
69 | this.buttonDown.Text = "Down";
70 | this.buttonDown.UseVisualStyleBackColor = true;
71 | //
72 | // buttonLeft
73 | //
74 | this.buttonLeft.Location = new System.Drawing.Point(319, 111);
75 | this.buttonLeft.Name = "buttonLeft";
76 | this.buttonLeft.Size = new System.Drawing.Size(65, 23);
77 | this.buttonLeft.TabIndex = 2;
78 | this.buttonLeft.Text = "Left";
79 | this.buttonLeft.UseVisualStyleBackColor = true;
80 | //
81 | // buttonRight
82 | //
83 | this.buttonRight.Location = new System.Drawing.Point(319, 140);
84 | this.buttonRight.Name = "buttonRight";
85 | this.buttonRight.Size = new System.Drawing.Size(65, 23);
86 | this.buttonRight.TabIndex = 3;
87 | this.buttonRight.Text = "Right";
88 | this.buttonRight.UseVisualStyleBackColor = true;
89 | //
90 | // buttonSelect
91 | //
92 | this.buttonSelect.Location = new System.Drawing.Point(459, 53);
93 | this.buttonSelect.Name = "buttonSelect";
94 | this.buttonSelect.Size = new System.Drawing.Size(65, 23);
95 | this.buttonSelect.TabIndex = 4;
96 | this.buttonSelect.Text = "Select";
97 | this.buttonSelect.UseVisualStyleBackColor = true;
98 | //
99 | // buttonStart
100 | //
101 | this.buttonStart.Location = new System.Drawing.Point(459, 82);
102 | this.buttonStart.Name = "buttonStart";
103 | this.buttonStart.Size = new System.Drawing.Size(65, 23);
104 | this.buttonStart.TabIndex = 5;
105 | this.buttonStart.Text = "Start";
106 | this.buttonStart.UseVisualStyleBackColor = true;
107 | //
108 | // buttonB
109 | //
110 | this.buttonB.Location = new System.Drawing.Point(319, 169);
111 | this.buttonB.Name = "buttonB";
112 | this.buttonB.Size = new System.Drawing.Size(65, 23);
113 | this.buttonB.TabIndex = 6;
114 | this.buttonB.Text = "B";
115 | this.buttonB.UseVisualStyleBackColor = true;
116 | //
117 | // buttonA
118 | //
119 | this.buttonA.Location = new System.Drawing.Point(319, 198);
120 | this.buttonA.Name = "buttonA";
121 | this.buttonA.Size = new System.Drawing.Size(65, 23);
122 | this.buttonA.TabIndex = 7;
123 | this.buttonA.Text = "A";
124 | this.buttonA.UseVisualStyleBackColor = true;
125 | //
126 | // buttonTurboB
127 | //
128 | this.buttonTurboB.Location = new System.Drawing.Point(459, 111);
129 | this.buttonTurboB.Name = "buttonTurboB";
130 | this.buttonTurboB.Size = new System.Drawing.Size(65, 23);
131 | this.buttonTurboB.TabIndex = 8;
132 | this.buttonTurboB.Text = "Turbo B";
133 | this.buttonTurboB.UseVisualStyleBackColor = true;
134 | //
135 | // buttonTurboA
136 | //
137 | this.buttonTurboA.Location = new System.Drawing.Point(459, 140);
138 | this.buttonTurboA.Name = "buttonTurboA";
139 | this.buttonTurboA.Size = new System.Drawing.Size(65, 23);
140 | this.buttonTurboA.TabIndex = 9;
141 | this.buttonTurboA.Text = "Turbo A";
142 | this.buttonTurboA.UseVisualStyleBackColor = true;
143 | //
144 | // textBoxUp
145 | //
146 | this.textBoxUp.Enabled = false;
147 | this.textBoxUp.Location = new System.Drawing.Point(390, 53);
148 | this.textBoxUp.Name = "textBoxUp";
149 | this.textBoxUp.Size = new System.Drawing.Size(62, 23);
150 | this.textBoxUp.TabIndex = 10;
151 | this.textBoxUp.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
152 | //
153 | // textBoxDown
154 | //
155 | this.textBoxDown.Enabled = false;
156 | this.textBoxDown.Location = new System.Drawing.Point(390, 82);
157 | this.textBoxDown.Name = "textBoxDown";
158 | this.textBoxDown.Size = new System.Drawing.Size(62, 23);
159 | this.textBoxDown.TabIndex = 11;
160 | this.textBoxDown.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
161 | //
162 | // textBoxRight
163 | //
164 | this.textBoxRight.Enabled = false;
165 | this.textBoxRight.Location = new System.Drawing.Point(390, 140);
166 | this.textBoxRight.Name = "textBoxRight";
167 | this.textBoxRight.Size = new System.Drawing.Size(62, 23);
168 | this.textBoxRight.TabIndex = 13;
169 | this.textBoxRight.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
170 | //
171 | // textBoxLeft
172 | //
173 | this.textBoxLeft.Enabled = false;
174 | this.textBoxLeft.Location = new System.Drawing.Point(390, 111);
175 | this.textBoxLeft.Name = "textBoxLeft";
176 | this.textBoxLeft.Size = new System.Drawing.Size(62, 23);
177 | this.textBoxLeft.TabIndex = 12;
178 | this.textBoxLeft.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
179 | //
180 | // textBoxA
181 | //
182 | this.textBoxA.Enabled = false;
183 | this.textBoxA.Location = new System.Drawing.Point(390, 198);
184 | this.textBoxA.Name = "textBoxA";
185 | this.textBoxA.Size = new System.Drawing.Size(62, 23);
186 | this.textBoxA.TabIndex = 15;
187 | this.textBoxA.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
188 | //
189 | // textBoxB
190 | //
191 | this.textBoxB.Enabled = false;
192 | this.textBoxB.Location = new System.Drawing.Point(390, 169);
193 | this.textBoxB.Name = "textBoxB";
194 | this.textBoxB.Size = new System.Drawing.Size(62, 23);
195 | this.textBoxB.TabIndex = 14;
196 | this.textBoxB.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
197 | //
198 | // textBoxTurboA
199 | //
200 | this.textBoxTurboA.Enabled = false;
201 | this.textBoxTurboA.Location = new System.Drawing.Point(530, 140);
202 | this.textBoxTurboA.Name = "textBoxTurboA";
203 | this.textBoxTurboA.Size = new System.Drawing.Size(62, 23);
204 | this.textBoxTurboA.TabIndex = 19;
205 | this.textBoxTurboA.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
206 | //
207 | // textBoxTurboB
208 | //
209 | this.textBoxTurboB.Enabled = false;
210 | this.textBoxTurboB.Location = new System.Drawing.Point(530, 111);
211 | this.textBoxTurboB.Name = "textBoxTurboB";
212 | this.textBoxTurboB.Size = new System.Drawing.Size(62, 23);
213 | this.textBoxTurboB.TabIndex = 18;
214 | this.textBoxTurboB.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
215 | //
216 | // textBoxStart
217 | //
218 | this.textBoxStart.Enabled = false;
219 | this.textBoxStart.Location = new System.Drawing.Point(530, 82);
220 | this.textBoxStart.Name = "textBoxStart";
221 | this.textBoxStart.Size = new System.Drawing.Size(62, 23);
222 | this.textBoxStart.TabIndex = 17;
223 | this.textBoxStart.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
224 | //
225 | // textBoxSelect
226 | //
227 | this.textBoxSelect.Enabled = false;
228 | this.textBoxSelect.Location = new System.Drawing.Point(530, 53);
229 | this.textBoxSelect.Name = "textBoxSelect";
230 | this.textBoxSelect.Size = new System.Drawing.Size(62, 23);
231 | this.textBoxSelect.TabIndex = 16;
232 | this.textBoxSelect.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
233 | //
234 | // MappingForm
235 | //
236 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
237 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
238 | this.ClientSize = new System.Drawing.Size(613, 357);
239 | this.Controls.Add(this.textBoxTurboA);
240 | this.Controls.Add(this.textBoxTurboB);
241 | this.Controls.Add(this.textBoxStart);
242 | this.Controls.Add(this.textBoxSelect);
243 | this.Controls.Add(this.textBoxA);
244 | this.Controls.Add(this.textBoxB);
245 | this.Controls.Add(this.textBoxRight);
246 | this.Controls.Add(this.textBoxLeft);
247 | this.Controls.Add(this.textBoxDown);
248 | this.Controls.Add(this.textBoxUp);
249 | this.Controls.Add(this.buttonTurboA);
250 | this.Controls.Add(this.buttonTurboB);
251 | this.Controls.Add(this.buttonA);
252 | this.Controls.Add(this.buttonB);
253 | this.Controls.Add(this.buttonStart);
254 | this.Controls.Add(this.buttonSelect);
255 | this.Controls.Add(this.buttonRight);
256 | this.Controls.Add(this.buttonLeft);
257 | this.Controls.Add(this.buttonDown);
258 | this.Controls.Add(this.buttonUp);
259 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
260 | this.Name = "MappingForm";
261 | this.Text = "Controllers";
262 | this.Load += new System.EventHandler(this.MappingForm_Load);
263 | this.ResumeLayout(false);
264 | this.PerformLayout();
265 |
266 | }
267 |
268 | #endregion
269 |
270 | private System.Windows.Forms.Button buttonUp;
271 | private System.Windows.Forms.Button buttonDown;
272 | private System.Windows.Forms.Button buttonLeft;
273 | private System.Windows.Forms.Button buttonRight;
274 | private System.Windows.Forms.Button buttonSelect;
275 | private System.Windows.Forms.Button buttonStart;
276 | private System.Windows.Forms.Button buttonB;
277 | private System.Windows.Forms.Button buttonA;
278 | private System.Windows.Forms.Button buttonTurboB;
279 | private System.Windows.Forms.Button buttonTurboA;
280 | private System.Windows.Forms.TextBox textBoxUp;
281 | private System.Windows.Forms.TextBox textBoxDown;
282 | private System.Windows.Forms.TextBox textBoxRight;
283 | private System.Windows.Forms.TextBox textBoxLeft;
284 | private System.Windows.Forms.TextBox textBoxA;
285 | private System.Windows.Forms.TextBox textBoxB;
286 | private System.Windows.Forms.TextBox textBoxTurboA;
287 | private System.Windows.Forms.TextBox textBoxTurboB;
288 | private System.Windows.Forms.TextBox textBoxStart;
289 | private System.Windows.Forms.TextBox textBoxSelect;
290 | }
291 | }
--------------------------------------------------------------------------------
/CS64.UI/MappingForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 | using CS64.Core.Interface;
4 | using CS64.Core.Interface.Input;
5 |
6 | namespace CS64.UI
7 | {
8 | public partial class MappingForm : Form
9 | {
10 | private readonly Main _main;
11 |
12 | public MappingForm(Main main)
13 | {
14 | _main = main;
15 | InitializeComponent();
16 | }
17 |
18 | private void ShowMapping(InputKeyEnum key)
19 | {
20 | _main.SetMapping(key);
21 | var mapWait = new MapWaitForm(_main.InputProvider, key);
22 | mapWait.Top = Top + (Height - mapWait.Height) / 2;
23 | mapWait.Left = Left + (Width - mapWait.Width) / 2;
24 | mapWait.ShowDialog(this);
25 | }
26 |
27 | //private void GetMapping(InputKeyEnum key, TextBox textbox)
28 | //{
29 | // textbox.Text = _main.InputProvider.GetMapping(key);
30 | //}
31 |
32 | private void MappingForm_Load(object sender, EventArgs e)
33 | {
34 | //GetMapping(InputKeyEnum.Up, textBoxUp);
35 | //GetMapping(InputKeyEnum.Down, textBoxDown);
36 | //GetMapping(InputKeyEnum.Left, textBoxLeft);
37 | //GetMapping(InputKeyEnum.Right, textBoxRight);
38 | //GetMapping(InputKeyEnum.B, textBoxB);
39 | //GetMapping(InputKeyEnum.A, textBoxA);
40 | //GetMapping(InputKeyEnum.Select, textBoxSelect);
41 | //GetMapping(InputKeyEnum.Start, textBoxStart);
42 |
43 |
44 | //buttonUp.Click += (o, e) => { ShowMapping(InputKeyEnum.Up); GetMapping(InputKeyEnum.Up, textBoxUp); };
45 | //buttonDown.Click += (o, e) => { ShowMapping(InputKeyEnum.Down); GetMapping(InputKeyEnum.Down, textBoxDown); };
46 | //buttonLeft.Click += (o, e) => { ShowMapping(InputKeyEnum.Left); GetMapping(InputKeyEnum.Left, textBoxLeft); };
47 | //buttonRight.Click += (o, e) => { ShowMapping(InputKeyEnum.Right); GetMapping(InputKeyEnum.Right, textBoxRight); };
48 | //buttonB.Click += (o, e) => { ShowMapping(InputKeyEnum.B); GetMapping(InputKeyEnum.B, textBoxB); };
49 | //buttonA.Click += (o, e) => { ShowMapping(InputKeyEnum.A); GetMapping(InputKeyEnum.A, textBoxA); };
50 | //buttonSelect.Click += (o, e) => { ShowMapping(InputKeyEnum.Select); GetMapping(InputKeyEnum.Select, textBoxSelect); };
51 | //buttonStart.Click += (o, e) => { ShowMapping(InputKeyEnum.Start); GetMapping(InputKeyEnum.Start, textBoxStart); };
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/CS64.UI/MappingForm.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | text/microsoft-resx
50 |
51 |
52 | 2.0
53 |
54 |
55 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
56 |
57 |
58 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
59 |
60 |
--------------------------------------------------------------------------------
/CS64.UI/famicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RupertAvery/CS64/eb089999233654cc494235ad89267d8ae60d38de/CS64.UI/famicon.ico
--------------------------------------------------------------------------------
/CS64.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31515.178
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CS64", "CS64\CS64.csproj", "{53299216-A392-4796-9C33-E094FF0472B1}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CS64.Core", "CS64.Core\CS64.Core.csproj", "{5B7D8A4F-1137-4F79-9218-E1C8232F6D10}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CS64.UI", "CS64.UI\CS64.UI.csproj", "{B21BE846-762D-49E6-825C-BBF0903C4F4E}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {53299216-A392-4796-9C33-E094FF0472B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {53299216-A392-4796-9C33-E094FF0472B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {53299216-A392-4796-9C33-E094FF0472B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {53299216-A392-4796-9C33-E094FF0472B1}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {5B7D8A4F-1137-4F79-9218-E1C8232F6D10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {5B7D8A4F-1137-4F79-9218-E1C8232F6D10}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {5B7D8A4F-1137-4F79-9218-E1C8232F6D10}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {5B7D8A4F-1137-4F79-9218-E1C8232F6D10}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {B21BE846-762D-49E6-825C-BBF0903C4F4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {B21BE846-762D-49E6-825C-BBF0903C4F4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {B21BE846-762D-49E6-825C-BBF0903C4F4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {B21BE846-762D-49E6-825C-BBF0903C4F4E}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | GlobalSection(ExtensibilityGlobals) = postSolution
35 | SolutionGuid = {D2F4EB27-904F-40DE-B728-C1A1673AFF02}
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/CS64/CS64.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net5.0-windows
6 | true
7 | win-x64
8 | true
9 | true
10 | true
11 | true
12 | false
13 | false
14 |
15 |
16 |
17 | true
18 |
19 |
20 |
21 | true
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | PreserveNewest
34 |
35 |
36 | PreserveNewest
37 |
38 |
39 | PreserveNewest
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/CS64/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CS64.Core.Interface;
3 | using CS64.UI;
4 |
5 | namespace CS64
6 | {
7 | class Program
8 | {
9 | [STAThread]
10 | static void Main(string[] args)
11 | {
12 | using (var main = new Main())
13 | {
14 |
15 | using (var form = new MainForm(main))
16 | {
17 | form.Show();
18 |
19 | main.Initialize(form);
20 | main.LoadROM("rom\\characters.bin", 0xD000, 0x1000);
21 | main.LoadROM("rom\\basic.bin", 0xA000, 0x2000);
22 | main.LoadROM("rom\\kernal.bin", 0xE000, 0x2000);
23 |
24 | string rom;
25 | //main.Test();
26 | ////main.Load(args[0]);
27 | //if (args.Length > 0)
28 | //{
29 | // main.Load(args[0]);
30 | //}
31 | main.Reset();
32 | main.Run();
33 | }
34 |
35 | }
36 |
37 |
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/CS64/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "CS64": {
4 | "commandName": "Project"
5 | }
6 | }
7 | }
--------------------------------------------------------------------------------
/CS64/publish.cmd:
--------------------------------------------------------------------------------
1 | dotnet publish --configuration Release
2 |
--------------------------------------------------------------------------------
/CS64/rom/basic.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RupertAvery/CS64/eb089999233654cc494235ad89267d8ae60d38de/CS64/rom/basic.bin
--------------------------------------------------------------------------------
/CS64/rom/characters.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RupertAvery/CS64/eb089999233654cc494235ad89267d8ae60d38de/CS64/rom/characters.bin
--------------------------------------------------------------------------------
/CS64/rom/kernal.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RupertAvery/CS64/eb089999233654cc494235ad89267d8ae60d38de/CS64/rom/kernal.bin
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) David Khristepher Santos
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CS64 - Commodore 64 Emulator
2 |
3 | **CS64** is a Commodore 64 emulator written in C#.
4 |
5 | This is a work in progress and only the most basic functionality is working. Currently the emulator will boot into BASIC
6 | and you should be able to type in programs and run them.
7 |
8 | # Screenshots
9 |
10 | 
11 |
12 | # Prerequisites
13 |
14 | * .NET 5 SDK
15 | * Visual Studio 2019 v16+
16 |
17 | # Todo
18 |
19 | * Fix interrupts
20 | * Excessive borders hack - look for documentation
21 | * Graphics modes and sprites
22 | * Joysticks
23 | * Test timers
24 | * States
25 | * Rewind
26 | * Light Pen
27 | * Tape I/O
28 | * Disk I/O
29 | * SID (Probably not, might use another emulator's SID implementation, or reSID)
30 |
31 | # Key mapping
32 |
33 | | Key | Function |
34 | |---------|--------------|
35 | | F10 | Reset |
36 |
37 |
38 | # Known Issues
39 |
40 |
--------------------------------------------------------------------------------