Initial Commit.

This commit is contained in:
sorgelig
2017-06-14 02:47:05 +08:00
commit 96f7caaf71
56 changed files with 17806 additions and 0 deletions

28
.gitignore vendored Normal file
View File

@@ -0,0 +1,28 @@
# ignore backup files
*~
# ignore vi swapfiles
.*.swp
# ignore compiled python
*pyc
# ignore some backup files
*bak
# ignore some c files
*\.o
*\.d
# ignore dropbox files
.dropbox.attr
# ignore directories
Debug
*.lst
*.elf
*.map
*.user
.vs
MiSTer

39
Makefile Normal file
View File

@@ -0,0 +1,39 @@
# makefile to fail if any command in pipe is failed.
SHELL = /bin/bash -o pipefail
# using gcc version 5.4.1 20161213 (Linaro GCC 5.4-2017.01-rc2)
BASE = arm-linux-gnueabihf
CC = $(BASE)-gcc
LD = $(CC)
STRIP = $(BASE)-strip
PRJ = MiSTer
SRC = $(wildcard *.c)
OBJ = $(SRC:.c=.o)
DEP = $(SRC:.c=.d)
CFLAGS = $(DFLAGS) -c -O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE -DVDATE=\"`date +"%y%m%d"`\"
LFLAGS = -lc
$(PRJ): $(OBJ)
@$(info $@)
@$(LD) $(LFLAGS) -o $@ $+
@$(STRIP) $@
clean:
rm -f *.d *.o *.elf *.map *.lst *.bak *.rej *.org *.user *~ $(PRJ)
rm -rf obj .vs DTAR* x64
%.o: %.c
@$(info $<)
@$(CC) $(CFLAGS) -o $@ -c $< 2>&1 | sed -e 's/\(.[a-zA-Z]\+\):\([0-9]\+\):\([0-9]\+\):/\1(\2,\ \3):/g'
-include $(DEP)
%.d: %.c
@$(CC) $(DFLAGS) -MM $< -MT $@ -MT $*.o -MF $@ 2>&1 | sed -e 's/\(.[a-zA-Z]\+\):\([0-9]\+\):\([0-9]\+\):/\1(\2,\ \3):/g'
# Ensure correct time stamp
main.o: $(filter-out main.o, $(OBJ))

6
MiSTer.ini Normal file
View File

@@ -0,0 +1,6 @@
[MiSTer]
key_menu_as_rgui=0 ; set to 1 to make the MENU key map to RGUI in Minimig (e.g. for Right Amiga)
forced_scandoubler=0 ; set to 1 to run scandoubler on VGA output always (depends on core).
ypbpr=0 ; set to 1 for YPbPr on VGA output.
composite_sync=0 ; set to 1 for composite sync on HSync signal of VGA output.
vga_scaler=1 ; set to 1 to connect VGA to scaler output.

19
MiSTer.sln Normal file
View File

@@ -0,0 +1,19 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MiSTer", "MiSTer.vcxproj", "{C1D6BEA2-1469-4FBC-8A27-A82BDE9041AC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C1D6BEA2-1469-4FBC-8A27-A82BDE9041AC}.Debug|x86.ActiveCfg = Debug|Win32
{C1D6BEA2-1469-4FBC-8A27-A82BDE9041AC}.Debug|x86.Build.0 = Debug|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

101
MiSTer.vcxproj Normal file
View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{C1D6BEA2-1469-4FBC-8A27-A82BDE9041AC}</ProjectGuid>
<Keyword>MakeFileProj</Keyword>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<NMakeBuildCommandLine>git.lnk ./build.sh</NMakeBuildCommandLine>
<NMakeOutput>MiSTer</NMakeOutput>
<NMakeCleanCommandLine>git.lnk ./clean.sh</NMakeCleanCommandLine>
<NMakePreprocessorDefinitions>WIN32;VDATE="000000";_FILE_OFFSET_BITS=64;_LARGEFILE64_SOURCE;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>c:\Work\Git\opt\gcc54\arm-linux-gnueabihf\libc\usr\include;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>
<OutDir>$(TEMP)</OutDir>
<IntDir>$(TEMP)</IntDir>
<AdditionalOptions>
</AdditionalOptions>
</PropertyGroup>
<ItemDefinitionGroup>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="archie.c" />
<ClCompile Include="boot.c" />
<ClCompile Include="config.c" />
<ClCompile Include="file_io.c" />
<ClCompile Include="fdd.c" />
<ClCompile Include="fpga_io.c" />
<ClCompile Include="hardware.c" />
<ClCompile Include="hdd.c" />
<ClCompile Include="ikbd.c" />
<ClCompile Include="ini_parser.c" />
<ClCompile Include="input.c" />
<ClCompile Include="main.c" />
<ClCompile Include="menu.c" />
<ClCompile Include="mist_cfg.c" />
<ClCompile Include="osd.c" />
<ClCompile Include="spi.c" />
<ClCompile Include="state.c" />
<ClCompile Include="tos.c" />
<ClCompile Include="user_io.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="archie.h" />
<ClInclude Include="boot.h" />
<ClInclude Include="charrom.h" />
<ClInclude Include="config.h" />
<ClInclude Include="debug.h" />
<ClInclude Include="errors.h" />
<ClInclude Include="file_io.h" />
<ClInclude Include="fdd.h" />
<ClInclude Include="fpga_base_addr_ac5.h" />
<ClInclude Include="fpga_io.h" />
<ClInclude Include="fpga_manager.h" />
<ClInclude Include="fpga_nic301.h" />
<ClInclude Include="fpga_reset_manager.h" />
<ClInclude Include="fpga_system_manager.h" />
<ClInclude Include="hardware.h" />
<ClInclude Include="hdd.h" />
<ClInclude Include="hdd_internal.h" />
<ClInclude Include="ikbd.h" />
<ClInclude Include="ini_parser.h" />
<ClInclude Include="input.h" />
<ClInclude Include="keycodes.h" />
<ClInclude Include="logo.h" />
<ClInclude Include="menu.h" />
<ClInclude Include="mist_cfg.h" />
<ClInclude Include="osd.h" />
<ClInclude Include="spi.h" />
<ClInclude Include="state.h" />
<ClInclude Include="tos.h" />
<ClInclude Include="user_io.h" />
</ItemGroup>
<ItemGroup>
<None Include="build.sh" />
<None Include="clean.sh" />
<None Include="Makefile" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

166
MiSTer.vcxproj.filters Normal file
View File

@@ -0,0 +1,166 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="archie.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="boot.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="config.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="fdd.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="fpga_io.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="hardware.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="hdd.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ikbd.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ini_parser.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="input.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="main.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="menu.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="mist_cfg.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="osd.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="spi.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="state.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="tos.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="user_io.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="file_io.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="archie.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="boot.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="charrom.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="config.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="debug.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="errors.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="fdd.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="fpga_base_addr_ac5.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="fpga_io.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="fpga_manager.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="fpga_nic301.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="fpga_reset_manager.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="fpga_system_manager.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="hardware.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="hdd.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="hdd_internal.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ikbd.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ini_parser.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="input.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="keycodes.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="logo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="menu.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="mist_cfg.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="osd.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="spi.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="state.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="tos.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="user_io.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="file_io.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="build.sh" />
<None Include="Makefile" />
<None Include="clean.sh" />
</ItemGroup>
</Project>

603
archie.c Normal file
View File

@@ -0,0 +1,603 @@
#include "stdio.h"
#include "string.h"
#include "hardware.h"
#include "fpga_io.h"
#include "menu.h"
#include "archie.h"
#include "debug.h"
#define MAX_FLOPPY 4
#define CONFIG_FILENAME "ARCHIE.CFG"
typedef struct
{
unsigned long system_ctrl; // system control word
char rom_img[1024]; // rom image file name
} archie_config_t;
static archie_config_t config;
fileTYPE floppy[MAX_FLOPPY];
#define ARCHIE_FILE_TX 0x53
#define ARCHIE_FILE_TX_DAT 0x54
#define ARCHIE_FDC_GET_STATUS 0x55
#define ARCHIE_FDC_TX_DATA 0x56
#define ARCHIE_FDC_SET_STATUS 0x57
#define archie_debugf(a, ...) iprintf("\033[1;31mARCHIE: " a "\033[0m\n", ##__VA_ARGS__)
// #define archie_debugf(a, ...)
#define archie_x_debugf(a, ...) iprintf("\033[1;32mARCHIE: " a "\033[0m\n", ##__VA_ARGS__)
enum state {
STATE_HRST, STATE_RAK1, STATE_RAK2, STATE_IDLE,
STATE_WAIT4ACK1, STATE_WAIT4ACK2, STATE_HOLD_OFF
} kbd_state;
// archie keyboard controller commands
#define HRST 0xff
#define RAK1 0xfe
#define RAK2 0xfd
#define RQPD 0x40 // mask 0xf0
#define PDAT 0xe0 // mask 0xf0
#define RQID 0x20
#define KBID 0x80 // mask 0xc0
#define KDDA 0xc0 // new key down data, mask 0xf0
#define KUDA 0xd0 // new key up data, mask 0xf0
#define RQMP 0x22 // request mouse data
#define MDAT 0x00 // mouse data, mask 0x80
#define BACK 0x3f
#define NACK 0x30 // disable kbd scan, disable mouse
#define SACK 0x31 // enable kbd scan, disable mouse
#define MACK 0x32 // disable kbd scan, enable mouse
#define SMAK 0x33 // enable kbd scan, enable mouse
#define LEDS 0x00 // mask 0xf8
#define PRST 0x21 // nop
#define QUEUE_LEN 8
static unsigned char tx_queue[QUEUE_LEN][2];
static unsigned char tx_queue_rptr, tx_queue_wptr;
#define QUEUE_NEXT(a) ((a+1)&(QUEUE_LEN-1))
static unsigned long ack_timeout;
static short mouse_x, mouse_y;
#define FLAG_SCAN_ENABLED 0x01
#define FLAG_MOUSE_ENABLED 0x02
static unsigned char flags;
// #define HOLD_OFF_TIME 2
#ifdef HOLD_OFF_TIME
static unsigned long hold_off_timer;
#endif
static char sector_buffer[1024];
static void nice_name(char *dest, char *src)
{
char *c;
// copy and append nul
strncpy(dest, src, 8);
for (c = dest + 7; *c == ' '; c--); c++;
*c++ = '.';
strncpy(c, src + 8, 3);
for (c += 2; *c == ' '; c--); c++;
*c++ = '\0';
}
static char buffer[17]; // local buffer to assemble file name (8+.+3+\0)
char *archie_get_rom_name(void)
{
nice_name(buffer, config.rom_img);
return buffer;
}
char *archie_get_floppy_name(char i)
{
if (!floppy[i].size)
strcpy(buffer, "* no disk *");
else
nice_name(buffer, floppy[i].name);
return buffer;
}
void archie_save_config(void)
{
FileSave(CONFIG_FILENAME, &config, sizeof(config));
}
void archie_send_file(unsigned char id, char *name)
{
archie_debugf("Sending file with id %d", id);
fileTYPE file;
if (!FileOpen(&file, name)) return;
// prepare transmission of new file
EnableFpga();
spi8(ARCHIE_FILE_TX);
spi8(id);
DisableFpga();
unsigned long time = GetTimer(0);
iprintf("[");
unsigned short i, blocks = file.size / 512;
for (i = 0; i<blocks; i++) {
if (!(i & 127)) iprintf("*");
DISKLED_ON;
FileRead(&file, sector_buffer);
DISKLED_OFF;
EnableFpga();
spi8(ARCHIE_FILE_TX_DAT);
spi_block_write(sector_buffer, 0);
DisableFpga();
// still bytes to send? read next sector
if (i != blocks - 1)
FileNextSector(&file);
}
FileClose(&file);
iprintf("]\n");
time = GetTimer(0) - time;
archie_debugf("Uploaded in %lu ms", time >> 20);
// signal end of transmission
EnableFpga();
spi8(ARCHIE_FILE_TX);
spi8(0x00);
DisableFpga();
}
void archie_fdc_set_status(void)
{
int i;
// send status bytes for all four possible floppies
EnableFpga();
spi8(ARCHIE_FDC_SET_STATUS);
for (i = 0; i<MAX_FLOPPY; i++)
{
unsigned char floppy_status = 0x00;
if (floppy[i].size) floppy_status |= 1;
spi8(floppy_status);
}
DisableFpga();
}
void archie_set_floppy(char i, char *name)
{
if (!name)
{
archie_debugf("Floppy %d eject", i);
FileClose(&floppy[i]);
floppy[i].size = 0;
}
else
{
archie_debugf("Floppy %d insert %s", i, name);
FileOpen(&floppy[i], name);
}
// update floppy status in fpga
archie_fdc_set_status();
}
char archie_floppy_is_inserted(char i)
{
return(floppy[i].size != 0);
}
void archie_set_rom(char *name)
{
if (!name) return;
// save file name
strcpy(config.rom_img, name);
archie_send_file(0x01, name);
}
static void archie_kbd_enqueue(unsigned char state, unsigned char byte)
{
if (QUEUE_NEXT(tx_queue_wptr) == tx_queue_rptr)
{
archie_debugf("KBD tx queue overflow");
return;
}
archie_debugf("KBD ENQUEUE %x (%x)", byte, state);
tx_queue[tx_queue_wptr][0] = state;
tx_queue[tx_queue_wptr][1] = byte;
tx_queue_wptr = QUEUE_NEXT(tx_queue_wptr);
}
static void archie_kbd_tx(unsigned char state, unsigned char byte)
{
archie_debugf("KBD TX %x (%x)", byte, state);
spi_uio_cmd_cont(0x05);
spi8(byte);
DisableIO();
kbd_state = state;
ack_timeout = GetTimer(10); // 10ms timeout
}
static void archie_kbd_send(unsigned char state, unsigned char byte)
{
// don't send if we are waiting for an ack
if ((kbd_state != STATE_WAIT4ACK1) && (kbd_state != STATE_WAIT4ACK2))
archie_kbd_tx(state, byte);
else
archie_kbd_enqueue(state, byte);
}
static void archie_kbd_reset(void)
{
archie_debugf("KBD reset");
tx_queue_rptr = tx_queue_wptr = 0;
kbd_state = STATE_HRST;
mouse_x = mouse_y = 0;
flags = 0;
}
void archie_init(void)
{
fileTYPE file;
char i;
archie_debugf("init");
// set config defaults
config.system_ctrl = 0;
strcpy(config.rom_img, "RISCOS.ROM");
// try to load config from card
if (FileOpen(&file, CONFIG_FILENAME))
{
if (file.size == sizeof(archie_config_t))
{
FileReadAdv(&file, &config, sizeof(archie_config_t));
}
else
archie_debugf("Unexpected config size %d != %d", file.size, sizeof(archie_config_t));
FileClose(&file);
}
else
archie_debugf("No %s config found", CONFIG_FILENAME);
// upload rom file
archie_set_rom(config.rom_img);
// upload ext file
archie_send_file(0x02, "RISCOS.EXT");
// try to open default floppies
for (i = 0; i<MAX_FLOPPY; i++)
{
char fdc_name[] = "FLOPPY0.ADF";
fdc_name[6] = '0' + i;
if (FileOpen(&floppy[i], fdc_name))
archie_debugf("Inserted floppy %d with %d bytes", i, floppy[i].size);
else
floppy[i].size = 0;
}
// update floppy status in fpga
archie_fdc_set_status();
archie_kbd_send(STATE_RAK1, HRST);
ack_timeout = GetTimer(20); // give archie 20ms to reply
}
void archie_kbd(unsigned short code)
{
archie_debugf("KBD key code %x", code);
// don't send anything yet if we are still in reset state
if (kbd_state <= STATE_RAK2)
{
archie_debugf("KBD still in reset");
return;
}
// ignore any key event if key scanning is disabled
if (!(flags & FLAG_SCAN_ENABLED))
{
archie_debugf("KBD keyboard scan is disabled!");
return;
}
// select prefix for up or down event
unsigned char prefix = (code & 0x8000) ? KUDA : KDDA;
archie_kbd_send(STATE_WAIT4ACK1, prefix | (code >> 4));
archie_kbd_send(STATE_WAIT4ACK2, prefix | (code & 0x0f));
}
void archie_mouse(unsigned char b, char x, char y)
{
archie_debugf("KBD MOUSE X:%d Y:%d B:%d", x, y, b);
// max values -64 .. 63
mouse_x += x;
if (mouse_x > 63) mouse_x = 63;
if (mouse_x < -64) mouse_x = -64;
mouse_y -= y;
if (mouse_y > 63) mouse_y = 63;
if (mouse_y < -64) mouse_y = -64;
// don't send anything yet if we are still in reset state
if (kbd_state <= STATE_RAK2)
{
archie_debugf("KBD still in reset");
return;
}
// ignore any mouse movement if mouse is disabled or if nothing to report
if ((flags & FLAG_MOUSE_ENABLED) && (mouse_x || mouse_y))
{
// send asap if no pending byte
if (kbd_state == STATE_IDLE) {
archie_kbd_send(STATE_WAIT4ACK1, mouse_x & 0x7f);
archie_kbd_send(STATE_WAIT4ACK2, mouse_y & 0x7f);
mouse_x = mouse_y = 0;
}
}
// ignore mouse buttons if key scanning is disabled
if (flags & FLAG_SCAN_ENABLED)
{
static const uint8_t remap[] = { 0, 2, 1 };
static unsigned char buts = 0;
uint8_t s;
// map all three buttons
for (s = 0; s<3; s++)
{
uint8_t mask = (1 << s);
if ((b&mask) != (buts&mask))
{
unsigned char prefix = (b&mask) ? KDDA : KUDA;
archie_kbd_send(STATE_WAIT4ACK1, prefix | 0x07);
archie_kbd_send(STATE_WAIT4ACK2, prefix | remap[s]);
}
}
buts = b;
}
}
static void archie_check_queue(void)
{
if (tx_queue_rptr == tx_queue_wptr)
return;
archie_kbd_tx(tx_queue[tx_queue_rptr][0], tx_queue[tx_queue_rptr][1]);
tx_queue_rptr = QUEUE_NEXT(tx_queue_rptr);
}
void archie_handle_kbd(void)
{
#ifdef HOLD_OFF_TIME
if ((kbd_state == STATE_HOLD_OFF) && CheckTimer(hold_off_timer)) {
archie_debugf("KBD resume after hold off");
kbd_state = STATE_IDLE;
archie_check_queue();
}
#endif
// timeout waiting for ack?
if ((kbd_state == STATE_WAIT4ACK1) || (kbd_state == STATE_WAIT4ACK2)) {
if (CheckTimer(ack_timeout)) {
if (kbd_state == STATE_WAIT4ACK1)
archie_debugf(">>>> KBD ACK TIMEOUT 1ST BYTE <<<<");
if (kbd_state == STATE_WAIT4ACK2)
archie_debugf(">>>> KBD ACK TIMEOUT 2ND BYTE <<<<");
kbd_state = STATE_IDLE;
}
}
// timeout in reset sequence?
if (kbd_state <= STATE_RAK2)
{
if (CheckTimer(ack_timeout))
{
archie_debugf("KBD timeout in reset state");
archie_kbd_send(STATE_RAK1, HRST);
ack_timeout = GetTimer(20); // 20ms timeout
}
}
spi_uio_cmd_cont(0x04);
if (spi_in() == 0xa1)
{
unsigned char data = spi_in();
DisableIO();
archie_debugf("KBD RX %x", data);
switch (data) {
// arm requests reset
case HRST:
archie_kbd_reset();
archie_kbd_send(STATE_RAK1, HRST);
ack_timeout = GetTimer(20); // 20ms timeout
break;
// arm sends reset ack 1
case RAK1:
if (kbd_state == STATE_RAK1) {
archie_kbd_send(STATE_RAK2, RAK1);
ack_timeout = GetTimer(20); // 20ms timeout
}
else
kbd_state = STATE_HRST;
break;
// arm sends reset ack 2
case RAK2:
if (kbd_state == STATE_RAK2) {
archie_kbd_send(STATE_IDLE, RAK2);
ack_timeout = GetTimer(20); // 20ms timeout
}
else
kbd_state = STATE_HRST;
break;
// arm request keyboard id
case RQID:
archie_kbd_send(STATE_IDLE, KBID | 1);
break;
// arm acks first byte
case BACK:
if (kbd_state != STATE_WAIT4ACK1)
archie_debugf("KBD unexpected BACK");
#ifdef HOLD_OFF_TIME
// wait some time before sending next byte
archie_debugf("KBD starting hold off");
kbd_state = STATE_HOLD_OFF;
hold_off_timer = GetTimer(10);
#else
kbd_state = STATE_IDLE;
archie_check_queue();
#endif
break;
// arm acks second byte
case NACK:
case SACK:
case MACK:
case SMAK:
if (((data == SACK) || (data == SMAK)) && !(flags & FLAG_SCAN_ENABLED)) {
archie_debugf("KBD Enabling key scanning");
flags |= FLAG_SCAN_ENABLED;
}
if (((data == NACK) || (data == MACK)) && (flags & FLAG_SCAN_ENABLED)) {
archie_debugf("KBD Disabling key scanning");
flags &= ~FLAG_SCAN_ENABLED;
}
if (((data == MACK) || (data == SMAK)) && !(flags & FLAG_MOUSE_ENABLED)) {
archie_debugf("KBD Enabling mouse");
flags |= FLAG_MOUSE_ENABLED;
}
if (((data == NACK) || (data == SACK)) && (flags & FLAG_MOUSE_ENABLED)) {
archie_debugf("KBD Disabling mouse");
flags &= ~FLAG_MOUSE_ENABLED;
}
// wait another 10ms before sending next byte
#ifdef HOLD_OFF_TIME
archie_debugf("KBD starting hold off");
kbd_state = STATE_HOLD_OFF;
hold_off_timer = GetTimer(10);
#else
kbd_state = STATE_IDLE;
archie_check_queue();
#endif
break;
}
}
else
DisableIO();
}
static unsigned char fdc_buffer[1024];
void archie_handle_fdc(void)
{
static unsigned char old_status[4] = { 0,0,0,0 };
unsigned char status[4];
// read status
EnableFpga();
spi8(ARCHIE_FDC_GET_STATUS);
status[0] = spi_in();
status[1] = spi_in();
status[2] = spi_in();
status[3] = spi_in();
DisableFpga();
if (memcmp(status, old_status, 4) != 0)
{
archie_x_debugf("status changed to %x %x %x %x",
status[0], status[1], status[2], status[3]);
memcpy(old_status, status, 4);
// top four bits must be magic marker 1010
if (((status[0] & 0xf0) == 0xa0) && (status[0] & 1))
{
archie_x_debugf("DIO: BUSY with commmand %lx", status[1]);
// check for read sector command
if ((status[1] & 0xe0) == 0x80)
{
if (status[0] & 2)
{
int floppy_map = status[3] >> 4;
int side = (status[2] & 0x80) ? 0 : 1;
int track = status[2] & 0x7f;
int sector = status[3] & 0x0f;
unsigned long lba = 2 * (10 * track + 5 * side + sector);
int floppy_index = -1;
// allow only single floppy drives to be selected
int i;
for (i = 0; i<MAX_FLOPPY; i++)
if (floppy_map == (0x0f ^ (1 << i)))
floppy_index = i;
if (floppy_index < 0)
archie_x_debugf("DIO: unexpected floppy_map %x", floppy_map);
else
{
fileTYPE *f = &floppy[floppy_index];
archie_x_debugf("DIO: floppy %d sector read SD%d T%d S%d -> %ld",
floppy_index, side, track, sector, lba);
if (!f->size)
archie_x_debugf("DIO: floppy not inserted. Core should not do this!!");
else {
DISKLED_ON;
// read two consecutive sectors
FileSeek(f, lba, SEEK_SET);
FileRead(f, fdc_buffer);
FileNextSector(f);
FileRead(f, fdc_buffer + 512);
DISKLED_OFF;
EnableFpga();
spi8(ARCHIE_FDC_TX_DATA);
spi_write(fdc_buffer, 1024, 0);
DisableFpga();
}
}
}
}
}
}
}
void archie_poll(void)
{
archie_handle_kbd();
archie_handle_fdc();
}

17
archie.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef ARCHIE_H
#define ARCHIE_H
#include "file_io.h"
void archie_init(void);
void archie_poll(void);
void archie_kbd(unsigned short code);
void archie_mouse(unsigned char b, char x, char y);
char *archie_get_rom_name(void);
char *archie_get_floppy_name(char b);
void archie_set_rom(char *);
void archie_set_floppy(char i, char *);
char archie_floppy_is_inserted(char i);
void archie_save_config(void);
#endif // ARCHIE_H

501
boot.c Normal file
View File

@@ -0,0 +1,501 @@
// boot.c
// bootscreen functions
// 2014, rok.krajnc@gmail.com
#include "string.h"
#include "stdio.h"
#include "boot.h"
#include "hardware.h"
#include "osd.h"
#include "spi.h"
#include "file_io.h"
#include "config.h"
#include "fdd.h"
static uint8_t buffer[1024];
static void mem_upload_init(unsigned long addr)
{
spi_osd_cmd32le_cont(OSD_CMD_WR, addr);
}
static void mem_upload_fini()
{
DisableOsd();
}
static void mem_write16(unsigned short x)
{
spi8((((x) >> 8) & 0xff)); spi8(((x)& 0xff));
}
//// boot cursor positions ////
unsigned short bcurx = 0;
unsigned short bcury = 96;
static int bootscreen_adr = 0x80000 + /*120*/112 * 640 / 8;
void BootHome()
{
bootscreen_adr = 0x80000 + /*120*/112 * 640 / 8;
}
//// boot font ////
static const char boot_font[96][8] =
{
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // SPACE
{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00 }, // !
{ 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // "
{ 0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00 }, // #
{ 0x18, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x18, 0x00 }, // $
{ 0x00, 0x66, 0xAC, 0xD8, 0x36, 0x6A, 0xCC, 0x00 }, // %
{ 0x38, 0x6C, 0x68, 0x76, 0xDC, 0xCE, 0x7B, 0x00 }, // &
{ 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }, // '
{ 0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00 }, // (
{ 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00 }, // )
{ 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00 }, // *
{ 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00 }, // +
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30 }, // ,
{ 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00 }, // -
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00 }, // .
{ 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x00 }, // /
{ 0x3C, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x3C, 0x00 }, // 0
{ 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x00 }, // 1
{ 0x3C, 0x66, 0x06, 0x0C, 0x18, 0x30, 0x7E, 0x00 }, // 2
{ 0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C, 0x00 }, // 3
{ 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x0C, 0x00 }, // 4
{ 0x7E, 0x60, 0x7C, 0x06, 0x06, 0x66, 0x3C, 0x00 }, // 5
{ 0x1C, 0x30, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00 }, // 6
{ 0x7E, 0x06, 0x06, 0x0C, 0x18, 0x18, 0x18, 0x00 }, // 7
{ 0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00 }, // 8
{ 0x3C, 0x66, 0x66, 0x3E, 0x06, 0x0C, 0x38, 0x00 }, // 9
{ 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00 }, // :
{ 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30 }, // ;
{ 0x00, 0x06, 0x18, 0x60, 0x18, 0x06, 0x00, 0x00 }, // <
{ 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00 }, // =
{ 0x00, 0x60, 0x18, 0x06, 0x18, 0x60, 0x00, 0x00 }, // >
{ 0x3C, 0x66, 0x06, 0x0C, 0x18, 0x00, 0x18, 0x00 }, // ?
{ 0x7C, 0xC6, 0xDE, 0xD6, 0xDE, 0xC0, 0x78, 0x00 }, // @
{ 0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00 }, // A
{ 0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00 }, // B
{ 0x1E, 0x30, 0x60, 0x60, 0x60, 0x30, 0x1E, 0x00 }, // C
{ 0x78, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0x78, 0x00 }, // D
{ 0x7E, 0x60, 0x60, 0x78, 0x60, 0x60, 0x7E, 0x00 }, // E
{ 0x7E, 0x60, 0x60, 0x78, 0x60, 0x60, 0x60, 0x00 }, // F
{ 0x3C, 0x66, 0x60, 0x6E, 0x66, 0x66, 0x3E, 0x00 }, // G
{ 0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00 }, // H
{ 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00 }, // I
{ 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x3C, 0x00 }, // J
{ 0xC6, 0xCC, 0xD8, 0xF0, 0xD8, 0xCC, 0xC6, 0x00 }, // K
{ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00 }, // L
{ 0xC6, 0xEE, 0xFE, 0xD6, 0xC6, 0xC6, 0xC6, 0x00 }, // M
{ 0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00 }, // N
{ 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00 }, // O
{ 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00 }, // P
{ 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xDC, 0x7E, 0x00 }, // Q
{ 0x7C, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0x66, 0x00 }, // R
{ 0x3C, 0x66, 0x70, 0x3C, 0x0E, 0x66, 0x3C, 0x00 }, // S
{ 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00 }, // T
{ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00 }, // U
{ 0x66, 0x66, 0x66, 0x66, 0x3C, 0x3C, 0x18, 0x00 }, // V
{ 0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0xEE, 0xC6, 0x00 }, // W
{ 0xC3, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0xC3, 0x00 }, // X
{ 0xC3, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x00 }, // Y
{ 0xFE, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xFE, 0x00 }, // Z
{ 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00 }, // [
{ 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x00 }, // Backslash
{ 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00 }, // ]
{ 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00 }, // ^
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE }, // _
{ 0x18, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00 }, // `
{ 0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00 }, // a
{ 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x00 }, // b
{ 0x00, 0x00, 0x3C, 0x60, 0x60, 0x60, 0x3C, 0x00 }, // c
{ 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00 }, // d
{ 0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00 }, // e
{ 0x1C, 0x30, 0x7C, 0x30, 0x30, 0x30, 0x30, 0x00 }, // f
{ 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x3C }, // g
{ 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00 }, // h
{ 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x0C, 0x00 }, // i
{ 0x0C, 0x00, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x78 }, // j
{ 0x60, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0x00 }, // k
{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0C, 0x00 }, // l
{ 0x00, 0x00, 0xEC, 0xFE, 0xD6, 0xC6, 0xC6, 0x00 }, // m
{ 0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00 }, // n
{ 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00 }, // o
{ 0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60 }, // p
{ 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x06 }, // q
{ 0x00, 0x00, 0x7C, 0x66, 0x60, 0x60, 0x60, 0x00 }, // r
{ 0x00, 0x00, 0x3C, 0x60, 0x3C, 0x06, 0x7C, 0x00 }, // s
{ 0x30, 0x30, 0x7C, 0x30, 0x30, 0x30, 0x1C, 0x00 }, // t
{ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00 }, // u
{ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00 }, // v
{ 0x00, 0x00, 0xC6, 0xC6, 0xD6, 0xFE, 0x6C, 0x00 }, // w
{ 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00 }, // x
{ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x30 }, // y
{ 0x00, 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x7E, 0x00 }, // z
{ 0x0E, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0E, 0x00 }, // {
{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00 }, // |
{ 0x70, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x70, 0x00 }, // }
{ 0x72, 0x9C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // ~
{ 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0x00 } //
};
//// BootEnableMem() ////
void BootEnableMem()
{
// TEMP enable 1MB memory
spi_osd_cmd8(OSD_CMD_MEM, 0x5);
//EnableOsd();
//spi8(OSD_CMD_RST);
//rstval = (SPI_CPU_HLT | SPI_RST_CPU);
//spi8(rstval);
//DisableOsd();
//SPIN(); SPIN(); SPIN(); SPIN();
//while ((read32(REG_SYS_STAT_ADR) & 0x2));
}
//// BootClearScreen() ////
void BootClearScreen(int adr, int size)
{
int i;
mem_upload_init(adr);
for (i = 0; i<size; i++)
{
mem_write16(0x0000);
//mem_write16(i);
}
mem_upload_fini();
}
//// BootUploadLogo() ////
void BootUploadLogo()
{
fileTYPE file;
int x, y;
int i = 0;
int adr;
if (FileOpen(&file, LOGO_FILE)) {
FileReadSec(&file, buffer);
mem_upload_init(SCREEN_BPL1 + LOGO_OFFSET);
adr = SCREEN_BPL1 + LOGO_OFFSET;
for (y = 0; y<LOGO_HEIGHT; y++)
{
for (x = 0; x<LOGO_WIDTH / 16; x++)
{
if (i == 512)
{
mem_upload_fini();
FileReadSec(&file, buffer);
mem_upload_init(adr);
i = 0;
}
spi8(buffer[i++]);
spi8(buffer[i++]);
//for (tmp=0; tmp<0x80000; tmp++);
//printf("i=%03d x=%03d y=%03d dat[0]=0x%08x dat[1]=0x%08x\n", i, x, y, buffer[i], buffer[i+1]);
adr += 2;
}
mem_upload_fini();
mem_upload_init(SCREEN_BPL1 + LOGO_OFFSET + (y + 1)*(SCREEN_WIDTH / 8));
adr = SCREEN_BPL1 + LOGO_OFFSET + (y + 1)*(SCREEN_WIDTH / 8);
}
mem_upload_fini();
mem_upload_init(SCREEN_BPL2 + LOGO_OFFSET);
adr = SCREEN_BPL2 + LOGO_OFFSET;
for (y = 0; y<LOGO_HEIGHT; y++)
{
for (x = 0; x<LOGO_WIDTH / 16; x++)
{
if (i == 512)
{
mem_upload_fini();
FileReadSec(&file, buffer);
mem_upload_init(adr);
i = 0;
}
spi8(buffer[i++]);
spi8(buffer[i++]);
adr += 2;
}
mem_upload_fini();
mem_upload_init(SCREEN_BPL2 + LOGO_OFFSET + (y + 1)*(SCREEN_WIDTH / 8));
adr = SCREEN_BPL2 + LOGO_OFFSET + (y + 1)*(SCREEN_WIDTH / 8);
}
mem_upload_fini();
FileClose(&file);
}
}
//// BootUploadBall() ////
void BootUploadBall()
{
fileTYPE file;
int x;
int i = 0;
int adr;
if (FileOpen(&file, BALL_FILE))
{
FileReadSec(&file, buffer);
mem_upload_init(BALL_ADDRESS);
adr = BALL_ADDRESS;
for (x = 0; x<BALL_SIZE / 2; x++)
{
if (i == 512)
{
mem_upload_fini();
FileReadSec(&file, buffer);
mem_upload_init(adr);
i = 0;
}
spi8(buffer[i++]);
spi8(buffer[i++]);
adr += 2;
}
mem_upload_fini();
FileClose(&file);
}
}
//// BootUploadCopper() ////
void BootUploadCopper()
{
fileTYPE file;
int x;
int i = 0;
int adr;
if (FileOpen(&file, COPPER_FILE))
{
FileReadSec(&file, buffer);
mem_upload_init(COPPER_ADDRESS);
adr = COPPER_ADDRESS;
for (x = 0; x<COPPER_SIZE / 2; x++)
{
if (i == 512)
{
mem_upload_fini();
FileReadSec(&file, buffer);
mem_upload_init(adr);
i = 0;
}
spi8(buffer[i++]);
spi8(buffer[i++]);
adr += 2;
}
mem_upload_fini();
FileClose(&file);
}
else {
mem_upload_init(COPPER_ADDRESS);
mem_write16(0x00e0); mem_write16(0x0008);
mem_write16(0x00e2); mem_write16(0x0000);
mem_write16(0x00e4); mem_write16(0x0008);
mem_write16(0x00e6); mem_write16(0x5000);
mem_write16(0x0100); mem_write16(0xa200);
mem_write16(0xffff); mem_write16(0xfffe);
mem_upload_fini();
}
}
//// BootCustomInit() ////
void BootCustomInit()
{
//move.w #$0000,$dff1fc ; FMODE, slow fetch mode for AGA compatibility
mem_upload_init(0xdff1fc);
mem_write16(0x0000);
mem_upload_fini();
//move.w #$0002,$dff02e ; COPCON, enable danger mode
mem_upload_init(0xdff02e);
mem_write16(0x0002);
mem_upload_fini();
//move.l #Copper1,$dff080 ; COP1LCH, copper 1 pointer
//move.l #Copper2,$dff084 ; CPO2LCH, copper 2 pointer
mem_upload_init(0xdff080);
mem_write16(0x0008); mem_write16(0xe680);
mem_write16(0x0008); mem_write16(0xe69c);
mem_upload_fini();
//move.w #$2c81,$dff08e ; DIWSTRT, screen upper left corner
//move.w #$f4c1,$dff090 ; DIWSTOP, screen lower right corner
//move.w #$003c,$dff092 ; DDFSTRT, display data fetch start
//move.w #$00d4,$dff094 ; DDFSTOP, display data fetch stop
//move.w #$87c0,$dff096 ; DMACON, enable important bits
//move.w #$0000,$dff098 ; CLXCON, TODO
//move.w #$7fff,$dff09a ; INTENA, disable all interrupts
//move.w #$7fff,$dff09c ; INTREQ, disable all interrupts
//move.w #$0000,$dff09e ; ADKCON, TODO
mem_upload_init(0xdff08e);
//mem_write16(0x1d64);
//mem_write16(0x38c7);
//mem_write16(0x0028);
//mem_write16(0x00d8);
mem_write16(0x2c81);
mem_write16(0xf4c1);
mem_write16(0x003c);
mem_write16(0x00d4);
mem_write16(0x87c0);
mem_write16(0x0000);
mem_write16(0x7fff);
mem_write16(0x7fff);
mem_upload_fini();
//move.w #(bpl1>>16)&$ffff,$dff0e0 ; BPL1PTH
//move.w #bpl1&$ffff,$dff0e2 ; BPL1PTL
//move.w #(bpl2>>16)&$ffff,$dff0e4 ; BPL2PTH
//move.w #bpl2&$ffff,$dff0e6 ; BPL2PTL
mem_upload_init(0xdff0e0);
mem_write16(0x0008); mem_write16(0x0000);
mem_write16(0x0008); mem_write16(0x5000);
mem_upload_fini();
//move.w #$a200,$dff100 ; BPLCON0, two bitplanes & colorburst enabled
//move.w #$0000,$dff102 ; BPLCON1, bitplane control scroll value
//move.w #$0000,$dff104 ; BPLCON2, misc bitplane bits
//move.w #$0000,$dff106 ; BPLCON3, TODO
//move.w #$0000,$dff108 ; BPL1MOD, bitplane modulo for odd planes
//move.w #$0000,$dff10a ; BPL2MOD, bitplane modulo for even planes
mem_upload_init(0xdff100);
mem_write16(0xa200);
mem_write16(0x0000);
mem_write16(0x0000);
mem_write16(0x0000);
mem_write16(0x0000);
mem_write16(0x0000);
mem_upload_fini();
//move.w #$09f0,$dff040 ; BLTCON0
//move.w #$0000,$dff042 ; BLTCON1
//move.w #$ffff,$dff044 ; BLTAFWM, blitter first word mask for srcA
//move.w #$ffff,$dff046 ; BLTALWM, blitter last word mask for srcA
mem_upload_init(0xdff040);
mem_write16(0x09f0);
mem_write16(0x0000);
mem_write16(0xffff);
mem_write16(0xffff);
mem_upload_fini();
//move.w #$0000,$dff064 ; BLTAMOD
//move.w #BLITS,$dff066 ; BLTDMOD
mem_upload_init(0xdff064);
mem_write16(0x0000);
mem_write16(BLITS);
mem_upload_fini();
//move.w #$0000,$dff180 ; COLOR00
//move.w #$0aaa,$dff182 ; COLOR01
//move.w #$0a00,$dff184 ; COLOR02
//move.w #$0000,$dff186 ; COLOR03
mem_upload_init(0xdff180);
mem_write16(0x0000);
mem_write16(0x0aaa);
mem_write16(0x0a00);
mem_write16(0x000a);
mem_upload_fini();
//move.w #$0000,$dff088 ; COPJMP1, restart copper at location 1
mem_upload_init(0xdff088);
mem_write16(0x0000);
mem_upload_fini();
}
extern adfTYPE df[4];
//// BootInit() ////
void BootInit()
{
puts("Running minimig setup");
EnableOsd();
spi8(OSD_CMD_VERSION);
char ver_beta = spi_b(0xff);
char ver_major = spi_b(0xff);
char ver_minor = spi_b(0xff);
char ver_minion = spi_b(0xff);
DisableOsd();
spi8(OSD_CMD_RST);
rstval = (SPI_RST_USR | SPI_RST_CPU | SPI_CPU_HLT);
spi8(rstval);
DisableOsd();
EnableOsd();
spi8(OSD_CMD_RST);
rstval = (SPI_RST_CPU | SPI_CPU_HLT);
spi8(rstval);
DisableOsd();
//default video config till real config loaded.
ConfigVideo(0,0, 0x40);
WaitTimer(100);
BootEnableMem();
BootClearScreen(SCREEN_ADDRESS, SCREEN_MEM_SIZE);
BootUploadLogo();
BootUploadBall();
BootUploadCopper();
BootCustomInit();
WaitTimer(500);
char rtl_ver[45];
siprintf(rtl_ver, "**** MINIMIG-AGA%s v%d.%d.%d for MiST ****", ver_beta ? " BETA" : "", ver_major, ver_minor, ver_minion);
BootPrintEx(rtl_ver);
BootPrintEx(" ");
BootPrintEx("MINIMIG-AGA for MiST by Rok Krajnc (rok.krajnc@gmail.com)");
BootPrintEx("Original Minimig by Dennis van Weeren");
BootPrintEx("Updates by Jakub Bednarski, Tobias Gubener, Sascha Boing, A.M. Robinson & others");
BootPrintEx("MiST by Till Harbaum (till@harbaum.org)");
BootPrintEx("For updates & code see https://github.com/rkrajnc/minimig-mist");
BootPrintEx(" ");
WaitTimer(1000);
//eject all disk
df[0].status = 0;
df[1].status = 0;
df[2].status = 0;
df[3].status = 0;
config.kickstart[0] = 0;
SetConfigurationFilename(0); // Use default config
LoadConfiguration(0); // Use slot-based config filename
}
//// BootPrint() ////
void BootPrintEx(char * str)
{
char buf[2];
unsigned char i, j;
unsigned char len;
iprintf(str);
iprintf("\n");
len = strlen(str);
len = (len>80) ? 80 : len;
for (j = 0; j<8; j++)
{
mem_upload_init(bootscreen_adr);
for (i = 0; i<len; i += 2)
{
spi8(boot_font[str[i] - 32][j]);
if (i == (len - 1))
spi8(boot_font[0][j]);
else
spi8(boot_font[str[i + 1] - 32][j]);
}
mem_upload_fini();
bootscreen_adr += 640 / 8;
}
}

43
boot.h Normal file
View File

@@ -0,0 +1,43 @@
// boot.h
// bootscreen functions
// 2014, rok.krajnc@gmail.com
#ifndef __BOOT_H__
#define __BOOT_H__
//// defines ////
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 256
#define SCREEN_SIZE SCREEN_WIDTH * SCREEN_HEIGHT
#define SCREEN_MEM_SIZE 2*SCREEN_SIZE/8
#define SCREEN_ADDRESS 0x80000
#define SCREEN_BPL1 0x80000
#define SCREEN_BPL2 0x85000
#define LOGO_WIDTH 208
#define LOGO_HEIGHT 32
#define LOGO_OFFSET (64*SCREEN_WIDTH/8+24)
#define LOGO_LSKIP (SCREEN_WIDTH-LOGO_WIDTH)/8
#define LOGO_SIZE 0x680
#define LOGO_FILE "MINIMIG.ART"
#define BALL_SIZE 0x4000
#define BALL_ADDRESS 0x8a000
#define BALL_FILE "MINIMIG.BAL"
#define COPPER_SIZE 0x35c
#define COPPER_ADDRESS 0x8e680
#define COPPER_FILE "MINIMIG.COP"
#define BLITS 64
//// functions ////
void BootInit();
void BootPrintEx(char * str);
void BootHome();
#endif // __BOOT_H__

20
build.sh Normal file
View File

@@ -0,0 +1,20 @@
#!/bin/bash
# make script fail if any command failed,
# so we don't need to check the exit status of every command.
set -e
set -o pipefail
make
set +e
plink root@192.168.1.75 -pw 1 'killall MiSTer'
set -e
ftp -n <<EOF
open 192.168.1.75
user root 1
binary
put MiSTer /media/fat/MiSTer
EOF
plink root@192.168.1.75 -pw 1 'PATH=/media/fat:$PATH;MiSTer >/dev/ttyS0 2>/dev/ttyS0 </dev/null &'

173
charrom.h Normal file
View File

@@ -0,0 +1,173 @@
#ifndef CHARROM_H
#define CHARROM_H
/*
Write protect. Characters are defined in columns, not rows, LSB first
. . . . . . . .
. . * * * . . .
. * . . . * . .
. * . . . * . .
* * * * * * * .
* * * * * * * .
* * * * * * * .
. . . . . . . .
0x70,0x7c,0x72,0x72,0x72,0x7c,0x70,0x00,0x00
Write enable
. . . . . . . .
. . . . . * * .
. . . . * . . *
. . . . * . . *
* * * * * * . .
* * * * * * . .
* * * * * * . .
. . . . . . . .
0x70,0x70,0x70,0x70,0x7c,0x72,0x02,0x0c
Middle Dot
. . . . . . . .
. . . . . . . .
. . . * * . . .
. . . * * . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
0x00,0x00,0x00,0x0c,0x0c,0x00,0x00,0x00
*/
// *character font
unsigned char charfont[128][8] =
{
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // 0 [0x0]
{ 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55 }, // 1 [0x1]
{ 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A }, // 2 [0x2]
{ 0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14 }, // 3 [0x3]
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // 4 [0x4]
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // 5 [0x5]
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // 6 [0x6]
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // 7 [0x7]
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // 8 [0x8]
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // 9 [0x9]
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // 10 [0xa]
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // 11 [0xb]
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // 12 [0xc]
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // 13 [0xd]
{ 0x00,0xc0,0xc0,0xe0,0x78,0x1f,0x00,0xff }, // 14 [0xe] atari logo left
{ 0xff,0x00,0x1f,0x78,0xe0,0xc0,0xc0,0x00 }, // 15 [0xf] atari logo right
{ 0x08,0x08,0x1C,0x1C,0x3E,0x3E,0x7F,0x7F }, // 16 [0x10] arrow left
{ 0x7F,0x7F,0x3E,0x3E,0x1C,0x1C,0x08,0x08 }, // 17 [0x11] arrow right
{ 0x00,0x10,0x18,0x7c,0x7c,0x18,0x10,0x00 }, // 18 [0x12] arrow up
{ 0x00,0x10,0x30,0x7c,0x7c,0x30,0x10,0x00 }, // 19 [0x13] arrow down
{ 0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x00,0x00 }, // 20 [0x14]
{ 0x00,0x00,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C }, // 21 [0x15]
{ 0x00,0x7C,0x7C,0x38,0x38,0x10,0x10,0x00 }, // 22 [0x16] mini arrow right
{ 0x70,0x7c,0x72,0x72,0x72,0x7c,0x70,0x00 }, // 23 [0x17] write protect
{ 0x70,0x70,0x70,0x70,0x7c,0x72,0x02,0x0c }, // 24 [0x18] write enable
{ 0x3e,0x3e,0x22,0x22,0x22,0x3e,0x3e,0x00 }, // 25 [0x19] unchecked checkbox
{ 0x3e,0x3e,0x3e,0x3e,0x3e,0x3e,0x3e,0x00 }, // 26 [0x1a] checked checkbox
{ 0x00,0x00,0x00,0x0c,0x0c,0x00,0x00,0x00 }, // 27 [0x1b] middle dot
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // 28 [0x1c]
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // 29 [0x1d]
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // 30 [0x1e]
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // 31 [0x1f]
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // 32 [0x20]
{ 0x00,0x00,0x00,0x5F,0x5F,0x00,0x00,0x00 }, // 33 [0x21]
{ 0x00,0x03,0x03,0x00,0x03,0x03,0x00,0x00 }, // 34 [0x22]
{ 0x14,0x7F,0x7F,0x14,0x7F,0x7F,0x14,0x00 }, // 35 [0x23]
{ 0x00,0x24,0x2E,0x6B,0x6B,0x3A,0x12,0x00 }, // 36 [0x24]
{ 0x4C,0x6A,0x36,0x18,0x6C,0x56,0x32,0x00 }, // 37 [0x25]
{ 0x30,0x7E,0x4F,0x59,0x77,0x3A,0x68,0x40 }, // 38 [0x26]
{ 0x00,0x00,0x04,0x07,0x03,0x00,0x00,0x00 }, // 39 [0x27]
{ 0x00,0x00,0x1C,0x3E,0x63,0x41,0x00,0x00 }, // 40 [0x28]
{ 0x00,0x00,0x41,0x63,0x3E,0x1C,0x00,0x00 }, // 41 [0x29]
{ 0x08,0x2A,0x3E,0x1C,0x1C,0x3E,0x2A,0x08 }, // 42 [0x2a]
{ 0x00,0x08,0x08,0x3E,0x3E,0x08,0x08,0x00 }, // 43 [0x2b]
{ 0x00,0x00,0x80,0xE0,0x60,0x00,0x00,0x00 }, // 44 [0x2c]
{ 0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00 }, // 45 [0x2d]
{ 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00 }, // 46 [0x2e]
{ 0x40,0x60,0x30,0x18,0x0C,0x06,0x03,0x01 }, // 47 [0x2f]
{ 0x00,0x3E,0x7F,0x59,0x4D,0x7F,0x3E,0x00 }, // 48 [0x30]
{ 0x00,0x04,0x06,0x7F,0x7F,0x00,0x00,0x00 }, // 49 [0x31]
{ 0x00,0x42,0x63,0x71,0x59,0x4F,0x46,0x00 }, // 50 [0x32]
{ 0x00,0x22,0x63,0x49,0x49,0x7F,0x36,0x00 }, // 51 [0x33]
{ 0x18,0x1C,0x16,0x13,0x7F,0x7F,0x10,0x00 }, // 52 [0x34]
{ 0x00,0x27,0x67,0x45,0x45,0x7D,0x39,0x00 }, // 53 [0x35]
{ 0x00,0x3C,0x7E,0x4B,0x49,0x79,0x30,0x00 }, // 54 [0x36]
{ 0x00,0x01,0x01,0x71,0x79,0x0F,0x07,0x00 }, // 55 [0x37]
{ 0x00,0x36,0x7F,0x49,0x49,0x7F,0x36,0x00 }, // 56 [0x38]
{ 0x00,0x06,0x4F,0x49,0x69,0x3F,0x1E,0x00 }, // 57 [0x39]
{ 0x00,0x00,0x00,0x66,0x66,0x00,0x00,0x00 }, // 58 [0x3a]
{ 0x00,0x00,0x80,0xE6,0x66,0x00,0x00,0x00 }, // 59 [0x3b]
{ 0x00,0x08,0x08,0x14,0x14,0x22,0x22,0x00 }, // 60 [0x3c]
{ 0x00,0x14,0x14,0x14,0x14,0x14,0x14,0x00 }, // 61 [0x3d]
{ 0x00,0x22,0x22,0x14,0x14,0x08,0x08,0x00 }, // 62 [0x3e]
{ 0x00,0x02,0x03,0x51,0x59,0x0F,0x06,0x00 }, // 63 [0x3f]
{ 0x3E,0x7F,0x41,0x5D,0x55,0x1F,0x1E,0x00 }, // 64 [0x40]
{ 0x00,0x7E,0x7F,0x09,0x09,0x7F,0x7E,0x00 }, // 65 [0x41]
{ 0x00,0x7F,0x7F,0x49,0x49,0x7F,0x36,0x00 }, // 66 [0x42]
{ 0x00,0x1C,0x3E,0x63,0x41,0x41,0x41,0x00 }, // 67 [0x43]
{ 0x00,0x7F,0x7F,0x41,0x63,0x3E,0x1C,0x00 }, // 68 [0x44]
{ 0x00,0x7F,0x7F,0x49,0x49,0x41,0x41,0x00 }, // 69 [0x45]
{ 0x00,0x7F,0x7F,0x09,0x09,0x01,0x01,0x00 }, // 70 [0x46]
{ 0x00,0x3E,0x7F,0x41,0x49,0x7B,0x7A,0x00 }, // 71 [0x47]
{ 0x00,0x7F,0x7F,0x08,0x08,0x7F,0x7F,0x00 }, // 72 [0x48]
{ 0x00,0x00,0x41,0x7F,0x7F,0x41,0x00,0x00 }, // 73 [0x49]
{ 0x00,0x20,0x60,0x40,0x40,0x7F,0x3F,0x00 }, // 74 [0x4a]
{ 0x7F,0x7F,0x08,0x1C,0x36,0x63,0x41,0x00 }, // 75 [0x4b]
{ 0x00,0x7F,0x7F,0x40,0x40,0x40,0x40,0x00 }, // 76 [0x4c]
{ 0x7F,0x7F,0x06,0x0C,0x06,0x7F,0x7F,0x00 }, // 77 [0x4d]
{ 0x7F,0x7F,0x06,0x0C,0x18,0x7F,0x7F,0x00 }, // 78 [0x4e]
{ 0x00,0x3E,0x7F,0x41,0x41,0x7F,0x3E,0x00 }, // 79 [0x4f]
{ 0x00,0x7F,0x7F,0x09,0x09,0x0F,0x06,0x00 }, // 80 [0x50]
{ 0x3E,0x7F,0x41,0x61,0x7F,0x7E,0x40,0x00 }, // 81 [0x51]
{ 0x00,0x7F,0x7F,0x09,0x19,0x7F,0x66,0x00 }, // 82 [0x52]
{ 0x00,0x26,0x6F,0x4D,0x59,0x7B,0x32,0x00 }, // 83 [0x53]
{ 0x00,0x01,0x01,0x7F,0x7F,0x01,0x01,0x00 }, // 84 [0x54]
{ 0x00,0x3F,0x7F,0x40,0x40,0x7F,0x3F,0x00 }, // 85 [0x55]
{ 0x00,0x0F,0x3F,0x70,0x70,0x3F,0x0F,0x00 }, // 86 [0x56]
{ 0x7F,0x7F,0x30,0x18,0x30,0x7F,0x7F,0x00 }, // 87 [0x57]
{ 0x41,0x63,0x36,0x1C,0x1C,0x36,0x63,0x41 }, // 88 [0x58]
{ 0x01,0x03,0x06,0x7C,0x7C,0x06,0x03,0x01 }, // 89 [0x59]
{ 0x61,0x71,0x59,0x4D,0x47,0x43,0x41,0x00 }, // 90 [0x5a]
{ 0x00,0x00,0x7F,0x7F,0x41,0x41,0x00,0x00 }, // 91 [0x5b]
{ 0x01,0x03,0x06,0x0C,0x18,0x30,0x60,0x40 }, // 92 [0x5c]
{ 0x00,0x00,0x41,0x41,0x7F,0x7F,0x00,0x00 }, // 93 [0x5d]
{ 0x08,0x0C,0x06,0x03,0x06,0x0C,0x08,0x00 }, // 94 [0x5e]
{ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00 }, // 95 [0x5f]
{ 0x00,0x00,0x00,0x03,0x07,0x04,0x00,0x00 }, // 96 [0x60]
{ 0x00,0x20,0x74,0x54,0x54,0x7C,0x78,0x00 }, // 97 [0x61]
{ 0x00,0x7F,0x7F,0x44,0x44,0x7C,0x38,0x00 }, // 98 [0x62]
{ 0x00,0x38,0x7C,0x44,0x44,0x44,0x00,0x00 }, // 99 [0x63]
{ 0x00,0x38,0x7C,0x44,0x44,0x7F,0x7F,0x00 }, // 100 [0x64]
{ 0x00,0x38,0x7C,0x54,0x54,0x5C,0x18,0x00 }, // 101 [0x65]
{ 0x00,0x04,0x7E,0x7F,0x05,0x05,0x00,0x00 }, // 102 [0x66]
{ 0x00,0x18,0xBC,0xA4,0xA4,0xFC,0x7C,0x00 }, // 103 [0x67]
{ 0x00,0x7F,0x7F,0x04,0x04,0x7C,0x78,0x00 }, // 104 [0x68]
{ 0x00,0x00,0x00,0x3D,0x7D,0x40,0x00,0x00 }, // 105 [0x69]
{ 0x00,0x80,0x80,0x80,0xFD,0x7D,0x00,0x00 }, // 106 [0x6a]
{ 0x00,0x7F,0x7F,0x10,0x38,0x6C,0x44,0x00 }, // 107 [0x6b]
{ 0x00,0x00,0x00,0x3F,0x7F,0x40,0x00,0x00 }, // 108 [0x6c]
{ 0x7C,0x7C,0x0C,0x18,0x0C,0x7C,0x78,0x00 }, // 109 [0x6d]
{ 0x00,0x7C,0x7C,0x04,0x04,0x7C,0x78,0x00 }, // 110 [0x6e]
{ 0x00,0x38,0x7C,0x44,0x44,0x7C,0x38,0x00 }, // 111 [0x6f]
{ 0x00,0xFC,0xFC,0x24,0x24,0x3C,0x18,0x00 }, // 112 [0x70]
{ 0x00,0x18,0x3C,0x24,0x24,0xFC,0xFC,0x00 }, // 113 [0x71]
{ 0x00,0x7C,0x7C,0x04,0x04,0x0C,0x08,0x00 }, // 114 [0x72]
{ 0x00,0x48,0x5C,0x54,0x54,0x74,0x20,0x00 }, // 115 [0x73]
{ 0x00,0x04,0x3F,0x7F,0x44,0x44,0x00,0x00 }, // 116 [0x74]
{ 0x00,0x3C,0x7C,0x40,0x40,0x7C,0x7C,0x00 }, // 117 [0x75]
{ 0x00,0x1C,0x3C,0x60,0x60,0x3C,0x1C,0x00 }, // 118 [0x76]
{ 0x3C,0x7C,0x60,0x30,0x60,0x7C,0x3C,0x00 }, // 119 [0x77]
{ 0x44,0x6C,0x38,0x10,0x38,0x6C,0x44,0x00 }, // 120 [0x78]
{ 0x00,0x1C,0xBC,0xE0,0x60,0x3C,0x1C,0x00 }, // 121 [0x79]
{ 0x00,0x44,0x64,0x74,0x5C,0x4C,0x44,0x00 }, // 122 [0x7a]
{ 0x00,0x08,0x08,0x3E,0x77,0x41,0x41,0x00 }, // 123 [0x7b]
{ 0x00,0x00,0x00,0x7F,0x7F,0x00,0x00,0x00 }, // 124 [0x7c]
{ 0x00,0x41,0x41,0x77,0x3E,0x08,0x08,0x00 }, // 125 [0x7d]
{ 0x02,0x01,0x01,0x03,0x02,0x02,0x01,0x00 }, // 126 [0x7e]
{ 0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x00 } // 127 [0x7f]
};
#endif

4
clean.sh Normal file
View File

@@ -0,0 +1,4 @@
#!/bin/bash
make clean
exit 0

492
config.c Normal file
View File

@@ -0,0 +1,492 @@
// config.c
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "errors.h"
#include "hardware.h"
#include "boot.h"
#include "file_io.h"
#include "osd.h"
#include "fdd.h"
#include "hdd.h"
#include "menu.h"
#include "config.h"
#include "user_io.h"
configTYPE config;
char configfilename[32];
char DebugMode = 0;
unsigned char romkey[3072];
void SendFileV2(fileTYPE* file, unsigned char* key, int keysize, int address, int size)
{
static uint8_t buf[512];
int i, j;
unsigned int keyidx = 0;
iprintf("File size: %dkB\n", size >> 1);
iprintf("[");
if (keysize)
{
// read header
FileReadAdv(file, buf, 0xb);
}
for (i = 0; i<size; i++)
{
if (!(i & 31)) iprintf("*");
FileReadAdv(file, buf, 512);
if (keysize)
{
// decrypt ROM
for (j = 0; j<512; j++)
{
buf[j] ^= key[keyidx++];
if (keyidx >= keysize) keyidx -= keysize;
}
}
EnableOsd();
unsigned int adr = address + i * 512;
spi8(OSD_CMD_WR);
spi8(adr & 0xff); adr = adr >> 8;
spi8(adr & 0xff); adr = adr >> 8;
spi8(adr & 0xff); adr = adr >> 8;
spi8(adr & 0xff); adr = adr >> 8;
for (j = 0; j<512; j = j + 4)
{
spi8(buf[j + 0]);
spi8(buf[j + 1]);
spi8(buf[j + 2]);
spi8(buf[j + 3]);
}
DisableOsd();
}
iprintf("]\n");
}
//// UploadKickstart() ////
char UploadKickstart(char *name)
{
fileTYPE file;
int keysize = 0;
BootPrint("Checking for Amiga Forever key file:");
if (FileOpen(&file, "ROM.KEY")) {
keysize = file.size;
if (file.size<sizeof(romkey))
{
FileReadAdv(&file, romkey, keysize);
BootPrint("Loaded Amiga Forever key file");
}
else
{
BootPrint("Amiga Forever keyfile is too large!");
}
FileClose(&file);
}
BootPrint("Loading file: ");
BootPrint(name);
if (FileOpen(&file, name)) {
if (file.size == 0x100000) {
// 1MB Kickstart ROM
BootPrint("Uploading 1MB Kickstart ...");
SendFileV2(&file, NULL, 0, 0xe00000, file.size >> 10);
SendFileV2(&file, NULL, 0, 0xf80000, file.size >> 10);
FileClose(&file);
return(1);
}
else if (file.size == 0x80000) {
// 512KB Kickstart ROM
BootPrint("Uploading 512KB Kickstart ...");
SendFileV2(&file, NULL, 0, 0xf80000, file.size >> 9);
FileClose(&file);
FileOpen(&file, name);
SendFileV2(&file, NULL, 0, 0xe00000, file.size >> 9);
FileClose(&file);
return(1);
}
else if ((file.size == 0x8000b) && keysize) {
// 512KB Kickstart ROM
BootPrint("Uploading 512 KB Kickstart (Probably Amiga Forever encrypted...)");
SendFileV2(&file, romkey, keysize, 0xf80000, file.size >> 9);
FileClose(&file);
FileOpen(&file, name);
SendFileV2(&file, romkey, keysize, 0xe00000, file.size >> 9);
FileClose(&file);
return(1);
}
else if (file.size == 0x40000) {
// 256KB Kickstart ROM
BootPrint("Uploading 256 KB Kickstart...");
SendFileV2(&file, NULL, 0, 0xf80000, file.size >> 9);
FileClose(&file);
FileOpen(&file, name); // TODO will this work
SendFileV2(&file, NULL, 0, 0xfc0000, file.size >> 9);
FileClose(&file);
return(1);
}
else if ((file.size == 0x4000b) && keysize) {
// 256KB Kickstart ROM
BootPrint("Uploading 256 KB Kickstart (Probably Amiga Forever encrypted...");
SendFileV2(&file, romkey, keysize, 0xf80000, file.size >> 9);
FileClose(&file);
FileOpen(&file, name); // TODO will this work
SendFileV2(&file, romkey, keysize, 0xfc0000, file.size >> 9);
FileClose(&file);
return(1);
}
else {
BootPrint("Unsupported ROM file size!");
}
FileClose(&file);
}
else {
printf("No \"%s\" file!\n", name);
}
return(0);
}
//// UploadActionReplay() ////
char UploadActionReplay()
{
fileTYPE file;
if (FileOpen(&file, "HRTMON.ROM")) {
int adr, data;
puts("Uploading HRTmon ROM... ");
SendFileV2(&file, NULL, 0, 0xa10000, (file.size + 511) >> 9);
// HRTmon config
adr = 0xa10000 + 20;
spi_osd_cmd32le_cont(OSD_CMD_WR, adr);
data = 0x00800000; // mon_size, 4 bytes
spi8((data >> 24) & 0xff); spi8((data >> 16) & 0xff);
spi8((data >> 8) & 0xff); spi8((data >> 0) & 0xff);
data = 0x00; // col0h, 1 byte
spi8((data >> 0) & 0xff);
data = 0x5a; // col0l, 1 byte
spi8((data >> 0) & 0xff);
data = 0x0f; // col1h, 1 byte
spi8((data >> 0) & 0xff);
data = 0xff; // col1l, 1 byte
spi8((data >> 0) & 0xff);
data = 0x01; // right, 1 byte
spi8((data >> 0) & 0xff);
data = 0x00; // keyboard, 1 byte
spi8((data >> 0) & 0xff);
data = 0x01; // key, 1 byte
spi8((data >> 0) & 0xff);
data = config.enable_ide ? 1 : 0; // ide, 1 byte
spi8((data >> 0) & 0xff);
data = 0x01; // a1200, 1 byte
spi8((data >> 0) & 0xff);
data = config.chipset&CONFIG_AGA ? 1 : 0; // aga, 1 byte
spi8((data >> 0) & 0xff);
data = 0x01; // insert, 1 byte
spi8((data >> 0) & 0xff);
data = 0x0f; // delay, 1 byte
spi8((data >> 0) & 0xff);
data = 0x01; // lview, 1 byte
spi8((data >> 0) & 0xff);
data = 0x00; // cd32, 1 byte
spi8((data >> 0) & 0xff);
data = config.chipset&CONFIG_NTSC ? 1 : 0; // screenmode, 1 byte
spi8((data >> 0) & 0xff);
data = 1; // novbr, 1 byte
spi8((data >> 0) & 0xff);
data = 0; // entered, 1 byte
spi8((data >> 0) & 0xff);
data = 1; // hexmode, 1 byte
spi8((data >> 0) & 0xff);
DisableOsd();
adr = 0xa10000 + 68;
spi_osd_cmd32le_cont(OSD_CMD_WR, adr);
data = ((config.memory & 0x3) + 1) * 512 * 1024; // maxchip, 4 bytes TODO is this correct?
spi8((data >> 24) & 0xff); spi8((data >> 16) & 0xff);
spi8((data >> 8) & 0xff); spi8((data >> 0) & 0xff);
DisableOsd();
FileClose(&file);
return(1);
}
else {
puts("\nhrtmon.rom not found!\n");
return(0);
}
return(0);
}
//// SetConfigurationFilename() ////
void SetConfigurationFilename(int config)
{
if (config)
siprintf(configfilename, "MINIMIG%d.CFG", config);
else
strcpy(configfilename, "MINIMIG.CFG");
}
//// ConfigurationExists() ////
unsigned char ConfigurationExists(char *filename)
{
if (!filename) {
// use slot-based filename if none provided
filename = configfilename;
}
fileTYPE f;
if(FileOpen(&f, filename))
{
FileClose(&f);
return(1);
}
return(0);
}
//// LoadConfiguration() ////
unsigned char LoadConfiguration(char *filename)
{
static const char config_id[] = "MNMGCFG0";
char updatekickstart = 0;
char result = 0;
unsigned char key, i;
static configTYPE tmpconf;
if (!filename)
{
// use slot-based filename if none provided
filename = configfilename;
}
fileTYPE file;
// load configuration data
if (FileOpen(&file, filename)) {
FileClose(&file);
BootPrint("Opened configuration file\n");
printf("Configuration file size: %s, %lu\n", file.name, file.size);
if (file.size == sizeof(config))
{
if (FileLoad(filename, &tmpconf, sizeof(tmpconf)))
{
// check file id and version
if (strncmp(tmpconf.id, config_id, sizeof(config.id)) == 0) {
// A few more sanity checks...
if (tmpconf.floppy.drives <= 4) {
// If either the old config and new config have a different kickstart file,
// or this is the first boot, we need to upload a kickstart image.
if (strcmp(tmpconf.kickstart, config.kickstart) != 0) {
updatekickstart = true;
}
memcpy((void*)&config, (void*)&tmpconf, sizeof(config));
result = 1; // We successfully loaded the config.
}
else BootPrint("Config file sanity check failed!\n");
}
else BootPrint("Wrong configuration file format!\n");
}
else printf("Cannot load configuration file\n");
}
else printf("Wrong configuration file size: %lu (expected: %lu)\n", file.size, sizeof(config));
}
if (!result) {
BootPrint("Can not open configuration file!\n");
BootPrint("Setting config defaults\n");
// set default configuration
memset((void*)&config, 0, sizeof(config)); // Finally found default config bug - params were reversed!
strncpy(config.id, config_id, sizeof(config.id));
strcpy(config.kickstart, "KICK.ROM");
config.memory = 0x15;
config.cpu = 0;
config.chipset = 0;
config.floppy.speed = CONFIG_FLOPPY2X;
config.floppy.drives = 1;
config.enable_ide = 0;
config.hardfile[0].enabled = 1;
strcpy(config.hardfile[0].long_name, "HARDFILE1.HDF");
config.hardfile[0].long_name[0] = 0;
strcpy(config.hardfile[1].long_name, "HARDFILE2.HDF");
config.hardfile[1].long_name[0] = 0;
config.hardfile[1].enabled = 1; // Default is access to entire SD card
updatekickstart = true;
BootPrint("Defaults set\n");
}
// print config to boot screen
char cfg_str[41];
siprintf(cfg_str, "CPU: %s", config_cpu_msg[config.cpu & 0x03]); BootPrintEx(cfg_str);
siprintf(cfg_str, "Chipset: %s", config_chipset_msg[(config.chipset >> 2) & 7]); BootPrintEx(cfg_str);
siprintf(cfg_str, "Memory: CHIP: %s FAST: %s SLOW: %s", config_memory_chip_msg[(config.memory >> 0) & 0x03], config_memory_fast_msg[(config.memory >> 4) & 0x03], config_memory_slow_msg[(config.memory >> 2) & 0x03]); BootPrintEx(cfg_str);
// wait up to 3 seconds for keyboard to appear. If it appears wait another
// two seconds for the user to press a key
/*
int8_t keyboard_present = 0;
for(i=0;i<3;i++) {
unsigned long to = GetTimer(1000);
//while(!CheckTimer(to)) usb_poll();
// check if keyboard just appeared
if(!keyboard_present && hid_keyboard_present()) {
// BootPrintEx("Press F1 for NTSC, F2 for PAL");
keyboard_present = 1;
i = 0;
}
}
*/
key = OsdGetCtrl();
if (key == KEY_F1) {
// BootPrintEx("Forcing NTSC video ...");
// force NTSC mode if F1 pressed
config.chipset |= CONFIG_NTSC;
}
if (key == KEY_F2) {
// BootPrintEx("Forcing PAL video ...");
// force PAL mode if F2 pressed
config.chipset &= ~CONFIG_NTSC;
}
ApplyConfiguration(updatekickstart);
return(result);
}
//// ApplyConfiguration() ////
void ApplyConfiguration(char reloadkickstart)
{
ConfigCPU(config.cpu);
if (reloadkickstart)
{
}
else {
ConfigChipset(config.chipset);
ConfigFloppy(config.floppy.drives, config.floppy.speed);
}
// Whether or not we uploaded a kickstart image we now need to set various parameters from the config.
if (OpenHardfile(0)) {
switch (hdf[0].type) {
// Customise message for SD card acces
case (HDF_FILE | HDF_SYNTHRDB) :
printf("\nHardfile 0 (with fake RDB): %s\n", hdf[0].file.name);
break;
case HDF_FILE:
printf("\nHardfile 0: %s\n", hdf[0].file.name);
break;
}
printf("CHS: %u.%u.%u\n", hdf[0].cylinders, hdf[0].heads, hdf[0].sectors);
printf("Size: %lu MB\n", ((((unsigned long)hdf[0].cylinders) * hdf[0].heads * hdf[0].sectors) >> 11));
printf("Offset: %ld\n", hdf[0].offset);
}
if (OpenHardfile(1)) {
switch (hdf[1].type) {
case (HDF_FILE | HDF_SYNTHRDB) :
printf("\nHardfile 1 (with fake RDB): %s\n", hdf[1].file.name);
break;
case HDF_FILE:
printf("\nHardfile 1: %s\n", hdf[1].file.name);
break;
}
printf("CHS: %u.%u.%u\n", hdf[1].cylinders, hdf[1].heads, hdf[1].sectors);
printf("Size: %lu MB\n", ((((unsigned long)hdf[1].cylinders) * hdf[1].heads * hdf[1].sectors) >> 11));
printf("Offset: %ld\n", hdf[1].offset);
}
ConfigIDE(config.enable_ide, config.hardfile[0].present && config.hardfile[0].enabled, config.hardfile[1].present && config.hardfile[1].enabled);
printf("CPU clock : %s\n", config.chipset & 0x01 ? "turbo" : "normal");
printf("Chip RAM size : %s\n", config_memory_chip_msg[config.memory & 0x03]);
printf("Slow RAM size : %s\n", config_memory_slow_msg[config.memory >> 2 & 0x03]);
printf("Fast RAM size : %s\n", config_memory_fast_msg[config.memory >> 4 & 0x03]);
printf("Floppy drives : %u\n", config.floppy.drives + 1);
printf("Floppy speed : %s\n", config.floppy.speed ? "fast" : "normal");
printf("\n");
printf("\nA600/A1200 IDE HDC is %s.\n", config.enable_ide ? "enabled" : "disabled");
printf("Master HDD is %s.\n", config.hardfile[0].present ? config.hardfile[0].enabled ? "enabled" : "disabled" : "not present");
printf("Slave HDD is %s.\n", config.hardfile[1].present ? config.hardfile[1].enabled ? "enabled" : "disabled" : "not present");
#if 0
if (cluster_size < 64) {
BootPrint("\n***************************************************");
BootPrint("* It's recommended to reformat your memory card *");
BootPrint("* using 32 KB clusters to improve performance *");
BootPrint("* when using large hardfiles. *"); // AMR
BootPrint("***************************************************");
}
iprintf("Bootloading is complete.\n");
#endif
printf("\nExiting bootloader...\n");
ConfigMemory(config.memory);
ConfigCPU(config.cpu);
ConfigChipset(config.chipset);
ConfigFloppy(config.floppy.drives, config.floppy.speed);
if (reloadkickstart)
{
UploadActionReplay();
printf("Reloading kickstart ...\n");
WaitTimer(1000);
EnableOsd();
spi8(OSD_CMD_RST);
rstval |= (SPI_RST_CPU | SPI_CPU_HLT);
spi8(rstval);
DisableOsd();
if (!UploadKickstart(config.kickstart))
{
strcpy(config.kickstart, "KICK.ROM");
if (!UploadKickstart(config.kickstart))
{
FatalError(6);
}
}
EnableOsd();
spi8(OSD_CMD_RST);
rstval |= (SPI_RST_USR | SPI_RST_CPU);
spi8(rstval);
DisableOsd();
EnableOsd();
spi8(OSD_CMD_RST);
rstval = 0;
spi8(rstval);
DisableOsd();
}
else
{
printf("Resetting ...\n");
EnableOsd();
spi8(OSD_CMD_RST);
rstval |= (SPI_RST_USR | SPI_RST_CPU);
spi8(rstval);
DisableOsd();
EnableOsd();
spi8(OSD_CMD_RST);
rstval = 0;
spi8(rstval);
DisableOsd();
}
ConfigVideo(config.filter.hires, config.filter.lores, config.scanlines);
}
//// SaveConfiguration() ////
unsigned char SaveConfiguration(char *filename)
{
if (!filename) {
// use slot-based filename if none provided
filename = configfilename;
}
return FileSave(filename, &config, sizeof(config));
}

50
config.h Normal file
View File

@@ -0,0 +1,50 @@
#include "file_io.h"
typedef struct
{
unsigned char lores;
unsigned char hires;
} filterTYPE;
typedef struct
{
unsigned char speed;
unsigned char drives;
} floppyTYPE;
typedef struct
{
unsigned char enabled; // 0: Disabled, 1: Hard file, 2: MMC (entire card), 3-6: Partition 1-4 of MMC card
unsigned char present;
char long_name[1024];
} hardfileTYPE;
typedef struct
{
char id[8];
unsigned long version;
char kickstart[1024];
filterTYPE filter;
unsigned char memory;
unsigned char chipset;
floppyTYPE floppy;
unsigned char disable_ar3;
unsigned char enable_ide;
unsigned char scanlines;
unsigned char pad1;
hardfileTYPE hardfile[2];
unsigned char cpu;
unsigned char autofire;
} configTYPE;
extern configTYPE config;
extern char DebugMode;
char UploadKickstart(char *name);
char UploadActionReplay();
void SetConfigurationFilename(int config); // Set configuration filename by slot number
unsigned char LoadConfiguration(char *filename); // Can supply NULL to use filename previously set by slot number
unsigned char SaveConfiguration(char *filename); // Can supply NULL to use filename previously set by slot number
unsigned char ConfigurationExists(char *filename);
void ApplyConfiguration(char reloadkickstart);

102
debug.h Normal file
View File

@@ -0,0 +1,102 @@
// this file allows to enabled and disable rs232 debugging on a detailed basis
#ifndef DEBUG_H
#define DEBUG_H
#include "hardware.h"
// ------------ generic debugging -----------
#if 0
#define menu_debugf(...) iprintf(__VA_ARGS__)
#else
#define menu_debugf(...)
#endif
// ----------- minimig debugging -------------
#if 0
#define hdd_debugf(a, ...) iprintf("\033[1;32mHDD: " a "\033[0m\n", ##__VA_ARGS__)
#else
#define hdd_debugf(...)
#endif
#if 0
#define fdd_debugf(...) iprintf(__VA_ARGS__)
#else
#define fdd_debugf(...)
#endif
// -------------- TOS debugging --------------
#if 1
#define tos_debugf(a, ...) iprintf("\033[1;32mTOS: " a "\033[0m\n", ##__VA_ARGS__)
#else
#define tos_debugf(...)
#endif
#if 1
// ikbd debug output in red
#define IKBD_DEBUG
#define ikbd_debugf(a, ...) iprintf("\033[1;31mIKBD: " a "\033[0m\n", ##__VA_ARGS__)
#else
#define ikbd_debugf(...)
#endif
#if 1
// 8bit debug output in blue
#define bit8_debugf(a, ...) iprintf("\033[1;34m8BIT: " a "\033[0m\n", ##__VA_ARGS__)
#else
#define bit8_debugf(...)
#endif
// ------------ usb debugging -----------
#if 0
#define hidp_debugf(a, ...) iprintf("\033[1;34mHIDP: " a "\033[0m\n", ##__VA_ARGS__)
#else
#define hidp_debugf(...)
#endif
#if 0
// usb asix debug output in blue
#define asix_debugf(a, ...) iprintf("\033[1;34mASIX: " a "\033[0m\n", ##__VA_ARGS__)
#else
#define asix_debugf(...)
#endif
#if 1
// usb hid debug output in green
#define hid_debugf(a, ...) iprintf("\033[1;32mHID: " a "\033[0m\n", ##__VA_ARGS__)
#else
#define hid_debugf(...)
#endif
#if 1
// usb mass storage debug output in purple
#define storage_debugf(a, ...) iprintf("\033[1;35mSTORAGE: " a "\033[0m\n", ##__VA_ARGS__)
#else
#define storage_debugf(...)
#endif
#if 0
// usb rts debug output in blue
#define usbrtc_debugf(a, ...) iprintf("\033[1;34mUSBRTC: " a "\033[0m\n", ##__VA_ARGS__)
#else
#define usbrtc_debugf(...)
#endif
#if 1
// usb rts debug output in blue
#define pl2303_debugf(a, ...) iprintf("\033[1;34mPL2303: " a "\033[0m\n", ##__VA_ARGS__)
#else
#define pl2303_debugf(...)
#endif
#if 1
// ini_parser debug output
#define ini_parser_debugf(a, ...) iprintf("\033[1;34mINI_PARSER : " a "\033[0m\n",## __VA_ARGS__)
#else
#define ini_parser_debugf(...)
#endif
#endif // DEBUG_H

9
errors.h Normal file
View File

@@ -0,0 +1,9 @@
#define ERROR_NONE 0
#define ERROR_FILE_NOT_FOUND 1
#define ERROR_INVALID_DATA 2
#define ERROR_UPDATE_FAILED 3
extern unsigned char Error;
void ErrorMessage(const char *message, unsigned char code);
void FatalError(unsigned long error);

630
fdd.c Normal file
View File

@@ -0,0 +1,630 @@
/*
Copyright 2005, 2006, 2007 Dennis van Weeren
Copyright 2008, 2009 Jakub Bednarski
This file is part of Minimig
Minimig 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.
Minimig 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/>.
*/
// 2009-11-14 - adapted gap size
// 2009-12-24 - updated sync word list
// - fixed sector header generation
// 2010-01-09 - support for variable number of tracks
#include <stdio.h>
#include "errors.h"
#include "hardware.h"
#include "file_io.h"
#include "fdd.h"
#include "config.h"
#include "debug.h"
#include "fpga_io.h"
unsigned char drives = 0; // number of active drives reported by FPGA (may change only during reset)
adfTYPE *pdfx; // drive select pointer
adfTYPE df[4]; // drive 0 information structure
static uint8_t sector_buffer[512];
#define TRACK_SIZE 12668
#define HEADER_SIZE 0x40
#define DATA_SIZE 0x400
#define SECTOR_SIZE (HEADER_SIZE + DATA_SIZE)
#define SECTOR_COUNT 11
#define LAST_SECTOR (SECTOR_COUNT - 1)
#define GAP_SIZE (TRACK_SIZE - SECTOR_COUNT * SECTOR_SIZE)
#define B2W(a,b) (((((uint16_t)(a))<<8) & 0xFF00) | ((uint16_t)(b) & 0x00FF))
// sends the data in the sector buffer to the FPGA, translated into an Amiga floppy format sector
// note that we do not insert clock bits because they will be stripped by the Amiga software anyway
void SendSector(unsigned char *pData, unsigned char sector, unsigned char track, unsigned char dsksynch, unsigned char dsksyncl)
{
unsigned char checksum[4];
unsigned short i;
unsigned char x,y;
unsigned char *p;
// preamble
spi_w(0xAAAA);
spi_w(0xAAAA);
// synchronization
spi_w(B2W(dsksynch, dsksyncl));
spi_w(B2W(dsksynch, dsksyncl));
// odd bits of header
x = 0x55;
checksum[0] = x;
y = track >> 1 & 0x55;
checksum[1] = y;
spi_w(B2W(x,y));
x = sector >> 1 & 0x55;
checksum[2] = x;
y = 11 - sector >> 1 & 0x55;
checksum[3] = y;
spi_w(B2W(x, y));
// even bits of header
x = 0x55;
checksum[0] ^= x;
y = track & 0x55;
checksum[1] ^= y;
spi_w(B2W(x, y));
x = sector & 0x55;
checksum[2] ^= x;
y = 11 - sector & 0x55;
checksum[3] ^= y;
spi_w(B2W(x, y));
// sector label and reserved area (changes nothing to checksum)
i = 0x10;
while (i--) spi_w(0xAAAA);
// send header checksum
spi_w(0xAAAA);
spi_w(0xAAAA);
spi_w(B2W(checksum[0] | 0xAA, checksum[1] | 0xAA));
spi_w(B2W(checksum[2] | 0xAA, checksum[3] | 0xAA));
// calculate data checksum
checksum[0] = 0;
checksum[1] = 0;
checksum[2] = 0;
checksum[3] = 0;
p = pData;
i = DATA_SIZE / 2 / 4;
while (i--)
{
x = *p++;
checksum[0] ^= x ^ x >> 1;
x = *p++;
checksum[1] ^= x ^ x >> 1;
x = *p++;
checksum[2] ^= x ^ x >> 1;
x = *p++;
checksum[3] ^= x ^ x >> 1;
}
// send data checksum
spi_w(0xAAAA);
spi_w(0xAAAA);
spi_w(B2W(checksum[0] | 0xAA, checksum[1] | 0xAA));
spi_w(B2W(checksum[2] | 0xAA, checksum[3] | 0xAA));
// odd bits of data field
i = DATA_SIZE / 4;
p = pData;
while (i--)
{
x = *p++ >> 1 | 0xAA;
y = *p++ >> 1 | 0xAA;
spi_w(B2W(x, y));
}
// even bits of data field
i = DATA_SIZE / 4;
p = pData;
while (i--)
{
x = *p++ | 0xAA;
y = *p++ | 0xAA;
spi_w(B2W(x, y));
}
}
void SendGap(void)
{
unsigned short i = GAP_SIZE/2;
while (i--) spi_w(0xAAAA);
}
// read a track from disk
void ReadTrack(adfTYPE *drive)
{
// track number is updated in drive struct before calling this function
unsigned char sector;
unsigned char status;
unsigned char track;
unsigned short dsksync;
unsigned short dsklen;
uint16_t tmp;
//unsigned short n;
if (drive->track >= drive->tracks)
{
fdd_debugf("Illegal track read: %d\n", drive->track);
drive->track = drive->tracks - 1;
}
unsigned long lba;
if (drive->track != drive->track_prev)
{ // track step or track 0, start at beginning of track
drive->track_prev = drive->track;
sector = 0;
drive->sector_offset = sector;
lba = drive->track * SECTOR_COUNT;
}
else
{ // same track, start at next sector in track
sector = drive->sector_offset;
lba = (drive->track * SECTOR_COUNT) + sector;
}
if (!FileSeekLBA(&drive->file, lba))
{
return;
}
EnableFpga();
tmp = spi_w(0);
status = (uint8_t)(tmp>>8); // read request signal
track = (uint8_t)tmp; // track number (cylinder & head)
dsksync = spi_w(0); // disk sync
dsklen = spi_w(0) & 0x3FFF; // mfm words to transfer
DisableFpga();
if (track >= drive->tracks)
track = drive->tracks - 1;
while (1)
{
FileReadSec(&drive->file, sector_buffer);
EnableFpga();
// check if FPGA is still asking for data
tmp = spi_w(0);
status = (uint8_t)(tmp >> 8); // read request signal
track = (uint8_t)tmp; // track number (cylinder & head)
dsksync = spi_w(0); // disk sync
dsklen = spi_w(0) & 0x3FFF; // mfm words to transfer
if (track >= drive->tracks)
track = drive->tracks - 1;
// workaround for Copy Lock in Wiz'n'Liz and North&South (might brake other games)
if (dsksync == 0x0000 || dsksync == 0x8914 || dsksync == 0xA144)
dsksync = 0x4489;
// North&South: $A144
// Wiz'n'Liz (Copy Lock): $8914
// Prince of Persia: $4891
// Commando: $A245
// some loaders stop dma if sector header isn't what they expect
// because we don't check dma transfer count after sending a word
// the track can be changed while we are sending the rest of the previous sector
// in this case let's start transfer from the beginning
if (track == drive->track)
{
// send sector if fpga is still asking for data
if (status & CMD_RDTRK)
{
//GenerateHeader(sector_header, sector_buffer, sector, track, dsksync);
//SendSector(sector_header, sector_buffer);
SendSector(sector_buffer, sector, track, (unsigned char)(dsksync >> 8), (unsigned char)dsksync);
if (sector == LAST_SECTOR)
SendGap();
}
}
// we are done accessing FPGA
DisableFpga();
// track has changed
if (track != drive->track)
break;
// read dma request
if (!(status & CMD_RDTRK))
break;
sector++;
if (sector >= SECTOR_COUNT)
{
// go to the start of current track
sector = 0;
lba = drive->track * SECTOR_COUNT;
if (!FileSeekLBA(&drive->file, lba))
{
return;
}
}
// remember current sector
drive->sector_offset = sector;
}
}
unsigned char FindSync(adfTYPE *drive)
// reads data from fifo till it finds sync word or fifo is empty and dma inactive (so no more data is expected)
{
unsigned char c1, c2, c3, c4;
unsigned short n;
uint16_t tmp;
while (1)
{
EnableFpga();
tmp = spi_w(0);
c1 = (uint8_t)(tmp >> 8); // write request signal
c2 = (uint8_t)tmp; // track number (cylinder & head)
if (!(c1 & CMD_WRTRK))
break;
if (c2 != drive->track)
break;
spi_w(0); //disk sync word
n = spi_w(0) & 0xBFFF; // mfm words to transfer
if (n == 0)
break;
n &= 0x3FFF;
while (n--)
{
if (spi_w(0) == 0x4489)
{
DisableFpga();
return 1;
}
}
DisableFpga();
}
DisableFpga();
return 0;
}
unsigned char GetHeader(unsigned char *pTrack, unsigned char *pSector)
// this function reads data from fifo till it finds sync word or dma is inactive
{
unsigned char c, c1, c2, c3, c4;
unsigned char i;
unsigned char checksum[4];
uint16_t tmp;
Error = 0;
while (1)
{
EnableFpga();
c1 = (uint8_t)(spi_w(0)>>8); // write request signal, track number (cylinder & head)
if (!(c1 & CMD_WRTRK))
break;
spi_w(0); //disk sync word
tmp = spi_w(0); // mfm words to transfer
if ((tmp & 0x3F00) != 0 || (tmp & 0xFF) > 24)// remaining header data is 25 mfm words
{
tmp = spi_w(0); // second sync
if (tmp != 0x4489)
{
Error = 21;
fdd_debugf("\nSecond sync word missing...\n");
break;
}
tmp = spi_w(0);
c = (uint8_t)(tmp >> 8);
checksum[0] = c;
c1 = (c & 0x55) << 1;
c = (uint8_t)tmp;
checksum[1] = c;
c2 = (c & 0x55) << 1;
tmp = spi_w(0);
c = (uint8_t)(tmp >> 8);
checksum[2] = c;
c3 = (c & 0x55) << 1;
c = (uint8_t)tmp;
checksum[3] = c;
c4 = (c & 0x55) << 1;
tmp = spi_w(0);
c = (uint8_t)(tmp >> 8);
checksum[0] ^= c;
c1 |= c & 0x55;
c = (uint8_t)tmp;
checksum[1] ^= c;
c2 |= c & 0x55;
tmp = spi_w(0);
c = (uint8_t)(tmp >> 8);
checksum[2] ^= c;
c3 |= c & 0x55;
c = (uint8_t)tmp;
checksum[3] ^= c;
c4 |= c & 0x55;
if (c1 != 0xFF) // always 0xFF
Error = 22;
else if (c2 > 159) // Track number (0-159)
Error = 23;
else if (c3 > 10) // Sector number (0-10)
Error = 24;
else if (c4 > 11 || c4 == 0) // Number of sectors to gap (1-11)
Error = 25;
if (Error)
{
fdd_debugf("\nWrong header: %u.%u.%u.%u\n", c1, c2, c3, c4);
break;
}
*pTrack = c2;
*pSector = c3;
for (i = 0; i < 8; i++)
{
tmp = spi_w(0);
checksum[0] ^= (uint8_t)(tmp >> 8);
checksum[1] ^= (uint8_t)tmp;
tmp = spi_w(0);
checksum[2] ^= (uint8_t)(tmp >> 8);
checksum[3] ^= (uint8_t)tmp;
}
checksum[0] &= 0x55;
checksum[1] &= 0x55;
checksum[2] &= 0x55;
checksum[3] &= 0x55;
tmp = (spi_w(0) & 0x5555) << 1;
c1 = (uint8_t)(tmp >> 8);
c2 = (uint8_t)tmp;
tmp = (spi_w(0) & 0x5555) << 1;
c3 = (uint8_t)(tmp >> 8);
c4 = (uint8_t)tmp;
tmp = spi_w(0) & 0x5555;
c1 |= (uint8_t)(tmp >> 8);
c2 |= (uint8_t)tmp;
tmp = spi_w(0) & 0x5555;
c3 |= (uint8_t)(tmp >> 8);
c4 |= (uint8_t)tmp;
if (c1 != checksum[0] || c2 != checksum[1] || c3 != checksum[2] || c4 != checksum[3])
{
Error = 26;
break;
}
DisableFpga();
return 1;
}
else if ((tmp & 0x8000) == 0) // not enough data for header and write dma is not active
{
Error = 20;
break;
}
DisableFpga();
}
DisableFpga();
return 0;
}
unsigned char GetData(void)
{
unsigned char c, c1, c2, c3, c4;
unsigned char i;
unsigned char *p;
unsigned short n;
unsigned char checksum[4];
uint16_t tmp;
Error = 0;
while (1)
{
EnableFpga();
c1 = (uint8_t)(spi_w(0) >> 8); // write request signal, track number (cylinder & head)
if (!(c1 & CMD_WRTRK))
break;
spi_w(0);
tmp = spi_w(0); // mfm words to transfer
n = tmp & 0x3FFF;
if (n >= 0x204)
{
tmp = (spi_w(0) & 0x5555) << 1;
c1 = (uint8_t)(tmp >> 8);
c2 = (uint8_t)tmp & 0x55;
tmp = (spi_w(0) & 0x5555) << 1;
c3 = (uint8_t)(tmp >> 8);
c4 = (uint8_t)tmp;
tmp = spi_w(0) & 0x5555;
c1 |= (uint8_t)(tmp >> 8);
c2 |= (uint8_t)tmp;
tmp = spi_w(0) & 0x5555;
c3 |= (uint8_t)(tmp >> 8);
c4 |= (uint8_t)tmp;
checksum[0] = 0;
checksum[1] = 0;
checksum[2] = 0;
checksum[3] = 0;
// odd bits of data field
i = 128;
p = sector_buffer;
do
{
tmp = spi_w(0);
c = (uint8_t)(tmp >> 8);
checksum[0] ^= c;
*p++ = (c & 0x55) << 1;
c = (uint8_t)tmp;
checksum[1] ^= c;
*p++ = (c & 0x55) << 1;
tmp = spi_w(0);
c = (uint8_t)(tmp >> 8);
checksum[2] ^= c;
*p++ = (c & 0x55) << 1;
c = (uint8_t)tmp;
checksum[3] ^= c;
*p++ = (c & 0x55) << 1;
} while (--i);
// even bits of data field
i = 128;
p = sector_buffer;
do
{
tmp = spi_w(0);
c = (uint8_t)(tmp >> 8);
checksum[0] ^= c;
*p++ |= c & 0x55;
c = (uint8_t)tmp;
checksum[1] ^= c;
*p++ |= c & 0x55;
tmp = spi_w(0);
c = (uint8_t)(tmp >> 8);
checksum[2] ^= c;
*p++ |= c & 0x55;
c = (uint8_t)tmp;
checksum[3] ^= c;
*p++ |= c & 0x55;
} while (--i);
checksum[0] &= 0x55;
checksum[1] &= 0x55;
checksum[2] &= 0x55;
checksum[3] &= 0x55;
if (c1 != checksum[0] || c2 != checksum[1] || c3 != checksum[2] || c4 != checksum[3])
{
Error = 29;
break;
}
DisableFpga();
return 1;
}
else if ((tmp & 0x8000) == 0) // not enough data in fifo and write dma is not active
{
Error = 28;
break;
}
DisableFpga();
}
DisableFpga();
return 0;
}
void WriteTrack(adfTYPE *drive)
{
unsigned char Track;
unsigned char Sector;
unsigned long lba = drive->track * SECTOR_COUNT;
drive->track_prev = drive->track + 1; // just to force next read from the start of current track
while (FindSync(drive))
{
if (GetHeader(&Track, &Sector))
{
if (Track == drive->track)
{
if (!FileSeekLBA(&drive->file, lba+Sector))
{
return;
}
if (GetData())
{
if (drive->status & DSK_WRITABLE)
{
FileWriteSec(&drive->file, sector_buffer);
}
else
{
Error = 30;
fdd_debugf("Write attempt to protected disk!\n");
}
}
}
else
Error = 27; //track number reported in sector header is not the same as current drive track
}
if (Error)
{
fdd_debugf("WriteTrack: error %u\n", Error);
ErrorMessage(" WriteTrack", Error);
}
}
}
void UpdateDriveStatus(void)
{
EnableFpga();
spi_w(0x1000 | df[0].status | (df[1].status << 1) | (df[2].status << 2) | (df[3].status << 3));
DisableFpga();
}
void HandleFDD(unsigned char c1, unsigned char c2)
{
unsigned char sel;
drives = (c1 >> 4) & 0x03; // number of active floppy drives
if (c1 & CMD_RDTRK)
{
DISKLED_ON;
sel = (c1 >> 6) & 0x03;
df[sel].track = c2;
ReadTrack(&df[sel]);
DISKLED_OFF;
}
else if (c1 & CMD_WRTRK)
{
DISKLED_ON;
sel = (c1 >> 6) & 0x03;
df[sel].track = c2;
WriteTrack(&df[sel]);
DISKLED_OFF;
}
}

31
fdd.h Normal file
View File

@@ -0,0 +1,31 @@
#ifndef FDD_H
#define FDD_H
#include "file_io.h"
// floppy disk interface defs
#define CMD_RDTRK 0x01
#define CMD_WRTRK 0x02
// floppy status
#define DSK_INSERTED 0x01 /*disk is inserted*/
#define DSK_WRITABLE 0x10 /*disk is writable*/
#define MAX_TRACKS (83*2)
typedef struct
{
fileTYPE file;
unsigned char status; /*status of floppy*/
unsigned char tracks; /*number of tracks*/
unsigned char sector_offset; /*sector offset to handle tricky loaders*/
unsigned char track; /*current track*/
unsigned char track_prev; /*previous track*/
char name[1024]; /*floppy name*/
} adfTYPE;
void UpdateDriveStatus(void);
void HandleFDD(unsigned char c1, unsigned char c2);
#endif

682
file_io.c Normal file
View File

@@ -0,0 +1,682 @@
#include "file_io.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <ctype.h>
#include <sys/vfs.h>
#include <linux/magic.h>
#include "osd.h"
#include "fpga_io.h"
int nDirEntries = 0;
struct dirent DirItem[1000];
int iSelectedEntry = 0; // selected entry index
int iFirstEntry = 0;
void FileClose(fileTYPE *file)
{
if (file->fd > 0) close(file->fd);
file->fd = -1;
}
static char full_path[1200];
unsigned char FileOpenEx(fileTYPE *file, const char *name, int mode)
{
sprintf(full_path, "%s/%s", getRootDir(), name);
FileClose(file);
file->mode = 0;
char *p = strrchr(full_path, '/');
strcpy(file->name, p+1);
file->fd = open(full_path, mode);
if (file->fd < 0)
{
printf("FileOpenEx(open) File:%s, error: %d.\n", full_path, file->fd);
file->fd = -1;
return 0;
}
struct stat64 st;
int ret = fstat64(file->fd, &st);
if ( ret < 0)
{
printf("FileOpenEx(fstat) File:%s, error: %d.\n", full_path, ret);
return 0;
}
file->size = st.st_size;
file->offset = 0;
file->mode = mode;
// printf("opened %s, size %lu\n", full_path, file->size);
return 1;
}
unsigned char FileOpen(fileTYPE *file, const char *name)
{
return FileOpenEx(file, name, O_RDONLY);
}
unsigned char FileNextSector(fileTYPE *file)
{
__off64_t newoff = lseek64(file->fd, file->offset + 512, SEEK_SET);
if (newoff != file->offset + 512)
{
//printf("Fail to seek to next sector. File: %s.\n", file->name);
lseek64(file->fd, file->offset, SEEK_SET);
return 0;
}
file->offset = newoff;
return 1;
}
unsigned char FileSeek(fileTYPE *file, __off64_t offset, unsigned long origin)
{
__off64_t newoff = lseek64(file->fd, offset, origin);
if(newoff<0)
{
printf("Fail to seek the file.\n");
return 0;
}
file->offset = newoff;
return 1;
}
unsigned char FileSeekLBA(fileTYPE *file, uint32_t offset)
{
__off64_t off64 = offset;
off64 <<= 9;
return FileSeek(file, off64, SEEK_SET);
}
// Read. MiST compatible. Avoid to use it.
unsigned char FileRead(fileTYPE *file, void *pBuffer)
{
return FileReadEx(file, pBuffer, 1);
}
unsigned char FileReadEx(fileTYPE *file, void *pBuffer, unsigned long nSize)
{
static uint8_t tmpbuff[512];
if (!FileSeek(file, file->offset, SEEK_SET))
{
printf("FileRead error(seek).\n");
return 0;
}
if (!pBuffer)
{
for (int i = 0; i < nSize; i++)
{
int ret = read(file->fd, tmpbuff, 512);
if (ret < 0)
{
printf("FileRead error(%d).\n", ret);
return 0;
}
EnableDMode();
spi_block_write(tmpbuff, 0);
DisableDMode();
}
}
else
{
int ret = read(file->fd, pBuffer, nSize * 512);
if (ret < 0)
{
printf("FileRead error(%d).\n", ret);
return 0;
}
}
return 1;
}
// Write. MiST compatible. Avoid to use it.
unsigned char FileWrite(fileTYPE *file, void *pBuffer)
{
if (!FileSeek(file, file->offset, SEEK_SET))
{
printf("FileWrite error(seek).\n");
return 0;
}
int ret = write(file->fd, pBuffer, 512);
if (ret < 0)
{
printf("FileWrite error(%d).\n", ret);
return 0;
}
return 1;
}
// Read with offset advancing
unsigned long FileReadAdv(fileTYPE *file, void *pBuffer, unsigned long length)
{
ssize_t ret = read(file->fd, pBuffer, length);
if (ret < 0)
{
printf("FileReadAdv error(%d).\n", ret);
return 0;
}
file->offset += ret;
return ret;
}
unsigned long FileReadSec(fileTYPE *file, void *pBuffer)
{
return FileReadAdv(file, pBuffer, 512);
}
// Write with offset advancing
unsigned long FileWriteAdv(fileTYPE *file, void *pBuffer, unsigned long length)
{
int ret = write(file->fd, pBuffer, length);
if (ret < 0)
{
printf("FileWriteAdv error(%d).\n", ret);
return 0;
}
file->offset += ret;
return ret;
}
unsigned long FileWriteSec(fileTYPE *file, void *pBuffer)
{
return FileWriteAdv(file, pBuffer, 512);
}
unsigned char FileSave(char *name, void *pBuffer, int size)
{
sprintf(full_path, "%s/%s", getRootDir(), name);
int fd = open(full_path, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRWXU | S_IRWXG | S_IRWXO);
if (fd < 0)
{
printf("FileSave(open) File:%s, error: %d.\n", full_path, fd);
return 0;
}
int ret = write(fd, pBuffer, size);
if (ret < 0)
{
printf("FileSave(write) File:%s, error: %d.\n", full_path, ret);
return 0;
}
close(fd);
return 1;
}
int FileLoad(char *name, void *pBuffer, int size)
{
sprintf(full_path, "%s/%s", getRootDir(), name);
int fd = open(full_path, O_RDONLY);
if (fd < 0)
{
printf("FileLoad(open) File:%s, error: %d.\n", full_path, fd);
return 0;
}
struct stat64 st;
int ret = fstat64(fd, &st);
if (ret < 0)
{
printf("FileLoad(fstat) File:%s, error: %d.\n", full_path, ret);
return -1;
}
ret = read(fd, pBuffer, size ? size : st.st_size);
if (ret < 0)
{
printf("FileLoad(write) File:%s, error: %d.\n", full_path, ret);
return 0;
}
close(fd);
return ret;
}
int FileCanWrite(char *name)
{
sprintf(full_path, "%s/%s", getRootDir(), name);
struct stat64 st;
int ret = stat64(full_path, &st);
if (ret < 0)
{
printf("FileCanWrite(stat) File:%s, error: %d.\n", full_path, ret);
return 0;
}
//printf("FileCanWrite: mode=%04o.\n", st.st_mode);
return ((st.st_mode & S_IWUSR) != 0);
}
char *make_name(char *short_name)
{
static char name[16];
memset(name, 0, sizeof(name));
memcpy(name, short_name, 8);
memcpy(name + 10, short_name + 8, 3);
for (int i = 7; i >= 0; i--)
{
if (name[i] <= 0x20) name[i] = 0;
else break;
}
for (int i = 12; i >= 10; i--)
{
if (name[i] <= 0x20) name[i] = 0;
else break;
}
if (strlen(name + 10))
{
strcat(name, ".");
strcat(name, name + 10);
}
return name;
}
static int device = 0;
static int usbnum = 0;
char *getRootDir()
{
static char dev[16];
if(!device) return "/media/fat";
sprintf(dev, "/media/usb%d", usbnum);
return dev;
}
void setStorage(int dev)
{
device = 0;
FileSave("device.bin", &dev, sizeof(int));
app_restart();
}
static int orig_device = 0;
int getStorage(int from_setting)
{
return from_setting ? orig_device : device;
}
int isPathMounted(int n)
{
char path[32];
sprintf(path, "/media/usb%d", n);
struct stat file_stat;
struct stat parent_stat;
if (-1 == stat(path, &file_stat))
{
printf("failed to stat %s\n", path);
return 0;
}
if (!(file_stat.st_mode & S_IFDIR))
{
printf("%s is not a directory.\n", path);
return 0;
}
if (-1 == stat("/media", &parent_stat))
{
printf("failed to stat /media\n");
return 0;
}
if (file_stat.st_dev != parent_stat.st_dev ||
(file_stat.st_dev == parent_stat.st_dev &&
file_stat.st_ino == parent_stat.st_ino))
{
printf("%s IS a mountpoint.\n", path);
struct statfs fs_stat;
if (!statfs(path, &fs_stat))
{
printf("%s is FS: 0x%08X\n", path, fs_stat.f_type);
if (fs_stat.f_type != EXT4_SUPER_MAGIC)
{
return 1;
}
}
}
printf("%s is NOT a VFAT mountpoint.\n", path);
return 0;
}
int isUSBMounted()
{
for (int i = 0; i < 4; i++)
{
if (isPathMounted(i))
{
usbnum = i;
return 1;
}
}
return 0;
}
void FindStorage(void)
{
printf("Looking for root device...\n");
device = 0;
FileLoad("device.bin", &device, sizeof(int));
orig_device = device;
if (device)
{
printf("Waiting for USB...\n");
int btn = 0;
int done = 0;
for (int i = 0; i < 10; i++)
{
if (isUSBMounted()) done = 1;
if (done) break;
for (int i = 0; i < 10; i++)
{
btn = fpga_get_buttons();
if (btn)
{
printf("Button has been pressed %d\n", btn);
device = 0;
done = 1;
break;
}
usleep(100000);
}
}
if (!done) device = 0;
}
if (device)
{
printf("Using USB as a root device\n");
}
else
{
printf("Using SD card as a root device\n");
}
}
int de_cmp(const void *e1, const void *e2)
{
const struct dirent *de1 = e1;
const struct dirent *de2 = e2;
if ((de1->d_type == DT_DIR) && !strcmp(de1->d_name, "..")) return -1;
if ((de2->d_type == DT_DIR) && !strcmp(de1->d_name, "..")) return 1;
if ((de1->d_type == DT_DIR) && (de2->d_type == DT_REG)) return -1;
if ((de1->d_type == DT_REG) && (de2->d_type == DT_DIR)) return 1;
if ((de1->d_type == DT_REG) && (de2->d_type == DT_REG))
{
int len1 = strlen(de1->d_name);
int len2 = strlen(de2->d_name);
if ((len1 > 4) && (de1->d_name[len1 - 4] == '.')) len1 -= 4;
if ((len2 > 4) && (de2->d_name[len2 - 4] == '.')) len2 -= 4;
int len = (len1 < len2) ? len1 : len2;
int ret = strncasecmp(de1->d_name, de2->d_name, len);
if (!ret)
{
return len1 - len2;
}
return ret;
}
return strcasecmp(de1->d_name, de2->d_name);
}
void AdjustDirectory(char *path)
{
sprintf(full_path, "%s/%s", getRootDir(), path);
struct stat64 st;
int ret = stat64(full_path, &st);
if (ret < 0)
{
printf("AdjustDirectory(stat) path:%s, error: %d.\n", full_path, ret);
path[0] = 0;
return;
}
if (st.st_mode & S_IFDIR) return;
char *p = strrchr(path, '/');
if (p)
{
*p = 0;
}
else
{
path[0] = 0;
}
}
int ScanDirectory(char* path, unsigned long mode, char *extension, unsigned char options)
{
int extlen = strlen(extension);
//printf("scan dir\n");
if (mode == SCAN_INIT)
{
sprintf(full_path, "%s/%s", getRootDir(), path);
printf("Start to scan dir: %s\n", full_path);
iFirstEntry = 0;
iSelectedEntry = 0;
nDirEntries = 0;
DIR *d = opendir(full_path);
if (!d)
{
printf("Couldn't open dir: %s\n", full_path);
return 0;
}
struct dirent *de;
while(nDirEntries < 1000)
{
de = readdir(d);
if (de == NULL) break;
if (de->d_type == DT_DIR)
{
if (!strcmp(de->d_name, ".")) continue;
if (!strcmp(de->d_name, ".."))
{
if(!strlen(path)) continue;
}
if (!(options & SCAN_DIR)) continue;
}
else if (de->d_type == DT_REG)
{
if (!strcasecmp(de->d_name, "menu.rbf")) continue;
if (extlen > 0)
{
int len = strlen(de->d_name);
char *ext = extension;
int found = 0;
while(*ext)
{
char e[5];
memcpy(e+1, ext, 3);
if (e[3] == 0x20)
{
e[3] = 0;
if (e[2] == 0x20)
{
e[2] = 0;
}
}
e[0] = '.';
e[4] = 0;
int l = strlen(e);
if((len>l) && !strncasecmp(de->d_name + len - l, e, l))
{
found = 1;
break;
}
if (strlen(ext) < 3) break;
ext += 3;
}
if (!found) continue;
}
}
else
{
continue;
}
memcpy(&DirItem[nDirEntries], de, sizeof(struct dirent));
nDirEntries++;
}
closedir(d);
printf("Got %d dir entries\n", nDirEntries);
if (!nDirEntries) return 0;
qsort(DirItem, nDirEntries, sizeof(struct dirent), de_cmp);
}
else
{
if (nDirEntries == 0) // directory is empty so there is no point in searching for any entry
return 0;
if (mode == SCAN_NEXT)
{
if(iSelectedEntry + 1 < nDirEntries) // scroll within visible items
{
iSelectedEntry++;
if (iSelectedEntry > iFirstEntry + OsdGetSize() - 1) iFirstEntry = iSelectedEntry - OsdGetSize() + 1;
}
return 0;
}
else if (mode == SCAN_PREV)
{
if (iSelectedEntry > 0) // scroll within visible items
{
iSelectedEntry--;
if (iSelectedEntry < iFirstEntry) iFirstEntry = iSelectedEntry;
}
return 0;
}
else if (mode == SCAN_NEXT_PAGE)
{
if (iSelectedEntry < iFirstEntry + OsdGetSize() - 1)
{
iSelectedEntry = iFirstEntry + OsdGetSize() - 1;
if (iSelectedEntry >= nDirEntries) iSelectedEntry = nDirEntries - 1;
}
else
{
iSelectedEntry += OsdGetSize();
iFirstEntry += OsdGetSize();
if (iSelectedEntry >= nDirEntries)
{
iSelectedEntry = nDirEntries - 1;
iFirstEntry = iSelectedEntry - OsdGetSize() + 1;
if (iFirstEntry < 0) iFirstEntry = 0;
}
else if (iFirstEntry + OsdGetSize() > nDirEntries)
{
iFirstEntry = nDirEntries - OsdGetSize();
}
}
return 0;
}
else if (mode == SCAN_PREV_PAGE)
{
if(iSelectedEntry != iFirstEntry)
{
iSelectedEntry = iFirstEntry;
}
else
{
iFirstEntry -= OsdGetSize();
if (iFirstEntry < 0) iFirstEntry = 0;
iSelectedEntry = iFirstEntry;
}
}
else if (mode == SCAN_SET_ITEM)
{
for (int i = 0; i < nDirEntries; i++)
{
if((DirItem[i].d_type == DT_DIR) && !strcmp(DirItem[i].d_name, extension))
{
iSelectedEntry = i;
if (iSelectedEntry + (OsdGetSize() / 2) - 1 >= nDirEntries) iFirstEntry = nDirEntries - OsdGetSize();
else iFirstEntry = iSelectedEntry - (OsdGetSize() / 2) + 1;
if (iFirstEntry < 0) iFirstEntry = 0;
break;
}
}
}
else
{
printf("dir scan for key: %x/%c\n", mode, mode);
mode = toupper(mode);
if ((mode >= '0' && mode <= '9') || (mode >= 'A' && mode <= 'Z'))
{
int found = -1;
for (int i = iSelectedEntry+1; i < nDirEntries; i++)
{
if (toupper(DirItem[i].d_name[0]) == mode)
{
found = i;
break;
}
}
if (found < 0)
{
for (int i = 0; i < nDirEntries; i++)
{
if (toupper(DirItem[i].d_name[0]) == mode)
{
found = i;
break;
}
}
}
if (found >= 0)
{
iSelectedEntry = found;
if (iSelectedEntry + (OsdGetSize() / 2) - 1 >= nDirEntries) iFirstEntry = nDirEntries - OsdGetSize();
else iFirstEntry = iSelectedEntry - (OsdGetSize()/2) + 1;
if (iFirstEntry < 0) iFirstEntry = 0;
}
}
}
}
return 0;
}

70
file_io.h Normal file
View File

@@ -0,0 +1,70 @@
#ifndef _FAT16_H_INCLUDED
#define _FAT16_H_INCLUDED
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include "spi.h"
typedef struct
{
int fd;
int mode;
__off64_t size;
__off64_t offset;
char name[261];
} fileTYPE;
extern int nDirEntries;
extern struct dirent DirItem[1000];
extern int iSelectedEntry;
extern int iFirstEntry;
// scanning flags
#define SCAN_INIT 0 // start search from beginning of directory
#define SCAN_NEXT 1 // find next file in directory
#define SCAN_PREV -1 // find previous file in directory
#define SCAN_NEXT_PAGE 2 // find next 8 files in directory
#define SCAN_PREV_PAGE -2 // find previous 8 files in directory
#define SCAN_SET_ITEM 3 // find exact item
// options flags
#define SCAN_DIR 1 // include subdirectories
void FindStorage();
int getStorage(int from_setting);
void setStorage(int dev);
int isUSBMounted();
unsigned char FileOpenEx(fileTYPE *file, const char *name, int mode);
unsigned char FileOpen(fileTYPE *file, const char *name);
void FileClose(fileTYPE *file);
unsigned char FileSeek(fileTYPE *file, __off64_t offset, unsigned long origin);
unsigned char FileSeekLBA(fileTYPE *file, uint32_t offset);
//MiST compatible functions. Avoid to use them.
unsigned char FileRead(fileTYPE *file, void *pBuffer);
unsigned char FileReadEx(fileTYPE *file, void *pBuffer, unsigned long nSize);
unsigned char FileWrite(fileTYPE *file, void *pBuffer);
unsigned char FileNextSector(fileTYPE *file);
//New functions.
unsigned long FileReadAdv(fileTYPE *file, void *pBuffer, unsigned long length);
unsigned long FileReadSec(fileTYPE *file, void *pBuffer);
unsigned long FileWriteAdv(fileTYPE *file, void *pBuffer, unsigned long length);
unsigned long FileWriteSec(fileTYPE *file, void *pBuffer);
int FileCanWrite(char *name);
unsigned char FileSave(char *name, void *pBuffer, int size);
int FileLoad(char *name, void *pBuffer, int size);
void AdjustDirectory(char *path);
int ScanDirectory(char* path, unsigned long mode, char *extension, unsigned char options);
char *make_name(char *short_name);
char *getRootDir();
#endif

62
fpga_base_addr_ac5.h Normal file
View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2012 Altera Corporation <www.altera.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef _SOCFPGA_BASE_ADDRS_H_
#define _SOCFPGA_BASE_ADDRS_H_
#define SOCFPGA_DAP_ADDRESS 0xff000000
#define SOCFPGA_EMAC0_ADDRESS 0xff700000
#define SOCFPGA_EMAC1_ADDRESS 0xff702000
#define SOCFPGA_SDMMC_ADDRESS 0xff704000
#define SOCFPGA_QSPI_ADDRESS 0xff705000
#define SOCFPGA_MGR_ADDRESS 0xff706000
#define SOCFPGA_GPIO0_ADDRESS 0xff708000
#define SOCFPGA_GPIO1_ADDRESS 0xff709000
#define SOCFPGA_GPIO2_ADDRESS 0xff70a000
#define SOCFPGA_L3REGS_ADDRESS 0xff800000
#define SOCFPGA_USB0_ADDRESS 0xffb00000
#define SOCFPGA_USB1_ADDRESS 0xffb40000
#define SOCFPGA_CAN0_ADDRESS 0xffc00000
#define SOCFPGA_CAN1_ADDRESS 0xffc01000
#define SOCFPGA_UART0_ADDRESS 0xffc02000
#define SOCFPGA_UART1_ADDRESS 0xffc03000
#define SOCFPGA_I2C0_ADDRESS 0xffc04000
#define SOCFPGA_I2C1_ADDRESS 0xffc05000
#define SOCFPGA_I2C2_ADDRESS 0xffc06000
#define SOCFPGA_I2C3_ADDRESS 0xffc07000
#define SOCFPGA_SDR_ADDRESS 0xffc20000
#define SOCFPGA_L4WD0_ADDRESS 0xffd02000
#define SOCFPGA_L4WD1_ADDRESS 0xffd03000
#define SOCFPGA_CLKMGR_ADDRESS 0xffd04000
#define SOCFPGA_RSTMGR_ADDRESS 0xffd05000
#define SOCFPGA_SYSMGR_ADDRESS 0xffd08000
#define SOCFPGA_SPIS0_ADDRESS 0xffe02000
#define SOCFPGA_SPIS1_ADDRESS 0xffe03000
#define SOCFPGA_SPIM0_ADDRESS 0xfff00000
#define SOCFPGA_SPIM1_ADDRESS 0xfff01000
#define SOCFPGA_SCANMGR_ADDRESS 0xfff02000
#define SOCFPGA_ROM_ADDRESS 0xfffd0000
#define SOCFPGA_MPUSCU_ADDRESS 0xfffec000
#define SOCFPGA_MPUL2_ADDRESS 0xfffef000
#define SOCFPGA_OCRAM_ADDRESS 0xffff0000
#define SOCFPGA_LWFPGASLAVES_ADDRESS 0xff200000
#define SOCFPGA_LWHPS2FPGAREGS_ADDRESS 0xff400000
#define SOCFPGA_HPS2FPGAREGS_ADDRESS 0xff500000
#define SOCFPGA_FPGA2HPSREGS_ADDRESS 0xff600000
#define SOCFPGA_FPGAMGRREGS_ADDRESS 0xff706000
#define SOCFPGA_ACPIDMAP_ADDRESS 0xff707000
#define SOCFPGA_NANDDATA_ADDRESS 0xff900000
#define SOCFPGA_QSPIDATA_ADDRESS 0xffa00000
#define SOCFPGA_NANDREGS_ADDRESS 0xffb80000
#define SOCFPGA_FPGAMGRDATA_ADDRESS 0xffb90000
#define SOCFPGA_SPTIMER0_ADDRESS 0xffc08000
#define SOCFPGA_SPTIMER1_ADDRESS 0xffc09000
#define SOCFPGA_OSC1TIMER0_ADDRESS 0xffd00000
#define SOCFPGA_OSC1TIMER1_ADDRESS 0xffd01000
#define SOCFPGA_DMANONSECURE_ADDRESS 0xffe00000
#define SOCFPGA_DMASECURE_ADDRESS 0xffe01000
#endif /* _SOCFPGA_BASE_ADDRS_H_ */

635
fpga_io.c Normal file
View File

@@ -0,0 +1,635 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include "fpga_io.h"
#include "file_io.h"
#include "fpga_base_addr_ac5.h"
#include "fpga_manager.h"
#include "fpga_system_manager.h"
#include "fpga_reset_manager.h"
#include "fpga_nic301.h"
#define FPGA_REG_BASE 0xFF000000
#define FPGA_REG_SIZE 0x01000000
#define MAP_ADDR(x) (volatile uint32_t*)(&map_base[(((uint32_t)(x)) & 0xFFFFFF)>>2])
#define IS_REG(x) (((((uint32_t)(x))-1)>=(FPGA_REG_BASE - 1)) && ((((uint32_t)(x))-1)<(FPGA_REG_BASE + FPGA_REG_SIZE - 1)))
#define fatal(x) munmap((void*)map_base, FPGA_REG_SIZE); close(fd); exit(x)
static struct socfpga_reset_manager *reset_regs = (void *)SOCFPGA_RSTMGR_ADDRESS;
static struct socfpga_fpga_manager *fpgamgr_regs = (void *)SOCFPGA_FPGAMGRREGS_ADDRESS;
static struct socfpga_system_manager *sysmgr_regs = (void *)SOCFPGA_SYSMGR_ADDRESS;
static struct nic301_registers *nic301_regs = (void *)SOCFPGA_L3REGS_ADDRESS;
static uint32_t *map_base;
static int fd;
static __inline void writel(uint32_t val, const void* reg)
{
/*
if(!IS_REG(reg))
{
printf("ERROR: Trying to write undefined address: %p\n.", reg);
fatal(-1);
}
*/
*MAP_ADDR(reg) = val;
}
static __inline uint32_t readl(const void* reg)
{
/*
if (!IS_REG(reg))
{
printf("ERROR: Trying to read undefined address: %p\n.", reg);
fatal(-1);
}
*/
return *MAP_ADDR(reg);
}
#define clrsetbits_le32(addr, clear, set) writel((readl(addr) & ~(clear)) | (set), addr)
#define setbits_le32(addr, set) writel( readl(addr) | (set), addr)
#define clrbits_le32(addr, clear) writel( readl(addr) & ~(clear), addr)
/* Timeout count */
#define FPGA_TIMEOUT_CNT 0x1000000
/* Set CD ratio */
static void fpgamgr_set_cd_ratio(unsigned long ratio)
{
clrsetbits_le32(&fpgamgr_regs->ctrl,
0x3 << FPGAMGRREGS_CTRL_CDRATIO_LSB,
(ratio & 0x3) << FPGAMGRREGS_CTRL_CDRATIO_LSB);
}
static int fpgamgr_dclkcnt_set(unsigned long cnt)
{
unsigned long i;
/* Clear any existing done status */
if (readl(&fpgamgr_regs->dclkstat))
writel(0x1, &fpgamgr_regs->dclkstat);
/* Write the dclkcnt */
writel(cnt, &fpgamgr_regs->dclkcnt);
/* Wait till the dclkcnt done */
for (i = 0; i < FPGA_TIMEOUT_CNT; i++) {
if (!readl(&fpgamgr_regs->dclkstat))
continue;
writel(0x1, &fpgamgr_regs->dclkstat);
return 0;
}
return -ETIMEDOUT;
}
/* Check whether FPGA Init_Done signal is high */
static int is_fpgamgr_initdone_high(void)
{
unsigned long val;
val = readl(&fpgamgr_regs->gpio_ext_porta);
return val & FPGAMGRREGS_MON_GPIO_EXT_PORTA_ID_MASK;
}
/* Get the FPGA mode */
static int fpgamgr_get_mode(void)
{
unsigned long val;
val = readl(&fpgamgr_regs->stat);
return val & FPGAMGRREGS_STAT_MODE_MASK;
}
/* Check whether FPGA is ready to be accessed */
static int fpgamgr_test_fpga_ready(void)
{
/* Check for init done signal */
if (!is_fpgamgr_initdone_high())
return 0;
/* Check again to avoid false glitches */
if (!is_fpgamgr_initdone_high())
return 0;
if (fpgamgr_get_mode() != FPGAMGRREGS_MODE_USERMODE)
return 0;
return 1;
}
/* Poll until FPGA is ready to be accessed or timeout occurred */
static int fpgamgr_poll_fpga_ready(void)
{
unsigned long i;
/* If FPGA is blank, wait till WD invoke warm reset */
for (i = 0; i < FPGA_TIMEOUT_CNT; i++) {
/* check for init done signal */
if (!is_fpgamgr_initdone_high())
continue;
/* check again to avoid false glitches */
if (!is_fpgamgr_initdone_high())
continue;
return 1;
}
return 0;
}
/* Start the FPGA programming by initialize the FPGA Manager */
static int fpgamgr_program_init(void)
{
unsigned long msel, i;
/* Get the MSEL value */
msel = readl(&fpgamgr_regs->stat);
msel &= FPGAMGRREGS_STAT_MSEL_MASK;
msel >>= FPGAMGRREGS_STAT_MSEL_LSB;
/*
* Set the cfg width
* If MSEL[3] = 1, cfg width = 32 bit
*/
if (msel & 0x8) {
setbits_le32(&fpgamgr_regs->ctrl,
FPGAMGRREGS_CTRL_CFGWDTH_MASK);
/* To determine the CD ratio */
/* MSEL[1:0] = 0, CD Ratio = 1 */
if ((msel & 0x3) == 0x0)
fpgamgr_set_cd_ratio(CDRATIO_x1);
/* MSEL[1:0] = 1, CD Ratio = 4 */
else if ((msel & 0x3) == 0x1)
fpgamgr_set_cd_ratio(CDRATIO_x4);
/* MSEL[1:0] = 2, CD Ratio = 8 */
else if ((msel & 0x3) == 0x2)
fpgamgr_set_cd_ratio(CDRATIO_x8);
}
else { /* MSEL[3] = 0 */
clrbits_le32(&fpgamgr_regs->ctrl,
FPGAMGRREGS_CTRL_CFGWDTH_MASK);
/* To determine the CD ratio */
/* MSEL[1:0] = 0, CD Ratio = 1 */
if ((msel & 0x3) == 0x0)
fpgamgr_set_cd_ratio(CDRATIO_x1);
/* MSEL[1:0] = 1, CD Ratio = 2 */
else if ((msel & 0x3) == 0x1)
fpgamgr_set_cd_ratio(CDRATIO_x2);
/* MSEL[1:0] = 2, CD Ratio = 4 */
else if ((msel & 0x3) == 0x2)
fpgamgr_set_cd_ratio(CDRATIO_x4);
}
/* To enable FPGA Manager configuration */
clrbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_NCE_MASK);
/* To enable FPGA Manager drive over configuration line */
setbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_EN_MASK);
/* Put FPGA into reset phase */
setbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_NCONFIGPULL_MASK);
/* (1) wait until FPGA enter reset phase */
for (i = 0; i < FPGA_TIMEOUT_CNT; i++) {
if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_RESETPHASE)
break;
}
/* If not in reset state, return error */
if (fpgamgr_get_mode() != FPGAMGRREGS_MODE_RESETPHASE) {
puts("FPGA: Could not reset\n");
return -1;
}
/* Release FPGA from reset phase */
clrbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_NCONFIGPULL_MASK);
/* (2) wait until FPGA enter configuration phase */
for (i = 0; i < FPGA_TIMEOUT_CNT; i++) {
if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_CFGPHASE)
break;
}
/* If not in configuration state, return error */
if (fpgamgr_get_mode() != FPGAMGRREGS_MODE_CFGPHASE) {
puts("FPGA: Could not configure\n");
return -2;
}
/* Clear all interrupts in CB Monitor */
writel(0xFFF, &fpgamgr_regs->gpio_porta_eoi);
/* Enable AXI configuration */
setbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_AXICFGEN_MASK);
return 0;
}
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
/* Write the RBF data to FPGA Manager */
static void fpgamgr_program_write(const void *rbf_data, unsigned long rbf_size)
{
uint32_t src = (uint32_t)rbf_data;
uint32_t dst = (uint32_t)MAP_ADDR(SOCFPGA_FPGAMGRDATA_ADDRESS);
/* Number of loops for 32-byte long copying. */
uint32_t loops32 = rbf_size / 32;
/* Number of loops for 4-byte long copying + trailing bytes */
uint32_t loops4 = DIV_ROUND_UP(rbf_size % 32, 4);
__asm volatile(
"1: ldmia %0!,{r0-r7} \n"
" stmia %1!,{r0-r7} \n"
" sub %1, #32 \n"
" subs %2, #1 \n"
" bne 1b \n"
" cmp %3, #0 \n"
" beq 3f \n"
"2: ldr %2, [%0], #4 \n"
" str %2, [%1] \n"
" subs %3, #1 \n"
" bne 2b \n"
"3: nop \n"
: "+r"(src), "+r"(dst), "+r"(loops32), "+r"(loops4) :
: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "cc");
}
/* Ensure the FPGA entering config done */
static int fpgamgr_program_poll_cd(void)
{
const uint32_t mask = FPGAMGRREGS_MON_GPIO_EXT_PORTA_NS_MASK |
FPGAMGRREGS_MON_GPIO_EXT_PORTA_CD_MASK;
unsigned long reg, i;
/* (3) wait until full config done */
for (i = 0; i < FPGA_TIMEOUT_CNT; i++) {
reg = readl(&fpgamgr_regs->gpio_ext_porta);
/* Config error */
if (!(reg & mask)) {
printf("FPGA: Configuration error.\n");
return -3;
}
/* Config done without error */
if (reg & mask)
break;
}
/* Timeout happened, return error */
if (i == FPGA_TIMEOUT_CNT) {
printf("FPGA: Timeout waiting for program.\n");
return -4;
}
/* Disable AXI configuration */
clrbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_AXICFGEN_MASK);
return 0;
}
/* Ensure the FPGA entering init phase */
static int fpgamgr_program_poll_initphase(void)
{
unsigned long i;
/* Additional clocks for the CB to enter initialization phase */
if (fpgamgr_dclkcnt_set(0x4))
return -5;
/* (4) wait until FPGA enter init phase or user mode */
for (i = 0; i < FPGA_TIMEOUT_CNT; i++) {
if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_INITPHASE)
break;
if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_USERMODE)
break;
}
/* If not in configuration state, return error */
if (i == FPGA_TIMEOUT_CNT)
return -6;
return 0;
}
/* Ensure the FPGA entering user mode */
static int fpgamgr_program_poll_usermode(void)
{
unsigned long i;
/* Additional clocks for the CB to exit initialization phase */
if (fpgamgr_dclkcnt_set(0x5000))
return -7;
/* (5) wait until FPGA enter user mode */
for (i = 0; i < FPGA_TIMEOUT_CNT; i++) {
if (fpgamgr_get_mode() == FPGAMGRREGS_MODE_USERMODE)
break;
}
/* If not in configuration state, return error */
if (i == FPGA_TIMEOUT_CNT)
return -8;
/* To release FPGA Manager drive over configuration line */
clrbits_le32(&fpgamgr_regs->ctrl, FPGAMGRREGS_CTRL_EN_MASK);
return 0;
}
/*
* FPGA Manager to program the FPGA. This is the interface used by FPGA driver.
* Return 0 for sucess, non-zero for error.
*/
static int socfpga_load(const void *rbf_data, size_t rbf_size)
{
unsigned long status;
if ((uint32_t)rbf_data & 0x3) {
printf("FPGA: Unaligned data, realign to 32bit boundary.\n");
return -EINVAL;
}
/* Initialize the FPGA Manager */
status = fpgamgr_program_init();
if (status)
return status;
/* Write the RBF data to FPGA Manager */
fpgamgr_program_write(rbf_data, rbf_size);
/* Ensure the FPGA entering config done */
status = fpgamgr_program_poll_cd();
if (status)
return status;
/* Ensure the FPGA entering init phase */
status = fpgamgr_program_poll_initphase();
if (status)
return status;
/* Ensure the FPGA entering user mode */
return fpgamgr_program_poll_usermode();
}
static void do_bridge(uint32_t enable)
{
if (enable)
{
writel(0x00003FFF, (void*)(SOCFPGA_SDR_ADDRESS + 0x5080));
writel(0x00000000, &reset_regs->brg_mod_reset);
writel(0x00000019, &nic301_regs->remap);
}
else
{
writel(0, &sysmgr_regs->fpgaintfgrp_module);
writel(0, (void*)(SOCFPGA_SDR_ADDRESS + 0x5080));
writel(7, &reset_regs->brg_mod_reset);
writel(1, &nic301_regs->remap);
}
}
#ifdef REBOOT_ON_RBF_LOAD
static int save_core_name(char *name)
{
if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) return -1;
void* buf = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x1FFFF000);
if (buf == (void *)-1)
{
printf("Unable to mmap(/dev/mem)\n");
close(fd);
return -1;
}
volatile char* str = (volatile char*)buf;
*str++ = 0x21;
*str++ = 0x43;
*str++ = 0x65;
*str++ = 0x87;
*str++ = 'c';
*str++ = 'o';
*str++ = 'r';
*str++ = 'e';
*str++ = '=';
*str++ = '"';
for (int i = 0; i < strlen(name); i++)
{
*str++ = name[i];
}
*str++ = '"';
*str++ = 0;
*str++ = 0;
*str++ = 0;
*str++ = 0;
munmap(buf, 0x1000);
return 0;
}
#endif
int fpga_load_rbf(char *name)
{
char path[512];
int ret = 0;
printf("Loading RBF: %s\n", name);
sprintf(path, "%s/%s", getRootDir(), name);
#ifdef REBOOT_ON_RBF_LOAD
do_bridge(0);
ret = save_core_name(name);
#else
int rbf = open(path, O_RDONLY);
if (rbf < 0)
{
printf("Couldn't open file %s\n", path);
return -1;
}
else
{
struct stat64 st;
if (fstat64(rbf, &st)<0)
{
printf("Couldn't get info of file %s\n", path);
ret = -1;
}
else
{
printf("Bitstream size: %lld bytes\n", st.st_size);
void *buf = malloc(st.st_size);
if (!buf)
{
printf("Couldn't allocate %d bytes.\n", st.st_size);
ret = -1;
}
else
{
if (read(rbf, buf, st.st_size)<st.st_size)
{
printf("Couldn't read file \n", st.st_size);
ret = -1;
}
else
{
do_bridge(0);
ret = socfpga_load(buf, st.st_size);
if (ret)
{
printf("Error %d while loading %s\n", ret, path);
}
else
{
do_bridge(1);
}
}
free(buf);
}
}
}
close(rbf);
app_restart();
#endif
return ret;
}
int fpga_io_init()
{
if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) return -1;
map_base = mmap(0, FPGA_REG_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, FPGA_REG_BASE);
if (map_base == (void *)-1)
{
printf("Unable to mmap(/dev/mem)\n");
close(fd);
return -1;
}
return 0;
}
static uint32_t gpo_copy = 0;
void fpga_gpo_write(uint32_t value)
{
gpo_copy = value;
writel(value, (void*)(SOCFPGA_MGR_ADDRESS + 0x10));
}
uint32_t fpga_gpo_read()
{
return gpo_copy; //readl((void*)(SOCFPGA_MGR_ADDRESS + 0x10));
}
uint32_t fpga_gpi_read()
{
return readl((void*)(SOCFPGA_MGR_ADDRESS + 0x14));
}
void fpga_core_write(uint32_t offset, uint32_t value)
{
if (offset <= 0x1FFFFF) writel(value, (void*)(SOCFPGA_LWFPGASLAVES_ADDRESS + (offset & ~3)));
}
uint32_t fpga_core_read(uint32_t offset)
{
if (offset <= 0x1FFFFF) return readl((void*)(SOCFPGA_LWFPGASLAVES_ADDRESS + (offset & ~3)));
return 0;
}
int fpga_core_id()
{
uint32_t gpo = (fpga_gpo_read() & 0x7FFFFFFF);
fpga_gpo_write(gpo);
uint32_t coretype = fpga_gpi_read();
gpo |= 0x80000000;
fpga_gpo_write(gpo);
if ((coretype >> 8) != 0x5CA623) return -1;
return coretype & 0xFF;
}
int fpga_get_fio_size()
{
return (fpga_gpi_read()>>16) & 1;
}
int fpga_get_io_version()
{
return (fpga_gpi_read() >> 18) & 3;
}
void fpga_set_led(uint32_t on)
{
uint32_t gpo = fpga_gpo_read();
fpga_gpo_write(on ? gpo | 0x20000000 : gpo & ~0x20000000);
}
int fpga_get_buttons()
{
fpga_gpo_write(fpga_gpo_read() | 0x80000000);
int gpi = fpga_gpi_read();
if (gpi < 0) gpi = 0; // FPGA is not in user mode. Ignore the data;
return (gpi >> 29) & 3;
}
void reboot(int cold)
{
sync();
if(cold) writel(0, &reset_regs->tstscratch);
writel(2, &reset_regs->ctrl);
while (1);
}
char *getappname()
{
static char dest[PATH_MAX];
memset(dest, 0, sizeof(dest));
char path[64];
sprintf(path, "/proc/%d/exe", getpid());
readlink(path, dest, PATH_MAX);
return dest;
}
void app_restart()
{
sync();
char *appname = getappname();
printf("restarting the %s\n", appname);
execve(appname, NULL, NULL);
printf("Something went wrong. Rebooting...\n");
reboot(0);
}
void fpga_core_reset(int run)
{
fpga_gpo_write(run ? fpga_gpo_read() | 0x40000000 : fpga_gpo_read() & ~0x40000000);
}
int fpga_ready()
{
return fpgamgr_test_fpga_ready();
}

37
fpga_io.h Normal file
View File

@@ -0,0 +1,37 @@
#include <stdint.h>
#ifndef FPGAIO_H
#define FPGAIO_H
#define DISKLED_ON fpga_set_led(1)
#define DISKLED_OFF fpga_set_led(0)
#define BUTTON_OSD 1
#define BUTTON_USR 2
int fpga_io_init();
void fpga_gpo_write(uint32_t value);
uint32_t fpga_gpo_read();
uint32_t fpga_gpi_read();
void fpga_set_led(uint32_t on);
int fpga_get_buttons();
void fpga_core_reset(int run);
void fpga_core_write(uint32_t offset, uint32_t value);
uint32_t fpga_core_read(uint32_t offset);
int fpga_core_id();
int fpga_ready();
int fpga_get_fio_size();
int fpga_get_io_version();
int fpga_load_rbf(char *name);
void reboot(int cold);
void app_restart();
char *getappname();
#endif

70
fpga_manager.h Normal file
View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2012 Altera Corporation <www.altera.com>
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _FPGA_MANAGER_H_
#define _FPGA_MANAGER_H_
struct socfpga_fpga_manager {
/* FPGA Manager Module */
uint32_t stat; /* 0x00 */
uint32_t ctrl;
uint32_t dclkcnt;
uint32_t dclkstat;
uint32_t gpo; /* 0x10 */
uint32_t gpi;
uint32_t misci; /* 0x18 */
uint32_t _pad_0x1c_0x82c[517];
/* Configuration Monitor (MON) Registers */
uint32_t gpio_inten; /* 0x830 */
uint32_t gpio_intmask;
uint32_t gpio_inttype_level;
uint32_t gpio_int_polarity;
uint32_t gpio_intstatus; /* 0x840 */
uint32_t gpio_raw_intstatus;
uint32_t _pad_0x848;
uint32_t gpio_porta_eoi;
uint32_t gpio_ext_porta; /* 0x850 */
uint32_t _pad_0x854_0x85c[3];
uint32_t gpio_1s_sync; /* 0x860 */
uint32_t _pad_0x864_0x868[2];
uint32_t gpio_ver_id_code;
uint32_t gpio_config_reg2; /* 0x870 */
uint32_t gpio_config_reg1;
};
#define FPGAMGRREGS_STAT_MODE_MASK 0x7
#define FPGAMGRREGS_STAT_MSEL_MASK 0xf8
#define FPGAMGRREGS_STAT_MSEL_LSB 3
#define FPGAMGRREGS_CTRL_CFGWDTH_MASK 0x200
#define FPGAMGRREGS_CTRL_AXICFGEN_MASK 0x100
#define FPGAMGRREGS_CTRL_NCONFIGPULL_MASK 0x4
#define FPGAMGRREGS_CTRL_NCE_MASK 0x2
#define FPGAMGRREGS_CTRL_EN_MASK 0x1
#define FPGAMGRREGS_CTRL_CDRATIO_LSB 6
#define FPGAMGRREGS_MON_GPIO_EXT_PORTA_CRC_MASK 0x8
#define FPGAMGRREGS_MON_GPIO_EXT_PORTA_ID_MASK 0x4
#define FPGAMGRREGS_MON_GPIO_EXT_PORTA_CD_MASK 0x2
#define FPGAMGRREGS_MON_GPIO_EXT_PORTA_NS_MASK 0x1
/* FPGA Mode */
#define FPGAMGRREGS_MODE_FPGAOFF 0x0
#define FPGAMGRREGS_MODE_RESETPHASE 0x1
#define FPGAMGRREGS_MODE_CFGPHASE 0x2
#define FPGAMGRREGS_MODE_INITPHASE 0x3
#define FPGAMGRREGS_MODE_USERMODE 0x4
#define FPGAMGRREGS_MODE_UNKNOWN 0x5
/* FPGA CD Ratio Value */
#define CDRATIO_x1 0x0
#define CDRATIO_x2 0x1
#define CDRATIO_x4 0x2
#define CDRATIO_x8 0x3
#endif /* _FPGA_MANAGER_H_ */

195
fpga_nic301.h Normal file
View File

@@ -0,0 +1,195 @@
/*
* Copyright (C) 2014 Marek Vasut <marex@denx.de>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef _NIC301_REGISTERS_H_
#define _NIC301_REGISTERS_H_
struct nic301_registers {
uint32_t remap; /* 0x0 */
/* Security Register Group */
uint32_t _pad_0x4_0x8[1];
uint32_t l4main;
uint32_t l4sp;
uint32_t l4mp; /* 0x10 */
uint32_t l4osc1;
uint32_t l4spim;
uint32_t stm;
uint32_t lwhps2fpgaregs; /* 0x20 */
uint32_t _pad_0x24_0x28[1];
uint32_t usb1;
uint32_t nanddata;
uint32_t _pad_0x30_0x80[20];
uint32_t usb0; /* 0x80 */
uint32_t nandregs;
uint32_t qspidata;
uint32_t fpgamgrdata;
uint32_t hps2fpgaregs; /* 0x90 */
uint32_t acp;
uint32_t rom;
uint32_t ocram;
uint32_t sdrdata; /* 0xA0 */
uint32_t _pad_0xa4_0x1fd0[1995];
/* ID Register Group */
uint32_t periph_id_4; /* 0x1FD0 */
uint32_t _pad_0x1fd4_0x1fe0[3];
uint32_t periph_id_0; /* 0x1FE0 */
uint32_t periph_id_1;
uint32_t periph_id_2;
uint32_t periph_id_3;
uint32_t comp_id_0; /* 0x1FF0 */
uint32_t comp_id_1;
uint32_t comp_id_2;
uint32_t comp_id_3;
uint32_t _pad_0x2000_0x2008[2];
/* L4 MAIN */
uint32_t l4main_fn_mod_bm_iss;
uint32_t _pad_0x200c_0x3008[1023];
/* L4 SP */
uint32_t l4sp_fn_mod_bm_iss;
uint32_t _pad_0x300c_0x4008[1023];
/* L4 MP */
uint32_t l4mp_fn_mod_bm_iss;
uint32_t _pad_0x400c_0x5008[1023];
/* L4 OSC1 */
uint32_t l4osc_fn_mod_bm_iss;
uint32_t _pad_0x500c_0x6008[1023];
/* L4 SPIM */
uint32_t l4spim_fn_mod_bm_iss;
uint32_t _pad_0x600c_0x7008[1023];
/* STM */
uint32_t stm_fn_mod_bm_iss;
uint32_t _pad_0x700c_0x7108[63];
uint32_t stm_fn_mod;
uint32_t _pad_0x710c_0x8008[959];
/* LWHPS2FPGA */
uint32_t lwhps2fpga_fn_mod_bm_iss;
uint32_t _pad_0x800c_0x8108[63];
uint32_t lwhps2fpga_fn_mod;
uint32_t _pad_0x810c_0xa008[1983];
/* USB1 */
uint32_t usb1_fn_mod_bm_iss;
uint32_t _pad_0xa00c_0xa044[14];
uint32_t usb1_ahb_cntl;
uint32_t _pad_0xa048_0xb008[1008];
/* NANDDATA */
uint32_t nanddata_fn_mod_bm_iss;
uint32_t _pad_0xb00c_0xb108[63];
uint32_t nanddata_fn_mod;
uint32_t _pad_0xb10c_0x20008[21439];
/* USB0 */
uint32_t usb0_fn_mod_bm_iss;
uint32_t _pad_0x2000c_0x20044[14];
uint32_t usb0_ahb_cntl;
uint32_t _pad_0x20048_0x21008[1008];
/* NANDREGS */
uint32_t nandregs_fn_mod_bm_iss;
uint32_t _pad_0x2100c_0x21108[63];
uint32_t nandregs_fn_mod;
uint32_t _pad_0x2110c_0x22008[959];
/* QSPIDATA */
uint32_t qspidata_fn_mod_bm_iss;
uint32_t _pad_0x2200c_0x22044[14];
uint32_t qspidata_ahb_cntl;
uint32_t _pad_0x22048_0x23008[1008];
/* FPGAMGRDATA */
uint32_t fpgamgrdata_fn_mod_bm_iss;
uint32_t _pad_0x2300c_0x23040[13];
uint32_t fpgamgrdata_wr_tidemark; /* 0x23040 */
uint32_t _pad_0x23044_0x23108[49];
uint32_t fn_mod;
uint32_t _pad_0x2310c_0x24008[959];
/* HPS2FPGA */
uint32_t hps2fpga_fn_mod_bm_iss;
uint32_t _pad_0x2400c_0x24040[13];
uint32_t hps2fpga_wr_tidemark; /* 0x24040 */
uint32_t _pad_0x24044_0x24108[49];
uint32_t hps2fpga_fn_mod;
uint32_t _pad_0x2410c_0x25008[959];
/* ACP */
uint32_t acp_fn_mod_bm_iss;
uint32_t _pad_0x2500c_0x25108[63];
uint32_t acp_fn_mod;
uint32_t _pad_0x2510c_0x26008[959];
/* Boot ROM */
uint32_t bootrom_fn_mod_bm_iss;
uint32_t _pad_0x2600c_0x26108[63];
uint32_t bootrom_fn_mod;
uint32_t _pad_0x2610c_0x27008[959];
/* On-chip RAM */
uint32_t ocram_fn_mod_bm_iss;
uint32_t _pad_0x2700c_0x27040[13];
uint32_t ocram_wr_tidemark; /* 0x27040 */
uint32_t _pad_0x27044_0x27108[49];
uint32_t ocram_fn_mod;
uint32_t _pad_0x2710c_0x42024[27590];
/* DAP */
uint32_t dap_fn_mod2;
uint32_t dap_fn_mod_ahb;
uint32_t _pad_0x4202c_0x42100[53];
uint32_t dap_read_qos; /* 0x42100 */
uint32_t dap_write_qos;
uint32_t dap_fn_mod;
uint32_t _pad_0x4210c_0x43100[1021];
/* MPU */
uint32_t mpu_read_qos; /* 0x43100 */
uint32_t mpu_write_qos;
uint32_t mpu_fn_mod;
uint32_t _pad_0x4310c_0x44028[967];
/* SDMMC */
uint32_t sdmmc_fn_mod_ahb;
uint32_t _pad_0x4402c_0x44100[53];
uint32_t sdmmc_read_qos; /* 0x44100 */
uint32_t sdmmc_write_qos;
uint32_t sdmmc_fn_mod;
uint32_t _pad_0x4410c_0x45100[1021];
/* DMA */
uint32_t dma_read_qos; /* 0x45100 */
uint32_t dma_write_qos;
uint32_t dma_fn_mod;
uint32_t _pad_0x4510c_0x46040[973];
/* FPGA2HPS */
uint32_t fpga2hps_wr_tidemark; /* 0x46040 */
uint32_t _pad_0x46044_0x46100[47];
uint32_t fpga2hps_read_qos; /* 0x46100 */
uint32_t fpga2hps_write_qos;
uint32_t fpga2hps_fn_mod;
uint32_t _pad_0x4610c_0x47100[1021];
/* ETR */
uint32_t etr_read_qos; /* 0x47100 */
uint32_t etr_write_qos;
uint32_t etr_fn_mod;
uint32_t _pad_0x4710c_0x48100[1021];
/* EMAC0 */
uint32_t emac0_read_qos; /* 0x48100 */
uint32_t emac0_write_qos;
uint32_t emac0_fn_mod;
uint32_t _pad_0x4810c_0x49100[1021];
/* EMAC1 */
uint32_t emac1_read_qos; /* 0x49100 */
uint32_t emac1_write_qos;
uint32_t emac1_fn_mod;
uint32_t _pad_0x4910c_0x4a028[967];
/* USB0 */
uint32_t usb0_fn_mod_ahb;
uint32_t _pad_0x4a02c_0x4a100[53];
uint32_t usb0_read_qos; /* 0x4A100 */
uint32_t usb0_write_qos;
uint32_t usb0_fn_mod;
uint32_t _pad_0x4a10c_0x4b100[1021];
/* NAND */
uint32_t nand_read_qos; /* 0x4B100 */
uint32_t nand_write_qos;
uint32_t nand_fn_mod;
uint32_t _pad_0x4b10c_0x4c028[967];
/* USB1 */
uint32_t usb1_fn_mod_ahb;
uint32_t _pad_0x4c02c_0x4c100[53];
uint32_t usb1_read_qos; /* 0x4C100 */
uint32_t usb1_write_qos;
uint32_t usb1_fn_mod;
};
#endif /* _NIC301_REGISTERS_H_ */

74
fpga_reset_manager.h Normal file
View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2012 Altera Corporation <www.altera.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef _RESET_MANAGER_H_
#define _RESET_MANAGER_H_
struct socfpga_reset_manager {
uint32_t status;
uint32_t ctrl;
uint32_t counts;
uint32_t padding1;
uint32_t mpu_mod_reset;
uint32_t per_mod_reset;
uint32_t per2_mod_reset;
uint32_t brg_mod_reset;
uint32_t misc_mod_reset;
uint32_t padding2[12];
uint32_t tstscratch;
};
#if defined(CONFIG_SOCFPGA_VIRTUAL_TARGET)
#define RSTMGR_CTRL_SWWARMRSTREQ_LSB 2
#else
#define RSTMGR_CTRL_SWWARMRSTREQ_LSB 1
#endif
/*
* Define a reset identifier, from which a permodrst bank ID
* and reset ID can be extracted using the subsequent macros
* RSTMGR_RESET() and RSTMGR_BANK().
*/
#define RSTMGR_BANK_OFFSET 8
#define RSTMGR_BANK_MASK 0x7
#define RSTMGR_RESET_OFFSET 0
#define RSTMGR_RESET_MASK 0x1f
#define RSTMGR_DEFINE(_bank, _offset) \
((_bank) << RSTMGR_BANK_OFFSET) | ((_offset) << RSTMGR_RESET_OFFSET)
/* Extract reset ID from the reset identifier. */
#define RSTMGR_RESET(_reset) \
(((_reset) >> RSTMGR_RESET_OFFSET) & RSTMGR_RESET_MASK)
/* Extract bank ID from the reset identifier. */
#define RSTMGR_BANK(_reset) \
(((_reset) >> RSTMGR_BANK_OFFSET) & RSTMGR_BANK_MASK)
/*
* SocFPGA Cyclone V/Arria V reset IDs, bank mapping is as follows:
* 0 ... mpumodrst
* 1 ... permodrst
* 2 ... per2modrst
* 3 ... brgmodrst
* 4 ... miscmodrst
*/
#define RSTMGR_EMAC0 RSTMGR_DEFINE(1, 0)
#define RSTMGR_EMAC1 RSTMGR_DEFINE(1, 1)
#define RSTMGR_NAND RSTMGR_DEFINE(1, 4)
#define RSTMGR_QSPI RSTMGR_DEFINE(1, 5)
#define RSTMGR_L4WD0 RSTMGR_DEFINE(1, 6)
#define RSTMGR_OSC1TIMER0 RSTMGR_DEFINE(1, 8)
#define RSTMGR_UART0 RSTMGR_DEFINE(1, 16)
#define RSTMGR_SPIM0 RSTMGR_DEFINE(1, 18)
#define RSTMGR_SPIM1 RSTMGR_DEFINE(1, 19)
#define RSTMGR_SDMMC RSTMGR_DEFINE(1, 22)
#define RSTMGR_DMA RSTMGR_DEFINE(1, 28)
#define RSTMGR_SDR RSTMGR_DEFINE(1, 29)
/* Create a human-readable reference to SoCFPGA reset. */
#define SOCFPGA_RESET(_name) RSTMGR_##_name
#endif /* _RESET_MANAGER_H_ */

141
fpga_system_manager.h Normal file
View File

@@ -0,0 +1,141 @@
/*
* Copyright (C) 2013 Altera Corporation <www.altera.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef _SYSTEM_MANAGER_H_
#define _SYSTEM_MANAGER_H_
struct socfpga_system_manager {
/* System Manager Module */
uint32_t siliconid1; /* 0x00 */
uint32_t siliconid2;
uint32_t _pad_0x8_0xf[2];
uint32_t wddbg; /* 0x10 */
uint32_t bootinfo;
uint32_t hpsinfo;
uint32_t parityinj;
/* FPGA Interface Group */
uint32_t fpgaintfgrp_gbl; /* 0x20 */
uint32_t fpgaintfgrp_indiv;
uint32_t fpgaintfgrp_module;
uint32_t _pad_0x2c_0x2f;
/* Scan Manager Group */
uint32_t scanmgrgrp_ctrl; /* 0x30 */
uint32_t _pad_0x34_0x3f[3];
/* Freeze Control Group */
uint32_t frzctrl_vioctrl; /* 0x40 */
uint32_t _pad_0x44_0x4f[3];
uint32_t frzctrl_hioctrl; /* 0x50 */
uint32_t frzctrl_src;
uint32_t frzctrl_hwctrl;
uint32_t _pad_0x5c_0x5f;
/* EMAC Group */
uint32_t emacgrp_ctrl; /* 0x60 */
uint32_t emacgrp_l3master;
uint32_t _pad_0x68_0x6f[2];
/* DMA Controller Group */
uint32_t dmagrp_ctrl; /* 0x70 */
uint32_t dmagrp_persecurity;
uint32_t _pad_0x78_0x7f[2];
/* Preloader (initial software) Group */
uint32_t iswgrp_handoff[8]; /* 0x80 */
uint32_t _pad_0xa0_0xbf[8]; /* 0xa0 */
/* Boot ROM Code Register Group */
uint32_t romcodegrp_ctrl; /* 0xc0 */
uint32_t romcodegrp_cpu1startaddr;
uint32_t romcodegrp_initswstate;
uint32_t romcodegrp_initswlastld;
uint32_t romcodegrp_bootromswstate; /* 0xd0 */
uint32_t __pad_0xd4_0xdf[3];
/* Warm Boot from On-Chip RAM Group */
uint32_t romcodegrp_warmramgrp_enable; /* 0xe0 */
uint32_t romcodegrp_warmramgrp_datastart;
uint32_t romcodegrp_warmramgrp_length;
uint32_t romcodegrp_warmramgrp_execution;
uint32_t romcodegrp_warmramgrp_crc; /* 0xf0 */
uint32_t __pad_0xf4_0xff[3];
/* Boot ROM Hardware Register Group */
uint32_t romhwgrp_ctrl; /* 0x100 */
uint32_t _pad_0x104_0x107;
/* SDMMC Controller Group */
uint32_t sdmmcgrp_ctrl;
uint32_t sdmmcgrp_l3master;
/* NAND Flash Controller Register Group */
uint32_t nandgrp_bootstrap; /* 0x110 */
uint32_t nandgrp_l3master;
/* USB Controller Group */
uint32_t usbgrp_l3master;
uint32_t _pad_0x11c_0x13f[9];
/* ECC Management Register Group */
uint32_t eccgrp_l2; /* 0x140 */
uint32_t eccgrp_ocram;
uint32_t eccgrp_usb0;
uint32_t eccgrp_usb1;
uint32_t eccgrp_emac0; /* 0x150 */
uint32_t eccgrp_emac1;
uint32_t eccgrp_dma;
uint32_t eccgrp_can0;
uint32_t eccgrp_can1; /* 0x160 */
uint32_t eccgrp_nand;
uint32_t eccgrp_qspi;
uint32_t eccgrp_sdmmc;
uint32_t _pad_0x170_0x3ff[164];
/* Pin Mux Control Group */
uint32_t emacio[20]; /* 0x400 */
uint32_t flashio[12]; /* 0x450 */
uint32_t generalio[28]; /* 0x480 */
uint32_t _pad_0x4f0_0x4ff[4];
uint32_t mixed1io[22]; /* 0x500 */
uint32_t mixed2io[8]; /* 0x558 */
uint32_t gplinmux[23]; /* 0x578 */
uint32_t gplmux[71]; /* 0x5d4 */
uint32_t nandusefpga; /* 0x6f0 */
uint32_t _pad_0x6f4;
uint32_t rgmii1usefpga; /* 0x6f8 */
uint32_t _pad_0x6fc_0x700[2];
uint32_t i2c0usefpga; /* 0x704 */
uint32_t sdmmcusefpga; /* 0x708 */
uint32_t _pad_0x70c_0x710[2];
uint32_t rgmii0usefpga; /* 0x714 */
uint32_t _pad_0x718_0x720[3];
uint32_t i2c3usefpga; /* 0x724 */
uint32_t i2c2usefpga; /* 0x728 */
uint32_t i2c1usefpga; /* 0x72c */
uint32_t spim1usefpga; /* 0x730 */
uint32_t _pad_0x734;
uint32_t spim0usefpga; /* 0x738 */
};
#define SYSMGR_ROMCODEGRP_CTRL_WARMRSTCFGPINMUX (1 << 0)
#define SYSMGR_ROMCODEGRP_CTRL_WARMRSTCFGIO (1 << 1)
#define SYSMGR_ECC_OCRAM_EN (1 << 0)
#define SYSMGR_ECC_OCRAM_SERR (1 << 3)
#define SYSMGR_ECC_OCRAM_DERR (1 << 4)
#define SYSMGR_FPGAINTF_USEFPGA 0x1
#define SYSMGR_FPGAINTF_SPIM0 (1 << 0)
#define SYSMGR_FPGAINTF_SPIM1 (1 << 1)
#define SYSMGR_FPGAINTF_EMAC0 (1 << 2)
#define SYSMGR_FPGAINTF_EMAC1 (1 << 3)
#define SYSMGR_FPGAINTF_NAND (1 << 4)
#define SYSMGR_FPGAINTF_SDMMC (1 << 5)
#if defined(CONFIG_TARGET_SOCFPGA_GEN5)
#define SYSMGR_SDMMC_SMPLSEL_SHIFT 3
#else
#define SYSMGR_SDMMC_SMPLSEL_SHIFT 4
#endif
#define SYSMGR_SDMMC_DRVSEL_SHIFT 0
/* EMAC Group Bit definitions */
#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0
#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1
#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII 0x2
#define SYSMGR_EMACGRP_CTRL_PHYSEL0_LSB 0
#define SYSMGR_EMACGRP_CTRL_PHYSEL1_LSB 2
#define SYSMGR_EMACGRP_CTRL_PHYSEL_MASK 0x3
#endif /* _SYSTEM_MANAGER_H_ */

75
hardware.c Normal file
View File

@@ -0,0 +1,75 @@
/*
Copyright 2008, 2009 Jakub Bednarski
This file is part of Minimig
Minimig 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.
Minimig 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 <ctype.h>
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include "hardware.h"
#include "user_io.h"
uint8_t rstval = 0;
void hexdump(void *data, uint16_t size, uint16_t offset)
{
uint8_t i, b2c;
uint16_t n = 0;
char *ptr = data;
if (!size) return;
while (size>0) {
iprintf("%04x: ", n + offset);
b2c = (size>16) ? 16 : size;
for (i = 0; i<b2c; i++) iprintf("%02x ", 0xff & ptr[i]);
iprintf(" ");
for (i = 0; i<(16 - b2c); i++) iprintf(" ");
for (i = 0; i<b2c; i++) iprintf("%c", isprint(ptr[i]) ? ptr[i] : '.');
iprintf("\n");
ptr += b2c;
size -= b2c;
n += b2c;
}
}
unsigned long GetTimer(unsigned long offset)
{
struct timespec tp;
clock_gettime(CLOCK_BOOTTIME, &tp);
unsigned long long res;
res = tp.tv_sec;
res *= 1000;
res += (tp.tv_nsec / 1000000);
return (unsigned long)(res + offset);
}
unsigned long CheckTimer(unsigned long time)
{
return GetTimer(0) >= time;
}
void WaitTimer(unsigned long time)
{
time = GetTimer(time);
while (!CheckTimer(time));
}

23
hardware.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef HARDWARE_H
#define HARDWARE_H
#include <inttypes.h>
#include <stdio.h>
#define iprintf(...) printf(__VA_ARGS__)
#define siprintf(...) sprintf(__VA_ARGS__)
#define BootPrint(text) iprintf("%s\n", text)
unsigned long GetTimer(unsigned long offset);
unsigned long CheckTimer(unsigned long t);
void WaitTimer(unsigned long time);
void hexdump(void *data, uint16_t size, uint16_t offset);
// minimig reset stuff
#define SPI_RST_USR 0x1
#define SPI_RST_CPU 0x2
#define SPI_CPU_HLT 0x4
extern uint8_t rstval;
#endif // HARDWARE_H

766
hdd.c Normal file
View File

@@ -0,0 +1,766 @@
/*
Copyright 2008, 2009 Jakub Bednarski
This file is part of Minimig
Minimig 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.
Minimig 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/>.
*/
// 2009-11-22 - read/write multiple implemented
#include <stdio.h>
#include <string.h>
#include "errors.h"
#include "hardware.h"
#include "file_io.h"
#include "hdd.h"
#include "hdd_internal.h"
#include "menu.h"
#include "config.h"
#include "debug.h"
#include "fpga_io.h"
#define SWAP(a) ((((a)&0x000000ff)<<24)|(((a)&0x0000ff00)<<8)|(((a)&0x00ff0000)>>8)|(((a)&0xff000000)>>24))
#define SWAPW(a) ((((a)<<8)&0xff00)|(((a)>>8)&0x00ff))
// hardfile structure
hdfTYPE hdf[2];
static uint8_t sector_buffer[512];
unsigned char GetDiskStatus(void)
{
unsigned char status;
EnableFpga();
status = (uint8_t)(spi_w(0) >> 8);
spi_w(0);
spi_w(0);
DisableFpga();
return status;
}
// RDBChecksum()
static void RDBChecksum(unsigned long *p)
{
unsigned long count = p[1];
unsigned long c2;
long result = 0;
p[2] = 0;
for (c2 = 0; c2<count; ++c2) result += p[c2];
p[2] = (unsigned long)-result;
}
// FakeRDB()
// if the hardfile doesn't have a RigidDiskBlock, we synthesize one
static void FakeRDB(int unit, int block)
{
int i;
// start by clearing the sector buffer
memset(sector_buffer, 0, 512);
// if we're asked for LBA 0 we create an RDSK block, and if LBA 1, a PART block
switch (block) {
case 0: {
// RDB
hdd_debugf("FAKE: RDB");
struct RigidDiskBlock *rdb = (struct RigidDiskBlock *)sector_buffer;
rdb->rdb_ID = 'R' << 24 | 'D' << 16 | 'S' << 8 | 'K';
rdb->rdb_Summedlongs = 0x40;
rdb->rdb_HostID = 0x07;
rdb->rdb_BlockBytes = 0x200;
rdb->rdb_Flags = 0x12; // (Disk ID valid, no LUNs after this one)
rdb->rdb_BadBlockList = 0xffffffff; // We don't provide a bad block list
rdb->rdb_PartitionList = 1;
rdb->rdb_FileSysHeaderList = 0xffffffff;
rdb->rdb_DriveInit = 0xffffffff;
rdb->rdb_Reserved1[0] = 0xffffffff;
rdb->rdb_Reserved1[1] = 0xffffffff;
rdb->rdb_Reserved1[2] = 0xffffffff;
rdb->rdb_Reserved1[3] = 0xffffffff;
rdb->rdb_Reserved1[4] = 0xffffffff;
rdb->rdb_Reserved1[5] = 0xffffffff;
rdb->rdb_Cylinders = hdf[unit].cylinders;
rdb->rdb_Sectors = hdf[unit].sectors;
rdb->rdb_Heads = hdf[unit].heads;
rdb->rdb_Interleave = 1;
rdb->rdb_Park = rdb->rdb_Cylinders;
rdb->rdb_WritePreComp = rdb->rdb_Cylinders;
rdb->rdb_ReducedWrite = rdb->rdb_Cylinders;
rdb->rdb_StepRate = 3;
rdb->rdb_RDBBlocksLo = 0;
rdb->rdb_RDBBlocksHi = 1;
rdb->rdb_LoCylinder = 1;
rdb->rdb_HiCylinder = rdb->rdb_Cylinders - 1;
rdb->rdb_CylBlocks = rdb->rdb_Heads * rdb->rdb_Sectors;
rdb->rdb_AutoParkSeconds = 0;
rdb->rdb_HighRDSKBlock = 1;
strcpy(rdb->rdb_DiskVendor, "Do not ");
strcpy(rdb->rdb_DiskProduct, "repartition!");
// swap byte order of strings to be able to "unswap" them after checksum
unsigned long *p = (unsigned long*)rdb;
for (i = 0; i<(8 + 16) / 4; i++) p[40 + i] = SWAP(p[40 + i]);
RDBChecksum((unsigned long *)rdb);
// swap byte order of first 0x40 long values
for (i = 0; i<0x40; i++) p[i] = SWAP(p[i]);
break;
}
case 1: {
// Partition
hdd_debugf("FAKE: Partition");
struct PartitionBlock *pb = (struct PartitionBlock *)sector_buffer;
pb->pb_ID = 'P' << 24 | 'A' << 16 | 'R' << 8 | 'T';
pb->pb_Summedlongs = 0x40;
pb->pb_HostID = 0x07;
pb->pb_Next = 0xffffffff;
pb->pb_Flags = 0x1; // bootable
pb->pb_DevFlags = 0;
strcpy(pb->pb_DriveName, unit ? "1HD\003" : "0HD\003"); // "DH0"/"DH1" BCPL string
pb->pb_Environment.de_TableSize = 0x10;
pb->pb_Environment.de_SizeBlock = 0x80;
pb->pb_Environment.de_Surfaces = hdf[unit].heads;
pb->pb_Environment.de_SectorPerBlock = 1;
pb->pb_Environment.de_BlocksPerTrack = hdf[unit].sectors;
pb->pb_Environment.de_Reserved = 2;
pb->pb_Environment.de_LowCyl = 1;
pb->pb_Environment.de_HighCyl = hdf[unit].cylinders - 1;
pb->pb_Environment.de_NumBuffers = 30;
pb->pb_Environment.de_MaxTransfer = 0xffffff;
pb->pb_Environment.de_Mask = 0x7ffffffe;
pb->pb_Environment.de_DosType = 0x444f5301;
RDBChecksum((unsigned long *)pb);
// swap byte order of first 0x40 entries
unsigned long *p = (unsigned long*)pb;
for (i = 0; i<0x40; i++) p[i] = SWAP(p[i]);
break;
}
default: {
break;
}
}
}
// IdentifiyDevice()
// builds Identify Device struct
void IdentifyDevice(unsigned short *pBuffer, unsigned char unit)
{
char *p, i, x;
unsigned long total_sectors = hdf[unit].cylinders * hdf[unit].heads * hdf[unit].sectors;
memset(pBuffer, 0, 512);
switch (hdf[unit].type) {
case HDF_FILE | HDF_SYNTHRDB:
case HDF_FILE:
pBuffer[0] = 1 << 6; // hard disk
pBuffer[1] = hdf[unit].cylinders; // cyl count
pBuffer[3] = hdf[unit].heads; // head count
pBuffer[6] = hdf[unit].sectors; // sectors per track
// FIXME - can get serial no from card itself.
memcpy((char*)&pBuffer[10], "MiSTMiniMigHardfile ", 20); // serial number - byte swapped
memcpy((char*)&pBuffer[23], ".100 ", 8); // firmware version - byte swapped
p = (char*)&pBuffer[27];
// FIXME - likewise the model name can be fetched from the card.
if (hdf[unit].type & HDF_SYNTHRDB) {
memcpy(p, "DON'T ", 40);
p += 8;
memcpy(p, "REPARTITION! ", 16);
}
else {
memcpy(p, "YAQUBE ", 40); // model name - byte swapped
p += 8;
for (i = 0; (x = config.hardfile[unit].long_name[i]) && i < 16; i++) // copy file name as model name
p[i] = x;
}
// SwapBytes((char*)&pBuffer[27], 40); //not for 68000
break;
}
pBuffer[47] = 0x8010; // maximum sectors per block in Read/Write Multiple command
pBuffer[53] = 1;
pBuffer[54] = hdf[unit].cylinders;
pBuffer[55] = hdf[unit].heads;
pBuffer[56] = hdf[unit].sectors;
pBuffer[57] = (unsigned short)total_sectors;
pBuffer[58] = (unsigned short)(total_sectors >> 16);
}
// chs2lba()
unsigned long chs2lba(unsigned short cylinder, unsigned char head, unsigned short sector, unsigned char unit)
{
return(cylinder * hdf[unit].heads + head) * hdf[unit].sectors + sector - 1;
}
// WriteTaskFile()
void WriteTaskFile(unsigned char error, unsigned char sector_count, unsigned char sector_number, unsigned char cylinder_low, unsigned char cylinder_high, unsigned char drive_head)
{
EnableFpga();
spi_w(CMD_IDE_REGS_WR<<8); // write task file registers command
spi_w(0); // dummy
spi_w(0); // dummy
spi_w(0); // dummy
spi_w(error); // error
spi_w(sector_count); // sector count
spi_w(sector_number); // sector number
spi_w(cylinder_low); // cylinder low
spi_w(cylinder_high); // cylinder high
spi_w(drive_head); // drive/head
DisableFpga();
}
// WriteStatus()
void WriteStatus(unsigned char status)
{
EnableFpga();
spi_w((CMD_IDE_STATUS_WR<<8) | status);
spi_w(0);
spi_w(0);
DisableFpga();
}
// ATA_Recalibrate()
static void ATA_Recalibrate(unsigned char* tfr, unsigned char unit)
{
// Recalibrate 0x10-0x1F (class 3 command: no data)
hdd_debugf("IDE%d: Recalibrate", unit);
WriteTaskFile(0, 0, 1, 0, 0, tfr[6] & 0xF0);
WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ);
}
// ATA_Diagnostic()
static void ATA_Diagnostic(unsigned char* tfr)
{
// Execute Drive Diagnostic (0x90)
hdd_debugf("IDE: Drive Diagnostic");
WriteTaskFile(1, 0, 0, 0, 0, 0);
WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ);
}
// ATA_IdentifyDevice()
static void ATA_IdentifyDevice(unsigned char* tfr, unsigned char unit)
{
int i;
// Identify Device (0xec)
hdd_debugf("IDE%d: Identify Device", unit);
IdentifyDevice((uint16_t*)sector_buffer, unit);
WriteTaskFile(0, tfr[2], tfr[3], tfr[4], tfr[5], tfr[6]);
WriteStatus(IDE_STATUS_RDY); // pio in (class 1) command type
EnableFpga();
spi_w(CMD_IDE_DATA_WR<<8); // write data command
spi_w(0);
spi_w(0);
spi_block_write_16be((uint16_t*)sector_buffer);
DisableFpga();
WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ);
}
// ATA_Initialize()
static void ATA_Initialize(unsigned char* tfr, unsigned char unit)
{
// Initialize Device Parameters (0x91)
hdd_debugf("Initialize Device Parameters");
hdd_debugf("IDE%d: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X", unit, tfr[0], tfr[1], tfr[2], tfr[3], tfr[4], tfr[5], tfr[6], tfr[7]);
WriteTaskFile(0, tfr[2], tfr[3], tfr[4], tfr[5], tfr[6]);
WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ);
}
// ATA_SetMultipleMode()
static void ATA_SetMultipleMode(unsigned char* tfr, unsigned char unit)
{
// Set Multiple Mode (0xc6)
hdd_debugf("Set Multiple Mode");
hdd_debugf("IDE%d: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X", unit, tfr[0], tfr[1], tfr[2], tfr[3], tfr[4], tfr[5], tfr[6], tfr[7]);
hdf[unit].sectors_per_block = tfr[2];
WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ);
}
// ATA_ReadSectors()
static void ATA_ReadSectors(unsigned char* tfr, unsigned short sector, unsigned short cylinder, unsigned char head, unsigned char unit, unsigned short sector_count)
{
// Read Sectors (0x20)
long lba;
sector = tfr[3];
cylinder = tfr[4] | (tfr[5] << 8);
head = tfr[6] & 0x0F;
sector_count = tfr[2];
if (sector_count == 0) sector_count = 0x100;
hdd_debugf("IDE%d: read %d.%d.%d, %d", unit, cylinder, head, sector, sector_count);
switch (hdf[unit].type) {
case HDF_FILE | HDF_SYNTHRDB:
case HDF_FILE:
lba = chs2lba(cylinder, head, sector, unit);
if (hdf[unit].file.size) HardFileSeek(&hdf[unit], (lba + hdf[unit].offset) < 0 ? 0 : lba + hdf[unit].offset);
while (sector_count) {
// decrease sector count
if (sector_count != 1) {
if (sector == hdf[unit].sectors) {
sector = 1;
head++;
if (head == hdf[unit].heads) {
head = 0;
cylinder++;
}
}
else {
sector++;
}
}
WriteTaskFile(0, tfr[2], sector, (unsigned char)cylinder, (unsigned char)(cylinder >> 8), (tfr[6] & 0xF0) | head);
WriteStatus(IDE_STATUS_RDY); // pio in (class 1) command type
// sector outside limit (fake rdb header) or to be modified sector of first partition
if (((lba + hdf[unit].offset)<0) || ((unit == 0) && (hdf[unit].type == HDF_FILE | HDF_SYNTHRDB) && (lba == 0)))
{
if ((lba + hdf[unit].offset)<0)
{
FakeRDB(unit, lba);
}
else
{
// read sector into buffer
FileReadSec(&hdf[unit].file, sector_buffer);
// adjust checksum by the difference between old and new flag value
struct RigidDiskBlock *rdb = (struct RigidDiskBlock *)sector_buffer;
rdb->rdb_ChkSum = SWAP(SWAP(rdb->rdb_ChkSum) + SWAP(rdb->rdb_Flags) - 0x12);
// adjust flags
rdb->rdb_Flags = SWAP(0x12);
}
EnableFpga();
spi_w(CMD_IDE_DATA_WR << 8); // write data command
spi_w(0);
spi_w(0);
spi_block_write_16be((uint16_t*)sector_buffer);
DisableFpga();
WriteStatus(sector_count == 1 ? IDE_STATUS_IRQ | IDE_STATUS_END : IDE_STATUS_IRQ);
}
else
{
while (!(GetDiskStatus() & CMD_IDECMD)); // wait for empty sector buffer
WriteStatus(IDE_STATUS_IRQ);
if (hdf[unit].file.size)
{
FileReadSec(&hdf[unit].file, sector_buffer);
EnableFpga();
spi_w(CMD_IDE_DATA_WR << 8); // write data command
spi_w(0);
spi_w(0);
spi_block_write_16be((uint16_t*)sector_buffer);
DisableFpga();
}
}
lba++;
sector_count--; // decrease sector count
}
break;
}
}
// HandleHDD()
void HandleHDD(unsigned char c1, unsigned char c2)
{
unsigned char tfr[8];
unsigned short i;
unsigned short sector;
unsigned short cylinder;
unsigned char head;
unsigned char unit;
unsigned short sector_count;
unsigned short block_count;
if (c1 & CMD_IDECMD)
{
DISKLED_ON;
EnableFpga();
spi_w(CMD_IDE_REGS_RD<<8); // read task file registers
spi_w(0);
spi_w(0);
for (i = 0; i < 8; i++) tfr[i] = (uint8_t)spi_w(0);
DisableFpga();
unit = tfr[6] & 0x10 ? 1 : 0; // master/slave selection
if (0) hdd_debugf("IDE%d: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X", unit, tfr[0], tfr[1], tfr[2], tfr[3], tfr[4], tfr[5], tfr[6], tfr[7]);
if ((tfr[7] & 0xF0) == ACMD_RECALIBRATE) {
ATA_Recalibrate(tfr, unit);
}
else if (tfr[7] == ACMD_DIAGNOSTIC) {
ATA_Diagnostic(tfr);
}
else if (tfr[7] == ACMD_IDENTIFY_DEVICE) {
ATA_IdentifyDevice(tfr, unit);
}
else if (tfr[7] == ACMD_INITIALIZE_DEVICE_PARAMETERS) {
ATA_Initialize(tfr, unit);
}
else if (tfr[7] == ACMD_SET_MULTIPLE_MODE) {
ATA_SetMultipleMode(tfr, unit);
}
else if (tfr[7] == ACMD_READ_SECTORS) {
ATA_ReadSectors(tfr, sector, cylinder, head, unit, sector_count);
}
else if (tfr[7] == ACMD_READ_MULTIPLE) {
// Read Multiple Sectors (multiple sector transfer per IRQ)
long lba;
WriteStatus(IDE_STATUS_RDY); // pio in (class 1) command type
sector = tfr[3];
cylinder = tfr[4] | (tfr[5] << 8);
head = tfr[6] & 0x0F;
sector_count = tfr[2];
if (sector_count == 0) sector_count = 0x100;
hdd_debugf("IDE%d: read_multi %d.%d.%d, %d", unit, cylinder, head, sector, sector_count);
switch (hdf[unit].type) {
case HDF_FILE | HDF_SYNTHRDB:
case HDF_FILE:
lba = chs2lba(cylinder, head, sector, unit);
if (hdf[unit].file.size) HardFileSeek(&hdf[unit], (lba + hdf[unit].offset) < 0 ? 0 : lba + hdf[unit].offset);
// FIXME - READM could cross the fake RDB -> real disk boundary.
// FIXME - but first we should make some attempt to generate fake RGB in multiple mode.
while (sector_count) {
while (!(GetDiskStatus() & CMD_IDECMD)); // wait for empty sector buffer
block_count = sector_count;
if (block_count > hdf[unit].sectors_per_block) block_count = hdf[unit].sectors_per_block;
WriteStatus(IDE_STATUS_IRQ);
while (block_count--)
{
if (hdf[unit].file.size)
{
FileReadSec(&hdf[unit].file, sector_buffer);
EnableFpga();
spi_w(CMD_IDE_DATA_WR << 8); // write data command
spi_w(0);
spi_w(0);
spi_block_write_16be((uint16_t*)sector_buffer);
DisableFpga();
}
if (sector_count != 1)
{
if (sector == hdf[unit].sectors)
{
sector = 1;
head++;
if (head == hdf[unit].heads)
{
head = 0;
cylinder++;
}
}
else {
sector++;
}
}
sector_count--;
}
WriteTaskFile(0, tfr[2], sector, (unsigned char)cylinder, (unsigned char)(cylinder >> 8), (tfr[6] & 0xF0) | head);
//WriteTaskFile(0, 0, sector, (unsigned char)cylinder, (unsigned char)(cylinder >> 8), (tfr[6] & 0xF0) | head);
}
//WriteTaskFile(0, 0, sector, (unsigned char)cylinder, (unsigned char)(cylinder >> 8), (tfr[6] & 0xF0) | head);
break;
}
WriteStatus(IDE_STATUS_END);
}
else if (tfr[7] == ACMD_WRITE_SECTORS) {
// write sectors
WriteStatus(IDE_STATUS_REQ); // pio out (class 2) command type
sector = tfr[3];
cylinder = tfr[4] | (tfr[5] << 8);
head = tfr[6] & 0x0F;
sector_count = tfr[2];
if (sector_count == 0) sector_count = 0x100;
long lba = chs2lba(cylinder, head, sector, unit);
//if (hdf[unit].type>=HDF_CARDPART0)
lba += hdf[unit].offset;
if (hdf[unit].file.size) {
// File size will be 0 in direct card modes
HardFileSeek(&hdf[unit], (lba>-1) ? lba : 0);
}
while (sector_count)
{
while (!(GetDiskStatus() & CMD_IDEDAT)); // wait for full write buffer
// decrease sector count
if (sector_count != 1)
{
if (sector == hdf[unit].sectors)
{
sector = 1;
head++;
if (head == hdf[unit].heads)
{
head = 0;
cylinder++;
}
}
else
{
sector++;
}
}
WriteTaskFile(0, tfr[2], sector, (unsigned char)cylinder, (unsigned char)(cylinder >> 8), (tfr[6] & 0xF0) | head);
EnableFpga();
spi_w(CMD_IDE_DATA_RD<<8); // read data command
spi_w(0);
spi_w(0);
spi_block_read_16be((uint16_t*)sector_buffer);
DisableFpga();
sector_count--; // decrease sector count
if (sector_count)
{
WriteStatus(IDE_STATUS_IRQ);
}
else
{
WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ);
}
switch (hdf[unit].type)
{
case HDF_FILE | HDF_SYNTHRDB:
case HDF_FILE:
// Don't attempt to write to fake RDB
if (hdf[unit].file.size && (lba>-1))
{
FileWriteSec(&hdf[unit].file, sector_buffer);
}
lba++;
break;
}
}
}
else if (tfr[7] == ACMD_WRITE_MULTIPLE) {
// write sectors
WriteStatus(IDE_STATUS_REQ); // pio out (class 2) command type
sector = tfr[3];
cylinder = tfr[4] | (tfr[5] << 8);
head = tfr[6] & 0x0F;
sector_count = tfr[2];
if (sector_count == 0) sector_count = 0x100;
long lba = chs2lba(cylinder, head, sector, unit);
//if (hdf[unit].type>=HDF_CARDPART0)
lba += hdf[unit].offset;
if (hdf[unit].file.size) {
// File size will be 0 in direct card modes
HardFileSeek(&hdf[unit], (lba>-1) ? lba : 0);
}
while (sector_count) {
block_count = sector_count;
if (block_count > hdf[unit].sectors_per_block) block_count = hdf[unit].sectors_per_block;
while (block_count) {
while (!(GetDiskStatus() & CMD_IDEDAT)); // wait for full write buffer
// decrease sector count
if (sector_count != 1) {
if (sector == hdf[unit].sectors) {
sector = 1;
head++;
if (head == hdf[unit].heads) {
head = 0;
cylinder++;
}
}
else {
sector++;
}
}
//WriteTaskFile(0, tfr[2], sector, (unsigned char)cylinder, (unsigned char)(cylinder >> 8), (tfr[6] & 0xF0) | head);
EnableFpga();
spi_w(CMD_IDE_DATA_RD<<8); // read data command
spi_w(0);
spi_w(0);
spi_block_read_16be((uint16_t*)sector_buffer);
DisableFpga();
switch (hdf[unit].type)
{
case HDF_FILE | HDF_SYNTHRDB:
case HDF_FILE:
if (hdf[unit].file.size && (lba>-1))
{
FileWriteSec(&hdf[unit].file, sector_buffer);
}
lba++;
break;
}
block_count--; // decrease block count
sector_count--; // decrease sector count
}
WriteTaskFile(0, tfr[2], sector, (unsigned char)cylinder, (unsigned char)(cylinder >> 8), (tfr[6] & 0xF0) | head);
if (sector_count) {
WriteStatus(IDE_STATUS_IRQ);
}
else {
WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ);
}
}
}
else {
hdd_debugf("Unknown ATA command");
hdd_debugf("IDE%d: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X", unit, tfr[0], tfr[1], tfr[2], tfr[3], tfr[4], tfr[5], tfr[6], tfr[7]);
WriteTaskFile(0x04, tfr[2], tfr[3], tfr[4], tfr[5], tfr[6]);
WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ | IDE_STATUS_ERR);
}
DISKLED_OFF;
}
}
// GetHardfileGeometry()
// this function comes from WinUAE, should return the same CHS as WinUAE
void GetHardfileGeometry(hdfTYPE *pHDF)
{
unsigned long total = 0;
unsigned long i, head, cyl, spt;
unsigned long sptt[] = { 63, 127, 255, 0 };
switch (pHDF->type) {
case (HDF_FILE | HDF_SYNTHRDB) :
if (pHDF->file.size == 0) return;
total = pHDF->file.size / 512;
pHDF->heads = 1;
pHDF->sectors = 32;
pHDF->cylinders = total / 32 + 1; // Add a cylinder for the fake RDB.
return;
case HDF_FILE:
if (pHDF->file.size == 0) return;
total = pHDF->file.size / 512;
break;
}
for (i = 0; sptt[i] != 0; i++) {
spt = sptt[i];
for (head = 4; head <= 16; head++) {
cyl = total / (head * spt);
if (total <= 1024 * 1024) {
if (cyl <= 1023) break;
}
else {
if (cyl < 16383)
break;
if (cyl < 32767 && head >= 5)
break;
if (cyl <= 65535) // Should there some head constraint here?
break;
}
}
if (head <= 16) break;
}
pHDF->cylinders = (unsigned short)cyl;
pHDF->heads = (unsigned short)head;
pHDF->sectors = (unsigned short)spt;
}
// HardFileSeek()
unsigned char HardFileSeek(hdfTYPE *pHDF, unsigned long lba)
{
return FileSeekLBA(&pHDF->file, lba);
}
// OpenHardfile()
unsigned char OpenHardfile(unsigned char unit)
{
unsigned long time;
switch (config.hardfile[unit].enabled)
{
case HDF_FILE | HDF_SYNTHRDB:
case HDF_FILE:
hdf[unit].type = config.hardfile[unit].enabled;
if (config.hardfile[unit].long_name[0])
{
if(FileOpenEx(&hdf[unit].file, config.hardfile[unit].long_name, FileCanWrite(config.hardfile[unit].long_name) ? O_RDWR : O_RDONLY))
{
GetHardfileGeometry(&hdf[unit]);
hdd_debugf("HARDFILE %d:", unit);
hdd_debugf("file: \"%.8s.%.3s\"", hdf[unit].file.name, &hdf[unit].file.name[8]);
hdd_debugf("size: %lu (%lu MB)", hdf[unit].file.size, hdf[unit].file.size >> 20);
hdd_debugf("CHS: %u.%u.%u", hdf[unit].cylinders, hdf[unit].heads, hdf[unit].sectors);
hdd_debugf(" (%lu MB)", ((((unsigned long)hdf[unit].cylinders) * hdf[unit].heads * hdf[unit].sectors) >> 11));
time = GetTimer(0);
time = GetTimer(0) - time;
hdd_debugf("Hardfile indexed in %lu ms", time >> 16);
if (config.hardfile[unit].enabled & HDF_SYNTHRDB) {
hdf[unit].offset = -(hdf[unit].heads*hdf[unit].sectors);
}
else {
hdf[unit].offset = 0;
}
config.hardfile[unit].present = 1;
return 1;
}
}
}
config.hardfile[unit].present = 0;
return 0;
}
// GetHDFFileType()
unsigned char GetHDFFileType(char *filename)
{
uint8_t type = HDF_FILETYPE_NOTFOUND;
fileTYPE rdbfile;
if(FileOpen(&rdbfile, filename))
{
type = HDF_FILETYPE_UNKNOWN;
for (int i = 0; i<16; ++i)
{
FileReadSec(&rdbfile, sector_buffer);
if (sector_buffer[0] == 'R' && sector_buffer[1] == 'D' && sector_buffer[2] == 'S' && sector_buffer[3] == 'K')
{
type = HDF_FILETYPE_RDB;
break;
}
if (sector_buffer[0] == 'D' && sector_buffer[1] == 'O' && sector_buffer[2] == 'S')
{
type = HDF_FILETYPE_DOS;
break;
}
if (sector_buffer[0] == 'P' && sector_buffer[1] == 'F' && sector_buffer[2] == 'S')
{
type = HDF_FILETYPE_DOS;
break;
}
if (sector_buffer[0] == 'S' && sector_buffer[1] == 'F' && sector_buffer[2] == 'S')
{
type = HDF_FILETYPE_DOS;
break;
}
}
FileClose(&rdbfile);
}
return type;
}

77
hdd.h Normal file
View File

@@ -0,0 +1,77 @@
// hdd.h
#ifndef __HDD_H__
#define __HDD_H__
// defines
#define CMD_IDECMD 0x04
#define CMD_IDEDAT 0x08
#define CMD_IDE_REGS_RD 0x80
#define CMD_IDE_REGS_WR 0x90
#define CMD_IDE_DATA_WR 0xA0
#define CMD_IDE_DATA_RD 0xB0
#define CMD_IDE_STATUS_WR 0xF0
#define IDE_STATUS_END 0x80
#define IDE_STATUS_IRQ 0x10
#define IDE_STATUS_RDY 0x08
#define IDE_STATUS_REQ 0x04
#define IDE_STATUS_ERR 0x01
#define ACMD_RECALIBRATE 0x10
#define ACMD_DIAGNOSTIC 0x90
#define ACMD_IDENTIFY_DEVICE 0xEC
#define ACMD_INITIALIZE_DEVICE_PARAMETERS 0x91
#define ACMD_READ_SECTORS 0x20
#define ACMD_WRITE_SECTORS 0x30
#define ACMD_READ_MULTIPLE 0xC4
#define ACMD_WRITE_MULTIPLE 0xC5
#define ACMD_SET_MULTIPLE_MODE 0xC6
#define HDF_DISABLED 0
#define HDF_FILE 1
#define HDF_TYPEMASK 15
#define HDF_SYNTHRDB 128 // flag to indicate whether we should auto-synthesize a RigidDiskBlock
#define HDF_FILETYPE_UNKNOWN 0
#define HDF_FILETYPE_NOTFOUND 1
#define HDF_FILETYPE_RDB 2
#define HDF_FILETYPE_DOS 3
// types
typedef struct
{
int type; // are we using a file, the entire SD card or a partition on the SD card?
fileTYPE file;
unsigned short cylinders;
unsigned short heads;
unsigned short sectors;
unsigned short sectors_per_block;
unsigned short partition; // partition no.
long offset; // if a partition, the lba offset of the partition. Can be negative if we've synthesized an RDB.
} hdfTYPE;
// variables
extern char debugmsg[40];
extern char debugmsg2[40];
extern hdfTYPE hdf[2];
// functions
void IdentifyDevice(unsigned short *pBuffer, unsigned char unit);
unsigned long chs2lba(unsigned short cylinder, unsigned char head, unsigned short sector, unsigned char unit);
void WriteTaskFile(unsigned char error, unsigned char sector_count, unsigned char sector_number, unsigned char cylinder_low, unsigned char cylinder_high, unsigned char drive_head);
void WriteStatus(unsigned char status);
void HandleHDD(unsigned char c1, unsigned char c2);
void GetHardfileGeometry(hdfTYPE *hdf);
unsigned char HardFileSeek(hdfTYPE *hdf, unsigned long lba);
unsigned char OpenHardfile(unsigned char unit);
unsigned char GetHDFFileType(char *filename);
#endif // __HDD_H__

88
hdd_internal.h Normal file
View File

@@ -0,0 +1,88 @@
#ifndef HDD_INTERNAL_H
#define HDD_INTERNAL_H
// Structure definitions for RDB emulation.
// For hardfiles that have no RDB information, we'll just create a single-partition RDB and Part block
// on blocks 0 and 1. All other blocks within the first cylinder will be translated into the hardfile
struct RigidDiskBlock {
unsigned long rdb_ID; // "RDSK"
unsigned long rdb_Summedlongs; // 0x40
long rdb_ChkSum; // Sum to zero
unsigned long rdb_HostID; // 0x07
unsigned long rdb_BlockBytes; // 0x200
unsigned long rdb_Flags; // 0x12 (Disk ID valid, no LUNs after this one)
unsigned long rdb_BadBlockList; // -1 since we don't provide one
unsigned long rdb_PartitionList; // 1
unsigned long rdb_FileSysHeaderList; // -1
unsigned long rdb_DriveInit; // -1
unsigned long rdb_Reserved1[6]; // 0xffffffff
unsigned long rdb_Cylinders;
unsigned long rdb_Sectors;
unsigned long rdb_Heads;
unsigned long rdb_Interleave; // 1
unsigned long rdb_Park; // =Cylinder count
unsigned long rdb_Reserved2[3];
unsigned long rdb_WritePreComp; // High cylinder ?
unsigned long rdb_ReducedWrite; // High cylinder ?
unsigned long rdb_StepRate; // 3 ?
unsigned long rdb_Reserved3[5];
unsigned long rdb_RDBBlocksLo; // block zero
unsigned long rdb_RDBBlocksHi; // block one
unsigned long rdb_LoCylinder; // 1
unsigned long rdb_HiCylinder; // From the hardfile: cylinder count -1
unsigned long rdb_CylBlocks; // From the hardfile: heads * sectors
unsigned long rdb_AutoParkSeconds; // zero
unsigned long rdb_HighRDSKBlock; // 1
unsigned long rdb_Reserved4;
char rdb_DiskVendor[8]; // "Don't"
char rdb_DiskProduct[16]; // " repartition!"
char rdb_DiskRevision[4];
char rdb_ControllerVendor[8];
char rdb_ControllerProduct[16];
char rdb_ControllerRevision[4];
unsigned long rdb_Reserved5[10];
} __attribute__((packed));
struct DosEnvec {
unsigned long de_TableSize; // Size of Environment vector - 0x10
unsigned long de_SizeBlock; // in longwords - 0x80
unsigned long de_SecOrg; // 0
unsigned long de_Surfaces; // Heads?
unsigned long de_SectorPerBlock; // 1
unsigned long de_BlocksPerTrack;
unsigned long de_Reserved; // 2 ?
unsigned long de_PreAlloc; // 0
unsigned long de_Interleave; // 0
unsigned long de_LowCyl;
unsigned long de_HighCyl;
unsigned long de_NumBuffers; // 30
unsigned long de_BufMemType; // 0 - any available
unsigned long de_MaxTransfer; // 0x00ffffff
unsigned long de_Mask; // 0x7ffffffe
long de_BootPri; // 0
unsigned long de_DosType; // 0x444f5301 or 3
// Extra fields
unsigned long de_Baud;
unsigned long de_Control;
unsigned long de_BootBlocks;
} __attribute__((packed));
struct PartitionBlock {
unsigned long pb_ID; // "PART"
unsigned long pb_Summedlongs; // 0x40
long pb_ChkSum; // Sum to zero
unsigned long pb_HostID; // 0x07
unsigned long pb_Next; // -1
unsigned long pb_Flags; // 1 - Bootable
unsigned long pb_Reserved1[2]; // 0
unsigned long pb_DevFlags; // 0
char pb_DriveName[32]; // 0x03"DH0"
unsigned long pb_Reserved2[15];
struct DosEnvec pb_Environment;
unsigned long pb_EReserved[12]; /* reserved for future environment vector */
} __attribute__((packed));
#endif /* HDD_INTERNAL_H */

652
ikbd.c Normal file
View File

@@ -0,0 +1,652 @@
/*
http://removers.free.fr/wikipendium/wakka.php?wiki=IntelligentKeyboardBible
https://www.kernel.org/doc/Documentation/input/atarikbd.txt
ikbd ToDo:
Feature Example using/needing it impl. tested
---------------------------------------------------------------------
mouse y at bottom Bolo X X
mouse button key events Goldrunner/A_008 X X
joystick interrogation mode Xevious/A_004 X X
Absolute mouse mode Addicataball/A_050 X X
disable mouse ? X
disable joystick ? X
Joysticks also generate Goldrunner X -X
mouse button events!
Pause/Resume PACMANIA_STE/Gembench X
mouse keycode mode Goldrunner X X
Games that have ikbd problems:
PowerMonger/PP_106 fixed
Stardust fixed
M1 tank platoon/A_385 fixed
*/
#include <stdio.h>
#include <string.h>
#include "user_io.h"
#include "spi.h"
#include "ikbd.h"
#include "debug.h"
#define IKBD_AUTO_MS 20
// atari ikbd stuff
#define IKBD_STATE_JOYSTICK_EVENT_REPORTING 0x01
#define IKBD_STATE_MOUSE_Y_BOTTOM 0x02
#define IKBD_STATE_MOUSE_BUTTON_AS_KEY 0x04 // mouse buttons act like keys
#define IKBD_STATE_MOUSE_DISABLED 0x08
#define IKBD_STATE_MOUSE_ABSOLUTE 0x10
#define IKBD_STATE_MOUSE_ABSOLUTE_IN_PROGRESS 0x20
#define IKBD_STATE_WAIT4RESET 0x40
#define IKBD_STATE_PAUSED 0x80
#define IKBD_DEFAULT IKBD_STATE_JOYSTICK_EVENT_REPORTING
/* ------------------- transmit queue ------------------- */
#define QUEUE_LEN 16 // power of 2!
static unsigned short tx_queue[QUEUE_LEN];
static unsigned char wptr = 0, rptr = 0;
static unsigned long ikbd_timer = 0;
/* -------- main structure to keep track of ikbd state -------- */
static struct {
unsigned char state;
unsigned long auto_timer; // auto report timer (50hz/20ms)
unsigned long rtc_timer;
// ----- joystick state -------
struct {
unsigned char state; // current state
unsigned char prev; // last reported state
} joy[2];
// ----- mouse state -------
struct {
// current state
unsigned char but, but_prev;
short x, y;
struct {
// absolute mouse state
unsigned char buttons;
struct { unsigned short x, y; } max;
struct { unsigned char x, y; } scale;
struct { unsigned short x, y; } pos;
} abs;
} mouse;
// ----- clock state ------
unsigned char date[6];
unsigned int tx_cnt; // tx byte counter for debugging
// ----- buffer tp hold incoming commands ------
struct {
char size;
union {
struct {
unsigned char code; // cmd code
// command specific structures
union {
unsigned char mouse_button_action;
unsigned char reset;
struct { unsigned short max_x, max_y; } __attribute__((packed)) abs_mouse_pos;
struct { unsigned char dist_x, dist_y; } __attribute__((packed)) mouse_keycode;
struct { unsigned char x, y; } __attribute__((packed)) mouse_threshold;
struct { unsigned char x, y; } __attribute__((packed)) mouse_scale;
struct { unsigned char f; unsigned short x, y; } __attribute__((packed)) load_mouse_pos;
unsigned char date[6];
};
} __attribute__((packed)) command;
unsigned char byte[0];
};
} buffer;
} ikbd;
// read a 16 bit word in big endian
unsigned short be16(unsigned short in) {
return ((in & 0xff) << 8) + ((in & 0xff00) >> 8);
}
static void enqueue(unsigned short b) {
if (((wptr + 1)&(QUEUE_LEN - 1)) == rptr)
return;
tx_queue[wptr] = b;
wptr = (wptr + 1)&(QUEUE_LEN - 1);
}
unsigned char bcd2bin(unsigned char in) {
return 10 * (in >> 4) + (in & 0x0f);
}
unsigned char bin2bcd(unsigned char in) {
return 16 * (in / 10) + (in % 10);
}
// convert internal joystick format into atari ikbd format
static unsigned char joystick_map2ikbd(unsigned char in) {
unsigned char out = 0;
if (in & JOY_UP) out |= 0x01;
if (in & JOY_DOWN) out |= 0x02;
if (in & JOY_LEFT) out |= 0x04;
if (in & JOY_RIGHT) out |= 0x08;
if (in & JOY_BTN1) out |= 0x80;
return out;
}
void ikbd_handler_mouse_button_action(void) {
unsigned char action = ikbd.buffer.command.mouse_button_action;
ikbd_debugf("mouse button action = %d", action);
// bit 2: Mouse buttons act like keys (LEFT=0x74 & RIGHT=0x75)
if (action & 0x04) ikbd.state |= IKBD_STATE_MOUSE_BUTTON_AS_KEY;
else ikbd.state &= ~IKBD_STATE_MOUSE_BUTTON_AS_KEY;
}
void ikbd_handler_set_relative_mouse_pos(void) {
ikbd_debugf("Set relative mouse positioning");
ikbd.state &= ~IKBD_STATE_MOUSE_DISABLED;
ikbd.state &= ~IKBD_STATE_MOUSE_ABSOLUTE;
}
void ikbd_handler_set_abs_mouse_pos(void) {
ikbd.mouse.abs.max.x = be16(ikbd.buffer.command.abs_mouse_pos.max_x);
ikbd.mouse.abs.max.y = be16(ikbd.buffer.command.abs_mouse_pos.max_y);
ikbd_debugf("Set absolute mouse positioning, max = %u/%u",
ikbd.mouse.abs.max.x, ikbd.mouse.abs.max.y);
ikbd.state &= ~IKBD_STATE_MOUSE_DISABLED;
ikbd.state |= IKBD_STATE_MOUSE_ABSOLUTE;
ikbd.mouse.abs.buttons = 2 | 8;
}
void ikbd_handler_set_mouse_keycode_mode(void) {
ikbd_debugf("Set mouse keycode mode dist %u/%u",
ikbd.buffer.command.mouse_keycode.dist_x,
ikbd.buffer.command.mouse_keycode.dist_y);
}
void ikbd_handler_set_mouse_threshold(void) {
ikbd_debugf("Set mouse threshold %u/%u",
ikbd.buffer.command.mouse_threshold.x,
ikbd.buffer.command.mouse_threshold.y);
}
void ikbd_handler_set_mouse_scale(void) {
ikbd_debugf("Set mouse scale %u/%u",
ikbd.buffer.command.mouse_scale.x,
ikbd.buffer.command.mouse_scale.y);
ikbd.mouse.abs.scale.x = ikbd.buffer.command.mouse_scale.x;
ikbd.mouse.abs.scale.y = ikbd.buffer.command.mouse_scale.y;
}
void ikbd_handler_interrogate_mouse_pos(void) {
// ikbd_debugf("Interrogate Mouse Position");
if (ikbd.state & IKBD_STATE_MOUSE_ABSOLUTE) {
enqueue(0x8000 + 3); // 3ms delay, hatari uses 18000 cycles (~2.25ms)
enqueue(0xf7);
enqueue(ikbd.mouse.abs.buttons);
enqueue(ikbd.mouse.abs.pos.x >> 8);
enqueue(ikbd.mouse.abs.pos.x & 0xff);
enqueue(ikbd.mouse.abs.pos.y >> 8);
enqueue(ikbd.mouse.abs.pos.y & 0xff);
ikbd.mouse.abs.buttons = 0;
}
}
void ikbd_handler_load_mouse_pos(void) {
ikbd.mouse.abs.pos.x = be16(ikbd.buffer.command.load_mouse_pos.x);
ikbd.mouse.abs.pos.y = be16(ikbd.buffer.command.load_mouse_pos.y);
ikbd_debugf("Load mouse position %u/%u", ikbd.mouse.abs.pos.x, ikbd.mouse.abs.pos.y);
}
void ikbd_handler_set_y_bottom(void) {
ikbd_debugf("Set Y at bottom");
ikbd.state |= IKBD_STATE_MOUSE_Y_BOTTOM;
}
void ikbd_handler_set_y_top(void) {
ikbd_debugf("Set Y at top");
ikbd.state &= ~IKBD_STATE_MOUSE_Y_BOTTOM;
}
void ikbd_handler_resume(void) {
ikbd.state &= ~IKBD_STATE_PAUSED;
}
void ikbd_handler_disable_mouse(void) {
ikbd_debugf("Disable mouse");
ikbd.state |= IKBD_STATE_MOUSE_DISABLED;
}
void ikbd_handler_pause(void) {
ikbd.state |= IKBD_STATE_PAUSED;
}
void ikbd_handler_set_joystick_event_reporting(void) {
ikbd_debugf("Set Joystick event reporting");
ikbd.state |= IKBD_STATE_JOYSTICK_EVENT_REPORTING;
ikbd.state &= ~IKBD_STATE_PAUSED;
}
void ikbd_handler_set_joystick_interrogation_mode(void) {
ikbd_debugf("Set Joystick interrogation mode");
ikbd.state &= ~IKBD_STATE_JOYSTICK_EVENT_REPORTING;
ikbd.state &= ~IKBD_STATE_PAUSED;
}
void ikbd_handler_interrogate_joystick(void) {
// send reply
enqueue(0xfd);
enqueue(ikbd.joy[0].state | ((ikbd.mouse.but & (1 << 0)) ? 0x80 : 0x00));
enqueue(ikbd.joy[1].state | ((ikbd.mouse.but & (1 << 1)) ? 0x80 : 0x00));
}
void ikbd_handler_disable_joysticks(void) {
ikbd_debugf("Disable joysticks");
ikbd.state &= ~IKBD_STATE_JOYSTICK_EVENT_REPORTING;
}
void ikbd_handler_time_set(void) {
unsigned char c;
for (c = 0; c<6; c++)
ikbd.date[c] = bcd2bin(ikbd.buffer.command.date[c]);
// release SPI since it will be used by usb when
// reading the time from the rtc
DisableIO();
// try to set time on rtc if present
//usb_rtc_set_time(ikbd.date);
spi_uio_cmd_cont(UIO_IKBD_IN);
ikbd_debugf("Time of day clock set: %u:%02u:%02u %u.%u.%u",
ikbd.date[3], ikbd.date[4], ikbd.date[5],
ikbd.date[2], ikbd.date[1], 1900 + ikbd.date[0]);
}
void ikbd_handler_interrogate_time(void) {
unsigned char i;
// release SPI since it will be used by usb when
// reading the time from the rtc
DisableIO();
// try to fetch time from rtc if present
//usb_rtc_get_time(ikbd.date);
spi_uio_cmd_cont(UIO_IKBD_IN);
ikbd_debugf("Interrogate time of day %u:%02u:%02u %u.%u.%u",
ikbd.date[3], ikbd.date[4], ikbd.date[5],
ikbd.date[2], ikbd.date[1], 1900 + ikbd.date[0]);
enqueue(0x8000 + 64); // wait 64ms
enqueue(0xfc);
for (i = 0; i<6; i++) enqueue(bin2bcd(ikbd.date[i]));
}
void ikbd_handler_reset(void) {
ikbd_debugf("Reset %x", ikbd.buffer.command.reset);
if (ikbd.buffer.command.reset == 1) {
ikbd.state = IKBD_DEFAULT;
enqueue(0x8000 + 300); // wait 300ms
enqueue(0xf0);
}
}
// ---- list of supported ikbd commands ----
struct {
unsigned char code;
unsigned char length;
void(*handler)(void);
} ikbd_command_handler[] = {
{ 0x07, 2, ikbd_handler_mouse_button_action },
{ 0x08, 1, ikbd_handler_set_relative_mouse_pos },
{ 0x09, 5, ikbd_handler_set_abs_mouse_pos },
{ 0x0a, 3, ikbd_handler_set_mouse_keycode_mode },
{ 0x0b, 3, ikbd_handler_set_mouse_threshold },
{ 0x0c, 3, ikbd_handler_set_mouse_scale },
{ 0x0d, 1, ikbd_handler_interrogate_mouse_pos },
{ 0x0e, 6, ikbd_handler_load_mouse_pos },
{ 0x0f, 1, ikbd_handler_set_y_bottom },
{ 0x10, 1, ikbd_handler_set_y_top },
{ 0x11, 1, ikbd_handler_resume },
{ 0x12, 1, ikbd_handler_disable_mouse },
{ 0x13, 1, ikbd_handler_pause },
{ 0x14, 1, ikbd_handler_set_joystick_event_reporting },
{ 0x15, 1, ikbd_handler_set_joystick_interrogation_mode },
{ 0x16, 1, ikbd_handler_interrogate_joystick },
{ 0x1a, 1, ikbd_handler_disable_joysticks },
{ 0x1c, 1, ikbd_handler_interrogate_time },
{ 0x1b, 7, ikbd_handler_time_set },
{ 0x80, 2, ikbd_handler_reset },
{ 0, 0, NULL } // end of list
};
void ikbd_init() {
// reset ikbd state
memset(&ikbd, 0, sizeof(ikbd));
ikbd.state = IKBD_DEFAULT | IKBD_STATE_WAIT4RESET;
ikbd.mouse.abs.max.x = ikbd.mouse.abs.max.y = 65535;
ikbd.mouse.abs.scale.x = ikbd.mouse.abs.scale.y = 1;
ikbd_debugf("Init");
// init ikbd date to some default
ikbd.date[0] = 113;
ikbd.date[1] = 7;
ikbd.date[2] = 20;
ikbd.date[3] = 20;
ikbd.date[4] = 58;
// handle auto events
ikbd.auto_timer = GetTimer(0);
ikbd.rtc_timer = GetTimer(1000);
}
void ikbd_reset(void) {
ikbd.tx_cnt = 0;
ikbd.state |= IKBD_STATE_WAIT4RESET;
}
// process inout from atari core into ikbd
void ikbd_handle_input(unsigned char cmd) {
// store byte in buffer
ikbd.buffer.byte[ikbd.buffer.size++] = cmd;
// check if there's a known command in the buffer
char c;
for (c = 0; ikbd_command_handler[c].length &&
(ikbd_command_handler[c].code != ikbd.buffer.command.code); c++);
// not a valid command? -> flush buffer
if (!ikbd_command_handler[c].length)
ikbd.buffer.size = 0;
else {
// valid command and enough bytes?
if (ikbd_command_handler[c].length == ikbd.buffer.size) {
ikbd_command_handler[c].handler();
ikbd.buffer.size = 0;
}
}
}
// advance the ikbd time by one second
static void ikbd_update_time()
{
static const char mdays[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
short year = 1900 + ikbd.date[0];
char is_leap = (!(year % 4) && (year % 100)) || !(year % 400);
// advance seconds
ikbd.date[5]++;
if (ikbd.date[5] == 60)
{
ikbd.date[5] = 0;
// advance minutes
ikbd.date[4]++;
if (ikbd.date[4] == 60)
{
ikbd.date[4] = 0;
// advance hours
ikbd.date[3]++;
if (ikbd.date[3] == 24)
{
ikbd.date[3] = 0;
// advance days
ikbd.date[2]++;
if ((ikbd.date[2] == mdays[ikbd.date[1] - 1] + 1) ||
(is_leap && (ikbd.date[1] == 2) && (ikbd.date[2] == 29)))
{
ikbd.date[2] = 1;
// advance month
ikbd.date[1]++;
if (ikbd.date[1] == 13)
{
ikbd.date[1] = 0;
// advance year
ikbd.date[0]++;
}
}
}
}
}
}
void ikbd_poll(void) {
#ifdef IKBD_DEBUG
static unsigned long xtimer = 0;
static int last_cnt = 0;
if (CheckTimer(xtimer)) {
xtimer = GetTimer(2000);
if (ikbd.tx_cnt != last_cnt) {
ikbd_debugf("sent bytes: %d", ikbd.tx_cnt);
last_cnt = ikbd.tx_cnt;
}
}
#endif
if (CheckTimer(ikbd.rtc_timer))
{
ikbd.rtc_timer = GetTimer(1000);
ikbd_update_time();
}
// do auto events every 20ms
if (CheckTimer(ikbd.auto_timer)) {
ikbd.auto_timer = GetTimer(IKBD_AUTO_MS);
if (!(ikbd.state & IKBD_STATE_WAIT4RESET) &&
!(ikbd.state & IKBD_STATE_PAUSED)) {
/* --------- joystick ---------- */
if (ikbd.state & IKBD_STATE_JOYSTICK_EVENT_REPORTING) {
char i;
for (i = 0; i<2; i++) {
unsigned char state = ikbd.joy[i].state;
// left mouse button 1 is also joystick 0 fire button
// right mouse button 0 is also joystick 1 fire button
if (ikbd.mouse.but & (2 >> i)) state |= 0x80;
if (state != ikbd.joy[i].prev) {
// iprintf("JOY%d: %x\n", i, state);
enqueue(0xfe + i);
enqueue(state);
ikbd.joy[i].prev = state;
}
}
}
/* ----------- relative mouse ---------- */
if (!(ikbd.state & IKBD_STATE_MOUSE_DISABLED) &&
!(ikbd.state & IKBD_STATE_MOUSE_ABSOLUTE)) {
unsigned char b = ikbd.mouse.but;
// include joystick buttons into mouse state
if (ikbd.joy[0].state & 0x80) b |= 2;
if (ikbd.joy[1].state & 0x80) b |= 1;
if (ikbd.mouse.x || ikbd.mouse.y || (b != ikbd.mouse.but_prev)) {
do {
char x, y;
if (ikbd.mouse.x < -128) x = -128;
else if (ikbd.mouse.x > 127) x = 127;
else x = ikbd.mouse.x;
if (ikbd.mouse.y < -128) y = -128;
else if (ikbd.mouse.y > 127) y = 127;
else y = ikbd.mouse.y;
// iprintf("RMOUSE: %x %x %x\n", b, x&0xff, y&0xff);
enqueue(0xf8 | b);
enqueue(x & 0xff);
enqueue(y & 0xff);
ikbd.mouse.x -= x;
ikbd.mouse.y -= y;
} while (ikbd.mouse.x || ikbd.mouse.y);
// check if mouse buttons are supposed to be treated like keys
if (ikbd.state & IKBD_STATE_MOUSE_BUTTON_AS_KEY) {
// check if mouse button state has changed
if (b != ikbd.mouse.but_prev) {
// Mouse buttons act like keys (LEFT=0x74 & RIGHT=0x75)
// handle left mouse button
if ((b ^ ikbd.mouse.but_prev) & 2) ikbd_keyboard(0x74 | ((b & 2) ? 0x00 : 0x80));
// handle right mouse button
if ((b ^ ikbd.mouse.but_prev) & 1) ikbd_keyboard(0x75 | ((b & 1) ? 0x00 : 0x80));
}
}
ikbd.mouse.but_prev = b;
}
}
}
}
static unsigned long mtimer = 0;
if (CheckTimer(mtimer)) {
mtimer = GetTimer(10);
// check for incoming ikbd data
spi_uio_cmd_cont(UIO_IKBD_IN);
while (spi_in())
ikbd_handle_input(spi_in());
DisableIO();
}
// everything below must not happen faster than 1khz
static unsigned long rtimer = 0;
if (!CheckTimer(rtimer))
return;
// next event 1 ms later
rtimer = GetTimer(1);
// timer active?
if (ikbd_timer) {
if (!CheckTimer(ikbd_timer))
return;
ikbd_timer = 0;
}
if (rptr == wptr) return;
if (tx_queue[rptr] & 0x8000) {
// request to start timer?
if (tx_queue[rptr] & 0x8000)
ikbd_timer = GetTimer(tx_queue[rptr] & 0x3fff);
rptr = (rptr + 1)&(QUEUE_LEN - 1);
return;
}
// transmit data from queue
spi_uio_cmd_cont(UIO_IKBD_OUT);
spi8(tx_queue[rptr]);
DisableIO();
ikbd.tx_cnt++;
rptr = (rptr + 1)&(QUEUE_LEN - 1);
}
// called from external parts to report joystick states
void ikbd_joystick(unsigned char joystick, unsigned char map) {
ikbd.joy[joystick].state = joystick_map2ikbd(map);
}
void ikbd_keyboard(unsigned char code) {
#ifdef IKBD_DEBUG
ikbd_debugf("send keycode %x%s", code & 0x7f, (code & 0x80) ? " BREAK" : "");
#endif
enqueue(code);
}
void ikbd_mouse(unsigned char b, char x, char y) {
// honour reversal of y axis
if (ikbd.state & IKBD_STATE_MOUSE_Y_BOTTOM)
y = -y;
// update relative mouse state
ikbd.mouse.but = ((b & 1) ? 2 : 0) | ((b & 2) ? 1 : 0);
ikbd.mouse.x += x;
ikbd.mouse.y += y;
// save button state for absolute mouse reports
if (ikbd.state & IKBD_STATE_MOUSE_ABSOLUTE) {
// include joystick buttons into mouse state
if (ikbd.joy[0].state & 0x80) b |= 2;
if (ikbd.joy[1].state & 0x80) b |= 1;
if (b & 2) ikbd.mouse.abs.buttons |= 1;
else ikbd.mouse.abs.buttons |= 2;
if (b & 1) ikbd.mouse.abs.buttons |= 4;
else ikbd.mouse.abs.buttons |= 8;
if (ikbd.mouse.abs.scale.x > 1) x *= ikbd.mouse.abs.scale.x;
if (ikbd.mouse.abs.scale.y > 1) y *= ikbd.mouse.abs.scale.y;
// ikbd_debugf("abs inc %d %d -> ", x, y);
if (x < 0) {
x = -x;
if (ikbd.mouse.abs.pos.x > x) ikbd.mouse.abs.pos.x -= x;
else ikbd.mouse.abs.pos.x = 0;
}
else if (x > 0) {
if (ikbd.mouse.abs.pos.x < ikbd.mouse.abs.max.x - x)
ikbd.mouse.abs.pos.x += x;
else
ikbd.mouse.abs.pos.x = ikbd.mouse.abs.max.x;
}
if (y < 0) {
y = -y;
if (ikbd.mouse.abs.pos.y > y) ikbd.mouse.abs.pos.y -= y;
else ikbd.mouse.abs.pos.y = 0;
}
else if (y > 0) {
if (ikbd.mouse.abs.pos.y < ikbd.mouse.abs.max.y - y)
ikbd.mouse.abs.pos.y += y;
else
ikbd.mouse.abs.pos.y = ikbd.mouse.abs.max.y;
}
}
}

11
ikbd.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef IKBD_H
#define IKBD_H
void ikbd_init(void);
void ikbd_poll(void);
void ikbd_reset(void);
void ikbd_joystick(unsigned char joy, unsigned char map);
void ikbd_mouse(unsigned char buttons, char x, char y);
void ikbd_keyboard(unsigned char code);
#endif // IKBD_H

368
ini_parser.c Normal file
View File

@@ -0,0 +1,368 @@
// ini_parser.c
// 2015, rok.krajnc@gmail.com
//// includes ////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <ctype.h>
#include "ini_parser.h"
#include "debug.h"
#include "file_io.h"
#include "user_io.h"
//// defines ////
#define INI_EOT 4 // End-Of-Transmission
#define INI_BUF_SIZE 512
#define INI_LINE_SIZE 65
#define INI_SECTION_START '['
#define INI_SECTION_END ']'
#define INI_SECTION_INVALID_ID 0
//// macros ////
#define CHAR_IS_NUM(c) (((c) >= '0') && ((c) <= '9'))
#define CHAR_IS_ALPHA_LOWER(c) (((c) >= 'a') && ((c) <= 'z'))
#define CHAR_IS_ALPHA_UPPER(c) (((c) >= 'A') && ((c) <= 'Z'))
#define CHAR_IS_ALPHA(c) (CHAR_IS_ALPHA_LOWER(c) || CHAR_IS_ALPHA_UPPER(c))
#define CHAR_IS_ALPHANUM(c) (CHAR_IS_ALPHA_LOWER(c) || CHAR_IS_ALPHA_UPPER(c) || CHAR_IS_NUM(c))
#define CHAR_IS_SPECIAL(c) (((c) == '[') || ((c) == ']') || ((c) == '-') || ((c) == '_') || ((c) == ',') || ((c) == '='))
#define CHAR_IS_VALID(c) (CHAR_IS_ALPHANUM(c) || CHAR_IS_SPECIAL(c))
#define CHAR_IS_WHITESPACE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\r') || ((c) == '\n'))
#define CHAR_IS_SPACE(c) (((c) == ' ') || ((c) == '\t'))
#define CHAR_IS_LINEEND(c) (((c) == '\n'))
#define CHAR_IS_COMMENT(c) (((c) == ';'))
#define CHAR_IS_QUOTE(c) (((c) == '"'))
fileTYPE ini_file;
static uint8_t buf[512];
int ini_pt = 0;
//// ini_getch() ////
char ini_getch()
{
if ((ini_pt & 0x3ff) == 0x200)
{
// reload buffer
FileReadSec(&ini_file, buf);
}
if (ini_pt >= ini_file.size) return 0;
else return buf[(ini_pt++) & 0x1ff];
}
//// ini_putch() ////
int ini_putch(char c)
{
static int ini_pt = 0;
buf[ini_pt++] = c;
if ((ini_pt % 0x3ff) == 0x200)
{
// write buffer
ini_pt = 0;
}
return ini_pt;
}
//// ini_findch() ////
char ini_findch(char c)
{
char t;
do {
t = ini_getch();
} while ((t != 0) && (t != c));
return t;
}
//// ini_getline() ////
int ini_getline(char* line)
{
char c;
char ignore = 0;
char literal = 0;
int i = 0;
while (i<(INI_LINE_SIZE - 1)) {
c = ini_getch();
if ((!c) || CHAR_IS_LINEEND(c)) break;
else if (CHAR_IS_QUOTE(c)) literal ^= 1;
else if (CHAR_IS_COMMENT(c) && !ignore && !literal) ignore++;
else if (literal) line[i++] = c;
else if (CHAR_IS_VALID(c) && !ignore) line[i++] = c;
}
line[i] = '\0';
return c == 0 ? INI_EOT : literal ? 1 : 0;
}
//// ini_putline() ////
int ini_putline(char* line)
{
int ini_pt, i = 0;
while (i<(INI_LINE_SIZE - 1)) {
if (!line[i]) break;
ini_pt = ini_putch(line[i++]);
}
return ini_pt;
}
char *get_core_name()
{
switch (user_io_core_type())
{
case CORE_TYPE_MINIMIG2:
return "MINIMIG";
case CORE_TYPE_PACE:
return "PACE";
case CORE_TYPE_MIST:
return "ST";
case CORE_TYPE_ARCHIE:
return "ARCHIE";
case CORE_TYPE_8BIT:
return user_io_get_core_name();
}
return "";
}
//// ini_get_section() ////
int ini_get_section(const ini_cfg_t* cfg, char* buf)
{
int i = 0;
// get section start marker
if (buf[0] != INI_SECTION_START) {
return INI_SECTION_INVALID_ID;
}
else buf++;
// get section stop marker
while (1) {
if (buf[i] == INI_SECTION_END) {
buf[i] = '\0';
break;
}
i++;
if (i >= INI_LINE_SIZE) {
return INI_SECTION_INVALID_ID;
}
}
// convert to uppercase
for (i = 0; i<INI_LINE_SIZE; i++) {
if (!buf[i]) break;
else buf[i] = toupper(buf[i]);
}
// parse section
for (i = 0; i<cfg->nsections; i++) {
if (!strcasecmp(buf, cfg->sections[i].name)) {
ini_parser_debugf("Got SECTION '%s' with ID %d", buf, cfg->sections[i].id);
return cfg->sections[i].id;
}
}
if (!strcasecmp(buf, get_core_name())) return cfg->sections[0].id;
return INI_SECTION_INVALID_ID;
}
//// ini_get_var() ////
void* ini_get_var(const ini_cfg_t* cfg, int cur_section, char* buf)
{
int i = 0, j = 0;
int var_id = -1;
// find var
while (1) {
if (buf[i] == '=') {
buf[i] = '\0';
break;
}
else if (buf[i] == '\0') return (void*)0;
i++;
}
// convert to uppercase
for (j = 0; j <= i; j++) {
if (!buf[j]) break;
else buf[j] = toupper(buf[j]);
}
// parse var
for (j = 0; j<cfg->nvars; j++) {
if ((!strcasecmp(buf, cfg->vars[j].name)) && (cfg->vars[j].section_id == cur_section)) var_id = j;
}
// get data
if (var_id != -1) {
ini_parser_debugf("Got VAR '%s' with VALUE %s", buf, &(buf[i + 1]));
i++;
switch (cfg->vars[var_id].type) {
case UINT8:
*(uint8_t*)(cfg->vars[var_id].var) = strtoul(&(buf[i]), NULL, 0);
if (*(uint8_t*)(cfg->vars[var_id].var) > cfg->vars[var_id].max) *(uint8_t*)(cfg->vars[var_id].var) = cfg->vars[var_id].max;
if (*(uint8_t*)(cfg->vars[var_id].var) < cfg->vars[var_id].min) *(uint8_t*)(cfg->vars[var_id].var) = cfg->vars[var_id].min;
break;
case INT8:
*(int8_t*)(cfg->vars[var_id].var) = strtol(&(buf[i]), NULL, 0);
if (*(int8_t*)(cfg->vars[var_id].var) > cfg->vars[var_id].max) *(int8_t*)(cfg->vars[var_id].var) = cfg->vars[var_id].max;
if (*(int8_t*)(cfg->vars[var_id].var) < cfg->vars[var_id].min) *(int8_t*)(cfg->vars[var_id].var) = cfg->vars[var_id].min;
break;
case UINT16:
*(uint16_t*)(cfg->vars[var_id].var) = strtoul(&(buf[i]), NULL, 0);
if (*(uint16_t*)(cfg->vars[var_id].var) > cfg->vars[var_id].max) *(uint16_t*)(cfg->vars[var_id].var) = cfg->vars[var_id].max;
if (*(uint16_t*)(cfg->vars[var_id].var) < cfg->vars[var_id].min) *(uint16_t*)(cfg->vars[var_id].var) = cfg->vars[var_id].min;
break;
case INT16:
*(int16_t*)(cfg->vars[var_id].var) = strtol(&(buf[i]), NULL, 0);
if (*(int16_t*)(cfg->vars[var_id].var) > cfg->vars[var_id].max) *(int16_t*)(cfg->vars[var_id].var) = cfg->vars[var_id].max;
if (*(int16_t*)(cfg->vars[var_id].var) < cfg->vars[var_id].min) *(int16_t*)(cfg->vars[var_id].var) = cfg->vars[var_id].min;
break;
case UINT32:
*(uint32_t*)(cfg->vars[var_id].var) = strtoul(&(buf[i]), NULL, 0);
if (*(uint32_t*)(cfg->vars[var_id].var) > (uint32_t)cfg->vars[var_id].max) *(uint32_t*)(cfg->vars[var_id].var) = cfg->vars[var_id].max;
if (*(uint32_t*)(cfg->vars[var_id].var) < (uint32_t)cfg->vars[var_id].min) *(uint32_t*)(cfg->vars[var_id].var) = cfg->vars[var_id].min;
break;
case INT32:
*(int32_t*)(cfg->vars[var_id].var) = strtol(&(buf[i]), NULL, 0);
if (*(int32_t*)(cfg->vars[var_id].var) > cfg->vars[var_id].max) *(int32_t*)(cfg->vars[var_id].var) = cfg->vars[var_id].max;
if (*(int32_t*)(cfg->vars[var_id].var) < cfg->vars[var_id].min) *(int32_t*)(cfg->vars[var_id].var) = cfg->vars[var_id].min;
break;
case FLOAT:
*(float*)(cfg->vars[var_id].var) = strtof(&(buf[i]), NULL);
if (*(float*)(cfg->vars[var_id].var) > cfg->vars[var_id].max) *(float*)(cfg->vars[var_id].var) = cfg->vars[var_id].max;
if (*(float*)(cfg->vars[var_id].var) < cfg->vars[var_id].min) *(float*)(cfg->vars[var_id].var) = cfg->vars[var_id].min;
break;
case STRING:
strncpy((char*)(cfg->vars[var_id].var), &(buf[i]), cfg->vars[var_id].max);
break;
case CUSTOM_HANDLER:
((custom_handler_t*)(cfg->vars[var_id].var))(&(buf[i]));
break;
}
return (void*)(&(cfg->vars[var_id].var));
}
return (void*)0;
}
//// ini_parse() ////
void ini_parse(const ini_cfg_t* cfg)
{
char line[INI_LINE_SIZE] = { 0 };
int section = INI_SECTION_INVALID_ID;
int line_status;
ini_parser_debugf("Start INI parser for core \"%s\".", get_core_name());
memset(&ini_file, 0, sizeof(ini_file));
if (!FileOpen(&ini_file, cfg->filename))
{
ini_parser_debugf("Can't open file %s !", cfg->filename);
return;
}
ini_parser_debugf("Opened file %s with size %d bytes.", cfg->filename, ini_file.size);
ini_pt = 0;
// preload buffer
FileReadSec(&ini_file, buf);
// parse ini
while (1)
{
// get line
line_status = ini_getline(line);
ini_parser_debugf("line(%d): \"%s\".", line_status, line);
// if valid line
if (line_status != 1)
{
if (line[0] == INI_SECTION_START)
{
// if first char in line is INI_SECTION_START, get section
section = ini_get_section(cfg, line);
}
else
{
// otherwise this is a variable, get it
ini_get_var(cfg, section, line);
}
}
// if end of file, stop
if (line_status == INI_EOT) break;
}
FileClose(&ini_file);
}
//// ini_save() ////
void ini_save(const ini_cfg_t* cfg)
{
// Not fully implemented yet.
/*
int section, var, ini_pt;
char line[INI_LINE_SIZE] = { 0 };
// open ini file
ini_parser_debugf("Can't open file %s !", cfg->filename);
return;
// loop over sections
for (section = 0; section<cfg->nsections; section++) {
ini_parser_debugf("writing section %s ...", cfg->sections[section].name);
siprintf(line, "[%s]\n", cfg->sections[section].name);
ini_pt = ini_putline(line);
// loop over vars
for (var = 0; var<cfg->nvars; var++) {
if (cfg->vars[var].section_id == cfg->sections[section].id) {
ini_parser_debugf("writing var %s", cfg->vars[var].name);
switch (cfg->vars[var].type) {
case UINT8:
case UINT16:
case UINT32:
siprintf(line, "%s=%u\n", cfg->vars[var].name, *(uint32_t*)(cfg->vars[var].var));
break;
case INT8:
case INT16:
case INT32:
siprintf(line, "%s=%d\n", cfg->vars[var].name, *(int32_t*)(cfg->vars[var].var));
break;
case FLOAT:
siprintf(line, "%s=%f\n", cfg->vars[var].name, *(float*)(cfg->vars[var].var));
break;
case STRING:
siprintf(line, "%s=\"%s\"\n", cfg->vars[var].name, (char*)(cfg->vars[var].var));
break;
}
ini_pt = ini_putline(line);
}
}
}
// in case the buffer is not written yet, write it now
if (ini_pt)
{
//fwrite(buf, sizeof(char), ini_pt, ini_fp);
}
*/
}

47
ini_parser.h Normal file
View File

@@ -0,0 +1,47 @@
// ini_parser.h
// 2015, rok.krajnc@gmail.com
#ifndef __INI_PARSER_H__
#define __INI_PARSER_H__
//// includes ////
#include <inttypes.h>
//// type definitions ////
typedef struct {
int id;
char* name;
} ini_section_t;
typedef enum {
UINT8 = 0, INT8, UINT16, INT16, UINT32, INT32, FLOAT,
STRING, CUSTOM_HANDLER
} ini_vartypes_t;
typedef void custom_handler_t(char*);
typedef struct {
char* name;
void* var;
ini_vartypes_t type;
int min;
int max;
int section_id;
} ini_var_t;
typedef struct {
const char* filename;
const ini_section_t* sections;
const ini_var_t* vars;
int nsections;
int nvars;
} ini_cfg_t;
//// functions ////
void ini_parse(const ini_cfg_t* cfg);
void ini_save(const ini_cfg_t* cfg);
#endif // __INI_PARSER_H__

955
input.c Normal file
View File

@@ -0,0 +1,955 @@
#include <stdio.h>
#include <fcntl.h>
#include <linux/input.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/inotify.h>
#include <sys/poll.h>
#include <sys/sysinfo.h>
#include "input.h"
#include "user_io.h"
#define NUMDEV 10
typedef struct
{
uint16_t vid, pid;
char led;
char has_map;
char last_l, last_r, last_u, last_d;
uint32_t map[32];
} devInput;
static devInput input[NUMDEV] = {0};
static int first_joystick = -1;
#define LCTRL 0x0100
#define LSHIFT 0x0200
#define LALT 0x0400
#define LGUI 0x0800
#define RCTRL 0x1000
#define RSHIFT 0x2000
#define RALT 0x4000
#define RGUI 0x8000
#define MODMASK 0xFF00
#define NONE 0
static int ev2usb[] =
{
NONE, //0 KEY_RESERVED
0x29, //1 KEY_ESC
0x1e, //2 KEY_1
0x1f, //3 KEY_2
0x20, //4 KEY_3
0x21, //5 KEY_4
0x22, //6 KEY_5
0x23, //7 KEY_6
0x24, //8 KEY_7
0x25, //9 KEY_8
0x26, //10 KEY_9
0x27, //11 KEY_0
0x2D, //12 KEY_MINUS
0x2E, //13 KEY_EQUAL
0x2A, //14 KEY_BACKSPACE
0x2B, //15 KEY_TAB
0x14, //16 KEY_Q
0x1a, //17 KEY_W
0x08, //18 KEY_E
0x15, //19 KEY_R
0x17, //20 KEY_T
0x1c, //21 KEY_Y
0x18, //22 KEY_U
0x0c, //23 KEY_I
0x12, //24 KEY_O
0x13, //25 KEY_P
0x2F, //26 KEY_LEFTBRACE
0x30, //27 KEY_RIGHTBRACE
0x28, //28 KEY_ENTER
LCTRL, //29 KEY_LEFTCTRL
0x04, //30 KEY_A
0x16, //31 KEY_S
0x07, //32 KEY_D
0x09, //33 KEY_F
0x0a, //34 KEY_G
0x0b, //35 KEY_H
0x0d, //36 KEY_J
0x0e, //37 KEY_K
0x0f, //38 KEY_L
0x33, //39 KEY_SEMICOLON
0x34, //40 KEY_APOSTROPHE
0x35, //41 KEY_GRAVE
LSHIFT, //42 KEY_LEFTSHIFT
0x31, //43 KEY_BACKSLASH
0x1d, //44 KEY_Z
0x1b, //45 KEY_X
0x06, //46 KEY_C
0x19, //47 KEY_V
0x05, //48 KEY_B
0x11, //49 KEY_N
0x10, //50 KEY_M
0x36, //51 KEY_COMMA
0x37, //52 KEY_DOT
0x38, //53 KEY_SLASH
RSHIFT, //54 KEY_RIGHTSHIFT
0x55, //55 KEY_KPASTERISK
LALT, //56 KEY_LEFTALT
0x2C, //57 KEY_SPACE
0x39, //58 KEY_CAPSLOCK
0x3a, //59 KEY_F1
0x3b, //60 KEY_F2
0x3c, //61 KEY_F3
0x3d, //62 KEY_F4
0x3e, //63 KEY_F5
0x3f, //64 KEY_F6
0x40, //65 KEY_F7
0x41, //66 KEY_F8
0x42, //67 KEY_F9
0x43, //68 KEY_F10
0x53, //69 KEY_NUMLOCK
0x47, //70 KEY_SCROLLLOCK
0x5F, //71 KEY_KP7
0x60, //72 KEY_KP8
0x61, //73 KEY_KP9
0x56, //74 KEY_KPMINUS
0x5C, //75 KEY_KP4
0x5D, //76 KEY_KP5
0x5E, //77 KEY_KP6
0x57, //78 KEY_KPPLUS
0x59, //79 KEY_KP1
0x5A, //80 KEY_KP2
0x5B, //81 KEY_KP3
0x62, //82 KEY_KP0
0x63, //83 KEY_KPDOT
NONE, //84 ???
NONE, //85 KEY_ZENKAKU
NONE, //86 KEY_102ND
0x44, //87 KEY_F11
0x45, //88 KEY_F12
NONE, //89 KEY_RO
NONE, //90 KEY_KATAKANA
NONE, //91 KEY_HIRAGANA
NONE, //92 KEY_HENKAN
NONE, //93 KEY_KATAKANA
NONE, //94 KEY_MUHENKAN
NONE, //95 KEY_KPJPCOMMA
0x28, //96 KEY_KPENTER
RCTRL, //97 KEY_RIGHTCTRL
0x54, //98 KEY_KPSLASH
NONE, //99 KEY_SYSRQ
RALT, //100 KEY_RIGHTALT
NONE, //101 KEY_LINEFEED
0x4A, //102 KEY_HOME
0x52, //103 KEY_UP
0x4B, //104 KEY_PAGEUP
0x50, //105 KEY_LEFT
0x4F, //106 KEY_RIGHT
0x4D, //107 KEY_END
0x51, //108 KEY_DOWN
0x4E, //109 KEY_PAGEDOWN
0x49, //110 KEY_INSERT
0x4C, //111 KEY_DELETE
NONE, //112 KEY_MACRO
NONE, //113 KEY_MUTE
NONE, //114 KEY_VOLUMEDOWN
NONE, //115 KEY_VOLUMEUP
NONE, //116 KEY_POWER
0x67, //117 KEY_KPEQUAL
NONE, //118 KEY_KPPLUSMINUS
0x48, //119 KEY_PAUSE
NONE, //120 KEY_SCALE
NONE, //121 KEY_KPCOMMA
NONE, //122 KEY_HANGEUL
NONE, //123 KEY_HANJA
NONE, //124 KEY_YEN
LGUI, //125 KEY_LEFTMETA
RGUI, //126 KEY_RIGHTMETA
0x65, //127 KEY_COMPOSE
NONE, //128 KEY_STOP
NONE, //129 KEY_AGAIN
NONE, //130 KEY_PROPS
NONE, //131 KEY_UNDO
NONE, //132 KEY_FRONT
NONE, //133 KEY_COPY
NONE, //134 KEY_OPEN
NONE, //135 KEY_PASTE
NONE, //136 KEY_FIND
NONE, //137 KEY_CUT
NONE, //138 KEY_HELP
NONE, //139 KEY_MENU
NONE, //140 KEY_CALC
NONE, //141 KEY_SETUP
NONE, //142 KEY_SLEEP
NONE, //143 KEY_WAKEUP
NONE, //144 KEY_FILE
NONE, //145 KEY_SENDFILE
NONE, //146 KEY_DELETEFILE
NONE, //147 KEY_XFER
NONE, //148 KEY_PROG1
NONE, //149 KEY_PROG2
NONE, //150 KEY_WWW
NONE, //151 KEY_MSDOS
NONE, //152 KEY_SCREENLOCK
NONE, //153 KEY_DIRECTION
NONE, //154 KEY_CYCLEWINDOWS
NONE, //155 KEY_MAIL
NONE, //156 KEY_BOOKMARKS
NONE, //157 KEY_COMPUTER
NONE, //158 KEY_BACK
NONE, //159 KEY_FORWARD
NONE, //160 KEY_CLOSECD
NONE, //161 KEY_EJECTCD
NONE, //162 KEY_EJECTCLOSECD
NONE, //163 KEY_NEXTSONG
NONE, //164 KEY_PLAYPAUSE
NONE, //165 KEY_PREVIOUSSONG
NONE, //166 KEY_STOPCD
NONE, //167 KEY_RECORD
NONE, //168 KEY_REWIND
NONE, //169 KEY_PHONE
NONE, //170 KEY_ISO
NONE, //171 KEY_CONFIG
NONE, //172 KEY_HOMEPAGE
NONE, //173 KEY_REFRESH
NONE, //174 KEY_EXIT
NONE, //175 KEY_MOVE
NONE, //176 KEY_EDIT
NONE, //177 KEY_SCROLLUP
NONE, //178 KEY_SCROLLDOWN
NONE, //179 KEY_KPLEFTPAREN
NONE, //180 KEY_KPRIGHTPAREN
NONE, //181 KEY_NEW
NONE, //182 KEY_REDO
NONE, //183 KEY_F13
NONE, //184 KEY_F14
NONE, //185 KEY_F15
NONE, //186 KEY_F16
NONE, //187 KEY_F17
NONE, //188 KEY_F18
NONE, //189 KEY_F19
NONE, //190 KEY_F20
NONE, //191 KEY_F21
NONE, //192 KEY_F22
NONE, //193 KEY_F23
NONE, //194 KEY_F24
NONE, //195 ???
NONE, //196 ???
NONE, //197 ???
NONE, //198 ???
NONE, //199 ???
NONE, //200 KEY_PLAYCD
NONE, //201 KEY_PAUSECD
NONE, //202 KEY_PROG3
NONE, //203 KEY_PROG4
NONE, //204 KEY_DASHBOARD
NONE, //205 KEY_SUSPEND
NONE, //206 KEY_CLOSE
NONE, //207 KEY_PLAY
NONE, //208 KEY_FASTFORWARD
NONE, //209 KEY_BASSBOOST
0x46, //210 KEY_PRINT
NONE, //211 KEY_HP
NONE, //212 KEY_CAMERA
NONE, //213 KEY_SOUND
NONE, //214 KEY_QUESTION
NONE, //215 KEY_EMAIL
NONE, //216 KEY_CHAT
NONE, //217 KEY_SEARCH
NONE, //218 KEY_CONNECT
NONE, //219 KEY_FINANCE
NONE, //220 KEY_SPORT
NONE, //221 KEY_SHOP
NONE, //222 KEY_ALTERASE
NONE, //223 KEY_CANCEL
NONE, //224 KEY_BRIGHT_DOWN
NONE, //225 KEY_BRIGHT_UP
NONE, //226 KEY_MEDIA
NONE, //227 KEY_SWITCHVIDEO
NONE, //228 KEY_DILLUMTOGGLE
NONE, //229 KEY_DILLUMDOWN
NONE, //230 KEY_DILLUMUP
NONE, //231 KEY_SEND
NONE, //232 KEY_REPLY
NONE, //233 KEY_FORWARDMAIL
NONE, //234 KEY_SAVE
NONE, //235 KEY_DOCUMENTS
NONE, //236 KEY_BATTERY
NONE, //237 KEY_BLUETOOTH
NONE, //238 KEY_WLAN
NONE, //239 KEY_UWB
NONE, //240 KEY_UNKNOWN
NONE, //241 KEY_VIDEO_NEXT
NONE, //242 KEY_VIDEO_PREV
NONE, //243 KEY_BRIGHT_CYCLE
NONE, //244 KEY_BRIGHT_AUTO
NONE, //245 KEY_DISPLAY_OFF
NONE, //246 KEY_WWAN
NONE, //247 KEY_RFKILL
NONE, //248 KEY_MICMUTE
NONE, //249 ???
NONE, //250 ???
NONE, //251 ???
NONE, //252 ???
NONE, //253 ???
NONE, //254 ???
NONE //255 ???
};
int mfd = -1;
int mwd = -1;
static int set_watch()
{
mwd = -1;
mfd = inotify_init();
if (mfd < 0)
{
printf("ERR: inotify_init");
return -1;
}
mwd = inotify_add_watch(mfd, "/dev/input",
IN_MODIFY | IN_CREATE | IN_DELETE);
if (mwd < 0)
{
printf("ERR: inotify_add_watch");
return -1;
}
return mfd;
}
#define EVENT_SIZE ( sizeof (struct inotify_event) )
#define BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
static int check_devs()
{
int result = 0;
int length, i = 0;
char buffer[BUF_LEN];
length = read(mfd, buffer, BUF_LEN);
if (length < 0)
{
printf("ERR: read\n");
return 0;
}
while (i<length)
{
struct inotify_event *event = (struct inotify_event *) &buffer[i];
if (event->len)
{
if (event->mask & IN_CREATE)
{
result = 1;
if (event->mask & IN_ISDIR)
{
printf("The directory %s was created.\n", event->name);
}
else
{
printf("The file %s was created.\n", event->name);
}
}
else if (event->mask & IN_DELETE)
{
result = 1;
if (event->mask & IN_ISDIR)
{
printf("The directory %s was deleted.\n", event->name);
}
else
{
printf("The file %s was deleted.\n", event->name);
}
}
/*
else if ( event->mask & IN_MODIFY )
{
result = 1;
if ( event->mask & IN_ISDIR )
{
printf( "The directory %s was modified.\n", event->name );
}
else
{
printf( "The file %s was modified.\n", event->name );
}
}
*/
}
i += EVENT_SIZE + event->len;
}
return result;
}
static void INThandler()
{
printf("\nExiting...\n");
if (mwd >= 0) inotify_rm_watch(mfd, mwd);
if (mfd >= 0) close(mfd);
exit(0);
}
#define test_bit(bit, array) (array [bit / 8] & (1 << (bit % 8)))
static char has_led(int fd)
{
unsigned char evtype_b[(EV_MAX + 7) / 8];
if (fd<0) return 0;
memset(&evtype_b, 0, sizeof(evtype_b));
if (ioctl(fd, EVIOCGBIT(0, sizeof(evtype_b)), evtype_b) < 0)
{
printf("ERR: evdev ioctl.\n");
return 0;
}
if (test_bit(EV_LED, evtype_b))
{
printf("has LEDs.\n");
return 1;
}
return 0;
}
static char leds_state = 0;
void set_kbdled(int mask, int state)
{
leds_state = state ? leds_state | (mask&HID_LED_MASK) : leds_state & ~(mask&HID_LED_MASK);
}
int get_kbdled(int mask)
{
return (leds_state & (mask&HID_LED_MASK)) ? 1 : 0;
}
int toggle_kbdled(int mask)
{
int state = !get_kbdled(mask);
set_kbdled(mask, state);
return state;
}
int mapping = 0;
int mapping_button;
int mapping_dev;
void start_map_setting()
{
mapping_button = 0;
mapping = 1;
mapping_dev = -1;
}
int get_map_button()
{
return mapping_button;
}
void finish_map_setting()
{
mapping = 0;
if (mapping_dev<0) return;
char name[32];
sprintf(name, "input_%04x_%04x.map", input[mapping_dev].vid, input[mapping_dev].pid);
FileSave(name, &input[mapping_dev].map, sizeof(input[mapping_dev].map));
}
uint16_t get_map_vid()
{
return input[mapping_dev].vid;
}
uint16_t get_map_pid()
{
return input[mapping_dev].pid;
}
#define KEY_EMU_LEFT (KEY_MAX+1)
#define KEY_EMU_RIGHT (KEY_MAX+2)
#define KEY_EMU_UP (KEY_MAX+3)
#define KEY_EMU_DOWN (KEY_MAX+4)
static char joy[2] = { 0 };
static void input_cb(struct input_event *ev, int dev);
static void joy_digital(int num, uint16_t mask, char press)
{
if (num < 2)
{
if (user_io_osd_is_visible() || (mask == JOY_OSD))
{
memset(joy, 0, sizeof(joy));
struct input_event ev;
ev.type = EV_KEY;
ev.value = press;
switch (mask)
{
case JOY_RIGHT:
ev.code = KEY_RIGHT;
break;
case JOY_LEFT:
ev.code = KEY_LEFT;
break;
case JOY_UP:
ev.code = KEY_UP;
break;
case JOY_DOWN:
ev.code = KEY_DOWN;
break;
case JOY_BTN1:
ev.code = KEY_ENTER;
break;
case JOY_BTN2:
ev.code = KEY_ESC;
break;
case JOY_BTN3:
ev.code = KEY_BACKSPACE;
break;
case JOY_OSD:
ev.code = KEY_F12;
break;
default:
ev.code = 0;
}
input_cb(&ev, 0);
}
else
{
if (press) joy[num] |= (char)mask;
else joy[num] &= ~(char)mask;
user_io_joystick(num, joy[num]);
}
}
}
static void input_cb(struct input_event *ev, int dev)
{
static int key_mapped = 0;
static uint8_t modifiers = 0;
static char keys[6] = { 0,0,0,0,0,0 };
static unsigned char mouse_btn = 0;
switch (ev->type)
{
case EV_KEY:
{
if (ev->code == 272)
{
if (ev->value <= 1)
{
mouse_btn = (mouse_btn & ~1) | ev->value;
user_io_mouse(mouse_btn, 0, 0);
}
return;
}
if (ev->code == 273)
{
if (ev->value <= 1)
{
mouse_btn = (mouse_btn & ~2) | (ev->value << 1);
user_io_mouse(mouse_btn, 0, 0);
}
return;
}
int key = (ev->code < (sizeof(ev2usb) / sizeof(ev2usb[0]))) ? ev2usb[ev->code] : NONE;
if ((key != NONE))
{
if (ev->value > 1)
{
return;
}
if (key & MODMASK)
{
modifiers = (ev->value) ? modifiers | (uint8_t)(key >> 8) : modifiers & ~(uint8_t)(key >> 8);
}
else
{
if (ev->value)
{
int found = 0;
for (int i = 0; i < (sizeof(keys) / sizeof(keys[0])); i++) if (keys[i] == (uint8_t)key) found = 1;
if (!found)
{
for (int i = 0; i < (sizeof(keys) / sizeof(keys[0])); i++)
{
if (!keys[i])
{
keys[i] = (uint8_t)key;
break;
}
}
}
}
else
{
for (int i = 0; i < (sizeof(keys) / sizeof(keys[0])); i++) if (keys[i] == (uint8_t)key) keys[i] = 0;
}
int j = 0;
for (int i = 0; i < (sizeof(keys) / sizeof(keys[0])); i++) if (keys[i]) keys[j++] = keys[i];
while (j < (sizeof(keys) / sizeof(keys[0]))) keys[j++] = 0;
}
user_io_kbd(modifiers, keys, input[dev].vid, input[dev].pid);
return;
}
}
break;
case EV_REL:
{
switch (ev->code)
{
case 0:
//printf("Mouse PosX: %d\n", ev->value);
user_io_mouse(mouse_btn, ev->value, 0);
return;
case 1:
//printf("Mouse PosY: %d\n", ev->value);
user_io_mouse(mouse_btn, 0, ev->value);
return;
}
}
break;
}
if (!input[dev].has_map)
{
char name[32];
sprintf(name, "input_%04x_%04x.map", input[dev].vid, input[dev].pid);
if (!FileLoad(name, &input[dev].map, sizeof(input[dev].map)))
{
memset(&input[dev].map, 0, sizeof(input[dev].map));
}
input[dev].has_map = 1;
}
//joystick
if (mapping && (mapping_dev >=0 || ev->value))
{
if (ev->type == EV_KEY && ev->value <= 1 && ev->code >= BTN_JOYSTICK)
{
if (mapping_dev < 0) mapping_dev = dev;
if (mapping_dev == dev && mapping_button < 9)
{
if (ev->value)
{
if(!mapping_button) memset(&input[dev].map, 0, sizeof(input[dev].map));
int found = 0;
for (int i = 0; i < mapping_button; i++) if (input[dev].map[i] == ev->code) found = 1;
if (!found)
{
input[dev].map[mapping_button] = ev->code;
key_mapped = 1;
}
}
else
{
if(key_mapped) mapping_button++;
key_mapped = 0;
}
}
}
}
else
{
key_mapped = 0;
switch (ev->type)
{
//buttons, digital directions
case EV_KEY:
if (ev->value <= 1)
{
if (first_joystick < 0) first_joystick = dev;
for (int i = 0; i < 9; i++)
{
if (ev->code == input[dev].map[i])
{
joy_digital((first_joystick == dev) ? 0 : 1, 1<<i, ev->value);
return;
}
}
}
break;
//analog joystick
case EV_ABS:
// skip if first joystick is not defined.
if (first_joystick < 0) break;
// TODO:
// 1) add analog axis mapping (input[dev].map[16], input[dev].map[17])
// 2) enable invertion
if (ev->code == 0) // x
{
int offset = 0;
if (ev->value < 127 || ev->value>129) offset = ev->value - 128;
//joy_analog((first_joystick == dev) ? 0 : 1, 0, offset);
return;
}
if (ev->code == 1) // y
{
int offset = 0;
if (ev->value < 127 || ev->value>129) offset = ev->value - 128;
//joy_analog((first_joystick == dev) ? 0 : 1, 1, offset);
return;
}
break;
}
}
}
static uint16_t read_hex(char *filename)
{
FILE *in;
unsigned int value;
in = fopen(filename, "rb");
if (!in) return 0;
if (fscanf(in, "%x", &value) == 1)
{
fclose(in);
return (uint16_t)value;
}
fclose(in);
return 0;
}
static void getVidPid(int num, uint16_t* vid, uint16_t* pid)
{
char name[256];
sprintf(name, "/sys/class/input/event%d/device/id/vendor", num);
*vid = read_hex(name);
sprintf(name, "/sys/class/input/event%d/device/id/product", num);
*pid = read_hex(name);
}
int input_poll(int getchar)
{
static struct pollfd pool[NUMDEV + 1];
static char cur_leds = 0;
static int state = 0;
char devname[20];
struct input_event ev;
if (state == 0)
{
signal(SIGINT, INThandler);
pool[NUMDEV].fd = set_watch();
pool[NUMDEV].events = POLLIN;
state++;
}
if (state == 1)
{
printf("Open up to %d input devices.\n", NUMDEV);
for (int i = 0; i<NUMDEV; i++)
{
sprintf(devname, "/dev/input/event%d", i);
pool[i].fd = open(devname, O_RDWR);
pool[i].events = POLLIN;
memset(&input[i], 0, sizeof(input[i]));
input[i].led = has_led(pool[i].fd);
if (pool[i].fd > 0) getVidPid(i, &input[i].vid, &input[i].pid);
if (pool[i].fd > 0) printf("opened %s (%04x:%04x)\n", devname, input[i].vid, input[i].pid);
}
cur_leds |= 0x80;
state++;
}
if (state == 2)
{
int return_value = poll(pool, NUMDEV + 1, 0);
if (return_value < 0)
{
printf("ERR: poll\n");
}
else if (return_value > 0)
{
if ((pool[NUMDEV].revents & POLLIN) && check_devs())
{
printf("Close all devices.\n");
for (int i = 0; i<NUMDEV; i++)
{
if (pool[i].fd >= 0) close(pool[i].fd);
}
state = 1;
return 0;
}
for (int i = 0; i<NUMDEV; i++)
{
if ((pool[i].fd >= 0) && (pool[i].revents & POLLIN))
{
memset(&ev, 0, sizeof(ev));
if (read(pool[i].fd, &ev, sizeof(ev)) == sizeof(ev))
{
if (getchar)
{
if (ev.type == EV_KEY && ev.value >= 1)
{
return ev.code;
}
}
else
{
if (is_menu_core())
{
switch (ev.type)
{
//keyboard, buttons
case EV_KEY:
printf("Input event: type=EV_KEY, code=%d(%x), value=%d\n", ev.code, ev.code, ev.value);
break;
//mouse
case EV_REL:
printf("Input event: type=EV_REL, Axis=%d, Offset:=%d\n", ev.code, ev.value);
break;
case EV_SYN:
case EV_MSC:
break;
//analog joystick
case EV_ABS:
if (ev.code == 61) break; //ps3 accel axis
if (ev.code == 60) break; //ps3 accel axis
if (ev.code == 59) break; //ps3 accel axis
//reduce spam on PS3 gamepad
if (input[i].vid == 0x054c && input[i].pid == 0x0268)
{
if (ev.code <= 5 && ev.value > 118 && ev.value < 138) break;
}
printf("Input event: type=EV_ABS, Axis=%d, Offset:=%d\n", ev.code, ev.value);
break;
default:
printf("Input event: type=%d, code=%d(%x), value=%d(%x)\n", ev.type, ev.code, ev.code, ev.value, ev.value);
}
}
input_cb(&ev, i);
//sumulate digital directions from analog
if (ev.type == EV_ABS)
{
// some pads use axis 16 for L/R PAD, axis 17 for U/D PAD
// emulate PAD on axis 0/1
char l, r, u, d;
l = r = u = d = 0;
if(ev.code == 0 || ev.code == 16) // x
{
if ((ev.code == 0 && ev.value < 90) || (ev.code == 16 && ev.value == -1)) l = 1;
if ((ev.code == 0 && ev.value > 164) || (ev.code == 16 && ev.value == 1)) r = 1;
ev.type = EV_KEY;
if (input[i].last_l != l)
{
ev.code = KEY_EMU_LEFT;
ev.value = l;
input_cb(&ev, i);
input[i].last_l = l;
}
if (input[i].last_r != r)
{
ev.code = KEY_EMU_RIGHT;
ev.value = r;
input_cb(&ev, i);
input[i].last_r = r;
}
}
if (ev.code == 1 || ev.code == 17) // y
{
if ((ev.code == 1 && ev.value < 90) || (ev.code == 17 && ev.value == -1)) u = 1;
if ((ev.code == 1 && ev.value > 164) || (ev.code == 17 && ev.value == 1)) d = 1;
ev.type = EV_KEY;
if (input[i].last_u != u)
{
ev.code = KEY_EMU_UP;
ev.value = u;
input_cb(&ev, i);
input[i].last_u = u;
}
if (input[i].last_d != d)
{
ev.code = KEY_EMU_DOWN;
ev.value = d;
input_cb(&ev, i);
input[i].last_d = d;
}
}
}
}
}
}
}
}
if (cur_leds != leds_state)
{
cur_leds = leds_state;
for (int i = 0; i<NUMDEV; i++)
{
if (input[i].led)
{
ev.type = EV_LED;
ev.code = LED_SCROLLL;
ev.value = (cur_leds&HID_LED_SCROLL_LOCK) ? 1 : 0;
write(pool[i].fd, &ev, sizeof(struct input_event));
ev.code = LED_NUML;
ev.value = (cur_leds&HID_LED_NUM_LOCK) ? 1 : 0;
write(pool[i].fd, &ev, sizeof(struct input_event));
ev.code = LED_CAPSL;
ev.value = (cur_leds&HID_LED_CAPS_LOCK) ? 1 : 0;
write(pool[i].fd, &ev, sizeof(struct input_event));
}
}
}
}
return 0;
}

23
input.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef EVINPUT_H
#define EVINPUT_H
#define HID_LED_NUM_LOCK 1
#define HID_LED_CAPS_LOCK 2
#define HID_LED_SCROLL_LOCK 4
#define HID_LED_MASK 7
void set_kbdled(int mask, int state);
int get_kbdled(int mask);
int toggle_kbdled(int mask);
int input_poll(int getchar);
void start_map_setting();
int get_map_button();
void finish_map_setting();
uint16_t get_map_vid();
uint16_t get_map_pid();
#endif

504
keycodes.h Normal file
View File

@@ -0,0 +1,504 @@
// http://wiki.amigaos.net/index.php/Keymap_Library
// http://www.win.tue.nl/~aeb/linux/kbd/scancodes-14.html
#include "osd.h"
#define MISS 0xff
#define KEYCODE_MAX (0x6f)
// The original minimig had the keyboard connected to the FPGA. Thus all key events (even for OSD)
// came from the FPGA core. The MIST has the keyboard attached to the arm controller. To be compatible
// with the minimig core all keys (incl. OSD!) are forwarded to the FPGA and the OSD keys are returned.
// These keys are tagged with the "OSD" flag
// The atari/mist core does not forwards keys through the FPGA but queues them inside the arm controller.
// Keys flagged with "OSD_OPEN" are used to open the OSD in non-minimig. They can have a keycode which
// will be sent into the core
#define OSD 0x0100 // to be used by OSD, not the core itself
#define OSD_OPEN 0x0200 // OSD key not forwarded to core, but queued in arm controller
#define CAPS_LOCK_TOGGLE 0x0400 // caps lock toggle behaviour
#define NUM_LOCK_TOGGLE 0x0800
#define EXT 0x1000 // extended PS/2 keycode
// amiga unmapped:
// 0x5a KP-( (mapped on Keyrah)
// 0x5b KP-) (mapped on Keyrah)
// codes >= 0x69 are for OSD only and are not sent to the amiga itself
// keycode translation table
const unsigned short usb2ami[] = {
MISS, // 00: NoEvent
MISS, // 01: Overrun Error
MISS, // 02: POST fail
MISS, // 03: ErrorUndefined
0x20, // 04: a
0x35, // 05: b
0x33, // 06: c
0x22, // 07: d
0x12, // 08: e
0x23, // 09: f
0x24, // 0a: g
0x25, // 0b: h
0x17, // 0c: i
0x26, // 0d: j
0x27, // 0e: k
0x28, // 0f: l
0x37, // 10: m
0x36, // 11: n
0x18, // 12: o
0x19, // 13: p
0x10, // 14: q
0x13, // 15: r
0x21, // 16: s
0x14, // 17: t
0x16, // 18: u
0x34, // 19: v
0x11, // 1a: w
0x32, // 1b: x
0x15, // 1c: y
0x31, // 1d: z
0x01, // 1e: 1
0x02, // 1f: 2
0x03, // 20: 3
0x04, // 21: 4
0x05, // 22: 5
0x06, // 23: 6
0x07, // 24: 7
0x08, // 25: 8
0x09, // 26: 9
0x0a, // 27: 0
0x44, // 28: Return
0x45, // 29: Escape
0x41, // 2a: Backspace
0x42, // 2b: Tab
0x40, // 2c: Space
0x0b, // 2d: -
0x0c, // 2e: =
0x1a, // 2f: [
0x1b, // 30: ]
0x0d, // 31: backslash (only on us keyboards)
0x2b, // 32: Europe 1 (only on international keyboards)
0x29, // 33: ;
0x2a, // 34: '
0x00, // 35: `
0x38, // 36: ,
0x39, // 37: .
0x3a, // 38: /
0x62 | CAPS_LOCK_TOGGLE, // 39: Caps Lock
0x50, // 3a: F1
0x51, // 3b: F2
0x52, // 3c: F3
0x53, // 3d: F4
0x54, // 3e: F5
0x55, // 3f: F6
0x56, // 40: F7
0x57, // 41: F8
0x58, // 42: F9
0x59, // 43: F10
0x5f, // 44: F11
OSD_OPEN, // 45: F12 (OSD)
0x6e | OSD, // 46: Print Screen (OSD)
NUM_LOCK_TOGGLE, // 47: Scroll Lock (OSD)
0x6f | OSD, // 48: Pause
0x0d, // 49: backslash to avoid panic in Germany ;)
0x6a, // 4a: Home
0x6c | OSD, // 4b: Page Up (OSD)
0x46, // 4c: Delete
MISS, // 4d: End
0x6d | OSD, // 4e: Page Down (OSD)
0x4e, // 4f: Right Arrow
0x4f, // 50: Left Arrow
0x4d, // 51: Down Arrow
0x4c, // 52: Up Arrow
NUM_LOCK_TOGGLE, // 53: Num Lock
0x5c, // 54: KP /
0x5d, // 55: KP *
0x4a, // 56: KP -
0x5e, // 57: KP +
0x43, // 58: KP Enter
0x1d, // 59: KP 1
0x1e, // 5a: KP 2
0x1f, // 5b: KP 3
0x2d, // 5c: KP 4
0x2e, // 5d: KP 5
0x2f, // 5e: KP 6
0x3d, // 5f: KP 7
0x3e, // 60: KP 8
0x3f, // 61: KP 9
0x0f, // 62: KP 0
0x3c, // 63: KP .
0x30, // 64: Europe 2
KEY_MENU | OSD, // 65: App
MISS, // 66: Power
MISS, // 67: KP =
0x5a, // 68: KP (
0x5b, // 69: KP )
MISS, // 6a: F15
0x5f, // 6b: help (for keyrah)
NUM_LOCK_TOGGLE | 1, // 6c: F17
NUM_LOCK_TOGGLE | 2, // 6d: F18
NUM_LOCK_TOGGLE | 3, // 6e: F19
NUM_LOCK_TOGGLE | 4 // 6f: F20
};
// unmapped atari keys:
// 0x63 KP (
// 0x64 KP )
// keycode translation table for atari
const unsigned short usb2atari[] = {
MISS, // 00: NoEvent
MISS, // 01: Overrun Error
MISS, // 02: POST fail
MISS, // 03: ErrorUndefined
0x1e, // 04: a
0x30, // 05: b
0x2e, // 06: c
0x20, // 07: d
0x12, // 08: e
0x21, // 09: f
0x22, // 0a: g
0x23, // 0b: h
0x17, // 0c: i
0x24, // 0d: j
0x25, // 0e: k
0x26, // 0f: l
0x32, // 10: m
0x31, // 11: n
0x18, // 12: o
0x19, // 13: p
0x10, // 14: q
0x13, // 15: r
0x1f, // 16: s
0x14, // 17: t
0x16, // 18: u
0x2f, // 19: v
0x11, // 1a: w
0x2d, // 1b: x
0x15, // 1c: y
0x2c, // 1d: z
0x02, // 1e: 1
0x03, // 1f: 2
0x04, // 20: 3
0x05, // 21: 4
0x06, // 22: 5
0x07, // 23: 6
0x08, // 24: 7
0x09, // 25: 8
0x0a, // 26: 9
0x0b, // 27: 0
0x1c, // 28: Return
0x01, // 29: Escape
0x0e, // 2a: Backspace
0x0f, // 2b: Tab
0x39, // 2c: Space
0x0c, // 2d: -
0x0d, // 2e: =
0x1a, // 2f: [
0x1b, // 30: ]
0x29, // 31: backslash, only on us keyboard
0x29, // 32: Europe 1, only on int. keyboard
0x27, // 33: ;
0x28, // 34: '
0x2b, // 35: `
0x33, // 36: ,
0x34, // 37: .
0x35, // 38: /
0x3a | CAPS_LOCK_TOGGLE, // 39: Caps Lock
0x3b, // 3a: F1
0x3c, // 3b: F2
0x3d, // 3c: F3
0x3e, // 3d: F4
0x3f, // 3e: F5
0x40, // 3f: F6
0x41, // 40: F7
0x42, // 41: F8
0x43, // 42: F9
0x44, // 43: F10
MISS, // 44: F11
OSD_OPEN, // 45: F12
MISS, // 46: Print Screen
NUM_LOCK_TOGGLE, // 47: Scroll Lock
MISS, // 48: Pause
0x52, // 49: Insert
0x47, // 4a: Home
0x62, // 4b: Page Up
0x53, // 4c: Delete
MISS, // 4d: End
0x61, // 4e: Page Down
0x4d, // 4f: Right Arrow
0x4b, // 50: Left Arrow
0x50, // 51: Down Arrow
0x48, // 52: Up Arrow
NUM_LOCK_TOGGLE, // 53: Num Lock
0x65, // 54: KP /
0x66, // 55: KP *
0x4a, // 56: KP -
0x4e, // 57: KP +
0x72, // 58: KP Enter
0x6d, // 59: KP 1
0x6e, // 5a: KP 2
0x6f, // 5b: KP 3
0x6a, // 5c: KP 4
0x6b, // 5d: KP 5
0x6c, // 5e: KP 6
0x67, // 5f: KP 7
0x68, // 60: KP 8
0x69, // 61: KP 9
0x70, // 62: KP 0
0x71, // 63: KP .
0x60, // 64: Europe 2
OSD_OPEN, // 65: App
MISS, // 66: Power
MISS, // 67: KP =
MISS, // 68: F13
MISS, // 69: F14
MISS, // 6a: F15
0x52, // 6b: insert (for keyrah)
NUM_LOCK_TOGGLE | 1, // 6c: F17
NUM_LOCK_TOGGLE | 2, // 6d: F18
NUM_LOCK_TOGGLE | 3, // 6e: F19
NUM_LOCK_TOGGLE | 4 // 6f: F20
};
// keycode translation table for ps2 emulation
const unsigned short usb2ps2[] = {
MISS, // 00: NoEvent
MISS, // 01: Overrun Error
MISS, // 02: POST fail
MISS, // 03: ErrorUndefined
0x1c, // 04: a
0x32, // 05: b
0x21, // 06: c
0x23, // 07: d
0x24, // 08: e
0x2b, // 09: f
0x34, // 0a: g
0x33, // 0b: h
0x43, // 0c: i
0x3b, // 0d: j
0x42, // 0e: k
0x4b, // 0f: l
0x3a, // 10: m
0x31, // 11: n
0x44, // 12: o
0x4d, // 13: p
0x15, // 14: q
0x2d, // 15: r
0x1b, // 16: s
0x2c, // 17: t
0x3c, // 18: u
0x2a, // 19: v
0x1d, // 1a: w
0x22, // 1b: x
0x35, // 1c: y
0x1a, // 1d: z
0x16, // 1e: 1
0x1e, // 1f: 2
0x26, // 20: 3
0x25, // 21: 4
0x2e, // 22: 5
0x36, // 23: 6
0x3d, // 24: 7
0x3e, // 25: 8
0x46, // 26: 9
0x45, // 27: 0
0x5a, // 28: Return
0x76, // 29: Escape
0x66, // 2a: Backspace
0x0d, // 2b: Tab
0x29, // 2c: Space
0x4e, // 2d: -
0x55, // 2e: =
0x54, // 2f: [
0x5b, // 30: ]
0x5d, // 31: backslash
0x5d, // 32: Europe 1
0x4c, // 33: ;
0x52, // 34: '
0x0e, // 35: `
0x41, // 36: ,
0x49, // 37: .
0x4a, // 38: /
0x58, // 39: Caps Lock
0x05, // 3a: F1
0x06, // 3b: F2
0x04, // 3c: F3
0x0c, // 3d: F4
0x03, // 3e: F5
0x0b, // 3f: F6
0x83, // 40: F7
0x0a, // 41: F8
0x01, // 42: F9
0x09, // 43: F10
0x78, // 44: F11
OSD_OPEN | 0x07, // 45: F12 (OSD)
EXT | 0x7c, // 46: Print Screen
NUM_LOCK_TOGGLE, // 47: Scroll Lock
0x77, // 48: Pause (special key handled inside user_io)
EXT | 0x70, // 49: Insert
EXT | 0x6c, // 4a: Home
EXT | 0x7d, // 4b: Page Up
EXT | 0x71, // 4c: Delete
EXT | 0x69, // 4d: End
EXT | 0x7a, // 4e: Page Down
EXT | 0x74, // 4f: Right Arrow
EXT | 0x6b, // 50: Left Arrow
EXT | 0x72, // 51: Down Arrow
EXT | 0x75, // 52: Up Arrow
NUM_LOCK_TOGGLE, // 53: Num Lock
EXT | 0x4a, // 54: KP /
0x7c, // 55: KP *
0x7b, // 56: KP -
0x79, // 57: KP +
EXT | 0x5a, // 58: KP Enter
0x69, // 59: KP 1
0x72, // 5a: KP 2
0x7a, // 5b: KP 3
0x6b, // 5c: KP 4
0x73, // 5d: KP 5
0x74, // 5e: KP 6
0x6c, // 5f: KP 7
0x75, // 60: KP 8
0x7d, // 61: KP 9
0x70, // 62: KP 0
0x71, // 63: KP .
0x61, // 64: Europe 2
OSD_OPEN | EXT | 0x2f, // 65: App
EXT | 0x37, // 66: Power
0x0f, // 67: KP =
0x77, // 68: Num Lock
0x7e, // 69: Scroll Lock
0x18, // 6a: F15
EXT | 0x70, // 6b: insert (for keyrah)
NUM_LOCK_TOGGLE | 1, // 6c: F17
NUM_LOCK_TOGGLE | 2, // 6d: F18
NUM_LOCK_TOGGLE | 3, // 6e: F19
NUM_LOCK_TOGGLE | 4 // 6f: F20
};
// Archimedes unmapped keys
// Missing sterling
// Missing kp_hash
// Missing button_1
// Missing button_2
// Missing button_3
// Missing button_4
// Missing button_5
// keycode translation table
const unsigned short usb2archie[] = {
MISS, // 00: NoEvent
MISS, // 01: Overrun Error
MISS, // 02: POST fail
MISS, // 03: ErrorUndefined
0x3c, // 04: a
0x52, // 05: b
0x50, // 06: c
0x3e, // 07: d
0x29, // 08: e
0x3f, // 09: f
0x40, // 0a: g
0x41, // 0b: h
0x2e, // 0c: i
0x42, // 0d: j
0x43, // 0e: k
0x44, // 0f: l
0x54, // 10: m
0x53, // 11: n
0x2f, // 12: o
0x30, // 13: p
0x27, // 14: q
0x2a, // 15: r
0x3d, // 16: s
0x2b, // 17: t
0x2d, // 18: u
0x51, // 19: v
0x28, // 1a: w
0x4f, // 1b: x
0x2c, // 1c: y
0x4e, // 1d: z
0x11, // 1e: 1
0x12, // 1f: 2
0x13, // 20: 3
0x14, // 21: 4
0x15, // 22: 5
0x16, // 23: 6
0x17, // 24: 7
0x18, // 25: 8
0x19, // 26: 9
0x1a, // 27: 0
0x47, // 28: Return
0x00, // 29: Escape
0x1e, // 2a: Backspace
0x26, // 2b: Tab
0x5f, // 2c: Space
0x1b, // 2d: -
0x1c, // 2e: =
0x31, // 2f: [
0x32, // 30: ]
0x33, // 31: backslash (only on us keyboards)
0x33, // 32: Europe 1 (only on international kbds)
0x45, // 33: ;
0x46, // 34: '
0x10, // 35: `
0x55, // 36: ,
0x56, // 37: .
0x57, // 38: /
0x5d, // 39: Caps Lock
0x01, // 3a: F1
0x02, // 3b: F2
0x03, // 3c: F3
0x04, // 3d: F4
0x05, // 3e: F5
0x06, // 3f: F6
0x07, // 40: F7
0x08, // 41: F8
0x09, // 42: F9
0x0a, // 43: F10
0x0b, // 44: F11
0x0c, // 45: F12 - Used heavily by the archie... OSD moved to printscreen.
// 0x0d, // 46: Print Screen
OSD_OPEN, // 46: Print Screen
0x0e, // 47: Scroll Lock
0x0f, // 48: Pause
0x1f, // 49: Insert
0x20, // 4a: Home
0x21, // 4b: Page Up
0x34, // 4c: Delete
0x35, // 4d: End
0x36, // 4e: Page Down
0x64, // 4f: Right Arrow
0x62, // 50: Left Arrow
0x63, // 51: Down Arrow
0x59, // 52: Up Arrow
0x22, // 53: Num Lock
0x23, // 54: KP /
0x24, // 55: KP *
0x3a, // 56: KP -
0x4b, // 57: KP +
0x67, // 58: KP Enter
0x5a, // 59: KP 1
0x5b, // 5a: KP 2
0x5c, // 5b: KP 3
0x48, // 5c: KP 4
0x49, // 5d: KP 5
0x4a, // 5e: KP 6
0x37, // 5f: KP 7
0x38, // 60: KP 8
0x39, // 61: KP 9
0x65, // 62: KP 0
0x66, // 63: KP decimal
MISS, // 64: Europe 2
0x72, // 65: App (maps to middle mouse button)
MISS, // 66: Power
MISS, // 67: KP =
MISS, // 68: F13
MISS, // 69: F14
MISS, // 6a: F15
0x1f, // 6b: insert (for keyrah)
MISS, // 6c: F17
MISS, // 6d: F18
MISS, // 6e: F19
MISS, // 6f: F20
};

143
logo.h Normal file
View File

@@ -0,0 +1,143 @@
#ifndef LOGO_H
#define LOGO_H
const unsigned char logodata[5][227] = {
{ 0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,
0x00,0x80,0xE0,0xE0,0xF0,0xF0,0xF8,0xF8,0xF8,0xFC,0xFC,0xFC,0xFC,0xF8,0xF8,0xF8,0xF0,0xF0,0xE0,
0xE0,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x00 },
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x40,
0x40,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,
0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x40,
0x40,0x41,0xC3,0xC7,0xC7,0xCF,0xCF,0xCF,0xCF,0xDF,0xDF,0xDF,0xDF,0xCF,0xCF,0xCF,0xCF,0xC7,0xC7,
0x43,0x41,0x40,0x40,0x40,0x40,0x00,0x80,0x80,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xE0,
0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0x00,0x00,0xC0,
0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,
0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
0xC0,0xE0,0xF8,0x3E,0x1F,0x0F,0x07,0x1F,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xF8,
0xE0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0xC0,0xC0,0x60,0x70,
0x30,0x38,0x1C,0x1C,0x8C,0xF6,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x1F,
0x0F,0x03,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
0xE0,0xF0,0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x3F,0x0F,0x07,0x03,0x01,0x00,0x00,
0x00,0x00,0x00,0x7E,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE7,0xE7,
0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0x83,0x83,0x83,0x83,0x83,0x87,0x07,0x07,0x07,0x00,0x00,0x07,
0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0xF0,0xFC,0x3E,0x1F,
0x07,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFE,0x70,0x60,0x30,0x38,0x38,0x1C,0x0C,0x0E,0x07,0x07,0x03,0x01,0x01,0x00,0x80,0xE0,
0xF0,0xF8,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x3F,0x0F,0x07,0x03,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xF0,0xF8,0xFC,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x3F,0x1F,0x07,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xC0,0xC1,0xC1,0xC3,0xC3,0xC3,0xC3,0xC7,0xC7,0xC7,0x87,0x87,0x8F,0x8F,0x8F,
0xCF,0xDF,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFE,0xFC,0x70,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x0C,0x0C,0x0C,0x0E,0x0F,0x0F,0x0F,0x0F,0x0C,0x0C,0x08,
0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0F,0x0F,0x07,0x07,0x03,0x03,
0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x0C,0x0C,0x0C,0x0E,0x0F,0x0F,
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0D,0x0C,0x08,0x08,0x08,0x08,0x08,0x00,0x00,
0x00,0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x0C,0x0C,0x0C,0x0E,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0C,0x08,0x08,0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x07,0x07,0x07,0x07,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,
0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x07,0x07,0x07,0x03,0x03,0x03,0x03,0x01,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,
0x0F,0x0F,0x0F,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }
};
#endif
/* -- original MINIMIG logo, we keep only one to save some space on the firmware binary
const unsigned char logodata_minimig[5][227] = {
{0xFF, 0x00, 0x80, 0xc0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xc0, 0xc0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xc0,
0xc0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x83, 0x83, 0x83, 0x83, 0x83,
0x83, 0x83, 0x83, 0x83, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x83, 0x83, 0x83, 0x83,
0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x83,
0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x03, 0x03, 0x03, 0x03,
0x03, 0x00, 0x00, 0x80, 0x83, 0x87, 0x87, 0x8f, 0x8f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x0f,
0x0f, 0x07, 0x07, 0x03, 0x00, 0x00, 0x00, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x83, 0x83,
0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x03, 0x03, 0x03, 0x83,
0x83, 0x03, 0x03},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xf0,
0x7c, 0x1e, 0x07, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xc0, 0x00, 0x00, 0x00, 0x80,
0x80, 0xc0, 0x60, 0x20, 0x30, 0x18, 0x8c, 0xc4, 0xf6, 0xfb, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x1f,
0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xe0, 0xf0, 0xfc, 0xff, 0xff, 0xff,
0xff, 0x3f, 0x1f, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xe0, 0xf0, 0x3c,
0x0e, 0x07, 0x03, 0x07, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80,
0x00, 0x00, 0x00, 0xc0, 0xf0, 0x38, 0x1e, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0xe0, 0xf0, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x1f, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x80, 0xc0, 0xf0, 0x7c, 0x1e, 0x07, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xfc, 0xc0, 0x00, 0x00, 0x00, 0x80, 0x80, 0xc0, 0x60, 0x20, 0x30, 0x18, 0x8c, 0xc4, 0xf6, 0xfb,
0xff, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0xc0, 0xf0, 0xf8, 0xfe, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0xe0, 0xf0, 0xf8, 0xfc, 0xfc, 0xfe, 0xfe, 0xfe, 0x3e, 0x1f, 0x0f, 0x07, 0x07, 0x03, 0x03, 0x01,
0x01, 0x41, 0x41, 0x40, 0x40, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, 0xc3, 0x47, 0x47, 0x43,
0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x30, 0x30, 0x38, 0x3e, 0x2f, 0x23, 0x20,
0x20, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3f, 0x3f, 0x1f, 0x1f, 0x0f, 0x07, 0x06, 0x03, 0x21, 0x21,
0x20, 0x20, 0x20, 0x30, 0x38, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x27, 0x21, 0x20, 0x00,
0x00, 0x20, 0x20, 0x20, 0x20, 0x30, 0x38, 0x3c, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x27, 0x21,
0x20, 0x20, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x30, 0x38, 0x3c, 0x3f, 0x27, 0x21, 0x20, 0x20,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f,
0x3f, 0x1e, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x30, 0x38, 0x3c, 0x3f,
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x27, 0x21, 0x20, 0x20, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x30,
0x30, 0x38, 0x3e, 0x2f, 0x23, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3f, 0x3f, 0x1f, 0x1f,
0x0f, 0x07, 0x06, 0x03, 0x21, 0x21, 0x20, 0x20, 0x20, 0x30, 0x38, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f,
0x3f, 0x3f, 0x27, 0x21, 0x20, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x38, 0x3e, 0x3f,
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x27, 0x21, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x07, 0x0f, 0x0f, 0x1f, 0x1f, 0x1f, 0x3f, 0x38, 0x38, 0x30, 0x30, 0x20, 0x20, 0x20, 0x20,
0x20, 0x30, 0x38, 0x3c, 0x3f, 0x3f, 0x3f, 0x1f, 0x1f, 0x0f, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18}
};*/

172
main.c Normal file
View File

@@ -0,0 +1,172 @@
/*
Copyright 2005, 2006, 2007 Dennis van Weeren
Copyright 2008, 2009 Jakub Bednarski
Copyright 2012 Till Harbaum
This file is part of Minimig
Minimig 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.
Minimig 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 <unistd.h>
#include <stdio.h>
#include "string.h"
#include "errors.h"
#include "hardware.h"
#include "file_io.h"
#include "osd.h"
#include "fdd.h"
#include "hdd.h"
#include "menu.h"
#include "user_io.h"
#include "tos.h"
#include "debug.h"
#include "mist_cfg.h"
#include "input.h"
#include "fpga_io.h"
#include "boot.h"
const char version[] = { "$VER:HPS" VDATE };
unsigned char Error;
void FatalError(unsigned long error)
{
unsigned long i;
iprintf("Fatal error: %lu\n", error);
while (1)
{
for (i = 0; i < error; i++)
{
DISKLED_ON;
WaitTimer(250);
DISKLED_OFF;
WaitTimer(250);
}
WaitTimer(1000);
}
}
void HandleDisk(void)
{
unsigned char c1, c2;
EnableFpga();
uint16_t tmp = spi_w(0);
c1 = (uint8_t)(tmp>>8); // cmd request and drive number
c2 = (uint8_t)tmp; // track number
spi_w(0);
spi_w(0);
DisableFpga();
HandleFDD(c1, c2);
HandleHDD(c1, c2);
UpdateDriveStatus();
}
void core_init()
{
user_io_detect_core_type();
mist_ini_parse();
user_io_send_buttons(1);
if (user_io_core_type() == CORE_TYPE_MINIMIG2)
{
BootInit();
} // end of minimig setup
if (user_io_core_type() == CORE_TYPE_MIST)
{
puts("Running mist setup");
tos_upload(NULL);
// end of mist setup
}
if (user_io_core_type() == CORE_TYPE_ARCHIE)
{
puts("Running archimedes setup");
} // end of archimedes setup
}
int main(int argc, char *argv[])
{
uint8_t mmc_ok = 0;
fpga_io_init();
DISKLED_OFF;
iprintf("\nMinimig by Dennis van Weeren");
iprintf("\nARM Controller by Jakub Bednarski\n\n");
iprintf("Version %s\n\n", version + 5);
FindStorage();
user_io_init();
tos_config_init();
core_init();
while(1)
{
//printf("fpga_ready:%d\n", fpga_ready());
if(!fpga_ready())
{
printf("FPGA is not ready. JTAG uploading?\n");
printf("Waiting for FPGA to be ready...\n");
//reset GPO to default value
fpga_gpo_write(0);
while (!fpga_ready())
{
sleep(1);
}
reboot(0);
}
user_io_poll();
input_poll(0);
switch (user_io_core_type())
{
// MIST (atari) core supports the same UI as Minimig
case CORE_TYPE_MIST:
HandleUI();
break;
// call original minimig handlers if minimig core is found
case CORE_TYPE_MINIMIG2:
HandleDisk();
HandleUI();
break;
// 8 bit cores can also have a ui if a valid config string can be read from it
case CORE_TYPE_8BIT:
if(user_io_is_8bit_with_config_string()) HandleUI();
break;
// Archie core will get its own treatment one day ...
case CORE_TYPE_ARCHIE:
HandleUI();
break;
}
}
return 0;
}

3621
menu.c Normal file

File diff suppressed because it is too large Load Diff

125
menu.h Normal file
View File

@@ -0,0 +1,125 @@
#ifndef MENU_H
#define MENU_H
#include "fdd.h" // for adfTYPE definition
/*menu states*/
enum MENU
{
MENU_NONE1,
MENU_NONE2,
MENU_MAIN1,
MENU_MAIN2,
MENU_FILE_SELECT1,
MENU_FILE_SELECT2,
MENU_FILE_SELECTED,
MENU_RESET1,
MENU_RESET2,
MENU_RECONF1,
MENU_RECONF2,
MENU_SETTINGS1,
MENU_SETTINGS2,
MENU_ROMFILE_SELECTED,
MENU_ROMFILE_SELECTED1,
MENU_ROMFILE_SELECTED2,
MENU_SETTINGS_VIDEO1,
MENU_SETTINGS_VIDEO2,
MENU_SETTINGS_MEMORY1,
MENU_SETTINGS_MEMORY2,
MENU_SETTINGS_CHIPSET1,
MENU_SETTINGS_CHIPSET2,
MENU_SETTINGS_DRIVES1,
MENU_SETTINGS_DRIVES2,
MENU_SETTINGS_HARDFILE1,
MENU_SETTINGS_HARDFILE2,
MENU_HARDFILE_SELECT1,
MENU_HARDFILE_SELECT2,
MENU_HARDFILE_SELECTED,
MENU_HARDFILE_EXIT,
MENU_HARDFILE_CHANGED1,
MENU_HARDFILE_CHANGED2,
MENU_SYNTHRDB1,
MENU_SYNTHRDB2,
MENU_SYNTHRDB2_1,
MENU_SYNTHRDB2_2,
MENU_LOADCONFIG_1,
MENU_LOADCONFIG_2,
MENU_SAVECONFIG_1,
MENU_SAVECONFIG_2,
MENU_FIRMWARE1,
MENU_FIRMWARE2,
MENU_FIRMWARE_CORE_FILE_SELECTED,
MENU_ERROR,
MENU_INFO,
MENU_STORAGE,
MENU_JOYDIGMAP,
MENU_JOYDIGMAP1,
// Mist/atari specific pages
MENU_MIST_MAIN1,
MENU_MIST_MAIN2,
MENU_MIST_MAIN_FILE_SELECTED,
MENU_MIST_STORAGE1,
MENU_MIST_STORAGE2,
MENU_MIST_STORAGE_FILE_SELECTED,
MENU_MIST_SYSTEM1,
MENU_MIST_SYSTEM2,
MENU_MIST_SYSTEM_FILE_SELECTED,
MENU_MIST_VIDEO1,
MENU_MIST_VIDEO2,
MENU_MIST_VIDEO_ADJUST1,
MENU_MIST_VIDEO_ADJUST2,
// archimedes menu entries
MENU_ARCHIE_MAIN1,
MENU_ARCHIE_MAIN2,
MENU_ARCHIE_MAIN_FILE_SELECTED,
// 8bit menu entries
MENU_8BIT_MAIN1,
MENU_8BIT_MAIN2,
MENU_8BIT_MAIN_FILE_SELECTED,
MENU_8BIT_MAIN_IMAGE_SELECTED,
MENU_8BIT_SYSTEM1,
MENU_8BIT_SYSTEM2,
MENU_8BIT_ABOUT1,
MENU_8BIT_ABOUT2,
MENU_8BIT_CONTROLLERS1,
MENU_8BIT_CONTROLLERS2,
MENU_8BIT_JOYTEST_A1,
MENU_8BIT_JOYTEST_A2,
MENU_8BIT_JOYTEST_B1,
MENU_8BIT_JOYTEST_B2,
MENU_8BIT_KEYTEST1,
MENU_8BIT_KEYTEST2,
MENU_8BIT_USB1,
MENU_8BIT_USB2,
MENU_8BIT_TURBO1,
MENU_8BIT_TURBO2,
MENU_8BIT_CHRTEST1,
MENU_8BIT_CHRTEST2
};
// UI strings, used by boot messages
extern const char *config_filter_msg[];
extern const char *config_memory_chip_msg[];
extern const char *config_memory_slow_msg[];
extern const char *config_memory_fast_msg[];
extern const char *config_scanline_msg[];
extern const char *config_cpu_msg[];
extern const char *config_hdf_msg[];
extern const char *config_chipset_msg[];
void InsertFloppy(adfTYPE *drive, char* path);
void HandleUI(void);
void PrintDirectory(void);
void ScrollLongName(void);
void InfoMessage(char *message);
void ShowSplash();
void HideSplash();
void EjectAllFloppies();
#endif

44
mist_cfg.c Normal file
View File

@@ -0,0 +1,44 @@
// mist_cfg.c
// 2015, rok.krajnc@gmail.com
#include <string.h>
#include "ini_parser.h"
#include "mist_cfg.h"
#include "user_io.h"
void mist_ini_parse()
{
memset(&mist_cfg, 0, sizeof(mist_cfg));
ini_parse(&mist_ini_cfg);
}
mist_cfg_t mist_cfg = { 0 };
// mist ini sections
const ini_section_t mist_ini_sections[] =
{
{ 1, "MiSTer" }
};
// mist ini vars
const ini_var_t mist_ini_vars[] = {
{ "YPBPR", (void*)(&(mist_cfg.ypbpr)), UINT8, 0, 1, 1 },
{ "COMPOSITE_SYNC", (void*)(&(mist_cfg.csync)), UINT8, 0, 1, 1 },
{ "FORCED_SCANDOUBLER", (void*)(&(mist_cfg.forced_scandoubler)), UINT8, 0, 1, 1 },
{ "VGA_SCALER", (void*)(&(mist_cfg.vga_scaler)), UINT8, 0, 1, 1 },
{ "KEYRAH_MODE", (void*)(&(mist_cfg.keyrah_mode)), UINT32, 0, 0xFFFFFFFF, 1 },
{ "RESET_COMBO", (void*)(&(mist_cfg.reset_combo)), UINT8, 0, 2, 1 },
{ "KEY_MENU_AS_RGUI", (void*)(&(mist_cfg.key_menu_as_rgui)), UINT8, 0, 1, 1 },
{ "KEY_REMAP", (void*)user_io_key_remap, CUSTOM_HANDLER, 0, 0, 1 },
{ "VIDEO_MODE", (void*)(&(mist_cfg.video_mode)), UINT8, 0, 9, 1 },
};
// mist ini config
const ini_cfg_t mist_ini_cfg = {
"MiSTer.ini",
mist_ini_sections,
mist_ini_vars,
(int)(sizeof(mist_ini_sections) / sizeof(ini_section_t)),
(int)(sizeof(mist_ini_vars) / sizeof(ini_var_t))
};

37
mist_cfg.h Normal file
View File

@@ -0,0 +1,37 @@
// mist_cfg.h
// 2015, rok.krajnc@gmail.com
#ifndef __MIST_CFG_H__
#define __MIST_CFG_H__
//// includes ////
#include <inttypes.h>
#include "ini_parser.h"
//// type definitions ////
typedef struct {
uint32_t keyrah_mode;
uint8_t forced_scandoubler;
uint8_t key_menu_as_rgui;
uint8_t reset_combo;
uint8_t ypbpr;
uint8_t csync;
uint8_t vga_scaler;
uint8_t video_mode;
} mist_cfg_t;
//// functions ////
void mist_ini_parse();
//// global variables ////
extern const ini_cfg_t mist_ini_cfg;
extern mist_cfg_t mist_cfg;
#endif // __MIST_CFG_H__

700
osd.c Normal file
View File

@@ -0,0 +1,700 @@
/*
Copyright 2005, 2006, 2007 Dennis van Weeren
Copyright 2008, 2009 Jakub Bednarski
This file is part of Minimig
Minimig 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.
Minimig 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/>.
This is the Minimig OSD (on-screen-display) handler.
2012-02-09 - Split character rom out to separate header file, with upper 128 entries
as rotated copies of the first 128 entries. -- AMR
29-12-2006 - created
30-12-2006 - improved and simplified
-- JB --
2008-10-04 - ARM version
2008-10-26 - added cpu and floppy configuration functions
2008-12-31 - added enable HDD command
2009-02-03 - full keyboard support
2009-06-23 - hires OSD display
2009-08-23 - adapted ConfigIDE() - support for 2 hardfiles
*/
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "osd.h"
#include "spi.h"
#include "charrom.h"
#include "logo.h"
#include "user_io.h"
#include "hardware.h"
// conversion table of Amiga keyboard scan codes to ASCII codes
const char keycode_table[128] =
{
0,'1','2','3','4','5','6','7','8','9','0', 0, 0, 0, 0, 0,
'Q','W','E','R','T','Y','U','I','O','P', 0, 0, 0, 0, 0, 0,
'A','S','D','F','G','H','J','K','L', 0, 0, 0, 0, 0, 0, 0,
0,'Z','X','C','V','B','N','M', 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static int osd_size = 8;
void OsdSetSize(int n)
{
osd_size = n;
}
int OsdGetSize()
{
return osd_size;
}
struct star
{
int x, y;
int dx, dy;
};
struct star stars[64];
char framebuffer[16][256];
void framebuffer_clear()
{
memset(framebuffer, 0, sizeof(framebuffer));
}
void framebuffer_plot(int x, int y)
{
y = (y * osd_size) / 8;
framebuffer[y / 8][x] |= (1 << (y & 7));
}
void StarsInit()
{
srand(time(NULL));
for (int i = 0; i<64; ++i)
{
stars[i].x = (rand() % 228) << 4; // X centre
stars[i].y = (rand() % 56) << 4; // Y centre
stars[i].dx = -(rand() & 7) - 3;
stars[i].dy = 0;
}
}
void StarsUpdate()
{
framebuffer_clear();
for (int i = 0; i<64; ++i)
{
stars[i].x += stars[i].dx;
stars[i].y += stars[i].dy;
if ((stars[i].x<0) || (stars[i].x>(228 << 4)) ||
(stars[i].y<0) || (stars[i].y>(56 << 4)))
{
stars[i].x = 228 << 4;
stars[i].y = (rand() % 56) << 4;
stars[i].dx = -(rand() & 7) - 3;
stars[i].dy = 0;
}
framebuffer_plot(stars[i].x >> 4, stars[i].y >> 4);
}
}
// time delay after which file/dir name starts to scroll
#define SCROLL_DELAY 1000
#define SCROLL_DELAY2 10
#define SCROLL_DELAY3 50
static unsigned long scroll_offset = 0; // file/dir name scrolling position
static unsigned long scroll_timer = 0; // file/dir name scrolling timer
static int arrow;
static unsigned char titlebuffer[128];
static void rotatechar(unsigned char *in, unsigned char *out)
{
int a;
int b;
int c;
for (b = 0; b<8; ++b)
{
a = 0;
for (c = 0; c<8; ++c)
{
a <<= 1;
a |= (in[c] >> b) & 1;
}
out[b] = a;
}
}
#define OSDHEIGHT (osd_size*8)
void OsdSetTitle(char *s, int a)
{
// Compose the title, condensing character gaps
arrow = a;
int zeros = 0;
int i = 0, j = 0;
int outp = 0;
while (1)
{
int c = s[i++];
if (c && (outp<OSDHEIGHT))
{
unsigned char *p = &charfont[c][0];
for (j = 0; j<8; ++j)
{
unsigned char nc = *p++;
if (nc)
{
zeros = 0;
titlebuffer[outp++] = nc;
}
else if (zeros == 0)
{
titlebuffer[outp++] = 0;
zeros = 1;
}
if (outp>63)
break;
}
}
else
break;
}
for (i = outp; i<OSDHEIGHT; i++)
{
titlebuffer[i] = 0;
}
// Now centre it:
int c = (OSDHEIGHT - 1 - outp) / 2;
memmove(titlebuffer + c, titlebuffer, outp);
for (i = 0; i<c; ++i)
titlebuffer[i] = 0;
// Finally rotate it.
for (i = 0; i<OSDHEIGHT; i += 8)
{
unsigned char tmp[8];
rotatechar(&titlebuffer[i], tmp);
for (c = 0; c<8; ++c)
{
titlebuffer[i + c] = tmp[c];
}
}
}
void OsdWrite(unsigned char n, char *s, unsigned char invert, unsigned char stipple)
{
OsdWriteOffset(n, s, invert, stipple, 0, 0);
}
// write a null-terminated string <s> to the OSD buffer starting at line <n>
void OsdWriteOffset(unsigned char n, char *s, unsigned char invert, unsigned char stipple, char offset, char leftchar)
{
//printf("OsdWriteOffset(%d)\n", n);
unsigned short i;
unsigned char b;
const unsigned char *p;
unsigned char stipplemask = 0xff;
int linelimit = OSDLINELEN;
int arrowmask = arrow;
if (n == (osd_size-1) && (arrow & OSD_ARROW_RIGHT))
linelimit -= 22;
if (stipple) {
stipplemask = 0x55;
stipple = 0xff;
}
else
stipple = 0;
// select buffer and line to write to
if (!is_minimig())
spi_osd_cmd_cont(MM1_OSDCMDWRITE | n);
else
spi_osd_cmd32_cont(OSD_CMD_OSD_WR, n);
if (invert) invert = 255;
i = 0;
// send all characters in string to OSD
while (1)
{
if (i == 0)
{ // Render sidestripe
unsigned char j;
unsigned char tmp[8];
if (leftchar)
{
unsigned char tmp2[8];
memcpy(tmp2, charfont[leftchar], 8);
rotatechar(tmp2, tmp);
p = tmp;
}
else
{
p = &titlebuffer[(osd_size - 1 - n) * 8];
}
spi16(0xffff); // left white border
for (j = 0; j < 8; j++)
spi_n(255 ^ *p++, 2);
spi16(0xffff); // right white border
spi16(0x0000); // blue gap
i += 22;
}
else if (n == (osd_size-1) && (arrowmask & OSD_ARROW_LEFT)) { // Draw initial arrow
unsigned char b;
spi24(0x00);
p = &charfont[0x10][0];
for (b = 0; b<8; b++) spi8(*p++ << offset);
p = &charfont[0x14][0];
for (b = 0; b<8; b++) spi8(*p++ << offset);
spi24(0x00);
spi_n(invert, 2);
i += 24;
arrowmask &= ~OSD_ARROW_LEFT;
if (*s++ == 0) break; // Skip 3 characters, to keep alignent the same.
if (*s++ == 0) break;
if (*s++ == 0) break;
}
else {
b = *s++;
if (b == 0) // end of string
break;
else if (b == 0x0d || b == 0x0a) { // cariage return / linefeed, go to next line
// increment line counter
if (++n >= linelimit)
n = 0;
// send new line number to OSD
DisableOsd();
if (!is_minimig())
spi_osd_cmd_cont(MM1_OSDCMDWRITE | n);
else
spi_osd_cmd32_cont(OSD_CMD_OSD_WR, n);
}
else if (i<(linelimit - 8)) { // normal character
unsigned char c;
p = &charfont[b][0];
for (c = 0; c<8; c++) {
spi8(((*p++ << offset)&stipplemask) ^ invert);
stipplemask ^= stipple;
}
i += 8;
}
}
}
for (; i < linelimit; i++) // clear end of line
{
spi8(invert);
}
if (n == (osd_size-1) && (arrowmask & OSD_ARROW_RIGHT))
{ // Draw final arrow if needed
unsigned char c;
spi24(0x00);
p = &charfont[0x15][0];
for (c = 0; c<8; c++) spi8(*p++ << offset);
p = &charfont[0x11][0];
for (c = 0; c<8; c++) spi8(*p++ << offset);
spi24(0x00);
i += 22;
}
// deselect OSD SPI device
DisableOsd();
}
void OsdDrawLogo(unsigned char n, char row, char superimpose)
{
unsigned short i;
const unsigned char *p;
int linelimit = OSDLINELEN;
int mag = (osd_size / 8);
n = n * mag;
// select buffer and line to write to
if (!is_minimig())
{
spi_osd_cmd_cont(MM1_OSDCMDWRITE | n);
}
else
{
spi_osd_cmd32_cont(OSD_CMD_OSD_WR, n);
}
for (int k = 0; k < mag; k++)
{
unsigned char bt = 0;
const unsigned char *lp = logodata[row];
int bytes = sizeof(logodata[0]);
if (row >= (sizeof(logodata) / sizeof(logodata[0]))) lp = 0;
char *bg = framebuffer[n + k];
i = 0;
while(i < linelimit)
{
if (i == 0)
{
unsigned char j;
p = &titlebuffer[(osd_size - 1 - n - k) * 8];
spi16(0xffff); // left white border
for (j = 0; j<8; j++) spi_n(255 ^ *p++, 2);
spi16(0xffff); // right white border
spi16(0x0000); // blue gap
i += 22;
}
if(lp && bytes)
{
bt = *lp++;
if(mag > 1)
{
if (k) bt >>= 4;
bt = (bt & 1) | ((bt & 1) << 1) | ((bt & 2) << 1) | ((bt & 2) << 2) | ((bt & 4) << 2) | ((bt & 4) << 3) | ((bt & 8) << 3) | ((bt & 8) << 4);
}
bytes--;
}
spi8(bt | *bg++);
++i;
}
}
// deselect OSD SPI device
DisableOsd();
}
// write a null-terminated string <s> to the OSD buffer starting at line <n>
void OSD_PrintText(unsigned char line, char *text, unsigned long start, unsigned long width, unsigned long offset, unsigned char invert)
{
// line : OSD line number (0-7)
// text : pointer to null-terminated string
// start : start position (in pixels)
// width : printed text length in pixels
// offset : scroll offset in pixels counting from the start of the string (0-7)
// invert : invertion flag
const unsigned char *p;
int i, j;
// select buffer and line to write to
if (!is_minimig())
spi_osd_cmd_cont(MM1_OSDCMDWRITE | line);
else
spi_osd_cmd32_cont(OSD_CMD_OSD_WR, line);
if (invert)
invert = 0xff;
p = &titlebuffer[(osd_size - 1 - line) * 8];
if (start>2) {
spi16(0xffff);
start -= 2;
}
i = start>16 ? 16 : start;
for (j = 0; j<(i / 2); ++j)
spi_n(255 ^ *p++, 2);
if (i & 1)
spi8(255 ^ *p);
start -= i;
if (start>2) {
spi16(0xffff);
start -= 2;
}
while (start--)
spi8(0x00);
if (offset) {
width -= 8 - offset;
p = &charfont[*text++][offset];
for (; offset < 8; offset++)
spi8(*p++^invert);
}
while (width > 8) {
unsigned char b;
p = &charfont[*text++][0];
for (b = 0; b<8; b++) spi8(*p++^invert);
width -= 8;
}
if (width) {
p = &charfont[*text++][0];
while (width--)
spi8(*p++^invert);
}
DisableOsd();
}
// clear OSD frame buffer
void OsdClear(void)
{
// select buffer to write to
if (!is_minimig())
spi_osd_cmd_cont(MM1_OSDCMDWRITE | 0x18);
else
spi_osd_cmd32_cont(OSD_CMD_OSD_WR, 0x18);
// clear buffer
spi_n(0x00, OSDLINELEN * OSDNLINE);
// deselect OSD SPI device
DisableOsd();
}
// enable displaying of OSD
void OsdEnable(unsigned char mode)
{
user_io_osd_key_enable(mode & DISABLE_KEYBOARD);
if (!is_minimig())
spi_osd_cmd(MM1_OSDCMDENABLE | (mode & DISABLE_KEYBOARD));
else
spi_osd_cmd8(OSD_CMD_OSD, 0x01 | (mode & DISABLE_KEYBOARD));
}
// disable displaying of OSD
void OsdDisable(void)
{
user_io_osd_key_enable(0);
if (!is_minimig())
spi_osd_cmd(MM1_OSDCMDDISABLE);
else
spi_osd_cmd8(OSD_CMD_OSD, 0x00);
}
void OsdReset(unsigned char boot)
{
spi_osd_cmd8(OSD_CMD_RST, 0x01);
spi_osd_cmd8(OSD_CMD_RST, 0x00);
}
void MM1_ConfigFilter(unsigned char lores, unsigned char hires) {
spi_osd_cmd(MM1_OSDCMDCFGFLT | ((hires & 0x03) << 2) | (lores & 0x03));
}
void ConfigVideo(unsigned char hires, unsigned char lores, unsigned char scanlines)
{
spi_osd_cmd16(OSD_CMD_VID, (((scanlines >> 6) & 0x03) << 10) | (((scanlines >> 4) & 0x03) << 8) | (((scanlines >> 2) & 0x03) << 6) | ((hires & 0x03) << 4) | ((lores & 0x03) << 2) | (scanlines & 0x03));
}
void ConfigMemory(unsigned char memory)
{
spi_osd_cmd8(OSD_CMD_MEM, memory);
}
void ConfigCPU(unsigned char cpu)
{
spi_osd_cmd8(OSD_CMD_CPU, cpu & 0x0f);
}
void ConfigChipset(unsigned char chipset)
{
spi_osd_cmd8(OSD_CMD_CHIP, chipset & 0x1f);
}
void ConfigFloppy(unsigned char drives, unsigned char speed)
{
spi_osd_cmd8(OSD_CMD_FLP, ((drives & 0x03) << 2) | (speed & 0x03));
}
void MM1_ConfigScanlines(unsigned char scanlines)
{
spi_osd_cmd(MM1_OSDCMDCFGSCL | (scanlines & 0x0F));
}
void ConfigIDE(unsigned char gayle, unsigned char master, unsigned char slave)
{
spi_osd_cmd8(OSD_CMD_HDD, (slave ? 4 : 0) | (master ? 2 : 0) | (gayle ? 1 : 0));
}
void ConfigAutofire(unsigned char autofire)
{
spi_osd_cmd8(OSD_CMD_JOY, autofire & 0x07);
}
static unsigned char disable_menu = 0;
// get key status
unsigned char OsdGetCtrl(void)
{
static unsigned char c2;
static unsigned long delay;
static unsigned long repeat;
static unsigned char repeat2;
unsigned char c1, c;
c1 = OsdKeyGet();
// OsdKeyGet permanently returns the last key event.
// generate normal "key-pressed" event
c = 0;
if (c1 != c2)
c = c1;
c2 = c1;
// inject a fake "MENU_KEY" if no menu is visible and the menu key is loaded
if (!user_io_osd_is_visible() && is_menu_core())
c = KEY_MENU;
// generate repeat "key-pressed" events
if ((c1 & KEY_UPSTROKE) || (!c1))
repeat = GetTimer(REPEATDELAY);
else if (CheckTimer(repeat)) {
repeat = GetTimer(REPEATRATE);
if (c1 == KEY_UP || c1 == KEY_DOWN)
c = c1;
repeat2++;
if (repeat2 == 2)
{
repeat2 = 0;
if (c1 == KEY_PGUP || c1 == KEY_PGDN || c1 == KEY_LEFT || c1 == KEY_RIGHT || GetASCIIKey(c1))
c = c1;
}
}
// currently no key pressed
if (!c)
{
static unsigned char last_but = 0;
if (!disable_menu)
{
unsigned char but = user_io_menu_button();
if (!but && last_but) c = KEY_MENU;
last_but = but;
}
else
{
last_but = 0;
}
}
return(c);
}
void OsdDisableMenuButton(unsigned char disable)
{
disable_menu = disable;
}
unsigned char GetASCIIKey(unsigned char keycode)
{
if (keycode & KEY_UPSTROKE)
return 0;
return keycode_table[keycode & 0x7F];
}
void ScrollText(char n, const char *str, int off, int len, int max_len, unsigned char invert)
{
// this function is called periodically when a string longer than the window is displayed.
#define BLANKSPACE 10 // number of spaces between the end and start of repeated name
char s[40];
long offset;
if (!max_len) max_len = 30;
if (str && str[0] && CheckTimer(scroll_timer)) // scroll if long name and timer delay elapsed
{
scroll_timer = GetTimer(SCROLL_DELAY2); // reset scroll timer to repeat delay
scroll_offset++; // increase scroll position (1 pixel unit)
memset(s, ' ', 32); // clear buffer
if (!len) len = strlen(str); // get name length
if (off+len > max_len) // scroll name if longer than display size
{
// reset scroll position if it exceeds predefined maximum
if (scroll_offset >= (len + BLANKSPACE) << 3) scroll_offset = 0;
offset = scroll_offset >> 3; // get new starting character of the name (scroll_offset is no longer in 2 pixel unit)
len -= offset; // remaining number of characters in the name
if (len>max_len) len = max_len;
if (len > 0) strncpy(s, &str[offset], len); // copy name substring
if (len < max_len - BLANKSPACE) // file name substring and blank space is shorter than display line size
{
strncpy(s + len + BLANKSPACE, str, max_len - len - BLANKSPACE); // repeat the name after its end and predefined number of blank space
}
OSD_PrintText(n, s, 22, (max_len - 1) << 3, (scroll_offset & 0x7), invert); // OSD print function with pixel precision
}
}
}
void ScrollReset()
{
scroll_timer = GetTimer(SCROLL_DELAY); // set timer to start name scrolling after predefined time delay
scroll_offset = 0; // start scrolling from the start
}
/* the Atari core handles OSD keys competely inside the core */
static unsigned char osd_key;
void OsdKeySet(unsigned char c) {
// iprintf("OSD enqueue: %x\n", c);
osd_key = c;
}
unsigned char OsdKeyGet() {
return osd_key;
}
/* core currently loaded */
static char lastcorename[261 + 10] = "CORE";
void OsdCoreNameSet(const char* str) {
siprintf(lastcorename, "%s", str);
}
char* OsdCoreName() {
return lastcorename;
}

147
osd.h Normal file
View File

@@ -0,0 +1,147 @@
#ifndef OSD_H_INCLUDED
#define OSD_H_INCLUDED
/*constants*/
#define OSDCTRLUP 0x01 /*OSD up control*/
#define OSDCTRLDOWN 0x02 /*OSD down control*/
#define OSDCTRLSELECT 0x04 /*OSD select control*/
#define OSDCTRLMENU 0x08 /*OSD menu control*/
#define OSDCTRLRIGHT 0x10 /*OSD right control*/
#define OSDCTRLLEFT 0x20 /*OSD left control*/
// some constants
#define OSDNLINE 8 // number of lines of OSD
#define OSDLINELEN 256 // single line length in bytes
// ---- old Minimig v1 constants -------
#define MM1_OSDCMDREAD 0x00 // OSD read controller/key status
#define MM1_OSDCMDWRITE 0x20 // OSD write video data command
#define MM1_OSDCMDENABLE 0x41 // OSD enable command
#define MM1_OSDCMDDISABLE 0x40 // OSD disable command
#define MM1_OSDCMDRST 0x80 // OSD reset command
#define MM1_OSDCMDAUTOFIRE 0x84 // OSD autofire command
#define MM1_OSDCMDCFGSCL 0xA0 // OSD settings: scanlines effect
#define MM1_OSDCMDCFGIDE 0xB0 // OSD enable HDD command
#define MM1_OSDCMDCFGFLP 0xC0 // OSD settings: floppy config
#define MM1_OSDCMDCFGCHP 0xD0 // OSD settings: chipset config
#define MM1_OSDCMDCFGFLT 0xE0 // OSD settings: filter
#define MM1_OSDCMDCFGMEM 0xF0 // OSD settings: memory config
#define MM1_OSDCMDCFGCPU 0xFC // OSD settings: CPU config
// ---- new Minimig v2 constants -------
#define OSD_CMD_READ 0x00
#define OSD_CMD_RST 0x08
#define OSD_CMD_CLK 0x18
#define OSD_CMD_OSD 0x28
#define OSD_CMD_CHIP 0x04
#define OSD_CMD_CPU 0x14
#define OSD_CMD_MEM 0x24
#define OSD_CMD_VID 0x34
#define OSD_CMD_FLP 0x44
#define OSD_CMD_HDD 0x54
#define OSD_CMD_JOY 0x64
#define OSD_CMD_OSD_WR 0x0c
#define OSD_CMD_WR 0x1c
#define OSD_CMD_VERSION 0x88
#define DISABLE_KEYBOARD 0x02 // disable keyboard while OSD is active
#define REPEATDELAY 500 // repeat delay in 1ms units
#define REPEATRATE 50 // repeat rate in 1ms units
#define BUTTONDELAY 20 // repeat rate in 1ms units
#define KEY_UPSTROKE 0x80
#define KEY_MENU 0x69
#define KEY_PGUP 0x6C
#define KEY_PGDN 0x6D
#define KEY_HOME 0x6A
#define KEY_ESC 0x45
#define KEY_KPENTER 0x43
#define KEY_ENTER 0x44
#define KEY_BACK 0x41
#define KEY_SPACE 0x40
#define KEY_UP 0x4C
#define KEY_DOWN 0x4D
#define KEY_LEFT 0x4F
#define KEY_RIGHT 0x4E
#define KEY_F1 0x50
#define KEY_F2 0x51
#define KEY_F3 0x52
#define KEY_F4 0x53
#define KEY_F5 0x54
#define KEY_F6 0x55
#define KEY_F7 0x56
#define KEY_F8 0x57
#define KEY_F9 0x58
#define KEY_F10 0x59
#define KEY_CTRL 0x63
#define KEY_LALT 0x64
#define KEY_KPPLUS 0x5E
#define KEY_KPMINUS 0x4A
#define KEY_KP0 0x0F
#define CONFIG_TURBO 1
#define CONFIG_NTSC 2
#define CONFIG_A1000 4
#define CONFIG_ECS 8
#define CONFIG_AGA 16
#define CONFIG_FLOPPY1X 0
#define CONFIG_FLOPPY2X 1
#define RESET_NORMAL 0
#define RESET_BOOTLOADER 1
#define OSD_ARROW_LEFT 1
#define OSD_ARROW_RIGHT 2
#define OSD_TURBO_STEP 50
#include <inttypes.h>
/*functions*/
void OsdSetTitle(char *s, int arrow); // arrow > 0 = display right arrow in bottom right, < 0 = display left arrow
void OsdWrite(unsigned char n, char *s, unsigned char inver, unsigned char stipple);
void OsdWriteOffset(unsigned char n, char *s, unsigned char inver, unsigned char stipple, char offset, char leftchar); // Used for scrolling "Exit" text downwards...
void OsdClear(void);
void OsdEnable(unsigned char mode);
void OsdDisable(void);
void OsdWaitVBL(void);
void OsdReset(unsigned char boot);
void ConfigFilter(unsigned char lores, unsigned char hires);
void OsdReconfig(); // Reset to Chameleon core.
// deprecated functions from Minimig 1
void MM1_ConfigFilter(unsigned char lores, unsigned char hires);
void MM1_ConfigScanlines(unsigned char scanlines);
void ConfigVideo(unsigned char hires, unsigned char lores, unsigned char scanlines);
void ConfigMemory(unsigned char memory);
void ConfigCPU(unsigned char cpu);
void ConfigChipset(unsigned char chipset);
void ConfigFloppy(unsigned char drives, unsigned char speed);
void ConfigIDE(unsigned char gayle, unsigned char master, unsigned char slave);
void ConfigAutofire(unsigned char autofire);
unsigned char OsdGetCtrl(void);
void OsdDisableMenuButton(unsigned char disable);
unsigned char GetASCIIKey(unsigned char c);
void OSD_PrintText(unsigned char line, char *text, unsigned long start, unsigned long width, unsigned long offset, unsigned char invert);
void OsdWriteDoubleSize(unsigned char n, char *s, unsigned char pass);
//void OsdDrawLogo(unsigned char n, char row);
void OsdDrawLogo(unsigned char n, char row, char superimpose);
void ScrollText(char n, const char *str, int off, int len, int max_len, unsigned char invert);
void ScrollReset();
void StarsInit();
void StarsUpdate();
void OsdKeySet(unsigned char);
unsigned char OsdKeyGet();
// get/set core currently loaded
void OsdCoreNameSet(const char* str);
char* OsdCoreName();
void OsdSetSize(int n);
int OsdGetSize();
#define OsdIsBig (OsdGetSize()>8)
#endif

314
spi.c Normal file
View File

@@ -0,0 +1,314 @@
#include "spi.h"
#include "hardware.h"
#include "fpga_io.h"
#define SSPI_STROBE (1<<17)
#define SSPI_ACK SSPI_STROBE
#define SSPI_FPGA_EN (1<<18)
#define SSPI_OSD_EN (1<<19)
#define SSPI_IO_EN (1<<20)
#define SSPI_DM_EN (1<<21)
#define SWAPW(a) ((((a)<<8)&0xff00)|(((a)>>8)&0x00ff))
static void spi_en(uint32_t mask, uint32_t en)
{
uint32_t gpo = fpga_gpo_read();
fpga_gpo_write(en ? gpo | mask : gpo & ~mask);
}
uint16_t spi_w(uint16_t word)
{
uint32_t gpo = (fpga_gpo_read() & ~(0xFFFF | SSPI_STROBE)) | word;
fpga_gpo_write(gpo);
fpga_gpo_write(gpo | SSPI_STROBE);
int gpi;
do
{
gpi = fpga_gpi_read();
if (gpi < 0)
{
printf("GPI[31]==1. FPGA is uninitialized?\n");
return 0;
}
} while (!(gpi & SSPI_ACK));
fpga_gpo_write(gpo);
do
{
gpi = fpga_gpi_read();
if (gpi < 0)
{
printf("GPI[31]==1. FPGA is uninitialized?\n");
return 0;
}
} while (gpi & SSPI_ACK);
return (uint16_t)gpi;
}
void spi_init(int enable)
{
printf("Init SPI.\n");
}
uint8_t spi_b(uint8_t parm)
{
return (uint8_t)spi_w(parm);
}
void EnableFpga()
{
spi_en(SSPI_FPGA_EN, 1);
}
void DisableFpga()
{
spi_en(SSPI_FPGA_EN, 0);
}
void EnableOsd()
{
spi_en(SSPI_OSD_EN, 1);
}
void DisableOsd()
{
spi_en(SSPI_OSD_EN, 0);
}
void EnableIO()
{
spi_en(SSPI_IO_EN, 1);
}
void DisableIO()
{
spi_en(SSPI_IO_EN, 0);
}
void EnableDMode()
{
spi_en(SSPI_DM_EN, 1);
}
void DisableDMode()
{
spi_en(SSPI_DM_EN, 0);
}
uint8_t spi_in()
{
return spi_b(0);
}
void spi8(uint8_t parm)
{
spi_b(parm);
}
void spi16(uint16_t parm)
{
spi8(parm >> 8);
spi8(parm >> 0);
}
void spi24(uint32_t parm)
{
spi8(parm >> 16);
spi8(parm >> 8);
spi8(parm >> 0);
}
void spi32(uint32_t parm)
{
spi8(parm >> 24);
spi8(parm >> 16);
spi8(parm >> 8);
spi8(parm >> 0);
}
// little endian: lsb first
void spi32le(uint32_t parm)
{
spi8(parm >> 0);
spi8(parm >> 8);
spi8(parm >> 16);
spi8(parm >> 24);
}
/* OSD related SPI functions */
void spi_osd_cmd_cont(uint8_t cmd)
{
EnableOsd();
spi8(cmd);
}
void spi_osd_cmd(uint8_t cmd)
{
spi_osd_cmd_cont(cmd);
DisableOsd();
}
void spi_osd_cmd8_cont(uint8_t cmd, uint8_t parm)
{
EnableOsd();
spi8(cmd);
spi8(parm);
}
void spi_osd_cmd8(uint8_t cmd, uint8_t parm)
{
spi_osd_cmd8_cont(cmd, parm);
DisableOsd();
}
void spi_osd_cmd16(uint8_t cmd, uint16_t parm)
{
EnableOsd();
spi8(cmd);
spi_w(parm);
DisableOsd();
}
void spi_osd_cmd32_cont(uint8_t cmd, uint32_t parm)
{
EnableOsd();
spi8(cmd);
spi32(parm);
}
void spi_osd_cmd32(uint8_t cmd, uint32_t parm)
{
spi_osd_cmd32_cont(cmd, parm);
DisableOsd();
}
void spi_osd_cmd32le_cont(uint8_t cmd, uint32_t parm)
{
EnableOsd();
spi8(cmd);
spi32le(parm);
}
void spi_osd_cmd32le(uint8_t cmd, uint32_t parm)
{
spi_osd_cmd32le_cont(cmd, parm);
DisableOsd();
}
/* User_io related SPI functions */
void spi_uio_cmd_cont(uint8_t cmd)
{
EnableIO();
spi8(cmd);
}
void spi_uio_cmd(uint8_t cmd)
{
spi_uio_cmd_cont(cmd);
DisableIO();
}
void spi_uio_cmd8_cont(uint8_t cmd, uint8_t parm)
{
EnableIO();
spi8(cmd);
spi8(parm);
}
void spi_uio_cmd8(uint8_t cmd, uint8_t parm)
{
spi_uio_cmd8_cont(cmd, parm);
DisableIO();
}
void spi_uio_cmd32(uint8_t cmd, uint32_t parm, int wide)
{
EnableIO();
spi8(cmd);
if (wide)
{
spi_w((uint16_t)parm);
spi_w((uint16_t)(parm >> 16));
}
else
{
spi8(parm);
spi8(parm >> 8);
spi8(parm >> 16);
spi8(parm >> 24);
}
DisableIO();
}
void spi_n(uint8_t value, uint16_t cnt)
{
while (cnt--) spi8(value);
}
void spi_read(uint8_t *addr, uint16_t len, int wide)
{
if (wide)
{
uint16_t len16 = len >> 1;
uint16_t *a16 = (uint16_t*)addr;
while (len16--) *a16++ = spi_w(0);
if (len & 1) *((uint8_t*)a16) = spi_w(0);
}
else
{
while (len--) *addr++ = spi_b(0);
}
}
void spi_write(uint8_t *addr, uint16_t len, int wide)
{
if (wide)
{
uint16_t len16 = len >> 1;
uint16_t *a16 = (uint16_t*)addr;
while (len16--) spi_w(*a16++);
if(len & 1) spi_w(*((uint8_t*)a16));
}
else
{
while (len--) spi8(*addr++);
}
}
void spi_block_read(uint8_t *addr, int wide)
{
spi_read(addr, 512, wide);
}
void spi_block_write(uint8_t *addr, int wide)
{
spi_write(addr, 512, wide);
}
void spi_block_write_16be(uint16_t *addr)
{
uint16_t len = 256;
uint16_t tmp;
while (len--)
{
tmp = *addr++;
spi_w(SWAPW(tmp));
}
}
void spi_block_read_16be(uint16_t *addr)
{
uint16_t len = 256;
uint16_t tmp;
while (len--)
{
tmp = spi_w(0xFFFF);
*addr++ = SWAPW(tmp);
}
}

59
spi.h Normal file
View File

@@ -0,0 +1,59 @@
#ifndef SPI_H
#define SPI_H
#include <inttypes.h>
/* main init functions */
void spi_init(int enable);
/* chip select functions */
void EnableFpga();
void DisableFpga();
void EnableOsd();
void DisableOsd();
void EnableDMode();
void DisableDMode();
void EnableIO();
void DisableIO();
// base functions
uint8_t spi_b(uint8_t parm);
uint16_t spi_w(uint16_t word);
// input only helper
uint8_t spi_in();
void spi8(uint8_t parm);
void spi16(uint16_t parm);
void spi24(uint32_t parm);
void spi32(uint32_t parm);
void spi32le(uint32_t parm);
void spi_n(uint8_t value, uint16_t cnt);
/* block transfer functions */
void spi_block_read(uint8_t *addr, int wide);
void spi_read(uint8_t *addr, uint16_t len, int wide);
void spi_block_write(uint8_t *addr, int wide);
void spi_write(uint8_t *addr, uint16_t len, int wide);
void spi_block_write_16be(uint16_t *addr);
void spi_block_read_16be(uint16_t *addr);
/* OSD related SPI functions */
void spi_osd_cmd_cont(uint8_t cmd);
void spi_osd_cmd(uint8_t cmd);
void spi_osd_cmd8_cont(uint8_t cmd, uint8_t parm);
void spi_osd_cmd8(uint8_t cmd, uint8_t parm);
void spi_osd_cmd16(uint8_t cmd, uint16_t parm);
void spi_osd_cmd32_cont(uint8_t cmd, uint32_t parm);
void spi_osd_cmd32(uint8_t cmd, uint32_t parm);
void spi_osd_cmd32le_cont(uint8_t cmd, uint32_t parm);
void spi_osd_cmd32le(uint8_t cmd, uint32_t parm);
/* User_io related SPI functions */
void spi_uio_cmd_cont(uint8_t cmd);
void spi_uio_cmd(uint8_t cmd);
void spi_uio_cmd8(uint8_t cmd, uint8_t parm);
void spi_uio_cmd8_cont(uint8_t cmd, uint8_t parm);
void spi_uio_cmd32(uint8_t cmd, uint32_t parm, int wide);
#endif // SPI_H

365
state.c Normal file
View File

@@ -0,0 +1,365 @@
/*
Copyright 2005, 2006, 2007 Dennis van Weeren
Copyright 2008, 2009 Jakub Bednarski
This file is part of Minimig
Minimig 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.
Minimig 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/>.
This code keeps status of MiST state
*/
#include <string.h>
#include "stdio.h"
#include "state.h"
#include "osd.h"
#include "hardware.h"
//#include "charrom.h"
// for I/O
static mist_joystick_t mist_joystick_temp = {
.vid = 0,
.pid = 0,
.num_buttons = 1, // DB9 has 1 button
.state = 0,
.state_extra = 0,
.usb_state = 0,
.usb_state_extra = 0,
.turbo = 0,
.turbo_counter = 0,
.turbo_mask = 0x30, // A and B buttons
.turbo_state = 0xFF // flip state (0 or 1)
};
/* latest joystick state */
static mist_joystick_t mist_joysticks[3] = { // 3rd one is dummy, used to store defaults
{
.vid = 0,
.pid = 0,
.num_buttons = 1, // DB9 has 1 button
.state = 0,
.state_extra = 0,
.usb_state = 0,
.usb_state_extra = 0,
.turbo = 0,
.turbo_counter = 0,
.turbo_mask = 0x30, // A and B buttons
.turbo_state = 0xFF // flip state (0 or 1)
},
{
.vid = 0,
.pid = 0,
.num_buttons = 1, // DB9 has 1 button
.state = 0,
.state_extra = 0,
.usb_state = 0,
.usb_state_extra = 0,
.turbo = 0,
.turbo_counter = 0,
.turbo_mask = 0x30, // A and B buttons
.turbo_state = 0xFF // flip state (0 or 1)
},
{
.vid = 0,
.pid = 0,
.num_buttons = 1, // DB9 has 1 button
.state = 0,
.state_extra = 0,
.usb_state = 0,
.usb_state_extra = 0,
.turbo = 0,
.turbo_counter = 0,
.turbo_mask = 0x30, // A and B buttons
.turbo_state = 0xFF // flip state (0 or 1)
}
};
void joy_reset(mist_joystick_t joy) {
joy.vid = 0;
joy.pid = 0;
joy.num_buttons = 1; // DB9 has 1 button
joy.state = 0;
joy.state_extra = 0;
joy.usb_state = 0;
joy.usb_state_extra = 0;
joy.turbo = 0;
joy.turbo_counter = 0;
joy.turbo_mask = 0x30; // A and B buttons
joy.turbo_state = 0xFF; // flip state (0 or 1)
}
// sets a joystick to input status
void StateJoyCopy(uint8_t num_joy, mist_joystick_t* joy) {
mist_joystick_t mine;
if (num_joy>1) return;
if (!joy) return;
mine = mist_joysticks[num_joy];
mine.vid = joy->vid;
mine.pid = joy->pid;
mine.num_buttons = joy->num_buttons;
mine.state = joy->state;
mine.state_extra = joy->state_extra;
mine.usb_state = joy->usb_state;
mine.usb_state_extra = joy->usb_state_extra;
mine.turbo = joy->turbo;
mine.turbo_counter = joy->turbo_counter;
mine.turbo_mask = joy->turbo_mask;
mine.turbo_state = joy->turbo_state;
}
void StateJoyRead(uint8_t num_joy, mist_joystick_t* joy) {
mist_joystick_t mine;
if (num_joy>1) return;
if (!joy) return;
mine = mist_joysticks[num_joy];
joy->vid = mine.vid;
joy->pid = mine.pid;
joy->num_buttons = mine.num_buttons;
joy->state = mine.state;
joy->state_extra = mine.state_extra;
joy->usb_state = mine.usb_state;
joy->usb_state_extra = mine.usb_state_extra;
joy->turbo = mine.turbo;
joy->turbo_counter = mine.turbo_counter;
joy->turbo_mask = mine.turbo_mask;
joy->turbo_state = mine.turbo_state;
}
// returns a copy of a status structure
mist_joystick_t StateJoyGetStructure(uint8_t num_joy) {
StateJoyRead(num_joy, &mist_joystick_temp);
return mist_joystick_temp;
}
// applies the turbo to a given joystick
mist_joystick_t StateJoyUpdateTurboStructure(uint8_t num_joy) {
StateJoyRead(num_joy, &mist_joystick_temp);
StateTurboUpdate(&mist_joystick_temp);
//mist_joystick_t mine = mist_joystick_temp;
StateJoyCopy(num_joy, &mist_joystick_temp);
}
uint8_t StateJoyStructureState(uint8_t num_joy) {
mist_joystick_t mine;
mine = StateJoyGetStructure(num_joy);
return mine.state;
}
/* latest joystick state */
static uint8_t osd_joy;
static uint8_t osd_joy_extra;
static uint8_t osd_joy2;
static uint8_t osd_joy_extra2;
void StateJoySet(uint8_t c, uint8_t joy_num) {
//iprintf("OSD joy: %x\n", c);
if (joy_num == 0)
osd_joy = c;
else
osd_joy2 = c;
}
void StateJoySetExtra(uint8_t c, uint8_t joy_num) {
if (joy_num == 0)
osd_joy_extra = c;
else
osd_joy_extra2 = c;
}
uint8_t StateJoyGet(uint8_t joy_num) {
return joy_num == 0 ? osd_joy : osd_joy2;
}
uint8_t StateJoyGetExtra(uint8_t joy_num) {
return joy_num == 0 ? osd_joy_extra : osd_joy_extra2;
}
static uint8_t raw_usb_joy; // four directions and 4 buttons
static uint8_t raw_usb_joy_extra; // eight extra buttons
static uint8_t raw_usb_joy_b; // four directions and 4 buttons
static uint8_t raw_usb_joy_extra_b; // eight extra buttons
void StateUsbJoySet(uint8_t usbjoy, uint8_t usbextra, uint8_t joy_num) {
if (joy_num == 0) {
raw_usb_joy = usbjoy;
raw_usb_joy_extra = usbextra;
}
else {
raw_usb_joy_b = usbjoy;
raw_usb_joy_extra_b = usbextra;
}
}
uint8_t StateUsbJoyGet(uint8_t joy_num) {
return (joy_num == 0) ? raw_usb_joy : raw_usb_joy_b;
}
uint8_t StateUsbJoyGetExtra(uint8_t joy_num) {
return (joy_num == 0) ? raw_usb_joy_extra : raw_usb_joy_extra_b;
}
static uint16_t usb_vid;
static uint16_t usb_pid;
static uint8_t num_buttons;
static uint16_t usb_vid_b;
static uint16_t usb_pid_b;
static uint8_t num_buttons_b;
void StateUsbIdSet(uint16_t vid, uint16_t pid, uint8_t num, uint8_t joy_num) {
if (joy_num == 0) {
usb_vid = vid;
usb_pid = pid;
num_buttons = num;
}
else {
usb_vid_b = vid;
usb_pid_b = pid;
num_buttons_b = num;
}
}
uint16_t StateUsbVidGet(uint8_t joy_num) {
return joy_num == 0 ? usb_vid : usb_vid_b;
}
uint16_t StateUsbPidGet(uint8_t joy_num) {
return joy_num == 0 ? usb_pid : usb_pid_b;
}
uint8_t StateUsbGetNumButtons(uint8_t joy_num) {
return (joy_num == 0) ? num_buttons : num_buttons_b;
}
// return joystick state take into account turbo settings
void StateJoyState(uint8_t joy_num, mist_joystick_t* joy) {
mist_joystick_t mine;
if (joy_num>1) return;
if (!joy) return;
joy->vid = StateUsbVidGet(joy_num);
joy->pid = StateUsbPidGet(joy_num);
//joy.num_buttons=1; // DB9 has 1 button
joy->state = StateUsbPidGet(joy_num);
joy->state_extra = StateJoyGetExtra(joy_num);
joy->usb_state = StateUsbJoyGet(joy_num);
joy->usb_state_extra = (joy_num);
//apply turbo status
joy->state = StateUsbPidGet(joy_num);
if (joy->turbo > 0) {
joy->state &= joy->turbo_state;
}
// chache into current static scope
StateJoyCopy(joy_num, joy);
}
/* handle button's turbo timers */
void StateTurboUpdate(mist_joystick_t* joy) {
if (!joy) return;
if (joy->turbo == 0) return; // nothing to do
joy->turbo_counter += 1;
if (joy->turbo_counter > joy->turbo) {
joy->turbo_counter = 0;
joy->turbo_state ^= joy->turbo_mask;
}
}
/* reset all turbo timers and state */
void StateTurboReset(mist_joystick_t* joy) {
if (!joy) return;
joy->turbo_counter = 0;
joy->turbo_state = 0xFF;
}
/* set a specific turbo mask and timeout */
void StateTurboSet(mist_joystick_t* joy, uint16_t turbo, uint16_t mask) {
if (!joy) return;
StateTurboReset(joy);
joy->turbo = turbo;
joy->turbo_mask = mask;
}
// Keep track of connected sticks
uint8_t joysticks = 0;
uint8_t StateNumJoysticks() {
return joysticks;
}
void StateNumJoysticksSet(uint8_t num) {
joysticks = num;
}
/* keyboard data */
static uint8_t key_modifier = 0;
static uint8_t key_pressed[6] = { 0,0,0,0,0,0 };
static uint16_t keys_ps2[6] = { 0,0,0,0,0,0 };
void StateKeyboardPressedPS2(uint16_t *keycodes) {
unsigned i = 0;
for (i = 0; i<6; i++) {
keycodes[i] = keys_ps2[i];
}
}
void StateKeyboardSet(uint8_t modifier, uint8_t* keycodes, uint16_t* keycodes_ps2) {
unsigned i = 0, j = 0;
key_modifier = modifier;
for (i = 0; i<6; i++) {
//iprintf("Key N=%d, USB=%x, PS2=%x\n", i, keycodes[i], keycodes_ps2[i]);
if (((keycodes[i] & 0xFF) != 0xFF) && (keycodes[i] & 0xFF)) {
key_pressed[j] = keycodes[i];
if ((keycodes_ps2[i] & 0xFF) != 0xFF) {
// translate EXT into 0E
if (0x1000 & keycodes_ps2[i]) {
keys_ps2[j++] = (keycodes_ps2[i] & 0xFF) | 0xE000;
}
else {
keys_ps2[j++] = keycodes_ps2[i] & 0xFF;
}
}
else {
keys_ps2[j++] = 0;
}
}
}
while (j<6) {
key_pressed[j] = 0;
keys_ps2[j++] = 0;
}
}
uint8_t StateKeyboardModifiers() {
return key_modifier;
}
void StateKeyboardPressed(uint8_t *keycodes) {
uint8_t i = 0;
for (i = 0; i<6; i++)
keycodes[i] = key_pressed[i];
}
/* core currently loaded */
static char lastcorename[261 + 10] = "CORE";
void StateCoreNameSet(const char* str) {
siprintf(lastcorename, "%s", str);
}
char* StateCoreName() {
return lastcorename;
}
// clear all states
void StateReset() {
strcpy(lastcorename, "CORE");
//State_key = 0;
//joysticks=0;
key_modifier = 0;
for (int i = 0; i<6; i++) {
key_pressed[i] = 0;
keys_ps2[i] = 0;
}
//joy_reset(mist_joy[0]);
//joy_reset(mist_joy[1]);
//joy_reset(mist_joy[2]);
}

82
state.h Normal file
View File

@@ -0,0 +1,82 @@
#ifndef STATE_H_INCLUDED
#define STATE_H_INCLUDED
#include <inttypes.h>
void StateReset();
//// type definitions ////
typedef struct {
uint16_t vid; // USB vendor ID
uint16_t pid; // USB product ID
uint8_t num_buttons; // number of physical buttons reported by HID parsing
uint8_t state; // virtual joystick: current state of 4 direction + 4 first buttons
uint8_t state_extra; // current state of 8 more buttons
uint8_t usb_state; // raw USB state of direction and buttons
uint8_t usb_state_extra; // raw USB state of 8 more buttons
uint16_t turbo; // 0 if disabled, otherwise max number to flip state
uint16_t turbo_counter; // increased when using turbo, flips state when passing turbo
uint8_t turbo_mask; // buttons subject to turbo
uint8_t turbo_state; // current mask to apply
} mist_joystick_t;
/*****
* Various functions to retrieve hardware state from the State
*/
// USB raw data for joystick
void StateUsbJoySet(uint8_t usbjoy, uint8_t usbextra, uint8_t joy_num);
void StateUsbIdSet(uint16_t vid, uint16_t pid, uint8_t num_buttons, uint8_t joy_num);
uint8_t StateUsbJoyGet(uint8_t joy_num);
uint8_t StateUsbJoyGetExtra(uint8_t joy_num);
uint8_t StateUsbGetNumButtons(uint8_t joy_num);
uint16_t StateUsbVidGet(uint8_t joy_num);
uint16_t StateUsbPidGet(uint8_t joy_num);
// State of first (virtual) internal joystisk i.e. after mapping
void StateJoySet(uint8_t c, uint8_t joy_num);
void StateJoySetExtra(uint8_t c, uint8_t joy_num);
uint8_t StateJoyGet(uint8_t joy_num);
uint8_t StateJoyGetExtra(uint8_t joy_num);
// turbo button functions
void StateTurboUpdate(mist_joystick_t* joy);
void StateTurboReset(mist_joystick_t* joy);
void StateTurboSet(mist_joystick_t* joy, uint16_t turbo, uint16_t mask);
mist_joystick_t StateJoyUpdateTurboStructure(uint8_t num_joy);
void StateJoyRead(uint8_t num_joy, mist_joystick_t* joy);
void StateJoyCopy(uint8_t num_joy, mist_joystick_t* joy);
uint8_t StateJoyStructureState(uint8_t num_joy);
// Keep track of connected sticks
uint8_t StateNumJoysticks();
void StateNumJoysticksSet(uint8_t num);
// to get data
void StateJoyState(uint8_t joy_num, mist_joystick_t* joy); // directions and 4 buttons, reflecting turbo settings
/*
// turbo function
void StateTurboUpdate(uint8_t joy_num);
void StateTurboReset(uint8_t joy_num);
void StateTurboSet ( uint16_t turbo, uint16_t mask, uint8_t joy_num );
*/
// keyboard status
void StateKeyboardSet(uint8_t modifier, uint8_t* pressed, uint16_t* pressed_ps2); //get usb and ps2 codes
uint8_t StateKeyboardModifiers();
void StateKeyboardPressed(uint8_t *pressed);
void StateKeyboardPressedPS2(uint16_t *keycodes);
// get/set core currently loaded
void StateCoreNameSet(const char* str);
char* StateCoreName();
#endif

1153
tos.c Normal file

File diff suppressed because it is too large Load Diff

107
tos.h Normal file
View File

@@ -0,0 +1,107 @@
#ifndef TOS_H
#define TOS_H
#include "file_io.h"
// FPGA spi cmommands
#define MIST_INVALID 0x00
// memory interface
#define MIST_SET_ADDRESS 0x01
#define MIST_WRITE_MEMORY 0x02
#define MIST_READ_MEMORY 0x03
#define MIST_SET_CONTROL 0x04
#define MIST_GET_DMASTATE 0x05 // reads state of dma and floppy controller
#define MIST_ACK_DMA 0x06 // acknowledge a dma command
#define MIST_BUS_REQ 0x07 // request bus
#define MIST_BUS_REL 0x08 // release bus
#define MIST_SET_VADJ 0x09
#define MIST_NAK_DMA 0x0a // reject a dma command
// tos sysconfig bits:
// 0 - RESET
// 1-3 - Memory configuration
// 4-5 - CPU configuration
// 6-7 - Floppy A+B write protection
// 8 - Color/Monochrome mode
// 9 - PAL mode in 56 or 50 Hz
// 10-17 - ACSI device enable
// memory configurations (0x02/0x04/0x08)
// (currently 4MB are fixed and cannot be changed)
#define TOS_MEMCONFIG_512K (0<<1) // not yet supported
#define TOS_MEMCONFIG_1M (1<<1) // not yet supported
#define TOS_MEMCONFIG_2M (2<<1) // not yet supported
#define TOS_MEMCONFIG_4M (3<<1) // not yet supported
#define TOS_MEMCONFIG_8M (4<<1)
#define TOS_MEMCONFIG_14M (5<<1)
#define TOS_MEMCONFIG_RES0 (6<<1) // reserved
#define TOS_MEMCONFIG_RES1 (7<<1) // reserved
// cpu configurations (0x10/0x20)
#define TOS_CPUCONFIG_68000 (0<<4)
#define TOS_CPUCONFIG_68010 (1<<4)
#define TOS_CPUCONFIG_RESERVED (2<<4)
#define TOS_CPUCONFIG_68020 (3<<4)
// control bits (all control bits have unknown state after core startup)
#define TOS_CONTROL_CPU_RESET 0x00000001
#define TOS_CONTROL_FDC_WR_PROT_A 0x00000040
#define TOS_CONTROL_FDC_WR_PROT_B 0x00000080
#define TOS_CONTROL_VIDEO_COLOR 0x00000100 // input to mfp
#define TOS_CONTROL_PAL50HZ 0x00000200 // display pal at 50hz (56 hz otherwise)
// up to eight acsi devices can be enabled
#define TOS_ACSI0_ENABLE 0x00000400
#define TOS_ACSI1_ENABLE 0x00000800
#define TOS_ACSI2_ENABLE 0x00001000
#define TOS_ACSI3_ENABLE 0x00002000
#define TOS_ACSI4_ENABLE 0x00004000
#define TOS_ACSI5_ENABLE 0x00008000
#define TOS_ACSI6_ENABLE 0x00010000
#define TOS_ACSI7_ENABLE 0x00020000
#define TOS_CONTROL_TURBO 0x00040000
#define TOS_CONTROL_BLITTER 0x00080000
#define TOS_CONTROL_SCANLINES0 0x00100000 // 0 = off, 1 = 25%, 2 = 50%, 3 = 75%
#define TOS_CONTROL_SCANLINES1 0x00200000
#define TOS_CONTROL_SCANLINES (TOS_CONTROL_SCANLINES0|TOS_CONTROL_SCANLINES1)
#define TOS_CONTROL_STEREO 0x00400000
#define TOS_CONTROL_STE 0x00800000
#define TOS_CONTROL_MSTE 0x01000000
#define TOS_CONTROL_ETHERNET 0x02000000
// USB redirection modes
// (NONE=0, RS232=1, PARALLEL=2, MIDI=3)
#define TOS_CONTROL_REDIR0 0x04000000
#define TOS_CONTROL_REDIR1 0x08000000
#define TOS_CONTROL_VIKING 0x10000000 // Viking graphics card
unsigned long tos_system_ctrl(void);
void tos_upload(char *);
void tos_poll();
void tos_update_sysctrl(unsigned long);
char *tos_get_disk_name(char);
char tos_disk_is_inserted(char index);
void tos_insert_disk(char i, char *name);
void tos_eject_all();
void tos_select_hdd_image(char i, char *name);
void tos_set_direct_hdd(char on);
char tos_get_direct_hdd();
void tos_reset(char cold);
char *tos_get_image_name();
char *tos_get_cartridge_name();
char tos_cartridge_is_inserted();
void tos_load_cartridge(char *);
void tos_set_video_adjust(char axis, char value);
char tos_get_video_adjust(char axis);
void tos_config_init(void);
void tos_config_save(void);
#endif

1956
user_io.c Normal file

File diff suppressed because it is too large Load Diff

192
user_io.h Normal file
View File

@@ -0,0 +1,192 @@
/*
* user_io.h
*
*/
#ifndef USER_IO_H
#define USER_IO_H
#include <inttypes.h>
#include "file_io.h"
#define UIO_STATUS 0x00
#define UIO_BUT_SW 0x01
// codes as used by minimig (amiga)
#define UIO_JOYSTICK0 0x02 // also used by 8 bit
#define UIO_JOYSTICK1 0x03 // -"-
#define UIO_MOUSE 0x04 // -"-
#define UIO_KEYBOARD 0x05 // -"-
#define UIO_KBD_OSD 0x06 // keycodes used by OSD only
// codes as used by MiST (atari)
// directions (in/out) are from an io controller view
#define UIO_IKBD_OUT 0x02
#define UIO_IKBD_IN 0x03
#define UIO_SERIAL_OUT 0x04
#define UIO_SERIAL_IN 0x05
#define UIO_PARALLEL_IN 0x06
#define UIO_MIDI_OUT 0x07
#define UIO_MIDI_IN 0x08
#define UIO_ETH_MAC 0x09
#define UIO_ETH_STATUS 0x0a
#define UIO_ETH_FRM_IN 0x0b
#define UIO_ETH_FRM_OUT 0x0c
#define UIO_SERIAL_STAT 0x0d
#define UIO_JOYSTICK2 0x10 // also used by minimig and 8 bit
#define UIO_JOYSTICK3 0x11 // -"-
#define UIO_JOYSTICK4 0x12 // -"-
#define UIO_JOYSTICK5 0x13 // -"-
// codes as currently used by 8bit only
#define UIO_GET_STRING 0x14
#define UIO_SET_STATUS 0x15
#define UIO_GET_SDSTAT 0x16 // read status of sd card emulation
#define UIO_SECTOR_RD 0x17 // SD card sector read
#define UIO_SECTOR_WR 0x18 // SD card sector write
#define UIO_SET_SDCONF 0x19 // send SD card configuration (CSD, CID)
#define UIO_ASTICK 0x1a
#define UIO_SIO_IN 0x1b // serial in
#define UIO_SET_SDSTAT 0x1c // set sd card status
#define UIO_SET_SDINFO 0x1d // send info about mounted image
#define UIO_SET_STATUS2 0x1e // 32bit status
#define UIO_GET_KBD_LED 0x1f // keyboard LEDs control
#define UIO_SET_VIDEO 0x20 // set HDMI video mode 0: 1280x720p60(TV), 1: 1280x1024p60(PC), 2-255: reserved
// codes as used by 8bit (atari 800, zx81) via SS2
#define UIO_GET_STATUS 0x50
#define UIO_SECTOR_SND 0x51
#define UIO_SECTOR_RCV 0x52
#define UIO_FILE_TX 0x53
#define UIO_FILE_TX_DAT 0x54
#define UIO_FILE_INDEX 0x55
#define UIO_FILE_INFO 0x56
#define JOY_RIGHT 0x01
#define JOY_LEFT 0x02
#define JOY_DOWN 0x04
#define JOY_UP 0x08
#define JOY_BTN_SHIFT 4
#define JOY_BTN1 0x10
#define JOY_BTN2 0x20
#define JOY_BTN3 0x40
#define JOY_BTN4 0x80
#define JOY_OSD 0x100
#define JOY_MOVE (JOY_RIGHT|JOY_LEFT|JOY_UP|JOY_DOWN)
#define BUTTON1 0x01
#define BUTTON2 0x02
// virtual gamepad buttons
#define JOY_A JOY_BTN1
#define JOY_B JOY_BTN2
#define JOY_SELECT JOY_BTN3
#define JOY_START JOY_BTN4
#define JOY_X 0x100
#define JOY_Y 0x200
#define JOY_L 0x400
#define JOY_R 0x800
#define JOY_L2 0x1000
#define JOY_R2 0x2000
#define JOY_L3 0x4000
#define JOY_R3 0x8000
// keyboard LEDs control
#define KBD_LED_CAPS_CONTROL 0x01
#define KBD_LED_CAPS_STATUS 0x02
#define KBD_LED_CAPS_MASK (KBD_LED_CAPS_CONTROL | KBD_LED_CAPS_STATUS)
#define KBD_LED_NUM_CONTROL 0x04
#define KBD_LED_NUM_STATUS 0x08
#define KBD_LED_NUM_MASK (KBD_LED_NUM_CONTROL | KBD_LED_NUM_STATUS)
#define KBD_LED_SCRL_CONTROL 0x10
#define KBD_LED_SCRL_STATUS 0x20
#define KBD_LED_SCRL_MASK (KBD_LED_SCRL_CONTROL | KBD_LED_SCRL_STATUS)
#define KBD_LED_FLAG_MASK 0xC0
#define KBD_LED_FLAG_STATUS 0x40
#define CONF_VGA_SCALER 0x04
#define CONF_CSYNC 0x08
#define CONF_FORCED_SCANDOUBLER 0x10
#define CONF_YPBPR 0x20
// core type value should be unlikely to be returned by broken cores
#define CORE_TYPE_UNKNOWN 0x55
#define CORE_TYPE_DUMB 0xa0 // core without any io controller interaction
#define CORE_TYPE_PACE 0xa2 // core from pacedev.net (joystick only)
#define CORE_TYPE_MIST 0xa3 // mist atari st core
#define CORE_TYPE_8BIT 0xa4 // atari 800/c64 like core
#define CORE_TYPE_MINIMIG2 0xa5 // new Minimig with AGA
#define CORE_TYPE_ARCHIE 0xa6 // Acorn Archimedes
// user io status bits (currently only used by 8bit)
#define UIO_STATUS_RESET 0x01
#define UIO_STOP_BIT_1 0
#define UIO_STOP_BIT_1_5 1
#define UIO_STOP_BIT_2 2
#define UIO_PARITY_NONE 0
#define UIO_PARITY_ODD 1
#define UIO_PARITY_EVEN 2
#define UIO_PARITY_MARK 3
#define UIO_PARITY_SPACE 4
#define UIO_PRIORITY_KEYBOARD 0
#define UIO_PRIORITY_GAMEPAD 1
// serial status data type returned from the core
typedef struct {
uint32_t bitrate; // 300, 600 ... 115200
uint8_t datasize; // 5,6,7,8 ...
uint8_t parity;
uint8_t stopbits;
uint8_t fifo_stat; // space in cores input fifo
} __attribute__((packed)) serial_status_t;
void user_io_init();
void user_io_detect_core_type();
unsigned char user_io_core_type();
char is_minimig();
char user_io_is_8bit_with_config_string();
void user_io_poll();
char user_io_menu_button();
char user_io_button_dip_switch1();
char user_io_user_button();
void user_io_osd_key_enable(char);
void user_io_serial_tx(char *, uint16_t);
char *user_io_8bit_get_string(char);
unsigned long user_io_8bit_set_status(unsigned long, unsigned long);
void user_io_file_tx(char *, unsigned char);
void user_io_sd_set_config(void);
char user_io_dip_switch1(void);
char user_io_serial_status(serial_status_t *, uint8_t);
void user_io_file_mount(char *name);
char *user_io_get_core_name();
char is_menu_core();
// io controllers interface for FPGA ethernet emulation using usb ethernet
// devices attached to the io controller (ethernec emulation)
void user_io_eth_send_mac(uint8_t *);
uint32_t user_io_eth_get_status(void);
void user_io_eth_send_rx_frame(uint8_t *, uint16_t);
void user_io_eth_receive_tx_frame(uint8_t *, uint16_t);
// hooks from the usb layer
void user_io_mouse(unsigned char b, int16_t x, int16_t y);
void user_io_kbd(unsigned char m, unsigned char *k, unsigned short vid, unsigned short pid);
char* user_io_create_config_name();
void user_io_digital_joystick(unsigned char, unsigned char);
void user_io_analog_joystick(unsigned char, char, char);
char user_io_osd_is_visible();
void user_io_send_buttons(char);
void user_io_joystick(unsigned char joystick, unsigned char map);
void user_io_key_remap(char *);
void add_modifiers(uint8_t mod, uint16_t* keys_ps2);
void user_io_set_index(unsigned char index);
unsigned char user_io_ext_idx(char *, char*);
#endif // USER_IO_H