Version 2

This commit is contained in:
jimmystones
2021-10-16 23:57:15 +01:00
parent 41f60b92d0
commit a3e0b608a3
19 changed files with 1848 additions and 912 deletions

1
.gitignore vendored
View File

@@ -53,3 +53,4 @@ src/os.sym
.vscode/settings.json
verilator/imgui.ini
verilator/imgui.ini
docs/*

View File

@@ -1,9 +1,9 @@
/*============================================================================
MiSTer test harness - emu module
Input Test - emu module
Author: Jim Gregory - https://github.com/JimmyStones/
Version: 1.0
Date: 2021-07-03
Date: 2021-07-12
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
@@ -209,7 +209,6 @@ localparam CONF_STR = {
"O89,Aspect ratio,Original,Full Screen,[ARC1],[ARC2];",
"-;",
"F0,BIN,Load BIOS;",
"F1,PF,Load Font;",
"-;",
"J1,A,B,X,Y,L,R,Select,Start,C,Z;",
"V,v",`BUILD_DATE
@@ -254,6 +253,7 @@ wire [8:0] spinner_5;
wire [10:0] ps2_key;
wire [24:0] ps2_mouse;
wire [15:0] ps2_mouse_ext;
wire [32:0] timestamp;
hps_io #(.CONF_STR(CONF_STR)) hps_io
(
@@ -301,7 +301,9 @@ hps_io #(.CONF_STR(CONF_STR)) hps_io
.ps2_key(ps2_key),
.ps2_mouse(ps2_mouse),
.ps2_mouse_ext(ps2_mouse_ext)
.ps2_mouse_ext(ps2_mouse_ext),
.TIMESTAMP(timestamp)
);
@@ -363,7 +365,8 @@ system system(
.paddle({paddle_5,paddle_4,paddle_3,paddle_2,paddle_1,paddle_0}),
.spinner({7'b0,spinner_5,7'b0,spinner_4,7'b0,spinner_3,7'b0,spinner_2,7'b0,spinner_1,7'b0,spinner_0}),
.ps2_key(ps2_key),
.ps2_mouse({ps2_mouse_ext,7'b0,ps2_mouse})
.ps2_mouse({ps2_mouse_ext,7'b0,ps2_mouse}),
.timestamp(timestamp)
);
endmodule

BIN
PETSCII.pf Normal file

Binary file not shown.

View File

@@ -2,12 +2,13 @@
## Overview
A custom mini-system running an input test utility. 3 test screens available:
A custom mini-system running an input test utility. There are 4 test screens available:
- Digital - shows joypad buttons for first two input devices
- Analog - shows analog axes for first two input devices
- Advanced - shows all standard HPS sourced inputs (joystick, analog, paddle, spinner) simultaneously as well as a ps2 keyboard console
Mode switching instructions are shown at the bottom of the screen.
- Button test - guided button test to help detect bounce / missed inputs and test man+machine lag
Hold (select) on the first joypad to enter the menu
## Acknowledgements
@@ -16,9 +17,13 @@ Created by JimmyStones (http://github.com/jimmystones), with thanks to:
- Jotego (https://github.com/jotego) for his very tidy JTFRAME modules
- Porkchop Express (https://misteraddons.com/) for testing a bunch of devices I couldn't
## History
- v1.0 - 2021-07-03 - First release with digital, analog and advanced tests
- v1.1 - 2021-07-18 - PETSCII UI update, add button test mode
## Hardware
A simple bespoke (i.e. I made it up with no real plan) 8-bit system with a 40x30 character display, each character can be set to one of 256 colours. No sound capabilities. All relevant MiSTer inputs are mapped into memory regions.
A simple bespoke (i.e. I made it up with no real plan) 8-bit system with a 40x30 character display, each character foreground and background colour can be set independently to one of 256 colours. No sound capabilities whatsoever. All relevant MiSTer inputs are mapped into memory regions for the CPU to access directly. There is also a dedicated millisecond timer (runs 0-32677, resettable by writing to any bit of the address).
### Components
- Z80 CPU (tv80 by Guy Hutchison, based on VHDL T80 core by Daniel Wallner)
@@ -29,14 +34,16 @@ A simple bespoke (i.e. I made it up with no real plan) 8-bit system with a 40x30
- 2Kb character ROM (port 1 cpu, port 2 download)
- 2Kb character RAM (port 1 cpu, port 2 graphics)
- 2Kb colour RAM (port 1 cpu, port 2 graphics)
- 7 Memory-mapped IO regions (all read-only). _Yes I know about MREQ but SFRs turned out to be annoying in sdcc so :)_
- Hardware state (H/V Sync, H/V Blank etc) (8 bytes)
- joystick_5->0 from HPS (192 bytes)
- joystick_analog_5->0 from HPS (96 bytes)
- paddle_5->0 from HPS (48 bytes)
- spinner_5->0 from HPS (96 bytes)
- ps2_key from HPS (11 bytes)
- 8 Memory-mapped IO regions. _Yes I know about MREQ but SFRs turned out to be annoying in sdcc so :)_
- Hardware state (H/V Sync, H/V Blank etc) (8 bytes, read-only)
- joystick_5->0 from HPS (192 bytes, read-only)
- joystick_analog_5->0 from HPS (96 bytes, read-only)
- paddle_5->0 from HPS (48 bytes, read-only)
- spinner_5->0 from HPS (96 bytes, read-only)
- ps2_key from HPS (11 bytes, read-only)
- ps2_mouse from HPS (48 bytes, combination of ps2_mouse and ps2_mouseext with some padding to align bytes) - currently unused
- timestamp from HPS (33 bytes, read-only)
- Hardware millisecond resolution timer (16 bytes read/write)
### Memory Map
Start|End|Length|Name
@@ -50,6 +57,8 @@ Start|End|Length|Name
0x7300|0x735F|0x0060|Spinner inputs
0x7400|0x740B|0x000C|PS2 keyboard input
0x7500|0x752F|0x0030|PS2 mouse input
0x7600|0x7020|0x0021|Timestamp
0x7700|0x7010|0x0011|Timer
0x8000|0x87FF|0x0800|Char RAM
0x8800|0x8FFF|0x0800|Colour RAM
0xC000|0xFFFF|0x4000|Work RAM

View File

@@ -12,6 +12,6 @@ cd ..
# Hexify roms
od -An -t x1 -v src/os.bin > verilator/rom.hex
od -An -t x1 -v src/os.bin > rtl/rom.hex
od -An -t x1 -v MiSTer.pf > verilator/font.hex
od -An -t x1 -v MiSTer.pf > rtl/font.hex
od -An -t x1 -v PETSCII.pf > verilator/font.hex
od -An -t x1 -v PETSCII.pf > rtl/font.hex

View File

@@ -1,6 +1,6 @@
`timescale 1ns / 1ps
/*============================================================================
MiSTer test harness - System module
Aznable (custom 8-bit computer system) - System module
Author: Jim Gregory - https://github.com/JimmyStones/
Version: 1.0
@@ -48,6 +48,9 @@ module system (
// [0-23] mouse data, [24] - toggles with every event, [25-31] - padding,
// [32-39] - wheel movements, [40-47] - reserved(additional buttons)
input [47:0] ps2_mouse,
// [31:0] - seconds since 1970-01-01 00:00:00, [32] - toggle with every change
input [32:0] timestamp,
output VGA_HS,
output VGA_VS,
@@ -87,20 +90,49 @@ jtframe_vtimer #(
.VS(VGA_VS)
);
// Millisecond timer
reg [15:0] timer;
reg [14:0] timer_divider = 15'd0;
always @(posedge clk_sys)
begin
if(timer_cs == 1'b1 && cpu_wr_n == 1'b0)
begin
timer <= 16'd0;
timer_divider <= 15'd0;
end
else
begin
if(timer_divider==15'd24000)
begin
timer <= timer + 16'd1;
timer_divider <= 15'd0;
end
else
begin
timer_divider <= timer_divider + 15'd1;
end
end
end
// Character map
wire [3:0] chpos_x = 4'd7 - hcnt[2:0];
wire [2:0] chpos_y = vcnt[2:0];
wire [5:0] chram_x = hcnt[8:3];
wire [5:0] chram_y = vcnt[8:3];
wire [11:0] chram_addr = {chram_y, chram_x};
wire [11:0] colram_addr = chram_addr;
wire [11:0] chrom_addr = {1'b0, chmap_data_out[7:0], chpos_y};
wire chpixel = chrom_data_out[chpos_x[2:0]];
// RGB output
assign VGA_R = chpixel ? {{2{colram_data_out[2:0]}},2'b0} : 8'b0;
assign VGA_G = chpixel ? {{2{colram_data_out[5:3]}},2'b0} : 8'b0;
assign VGA_B = chpixel ? {{3{colram_data_out[7:6]}},2'b0} : 8'b0;
// RGB mixer
wire [2:0] r_temp = chpixel ? fgcolram_data_out[2:0] : bgcolram_data_out[2:0];
wire [2:0] g_temp = chpixel ? fgcolram_data_out[5:3] : bgcolram_data_out[5:3];
wire [1:0] b_temp = chpixel ? fgcolram_data_out[7:6] : bgcolram_data_out[7:6];
// Convert RGb to 24bpp
assign VGA_R = {{2{r_temp}},2'b0};
assign VGA_G = {{2{g_temp}},2'b0};
assign VGA_B = {{3{b_temp}},2'b0};
// CPU control signals
wire [15:0] cpu_addr;
@@ -136,25 +168,29 @@ wire [7:0] pgrom_data_out;
wire [7:0] chrom_data_out;
wire [7:0] wkram_data_out;
wire [7:0] chram_data_out;
wire [7:0] colram_data_out;
wire [7:0] fgcolram_data_out;
wire [7:0] bgcolram_data_out;
// RAM data to GFX
wire [7:0] chmap_data_out;
// Hardware inputs
wire [7:0] in0_data_out = {VGA_HS, VGA_VS, 6'b101000};
wire [7:0] in0_data_out = {VGA_HS, VGA_VS,VGA_HB, VGA_VB, 4'b1000};
wire [7:0] joystick_data_out = joystick[cpu_addr[7:0] +: 8];
wire [7:0] analog_data_out = analog[cpu_addr[6:0] +: 8];
wire [7:0] paddle_data_out = paddle[cpu_addr[5:0] +: 8];
wire [7:0] spinner_data_out = spinner[cpu_addr[6:0] +: 8];
wire [7:0] ps2_key_data_out = ps2_key[cpu_addr[3:0] +: 8];
wire [7:0] ps2_mouse_data_out = ps2_mouse[cpu_addr[5:0] +: 8];
wire [7:0] timestamp_data_out = timestamp[cpu_addr[5:0] +: 8];
wire [7:0] timer_data_out = timer[cpu_addr[3:0] +: 8];
// CPU address decodes
wire pgrom_cs = cpu_addr[15:14] == 2'b00;
//wire chrom_cs = cpu_addr[15:12] == 4'b0100; // CPU never accesses the character ROM data directly
wire chram_cs = cpu_addr[15:11] == 5'b10000;
wire colram_cs = cpu_addr[15:11] == 5'b10001;
wire fgcolram_cs = cpu_addr[15:11] == 5'b10001;
wire bgcolram_cs = cpu_addr[15:11] == 5'b10010;
wire wkram_cs = cpu_addr[15:14] == 2'b11;
wire in0_cs = cpu_addr == 16'h6000;
wire joystick_cs = cpu_addr[15:8] == 8'b01110000;
@@ -163,25 +199,31 @@ wire paddle_cs = cpu_addr[15:8] == 8'b01110010;
wire spinner_cs = cpu_addr[15:8] == 8'b01110011;
wire ps2_key_cs = cpu_addr[15:8] == 8'b01110100;
wire ps2_mouse_cs = cpu_addr[15:8] == 8'b01110101;
wire timestamp_cs = cpu_addr[15:8] == 8'b01110110;
wire timer_cs = cpu_addr[15:8] == 8'b01110111;
// always @(posedge clk_sys) begin
// always @(posedge timestamp[32]) begin
// $display("%b", timestamp);
// end
//always @(posedge clk_sys) begin
// if(pgrom_cs) $display("%x pgrom o %x", cpu_addr, pgrom_data_out);
// if(wkram_cs) $display("%x wkram i %x o %x w %b", cpu_addr, cpu_dout, wkram_data_out, wkram_wr);
// if(chram_cs) $display("%x chram i %x o %x w %b", cpu_addr, cpu_dout, chram_data_out, chram_wr);
// if(colram_cs) $display("%x colram i %x o %x w %b", cpu_addr, cpu_dout, colram_data_out, colram_wr);
// if(fgcolram_cs) $display("%x fgcolram i %x o %x w %b", cpu_addr, cpu_dout, fgcolram_data_out, fgcolram_wr);
// if(in0_cs) $display("%x in0 i %x o %x", cpu_addr, cpu_dout, in0_data_out);
// if(joystick_cs) $display("joystick %b %b", joystick_bit, joystick_data_out);
// if(analog_cs) $display("analog %b %b", analog_bit, analog_data_out);
// if(paddle_cs) $display("paddle %b", paddle_data_out);
// if(ps2_key_cs) $display("ps2_key %b %x", ps2_key_data_out, cpu_addr[3:0]);
// $display("%x", cpu_addr);
// end
//end
// CPU data mux
assign cpu_din = pgrom_cs ? pgrom_data_out :
wkram_cs ? wkram_data_out :
chram_cs ? chram_data_out :
colram_cs ? colram_data_out :
fgcolram_cs ? fgcolram_data_out :
bgcolram_cs ? bgcolram_data_out :
in0_cs ? in0_data_out :
joystick_cs ? joystick_data_out :
analog_cs ? analog_data_out :
@@ -189,6 +231,8 @@ assign cpu_din = pgrom_cs ? pgrom_data_out :
spinner_cs ? spinner_data_out :
ps2_key_cs ? ps2_key_data_out :
ps2_mouse_cs ? ps2_mouse_data_out :
timestamp_cs ? timestamp_data_out :
timer_cs ? timer_data_out :
8'b00000000;
// Rom upload write enables
@@ -198,7 +242,8 @@ wire chrom_wr = dn_wr && dn_index == 8'b1;
// Ram write enables
wire wkram_wr = !cpu_wr_n && wkram_cs;
wire chram_wr = !cpu_wr_n && chram_cs;
wire colram_wr = !cpu_wr_n && colram_cs;
wire fgcolram_wr = !cpu_wr_n && fgcolram_cs;
wire bgcolram_wr = !cpu_wr_n && bgcolram_cs;
// MEMORY
@@ -253,20 +298,36 @@ dpram #(11,8) chram
.q_b(chmap_data_out)
);
// Char color RAM - 0x8800 - 0x8FFF (0x0800 / 2048 bytes)
dpram #(11,8) colram
// Char foreground color RAM - 0x8800 - 0x8FFF (0x0800 / 2048 bytes)
dpram #(11,8) fgcolram
(
.clock_a(clk_sys),
.address_a(cpu_addr[10:0]),
.wren_a(colram_wr),
.wren_a(fgcolram_wr),
.data_a(cpu_dout),
.q_a(),
.clock_b(clk_sys),
.address_b(colram_addr[10:0]),
.address_b(chram_addr[10:0]),
.wren_b(1'b0),
.data_b(),
.q_b(colram_data_out)
.q_b(fgcolram_data_out)
);
// Char background color RAM - 0x9000 - 0x97FF (0x0800 / 2048 bytes)
dpram #(11,8) bgcolram
(
.clock_a(clk_sys),
.address_a(cpu_addr[10:0]),
.wren_a(bgcolram_wr),
.data_a(cpu_dout),
.q_a(),
.clock_b(clk_sys),
.address_b(chram_addr[10:0]),
.wren_b(1'b0),
.data_b(),
.q_b(bgcolram_data_out)
);
// Work RAM - 0xC000 - 0xFFFF (0x4000 / 16384 bytes)

84
src/fader.c Normal file
View File

@@ -0,0 +1,84 @@
/*============================================================================
Input Test - Fader
Author: Jim Gregory - https://github.com/JimmyStones/
Version: 1.0
Date: 2021-07-12
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
===========================================================================*/
#pragma once
#include "sys.c"
// Fade in/out constants
#define fadefreq 4
// Fade in/out variables
unsigned char fade = 0;
unsigned char fadetimer = 0;
// Initialise fadeout state
void start_fadeout()
{
state = STATE_FADEOUT;
fadetimer = fadefreq;
fade = 0;
}
// Initialise fadein state
void start_fadein()
{
state = STATE_FADEIN;
fadetimer = fadefreq;
fade = 15;
}
// Fade out state
void fadeout()
{
if (VBLANK_RISING)
{
fadetimer--;
if (fadetimer == 0)
{
box(fade, fade, 39 - fade, 29 - fade, 127, 0b0000111);
fadetimer = fadefreq;
fade++;
if (fade == 16)
{
start_fadein();
}
}
}
}
// Fade in state
void fadein()
{
if (VBLANK_RISING)
{
fadetimer--;
if (fadetimer == 0)
{
box(fade, fade, 39 - fade, 29 - fade, 0, 0b0000000);
fadetimer = fadefreq;
fade--;
if (fade == 0)
{
state = nextstate;
}
}
}
}

837
src/inputtester.c Normal file
View File

@@ -0,0 +1,837 @@
/*============================================================================
Input Test - Input test state handlers
Author: Jim Gregory - https://github.com/JimmyStones/
Version: 1.0
Date: 2021-07-13
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
===========================================================================*/
#pragma once
#include "sys.c"
#include "fader.c"
#include "menu.c"
// Input tester variables
unsigned char joystick_last[12];
signed char ax_last[6];
signed char ay_last[6];
unsigned char px_last[6];
signed char sx_toggle_last[6];
signed char sx_last[6];
unsigned long sx_pos[6];
// Console constants
#define con_cursorfreq 30
// Console variables
unsigned char con_x; // Console cursor X position
unsigned char con_y; // Console cursor X position
unsigned char con_l = 2; // Console left edge X
unsigned char con_t = 22; // Console top edge Y
unsigned char con_r = 37; // Console right edge X
unsigned char con_b = 36; // Console bottom edge Y
bool con_cursor;
unsigned char con_cursortimer = 1;
// Mode switcher variables
char modeswitchtimer_select = 0;
char modeswitchtimer_start = 0;
#define HISTORY_LENGTH 6
char history[HISTORY_LENGTH];
#define PAD_COUNT 2
#define BUTTON_COUNT 12
char pad_offset_x[PAD_COUNT] = {7, 7};
char pad_offset_y[PAD_COUNT] = {5, 16};
char button_symbol[BUTTON_COUNT][6] = {
"R",
"L",
"D",
"U",
"A",
"B",
"X",
"Y",
"L",
"R",
"Sel",
"Start"};
char button_name[BUTTON_COUNT][12] = {
"DPAD Right",
"DPAD Left",
"DPAD Down",
"DPAD Up",
"A",
"B",
"X",
"Y",
"L",
"R",
"Select",
"Start"};
char button_x[BUTTON_COUNT] = {6, 2, 4, 4, 24, 22, 22, 20, 3, 23, 9, 13};
char button_y[BUTTON_COUNT] = {5, 5, 6, 4, 5, 6, 4, 5, 1, 1, 5, 5};
#define color_button_active 0xFF
#define color_button_inactive 0b10100100
char analog_offset_x[PAD_COUNT] = {1, 20};
char analog_offset_y[PAD_COUNT] = {5, 5};
signed char analog_x[PAD_COUNT];
signed char analog_y[PAD_COUNT];
#define analog_size 18
#define analog_ratio 15 // 256 / 17;
#define timestamp_clock_index 32
#define timestamp_index_1 8
#define timestamp_index_2 16
#define timestamp_index_3 24
bool last_timestamp_clock = 0;
// Button test constants
#define btntest_mode_select 0
#define btntest_mode_ready 1
#define btntest_mode_test 2
#define btntest_mode_results 3
#define btntest_timer_start 60 // vblanks to wait before first prompt
#define btntest_timer_interval 90 // vblanks to wait between prompts
#define btntest_timer_visible 30 // vblanks to wait between prompts
#define btntest_counter_max 5 // Number of button presses required
#define btntest_max 255
#define btntest_highlight_start 2
#define btntest_highlight_end 35
// Button test variables
char btntest_mode = 0;
char btntest_buttonbank = 0;
char btntest_buttonindex = 0;
char btntest_buttonlast = 1;
char btntest_timer = 0;
char btntest_counter = 0;
unsigned short btntest_presses[btntest_max];
unsigned short btntest_prompts[btntest_counter_max];
char btntest_pos = 0;
char btntest_buttondownlast = 0;
bool btntest_results_refresh = 0;
char btntest_results_offset = 0;
char btntest_highlight = 0;
char btntest_aftertimer = 0;
// Draw static elements for digital input test page
void page_inputtester_digital()
{
page_frame(true, false);
// Draw pads
for (char j = 0; j < PAD_COUNT; j++)
{
write_stringf("JOY %d", 0xFF, pad_offset_x[j] - 5, pad_offset_y[j] + 5, j + 1);
draw_pad(pad_offset_x[j], pad_offset_y[j]);
}
}
// Draw static elements for analog input test page
void page_inputtester_analog()
{
page_frame(true, false);
// Draw analog grids
for (char j = 0; j < PAD_COUNT; j++)
{
write_stringf("ANALOG %d", 0xFF, analog_offset_x[j] + 5, analog_offset_y[j] - 1, j + 1);
draw_analog(analog_offset_x[j], analog_offset_y[j], analog_size, analog_size);
write_char('X', 0xFF, analog_offset_x[j] + 5, analog_offset_y[j] + analog_size + 1);
write_char('Y', 0xFF, analog_offset_x[j] + 13, analog_offset_y[j] + analog_size + 1);
}
}
// Draw static elements for advanced input test page
void page_inputtester_advanced()
{
page_frame(true, false);
write_string("RLDUABXYLRsS", 0xFF, 7, 5);
write_string("AX", 0xFF, 22, 5);
write_string("AY", 0xFF, 27, 5);
write_string("POS", 0xFF, 7, 13);
write_string("SPD POS", 0xFF, 18, 13);
char label[5];
for (unsigned char j = 0; j < 6; j++)
{
sprintf(label, "JOY%d", j + 1);
write_string(label, 0xFF - (j * 2), 2, 6 + j);
sprintf(label, "PAD%d", j + 1);
write_string(label, 0xFF - (j * 2), 2, 14 + j);
sprintf(label, "SPN%d", j + 1);
write_string(label, 0xFF - (j * 2), 14, 14 + j);
}
write_string("CON", 0xFF, 2, 21);
}
// Draw static elements for button test page
void page_btntest(bool showMenuButton, bool showContinueButton)
{
page_frame(showMenuButton, showContinueButton);
write_string("BUTTON TEST", 0xAA, 15, 4);
}
void reset_inputstates()
{
modeswitchtimer_select = 0;
modeswitchtimer_start = 0;
for (char i = 0; i < 12; i++)
{
joystick_last[i] = 1;
}
for (char i = 0; i < 6; i++)
{
ax_last[i] = 1;
ay_last[i] = 1;
px_last[i] = 1;
sx_toggle_last[i] = 1;
sx_last[i] = 1;
sx_pos[i] = 0;
}
}
// Initialise digital inputtester state and draw static elements
void start_inputtester_digital()
{
state = STATE_INPUTTESTER;
// Draw page
page_inputtester_digital();
// Reset last states for inputs
reset_inputstates();
}
// Initialise analog inputtester state and draw static elements
void start_inputtester_analog()
{
state = STATE_INPUTTESTERANALOG;
// Draw page
page_inputtester_analog();
// Reset last states for inputs
reset_inputstates();
}
// Initialise advanced inputtester state and draw static elements
void start_inputtester_advanced()
{
state = STATE_INPUTTESTERADVANCED;
// Draw page
page_inputtester_advanced();
// Reset console cursor
con_x = con_l;
con_y = con_t;
// Reset last states for inputs
reset_inputstates();
}
// Initialise button test state and draw static elements
void start_btntest()
{
state = STATE_BTNTEST;
btntest_mode = btntest_mode_select;
btntest_buttonbank = 0;
btntest_buttonindex = 0;
btntest_buttonlast = 1;
btntest_timer = 0;
btntest_counter = 0;
btntest_pos = 0;
// Draw page
page_btntest(true, false);
write_string("Press the button you want to test", 0xFF, 3, 14);
write_string("Remember to enable fast USB polling!", 0xEE, 2, 25);
}
// Rotate DPAD direction history and push new entry
void pushhistory(char new)
{
for (char h = 1; h < HISTORY_LENGTH; h++)
{
history[h - 1] = history[h];
}
history[HISTORY_LENGTH - 1] = new;
}
// Track input history of P1 DPAD for secret codes!
void handle_codes()
{
if (!input_up && input_up_last)
{
pushhistory(1);
}
if (!input_down && input_down_last)
{
pushhistory(2);
}
if (!input_left && input_left_last)
{
pushhistory(3);
}
if (!input_right && input_right_last)
{
pushhistory(4);
}
// Check for SNEK code
if (history[0] == 1 && history[1] == 1 && history[2] == 2 && history[3] == 2 && history[4] == 3 && history[5] == 4)
{
nextstate = STATE_START_ATTRACT;
pushhistory(0);
start_fadeout();
return;
}
}
// Menu opening handler
bool modeswitcher()
{
// Open menu if select is held
if (input_select)
{
modeswitchtimer_select++;
if (modeswitchtimer_select == menu_openholdtime)
{
modeswitchtimer_select = 0;
start_menu();
return 1;
}
}
else
{
modeswitchtimer_select = 0;
}
return 0;
}
// Digital input tester state
void inputtester_digital()
{
// Handle PS/2 inputs whenever possible to improve latency
handle_ps2();
// Handle secret code detection (joypad 1 directions)
if (HBLANK_RISING)
{
basic_input();
handle_codes();
}
// As soon as vsync is detected start drawing screen updates
if (VBLANK_RISING)
{
// Handle test mode switch
if (modeswitcher())
{
return;
}
// Draw control pad buttons
for (char joy = 0; joy < PAD_COUNT; joy++)
{
char index = joy * 32;
for (char button = 0; button < BUTTON_COUNT; button++)
{
char color = (button < 8 ? CHECK_BIT(joystick[index], button) : CHECK_BIT(joystick[index + 8], button - 8)) ? color_button_active : color_button_inactive;
write_string(button_symbol[button], color, pad_offset_x[joy] + button_x[button], pad_offset_y[joy] + button_y[button]);
}
}
}
}
// Analog input tester state
void inputtester_analog()
{
// Handle PS/2 inputs whenever possible to improve latency
handle_ps2();
// Handle secret code detection (joypad 1 directions)
if (HBLANK_RISING)
{
basic_input();
handle_codes();
}
// As soon as vsync is detected start drawing screen updates
if (VBLANK_RISING)
{
// Handle test mode switch
if (modeswitcher())
{
return;
}
// Draw analog point
for (char j = 0; j < PAD_COUNT; j++)
{
char mx = analog_offset_x[j] + (analog_size / 2);
char my = analog_offset_y[j] + (analog_size / 2);
// Reset previous color
set_fgcolour(color_analog_grid, analog_x[j] + mx, analog_y[j] + my);
signed char ax = analog[(j * 16)];
signed char ay = analog[(j * 16) + 8];
analog_x[j] = ax / analog_ratio;
analog_y[j] = ay / analog_ratio;
// Set new color
set_fgcolour(0xFF, analog_x[j] + mx, analog_y[j] + my);
write_stringfs("%4d", 0xFF, analog_offset_x[j] + 2, analog_offset_y[j] + analog_size + 2, ax);
write_stringfs("%4d", 0xFF, analog_offset_x[j] + 10, analog_offset_y[j] + analog_size + 2, ay);
}
}
}
// Advanced input tester state
void inputtester_advanced()
{
// Handle PS/2 inputs whenever possible to improve latency
handle_ps2();
// Handle secret code detection (joypad 1 directions)
if (HBLANK_RISING)
{
basic_input();
handle_codes();
}
// As soon as vsync is detected start drawing screen updates
if (VBLANK_RISING)
{
// Handle test mode switch
if (modeswitcher())
{
return;
}
// Draw joystick inputs (only update each byte if value has changed)
for (char inputindex = 0; inputindex < 6; inputindex++)
{
char m = 0b00000001;
char x = 6;
char y = 6 + inputindex;
char inputoffset = (inputindex * 32);
char lastoffset = (inputindex * 2);
for (char b = 0; b < 2; b++)
{
char index = (b * 8) + inputoffset;
char lastindex = b + lastoffset;
char joy = joystick[index];
if (joy != joystick_last[lastindex])
{
m = 0b00000001;
char bytes = (b == 0 ? 8 : 4);
for (char i = 0; i < bytes; i++)
{
x++;
write_char((joy & m) ? asc_1 : asc_0, 0xFF, x, y);
m <<= 1;
}
}
else
{
x += 8;
}
joystick_last[lastindex] = joy;
}
// Draw analog inputs (only update if value has changed)
signed char ax = analog[(inputindex * 16)];
signed char ay = analog[(inputindex * 16) + 10];
if (ax != ax_last[inputindex] || ay != ay_last[inputindex])
{
char stra[10];
sprintf(stra, "%4d %4d", ax, ay);
write_string(stra, 0xFF, 20, 6 + inputindex);
}
ax_last[inputindex] = ax;
ay_last[inputindex] = ay;
// Draw paddle inputs (only update if value has changed)
unsigned char px = paddle[(inputindex * 8)];
if (px != px_last[inputindex])
{
char strp[5];
sprintf(strp, "%4d", px);
write_string(strp, 0xFF, 6, 14 + inputindex);
}
px_last[inputindex] = px;
// Draw spinner inputs (only update when update clock changes)
bool sx_toggle = CHECK_BIT(spinner[(inputindex * 16) + 8], 0);
signed char sx = spinner[(inputindex * 16)];
if (sx_toggle != sx_toggle_last[inputindex])
{
sx_pos[inputindex] += sx;
write_stringf("%4d", 0xFF, 22, 14 + inputindex, sx_pos[inputindex] / 8);
}
else
{
if (sx == 1 || sx == -1)
{
sx = 0;
}
}
if (sx_last[inputindex] != sx)
{
write_stringfs("%4d", 0xFF, 17, 14 + inputindex, sx);
}
sx_last[inputindex] = sx;
sx_toggle_last[inputindex] = sx_toggle;
}
// Keyboard test console
if (kbd_buffer_len > 0)
{
// Clear existing cursor if visible
if (con_cursor)
{
write_char(' ', 0xFF, con_x, con_y);
}
// Write characters in buffer
for (char k = 0; k < kbd_buffer_len; k++)
{
if (kbd_buffer[k] == '\n')
{
// New line
con_x = con_l;
con_y++;
if (con_y > con_b)
{
// Wrap to top
con_y = con_t;
}
}
else if (kbd_buffer[k] == '\b')
{
// Backspace - only if not at beginning of line
if (con_x > con_l)
{
con_x--;
// Clear existing character
write_char(' ', 0xFF, con_x, con_y);
}
}
else
{
// Write character
write_char(kbd_buffer[k], 0xFF, con_x, con_y);
// Move cursor right
con_x++;
if (con_x > con_r)
{
// New line
con_x = con_l;
con_y++;
if (con_y > con_b)
{
// Wrap to top
con_y = con_t;
}
}
}
}
// Clear buffer and enable cursor
kbd_buffer_len = 0;
con_cursor = 0;
con_cursortimer = 1;
}
// Cursor blink timer
con_cursortimer--;
if (con_cursortimer <= 0)
{
con_cursor = !con_cursor;
con_cursortimer = con_cursorfreq;
write_char(con_cursor ? '|' : ' ', 0xFF, con_x, con_y);
}
}
}
void btntest_starttest()
{
btntest_mode = btntest_mode_test;
page_btntest(false, false); // reset screen
// Prepare variables
btntest_timer = btntest_timer_start;
// Reset hardware timer
timer[0] = 0;
write_string("Press here", 0xDD, 14, 14);
write_char(19, 0xDD, 19, 15);
write_string("---\x3\x3\x3\x2\x2\x2\x1\x1\x1\xA6\xA6\xA6\x7f\x7f\x7f\xA6\xA6\xA6\x1\x1\x1\x2\x2\x2\x3\x3\x3---", 0xDD, 3, 16);
}
// Button test - button select state
void btntest_select()
{
if (HBLANK_RISING)
{
// If any of 1st 8 buttons is pressed
if (joystick[0] != 0)
{
// Find which button
for (char b = 0; b < 8; b++)
{
if (CHECK_BIT(joystick[0], b))
{
btntest_buttonindex = b;
break;
}
}
btntest_mode = btntest_mode_ready;
page_btntest(false, false); // reset screen
write_string("Hit the button at each prompt", 0b11111000, 5, 14);
write_string("Selected button is: ", 0xFF, 6, 16);
char i = btntest_buttonindex;
write_string(button_name[i], 0b00000111, 26, 16);
write_string("Press again to start test", 0b00111000, 7, 18);
btntest_timer = 10;
return;
}
}
if (VBLANK_RISING)
{
// Check inputs for select hold to enter menu
basic_input();
modeswitcher();
}
}
// Button test - ready state
void btntest_ready()
{
if (HBLANK_RISING)
{
char button = CHECK_BIT(joystick[0], btntest_buttonindex);
if (btntest_timer > 0)
{
if (joystick[0] == 0)
{
btntest_timer--;
}
}
else
{
// Check for selected button to be pressed again
if (!button && btntest_buttonlast)
{
btntest_starttest();
}
}
btntest_buttonlast = button;
}
}
// Button test - begin test state
void btntest_test()
{
// Track button presses
bool down = CHECK_BIT(joystick[0], btntest_buttonindex);
if (down && !btntest_buttondownlast)
{
btntest_presses[btntest_pos] = GET_TIMER;
btntest_pos++;
}
btntest_buttondownlast = down;
if (VBLANK_RISING)
{
// Count down to next prompt on vblanks
btntest_timer--;
set_bgcolour(0x0, btntest_highlight, 16);
if (btntest_timer == 30)
{
btntest_highlight = btntest_highlight_start;
}
if (btntest_highlight > 0)
{
if ((btntest_timer % 2) == 0)
{
btntest_highlight++;
if (btntest_highlight > btntest_highlight_end)
{
btntest_highlight = 0;
}
}
}
if (btntest_highlight > 0)
{
set_bgcolour(0xFF, btntest_highlight, 16);
}
if (btntest_aftertimer > 0)
{
btntest_aftertimer--;
if (btntest_aftertimer == 0)
{
// End test here
if (btntest_counter == btntest_counter_max)
{
btntest_mode = btntest_mode_results;
btntest_results_refresh = true;
page_btntest(true, true); // reset screen
return;
}
}
}
if (btntest_timer == 0)
{
// Capture timer
btntest_prompts[btntest_counter] = GET_TIMER;
// Display test counter
write_stringf("%2d", 0xFF, 18, 6, btntest_counter + 1);
btntest_timer = btntest_timer_interval;
btntest_aftertimer = 33;
btntest_counter++;
}
}
}
unsigned short abs(signed short value)
{
if (value < 0)
{
value = value * -1;
}
return value;
}
// Button test - results view
void btntest_results()
{
if (btntest_results_refresh)
{
write_string("Prompts", 0xFF, 2, 6);
// write_string("-------", 0xFF, 2, 7);
write_string("Presses", 0xFF, 11, 6);
// write_string("-------", 0xFF, 11, 7);
char y = 7;
char press = 0;
char prompt = 0;
char done = false;
unsigned short prompt_last = 0;
unsigned short press_last = 0;
while (!done)
{
unsigned short prompt_time = prompt < btntest_counter_max ? btntest_prompts[prompt] : 65535;
unsigned short press_time = press < btntest_pos ? btntest_presses[press] : 65535;
if (prompt_time < press_time)
{
write_stringf_ushort("%6dms", 0xFF, 2, y, btntest_prompts[prompt]);
y++;
prompt++;
prompt_last = prompt_time;
}
else if (press_time < prompt_time)
{
write_stringf_ushort("%6dms", 0xFF, 11, y, btntest_presses[press]);
// Is this early or late or just nowhere near?!
unsigned short prompt_next = prompt + 1 <= btntest_counter_max ? btntest_prompts[prompt] : 65535;
unsigned short diff_last = abs(press_time - prompt_last);
unsigned short diff_next = abs(prompt_next - press_time);
if (diff_next < 500)
{
write_stringf_short("%3dms before", 0b11111000, 20, y, diff_next);
}
else if (diff_last <= 500)
{
write_stringf_short("%3dms after", 0b00111111, 20, y, diff_last);
}
else
{
write_stringf_short("????", 0b00000111, 20, y, diff_next);
}
y++;
press++;
}
else
{
done = true;
}
}
btntest_results_refresh = 0;
}
if (VBLANK_RISING)
{
// Check inputs for select hold to enter menu
basic_input();
modeswitcher();
if (input_start && !input_start_last)
{
start_btntest();
}
}
}
// Button test - mode handler
void btntest()
{
switch (btntest_mode)
{
case btntest_mode_select:
btntest_select();
break;
case btntest_mode_ready:
btntest_ready();
break;
case btntest_mode_test:
btntest_test();
break;
case btntest_mode_results:
btntest_results();
break;
}
}

139
src/menu.c Normal file
View File

@@ -0,0 +1,139 @@
/*============================================================================
Aznable OS - Popup menu functions
Author: Jim Gregory - https://github.com/JimmyStones/
Version: 1.0
Date: 2021-07-13
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
===========================================================================*/
#pragma once
// Menu constants
#define menu_tx 12
#define menu_bx 28
#define menu_my 15
#define menu_openholdtime 60
#define menu_count 4
// Menu variables
char menu_timer = 0;
char menu_index;
bool menu_dirty = 0;
bool menu_ready = 0;
char *menu_string[] = {
"Digital",
"Analog",
"Advanced",
"Button test"};
// Initialise menu state
void start_menu()
{
state = STATE_MENU;
menu_timer = 0;
menu_ready = 0;
}
// Menu state
void menu()
{
// Check inputs at end of each scanline. Is this too much?!
if (HBLANK_RISING)
{
basic_input();
if (input_up && !input_up_last) // up
{
menu_index--;
if (menu_index > 128)
{
menu_index = menu_count - 1;
}
menu_dirty = true;
}
if (input_down && !input_down_last) // down
{
menu_index++;
if (menu_index == menu_count)
{
menu_index = 0;
}
menu_dirty = true;
}
if (input_start && input_start_last)
{
switch (menu_index)
{
case 0:
state = STATE_START_INPUTTESTER;
break;
case 1:
state = STATE_START_INPUTTESTERANALOG;
break;
case 2:
state = STATE_START_INPUTTESTERADVANCED;
break;
case 3:
state = STATE_START_BTNTEST;
break;
}
}
}
// As soon as vsync is detected start drawing screen updates
if (VBLANK_RISING)
{
char maxsize = (menu_count * 3) + 1;
if (menu_timer < maxsize)
{
char oy1 = ((menu_timer) / 2);
char oy2 = oy1 + ((menu_count % 2) ? 0 : 1);
char my = menu_my;
panel_shaded(menu_tx, my - oy1, menu_bx, my + oy2, 0xFF, 0b00110110, 0b00100100);
if (oy1 > 1)
{
fill(menu_tx + 1, my - (oy1 - 1), menu_bx - 1, my + (oy2 - 1), 0, 0);
fill_bgcolor(menu_tx + 1, my - (oy1 - 1), menu_bx - 1, my + (oy2 - 1), 0b00001001);
}
menu_timer++;
if (menu_timer == maxsize)
{
menu_dirty = true;
}
}
else
{
if (menu_dirty)
{
char ty = menu_my - ((menu_count * 3) / 2) + 1;
for (char m = 0; m < menu_count; m++)
{
if (menu_index == m)
{
panel_shaded(menu_tx + 1, ty, menu_bx - 1, ty + 2, 0b11111111, 0b10111111, 0b10110110);
write_string(menu_string[m], 0b11111111, menu_tx + 2, ty + 1);
}
else
{
panel_shaded(menu_tx + 1, ty, menu_bx - 1, ty + 2, 0b10111111, 0b10110110, 0b01100100);
write_string(menu_string[m], 0b10110110, menu_tx + 2, ty + 1);
}
ty += 3;
}
menu_dirty = false;
menu_ready = true;
}
}
}
}

789
src/os.c
View File

@@ -3,7 +3,7 @@
Author: Jim Gregory - https://github.com/JimmyStones/
Version: 1.0
Date: 2021-07-03
Date: 2021-07-12
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
@@ -22,61 +22,22 @@
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "sys.c"
#include "ui.c"
#include "sys_custom.c"
#include "ps2.c"
#include "ui.c"
#include "ui_custom.c"
#include "menu.c"
#include "inputtester.c"
#include "snek.c"
#include "fader.c"
bool hsync;
bool hsync_last;
bool vsync;
bool vsync_last;
// Application state
#define STATE_START_INPUTTESTER 0
#define STATE_INPUTTESTER 1
#define STATE_START_INPUTTESTERADVANCED 2
#define STATE_INPUTTESTERADVANCED 3
#define STATE_START_INPUTTESTERANALOG 4
#define STATE_INPUTTESTERANALOG 5
#define STATE_FADEOUT 8
#define STATE_START_FADEIN 9
#define STATE_FADEIN 10
#define STATE_START_ATTRACT 16
#define STATE_ATTRACT 17
#define STATE_START_GAME 24
#define STATE_GAME 25
char state = STATE_START_INPUTTESTER;
char nextstate = 0;
// SNEK variables
unsigned char movefreqinit = 14;
unsigned char movefreqdecfreq = 200;
unsigned char movefreqdectimer = 0;
unsigned char movefreq = 0;
unsigned char movetimer = 0;
signed int x = 20;
signed int y = 15;
signed char xd = 0;
signed char yd = 1;
signed char nxd = 0;
signed char nyd = 1;
unsigned int length = 0;
unsigned char playerchar = 83;
// Fade in/out variables
unsigned char fade = 0;
unsigned char fadetimer = 0;
unsigned char fadefreq = 4;
// Attract mode variables
unsigned char attractstate = 0;
// Input tester variables
unsigned char joystick_last[12];
signed char ax_last[6];
@@ -86,19 +47,6 @@ signed char sx_toggle_last[6];
signed char sx_last[6];
unsigned long sx_pos[6];
unsigned char con_x; // Console cursor X position
unsigned char con_y; // Console cursor X position
unsigned char con_l = 2; // Console left edge X
unsigned char con_t = 20; // Console top edge Y
unsigned char con_r = 37; // Console right edge X
unsigned char con_b = 37; // Console bottom edge Y
bool con_cursor;
unsigned char con_cursortimer = 1;
unsigned char con_cursorfreq = 30;
char modeswitchtimer_select = 0;
char modeswitchtimer_start = 0;
// DPAD tracker
bool bdown_left = 0;
bool bdown_left_last = 0;
@@ -109,696 +57,6 @@ bool bdown_up_last = 0;
bool bdown_down = 0;
bool bdown_down_last = 0;
#define HISTORY_LENGTH 6
char history[HISTORY_LENGTH];
#define PAD_COUNT 2
#define BUTTON_COUNT 12
char pad_offset_x[PAD_COUNT] = {7, 7};
char pad_offset_y[PAD_COUNT] = {7, 16};
char button_name[BUTTON_COUNT][6] = {
"R",
"L",
"D",
"U",
"A",
"B",
"X",
"Y",
"L",
"R",
"Sel",
"Start"};
char button_x[BUTTON_COUNT] = {6, 2, 4, 4, 24, 22, 22, 20, 3, 23, 9, 13};
char button_y[BUTTON_COUNT] = {3, 3, 4, 2, 3, 4, 2, 3, 0, 0, 3, 3};
char analog_offset_x[PAD_COUNT] = {1, 20};
char analog_offset_y[PAD_COUNT] = {5, 5};
char analog_size = 18;
signed char analog_x[PAD_COUNT];
signed char analog_y[PAD_COUNT];
char analog_ratio = 256 / 17;
// Draw static elements for digital input test page
void page_inputtester_digital()
{
clear_chars(0);
page_border(0b00000111);
write_string("- MiSTer Input Tester -", 0b11100011, 8, 1);
write_string("Hold: Select=analog Start=advanced", 0b11100011, 3, 29);
// Draw pads
for (char j = 0; j < PAD_COUNT; j++)
{
write_stringf("JOY %d", 0xFF, pad_offset_x[j] - 5, pad_offset_y[j] + 3, j + 1);
draw_pad(pad_offset_x[j], pad_offset_y[j]);
}
}
// Draw static elements for analog input test page
void page_inputtester_analog()
{
clear_chars(0);
page_border(0b00000111);
write_string("- MiSTer Input Tester -", 0b11100011, 8, 1);
write_string("Hold: Select=digital Start=advanced", 0b11100011, 3, 29);
for (char j = 0; j < PAD_COUNT; j++)
{
write_stringf("ANALOG %d", 0xFF, analog_offset_x[j] + 5, analog_offset_y[j] - 1, j + 1);
draw_analog(analog_offset_x[j], analog_offset_y[j], analog_size, analog_size);
}
}
// Draw static elements for advanced input test page
void page_inputtester_advanced()
{
clear_chars(0);
page_border(0b00000111);
write_string("- MiSTer Input Tester -", 0b11100011, 8, 1);
write_string("Hold: Select=digital Start=analog", 0b11100011, 3, 29);
write_string("RLDUABXYLRsS", 0xFF, 7, 3);
write_string("AX", 0xFF, 22, 3);
write_string("AY", 0xFF, 27, 3);
write_string("POS", 0xFF, 7, 11);
write_string("SPD POS", 0xFF, 18, 11);
char label[5];
for (unsigned char j = 0; j < 6; j++)
{
sprintf(label, "JOY%d", j + 1);
write_string(label, 0xFF - (j * 2), 2, 4 + j);
sprintf(label, "PAD%d", j + 1);
write_string(label, 0xFF - (j * 2), 2, 12 + j);
sprintf(label, "SPN%d", j + 1);
write_string(label, 0xFF - (j * 2), 14, 12 + j);
}
write_string("CON", 0xFF, 2, 19);
}
void reset_inputstates()
{
modeswitchtimer_select = 0;
modeswitchtimer_start = 0;
for (char i = 0; i < 12; i++)
{
joystick_last[i] = 1;
}
for (char i = 0; i < 6; i++)
{
ax_last[i] = 1;
ay_last[i] = 1;
px_last[i] = 1;
sx_toggle_last[i] = 1;
sx_last[i] = 1;
sx_pos[i] = 0;
}
}
// Initialise digital inputtester state and draw static elements
void start_inputtester_digital()
{
state = STATE_INPUTTESTER;
// Draw page
page_inputtester_digital();
// Reset last states for inputs
reset_inputstates();
}
// Initialise analog inputtester state and draw static elements
void start_inputtester_analog()
{
state = STATE_INPUTTESTERANALOG;
// Draw page
page_inputtester_analog();
// Reset last states for inputs
reset_inputstates();
}
// Initialise advanced inputtester state and draw static elements
void start_inputtester_advanced()
{
state = STATE_INPUTTESTERADVANCED;
// Draw page
page_inputtester_advanced();
// Reset console cursor
con_x = con_l;
con_y = con_t;
// Reset last states for inputs
reset_inputstates();
}
// Initialise fadeout state
void start_fadeout()
{
state = STATE_FADEOUT;
fadetimer = fadefreq;
fade = 0;
}
// Initialise fadein state
void start_fadein()
{
state = STATE_FADEIN;
fadetimer = fadefreq;
fade = 15;
}
// Initialise attract state and draw static elements
void start_attract()
{
state = STATE_ATTRACT;
attractstate = 0;
clear_chars(0);
page_border(0b00000111);
write_string("SNEK", 0b00000111, 18, 0);
movefreq = 5;
movetimer = 1;
}
// Initialise attract state and draw static elements
void start_gameplay()
{
state = STATE_GAME;
length = 0;
x = 20;
y = 15;
xd = 0;
yd = 1;
nxd = 0;
nyd = 1;
clear_chars(0);
page_border(0b00000111);
write_string("SNEK", 0b00000111, 18, 0);
write_char(playerchar, 0xFF, x, y);
movefreq = movefreqinit;
movefreqdectimer = movefreqdecfreq;
movetimer = movefreq;
}
// Fade out state
void fadeout()
{
if (vsync && !vsync_last)
{
fadetimer--;
if (fadetimer == 0)
{
box(fade, fade, 39 - fade, 29 - fade, 127, 0b0000111);
fadetimer = fadefreq;
fade++;
if (fade == 16)
{
start_fadein();
}
}
}
}
// Fade in state
void fadein()
{
if (vsync && !vsync_last)
{
fadetimer--;
if (fadetimer == 0)
{
box(fade, fade, 39 - fade, 29 - fade, 0, 0b0000000);
fadetimer = fadefreq;
fade--;
if (fade == 0)
{
state = nextstate;
}
}
}
}
// Rotate DPAD direction history and push new entry
void pushhistory(char new)
{
for (char h = 1; h < HISTORY_LENGTH; h++)
{
history[h - 1] = history[h];
}
history[HISTORY_LENGTH - 1] = new;
}
// Track input history of P1 DPAD for secret codes!
void handle_codes()
{
bdown_up_last = bdown_up;
bdown_down_last = bdown_down;
bdown_left_last = bdown_left;
bdown_right_last = bdown_right;
bdown_up = CHECK_BIT(joystick[0], 3);
bdown_down = CHECK_BIT(joystick[0], 2);
bdown_left = CHECK_BIT(joystick[0], 1);
bdown_right = CHECK_BIT(joystick[0], 0);
if (!bdown_up && bdown_up_last)
{
pushhistory(1);
}
if (!bdown_down && bdown_down_last)
{
pushhistory(2);
}
if (!bdown_left && bdown_left_last)
{
pushhistory(3);
}
if (!bdown_right && bdown_right_last)
{
pushhistory(4);
}
// Check for SNEK code
if (history[0] == 1 && history[1] == 1 && history[2] == 2 && history[3] == 2 && history[4] == 3 && history[5] == 4)
{
nextstate = STATE_START_ATTRACT;
pushhistory(0);
start_fadeout();
return;
}
}
bool modeswitcher()
{
// Switch to advanced mode if start is held for 1 second
if (CHECK_BIT(joystick[8], 3))
{
modeswitchtimer_start++;
if (modeswitchtimer_start == 60)
{
if (state == STATE_INPUTTESTERADVANCED)
{
start_inputtester_analog();
}
else
{
start_inputtester_advanced();
}
return 1;
}
}
else
{
modeswitchtimer_start = 0;
}
// Switch between digital/analog mode if select is held for 1 second
if (CHECK_BIT(joystick[8], 2))
{
modeswitchtimer_select++;
if (modeswitchtimer_select == 60)
{
if (state == STATE_INPUTTESTER)
{
start_inputtester_analog();
}
else
{
start_inputtester_digital();
}
return 1;
}
}
else
{
modeswitchtimer_select = 0;
}
return 0;
}
// Digital input tester state
void inputtester_digital()
{
// Handle PS/2 inputs whenever possible to improve latency
handle_ps2();
// Handle secret code detection (joypad 1 directions)
if (hsync && !hsync_last)
{
handle_codes();
}
// As soon as vsync is detected start drawing screen updates
if (vsync && !vsync_last)
{
// Handle test mode switch
if (modeswitcher())
{
return;
}
// Draw control pad buttons
for (char joy = 0; joy < PAD_COUNT; joy++)
{
char index = joy * 32;
for (char button = 0; button < BUTTON_COUNT; button++)
{
char color = (button < 8 ? CHECK_BIT(joystick[index], button) : CHECK_BIT(joystick[index + 8], button - 8)) ? 0xFF : 0b10010010;
write_string(button_name[button], color, pad_offset_x[joy] + button_x[button], pad_offset_y[joy] + button_y[button]);
}
}
}
}
// Analog input tester state
void inputtester_analog()
{
// Handle PS/2 inputs whenever possible to improve latency
handle_ps2();
// Handle secret code detection (joypad 1 directions)
if (hsync && !hsync_last)
{
handle_codes();
}
// As soon as vsync is detected start drawing screen updates
if (vsync && !vsync_last)
{
// Handle test mode switch
if (modeswitcher())
{
return;
}
// Draw analog point
for (char j = 0; j < PAD_COUNT; j++)
{
char mx = analog_offset_x[j] + (analog_size / 2);
char my = analog_offset_y[j] + (analog_size / 2);
// Reset previous color
set_colour(color_analog_grid, analog_x[j] + mx, analog_y[j] + my);
signed char ax = analog[(j * 16)];
signed char ay = analog[(j * 16) + 8];
analog_x[j] = ax / analog_ratio;
analog_y[j] = ay / analog_ratio;
// Set new color
set_colour(0xFF, analog_x[j] + mx, analog_y[j] + my);
write_stringfs("%4d", 0xFF, analog_offset_x[j], analog_offset_y[j] + analog_size + 1, ax);
write_stringfs("%4d", 0xFF, analog_offset_x[j] + 5, analog_offset_y[j] + analog_size + 1, ay);
}
}
}
// Advanced input tester state
void inputtester_advanced()
{
// Handle PS/2 inputs whenever possible to improve latency
handle_ps2();
// Handle secret code detection (joypad 1 directions)
if (hsync && !hsync_last)
{
handle_codes();
}
// As soon as vsync is detected start drawing screen updates
if (vsync && !vsync_last)
{
// Handle test mode switch
if (modeswitcher())
{
return;
}
// Draw joystick inputs (only update each byte if value has changed)
for (char inputindex = 0; inputindex < 6; inputindex++)
{
char m = 0b00000001;
char x = 6;
char y = 4 + inputindex;
char inputoffset = (inputindex * 32);
char lastoffset = (inputindex * 2);
for (char b = 0; b < 2; b++)
{
char index = (b * 8) + inputoffset;
char lastindex = b + lastoffset;
char joy = joystick[index];
if (joy != joystick_last[lastindex])
{
m = 0b00000001;
char bytes = (b == 0 ? 8 : 4);
for (char i = 0; i < bytes; i++)
{
x++;
write_char((joy & m) ? asc_1 : asc_0, 0xFF, x, y);
m <<= 1;
}
}
else
{
x += 8;
}
joystick_last[lastindex] = joy;
}
// Draw analog inputs (only update if value has changed)
signed char ax = analog[(inputindex * 16)];
signed char ay = analog[(inputindex * 16) + 8];
if (ax != ax_last[inputindex] || ay != ay_last[inputindex])
{
char stra[10];
sprintf(stra, "%4d %4d", ax, ay);
write_string(stra, 0xFF, 20, 4 + inputindex);
}
ax_last[inputindex] = ax;
ay_last[inputindex] = ay;
// Draw paddle inputs (only update if value has changed)
unsigned char px = paddle[(inputindex * 8)];
if (px != px_last[inputindex])
{
char strp[5];
sprintf(strp, "%4d", px);
write_string(strp, 0xFF, 6, 12 + inputindex);
}
px_last[inputindex] = px;
// Draw spinner inputs (only update when update clock changes)
bool sx_toggle = CHECK_BIT(spinner[(inputindex * 16) + 8], 0);
signed char sx = spinner[(inputindex * 16)];
if (sx_toggle != sx_toggle_last[inputindex])
{
sx_pos[inputindex] += sx;
write_stringf("%4d", 0xFF, 22, 12 + inputindex, sx_pos[inputindex] / 8);
}
else
{
if (sx == 1 || sx == -1)
{
sx = 0;
}
}
if (sx_last[inputindex] != sx)
{
write_stringfs("%4d", 0xFF, 17, 12 + inputindex, sx);
}
sx_last[inputindex] = sx;
sx_toggle_last[inputindex] = sx_toggle;
}
// Keyboard test console
if (kbd_buffer_len > 0)
{
// Clear existing cursor if visible
if (con_cursor)
{
write_char(' ', 0xFF, con_x, con_y);
}
// Write characters in buffer
for (char k = 0; k < kbd_buffer_len; k++)
{
if (kbd_buffer[k] == '\n')
{
// New line
con_x = con_l;
con_y++;
if (con_y > con_b)
{
// Wrap to top
con_y = con_t;
}
}
else if (kbd_buffer[k] == '\b')
{
// Backspace - only if not at beginning of line
if (con_x > con_l)
{
con_x--;
// Clear existing character
write_char(' ', 0xFF, con_x, con_y);
}
}
else
{
// Write character
write_char(kbd_buffer[k], 0xFF, con_x, con_y);
// Move cursor right
con_x++;
if (con_x > con_r)
{
// New line
con_x = con_l;
con_y++;
if (con_y > con_b)
{
// Wrap to top
con_y = con_t;
}
}
}
}
// Clear buffer and enable cursor
kbd_buffer_len = 0;
con_cursor = 0;
con_cursortimer = 1;
}
// Cursor blink timer
con_cursortimer--;
if (con_cursortimer <= 0)
{
con_cursor = !con_cursor;
con_cursortimer = con_cursorfreq;
write_char(con_cursor ? '|' : ' ', 0xFF, con_x, con_y);
}
}
}
// SNEK - gameplay state
void gameplay()
{
if (hsync && !hsync_last)
{
if (yd != 1 && joystick[0] & 0b00001000) // up
{
nyd = -1;
nxd = 0;
}
if (yd != -1 && joystick[0] & 0b00000100) // down
{
nyd = 1;
nxd = 0;
}
if (xd != 1 && joystick[0] & 0b00000010) // left
{
nxd = -1;
nyd = 0;
}
if (xd != -1 && joystick[0] & 0b00000001) //right
{
nxd = 1;
nyd = 0;
}
if (CHECK_BIT(joystick[8], 2)) // select to quit
{
start_inputtester_advanced();
return;
}
}
if (vsync && !vsync_last)
{
movetimer--;
if (movetimer == 0)
{
write_char(127, 0x66, x, y);
xd = nxd;
yd = nyd;
x += xd;
y += yd;
unsigned int p = (y * chram_cols) + x;
if (chram[p] > 0)
{
nextstate = STATE_START_ATTRACT;
start_fadeout();
return;
}
length++;
write_char(playerchar, 0xFF, x, y);
movetimer = movefreq;
char score[5];
sprintf(score, "%4d", length);
write_string(score, 0xFF, 35, 0);
}
movefreqdectimer--;
if (movefreqdectimer == 0)
{
movefreqdectimer = movefreqdecfreq;
if (movefreq > 3)
{
movefreq--;
}
char str_movefreq[3];
sprintf(str_movefreq, "%4d", movefreq);
write_string(str_movefreq, 0xFF, 35, 29);
}
}
}
// SNEK - attract state
void attract()
{
if (hsync && !hsync_last)
{
if (CHECK_BIT(joystick[8], 3)) // start to start
{
start_gameplay();
return;
}
if (CHECK_BIT(joystick[8], 2)) // select to quit
{
start_inputtester_advanced();
return;
}
}
if (vsync && !vsync_last)
{
movetimer--;
if (movetimer == 0)
{
attractstate = !attractstate;
write_string("PRESS START", attractstate == 0 ? 0x00 : 0xFF, 16, 15);
movetimer = movefreq;
}
}
}
// Main entry and state machine
void main()
{
@@ -807,6 +65,8 @@ void main()
{
hsync = input0 & 0x80;
vsync = input0 & 0x40;
hblank = input0 & 0x20;
vblank = input0 & 0x10;
switch (state)
{
case STATE_START_INPUTTESTER:
@@ -830,6 +90,20 @@ void main()
inputtester_analog();
break;
case STATE_START_BTNTEST:
start_btntest();
break;
case STATE_BTNTEST:
btntest();
break;
case STATE_START_MENU:
start_menu();
break;
case STATE_MENU:
menu();
break;
case STATE_FADEOUT:
fadeout();
break;
@@ -841,20 +115,25 @@ void main()
start_attract();
break;
case STATE_ATTRACT:
attract();
snek_attract();
break;
case STATE_START_GAME:
case STATE_START_GAME_SNEK:
start_gameplay();
break;
case STATE_GAME:
gameplay();
case STATE_GAME_SNEK:
snek_gameplay();
break;
default:
// Start default state
start_inputtester_digital();
//start_btntest();
break;
}
hsync_last = hsync;
vsync_last = vsync;
hblank_last = hblank;
vblank_last = vblank;
}
}

View File

@@ -1,5 +1,5 @@
/*============================================================================
MiSTer test harness OS - PS/2 interface functions
Aznable OS - PS/2 interface functions
Author: Jim Gregory - https://github.com/JimmyStones/
Version: 1.0

181
src/snek.c Normal file
View File

@@ -0,0 +1,181 @@
/*============================================================================
Input Test - Snek mini-game
Author: Jim Gregory - https://github.com/JimmyStones/
Version: 1.0
Date: 2021-07-12
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
===========================================================================*/
#pragma once
#include "sys.c"
#include "fader.c"
// SNEK constants
#define movefreqinit 14
#define movefreqdecfreq 200
#define playerchar 83
// SNEK variables
unsigned char movefreqdectimer = 0;
unsigned char movefreq = 0;
unsigned char movetimer = 0;
signed int x = 20;
signed int y = 15;
signed char xd = 0;
signed char yd = 1;
signed char nxd = 0;
signed char nyd = 1;
unsigned int length = 0;
// Attract mode variables
unsigned char attractstate = 0;
// Initialise attract state and draw static elements
void start_attract()
{
state = STATE_ATTRACT;
attractstate = 0;
clear_chars(0);
page_border(0b00000111);
write_string("SNEK", 0b00000111, 18, 0);
movefreq = 5;
movetimer = 1;
}
// Initialise attract state and draw static elements
void start_gameplay()
{
state = STATE_GAME_SNEK;
length = 0;
x = 20;
y = 15;
xd = 0;
yd = 1;
nxd = 0;
nyd = 1;
clear_chars(0);
page_border(0b00000111);
write_string("SNEK", 0b00000111, 18, 0);
write_char(playerchar, 0xFF, x, y);
movefreq = movefreqinit;
movefreqdectimer = movefreqdecfreq;
movetimer = movefreq;
}
// SNEK - gameplay state
void snek_gameplay()
{
if (HBLANK_RISING)
{
if (yd != 1 && joystick[0] & 0b00001000) // up
{
nyd = -1;
nxd = 0;
}
if (yd != -1 && joystick[0] & 0b00000100) // down
{
nyd = 1;
nxd = 0;
}
if (xd != 1 && joystick[0] & 0b00000010) // left
{
nxd = -1;
nyd = 0;
}
if (xd != -1 && joystick[0] & 0b00000001) //right
{
nxd = 1;
nyd = 0;
}
if (CHECK_BIT(joystick[8], 2)) // select to quit
{
state = 0;
return;
}
}
if (VBLANK_RISING)
{
movetimer--;
if (movetimer == 0)
{
write_char(127, 0x66, x, y);
xd = nxd;
yd = nyd;
x += xd;
y += yd;
unsigned int p = (y * chram_cols) + x;
if (chram[p] > 0)
{
nextstate = STATE_START_ATTRACT;
start_fadeout();
return;
}
length++;
write_char(playerchar, 0xFF, x, y);
movetimer = movefreq;
char score[5];
sprintf(score, "%4d", length);
write_string(score, 0xFF, 35, 0);
}
movefreqdectimer--;
if (movefreqdectimer == 0)
{
movefreqdectimer = movefreqdecfreq;
if (movefreq > 3)
{
movefreq--;
}
char str_movefreq[3];
sprintf(str_movefreq, "%4d", movefreq);
write_string(str_movefreq, 0xFF, 35, 29);
}
}
}
// SNEK - attract state
void snek_attract()
{
if (HBLANK_RISING)
{
if (CHECK_BIT(joystick[8], 3)) // start to start
{
start_gameplay();
return;
}
if (CHECK_BIT(joystick[8], 2)) // select to quit
{
state = 0;
return;
}
}
if (VBLANK_RISING)
{
movetimer--;
if (movetimer == 0)
{
attractstate = !attractstate;
write_string("PRESS START", attractstate == 0 ? 0x00 : 0xFF, 16, 15);
movetimer = movefreq;
}
}
}

View File

@@ -1,9 +1,9 @@
/*============================================================================
MiSTer test harness OS - System interface functions
Aznable OS - System interface functions
Author: Jim Gregory - https://github.com/JimmyStones/
Version: 1.0
Date: 2021-07-03
Date: 2021-07-12
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
@@ -25,7 +25,8 @@
#include <stdbool.h>
#include <string.h>
// Memory maps
// Memory mapped IO
// - Inputs
unsigned char __at(0x6000) input0;
unsigned char __at(0x7000) joystick[24];
unsigned char __at(0x7100) analog[12];
@@ -33,13 +34,37 @@ unsigned char __at(0x7200) paddle[6];
unsigned char __at(0x7300) spinner[12];
unsigned char __at(0x7400) ps2_key[2];
unsigned char __at(0x7500) ps2_mouse[6];
unsigned char __at(0x7600) timestamp[5];
unsigned char __at(0x7700) timer[2];
// - Graphics RAM
unsigned char __at(0x8000) chram[2048];
unsigned char __at(0x8800) colram[2048];
unsigned char __at(0x8800) fgcolram[2048];
unsigned char __at(0x9000) bgcolram[2048];
// Character map
const unsigned char chram_cols = 64;
const unsigned char chram_rows = 32;
unsigned int chram_size;
// Hardware inputs
bool hsync;
bool hsync_last;
bool vsync;
bool vsync_last;
bool hblank;
bool hblank_last;
bool vblank;
bool vblank_last;
// Macros
#define CHECK_BIT(var, pos) ((var) & (1 << (pos)))
#define CHECK_BIT(var, pos) ((var) & (1 << (pos)))
#define SET_BIT(var,pos) ((var) |= (1 << (pos)))
#define CLEAR_BIT(var,pos) ((var) &= ~(1 << (pos)))
#define VBLANK_RISING (vblank && !vblank_last)
#define VSYNC_RISING (vsync && !vsync_last)
#define HBLANK_RISING (hblank && !hblank_last)
#define HSYNC_RISING (hsync && !hsync_last)
// Application state
char state = 0;
char nextstate = 0;

83
src/sys_custom.c Normal file
View File

@@ -0,0 +1,83 @@
/*============================================================================
Input Test - Custom system functions
Author: Jim Gregory - https://github.com/JimmyStones/
Version: 1.0
Date: 2021-07-12
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
===========================================================================*/
#pragma once
#include "sys.c"
// Application states
#define STATE_START_INPUTTESTER 1
#define STATE_INPUTTESTER 2
#define STATE_START_INPUTTESTERADVANCED 3
#define STATE_INPUTTESTERADVANCED 4
#define STATE_START_INPUTTESTERANALOG 5
#define STATE_INPUTTESTERANALOG 6
#define STATE_START_BTNTEST 7
#define STATE_BTNTEST 8
#define STATE_START_MENU 9
#define STATE_MENU 10
#define STATE_FADEOUT 20
#define STATE_START_FADEIN 21
#define STATE_FADEIN 22
#define STATE_START_ATTRACT 30
#define STATE_ATTRACT 31
#define STATE_START_GAME_SNEK 40
#define STATE_GAME_SNEK 41
#define GET_TIMER ((unsigned short)timer[8] << 8) | (unsigned char)timer[0]
// DPAD tracker
bool input_left = 0;
bool input_left_last = 0;
bool input_right = 0;
bool input_right_last = 0;
bool input_up = 0;
bool input_up_last = 0;
bool input_down = 0;
bool input_down_last = 0;
bool input_start;
bool input_start_last = 0;
bool input_select;
bool input_select_last = 0;
// Track joypad 1 directions and start for menu control
void basic_input()
{
input_up_last = input_up;
input_down_last = input_down;
input_left_last = input_left;
input_right_last = input_right;
input_start_last = input_start;
input_select_last = input_select;
input_up = CHECK_BIT(joystick[0], 3);
input_down = CHECK_BIT(joystick[0], 2);
input_left = CHECK_BIT(joystick[0], 1);
input_right = CHECK_BIT(joystick[0], 0);
input_start = CHECK_BIT(joystick[8], 3);
input_select = CHECK_BIT(joystick[8], 2);
}

265
src/ui.c
View File

@@ -1,9 +1,9 @@
/*============================================================================
MiSTer test harness OS - User interface functions
Aznable OS - User interface functions
Author: Jim Gregory - https://github.com/JimmyStones/
Version: 1.0
Date: 2021-07-03
Version: 1.1
Date: 2021-07-15
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
@@ -25,30 +25,51 @@
char asc_0 = 48;
char asc_1 = 49;
#define char_corner_round_tl 149
#define char_corner_round_tr 137
#define char_corner_round_bl 138
#define char_corner_round_br 139
#define char_line_h 131
#define char_line_v 130
#define char_t_up 177
#define char_dot 27
#define char_cross 155
// Set all character RAM to specified character
void clear_chars(char c)
{
for (unsigned int p = 0; p < chram_size; p++)
{
chram[p] = c;
fgcolram[p] = c;
bgcolram[p] = c;
}
}
// Set all character background colours to specified
void clear_bgcolor(char color)
{
for (unsigned int p = 0; p < chram_size; p++)
{
bgcolram[p] = color;
}
}
// Write string to character RAM
void write_string(const char *string, char color, unsigned int x, unsigned int y)
void write_string(const char *string, char color, unsigned char x, unsigned char y)
{
unsigned int p = (y * chram_cols) + x;
unsigned char l = strlen(string);
for (char c = 0; c < l; c++)
{
chram[p] = string[c];
colram[p] = color;
fgcolram[p] = color;
p++;
}
}
// Write formatted string to character RAM (signed char data)
void write_stringfs(const char *format, char color, unsigned int x, unsigned int y, signed char data)
void write_stringfs(const char *format, char color, unsigned char x, unsigned char y, signed char data)
{
unsigned int p = (y * chram_cols) + x;
char temp[30];
@@ -61,13 +82,13 @@ void write_stringfs(const char *format, char color, unsigned int x, unsigned int
return;
}
chram[p] = temp[c];
colram[p] = color;
fgcolram[p] = color;
p++;
}
}
// Write formatted string to character RAM (unsigned char data)
void write_stringf(const char *format, char color, unsigned int x, unsigned int y, char data)
void write_stringf(const char *format, char color, unsigned char x, unsigned char y, char data)
{
unsigned int p = (y * chram_cols) + x;
char temp[30];
@@ -80,28 +101,115 @@ void write_stringf(const char *format, char color, unsigned int x, unsigned int
return;
}
chram[p] = temp[c];
colram[p] = color;
fgcolram[p] = color;
p++;
}
}
// Write formatted string to character RAM (unsigned short data)
void write_stringf_ushort(const char *format, char color, unsigned char x, unsigned char y, unsigned short data)
{
unsigned int p = (y * chram_cols) + x;
char temp[40];
sprintf(temp, format, data);
unsigned char l = strlen(temp);
for (char c = 0; c < l; c++)
{
if (temp[c] == 0)
{
return;
}
chram[p] = temp[c];
fgcolram[p] = color;
p++;
}
}
// Write formatted string to character RAM (signed short data)
void write_stringf_short(const char *format, char color, unsigned char x, unsigned char y, short data)
{
unsigned int p = (y * chram_cols) + x;
char temp[40];
sprintf(temp, format, data);
unsigned char l = strlen(temp);
for (char c = 0; c < l; c++)
{
if (temp[c] == 0)
{
return;
}
chram[p] = temp[c];
fgcolram[p] = color;
p++;
}
}
// Write formatted string to character RAM (unsigned long data)
void write_stringf_ulong(const char *format, char color, unsigned char x, unsigned char y, unsigned long data)
{
unsigned int p = (y * chram_cols) + x;
char temp[40];
sprintf(temp, format, data);
unsigned char l = strlen(temp);
for (char c = 0; c < l; c++)
{
if (temp[c] == 0)
{
return;
}
chram[p] = temp[c];
fgcolram[p] = color;
p++;
}
}
// Write single char to character RAM and colour RAM
void write_char(unsigned char c, char color, unsigned int x, unsigned int y)
void write_char(unsigned char c, char color, unsigned char x, unsigned char y)
{
unsigned int p = (y * chram_cols) + x;
chram[p] = c;
colram[p] = color;
fgcolram[p] = color;
}
// Write row of consecutive chars to character RAM and colour RAM
void write_char_row(unsigned char c, char color, unsigned char x, unsigned char y, unsigned char count)
{
unsigned int p = (y * chram_cols) + x;
for (char b = 0; b < count; b++)
{
chram[p] = c;
fgcolram[p] = color;
p++;
}
}
// Set colour of single char
void set_colour(char color, unsigned int x, unsigned int y)
void set_fgcolour(char color, char x, char y)
{
unsigned int p = (y * chram_cols) + x;
colram[p] = color;
fgcolram[p] = color;
}
// Set background colour of single char
void set_bgcolour(char color, char x, char y)
{
unsigned int p = (y * chram_cols) + x;
bgcolram[p] = color;
}
// Write row of consecutive chars to character RAM and colour RAM
void write_bgcol_row(char color, unsigned char x, unsigned char y, unsigned char count)
{
unsigned int p = (y * chram_cols) + x;
for (char b = 0; b < count; b++)
{
bgcolram[p] = color;
p++;
}
}
// Write grouped bits to character RAM
void write_bits(char bits[], char multi, unsigned char first, unsigned char length, char color, unsigned int x, unsigned int y)
void write_bits(char bits[], char multi, unsigned char first, unsigned char length, char color, unsigned char x, unsigned char y)
{
for (char b = first; b < first + length; b++)
{
@@ -118,14 +226,14 @@ void write_bits(char bits[], char multi, unsigned char first, unsigned char leng
}
// Draw box outline with specified character
void box(unsigned int tx, unsigned int ty, unsigned int bx, unsigned int by, char c, char color)
void box(unsigned char tx, unsigned char ty, unsigned char bx, unsigned char by, char c, char color)
{
for (unsigned int x = tx; x <= bx; x++)
for (unsigned char x = tx; x <= bx; x++)
{
write_char(c, color, x, ty);
write_char(c, color, x, by);
}
for (unsigned int y = ty + 1; y < by; y++)
for (unsigned char y = ty + 1; y < by; y++)
{
write_char(c, color, tx, y);
write_char(c, color, bx, y);
@@ -135,30 +243,60 @@ void box(unsigned int tx, unsigned int ty, unsigned int bx, unsigned int by, cha
// Draw UI panel
void panel(char tx, char ty, char bx, char by, char color)
{
write_char(128, color, tx, ty);
write_char(130, color, bx, ty);
write_char(133, color, tx, by);
write_char(132, color, bx, by);
write_char(char_corner_round_tl, color, tx, ty);
write_char(char_corner_round_tr, color, bx, ty);
write_char(char_corner_round_bl, color, tx, by);
write_char(char_corner_round_br, color, bx, by);
for (char x = tx + 1; x < bx; x++)
{
write_char(129, color, x, ty);
write_char(129, color, x, by);
write_char(char_line_h, color, x, ty);
write_char(char_line_h, color, x, by);
}
for (char y = ty + 1; y < by; y++)
{
write_char(131, color, tx, y);
write_char(131, color, bx, y);
write_char(char_line_v, color, tx, y);
write_char(char_line_v, color, bx, y);
}
}
// Shaded panel
void panel_shaded(char tx, char ty, char bx, char by, char color_high, char color1, char color2)
{
write_char(char_corner_round_tl, color_high, tx, ty);
write_char(char_corner_round_tr, color1, bx, ty);
write_char(char_corner_round_bl, color1, tx, by);
write_char(char_corner_round_br, color2, bx, by);
for (char x = tx + 1; x < bx; x++)
{
write_char(char_line_h, color1, x, ty);
write_char(char_line_h, color2, x, by);
}
for (char y = ty + 1; y < by; y++)
{
write_char(char_line_v, color1, tx, y);
write_char(char_line_v, color2, bx, y);
}
}
void fill(char tx, char ty, char bx, char by, char c, char color)
{
for (char x = tx; x <= bx; x++)
for (char y = ty; y <= by; y++)
{
for (char y = ty; y <= by; y++)
{
write_char(c, color, x, y);
}
write_char_row(c, color, tx, y, (bx - tx) + 1);
}
}
void fill_bgcolor(char tx, char ty, char bx, char by, char bgcolor)
{
for (char y = ty; y <= by; y++)
{
write_bgcol_row(bgcolor, tx, y, (bx - tx) + 1);
}
}
void fill_bgcol(char tx, char ty, char bx, char by, char color)
{
for (char y = ty; y <= by; y++)
{
write_bgcol_row(color, tx, y, bx - tx);
}
}
@@ -168,65 +306,16 @@ void page_border(char color)
panel(0, 0, 39, 29, color);
}
char color_pad_outline = 0xFE;
// Draw game pad outline
void draw_pad(char xo, char yo)
void draw_charactermap()
{
// Outline
write_char(134, color_pad_outline, xo, yo + 1);
for (char x = 1; x < 26; x++)
char c = 94;
for (char y = 0; y < 29; y += 2)
{
write_char(135, color_pad_outline, xo + x, yo + 1);
for (char x = 0; x < 39; x += 4)
{
write_stringf("%d", 0xFF, x, y, c);
write_char(c, 0, x, y + 1);
c++;
}
}
write_char(136, color_pad_outline, xo + 26, yo + 1);
for (char y = 2; y < 5; y++)
{
write_char(137, color_pad_outline, xo, yo + y);
write_char(137, color_pad_outline, xo + 26, yo + y);
}
write_char(139, color_pad_outline, xo, yo + 5);
write_char(138, color_pad_outline, xo + 26, yo + 5);
write_char(138, color_pad_outline, xo + 8, yo + 5);
write_char(139, color_pad_outline, xo + 18, yo + 5);
write_char(134, color_pad_outline, xo + 8, yo + 4);
write_char(136, color_pad_outline, xo + 18, yo + 4);
for (char x = 1; x < 8; x++)
{
write_char(135, color_pad_outline, xo + x, yo + 5);
}
for (char x = 9; x < 18; x++)
{
write_char(135, color_pad_outline, xo + x, yo + 4);
}
for (char x = 19; x < 26; x++)
{
write_char(135, color_pad_outline, xo + x, yo + 5);
}
// Shoulders
write_char(134, color_pad_outline, xo + 1, yo);
write_char(136, color_pad_outline, xo + 5, yo);
write_char(134, color_pad_outline, xo + 21, yo);
write_char(136, color_pad_outline, xo + 25, yo);
}
char color_analog_grid = 0x23;
// Draw game pad outline
void draw_analog(char xo, char yo, char xs, char ys)
{
panel(xo, yo, xo + xs, yo + ys, 0xFF);
fill(xo + 1, yo + 1, xo + xs - 1, yo + ys - 1, 27, color_analog_grid);
char mx = xo + (xs / 2);
char my = yo + (ys / 2);
for (char x = xo + 1; x < xo + xs; x++)
{
write_char(129, color_analog_grid, x, my);
}
for (char y = yo + 1; y < yo + ys; y++)
{
write_char(131, color_analog_grid, mx, y);
}
write_char('+', color_analog_grid, mx, my);
}

111
src/ui_custom.c Normal file
View File

@@ -0,0 +1,111 @@
/*============================================================================
Input Test - Custom UI functions
Author: Jim Gregory - https://github.com/JimmyStones/
Version: 1.0
Date: 2021-07-12
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
===========================================================================*/
#pragma once
#include <stdbool.h>
#define color_pad_outline 0xFE
void page_frame(bool showMenuButton, bool showContinueButton)
{
bool footer = showMenuButton || showContinueButton;
clear_chars(0);
write_string("MiSTer Input Tester", 0b11111111, 11, 1);
panel_shaded(0, 0, 39, 2, 0b00000111, 0b00000110, 0b00000100);
panel_shaded(0, 3, 39, footer ? 26 : 29, 0b10100100, 0b10100100, 0b01010010);
if (footer)
{
char buttons = showMenuButton && showContinueButton ? 2 : 1;
char x1 = buttons == 1 ? 39 : 19;
char m1 = buttons == 1 ? 20 : 10;
panel_shaded(0, 27, x1, 29, 0b11000000, 0b10000000, 0b01000000);
if (buttons == 2)
{
panel_shaded(x1 + 1, 27, 39, 29, 0b11000000, 0b10000000, 0b01000000);
}
if (showMenuButton)
{
write_string("Hold Select: Menu", 0b11011011, m1 - 9, 28);
m1 += 20;
}
if (showContinueButton)
{
write_string("Start: Continue", 0b11011011, m1 - 8, 28);
}
}
}
// Draw game pad outline
void draw_pad(char xo, char yo)
{
// Outline
write_char(char_corner_round_tl, color_pad_outline, xo, yo + 2);
write_char_row(char_line_h, color_pad_outline, xo + 1, yo + 2, 25);
write_char(char_corner_round_tr, color_pad_outline, xo + 26, yo + 2);
for (char y = 3; y < 8; y++)
{
write_char(char_line_v, color_pad_outline, xo, yo + y);
write_char(char_line_v, color_pad_outline, xo + 26, yo + y);
}
write_char(char_corner_round_bl, color_pad_outline, xo, yo + 8);
write_char(char_corner_round_br, color_pad_outline, xo + 26, yo + 8);
write_char(char_corner_round_br, color_pad_outline, xo + 8, yo + 8);
write_char(char_corner_round_bl, color_pad_outline, xo + 18, yo + 8);
write_char(char_corner_round_tl, color_pad_outline, xo + 8, yo + 7);
write_char(char_corner_round_tr, color_pad_outline, xo + 18, yo + 7);
write_char_row(char_line_h, color_pad_outline, xo + 1, yo + 8, 7);
write_char_row(char_line_h, color_pad_outline, xo + 9, yo + 7, 9);
write_char_row(char_line_h, color_pad_outline, xo + 19, yo + 8, 7);
// Shoulders
write_char(char_line_v, color_pad_outline, xo + 1, yo + 1);
write_char(char_t_up, color_pad_outline, xo + 1, yo + 2);
write_char(char_corner_round_tl, color_pad_outline, xo + 1, yo);
write_char_row(char_line_h, color_pad_outline, xo + 2, yo, 3);
write_char(char_line_v, color_pad_outline, xo + 5, yo + 1);
write_char(char_t_up, color_pad_outline, xo + 5, yo + 2);
write_char(char_corner_round_tr, color_pad_outline, xo + 5, yo);
write_char(char_line_v, color_pad_outline, xo + 21, yo + 1);
write_char(char_t_up, color_pad_outline, xo + 21, yo + 2);
write_char(char_corner_round_tl, color_pad_outline, xo + 21, yo);
write_char_row(char_line_h, color_pad_outline, xo + 22, yo, 3);
write_char(char_line_v, color_pad_outline, xo + 25, yo + 1);
write_char(char_t_up, color_pad_outline, xo + 25, yo + 2);
write_char(char_corner_round_tr, color_pad_outline, xo + 25, yo);
}
#define color_analog_grid 0x23
// Draw game pad outline
void draw_analog(char xo, char yo, char xs, char ys)
{
panel(xo, yo, xo + xs, yo + ys, 0xFF);
fill(xo + 1, yo + 1, xo + xs - 1, yo + ys - 1, char_dot, color_analog_grid);
char mx = xo + (xs / 2);
char my = yo + (ys / 2);
write_char_row(char_line_h, color_analog_grid, xo + 1, my, xs - 1);
for (char y = yo + 1; y < yo + ys; y++)
{
write_char(char_line_v, color_analog_grid, mx, y);
}
write_char(char_cross, color_analog_grid, mx, my);
}

View File

@@ -1,10 +1,10 @@
`timescale 1ns / 1ps
/*============================================================================
MiSTer test harness - Verilator emu module
Input Test - Verilator emu module
Author: Jim Gregory - https://github.com/JimmyStones/
Version: 0.1
Date: 2021-06-29
Version: 1.0
Date: 2021-07-12
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
@@ -61,6 +61,9 @@ module emu (
input [24:0] ps2_mouse,
input [15:0] ps2_mouse_ext, // 15:8 - reserved(additional buttons), 7:0 - wheel movements
// [31:0] - seconds since 1970-01-01 00:00:00, [32] - toggle with every change
input [32:0] timestamp,
output [7:0] VGA_R,
output [7:0] VGA_G,
output [7:0] VGA_B,
@@ -84,8 +87,8 @@ wire ce_pix;
jtframe_cen24 divider
(
.clk(clk_sys),
//.cen12(ce_pix), // <-- dodgy video speed for faster simulation, will cause bearable char map corruption
.cen4(ce_pix) // <-- correct video speed
.cen12(ce_pix), // <-- dodgy video speed for faster simulation, will cause bearable char map corruption
//.cen4(ce_pix) // <-- correct video speed
);
/* verilator lint_on PINMISSING */
@@ -110,7 +113,8 @@ system system(
.paddle({paddle_5,paddle_4,paddle_3,paddle_2,paddle_1,paddle_0}),
.spinner({7'b0,spinner_5,7'b0,spinner_4,7'b0,spinner_3,7'b0,spinner_2,7'b0,spinner_1,7'b0,spinner_0}),
.ps2_key(ps2_key),
.ps2_mouse({ps2_mouse_ext,7'b0,ps2_mouse})
.ps2_mouse({ps2_mouse_ext,7'b0,ps2_mouse}),
.timestamp(timestamp)
);
endmodule

View File

@@ -10,6 +10,7 @@
#define WIN32
#include <dinput.h>
#endif
#include <chrono>
#include "sim_console.h"
#include "sim_bus.h"
@@ -45,7 +46,7 @@ const int input_b = 5;
const int input_x = 6;
const int input_y = 7;
const int input_l = 8;
const int input_r= 9;
const int input_r = 9;
const int input_select = 10;
const int input_start = 11;
@@ -69,15 +70,30 @@ int multi_step_amount = 1024;
// --------------
Vemu* top = NULL;
vluint64_t main_time = 0; // Current simulation time.
double sc_time_stamp() { // Called by $time in Verilog.
vluint64_t main_time = 0; // Current simulation time.
vluint32_t timestamp = 0; // Simulated Unix timestamp.
vluint16_t timestamp_ticksperms = 24000; // Number of simulation ticks per simulated millisecond
vluint16_t timestamp_ticks = 0; // Ticks left until next ms
unsigned short timestamp_updatefreq = 5000; // Only update sim every 5 seconds
unsigned short timestamp_update = 0; // Ms to next update.
bool timestamp_clock = 1; // Timestamp update clock
double sc_time_stamp() { // Called by $time in Verilog.
return main_time;
}
SimClock clk_sys(1);
long GetTime() {
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
void resetSim() {
main_time = 0;
timestamp = GetTime();
top->reset = 1;
clk_sys.Reset();
}
@@ -94,6 +110,24 @@ int verilate() {
// Clock dividers
clk_sys.Tick();
// Increment timestamp
timestamp_ticks ++;
if (timestamp_ticks >= timestamp_ticksperms) {
timestamp_ticks -= timestamp_ticksperms;
timestamp++;
top->timestamp = timestamp;
timestamp_clock = !timestamp_clock;
if (timestamp_clock) {
timestamp_update--;
if (timestamp_update <= 0) {
top->timestamp |= ((uint64_t)1) << 32;
timestamp_update = timestamp_updatefreq;
}
}
}
// Set system clock in core
top->clk_sys = clk_sys.clk;
@@ -167,7 +201,7 @@ int main(int argc, char** argv, char** env) {
input.SetMapping(input_r, DIK_W); // R
input.SetMapping(input_select, DIK_1); // Select
input.SetMapping(input_start, DIK_2); // Start
#else
input.SetMapping(input_up, SDL_SCANCODE_UP);
input.SetMapping(input_right, SDL_SCANCODE_RIGHT);
@@ -185,6 +219,9 @@ int main(int argc, char** argv, char** env) {
// Setup video output
if (video.Initialise(windowTitle) == 1) { return 1; }
// Initial reset
resetSim();
#ifdef WIN32
MSG msg;
ZeroMemory(&msg, sizeof(msg));
@@ -243,6 +280,7 @@ int main(int argc, char** argv, char** env) {
ImGui::Checkbox("Flip V", &video.output_vflip);
ImGui::Text("main_time: %d frame_count: %d sim FPS: %f", main_time, video.count_frame, video.stats_fps);
ImGui::Text("timestamp: %d actual ms: %d frame_ms: %d ", timestamp, timestamp/1000, video.count_frame * 1000);
ImGui::Text("minx: %d maxx: %d miny: %d maxy: %d", video.stats_xMin, video.stats_xMax, video.stats_yMin, video.stats_yMax);
// Draw VGA output
@@ -328,7 +366,8 @@ int main(int argc, char** argv, char** env) {
for (char b = 8; b < 16; b++) {
top->spinner_0 &= ~(1UL << b);
}
if (spinner_toggle) { top->spinner_0 |= 1UL << 8; } }
if (spinner_toggle) { top->spinner_0 |= 1UL << 8; }
}
//top->spinner_1 -= 1;
//top->spinner_2 += 1;
//top->spinner_3 -= 1;

View File

@@ -1,25 +1,16 @@
set -e
if grep -qEi "(Microsoft|WSL)" /proc/version &> /dev/null ; then
$VERILATOR_ROOT/bin/verilator \
-cc \
-exe \
--public \
verilator \
--cc \
--compiler msvc +define+SIMULATION=1 \
-O3 --x-assign fast --x-initial fast --noassert \
-Wno-TIMESCALEMOD \
--converge-limit 6000 \
--top-module emu sim.v \
../rtl/dpram.v \
../rtl/spram.v \
../rtl/JTFRAME/jtframe_vtimer.v \
../rtl/JTFRAME/jtframe_cen24.v \
../rtl/system.v \
../rtl/tv80/tv80_core.v \
../rtl/tv80/tv80_alu.v \
../rtl/tv80/tv80_mcode.v \
../rtl/tv80/tv80_reg.v \
../rtl/tv80/tv80n.v \
../rtl/tv80/tv80s.v
-I../rtl \
-I../rtl/JTFRAME \
-I../rtl/tv80
else
echo "not running on windows"
fi