-
Notifications
You must be signed in to change notification settings - Fork 0
/
Invaders.cs
187 lines (174 loc) · 6.08 KB
/
Invaders.cs
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
using SDL2;
using System.Diagnostics;
namespace cs8080
{
class SpaceInvadersIO : State
{
public SpaceInvadersIO(ushort memsize) : base(memsize) { }
/*
PORT 1
bit function
0 coin(0 when active)
1 P2 start button
2 P1 start button
3 ?
4 P1 shoot button
5 P1 joystick left
6 P1 joystick right
7 ?
*/
public byte port1;
/*
PORT 2
bit function
0-1 'dipswitch' number of lives (00 being 3 and 11 being 6)
2 tilt button
3 'dipswitch' bonus lives
4 P2 shoot button
5 P2 joystick left
6 P2 joystick right
7 'dipswitch' coin info (0 means on)
*/
public byte port2;
private byte shiftoffset,msbyte,lsbyte;
override public byte PortIN(State i8080, byte port)
{
switch (port)
{
case 0: break; // not called by space invaders
case 1: i8080.A = port1; break; // read port 1
case 2: i8080.A = port2; break; // read port 2
case 3: i8080.A = (byte)((ushort)((msbyte << 8) | lsbyte) >> (8 - shiftoffset) & 0xff); break; // shifting mechanism output
}
return i8080.A;
}
override public void PortOUT(State i8080, byte port)
{
switch (port)
{
case 2: shiftoffset = (byte)(i8080.A & 0x07); break; // set shift offset
case 3: break; // play a sound
case 4: // shifts data around
lsbyte = msbyte;
msbyte = i8080.A;
break;
case 5: break; // play a sound from bank 2
case 6: break; // some debug? port
}
}
}
class SpaceInvaders
{
private static SpaceInvadersIO invaders = new(16 * 1024); // 16kibs of ram as a byte array
private static bool exit; // should we exit the program
private static void CPUSTEP()
{
Stopwatch clock = new();
clock.Start();
while (!exit)
{
Emulate.Exec(invaders);
// Throttle the CPU emulation if needed.
if (invaders.cycles >= (State.CLOCKSPEED / 60))
{
clock.Stop();
if (clock.Elapsed.TotalMilliseconds < 16.6)
{
var sleepForMs = 16.6 - clock.Elapsed.TotalMilliseconds;
if (sleepForMs >= 1)
{
Thread.Sleep((int)sleepForMs);
}
}
invaders.totalcycles = 0; // reset the total cycles
clock.Restart();
}
Emulate.InterruptGen(invaders, 0xcd);
}
}
private static SpaceInvadersIO SDLGUI()
{
Stopwatch clock = new();
while (SDL.SDL_PollEvent(out SDL.SDL_Event e) != 0)
{
if (e.type == SDL.SDL_EventType.SDL_KEYDOWN)
{
switch (e.key.keysym.scancode)
{
case SDL.SDL_Scancode.SDL_SCANCODE_C: invaders.port1 |= 0b1; break; // coin
case SDL.SDL_Scancode.SDL_SCANCODE_1: invaders.port1 |= 0b100; break; // player 1 start
case SDL.SDL_Scancode.SDL_SCANCODE_2: invaders.port1 |= 0b10; break; // player 2 start
case SDL.SDL_Scancode.SDL_SCANCODE_SPACE: Console.WriteLine("space"); invaders.port1 |= 0b10000; break; // player 1 shoot
case SDL.SDL_Scancode.SDL_SCANCODE_LEFT: invaders.port1 |= 0b100000; break; // player 1 left
case SDL.SDL_Scancode.SDL_SCANCODE_RIGHT: invaders.port1 |= 0b1000000; break; // player 1 right
case SDL.SDL_Scancode.SDL_SCANCODE_S: invaders.port2 |= 0b10000; break; // player 2 shoot
case SDL.SDL_Scancode.SDL_SCANCODE_A: invaders.port2 |= 0b100000; break; // player 2 left
case SDL.SDL_Scancode.SDL_SCANCODE_D: invaders.port2 |= 0b1000000; break; // player 2 right
}
}
else if (e.type == SDL.SDL_EventType.SDL_KEYUP)
{
switch (e.key.keysym.scancode)
{
case SDL.SDL_Scancode.SDL_SCANCODE_C: invaders.port1 &= 0b11111110; break; // same as above
case SDL.SDL_Scancode.SDL_SCANCODE_2: invaders.port1 &= 0b11111101; break;
case SDL.SDL_Scancode.SDL_SCANCODE_1: invaders.port1 &= 0b11111011; break;
case SDL.SDL_Scancode.SDL_SCANCODE_SPACE: invaders.port1 &= 0b11101111; break;
case SDL.SDL_Scancode.SDL_SCANCODE_LEFT: invaders.port1 &= 0b11011111; break;
case SDL.SDL_Scancode.SDL_SCANCODE_RIGHT: invaders.port1 &= 0b10111111; break;
case SDL.SDL_Scancode.SDL_SCANCODE_S: invaders.port2 &= 0b11101111; break;
case SDL.SDL_Scancode.SDL_SCANCODE_A: invaders.port2 &= 0b11011111; break;
case SDL.SDL_Scancode.SDL_SCANCODE_D: invaders.port2 &= 0b10111111; break;
}
}
else if (e.type == SDL.SDL_EventType.SDL_QUIT)
{
exit = true;
}
}
return invaders;
}
private static SpaceInvadersIO Executor()
{
Thread exec8080 = new(new ThreadStart(CPUSTEP))
{
Name = "thread"
};
exec8080.Start();
SDLGUI();
/*
uint count = 0;
while (count < count * State.CLOCKSPEED / 1000)
{
uint cyc = invaders.totalcycles;
Emulate.OpcodeHandler(invaders, invaders.Mem[invaders.PC]);
uint elapsed = invaders.totalcycles - cyc;
count += elapsed;
if (invaders.totalcycles >= State.CLOCKSPEED / 60 / 2)
{
invaders.totalcycles -= State.CLOCKSPEED / 60 / 2;
Emulate.InterruptGen(invaders, invaders.interruptop);
invaders.interruptop = (ushort)(invaders.interruptop == 0xcf ? 0xd7 : 0xcf);
}
}
*/
#if DEBUG
Console.WriteLine($"PC: {invaders.PC:X4}, AF: {invaders.A:X2}{Ops.B2F(invaders):X2}, BC: {invaders.B:X2}{invaders.C:X2}, DE: {invaders.D:X2}{invaders.E:X2}, HL: {invaders.H:X2}{invaders.L:X2}, SP: {invaders.SP:X4} - {Disassembler.OPlookup(invaders.Mem[invaders.PC], invaders.Mem[invaders.PC + 1], invaders.Mem[invaders.PC + 2])}");
//FM.DumpAll(invaders, "dump");
#endif
return invaders;
}
public static void SIrun(string rompath)
{
Console.WriteLine("Loading Space Invaders...");
invaders.Mem = FM.LoadROM(File.ReadAllBytes(rompath), invaders.Mem, 0);
FM.DumpAll(invaders, "dump");
Console.WriteLine($"Finished loading, running...");
// initialise an SDL window
int sdlinit = SDL.SDL_Init(SDL.SDL_INIT_VIDEO | SDL.SDL_INIT_AUDIO);
nint window = SDL.SDL_CreateWindow("Space Invaders", SDL.SDL_WINDOWPOS_CENTERED, SDL.SDL_WINDOWPOS_CENTERED, 640, 480, SDL.SDL_WindowFlags.SDL_WINDOW_RESIZABLE);
nint renderer = SDL.SDL_CreateRenderer(window, -1, SDL.SDL_RendererFlags.SDL_RENDERER_ACCELERATED | SDL.SDL_RendererFlags.SDL_RENDERER_PRESENTVSYNC);
Executor();
}
}
}