mirror of
https://github.com/MiSTer-devel/InputTest_MiSTer.git
synced 2026-04-19 03:04:18 +00:00
603 lines
12 KiB
C
603 lines
12 KiB
C
/*============================================================================
|
|
MiSTer test harness OS - Main application
|
|
|
|
Author: Jim Gregory - https://github.com/JimmyStones/
|
|
Version: 0.1
|
|
Date: 2021-06-29
|
|
|
|
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/>.
|
|
===========================================================================*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include "sys.c"
|
|
#include "ui.c"
|
|
#include "ps2.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_INPUTTESTERADV 2
|
|
#define STATE_INPUTTESTERADV 3
|
|
|
|
#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_INPUTTESTERADV;
|
|
char nextstate = STATE_START_INPUTTESTERADV;
|
|
|
|
// 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 __at(0xC200) joystick_last[12];
|
|
signed char __at(0xC210) ax_last[6];
|
|
signed char __at(0xC220) ay_last[6];
|
|
unsigned char __at(0xC230) px_last[6];
|
|
signed char __at(0xC240) sx_toggle_last[6];
|
|
signed char __at(0xC250) sx_last[6];
|
|
unsigned long __at(0xC260) 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;
|
|
|
|
// DPAD tracker
|
|
bool bdown_left = 0;
|
|
bool bdown_left_last = 0;
|
|
bool bdown_right = 0;
|
|
bool bdown_right_last = 0;
|
|
bool bdown_up = 0;
|
|
bool bdown_up_last = 0;
|
|
bool bdown_down = 0;
|
|
bool bdown_down_last = 0;
|
|
char history[4];
|
|
|
|
// Draw static elements for input test page
|
|
void page_inputtester_adv()
|
|
{
|
|
clear_chars(0);
|
|
page_border(0b00000111);
|
|
|
|
write_string("- MiSTer Input Tester -", 0b11100011, 8, 1);
|
|
write_string("RLDUABXYLRsSCZ", 0xFF, 7, 3);
|
|
write_string("AX", 0xFF, 26, 3);
|
|
write_string("AY", 0xFF, 31, 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()
|
|
{
|
|
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 advanced inputtester state and draw static elements
|
|
void start_inputtester_adv()
|
|
{
|
|
state = STATE_INPUTTESTERADV;
|
|
|
|
// Draw page
|
|
page_inputtester_adv();
|
|
|
|
// 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 < 4; h++)
|
|
{
|
|
history[h - 1] = history[h];
|
|
}
|
|
history[3] = new;
|
|
}
|
|
|
|
void handle_codes()
|
|
{
|
|
// Track input history of P1 DPAD for secret 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] == 4 && history[1] == 2 && history[2] == 3 && history[3] == 1)
|
|
{
|
|
nextstate = STATE_START_ATTRACT;
|
|
pushhistory(0);
|
|
start_fadeout();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Input tester state
|
|
void inputtester_adv()
|
|
{
|
|
|
|
// 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)
|
|
{
|
|
|
|
// 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;
|
|
for (char i = 0; i < 8; 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, 24, 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] / 16);
|
|
}
|
|
else
|
|
{
|
|
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_adv();
|
|
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 = 4;
|
|
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
|
|
{
|
|
start_gameplay();
|
|
}
|
|
}
|
|
|
|
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()
|
|
{
|
|
chram_size = chram_cols * chram_rows;
|
|
while (1)
|
|
{
|
|
hsync = input0 & 0x80;
|
|
vsync = input0 & 0x40;
|
|
switch (state)
|
|
{
|
|
case STATE_START_INPUTTESTERADV:
|
|
start_inputtester_adv();
|
|
break;
|
|
case STATE_INPUTTESTERADV:
|
|
inputtester_adv();
|
|
break;
|
|
|
|
case STATE_FADEOUT:
|
|
fadeout();
|
|
break;
|
|
case STATE_FADEIN:
|
|
fadein();
|
|
break;
|
|
|
|
case STATE_START_ATTRACT:
|
|
start_attract();
|
|
break;
|
|
case STATE_ATTRACT:
|
|
attract();
|
|
break;
|
|
|
|
case STATE_START_GAME:
|
|
start_gameplay();
|
|
break;
|
|
case STATE_GAME:
|
|
gameplay();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
hsync_last = hsync;
|
|
vsync_last = vsync;
|
|
}
|
|
}
|