Atari7800Hawk: More Core Work

- Adds Fire button (centipede and Asteroid now playable)
- Improve Maria, add holey DMA, fix numerous bugs
- Fix slow cycle memory mapping.
This commit is contained in:
alyosha-tas 2017-07-06 15:25:21 -04:00 committed by GitHub
parent 8a5e613fff
commit d9cc1558aa
6 changed files with 223 additions and 189 deletions

View File

@ -21,6 +21,8 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
// input state of controllers and console // input state of controllers and console
public byte p1_state; public byte p1_state;
public byte p2_state; public byte p2_state;
public byte p1_fire;
public byte p2_fire;
public byte con_state; public byte con_state;
// there are 4 maria cycles in a CPU cycle (fast access, both NTSC and PAL) // there are 4 maria cycles in a CPU cycle (fast access, both NTSC and PAL)
@ -50,9 +52,8 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
_islag = true; _islag = true;
p1_state = GetControllerState(controller, 1); GetControllerState(controller);
p2_state = GetControllerState(controller, 2); GetConsoleState(controller);
con_state = GetConsoleState(controller);
maria.RunFrame(); maria.RunFrame();
@ -114,41 +115,43 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
} }
// determine if the next access will be fast or slow // determine if the next access will be fast or slow
if (cpu.PC < 0x0400) if ((cpu.PC & 0xFCE0) == 0)
{ {
if ((cpu.PC & 0xFF) < 0x20) // return TIA registers or control register if it is still unlocked
{ if ((A7800_control_register & 0x1) == 0)
if ((A7800_control_register & 0x1) == 0 && (cpu.PC < 0x20))
{
slow_access = false;
}
else
{
slow_access = true;
}
}
else if (cpu.PC < 0x300)
{
slow_access = true;
}
else
{ {
slow_access = false; slow_access = false;
} }
else
{
slow_access = true;
}
}
else if ((cpu.PC & 0xFF80) == 0x280)
{
slow_access = true;
}
else if ((cpu.PC & 0xFE80) == 0x480)
{
slow_access = true;
}
else
{
slow_access = false;
} }
} }
public byte GetControllerState(IController controller, int index) public void GetControllerState(IController controller)
{ {
InputCallbacks.Call(); InputCallbacks.Call();
if (index == 1) p1_state = _controllerDeck.ReadPort1(controller);
return _controllerDeck.ReadPort1(controller); p2_state = _controllerDeck.ReadPort2(controller);
else p1_fire = _controllerDeck.ReadFire1(controller);
return _controllerDeck.ReadPort2(controller); p2_fire = _controllerDeck.ReadFire2(controller);
} }
public byte GetConsoleState(IController controller) public void GetConsoleState(IController controller)
{ {
byte result = 0; byte result = 0;
@ -173,7 +176,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
result |= 1; result |= 1;
} }
return result; con_state = result;
} }
public int Frame => _frame; public int Frame => _frame;

View File

@ -143,6 +143,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
maria.Core = this; maria.Core = this;
m6532.Core = this; m6532.Core = this;
tia.Core = this;
ser.Register<IVideoProvider>(maria); ser.Register<IVideoProvider>(maria);
ser.Register<ISoundProvider>(tia); ser.Register<ISoundProvider>(tia);

View File

@ -60,6 +60,16 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
return Port2.Read(c); return Port2.Read(c);
} }
public byte ReadFire1(IController c)
{
return Port1.ReadFire(c);
}
public byte ReadFire2(IController c)
{
return Port2.ReadFire(c);
}
public ControllerDefinition Definition { get; } public ControllerDefinition Definition { get; }
public void SyncState(Serializer ser) public void SyncState(Serializer ser)

View File

@ -1,120 +1,125 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using BizHawk.Common; using BizHawk.Common;
using BizHawk.Emulation.Common; using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{ {
/// <summary> /// <summary>
/// Represents a controller plugged into a controller port on the intellivision /// Represents a controller plugged into a controller port on the intellivision
/// </summary> /// </summary>
public interface IPort public interface IPort
{ {
byte Read(IController c); byte Read(IController c);
byte ReadFire(IController c); byte ReadFire(IController c);
ControllerDefinition Definition { get; } ControllerDefinition Definition { get; }
void SyncState(Serializer ser); void SyncState(Serializer ser);
int PortNum { get; } int PortNum { get; }
} }
[DisplayName("Unplugged Controller")] [DisplayName("Unplugged Controller")]
public class UnpluggedController : IPort public class UnpluggedController : IPort
{ {
public UnpluggedController(int portNum) public UnpluggedController(int portNum)
{ {
PortNum = portNum; PortNum = portNum;
Definition = new ControllerDefinition Definition = new ControllerDefinition
{ {
BoolButtons = new List<string>() BoolButtons = new List<string>()
}; };
} }
public byte Read(IController c) public byte Read(IController c)
{ {
return 0; return 0;
} }
public byte ReadFire(IController c) public byte ReadFire(IController c)
{ {
return 0; return 0;
} }
public ControllerDefinition Definition { get; } public ControllerDefinition Definition { get; }
public void SyncState(Serializer ser) public void SyncState(Serializer ser)
{ {
// Do nothing // Do nothing
} }
public int PortNum { get; } public int PortNum { get; }
} }
[DisplayName("Joystick Controller")] [DisplayName("Joystick Controller")]
public class StandardController : IPort public class StandardController : IPort
{ {
public StandardController(int portNum) public StandardController(int portNum)
{ {
PortNum = portNum; PortNum = portNum;
Definition = new ControllerDefinition Definition = new ControllerDefinition
{ {
BoolButtons = BaseDefinition BoolButtons = BaseDefinition
.Select(b => "P" + PortNum + " " + b) .Select(b => "P" + PortNum + " " + b)
.ToList() .ToList()
}; };
} }
public int PortNum { get; } public int PortNum { get; }
public byte Read(IController c) public byte Read(IController c)
{ {
byte result = 0xF; byte result = 0xF;
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
if (c.IsPressed(Definition.BoolButtons[i])) if (c.IsPressed(Definition.BoolButtons[i]))
{ {
result -= (byte)(1 << i); result -= (byte)(1 << i);
} }
} }
if (PortNum==1) if (PortNum==1)
{ {
result = (byte)(result << 4); result = (byte)(result << 4);
} }
return result; return result;
} }
public byte ReadFire(IController c) public byte ReadFire(IController c)
{ {
return 0; byte result = 0x80;
} if (c.IsPressed(Definition.BoolButtons[4]))
{
public ControllerDefinition Definition { get; } result = 0x00; // zero means fire is pressed
}
return result;
public void SyncState(Serializer ser) }
{
// Nothing todo, I think public ControllerDefinition Definition { get; }
}
private static readonly string[] BaseDefinition = public void SyncState(Serializer ser)
{ {
"U", "D", "L", "R", "Fire" // Nothing todo, I think
}; }
private static byte[] HandControllerButtons = private static readonly string[] BaseDefinition =
{ {
0x0, // UP "U", "D", "L", "R", "Fire"
0x0, // Down };
0x0, // Left
0x0, // Right private static byte[] HandControllerButtons =
}; {
} 0x0, // UP
} 0x0, // Down
0x0, // Left
0x0, // Right
};
}
}

View File

@ -21,7 +21,6 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
// additional entries used only in 5-byte header mode // additional entries used only in 5-byte header mode
public bool write_mode; public bool write_mode;
public bool ind_mode; public bool ind_mode;
public bool exp_mode;
public byte[] obj; // up to 32 bytes can compose one object public byte[] obj; // up to 32 bytes can compose one object
} }
@ -292,7 +291,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
GFX_Objects[header_counter].ind_mode = temp.Bit(5); GFX_Objects[header_counter].ind_mode = temp.Bit(5);
header_pointer++; header_pointer++;
temp = (byte)(ReadMemory((ushort)(current_DLL_addr + header_pointer))); temp = (byte)(ReadMemory((ushort)(current_DLL_addr + header_pointer)));
GFX_Objects[header_counter].addr |= (ushort)((temp/* + current_DLL_offset*/)<< 8); GFX_Objects[header_counter].addr |= (ushort)(temp << 8);
header_pointer++; header_pointer++;
temp = ReadMemory((ushort)(current_DLL_addr + header_pointer)); temp = ReadMemory((ushort)(current_DLL_addr + header_pointer));
int temp_w = (temp & 0x1F); // this is the 2's complement of width (for reasons that escape me) int temp_w = (temp & 0x1F); // this is the 2's complement of width (for reasons that escape me)
@ -314,7 +313,6 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
GFX_Objects[header_counter].h_pos = ReadMemory((ushort)(current_DLL_addr + header_pointer)); GFX_Objects[header_counter].h_pos = ReadMemory((ushort)(current_DLL_addr + header_pointer));
header_pointer++; header_pointer++;
GFX_Objects[header_counter].exp_mode = true;
DMA_phase_next = DMA_GRAPHICS; DMA_phase_next = DMA_GRAPHICS;
header_read_time = 10; header_read_time = 10;
@ -330,16 +328,17 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
GFX_Objects[header_counter].palette = (byte)((temp & 0xE0) >> 5); GFX_Objects[header_counter].palette = (byte)((temp & 0xE0) >> 5);
header_pointer++; header_pointer++;
temp = (byte)(ReadMemory((ushort)(current_DLL_addr + header_pointer))); temp = (byte)(ReadMemory((ushort)(current_DLL_addr + header_pointer)));
GFX_Objects[header_counter].addr |= (ushort)((temp + current_DLL_offset)<< 8); GFX_Objects[header_counter].addr |= (ushort)(temp << 8);
header_pointer++; header_pointer++;
GFX_Objects[header_counter].h_pos = ReadMemory((ushort)(current_DLL_addr + header_pointer)); GFX_Objects[header_counter].h_pos = ReadMemory((ushort)(current_DLL_addr + header_pointer));
header_pointer++; header_pointer++;
GFX_Objects[header_counter].exp_mode = false;
DMA_phase_next = DMA_GRAPHICS; DMA_phase_next = DMA_GRAPHICS;
GFX_Objects[header_counter].write_mode = global_write_mode; GFX_Objects[header_counter].write_mode = global_write_mode;
GFX_Objects[header_counter].ind_mode = false;
header_read_time = 8; header_read_time = 8;
} }
@ -356,55 +355,61 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{ {
if (DMA_phase_counter == 1) if (DMA_phase_counter == 1)
{ {
// get all the graphics data ushort addr_t = 0;
// the time this takes depend on the source and number of bytes
if (GFX_Objects[header_counter].exp_mode) // in 5 byte mode, we first have to check if we are in direct or indirect mode
if (GFX_Objects[header_counter].ind_mode)
{ {
// in 5 byte mode, we first have to check if we are in direct or indirect mode int ch_size = 0;
if (GFX_Objects[header_counter].ind_mode)
if (Core.Maria_regs[0x1C].Bit(4))
{ {
//Console.Write(" Indirect graphics"); graphics_read_time = 9 * GFX_Objects[header_counter].width + 3;
ch_size = 2;
int ch_size = 0;
if (Core.Maria_regs[0x1C].Bit(4))
{
graphics_read_time = 6 * GFX_Objects[header_counter].width + 3;
ch_size = 1;
}
else
{
graphics_read_time = 9 * GFX_Objects[header_counter].width + 3;
ch_size = 2;
}
// the address here is specified by CHAR_BASE maria registers
//ushort addr = (ushort)(GFX_Objects[header_counter].addr & 0xFF);
ushort addr = (ushort)(ReadMemory(GFX_Objects[header_counter].addr));
addr |= (ushort)((Core.Maria_regs[0x14] + current_DLL_offset) << 8);
for (int i = 0; i < GFX_Objects[header_counter].width; i ++)
{
// if game is programmed correctly, should always be less then 32
// but in order to not go out of bounds, lets clamp it here anyway
if (i * ch_size < 32)
{
GFX_Objects[header_counter].obj[i * ch_size] = ReadMemory((ushort)(addr + i));
}
if ((i * ch_size + 1 < 32) && (ch_size == 2))
{
GFX_Objects[header_counter].obj[i * ch_size + 1] = ReadMemory((ushort)(addr + i));
}
}
} }
else else
{ {
graphics_read_time = 3 * GFX_Objects[header_counter].width; graphics_read_time = 6 * GFX_Objects[header_counter].width + 3;
ch_size = 1;
}
// do direct reads same as in 4 byte mode // the address here is specified by CHAR_BASE maria registers
for (int i = 0; i < GFX_Objects[header_counter].width; i++) //ushort addr = (ushort)(GFX_Objects[header_counter].addr & 0xFF);
for (int i = 0; i < GFX_Objects[header_counter].width; i++)
{
addr_t = ReadMemory((ushort)(GFX_Objects[header_counter].addr + i));
addr_t |= (ushort)((Core.Maria_regs[0x14] + current_DLL_offset) << 8);
if ((current_DLL_H16 && addr_t.Bit(12)) || (current_DLL_H8 && addr_t.Bit(11)))
{ {
GFX_Objects[header_counter].obj[i] = ReadMemory((ushort)((GFX_Objects[header_counter].addr + (current_DLL_offset << 8) + i))); if (i * ch_size < 32)
{
GFX_Objects[header_counter].obj[i * ch_size] = 0;
}
if ((i * ch_size + 1 < 32) && (ch_size == 2))
{
GFX_Objects[header_counter].obj[i * ch_size + 1] = 0;
}
if (ch_size == 1)
{
graphics_read_time -= 6;
}
else
{
graphics_read_time -= 9;
}
}
else
{
if (i * ch_size < 32)
{
GFX_Objects[header_counter].obj[i * ch_size] = ReadMemory(addr_t);
}
if ((i * ch_size + 1 < 32) && (ch_size == 2))
{
GFX_Objects[header_counter].obj[i * ch_size + 1] = ReadMemory(addr_t);
}
} }
} }
} }
@ -414,12 +419,21 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
for (int i = 0; i < GFX_Objects[header_counter].width; i++) for (int i = 0; i < GFX_Objects[header_counter].width; i++)
{ {
GFX_Objects[header_counter].obj[i] = ReadMemory((ushort)(GFX_Objects[header_counter].addr + i)); addr_t = (ushort)(GFX_Objects[header_counter].addr + (current_DLL_offset << 8) + i);
if ((current_DLL_H16 && addr_t.Bit(12)) || (current_DLL_H8 && addr_t.Bit(11)))
{
GFX_Objects[header_counter].obj[i] = 0;
graphics_read_time -= 3;
}
else
{
GFX_Objects[header_counter].obj[i] = ReadMemory(addr_t);
}
} }
} }
} }
if (DMA_phase_counter == graphics_read_time) if (DMA_phase_counter == graphics_read_time || graphics_read_time == 0)
{ {
// We have read the graphics data, for this header, now return to the header list // We have read the graphics data, for this header, now return to the header list
// This loop will continue until a header indicates its time to stop // This loop will continue until a header indicates its time to stop

View File

@ -34,7 +34,8 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
} }
else else
{ {
return TIA_regs[addr & 0x1F]; // TODO: what to return here? return tia.ReadMemory((ushort)(addr & 0x1F), false);
//return TIA_regs[addr & 0x1F]; // TODO: what to return here?
} }
} }
else if ((addr & 0xFCE0) == 0x20) else if ((addr & 0xFCE0) == 0x20)