Add Gunsight mode

This commit is contained in:
jimmystones
2022-08-23 21:19:22 +01:00
parent 65e5cb984f
commit d7241e7735
25 changed files with 3355 additions and 2376 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 B

After

Width:  |  Height:  |  Size: 279 B

661
rommaker/Program.cs Normal file
View File

@@ -0,0 +1,661 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
namespace rommaker
{
class Program
{
static string TilemapRomPath => $@"{resourceOutputPath}tilemap.bin";
static string TilemapPath => $@"{resourcePath}tilemap\tilemap.png";
static string TilemapExtractPath => $@"{resourcePath}tilemap\tilemap_extract.png";
static string TilemapSourcePath => $@"{sourcePath}tilemap_indexes";
static string SpriteRomPath => $@"{resourceOutputPath}sprite.bin";
static string SpritePath => $@"{resourcePath}sprites\";
static string PalettePath => $@"{resourceOutputPath}palette.bin";
static string SpriteSourcePath => $@"{sourcePath}sprite_images";
static string MusicPath => $@"{resourcePath}music\";
static string MusicTrackListPath => $@"{resourcePath}music\tracks.txt";
static string MusicRomPath => $@"{resourceOutputPath}music.bin";
static string MusicSourcePath => $@"{sourcePath}music_tracks";
static string SoundPath => $@"{resourcePath}sound\";
static string SoundListPath => $@"{resourcePath}sound\samples.txt";
static string SoundRomPath => $@"{resourceOutputPath}sound.bin";
static string SoundSourcePath => $@"{sourcePath}sound_samples";
static readonly int PaletteMax = 4;
static readonly int PaletteIndexMax = 32;
static readonly List<List<Color>> Palettes = new();
static long musicRomLength = (1024 * 64);
static void CreateMusicRom()
{
Console.WriteLine("CREATING MUSIC ROM");
if (File.Exists(MusicRomPath)) { File.Delete(MusicRomPath); }
List<byte> trackData = new();
string[] trackPos = new string[0];
StringBuilder builder = new();
builder.AppendLine("#ifndef MUSIC_TRACKS_H");
builder.AppendLine("#define MUSIC_TRACKS_H");
builder.AppendLine("#define const_music_track_max 32");
builder.AppendLine("extern unsigned long music_track_address[const_music_track_max];");
if (File.Exists(MusicTrackListPath))
{
// Read track list
string[] tracks = File.ReadAllLines(MusicTrackListPath);
trackPos = new string[tracks.Length];
int t = 0;
uint p = 0;
foreach (string track in tracks)
{
string file = track.Split("#")[0];
string name = track.Split("#")[1];
byte[] data = File.ReadAllBytes(MusicPath + file);
trackData.AddRange(data);
trackPos[t] = p + "u";
builder.AppendLine($"#define const_music_{name} {t}");
Console.WriteLine($"\t{t} - {p} > {file}");
p += (uint)data.Length;
t++;
}
}
//while (trackData.Count < musicRomLength)
//{
// trackData.Add(0);
//}
builder.AppendLine("#endif");
File.WriteAllText(MusicSourcePath + ".h", builder.ToString());
builder = new StringBuilder();
builder.AppendLine("#ifndef MUSIC_TRACKS_C");
builder.AppendLine("#define MUSIC_TRACKS_C");
builder.AppendLine("#include \"music_tracks.h\"");
if (trackPos.Length > 0)
{
builder.AppendLine("unsigned long music_track_address[] = {" + string.Join(",", trackPos) + "};");
}
else
{
builder.AppendLine("unsigned long music_track_address[] = {0u};");
}
builder.AppendLine("#endif");
File.WriteAllText(MusicSourcePath + ".c", builder.ToString());
File.WriteAllBytes(MusicRomPath, trackData.ToArray());
}
static void CreateSoundRom()
{
Console.WriteLine("CREATING SOUND ROM");
if (File.Exists(SoundRomPath)) { File.Delete(SoundRomPath); }
// Read sample list
string[] samples = { };
if (File.Exists(SoundListPath))
{
samples = File.ReadAllLines(SoundListPath);
}
List<byte> soundData = new();
string[] soundPos = new string[samples.Length];
string[] soundLen = new string[samples.Length];
string command = $@"C:\Program Files (x86)\sox-14-4-2\sox.exe";
int t = 0;
uint p = 0;
StringBuilder builder = new();
builder.AppendLine("#ifndef SOUND_SAMPLES_H");
builder.AppendLine("#define SOUND_SAMPLES_H");
builder.AppendLine("#define const_sound_sample_max 32");
builder.AppendLine("extern unsigned long sound_sample_address[const_sound_sample_max];");
builder.AppendLine("extern unsigned long sound_sample_length[const_sound_sample_max];");
builder.AppendLine($"#define const_sound_sample_used {samples.Length}");
foreach (string sample in samples)
{
string file = sample.Split("#")[0];
string name = sample.Split("#")[1];
if (!File.Exists($"{SoundPath}{file}.vox") || File.GetLastWriteTimeUtc($"{SoundPath}{file}.wav") > File.GetLastWriteTimeUtc($"{SoundPath}{file}.vox"))
{
string args = $"{SoundPath}{file}.wav {SoundPath}{file}.vox rate 8k";
Process.Start(command, args).WaitForExit();
}
byte[] data = File.ReadAllBytes($"{SoundPath}{file}.vox");
soundData.AddRange(data);
soundPos[t] = p + "u";
uint l = (uint)data.Length;
soundLen[t] = l + "u";
builder.AppendLine($"#define const_sound_{name} {t}");
Console.WriteLine($"\t{t} - {p} > {file}");
p += l;
t++;
}
builder.AppendLine("#endif");
File.WriteAllText(SoundSourcePath + ".h", builder.ToString());
builder = new StringBuilder();
builder.AppendLine("#ifndef SOUND_SAMPLES_C");
builder.AppendLine("#define SOUND_SAMPLES_C");
builder.AppendLine("#include \"sound_samples.h\"");
if (soundPos.Length > 0)
{
builder.AppendLine("unsigned long sound_sample_address[] = {" + string.Join(",", soundPos) + "};");
builder.AppendLine("unsigned long sound_sample_length[] = {" + string.Join(",", soundLen) + "};");
}
else
{
builder.AppendLine("unsigned long sound_sample_address[] = {0u};");
builder.AppendLine("unsigned long sound_sample_length[] = {0u};");
}
builder.AppendLine("#endif");
File.WriteAllText(SoundSourcePath + ".c", builder.ToString());
File.WriteAllBytes(SoundRomPath, soundData.ToArray());
}
static void CreateSpriteRom()
{
for (int p = 0; p < PaletteMax; p++)
{
Palettes.Add(new List<Color>());
Palettes[p].Add(Color.FromArgb(0, 0, 0, 0));
}
if (File.Exists(SpriteRomPath)) { File.Delete(SpriteRomPath); }
if (File.Exists(PalettePath)) { File.Delete(PalettePath); }
Dictionary<string, string> spriteSourceItems = new();
if (Directory.Exists(SpritePath))
{
FileStream spriteStream = File.OpenWrite(SpriteRomPath);
BinaryWriter spriteStreamWriter = new(spriteStream, Encoding.Default);
uint pos = 0;
int[] groups = { 32, 16, 8 };
Dictionary<int, MemoryStream> groupStreams = new();
int groupIndex = groups.Length - 1;
for (int gi = 0; gi < groups.Length; gi++)
{
int g = groups[gi];
int index = 0;
ushort groupStartPos = (ushort)(pos + (groups.Length * 2));
byte[] groupStartBytes = BitConverter.GetBytes(groupStartPos);
Console.WriteLine($"Starting image group {g} at {groupStartPos}");
spriteStreamWriter.Write(groupStartBytes[1]); // Write start point for size group
spriteStreamWriter.Write(groupStartBytes[0]); // Write start point for size group
MemoryStream groupStream = new();
ushort groupLen = 0;
foreach (string image in Directory.GetFiles(SpritePath, "*.png", SearchOption.TopDirectoryOnly).Where(x => x.Contains($"\\{g}_")))
{
Bitmap img = new(image);
// Remove extension
string title = image[0..^4];
// Detect palette
if (!title.Contains('#'))
{
throw new Exception("No palette data");
}
string[] paletteParts = title.Split("#");
title = paletteParts[0];
int paletteIndex = int.Parse(paletteParts[1]) - 1;
// Get name
int size = int.Parse(title[(title.LastIndexOf("\\") + 1)..].Split('_')[0]);
string name = title.Split("-")[0].Split("_")[1];
// Detect slices
int imageSizeX = img.Width;
int imageSizeY = img.Height;
int slicesX = imageSizeX / size;
int slicesY = imageSizeY / size;
// Add header items
spriteSourceItems.Add($"sprite_index_{name}_first", index.ToString());
spriteSourceItems.Add($"sprite_index_{name}_count", $"{slicesX * slicesY}");
spriteSourceItems.Add($"sprite_index_{name}_last", $"{index + (slicesX * slicesY) - 1}");
spriteSourceItems.Add($"sprite_palette_{name}", $"{paletteIndex}");
spriteSourceItems.Add($"sprite_size_{name}", $"{gi}");
spriteSourceItems.Add($"sprite_pixelsize_{name}", $"{g}");
spriteSourceItems.Add($"sprite_halfpixelsize_{name}", $"{g/2}");
for (int ys = 0; ys < slicesY; ys++)
{
for (int xs = 0; xs < slicesX; xs++)
{
Console.WriteLine($"{index}: {name} - {xs},{ys} --> {pos}");
int ymin = ys * size;
int ymax = ymin + size;
int xmin = xs * size;
int xmax = xmin + size;
for (int y = ymin; y < ymax; y++)
{
for (int x = xmin; x < xmax; x++)
{
Color c = img.GetPixel(x, y);
// Find colour in palette
int pi = -1;
for (int ci = 0; ci < Palettes[paletteIndex].Count; ci++)
{
if (Palettes[paletteIndex][ci] == c)
{
pi = ci;
}
}
// Colour not found, add to paletta
if (pi == -1)
{
pi = Palettes[paletteIndex].Count;
if (pi == PaletteIndexMax)
{
// throw new Exception("too many colours");
Console.WriteLine($"Palette full: {image} - {xs},{ys} - {pi}, {c}");
pi = 0;
}
else
{
Palettes[paletteIndex].Add(c);
// Console.WriteLine($"Adding to palette: {image} - {xs},{ys} - {pi}, {c}");
}
}
// Write palette index to sprite rom
groupStream.WriteByte(Convert.ToByte(pi));
pos += 1;
groupLen += 1;
}
}
index++;
}
}
}
Console.WriteLine($"Ending image group {g} - length = {groupLen}");
groupIndex--;
groupStreams.Add(g, groupStream);
}
foreach (int g in groups)
{
spriteStreamWriter.Write(groupStreams[g].ToArray());
}
spriteStreamWriter.Dispose();
}
StringBuilder builder = new();
builder.AppendLine("#ifndef SPRITE_IMAGES_H");
builder.AppendLine("#define SPRITE_IMAGES_H");
foreach (string v in spriteSourceItems.Keys)
{
builder.AppendLine("#define " + v + " " + spriteSourceItems[v]);
}
builder.AppendLine("#endif");
File.WriteAllText(SpriteSourcePath + ".h", builder.ToString());
// Palettes
foreach (List<Color> palette in Palettes)
{
while (palette.Count < PaletteIndexMax)
{
palette.Add(Color.FromArgb(255, 0, 255, 0));
}
}
if (File.Exists(PalettePath))
{
File.Delete(PalettePath);
}
using (FileStream paletteStream = File.OpenWrite(PalettePath))
{
using (BinaryWriter paletteWriter = new(paletteStream, Encoding.BigEndianUnicode))
{
int pi = 0;
foreach (List<Color> palette in Palettes)
{
for (int p = 0; p < palette.Count; p++)
{
ushort a = (ushort)(palette[p].A == 255 ? 1 : 0);
ushort color = (ushort)((palette[p].R / 8) |
((palette[p].G / 8) << 5) |
((palette[p].B / 8) << 10) |
a << 15);
byte high = (byte)(color >> 8);
byte low = (byte)color;
// Console.WriteLine($"PALETTE {pi}: {p} - {palette[p]} - {a} - {color.ToString("X2")} - {high} {low}");
paletteWriter.Write(high);
paletteWriter.Write(low);
}
pi++;
}
}
}
}
static void CreateTilemapRom()
{
if (File.Exists(TilemapRomPath)) { File.Delete(TilemapRomPath); }
FileStream stream = File.OpenWrite(TilemapRomPath);
BinaryWriter streamWriter = new(stream, Encoding.Default);
if (!File.Exists(TilemapPath))
{
Console.WriteLine("No tilemap!");
return;
}
Dictionary<ushort, int> colours = new();
uint pos = 0;
Bitmap img = new(TilemapPath);
int size = 16;
int slicesX = img.Width / size;
int slicesY = img.Height / size;
for (int ys = 0; ys < slicesY; ys++)
{
for (int xs = 0; xs < slicesX; xs++)
{
int ymin = ys * size;
int ymax = ymin + size;
int xmin = xs * size;
int xmax = xmin + size;
for (int y = ymin; y < ymax; y++)
{
for (int x = xmin; x < xmax; x++)
{
Color c = img.GetPixel(x, y);
ushort a = (ushort)(c.A == 255 ? 1 : 0);
ushort color = (ushort)((c.R / 8) |
((c.G / 8) << 5) |
((c.B / 8) << 10) |
a << 15);
if (colours.ContainsKey(color))
{
colours[color]++;
}
else
{
colours[color] = 1;
}
byte high = (byte)(color >> 8);
byte low = (byte)color;
streamWriter.Write(high);
streamWriter.Write(low);
pos += 1;
}
}
}
}
streamWriter.Dispose();
Console.WriteLine($"Tilemap created. Unique colours={colours.Keys.Count}");
}
static void ExtractTilemapRom()
{
if (File.Exists(TilemapRomPath)) { File.Delete(TilemapRomPath); }
Dictionary<ushort, int> colours = new();
uint pos = 0;
Bitmap img = new(TilemapExtractPath);
int size = 16;
int slicesX = img.Width / size;
int slicesY = img.Height / size;
// Find unique tiles in image
int[,] tileIndexes = new int[slicesX, slicesY];
Dictionary<string, Tile> tiles = new();
int droppedTransparent = 0;
int droppedDuplicate = 0;
Tile transTile = new Tile();
tiles.Add(transTile.Hash, transTile);
int nextTileIndex = 1;
for (int ys = 0; ys < slicesY; ys++)
{
for (int xs = 0; xs < slicesX; xs++)
{
int ymin = ys * size;
int ymax = ymin + size;
int xmin = xs * size;
int xmax = xmin + size;
Tile tile = new();
bool nonTransparent = false;
for (int y = ymin; y < ymax; y++)
{
for (int x = xmin; x < xmax; x++)
{
Color c = img.GetPixel(x, y);
ushort a = (ushort)(c.A == 255 ? 1 : 0);
if (a == 1) { nonTransparent = true; }
ushort color = (ushort)((c.R / 8) |
((c.G / 8) << 5) |
((c.B / 8) << 10) |
a << 15);
tile.Color[x - xmin][y - ymin] = color;
if (colours.ContainsKey(color))
{
colours[color]++;
}
else
{
colours[color] = 1;
}
}
}
if (nonTransparent)
{
string hash = tile.Hash;
if (!tiles.ContainsKey(hash))
{
tile.Index = nextTileIndex;
nextTileIndex++;
tiles[hash] = tile;
tileIndexes[xs, ys] = tile.Index;
}
else
{
droppedDuplicate++;
tileIndexes[xs, ys] = tiles[hash].Index;
Console.WriteLine("Hash match found");
}
}
else
{
droppedTransparent++;
}
}
}
FileStream stream = File.OpenWrite(TilemapRomPath);
BinaryWriter streamWriter = new(stream, Encoding.Default);
foreach (Tile tile in tiles.Values)
{
for (int y = 0; y < size; y++)
{
for (int x = 0; x < size; x++)
{
byte high = (byte)(tile.Color[x][y] >> 8);
byte low = (byte)tile.Color[x][y];
streamWriter.Write(high);
streamWriter.Write(low);
pos += 1;
}
}
}
streamWriter.Dispose();
Console.WriteLine($"Tilemap created. Next tile index={nextTileIndex} Total tiles={tiles.Count} Dropped Transparent={droppedTransparent } Dropped Duplicate={droppedDuplicate} Unique colours={colours.Keys.Count}");
StringBuilder builder = new();
builder.AppendLine("#ifndef TILEMAP_INDEXES_H");
builder.AppendLine("#define TILEMAP_INDEXES_H");
builder.AppendLine($"#define const_tilemap_index_x_max {tileIndexes.GetUpperBound(0) + 1}");
builder.AppendLine($"#define const_tilemap_index_y_max {tileIndexes.GetUpperBound(1) + 1}");
builder.AppendLine("extern unsigned long tilemap_index[const_tilemap_index_y_max][const_tilemap_index_x_max];");
builder.AppendLine("#endif");
File.WriteAllText(TilemapSourcePath + ".h", builder.ToString());
builder = new StringBuilder();
builder.AppendLine("#ifndef TILEMAP_INDEXES_C");
builder.AppendLine("#define TILEMAP_INDEXES_C");
builder.AppendLine("#include \"tilemap_indexes.h\"");
builder.AppendLine("unsigned long tilemap_index[const_tilemap_index_y_max][const_tilemap_index_x_max] = {");
for (int y = 0; y < slicesY; y++)
{
builder.Append("{");
for (int x = 0; x < slicesX; x++)
{
builder.Append($"{tileIndexes[x, y]}");
if (x < slicesX - 1) { builder.Append(","); }
}
builder.Append("}");
if (y < slicesY - 1) { builder.AppendLine(","); }
}
builder.AppendLine("};");
builder.AppendLine("#endif");
File.WriteAllText(TilemapSourcePath + ".c", builder.ToString());
}
class Tile
{
public ushort[][] Color;
public int Index;
public string Hash
{
get
{
StringBuilder sb = new StringBuilder();
for (int y = 0; y < 16; y++)
{
for (int x = 0; x < 16; x++)
{
sb.Append(Color[x][y].ToString() + ",");
}
}
return sb.ToString();
}
}
public Tile()
{
Color = new ushort[16][];
for (int y = 0; y < 16; y++)
{
Color[y] = new ushort[16];
}
}
}
static string resourcePath;
static string sourcePath;
static string resourceOutputPath;
static void Main(string[] args)
{
string rootPath = @"..\..\..\..\";
string currentProject = File.ReadAllText(rootPath + "CURRENT_PROJECT");
resourceOutputPath = $@"{rootPath}resources\";
resourcePath = $@"{resourceOutputPath}{currentProject}\";
resourceOutputPath = resourcePath;
sourcePath = $@"{rootPath}src\{currentProject}\";
CreateSpriteRom();
if (File.Exists(TilemapPath))
{
CreateTilemapRom();
}
if (File.Exists(TilemapExtractPath))
{
ExtractTilemapRom();
}
CreateMusicRom();
CreateSoundRom();
}
}
}

12
rommaker/rommaker.csproj Normal file
View File

@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
</ItemGroup>
</Project>

25
rommaker/rommaker.sln Normal file
View File

@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31912.275
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "rommaker", "rommaker.csproj", "{E7DBEC05-3B5C-45C3-8A15-F8F3AAFDDA6B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E7DBEC05-3B5C-45C3-8A15-F8F3AAFDDA6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E7DBEC05-3B5C-45C3-8A15-F8F3AAFDDA6B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E7DBEC05-3B5C-45C3-8A15-F8F3AAFDDA6B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E7DBEC05-3B5C-45C3-8A15-F8F3AAFDDA6B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {38A1A086-AD99-4DDC-B0DA-6A715E7F48CA}
EndGlobalSection
EndGlobal

View File

@@ -7,7 +7,7 @@
bd ff fe eb d5 80 80 07 83 e0 83 e0 83 e0 83 e0
83 e0 83 e0 83 e0 83 e0 83 e0 83 e0 83 e0 83 e0
00 00 83 ff 83 7f 82 df 81 b6 80 8d 80 00 ff ff
d7 3f 90 9b 83 e0 83 e0 83 e0 83 e0 83 e0 83 e0
d7 3f 90 9b fd 80 83 e0 83 e0 83 e0 83 e0 83 e0
83 e0 83 e0 83 e0 83 e0 83 e0 83 e0 83 e0 83 e0
83 e0 83 e0 83 e0 83 e0 83 e0 83 e0 83 e0 83 e0
00 00 83 e0 83 e0 83 e0 83 e0 83 e0 83 e0 83 e0

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
00 06 10 06 35 06 00 00 00 00 00 00 00 00 00 00
00 06 10 06 38 06 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
@@ -846,6 +846,54 @@
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 06 06 06 06
00 00 00 00 00 00 00 00 00 00 00 00 06 07 07 06
00 00 00 00 00 00 00 00 00 00 00 00 06 07 07 06
00 00 00 00 00 00 00 00 00 00 00 00 06 07 07 06
00 00 00 00 00 00 00 00 00 00 00 00 06 07 07 06
00 00 00 00 00 00 00 00 00 00 00 00 06 07 07 06
00 00 00 00 00 00 06 06 06 06 06 06 06 07 07 06
06 06 06 06 06 06 06 07 07 07 07 07 07 07 07 07
07 07 07 07 07 06 06 07 07 07 07 07 07 07 07 07
07 07 07 07 07 06 06 06 06 06 06 06 06 07 07 06
06 06 06 06 06 06 00 00 00 00 00 00 06 07 07 06
00 00 00 00 00 00 00 00 00 00 00 00 06 07 07 06
00 00 00 00 00 00 00 00 00 00 00 00 06 07 07 06
00 00 00 00 00 00 00 00 00 00 00 00 06 07 07 06
00 00 00 00 00 00 00 00 00 00 00 00 06 07 07 06
00 00 00 00 00 00 00 00 00 00 00 00 06 06 06 06
00 00 00 00 00 00 00 00 00 00 00 00 07 07 07 07
00 00 00 00 00 00 00 00 00 00 00 00 07 06 06 07
00 00 00 00 00 00 00 00 00 00 00 00 07 06 06 07
00 00 00 00 00 00 00 00 00 00 00 00 07 06 06 07
00 00 00 00 00 00 00 00 00 00 00 00 07 06 06 07
00 00 00 00 00 00 00 00 00 00 00 00 07 06 06 07
00 00 00 00 00 00 07 07 07 07 07 07 07 06 06 07
07 07 07 07 07 07 07 06 06 06 06 06 06 06 06 06
06 06 06 06 06 07 07 06 06 06 06 06 06 06 06 06
06 06 06 06 06 07 07 07 07 07 07 07 07 06 06 07
07 07 07 07 07 07 00 00 00 00 00 00 07 06 06 07
00 00 00 00 00 00 00 00 00 00 00 00 07 06 06 07
00 00 00 00 00 00 00 00 00 00 00 00 07 06 06 07
00 00 00 00 00 00 00 00 00 00 00 00 07 06 06 07
00 00 00 00 00 00 00 00 00 00 00 00 07 06 06 07
00 00 00 00 00 00 00 00 00 00 00 00 07 07 07 07
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 0a 0a 00
00 00 00 00 00 00 00 00 00 00 00 00 00 0a 0a 00
00 00 00 00 00 00 00 00 00 00 00 00 00 0a 0a 00
00 00 00 00 00 00 00 00 00 00 00 00 00 0a 0a 00
00 00 00 00 00 00 00 00 00 00 00 00 00 0a 0a 00
00 00 00 00 00 00 00 00 00 00 00 00 00 0a 0a 00
00 00 00 00 00 00 00 0a 0a 0a 0a 0a 0a 0a 0a 0a
0a 0a 0a 0a 0a 00 00 0a 0a 0a 0a 0a 0a 0a 0a 0a
0a 0a 0a 0a 0a 00 00 00 00 00 00 00 00 0a 0a 00
00 00 00 00 00 00 00 00 00 00 00 00 00 0a 0a 00
00 00 00 00 00 00 00 00 00 00 00 00 00 0a 0a 00
00 00 00 00 00 00 00 00 00 00 00 00 00 0a 0a 00
00 00 00 00 00 00 00 00 00 00 00 00 00 0a 0a 00
00 00 00 00 00 00 00 00 00 00 00 00 00 0a 0a 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 07 04 00 00 00 00 00 00 04 03 00 00 00 00 00

View File

@@ -146,6 +146,15 @@ char btntest_results_offset = 0;
char btntest_highlight = 0;
char btntest_aftertimer = 0;
unsigned char gunsight_back_colour = 0;
#define gunsight_cursor_colour_max 3
unsigned char gunsight_cursor_colour = 0;
#define gunsight_back_colour_max 2
unsigned char gunsight_back_colours[gunsight_back_colour_max] = {0, 255};
unsigned char gunsight_text_colour = 0b11101000;
unsigned short gunsight_pos_x;
unsigned short gunsight_pos_y;
// Draw static elements for digital input test page
void page_inputtester_digital()
{
@@ -307,6 +316,24 @@ void start_btntest()
write_string("Remember to enable fast USB polling!", 0xEE, 2, 25);
}
// Initialise Gunsight test state and draw static elements
void start_gunsight()
{
state = STATE_GUNSIGHT;
enable_sprite(MOUSE_POINTER_SPRITE, sprite_palette_pointer, sprite_size_pointer, 0);
spr_index[MOUSE_POINTER_SPRITE] = sprite_index_pointer_first + 1 + gunsight_cursor_colour;
spr_on[MOUSE_POINTER_SPRITE] = 1;
SET_BIT(video_ctl, 0); // Enable sprite priority over charmap
clear_chars(0);
clear_bgcolor(gunsight_back_colours[gunsight_back_colour]);
write_string("GUNSIGHT", gunsight_text_colour, 15, 0);
write_string("A) Cycle Background", gunsight_text_colour, 21, 28);
write_string("B) Cycle Crosshair", gunsight_text_colour, 21, 29);
}
// Rotate DPAD direction history and push new entry
void pushhistory(char new)
{
@@ -369,6 +396,8 @@ bool modeswitcher()
{
system_menu = 0;
modeswitchtimer_select = 0;
clear_sprites();
update_sprites();
start_menu();
return 1;
}
@@ -1034,3 +1063,72 @@ void btntest()
break;
}
}
// Gunsight test state
void gunsight()
{
// Handle PS/2 inputs whenever possible to improve latency
handle_ps2();
if (HBLANK_RISING)
{
basic_input();
handle_codes();
if (input_a && !input_a_last)
{
gunsight_back_colour++;
if (gunsight_back_colour == gunsight_back_colour_max)
{
gunsight_back_colour = 0;
}
start_gunsight();
}
if (input_b && !input_b_last)
{
gunsight_cursor_colour++;
if (gunsight_cursor_colour == gunsight_cursor_colour_max)
{
gunsight_cursor_colour = 0;
}
start_gunsight();
}
}
// As soon as vsync is detected start drawing screen updates
if (VBLANK_RISING)
{
// Handle test mode switch
if (modeswitcher())
{
return;
}
signed char ax_l = analog_l[0];
signed char ay_l = analog_l[1];
if (ax_l != ax_l_last[0])
{
write_stringfs("X: %4d", gunsight_text_colour, 0, 29, ax_l);
gunsight_pos_x = 184 + ax_l;
gunsight_pos_x += (ax_l / 4);
}
ax_l_last[0] = ax_l;
if (ay_l != ay_l_last[0])
{
write_stringfs("Y: %4d", gunsight_text_colour, 8, 29, ay_l);
signed short tempy = (ay_l * 120);
tempy /= 128;
// if (ay_l < 0)
// {
// tempy = -tempy;
// }
gunsight_pos_y = 144 + tempy;
}
ay_l_last[0] = ay_l;
set_sprite_position(MOUSE_POINTER_SPRITE, gunsight_pos_x, gunsight_pos_y);
update_sprites();
}
}

View File

@@ -62,6 +62,9 @@ extern void start_inputtester_advanced();
// Initialise button test state and draw static elements
extern void start_btntest();
// Initialise Gunsight test state and draw static elements
extern void start_gunsight();
// Digital input tester state
extern void inputtester_digital();
@@ -74,5 +77,8 @@ extern void inputtester_advanced();
// Button test - mode handler
extern void btntest();
// Gunsight test - mode handler
extern void gunsight();
#endif

View File

@@ -35,8 +35,11 @@
#define STATE_START_BTNTEST 7
#define STATE_BTNTEST 8
#define STATE_START_MENU 9
#define STATE_MENU 10
#define STATE_START_GUNSIGHT 9
#define STATE_GUNSIGHT 10
#define STATE_START_MENU 11
#define STATE_MENU 12
#define STATE_FADEOUT 20
#define STATE_START_FADEIN 21

View File

@@ -33,6 +33,7 @@ char *menu_string[] = {
"Digital",
"Analog",
"Advanced",
"Gunsight",
"Button test",
"Credits"};
@@ -88,9 +89,12 @@ void menu()
state = STATE_START_INPUTTESTERADVANCED;
break;
case 3:
state = STATE_START_BTNTEST;
state = STATE_START_GUNSIGHT;
break;
case 4:
state = STATE_START_BTNTEST;
break;
case 5:
state = STATE_START_CREDITS;
break;
}
@@ -100,7 +104,8 @@ void menu()
// As soon as vsync is detected start drawing screen updates
if (VBLANK_RISING)
{
char maxsize = (menu_count * 3) + 1 + (menu_count % 2);
//char maxsize = (menu_count * 3) + 1 + (menu_count % 2);
char maxsize = (menu_count * 3) + 2;
if (menu_timer < maxsize)
{
@@ -124,19 +129,18 @@ void menu()
if (menu_dirty)
{
char ty = menu_my - ((menu_count * 3) / 2);
if((menu_count % 2)==0) ty++;
for (char m = 0; m < menu_count; m++)
{
if (menu_index == m)
{
panel_shaded(menu_tx + 1, ty, menu_bx - 1, ty + 2, menu_sel_outline_high, menu_sel_outline_mid, menu_sel_outline_low);
write_string(menu_string[m], menu_sel_text, menu_tx + 2, ty + 1);
// fill_bgcol(menu_tx + 1, ty, menu_bx - 1, ty + 2, menu_sel_back);
}
else
{
panel_shaded(menu_tx + 1, ty, menu_bx - 1, ty + 2, menu_outline_high, menu_outline_mid, menu_outline_low);
write_string(menu_string[m], menu_text, menu_tx + 2, ty + 1);
// fill_bgcol(menu_tx + 1, ty, menu_bx - 1, ty + 2, menu_back);
}
ty += 3;
}

View File

@@ -27,7 +27,7 @@
#define menu_bx 28
#define menu_my 15
#define menu_openholdtime 60
#define menu_count 5
#define menu_count 6
#define menu_panel_outline_high 0xFF
#define menu_panel_outline_mid 0b00110110

View File

@@ -42,7 +42,7 @@ void app_main()
{
hsync = input0 & 0x80;
vsync = input0 & 0x40;
hblank = input0 & 0x20;
hblank = CHECK_BIT(input0, INPUT_HBLANK);
vblank = CHECK_BIT(input0, INPUT_VBLANK);
switch (state)
{
@@ -74,6 +74,13 @@ void app_main()
btntest();
break;
case STATE_START_GUNSIGHT:
start_gunsight();
break;
case STATE_GUNSIGHT:
gunsight();
break;
case STATE_START_MENU:
start_menu();
break;

View File

@@ -20,10 +20,12 @@
.globl _app_zorblaxx
.globl _menu
.globl _start_menu
.globl _gunsight
.globl _btntest
.globl _inputtester_advanced
.globl _inputtester_analog
.globl _inputtester_digital
.globl _start_gunsight
.globl _start_btntest
.globl _start_inputtester_advanced
.globl _start_inputtester_analog
@@ -123,15 +125,15 @@ _app_main::
ld l, #0x00
ld d, l
ld b, #0x08
00221$:
00233$:
add hl, hl
jr NC,00222$
jr NC,00234$
add hl, de
00222$:
djnz 00221$
00234$:
djnz 00233$
ld (_chram_size), hl
;os.c:41: while (1)
00122$:
00124$:
;os.c:43: hsync = input0 & 0x80;
ld iy, #_input0
ld a, 0 (iy)
@@ -141,15 +143,6 @@ _app_main::
;os.c:44: vsync = input0 & 0x40;
ld a, 0 (iy)
and a, #0x40
ld c,a
or a,#0x00
add a, #0xff
ld a, #0x00
rla
ld (_vsync+0), a
;os.c:45: hblank = input0 & 0x20;
ld a, 0 (iy)
and a, #0x20
ld c, a
ld b, #0x00
ld a, b
@@ -157,6 +150,14 @@ _app_main::
add a, #0xff
ld a, #0x00
rla
ld (_vsync+0), a
;os.c:45: hblank = CHECK_BIT(input0, INPUT_HBLANK);
ld a, 0 (iy)
and a, #0x20
ld c, a
xor a, a
cp a, c
rla
ld (_hblank+0), a
;os.c:46: vblank = CHECK_BIT(input0, INPUT_VBLANK);
ld a, 0 (iy)
@@ -188,194 +189,212 @@ _app_main::
jp Z,00106$
ld a, 0 (iy)
sub a, #0x07
jr Z,00107$
jp Z,00107$
ld a, 0 (iy)
sub a, #0x08
jr Z,00108$
jp Z,00108$
ld a, 0 (iy)
sub a, #0x09
jr Z,00109$
jp Z,00109$
ld a, 0 (iy)
sub a, #0x0a
jr Z,00110$
jp Z,00110$
ld a, 0 (iy)
sub a, #0x0b
jp Z,00111$
ld a, 0 (iy)
sub a, #0x0c
jp Z,00112$
ld a, 0 (iy)
sub a, #0x14
jr Z,00111$
ld a, 0 (iy)
sub a, #0x16
jr Z,00112$
ld a, 0 (iy)
sub a, #0x1e
jr Z,00113$
ld a, 0 (iy)
sub a, #0x1f
sub a, #0x16
jr Z,00114$
ld a, 0 (iy)
sub a, #0x28
jr Z,00116$
ld a, 0 (iy)
sub a, #0x29
jr Z,00117$
ld a, 0 (iy)
sub a, #0x2a
sub a, #0x1e
jr Z,00115$
ld a, 0 (iy)
sub a, #0x1f
jp Z,00116$
ld a, 0 (iy)
sub a, #0x28
jp Z,00118$
ld a, 0 (iy)
sub a, #0x29
jp Z,00119$
ld a, 0 (iy)
sub a, #0x2a
jr Z,00117$
ld a, 0 (iy)
sub a, #0x2b
jr Z,00118$
jp 00119$
jr Z,00120$
jp 00121$
;os.c:49: case STATE_START_INPUTTESTER:
00101$:
;os.c:50: start_inputtester_digital();
call _start_inputtester_digital
;os.c:51: break;
jp 00120$
jp 00122$
;os.c:52: case STATE_INPUTTESTER:
00102$:
;os.c:53: inputtester_digital();
call _inputtester_digital
;os.c:54: break;
jp 00120$
jp 00122$
;os.c:56: case STATE_START_INPUTTESTERADVANCED:
00103$:
;os.c:57: start_inputtester_advanced();
call _start_inputtester_advanced
;os.c:58: break;
jp 00120$
jp 00122$
;os.c:59: case STATE_INPUTTESTERADVANCED:
00104$:
;os.c:60: inputtester_advanced();
call _inputtester_advanced
;os.c:61: break;
jr 00120$
jp 00122$
;os.c:63: case STATE_START_INPUTTESTERANALOG:
00105$:
;os.c:64: start_inputtester_analog();
call _start_inputtester_analog
;os.c:65: break;
jr 00120$
jp 00122$
;os.c:66: case STATE_INPUTTESTERANALOG:
00106$:
;os.c:67: inputtester_analog();
call _inputtester_analog
;os.c:68: break;
jr 00120$
jr 00122$
;os.c:70: case STATE_START_BTNTEST:
00107$:
;os.c:71: start_btntest();
call _start_btntest
;os.c:72: break;
jr 00120$
jr 00122$
;os.c:73: case STATE_BTNTEST:
00108$:
;os.c:74: btntest();
call _btntest
;os.c:75: break;
jr 00120$
;os.c:77: case STATE_START_MENU:
jr 00122$
;os.c:77: case STATE_START_GUNSIGHT:
00109$:
;os.c:78: start_menu();
call _start_menu
;os.c:78: start_gunsight();
call _start_gunsight
;os.c:79: break;
jr 00120$
;os.c:80: case STATE_MENU:
jr 00122$
;os.c:80: case STATE_GUNSIGHT:
00110$:
;os.c:81: menu();
call _menu
;os.c:81: gunsight();
call _gunsight
;os.c:82: break;
jr 00120$
;os.c:84: case STATE_FADEOUT:
jr 00122$
;os.c:84: case STATE_START_MENU:
00111$:
;os.c:85: fadeout();
call _fadeout
;os.c:85: start_menu();
call _start_menu
;os.c:86: break;
jr 00120$
;os.c:87: case STATE_FADEIN:
jr 00122$
;os.c:87: case STATE_MENU:
00112$:
;os.c:88: fadein();
call _fadein
;os.c:88: menu();
call _menu
;os.c:89: break;
jr 00120$
;os.c:91: case STATE_START_ATTRACT:
jr 00122$
;os.c:91: case STATE_FADEOUT:
00113$:
;os.c:92: state = 0;
;os.c:92: fadeout();
call _fadeout
;os.c:93: break;
jr 00122$
;os.c:94: case STATE_FADEIN:
00114$:
;os.c:95: fadein();
call _fadein
;os.c:96: break;
jr 00122$
;os.c:98: case STATE_START_ATTRACT:
00115$:
;os.c:99: state = 0;
ld hl,#_state + 0
ld (hl), #0x00
;os.c:93: loader("SNEK.AZN");
;os.c:100: loader("SNEK.AZN");
ld hl, #___str_0
push hl
call _loader
pop af
;os.c:94: start_snek_attract();
;os.c:101: start_snek_attract();
call _start_snek_attract
;os.c:95: break;
jr 00120$
;os.c:96: case STATE_ATTRACT:
00114$:
;os.c:97: snek_attract();
call _snek_attract
;os.c:98: break;
jr 00120$
;os.c:99: case STATE_START_CREDITS:
00115$:
;os.c:100: app_credits();
call _app_credits
;os.c:101: break;
jr 00120$
;os.c:103: case STATE_START_GAME_SNEK:
;os.c:102: break;
jr 00122$
;os.c:103: case STATE_ATTRACT:
00116$:
;os.c:104: start_snek_gameplay();
call _start_snek_gameplay
;os.c:104: snek_attract();
call _snek_attract
;os.c:105: break;
jr 00120$
;os.c:106: case STATE_GAME_SNEK:
jr 00122$
;os.c:106: case STATE_START_CREDITS:
00117$:
;os.c:107: snek_gameplay();
call _snek_gameplay
;os.c:107: app_credits();
call _app_credits
;os.c:108: break;
jr 00120$
;os.c:109: case STATE_START_ZORBLAXX:
jr 00122$
;os.c:110: case STATE_START_GAME_SNEK:
00118$:
;os.c:110: state = 0;
;os.c:111: start_snek_gameplay();
call _start_snek_gameplay
;os.c:112: break;
jr 00122$
;os.c:113: case STATE_GAME_SNEK:
00119$:
;os.c:114: snek_gameplay();
call _snek_gameplay
;os.c:115: break;
jr 00122$
;os.c:116: case STATE_START_ZORBLAXX:
00120$:
;os.c:117: state = 0;
ld hl,#_state + 0
ld (hl), #0x00
;os.c:111: loader("ZORBLAXX.AZN");
;os.c:118: loader("ZORBLAXX.AZN");
ld hl, #___str_1
push hl
call _loader
pop af
;os.c:112: app_zorblaxx();
;os.c:119: app_zorblaxx();
call _app_zorblaxx
;os.c:113: break;
jr 00120$
;os.c:115: default:
00119$:
;os.c:120: loader("INPUTTESTER.AZN");
;os.c:120: break;
jr 00122$
;os.c:122: default:
00121$:
;os.c:127: loader("INPUTTESTER.AZN");
ld hl, #___str_2
push hl
call _loader
pop af
;os.c:121: start_inputtester_digital();
;os.c:128: start_inputtester_digital();
call _start_inputtester_digital
;os.c:126: }
00120$:
;os.c:128: hsync_last = hsync;
;os.c:133: }
00122$:
;os.c:135: hsync_last = hsync;
ld a,(#_hsync + 0)
ld iy, #_hsync_last
ld 0 (iy), a
;os.c:129: vsync_last = vsync;
;os.c:136: vsync_last = vsync;
ld a,(#_vsync + 0)
ld iy, #_vsync_last
ld 0 (iy), a
;os.c:130: hblank_last = hblank;
;os.c:137: hblank_last = hblank;
ld a,(#_hblank + 0)
ld iy, #_hblank_last
ld 0 (iy), a
;os.c:131: vblank_last = vblank;
;os.c:138: vblank_last = vblank;
ld a,(#_vblank + 0)
ld iy, #_vblank_last
ld 0 (iy), a
;os.c:133: }
jp 00122$
;os.c:140: }
jp 00124$
___str_0:
.ascii "SNEK.AZN"
.db 0x00
@@ -385,13 +404,13 @@ ___str_1:
___str_2:
.ascii "INPUTTESTER.AZN"
.db 0x00
;os.c:136: void main()
;os.c:143: void main()
; ---------------------------------
; Function main
; ---------------------------------
_main::
;os.c:138: app_main();
;os.c:139: }
;os.c:145: app_main();
;os.c:146: }
jp _app_main
.area _CODE
.area _INITIALIZER

View File

@@ -43,8 +43,8 @@
#define sprite_pixelsize_player 16
#define sprite_halfpixelsize_player 8
#define sprite_index_pointer_first 36
#define sprite_index_pointer_count 1
#define sprite_index_pointer_last 36
#define sprite_index_pointer_count 4
#define sprite_index_pointer_last 39
#define sprite_palette_pointer 2
#define sprite_size_pointer 1
#define sprite_pixelsize_pointer 16

View File

@@ -238,8 +238,8 @@ void handle_ps2()
if (mse_clock != mse_lastclock)
{
mse_changed = 1;
mse_button1 = ps2_mouse[0];
mse_button2 = ps2_mouse[5];
mse_button1 = CHECK_BIT(ps2_mouse[0],0);
mse_button2 = CHECK_BIT(ps2_mouse[0],1);
mse_x = ps2_mouse[1];
mse_y = ps2_mouse[2];
mse_w = ps2_mouse[4];

View File

@@ -157,8 +157,8 @@ int verilate() {
unsigned char mouse_clock = 0;
unsigned char mouse_clock_reduce = 0;
unsigned char mouse_buttons = 0;
unsigned char mouse_x = 0;
unsigned char mouse_y = 0;
signed int mouse_x = 0;
signed int mouse_y = 0;
char spinner_toggle = 0;
@@ -413,21 +413,79 @@ int main(int argc, char** argv, char** env) {
// if (spinner_toggle) { top->spinner_0 |= 1UL << 8; }
//}
mouse_buttons = 0;
mouse_x = 0;
mouse_y = 0;
if (input.inputs[input_left]) { mouse_x = -2; }
if (input.inputs[input_right]) { mouse_x = 2; }
if (input.inputs[input_up]) { mouse_y = 2; }
if (input.inputs[input_down]) { mouse_y = -2; }
if (input.inputs[input_a]) { mouse_buttons |= (1UL << 0); }
if (input.inputs[input_b]) { mouse_buttons |= (1UL << 1); }
int acc = 8;
int dec = 1;
int fric = 2;
if (input.inputs[input_left]) { mouse_x -= acc; }
else if (mouse_x < 0) { mouse_x += (dec + (-mouse_x / fric)); }
if (input.inputs[input_right]) { mouse_x += acc; }
else if (mouse_x > 0) { mouse_x -= (dec + (mouse_x / fric)); }
if (input.inputs[input_up]) { mouse_y += acc; }
else if (mouse_y > 0) { mouse_y -= (dec + (mouse_y / fric)); }
if (input.inputs[input_down]) { mouse_y -= acc; }
else if (mouse_y < 0) { mouse_y += (dec + (-mouse_y / fric)); }
int lim = 127;
if (mouse_x > lim) { mouse_x = lim; }
if (mouse_x < -lim) { mouse_x = -lim; }
if (mouse_y > lim) { mouse_y = lim; }
if (mouse_y < -lim) { mouse_y = -lim; }
top->joystick_l_analog_0 = mouse_x;
top->joystick_l_analog_0 |= mouse_y << 8;
unsigned char ps2_mouse1;
unsigned char ps2_mouse2;
int x = mouse_x;
mouse_buttons |= (x < 0) ? 0x10 : 0x00;
if (x < -255)
{
// min possible value + overflow flag
mouse_buttons |= 0x40;
ps2_mouse1 = 1; // -255
}
else if (x > 255)
{
// max possible value + overflow flag
mouse_buttons |= 0x40;
ps2_mouse1 = 255;
}
else
{
ps2_mouse1 = (char)x;
}
// ------ Y axis -----------
// store sign bit in first byte
int y = mouse_y;
mouse_buttons |= (y < 0) ? 0x20 : 0x00;
if (y < -255)
{
// min possible value + overflow flag
mouse_buttons |= 0x80;
ps2_mouse2 = 1; // -255;
}
else if (y > 255)
{
// max possible value + overflow flag
mouse_buttons |= 0x80;
ps2_mouse2 = 255;
}
else
{
ps2_mouse2 = (char)y;
}
unsigned long mouse_temp = mouse_buttons;
mouse_temp += (mouse_x << 8);
mouse_temp += (mouse_y << 16);
mouse_temp += (((unsigned char)ps2_mouse1) << 8);
mouse_temp += (((unsigned char)ps2_mouse2) << 16);
if (mouse_clock) { mouse_temp |= (1UL << 24); }
mouse_clock = !mouse_clock;
top->ps2_mouse = mouse_temp;