Waterbox: Connect basic stdio. You get an empty stdin, real stdout and stderr, and can present readonly files to the core.
This commit is contained in:
parent
dafe5a43cd
commit
bc9726f687
|
@ -52,6 +52,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
_exe.Exit();
|
||||
}
|
||||
|
||||
private readonly List<string> _readonlyFiles = new List<string>();
|
||||
|
||||
public void AddReadonlyFile(byte[] data, string name)
|
||||
{
|
||||
_exe.AddReadonlyFile(data, name);
|
||||
_readonlyFiles.Add(name);
|
||||
}
|
||||
|
||||
public LibsnesApi(string dllPath)
|
||||
{
|
||||
_exe = new PeRunner(new PeRunnerOptions
|
||||
|
@ -366,6 +374,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
_core.DllInit();
|
||||
_exe.Seal();
|
||||
_sealed = true;
|
||||
foreach (var s in _readonlyFiles)
|
||||
{
|
||||
_exe.RemoveReadonlyFile(s);
|
||||
}
|
||||
_readonlyFiles.Clear();
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
|
|
|
@ -294,16 +294,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
return "";
|
||||
}
|
||||
|
||||
// build romfilename
|
||||
string test = CoreComm.CoreFileProvider.GetFirmwarePath("SNES", firmwareId, false, "Game may function incorrectly without the requested firmware.");
|
||||
string ret;
|
||||
var data = CoreComm.CoreFileProvider.GetFirmware("SNES", firmwareId, false, "Game may function incorrectly without the requested firmware.");
|
||||
if (data != null)
|
||||
{
|
||||
ret = hint;
|
||||
Api.AddReadonlyFile(data, hint);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = "";
|
||||
}
|
||||
|
||||
// we need to return something to bsnes
|
||||
test = test ?? "";
|
||||
|
||||
Console.WriteLine("Served libsnes request for firmware \"{0}\" with \"{1}\"", hint, test);
|
||||
Console.WriteLine("Served libsnes request for firmware \"{0}\"", hint);
|
||||
|
||||
// return the path we built
|
||||
return test;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void snes_trace(uint which, string msg)
|
||||
|
|
|
@ -226,12 +226,111 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
/// <summary>
|
||||
/// syscall emulation layer, as well as a bit of other stuff
|
||||
/// </summary>
|
||||
private class Syscalls
|
||||
private class Syscalls : IBinaryStateable
|
||||
{
|
||||
public interface IFileObject : IBinaryStateable
|
||||
{
|
||||
bool Open(FileAccess access);
|
||||
bool Close();
|
||||
Stream Stream { get; }
|
||||
string Name { get; }
|
||||
}
|
||||
|
||||
private class SpecialFile : IFileObject
|
||||
{
|
||||
// stdin, stdout, stderr
|
||||
public string Name { get; }
|
||||
public Stream Stream { get; }
|
||||
public bool Close() => false;
|
||||
public bool Open(FileAccess access) => false;
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer) { }
|
||||
public void LoadStateBinary(BinaryReader reader) { }
|
||||
|
||||
public SpecialFile(Stream stream, string name)
|
||||
{
|
||||
Stream = stream;
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
private class ReadonlyFirmware : IFileObject
|
||||
{
|
||||
private readonly byte[] _data;
|
||||
private readonly byte[] _hash;
|
||||
|
||||
public string Name { get; }
|
||||
public Stream Stream { get; private set; }
|
||||
public bool Close()
|
||||
{
|
||||
if (Stream == null)
|
||||
return false;
|
||||
Stream = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Open(FileAccess access)
|
||||
{
|
||||
if (Stream != null || access != FileAccess.Read)
|
||||
return false;
|
||||
Stream = new MemoryStream(_data, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader br)
|
||||
{
|
||||
if (!br.ReadBytes(_hash.Length).SequenceEqual(_hash))
|
||||
throw new InvalidOperationException("Savestate internal firmware mismatch");
|
||||
var pos = br.ReadInt64();
|
||||
if (pos == -1)
|
||||
{
|
||||
Stream = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Stream == null)
|
||||
Open(FileAccess.Read);
|
||||
Stream.Position = pos;
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(_hash);
|
||||
bw.Write(Stream != null ? Stream.Position : -1);
|
||||
}
|
||||
|
||||
public ReadonlyFirmware(byte[] data, string name)
|
||||
{
|
||||
_data = data;
|
||||
_hash = WaterboxUtils.Hash(data);
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<IFileObject> _openFiles = new List<IFileObject>();
|
||||
private readonly Dictionary<string, IFileObject> _availableFiles = new Dictionary<string, IFileObject>();
|
||||
|
||||
private readonly PeRunner _parent;
|
||||
public Syscalls(PeRunner parent)
|
||||
{
|
||||
_parent = parent;
|
||||
var stdin = new SpecialFile(Stream.Null, "___stdin");
|
||||
var stdout = new SpecialFile(Console.OpenStandardOutput(), "___stdout");
|
||||
var stderr = new SpecialFile(Console.OpenStandardError(), "___stderr");
|
||||
|
||||
_openFiles = new List<IFileObject>
|
||||
{
|
||||
stdin,
|
||||
stdout,
|
||||
stderr
|
||||
};
|
||||
_availableFiles = new Dictionary<string, IFileObject>
|
||||
{
|
||||
[stdin.Name] = stdin,
|
||||
[stdout.Name] = stdout,
|
||||
[stderr.Name] = stderr
|
||||
};
|
||||
}
|
||||
|
||||
private IntPtr _pthreadSelf;
|
||||
|
@ -243,6 +342,14 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
_pthreadSelf = Z.US(_parent._plainheap.Allocate(512, 1));
|
||||
}
|
||||
|
||||
private Stream StreamForFd(int fd)
|
||||
{
|
||||
if (fd >= 0 && fd < _openFiles.Count)
|
||||
return _openFiles[fd].Stream;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
[BizExport(CallingConvention.Cdecl, EntryPoint = "log_output")]
|
||||
public void DebugPuts(IntPtr s)
|
||||
{
|
||||
|
@ -297,41 +404,120 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
[BizExport(CallingConvention.Cdecl, EntryPoint = "n0")]
|
||||
public long Read(int fd, IntPtr buff, ulong count)
|
||||
{
|
||||
return 0;
|
||||
var s = StreamForFd(fd);
|
||||
if (s == null || !s.CanRead)
|
||||
return -1;
|
||||
var tmp = new byte[count];
|
||||
var ret = s.Read(tmp, 0, (int)count);
|
||||
Marshal.Copy(tmp, 0, buff, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
[BizExport(CallingConvention.Cdecl, EntryPoint = "n1")]
|
||||
public long Write(int fd, IntPtr buff, ulong count)
|
||||
{
|
||||
var s = StreamForFd(fd);
|
||||
if (s == null || !s.CanWrite)
|
||||
return -1;
|
||||
var tmp = new byte[count];
|
||||
Marshal.Copy(buff, tmp, 0, (int)count);
|
||||
s.Write(tmp, 0, tmp.Length);
|
||||
return (long)count;
|
||||
}
|
||||
|
||||
[BizExport(CallingConvention.Cdecl, EntryPoint = "n19")]
|
||||
public unsafe long Readv(int fd, Iovec* iov, int iovcnt)
|
||||
{
|
||||
return 0;
|
||||
long ret = 0;
|
||||
for (int i = 0; i < iovcnt; i++)
|
||||
{
|
||||
var len = Read(fd, iov[i].Base, iov[i].Length);
|
||||
if (len < 0)
|
||||
return len;
|
||||
ret += len;
|
||||
if (len != (long)iov[i].Length)
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
[BizExport(CallingConvention.Cdecl, EntryPoint = "n20")]
|
||||
public unsafe long Writev(int fd, Iovec* iov, int iovcnt)
|
||||
{
|
||||
long ret = 0;
|
||||
for (int i = 0; i < iovcnt; i++)
|
||||
{
|
||||
ret += (long)iov[i].Length;
|
||||
}
|
||||
ret += Write(fd, iov[i].Base, iov[i].Length);
|
||||
return ret;
|
||||
}
|
||||
|
||||
[BizExport(CallingConvention.Cdecl, EntryPoint = "n2")]
|
||||
public int Open(string path, int flags, int mode)
|
||||
{
|
||||
return -1;
|
||||
IFileObject o;
|
||||
if (!_availableFiles.TryGetValue(path, out o))
|
||||
return -1;
|
||||
if (_openFiles.Contains(o))
|
||||
return -1;
|
||||
FileAccess access;
|
||||
switch (flags & 3)
|
||||
{
|
||||
case 0:
|
||||
access = FileAccess.Read;
|
||||
break;
|
||||
case 1:
|
||||
access = FileAccess.Write;
|
||||
break;
|
||||
case 2:
|
||||
access = FileAccess.ReadWrite;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
if (!o.Open(access))
|
||||
return -1;
|
||||
int fd;
|
||||
for (fd = 0; fd < _openFiles.Count; fd++)
|
||||
if (_openFiles[fd] == null)
|
||||
break;
|
||||
if (fd == _openFiles.Count)
|
||||
_openFiles.Add(o);
|
||||
else
|
||||
_openFiles[fd] = o;
|
||||
return fd;
|
||||
}
|
||||
[BizExport(CallingConvention.Cdecl, EntryPoint = "n3")]
|
||||
public int Close(int fd)
|
||||
{
|
||||
if (fd < 0 || fd >= _openFiles.Count)
|
||||
return -1;
|
||||
var o = _openFiles[fd];
|
||||
if (o == null || !o.Close())
|
||||
return -1;
|
||||
_openFiles[fd] = null;
|
||||
return 0;
|
||||
}
|
||||
[BizExport(CallingConvention.Cdecl, EntryPoint = "n8")]
|
||||
public long Seek(int fd, long offset, int type)
|
||||
{
|
||||
var s = StreamForFd(fd);
|
||||
if (s == null || !s.CanSeek)
|
||||
return -1;
|
||||
SeekOrigin o;
|
||||
switch (type)
|
||||
{
|
||||
case 0:
|
||||
o = SeekOrigin.Begin;
|
||||
break;
|
||||
case 1:
|
||||
o = SeekOrigin.Current;
|
||||
break;
|
||||
case 2:
|
||||
o = SeekOrigin.End;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return s.Seek(offset, o);
|
||||
}
|
||||
|
||||
[BizExport(CallingConvention.Cdecl, EntryPoint = "n4")]
|
||||
public int Stat(string path, IntPtr statbuf)
|
||||
|
@ -475,6 +661,59 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
}
|
||||
return _parent._mmapheap.Protect((ulong)address, (ulong)size, mprot) ? 0 : -1;
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(_availableFiles.Count);
|
||||
foreach (var f in _availableFiles.Values.OrderBy(f => f.Name))
|
||||
{
|
||||
bw.Write(f.Name);
|
||||
f.SaveStateBinary(bw);
|
||||
}
|
||||
bw.Write(_openFiles.Count);
|
||||
foreach (var f in _openFiles)
|
||||
{
|
||||
bw.Write(f != null);
|
||||
if (f != null)
|
||||
bw.Write(f.Name);
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader br)
|
||||
{
|
||||
if (_availableFiles.Count != br.ReadInt32())
|
||||
throw new InvalidOperationException("Internal savestate error: Filelist change");
|
||||
foreach (var f in _availableFiles.Values.OrderBy(f => f.Name))
|
||||
{
|
||||
if (br.ReadString() != f.Name)
|
||||
throw new InvalidOperationException("Internal savestate error: Filelist change");
|
||||
f.LoadStateBinary(br);
|
||||
}
|
||||
var c = br.ReadInt32();
|
||||
_openFiles.Clear();
|
||||
for (int i = 0; i < c; i++)
|
||||
{
|
||||
_openFiles.Add(br.ReadBoolean() ? _availableFiles[br.ReadString()] : null);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddReadonlyFile(byte[] data, string name)
|
||||
{
|
||||
_availableFiles.Add(name, new ReadonlyFirmware(data, name));
|
||||
}
|
||||
|
||||
public void RemoveReadonlyFile(string name)
|
||||
{
|
||||
IFileObject o;
|
||||
if (!_availableFiles.TryGetValue(name, out o))
|
||||
throw new InvalidOperationException("Firmware was never registered!");
|
||||
var f = o as ReadonlyFirmware;
|
||||
if (f == null)
|
||||
throw new InvalidOperationException("Object was not a firmware!");
|
||||
if (_openFiles.Contains(o))
|
||||
throw new InvalidOperationException("Core never closed the firmware!");
|
||||
_availableFiles.Remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -559,8 +798,6 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
// int pthread_mutex_unlock(pthread_mutex_t* mutex);
|
||||
[BizExport(CallingConvention.Cdecl, EntryPoint = "pthread_mutex_unlock")]
|
||||
public int PthreadMutexUnlock(IntPtr mutex) { return 0; }*/
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -832,6 +1069,27 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a file that will appear to the waterbox core's libc. the file will be read only.
|
||||
/// All savestates must have the same file list, so either leave it up forever or remove it during init!
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="name">the filename that the unmanaged core will access the file by</param>
|
||||
public void AddReadonlyFile(byte[] data, string name)
|
||||
{
|
||||
_syscalls.AddReadonlyFile((byte[])data.Clone(), name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a file previously added by AddReadonlyFile. Frees the internal copy of the filedata, saving memory.
|
||||
/// All savestates must have the same file list, so either leave it up forever or remove it during init!
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
public void RemoveReadonlyFile(string name)
|
||||
{
|
||||
_syscalls.RemoveReadonlyFile(name);
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(_createstamp);
|
||||
|
|
Binary file not shown.
|
@ -118,12 +118,12 @@ static void cmd_trn(uint32_t which)
|
|||
}
|
||||
else
|
||||
{
|
||||
utils_log("SGB: TRN already queued!");
|
||||
utils_log("SGB: TRN already queued!\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
utils_log("SGB: cmd_trn bad length");
|
||||
utils_log("SGB: cmd_trn bad length\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ static void cmd_pal(int a, int b)
|
|||
}
|
||||
else
|
||||
{
|
||||
utils_log("SGB: cmd_pal bad length");
|
||||
utils_log("SGB: cmd_pal bad length\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,7 +178,7 @@ static void cmd_pal_set(void)
|
|||
}
|
||||
else
|
||||
{
|
||||
utils_log("SGB: cmd_pal bad length");
|
||||
utils_log("SGB: cmd_pal bad length\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,13 +187,13 @@ static void cmd_attr_blk()
|
|||
int nset = sgb.command[1];
|
||||
if (nset <= 0 || nset >= 19)
|
||||
{
|
||||
utils_log("SGB: cmd_attr_blk bad nset");
|
||||
utils_log("SGB: cmd_attr_blk bad nset\n");
|
||||
return;
|
||||
}
|
||||
int npacket = (nset * 6 + 16) / 16;
|
||||
if ((sgb.command[0] & 7) != npacket)
|
||||
{
|
||||
utils_log("SGB: cmd_attr_blk bad length");
|
||||
utils_log("SGB: cmd_attr_blk bad length\n");
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < nset; i++)
|
||||
|
@ -242,13 +242,13 @@ static void cmd_attr_lin()
|
|||
int nset = sgb.command[1];
|
||||
if (nset <= 0 || nset >= 111)
|
||||
{
|
||||
utils_log("SGB: cmd_attr_lin bad nset");
|
||||
utils_log("SGB: cmd_attr_lin bad nset\n");
|
||||
return;
|
||||
}
|
||||
int npacket = (nset + 17) / 16;
|
||||
if ((sgb.command[0] & 7) != npacket)
|
||||
{
|
||||
utils_log("SGB: cmd_attr_lin bad length");
|
||||
utils_log("SGB: cmd_attr_lin bad length\n");
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < nset; i++)
|
||||
|
@ -315,7 +315,7 @@ static void cmd_attr_div()
|
|||
}
|
||||
else
|
||||
{
|
||||
utils_log("SGB: cmd_attr_div bad length");
|
||||
utils_log("SGB: cmd_attr_div bad length\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,13 +326,13 @@ static void cmd_attr_chr()
|
|||
int n = sgb.command[3] | sgb.command[4] << 8;
|
||||
if (n > 360)
|
||||
{
|
||||
utils_log("SGB: cmd_attr_chr bad n");
|
||||
utils_log("SGB: cmd_attr_chr bad n\n");
|
||||
return;
|
||||
}
|
||||
int npacket = (n + 87) / 64;
|
||||
if ((sgb.command[0] & 7) != npacket)
|
||||
{
|
||||
utils_log("SGB: cmd_attr_chr bad length");
|
||||
utils_log("SGB: cmd_attr_chr bad length\n");
|
||||
return;
|
||||
}
|
||||
uint8_t *dst = sgb.attr;
|
||||
|
@ -386,7 +386,7 @@ static void cmd_attr_set()
|
|||
}
|
||||
else
|
||||
{
|
||||
utils_log("SGB: cmd_attr_set bad length");
|
||||
utils_log("SGB: cmd_attr_set bad length\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -410,11 +410,11 @@ static void cmd_mlt_req(void)
|
|||
sgb.joypad_index = 1;
|
||||
break;
|
||||
}
|
||||
utils_log("SGB: %u joypads", sgb.num_joypads);
|
||||
utils_log("SGB: %u joypads\n", sgb.num_joypads);
|
||||
}
|
||||
else
|
||||
{
|
||||
utils_log("SGB: cmd_mlt_req bad length");
|
||||
utils_log("SGB: cmd_mlt_req bad length\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -440,7 +440,7 @@ static void cmd_mask(void)
|
|||
}
|
||||
else
|
||||
{
|
||||
utils_log("SGB: cmd_mask bad length");
|
||||
utils_log("SGB: cmd_mask bad length\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -455,7 +455,7 @@ static void cmd_sound(void)
|
|||
}
|
||||
else
|
||||
{
|
||||
utils_log("SGB: cmd_sound bad length");
|
||||
utils_log("SGB: cmd_sound bad length\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -465,69 +465,69 @@ static void do_command(void)
|
|||
switch (command)
|
||||
{
|
||||
default:
|
||||
utils_log("SGB: Unknown or unimplemented command %02xh", command);
|
||||
utils_log("SGB: Unknown or unimplemented command %02xh\n", command);
|
||||
break;
|
||||
|
||||
case 0x00: // PAL01
|
||||
utils_log("SGB: PAL01");
|
||||
utils_log("SGB: PAL01\n");
|
||||
cmd_pal(0, 1);
|
||||
break;
|
||||
case 0x01: // PAL23
|
||||
utils_log("SGB: PAL23");
|
||||
utils_log("SGB: PAL23\n");
|
||||
cmd_pal(2, 3);
|
||||
break;
|
||||
case 0x02: // PAL03
|
||||
utils_log("SGB: PAL03");
|
||||
utils_log("SGB: PAL03\n");
|
||||
cmd_pal(0, 3);
|
||||
break;
|
||||
case 0x03: // PAL12
|
||||
utils_log("SGB: PAL12");
|
||||
utils_log("SGB: PAL12\n");
|
||||
cmd_pal(1, 2);
|
||||
break;
|
||||
case 0x0a: // PAL_SET
|
||||
utils_log("SGB: PAL_SET");
|
||||
utils_log("SGB: PAL_SET\n");
|
||||
cmd_pal_set();
|
||||
break;
|
||||
|
||||
case 0x04: // ATTR_BLK
|
||||
utils_log("SGB: ATTR_BLK");
|
||||
utils_log("SGB: ATTR_BLK\n");
|
||||
cmd_attr_blk();
|
||||
break;
|
||||
case 0x05: // ATTR_LIN
|
||||
utils_log("SGB: ATTR_LIN");
|
||||
utils_log("SGB: ATTR_LIN\n");
|
||||
cmd_attr_lin();
|
||||
break;
|
||||
case 0x06: // ATTR_DIV
|
||||
utils_log("SGB: ATTR_DIV");
|
||||
utils_log("SGB: ATTR_DIV\n");
|
||||
cmd_attr_div();
|
||||
break;
|
||||
case 0x07: // ATTR_CHR
|
||||
utils_log("SGB: ATTR_CHR");
|
||||
utils_log("SGB: ATTR_CHR\n");
|
||||
cmd_attr_chr();
|
||||
break;
|
||||
case 0x16: // ATTR_SET
|
||||
utils_log("SGB: ATTR_SET");
|
||||
utils_log("SGB: ATTR_SET\n");
|
||||
cmd_attr_set();
|
||||
break;
|
||||
|
||||
case 0x17: // MASK_EN
|
||||
utils_log("SGB: MASK_EN");
|
||||
utils_log("SGB: MASK_EN\n");
|
||||
cmd_mask();
|
||||
break;
|
||||
|
||||
// unknown functions
|
||||
case 0x0c: // ATRC_EN
|
||||
utils_log("SGB: ATRC_EN??");
|
||||
utils_log("SGB: ATRC_EN??\n");
|
||||
break;
|
||||
case 0x0d: // TEST_EN
|
||||
utils_log("SGB: TEST_EN??");
|
||||
utils_log("SGB: TEST_EN??\n");
|
||||
break;
|
||||
case 0x0e: // ICON_EN
|
||||
utils_log("SGB: ICON_EN??");
|
||||
utils_log("SGB: ICON_EN??\n");
|
||||
break;
|
||||
case 0x18: // OBJ_TRN
|
||||
// no game used this
|
||||
utils_log("SGB: OBJ_TRN??");
|
||||
utils_log("SGB: OBJ_TRN??\n");
|
||||
break;
|
||||
|
||||
// unimplementable functions
|
||||
|
@ -535,46 +535,46 @@ static void do_command(void)
|
|||
// TODO: Is it possible for this (and DATA_TRN) to write data to
|
||||
// memory areas used for the attribute file, etc?
|
||||
// If so, do games do this?
|
||||
utils_log("SGB: DATA_SND!! %02x:%02x%02x [%02x]", sgb.command[3], sgb.command[2], sgb.command[1], sgb.command[4]);
|
||||
utils_log("SGB: DATA_SND!! %02x:%02x%02x [%02x]\n", sgb.command[3], sgb.command[2], sgb.command[1], sgb.command[4]);
|
||||
break;
|
||||
case 0x10: // DATA_TRN
|
||||
utils_log("SGB: DATA_TRN!!");
|
||||
utils_log("SGB: DATA_TRN!!\n");
|
||||
break;
|
||||
case 0x12: // JUMP
|
||||
utils_log("SGB: JUMP!!");
|
||||
utils_log("SGB: JUMP!!\n");
|
||||
break;
|
||||
|
||||
// joypad
|
||||
case 0x11: // MLT_REQ
|
||||
utils_log("SGB: MLT_REQ");
|
||||
utils_log("SGB: MLT_REQ\n");
|
||||
cmd_mlt_req();
|
||||
break;
|
||||
|
||||
// sound
|
||||
case 0x08: // SOUND
|
||||
utils_log("SGB: SOUND %02x %02x %02x %02x", sgb.command[1], sgb.command[2], sgb.command[3], sgb.command[4]);
|
||||
utils_log("SGB: SOUND %02x %02x %02x %02x\n", sgb.command[1], sgb.command[2], sgb.command[3], sgb.command[4]);
|
||||
cmd_sound();
|
||||
break;
|
||||
|
||||
// all vram transfers
|
||||
case 0x09: // SOU_TRN
|
||||
utils_log("SGB: SOU_TRN");
|
||||
utils_log("SGB: SOU_TRN\n");
|
||||
cmd_trn(TRN_SOUND);
|
||||
break;
|
||||
case 0x0b: // PAL_TRN
|
||||
utils_log("SGB: PAL_TRN");
|
||||
utils_log("SGB: PAL_TRN\n");
|
||||
cmd_trn(TRN_PAL);
|
||||
break;
|
||||
case 0x13: // CHR_TRN
|
||||
utils_log("SGB: CHR_TRN");
|
||||
utils_log("SGB: CHR_TRN\n");
|
||||
cmd_trn(sgb.command[1] & 1 ? TRN_CHR_HI : TRN_CHR_LOW);
|
||||
break;
|
||||
case 0x14: // PCT_TRN
|
||||
utils_log("SGB: PCT_TRN");
|
||||
utils_log("SGB: PCT_TRN\n");
|
||||
cmd_trn(TRN_PCT);
|
||||
break;
|
||||
case 0x15: // ATTR_TRN
|
||||
utils_log("SGB: ATTR_TRN");
|
||||
utils_log("SGB: ATTR_TRN\n");
|
||||
cmd_trn(TRN_ATTR);
|
||||
break;
|
||||
}
|
||||
|
@ -590,7 +590,7 @@ static void do_packet(void)
|
|||
|
||||
if (sgb.expected_packets == 0) // huh?
|
||||
{
|
||||
utils_log("SGB: zero packet command");
|
||||
utils_log("SGB: zero packet command\n");
|
||||
sgb.expected_packets = 0;
|
||||
sgb.next_packet = 0;
|
||||
}
|
||||
|
@ -617,7 +617,7 @@ int sgb_init(const uint8_t *spc, int length)
|
|||
spc_reset(sgb.spc);
|
||||
if (spc_load_spc(sgb.spc, spc, length) != NULL)
|
||||
{
|
||||
utils_log("SGB: Failed to load SPC");
|
||||
utils_log("SGB: Failed to load SPC\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -648,7 +648,7 @@ void sgb_write_ff00(uint8_t val, uint64_t time)
|
|||
if (p14_fell)
|
||||
do_packet();
|
||||
else
|
||||
utils_log("SGB: Stop bit not present");
|
||||
utils_log("SGB: Stop bit not present\n");
|
||||
sgb.read_index = 255;
|
||||
}
|
||||
else
|
||||
|
@ -733,17 +733,17 @@ static void trn_sound(const uint8_t* data)
|
|||
{
|
||||
int len = data[0] | data[1] << 8;
|
||||
int addr = data[2] | data[3] << 8;
|
||||
utils_log("TRN_SOUND %04x %04x", addr, len);
|
||||
utils_log("TRN_SOUND %04x %04x\n", addr, len);
|
||||
uint8_t* dst = spc_get_ram(sgb.spc);
|
||||
|
||||
if (len > 0xffc)
|
||||
{
|
||||
utils_log("TRN_SOUND src overflow");
|
||||
utils_log("TRN_SOUND src overflow\n");
|
||||
return;
|
||||
}
|
||||
if (len + addr >= 0x10000)
|
||||
{
|
||||
utils_log("TRN_SOUND dst overflow");
|
||||
utils_log("TRN_SOUND dst overflow\n");
|
||||
return;
|
||||
}
|
||||
memcpy(dst + addr, data + 4, len);
|
||||
|
|
|
@ -36,7 +36,7 @@ void utils_log(const char *format, ...)
|
|||
va_list args;
|
||||
va_start(args, format);
|
||||
vsnprintf(buf, 256, format, args);
|
||||
_debug_puts(buf);
|
||||
fputs(buf, stdout);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ void utils_log_urgent(const char *format, ...)
|
|||
va_list args;
|
||||
va_start(args, format);
|
||||
vsnprintf(buf, 256, format, args);
|
||||
_debug_puts(buf);
|
||||
fputs(buf, stdout);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ EXPORT int Init(const void *rom, int romlen, int sgb, const void *spc, int spcle
|
|||
return 0; // failure
|
||||
global_sgb = !!sgb;
|
||||
if (global_sgb && global_cgb)
|
||||
utils_log("Warn: CGB game in SGB mode");
|
||||
utils_log("Warn: CGB game in SGB mode\n");
|
||||
if (sgb && !sgb_init((const uint8_t*)spc, spclen))
|
||||
return 0;
|
||||
|
||||
|
|
Loading…
Reference in New Issue