diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..644e7266 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..e1774318 --- /dev/null +++ b/Makefile @@ -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)) diff --git a/MiSTer.ini b/MiSTer.ini new file mode 100644 index 00000000..425898e0 --- /dev/null +++ b/MiSTer.ini @@ -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. diff --git a/MiSTer.sln b/MiSTer.sln new file mode 100644 index 00000000..87178b21 --- /dev/null +++ b/MiSTer.sln @@ -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 diff --git a/MiSTer.vcxproj b/MiSTer.vcxproj new file mode 100644 index 00000000..f1e32f2a --- /dev/null +++ b/MiSTer.vcxproj @@ -0,0 +1,101 @@ + + + + + Debug + Win32 + + + + {C1D6BEA2-1469-4FBC-8A27-A82BDE9041AC} + MakeFileProj + + + + Makefile + true + v140 + + + + + + + + + + + + git.lnk ./build.sh + MiSTer + git.lnk ./clean.sh + WIN32;VDATE="000000";_FILE_OFFSET_BITS=64;_LARGEFILE64_SOURCE;$(NMakePreprocessorDefinitions) + c:\Work\Git\opt\gcc54\arm-linux-gnueabihf\libc\usr\include;$(NMakeIncludeSearchPath) + $(TEMP) + $(TEMP) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MiSTer.vcxproj.filters b/MiSTer.vcxproj.filters new file mode 100644 index 00000000..37ff07f9 --- /dev/null +++ b/MiSTer.vcxproj.filters @@ -0,0 +1,166 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + + + + \ No newline at end of file diff --git a/archie.c b/archie.c new file mode 100644 index 00000000..07ac26bb --- /dev/null +++ b/archie.c @@ -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> 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> 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 %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(); +} diff --git a/archie.h b/archie.h new file mode 100644 index 00000000..130276d4 --- /dev/null +++ b/archie.h @@ -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 diff --git a/boot.c b/boot.c new file mode 100644 index 00000000..9114d1dd --- /dev/null +++ b/boot.c @@ -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>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/dev/ttyS0 2>/dev/ttyS0 +#include +#include + +#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= 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> 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)); +} diff --git a/config.h b/config.h new file mode 100644 index 00000000..ffc90b79 --- /dev/null +++ b/config.h @@ -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); + diff --git a/debug.h b/debug.h new file mode 100644 index 00000000..b0a11b0f --- /dev/null +++ b/debug.h @@ -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 diff --git a/errors.h b/errors.h new file mode 100644 index 00000000..947473fb --- /dev/null +++ b/errors.h @@ -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); diff --git a/fdd.c b/fdd.c new file mode 100644 index 00000000..173847e1 --- /dev/null +++ b/fdd.c @@ -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 . +*/ + +// 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 +#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; + } +} + diff --git a/fdd.h b/fdd.h new file mode 100644 index 00000000..c651028a --- /dev/null +++ b/fdd.h @@ -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 + diff --git a/file_io.c b/file_io.c new file mode 100644 index 00000000..d87bf5e9 --- /dev/null +++ b/file_io.c @@ -0,0 +1,682 @@ +#include "file_io.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/file_io.h b/file_io.h new file mode 100644 index 00000000..b1d40b29 --- /dev/null +++ b/file_io.h @@ -0,0 +1,70 @@ +#ifndef _FAT16_H_INCLUDED +#define _FAT16_H_INCLUDED + +#include +#include +#include +#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 diff --git a/fpga_base_addr_ac5.h b/fpga_base_addr_ac5.h new file mode 100644 index 00000000..3f685fa6 --- /dev/null +++ b/fpga_base_addr_ac5.h @@ -0,0 +1,62 @@ +/* +* Copyright (C) 2012 Altera Corporation +* +* 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_ */ diff --git a/fpga_io.c b/fpga_io.c new file mode 100644 index 00000000..36bf4832 --- /dev/null +++ b/fpga_io.c @@ -0,0 +1,635 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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)> 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(); +} diff --git a/fpga_io.h b/fpga_io.h new file mode 100644 index 00000000..5425c2a4 --- /dev/null +++ b/fpga_io.h @@ -0,0 +1,37 @@ + +#include + +#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 diff --git a/fpga_manager.h b/fpga_manager.h new file mode 100644 index 00000000..c032b544 --- /dev/null +++ b/fpga_manager.h @@ -0,0 +1,70 @@ +/* +* Copyright (C) 2012 Altera Corporation +* 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_ */ diff --git a/fpga_nic301.h b/fpga_nic301.h new file mode 100644 index 00000000..190fd5c6 --- /dev/null +++ b/fpga_nic301.h @@ -0,0 +1,195 @@ +/* +* Copyright (C) 2014 Marek Vasut +* +* 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_ */ diff --git a/fpga_reset_manager.h b/fpga_reset_manager.h new file mode 100644 index 00000000..0e550948 --- /dev/null +++ b/fpga_reset_manager.h @@ -0,0 +1,74 @@ +/* +* Copyright (C) 2012 Altera Corporation +* +* 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_ */ diff --git a/fpga_system_manager.h b/fpga_system_manager.h new file mode 100644 index 00000000..e37a9baa --- /dev/null +++ b/fpga_system_manager.h @@ -0,0 +1,141 @@ +/* +* Copyright (C) 2013 Altera Corporation +* +* 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_ */ diff --git a/hardware.c b/hardware.c new file mode 100644 index 00000000..716ed752 --- /dev/null +++ b/hardware.c @@ -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 . +*/ + +#include +#include +#include +#include +#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= time; +} + +void WaitTimer(unsigned long time) +{ + time = GetTimer(time); + while (!CheckTimer(time)); +} diff --git a/hardware.h b/hardware.h new file mode 100644 index 00000000..b7766af4 --- /dev/null +++ b/hardware.h @@ -0,0 +1,23 @@ +#ifndef HARDWARE_H +#define HARDWARE_H + +#include +#include + +#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 diff --git a/hdd.c b/hdd.c new file mode 100644 index 00000000..d5cdf813 --- /dev/null +++ b/hdd.c @@ -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 . +*/ + +// 2009-11-22 - read/write multiple implemented + + +#include +#include +#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; c2rdb_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; +} diff --git a/hdd.h b/hdd.h new file mode 100644 index 00000000..41b4b999 --- /dev/null +++ b/hdd.h @@ -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__ + diff --git a/hdd_internal.h b/hdd_internal.h new file mode 100644 index 00000000..ecc824a0 --- /dev/null +++ b/hdd_internal.h @@ -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 */ + diff --git a/ikbd.c b/ikbd.c new file mode 100644 index 00000000..970ffebe --- /dev/null +++ b/ikbd.c @@ -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 +#include + +#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; + } + } +} diff --git a/ikbd.h b/ikbd.h new file mode 100644 index 00000000..ca0eb242 --- /dev/null +++ b/ikbd.h @@ -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 diff --git a/ini_parser.c b/ini_parser.c new file mode 100644 index 00000000..2d113b68 --- /dev/null +++ b/ini_parser.c @@ -0,0 +1,368 @@ +// ini_parser.c +// 2015, rok.krajnc@gmail.com + + +//// includes //// +#include +#include +#include +#include +#include +#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; insections; 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; jnvars; 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; sectionnsections; 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; varnvars; 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); + } + */ +} diff --git a/ini_parser.h b/ini_parser.h new file mode 100644 index 00000000..3016d997 --- /dev/null +++ b/ini_parser.h @@ -0,0 +1,47 @@ +// ini_parser.h +// 2015, rok.krajnc@gmail.com + +#ifndef __INI_PARSER_H__ +#define __INI_PARSER_H__ + +//// includes //// +#include + + +//// 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__ + diff --git a/input.c b/input.c new file mode 100644 index 00000000..5baf7a37 --- /dev/null +++ b/input.c @@ -0,0 +1,955 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 (ilen) + { + 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<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 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= 0) close(pool[i].fd); + } + state = 1; + return 0; + } + + for (int i = 0; i= 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= 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 +}; diff --git a/logo.h b/logo.h new file mode 100644 index 00000000..36b4146f --- /dev/null +++ b/logo.h @@ -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} +};*/ \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 00000000..d0d42bc6 --- /dev/null +++ b/main.c @@ -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 . +*/ + +#include +#include +#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; +} diff --git a/menu.c b/menu.c new file mode 100644 index 00000000..d860dbc8 --- /dev/null +++ b/menu.c @@ -0,0 +1,3621 @@ +/* +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 . +*/ + +// 2009-11-14 - OSD labels changed +// 2009-12-15 - added display of directory name extensions +// 2010-01-09 - support for variable number of tracks +// 2016-06-01 - improvements to 8-bit menu + +//#include "stdbool.h" +#include +#include +#include +#include +#include "stdio.h" +#include "string.h" +#include "errors.h" +#include "file_io.h" +#include "osd.h" +#include "state.h" +#include "fdd.h" +#include "hdd.h" +#include "hardware.h" +#include "config.h" +#include "menu.h" +#include "user_io.h" +#include "tos.h" +#include "debug.h" +#include "boot.h" +#include "archie.h" +#include "fpga_io.h" +#include +#include "mist_cfg.h" +#include "input.h" + +// test features (not used right now) +// #define ALLOW_TEST_MENU 0 //remove to disable in prod version + + +// central MiST joystick status + +static mist_joystick_t mist_joy[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) + } +}; +// state of MIST virtual joystick + + +void NumJoysticksSet(unsigned char num) { + mist_joystick_t joy; + StateNumJoysticksSet(num); + if (num<3) { + //clear USB joysticks + if (num<2) + joy = mist_joy[0]; + else + joy = mist_joy[1]; + + joy.vid = 0; + joy.vid = 0; + joy.num_buttons = 1; + joy.state = 0; + joy.state_extra = 0; + joy.usb_state = 0; + joy.usb_state_extra = 0; + } +} + +// other constants +#define DIRSIZE 8 // number of items in directory display window + +unsigned char menustate = MENU_NONE1; +unsigned char parentstate; +unsigned char menusub = 0; +unsigned char menusub_last = 0; //for when we allocate it dynamically and need to know last row +unsigned int menumask = 0; // Used to determine which rows are selectable... +unsigned long menu_timer = 0; + +extern unsigned char drives; +extern adfTYPE df[4]; + +extern configTYPE config; + +extern const char version[]; +const char *config_tos_mem[] = { "512 kB", "1 MB", "2 MB", "4 MB", "8 MB", "14 MB", "--", "--" }; +const char *config_tos_wrprot[] = { "none", "A:", "B:", "A: and B:" }; +const char *config_tos_usb[] = { "none", "control", "debug", "serial", "parallel", "midi" }; + +const char *config_filter_msg[] = { "none", "HORIZONTAL", "VERTICAL", "H+V" }; +const char *config_memory_chip_msg[] = { "0.5 MB", "1.0 MB", "1.5 MB", "2.0 MB" }; +const char *config_memory_slow_msg[] = { "none ", "0.5 MB", "1.0 MB", "1.5 MB" }; +const char *config_scanlines_msg[] = { "off", "dim", "black" }; +const char *config_ar_msg[] = { "4:3", "16:9" }; +const char *config_blank_msg[] = { "Blank", "Blank+" }; +const char *config_dither_msg[] = { "off", "SPT", "RND", "S+R" }; +const char *config_memory_fast_msg[] = { "none ", "2.0 MB", "4.0 MB","24.0 MB","24.0 MB" }; +const char *config_cpu_msg[] = { "68000 ", "68010", "-----","68020" }; +const char *config_hdf_msg[] = { "Disabled", "Hardfile (disk img)", "MMC/SD card", "MMC/SD partition 1", "MMC/SD partition 2", "MMC/SD partition 3", "MMC/SD partition 4" }; +const char *config_chipset_msg[] = { "OCS-A500", "OCS-A1000", "ECS", "---", "---", "---", "AGA", "---" }; +const char *config_turbo_msg[] = { "none", "CHIPRAM", "KICK", "BOTH" }; +char *config_autofire_msg[] = { " AUTOFIRE OFF", " AUTOFIRE FAST", " AUTOFIRE MEDIUM", " AUTOFIRE SLOW" }; +const char *config_cd32pad_msg[] = { "OFF", "ON" }; +char *config_button_turbo_msg[] = { "OFF", "FAST", "MEDIUM", "SLOW" }; +char *config_button_turbo_choice_msg[] = { "A only", "B only", "A & B" }; +char *joy_button_map[] = { "RIGHT", "LEFT", "DOWN", "UP", "BUTTON 1", "BUTTON 2", "BUTTON 3", "BUTTON 4", "BUTTON OSD" }; + + +enum HelpText_Message { HELPTEXT_NONE, HELPTEXT_MAIN, HELPTEXT_HARDFILE, HELPTEXT_CHIPSET, HELPTEXT_MEMORY, HELPTEXT_VIDEO }; +const char *helptexts[] = { + 0, + " Welcome to MiST! Use the cursor keys to navigate the menus. Use space bar or enter to select an item. Press Esc or F12 to exit the menus. Joystick emulation on the numeric keypad can be toggled with the numlock key, while pressing Ctrl-Alt-0 (numeric keypad) toggles autofire mode.", + " Minimig can emulate an A600 IDE harddisk interface. The emulation can make use of Minimig-style hardfiles (complete disk images) or UAE-style hardfiles (filesystem images with no partition table). It is also possible to use either the entire SD card or an individual partition as an emulated harddisk.", + " Minimig's processor core can emulate a 68000 or 68020 processor (though the 68020 mode is still experimental.) If you're running software built for 68000, there's no advantage to using the 68020 mode, since the 68000 emulation runs just as fast.", + " Minimig can make use of up to 2 megabytes of Chip RAM, up to 1.5 megabytes of Slow RAM (A500 Trapdoor RAM), and up to 24 megabytes of true Fast RAM. To use the HRTmon feature you will need a file on the SD card named hrtmon.rom.", + " Minimig's video features include a blur filter, to simulate the poorer picture quality on older monitors, and also scanline generation to simulate the appearance of a screen with low vertical resolution.", + 0 +}; + +// one screen width +const char* HELPTEXT_SPACER = " "; +char helptext_custom[320]; + +const char* scanlines[] = { "Off","25%","50%","75%" }; +const char* stereo[] = { "Mono","Stereo" }; +const char* atari_chipset[] = { "ST","STE","MegaSTE","STEroids" }; + +unsigned char config_autofire = 0; + +// file selection menu variables +char fs_pFileExt[13] = "xxx"; +unsigned char fs_ExtLen = 0; +unsigned char fs_Options; +unsigned char fs_MenuSelect; +unsigned char fs_MenuCancel; + +char* GetExt(char *ext) { + static char extlist[32]; + char *p = extlist; + + while (*ext) { + strcpy(p, ","); + strncat(p, ext, 3); + if (strlen(ext) <= 3) break; + ext += 3; + p += strlen(p); + } + + return extlist + 1; +} + +char SelectedPath[1024] = { 0 }; +int changeDir(char *dir) +{ + char curdir[128]; + memset(curdir, 0, sizeof(curdir)); + if(!dir || !strcmp(dir, "..")) + { + if (!strlen(SelectedPath)) + { + return 0; + } + + char *p = strrchr(SelectedPath, '/'); + if (p) + { + *p = 0; + int len = strlen(p+1); + if (len > sizeof(curdir) - 1) len = sizeof(curdir) - 1; + strncpy(curdir, p+1, len); + } + else + { + int len = strlen(SelectedPath); + if (len > sizeof(curdir) - 1) len = sizeof(curdir) - 1; + strncpy(curdir, SelectedPath, len); + SelectedPath[0] = 0; + } + } + else + { + if (strlen(SelectedPath) + strlen(dir) > sizeof(SelectedPath) - 100) + { + return 0; + } + + if (strlen(SelectedPath)) strcat(SelectedPath, "/"); + strcat(SelectedPath, dir); + } + + ScanDirectory(SelectedPath, SCAN_INIT, fs_pFileExt, fs_Options); + if(curdir[0]) + { + ScanDirectory(SelectedPath, SCAN_SET_ITEM, curdir, fs_Options); + } + return 1; +} + +static void SelectFile(char* pFileExt, unsigned char Options, unsigned char MenuSelect, unsigned char MenuCancel, char chdir) +{ + // this function displays file selection menu + + iprintf("%s - %s\n", pFileExt, fs_pFileExt); + AdjustDirectory(SelectedPath); + + if (strncmp(pFileExt, fs_pFileExt, 12) != 0) // check desired file extension + { // if different from the current one go to the root directory and init entry buffer + SelectedPath[0] = 0; + + // for 8 bit cores try to + if ((user_io_core_type() == CORE_TYPE_8BIT) && chdir) { + strcpy(SelectedPath, user_io_get_core_name()); + ScanDirectory(SelectedPath, SCAN_INIT, pFileExt, Options); + if (!nDirEntries) + { + SelectedPath[0] = 0; + ScanDirectory(SelectedPath, SCAN_INIT, pFileExt, Options); + } + } + else + { + ScanDirectory(SelectedPath, SCAN_INIT, pFileExt, Options); + } + } + + iprintf("pFileExt = %3s\n", pFileExt); + strcpy(fs_pFileExt, pFileExt); + fs_ExtLen = strlen(fs_pFileExt); + // fs_pFileExt = pFileExt; + fs_Options = Options; + fs_MenuSelect = MenuSelect; + fs_MenuCancel = MenuCancel; + + menustate = MENU_FILE_SELECT1; +} + + +static void substrcpy(char *d, char *s, char idx) { + char p = 0; + + while (*s) { + if ((p == idx) && *s && (*s != ',')) + *d++ = *s; + + if (*s == ',') + p++; + + s++; + } + + *d = 0; +} + +#define STD_EXIT " exit" +#define STD_SPACE_EXIT " SPACE to exit" +#define STD_COMBO_EXIT " Ctrl+ESC to exit" + +#define HELPTEXT_DELAY 10000 +#define FRAME_DELAY 150 + +// prints input as a string of binary (on/off) values +// assumes big endian, returns using special characters (checked box/unchecked box) +void siprintbinary(char* buffer, size_t const size, void const * const ptr) +{ + unsigned char *b = (unsigned char*)ptr; + unsigned char byte; + int i, j; + memset(buffer, '\0', sizeof(buffer)); + for (i = size - 1; i >= 0; i--) + { + for (j = 0; j<8; j++) + { + byte = (b[i] >> j) & 1; + buffer[j] = byte ? '\x1a' : '\x19'; + } + } + return; +} + +void get_joystick_state(char *joy_string, char *joy_string2, uint8_t joy_num) { + // helper to get joystick status (both USB or DB9) + uint16_t vjoy; + memset(joy_string, '\0', sizeof(joy_string)); + memset(joy_string2, '\0', sizeof(joy_string2)); + vjoy = StateJoyGet(joy_num); + vjoy |= StateJoyGetExtra(joy_num) << 8; + if (vjoy == 0) { + strcpy(joy_string2, " "); + memset(joy_string2, ' ', 8); + memset(joy_string2 + 8, '\x14', 1); + memset(joy_string2 + 9, ' ', 1); + strcat(joy_string2, "\0"); + return; + } + strcpy(joy_string, " \x12 X Y L R L2 R2 L3"); + strcpy(joy_string2, " < \x13 > A B Sel Sta R3"); + if (!(vjoy & JOY_UP)) memset(joy_string + 8, ' ', 1); + if (!(vjoy & JOY_X)) memset(joy_string + 12, ' ', 1); + if (!(vjoy & JOY_Y)) memset(joy_string + 14, ' ', 1); + if (!(vjoy & JOY_L)) memset(joy_string + 16, ' ', 1); + if (!(vjoy & JOY_R)) memset(joy_string + 18, ' ', 1); + if (!(vjoy & JOY_L2)) memset(joy_string + 20, ' ', 2); + if (!(vjoy & JOY_R2)) memset(joy_string + 23, ' ', 2); + if (!(vjoy & JOY_L3)) memset(joy_string + 26, ' ', 2); + if (!(vjoy & JOY_LEFT)) memset(joy_string2 + 6, ' ', 1); + if (!(vjoy & JOY_DOWN)) memset(joy_string2 + 8, '\x14', 1); + if (!(vjoy & JOY_RIGHT)) memset(joy_string2 + 10, ' ', 1); + if (!(vjoy & JOY_A)) memset(joy_string2 + 12, ' ', 1); + if (!(vjoy & JOY_B)) memset(joy_string2 + 14, ' ', 1); + if (!(vjoy & JOY_SELECT))memset(joy_string2 + 16, ' ', 3); + if (!(vjoy & JOY_START)) memset(joy_string2 + 20, ' ', 3); + if (!(vjoy & JOY_R3)) memset(joy_string2 + 24, ' ', 2); + return; +} + +void get_joystick_state_usb(char *s, unsigned char joy_num) { + /* USB specific - current "raw" state + (in reverse binary format to correspont to MIST.INI mapping entries) + */ + char buffer[5]; + unsigned short i; + char binary_string[9] = "00000000"; + unsigned char joy = 0; + unsigned int max_btn = 1; + if (StateNumJoysticks() == 0 || (joy_num == 1 && StateNumJoysticks()<2)) + { + strcpy(s, " "); + return; + } + max_btn = StateUsbGetNumButtons(joy_num); + joy = StateUsbJoyGet(joy_num); + siprintf(s, " USB: ---- 0000 0000 0000"); + siprintbinary(binary_string, sizeof(joy), &joy); + s[7] = binary_string[0] == '\x1a' ? '>' : '\x1b'; + s[8] = binary_string[1] == '\x1a' ? '<' : '\x1b'; + s[9] = binary_string[2] == '\x1a' ? '\x13' : '\x1b'; + s[10] = binary_string[3] == '\x1a' ? '\x12' : '\x1b'; + s[12] = binary_string[4]; + s[13] = max_btn>1 ? binary_string[5] : ' '; + s[14] = max_btn>2 ? binary_string[6] : ' '; + s[15] = max_btn>3 ? binary_string[7] : ' '; + joy = StateUsbJoyGetExtra(joy_num); + siprintbinary(binary_string, sizeof(joy), &joy); + s[17] = max_btn>4 ? binary_string[0] : ' '; + s[18] = max_btn>5 ? binary_string[1] : ' '; + s[19] = max_btn>6 ? binary_string[2] : ' '; + s[20] = max_btn>7 ? binary_string[3] : ' '; + s[22] = max_btn>8 ? binary_string[4] : ' '; + s[23] = max_btn>9 ? binary_string[5] : ' '; + s[24] = max_btn>10 ? binary_string[6] : ' '; + s[25] = max_btn>11 ? binary_string[7] : ' '; + return; +} + +void append_joystick_usbid(char *usb_id, unsigned int usb_vid, unsigned int usb_pid) { + siprintf(usb_id, "VID:%04X PID:%04X", usb_vid, usb_pid); +} + +void get_joystick_id(char *usb_id, unsigned char joy_num, short raw_id) { + /* + Builds a string containing the USB VID/PID information of a joystick + */ + char buffer[32] = ""; + mist_joystick_t joystick; + if (joy_num>3) + joystick = mist_joy[2]; + else + joystick = mist_joy[joy_num]; + + if (raw_id == 0) { + if (StateNumJoysticks() == 0 || (joy_num == 1 && StateNumJoysticks()<2)) + { + strcpy(usb_id, " "); + strcat(usb_id, "Atari DB9 Joystick"); + return; + } + } + + //hack populate from outside + joystick.vid = StateUsbVidGet(joy_num); + joystick.pid = StateUsbPidGet(joy_num); + + memset(usb_id, '\0', sizeof(usb_id)); + if (joystick.vid>0) { + /* + if (raw_id == 0) { + strcpy(buffer, get_joystick_alias( joystick.vid, joystick.pid )); + } + */ + if (strlen(buffer) == 0) { + append_joystick_usbid(buffer, joystick.vid, joystick.pid); + } + } + else { + strcpy(buffer, "Atari DB9 Joystick"); + } + if (raw_id == 0) + siprintf(usb_id, "%*s", (28 - strlen(buffer)) / 2, " "); + else + strcpy(usb_id, ""); + strcat(usb_id, buffer); + return; +} + + +unsigned char getIdx(char *opt) { + if ((opt[1] >= '0') && (opt[1] <= '9')) return opt[1] - '0'; + if ((opt[1] >= 'A') && (opt[1] <= 'V')) return opt[1] - 'A' + 10; + return 0; // basically 0 cannot be valid because used as a reset. Thus can be used as a error. +} + +unsigned long getStatus(char *opt, unsigned long status) { + char idx1 = getIdx(opt); + char idx2 = getIdx(opt + 1); + unsigned long x = (status & (1 << idx1)) ? 1 : 0; + + if (idx2>idx1) { + x = status >> idx1; + x = x & ~(0xffffffff << (idx2 - idx1 + 1)); + } + + return x; +} + +unsigned long setStatus(char *opt, unsigned long status, unsigned long value) { + unsigned char idx1 = getIdx(opt); + unsigned char idx2 = getIdx(opt + 1); + unsigned long x = 1; + + if (idx2>idx1) x = ~(0xffffffff << (idx2 - idx1 + 1)); + x = x << idx1; + + return (status & ~x) | ((value << idx1) & x); +} + +unsigned long getStatusMask(char *opt) { + char idx1 = getIdx(opt); + char idx2 = getIdx(opt + 1); + unsigned long x = 1; + + if (idx2>idx1) x = ~(0xffffffff << (idx2 - idx1 + 1)); + + //iprintf("grtStatusMask %d %d %x\n", idx1, idx2, x); + + return x << idx1; +} + +char* get_keycode_table() +{ + switch (user_io_core_type()) + { + case CORE_TYPE_MINIMIG2: + return "Amiga"; + + case CORE_TYPE_MIST: + return " ST"; + + case CORE_TYPE_ARCHIE: + return "Archie"; + } + + return " PS/2"; +} + +void HandleUI(void) +{ + char *p; + char s[40]; + unsigned char i, c, m, up, down, select, menu, right, left, plus, minus; + uint8_t mod; + unsigned long len; + static hardfileTYPE t_hardfile[2]; // temporary copy of former hardfile configuration + static unsigned char ctrl = false; + static unsigned char lalt = false; + char enable; + static long helptext_timer; + static const char *helptext; + static char helpstate = 0; + uint8_t keys[6] = { 0,0,0,0,0,0 }; + uint16_t keys_ps2[6] = { 0,0,0,0,0,0 }; + + mist_joystick_t joy0, joy1; + + /* check joystick status */ + char joy_string[32]; + char joy_string2[32]; + char usb_id[64]; + + // update turbo status for joysticks + //StateTurboUpdate(0); + //StateTurboUpdate(1); + + // get user control codes + c = OsdGetCtrl(); + + // decode and set events + menu = false; + select = false; + up = false; + down = false; + left = false; + right = false; + plus = false; + minus = false; + + switch (c) + { + case KEY_CTRL: + ctrl = true; + break; + case KEY_CTRL | KEY_UPSTROKE: + ctrl = false; + break; + case KEY_LALT: + lalt = true; + break; + case KEY_LALT | KEY_UPSTROKE: + lalt = false; + break; + case KEY_KP0: + if (ctrl && lalt) + { + if (menustate == MENU_NONE2 || menustate == MENU_INFO) + { + config_autofire++; + config_autofire &= 3; + ConfigAutofire(config_autofire); + if (menustate == MENU_NONE2 || menustate == MENU_INFO) + InfoMessage(config_autofire_msg[config_autofire]); + } + } + break; + + case KEY_MENU: + menu = true; + OsdKeySet(KEY_MENU | KEY_UPSTROKE); + break; + + // Within the menu the esc key acts as the menu key. problem: + // if the menu is left with a press of ESC, then the follwing + // break code for the ESC key when the key is released will + // reach the core which never saw the make code. Simple solution: + // react on break code instead of make code + case KEY_ESC | KEY_UPSTROKE: + if (menustate != MENU_NONE2) + menu = true; + break; + case KEY_ENTER: + case KEY_SPACE: + select = true; + break; + case KEY_UP: + up = true; + break; + case KEY_DOWN: + down = true; + break; + case KEY_LEFT: + left = true; + break; + case KEY_RIGHT: + right = true; + break; + case KEY_KPPLUS: + case 0x0c: // =/+ + plus = true; + break; + case KEY_KPMINUS: + case 0x0b: // -/_ + minus = true; + break; + + case 0x01: // 1: 1280x720 mode + if (user_io_osd_is_visible) mist_cfg.video_mode = 0; + break; + + case 0x02: // 2: 1280x1024 mode + if (user_io_osd_is_visible) mist_cfg.video_mode = 1; + break; + } + + if (menu || select || up || down || left || right) + { + if (helpstate) + OsdWrite(OsdGetSize()-1, STD_EXIT, (menumask - ((1 << (menusub + 1)) - 1)) <= 0, 0); // Redraw the Exit line... + helpstate = 0; + helptext_timer = GetTimer(HELPTEXT_DELAY); + } + + if (helptext) + { + if (helpstate<9) + { + if (CheckTimer(helptext_timer)) + { + helptext_timer = GetTimer(FRAME_DELAY); + OsdWriteOffset(OsdGetSize() - 1, STD_EXIT, 0, 0, helpstate, 0); + ++helpstate; + } + } + else if (helpstate == 9) + { + ScrollReset(); + ++helpstate; + } + else + ScrollText(OsdGetSize()-1, helptext, 0, 0, 0, 0); + } + + // Standardised menu up/down. + // The screen should set menumask, bit 0 to make the top line selectable, bit 1 for the 2nd line, etc. + // (Lines in this context don't have to correspond to rows on the OSD.) + // Also set parentstate to the appropriate menustate. + if (menumask) + { + if (down && (menumask >= (1 << (menusub + 1)))) // Any active entries left? + { + do + menusub++; + while ((menumask & (1 << menusub)) == 0); + menustate = parentstate; + } + + if (up && menusub > 0 && (menumask << (OsdGetSize() - menusub))) + { + do + --menusub; + while ((menumask & (1 << menusub)) == 0); + menustate = parentstate; + } + } + + + // Switch to current menu screen + switch (menustate) + { + /******************************************************************/ + /* no menu selected */ + /******************************************************************/ + case MENU_NONE1: + helptext = helptexts[HELPTEXT_NONE]; + menumask = 0; + OsdDisable(); + menustate = MENU_NONE2; + break; + + case MENU_NONE2: + if (menu) + { + if (user_io_core_type() == CORE_TYPE_MINIMIG2) + menustate = MENU_MAIN1; + else if (user_io_core_type() == CORE_TYPE_MIST) + menustate = MENU_MIST_MAIN1; + else if (user_io_core_type() == CORE_TYPE_ARCHIE) + menustate = MENU_ARCHIE_MAIN1; + else { + // the "menu" core is special in jumps directly to the core selection menu + if (is_menu_core()) + { + OsdSetSize(16); + OsdCoreNameSet(""); + SelectFile("RBF", 0, MENU_FIRMWARE_CORE_FILE_SELECTED, MENU_FIRMWARE1, 0); + } + else + menustate = MENU_8BIT_MAIN1; + } + menusub = 0; + OsdClear(); + OsdEnable(DISABLE_KEYBOARD); + } + break; + + /******************************************************************/ + /* archimedes main menu */ + /******************************************************************/ + + case MENU_ARCHIE_MAIN1: { + menumask = 0x3f; + OsdSetTitle("ARCHIE", 0); + + strcpy(s, " Floppy 0: "); + strcat(s, archie_get_floppy_name(0)); + OsdWrite(0, s, menusub == 0, 0); + + strcpy(s, " Floppy 1: "); + strcat(s, archie_get_floppy_name(1)); + OsdWrite(1, s, menusub == 1, 0); + + strcpy(s, " OS ROM: "); + strcat(s, archie_get_rom_name()); + OsdWrite(2, s, menusub == 2, 0); + + OsdWrite(3, "", 0, 0); + + // the following is exactly like the atatri st core + OsdWrite(4, " Firmware & Core \x16", menusub == 3, 0); + OsdWrite(5, " Save config ", menusub == 4, 0); + OsdWrite(6, "", 0, 0); + OsdWrite(7, STD_EXIT, menusub == 5, 0); + menustate = MENU_ARCHIE_MAIN2; + parentstate = MENU_ARCHIE_MAIN1; + } break; + + case MENU_ARCHIE_MAIN2: + // menu key closes menu + if (menu) + menustate = MENU_NONE1; + if (select) { + switch (menusub) { + case 0: // Floppy 0 + case 1: // Floppy 1 + if (archie_floppy_is_inserted(menusub)) { + archie_set_floppy(menusub, NULL); + menustate = MENU_ARCHIE_MAIN1; + } + else + SelectFile("ADF", SCAN_DIR, MENU_ARCHIE_MAIN_FILE_SELECTED, MENU_ARCHIE_MAIN1, 1); + break; + + case 2: // Load ROM + SelectFile("ROM", 0, MENU_ARCHIE_MAIN_FILE_SELECTED, MENU_ARCHIE_MAIN1, 1); + break; + + case 3: // Firmware submenu + menustate = MENU_FIRMWARE1; + menusub = 1; + break; + + case 4: // Save config + menustate = MENU_NONE1; + archie_save_config(); + break; + + case 5: // Exit + menustate = MENU_NONE1; + break; + } + } + break; + + case MENU_ARCHIE_MAIN_FILE_SELECTED: // file successfully selected + if (menusub == 0) archie_set_floppy(0, SelectedPath); + if (menusub == 1) archie_set_floppy(1, SelectedPath); + if (menusub == 2) archie_set_rom(SelectedPath); + menustate = MENU_ARCHIE_MAIN1; + break; + + /******************************************************************/ + /* 8 bit main menu */ + /******************************************************************/ + + case MENU_8BIT_MAIN1: { + int entry; + int selentry; + + int old_osd_size = OsdGetSize(); + while (1) + { + selentry = 0; + entry = 0; + menumask = 0; + p = user_io_get_core_name(); + if (!p[0]) OsdSetTitle("8BIT", OSD_ARROW_RIGHT); + else OsdSetTitle(p, OSD_ARROW_RIGHT); + + if (!p[0]) OsdCoreNameSet("8BIT"); + else OsdCoreNameSet(p); + + // check if there's a file type supported + p = user_io_8bit_get_string(1); + if (p && strlen(p)) { + entry++; + selentry++; + menumask = 1; + strcpy(s, " Load *."); + strcat(s, GetExt(p)); + OsdWrite(0, s, menusub == 0, 0); + } + + // add options as requested by core + i = 2; + do { + char* pos; + unsigned long status = user_io_8bit_set_status(0, 0); // 0,0 gets status + + p = user_io_8bit_get_string(i); + // iprintf("Option %d: %s\n", i-1, p); + + // check for 'F'ile or 'S'D image strings + if (p && ((p[0] == 'F') || (p[0] == 'S'))) { + substrcpy(s, p, 2); + if (strlen(s)) { + strcpy(s, " "); + substrcpy(s + 1, p, 2); + strcat(s, " *."); + } + else { + if (p[0] == 'F') strcpy(s, " Load *."); + else strcpy(s, " Mount *."); + } + pos = s + strlen(s); + substrcpy(pos, p, 1); + strcpy(pos, GetExt(pos)); + OsdWrite(entry, s, menusub == selentry, 0); + + // add bit in menu mask + menumask = (menumask << 1) | 1; + entry++; + selentry++; + } + + // check for 'T'oggle strings + if (p && (p[0] == 'T')) { + + s[0] = ' '; + substrcpy(s + 1, p, 1); + OsdWrite(entry, s, menusub == selentry, 0); + + // add bit in menu mask + menumask = (menumask << 1) | 1; + entry++; + selentry++; + } + + // check for 'O'ption strings + if (p && (p[0] == 'O')) { + unsigned long x = getStatus(p, status); + + // get currently active option + substrcpy(s, p, 2 + x); + char l = strlen(s); + if (!l) { + // option's index is outside of available values. + // reset to 0. + x = 0; + user_io_8bit_set_status(setStatus(p, status, x), 0xffffffff); + substrcpy(s, p, 2 + x); + l = strlen(s); + } + + s[0] = ' '; + substrcpy(s + 1, p, 1); + strcat(s, ":"); + l = 28 - l - strlen(s); + while (l--) strcat(s, " "); + + substrcpy(s + strlen(s), p, 2 + x); + + OsdWrite(entry, s, menusub == selentry, 0); + + // add bit in menu mask + menumask = (menumask << 1) | 1; + entry++; + selentry++; + } + + // delimiter + if (p && (p[0] == '-')) + { + OsdWrite(entry, "", 0, 0); + entry++; + } + + // check for 'V'ersion strings + if (p && (p[0] == 'V')) { + + // p[1] is not used but kept for future use + char x = p[1]; + + // get version string + strcpy(s, OsdCoreName()); + strcat(s, " "); + substrcpy(s + strlen(s), p, 1); + OsdCoreNameSet(s); + } + i++; + } while (p); + + OsdSetSize(entry > 7 ? 16 : 8); + if (old_osd_size == OsdGetSize()) break; + old_osd_size = OsdGetSize(); + } + + // exit row + OsdWrite(OsdGetSize() - 1, STD_EXIT, menusub == selentry, 0); + menusub_last = selentry; + menumask = (menumask << 1) | 1; + + for (; entry=9) + { + finish_map_setting(); + menustate = MENU_8BIT_SYSTEM1; + menusub = 1; + } + break; + + case MENU_8BIT_ABOUT1: + menumask = 0; + helptext = helptexts[HELPTEXT_NONE]; + OsdSetTitle("About", 0); + menustate = MENU_8BIT_ABOUT2; + parentstate = MENU_8BIT_ABOUT1; + for (int i = 5; i < OsdGetSize() - 1; i++) OsdWrite(i, "", 0, 0); + OsdDrawLogo(0, 0, 1); + OsdDrawLogo(1, 1, 1); + OsdDrawLogo(2, 2, 1); + OsdDrawLogo(3, 3, 1); + OsdDrawLogo(4, 4, 1); + OsdDrawLogo(5, 5, 1); + OsdWrite(OsdGetSize() - 1, STD_EXIT, menusub == 0, 0); + StarsInit(); + ScrollReset(); + break; + + case MENU_8BIT_ABOUT2: + StarsUpdate(); + OsdDrawLogo(0, 0, 1); + OsdDrawLogo(1, 1, 1); + OsdDrawLogo(2, 2, 1); + OsdDrawLogo(3, 3, 1); + OsdDrawLogo(4, 4, 1); + OsdDrawLogo(5, 5, 1); + ScrollText(OsdIsBig ? 13 : 6, " MiST by Till Harbaum, based on Minimig by Dennis van Weeren and other projects. MiST hardware and software is distributed under the terms of the GNU General Public License version 3. MiST FPGA cores are the work of their respective authors under individual licensing.", 0, 0, 0, 0); + // menu key closes menu + if (menu) { + menustate = MENU_8BIT_SYSTEM1; + menusub = 5-m; + } + if (select) { + //iprintf("Selected", 0); + if (menusub == 0) { + menustate = MENU_8BIT_SYSTEM1; + menusub = 5-m; + } + } + else { + if (left) + { + menustate = MENU_8BIT_SYSTEM1; + menusub = 5-m; + } + } + break; + + case MENU_8BIT_CONTROLLERS1: + helptext = helptexts[HELPTEXT_NONE]; + menumask = 0x3f; + OsdSetTitle("Inputs", 0); + menustate = MENU_8BIT_CONTROLLERS2; + parentstate = MENU_8BIT_CONTROLLERS1; + OsdWrite(0, " Turbo Settings (disabled) ", menusub == 0, 1); + //OsdWrite(0, " Turbo Settings \x16", menusub==0, 0); + OsdWrite(1, " Joystick 1 Test \x16", menusub == 1, 0); + OsdWrite(2, " Joystick 2 Test \x16", menusub == 2, 0); + OsdWrite(3, " Keyboard Test \x16", menusub == 3, 0); + OsdWrite(4, " USB status \x16", menusub == 4, 0); + OsdWrite(5, "", 0, 0); + //OsdWrite(5, " CHR test \x16", menusub==6, 0); + for (int i = 6; i < OsdGetSize() - 1; i++) OsdWrite(i, "", 0, 0); + OsdWrite(OsdGetSize() - 1, STD_EXIT, menusub == 5, 0); + break; + + case MENU_8BIT_CONTROLLERS2: + // menu key goes back to previous menu + if (menu) { + menusub = 1; + menustate = MENU_8BIT_SYSTEM1; + } + if (select) { + switch (menusub) { + case 0: + // Turbo config + //menustate = MENU_8BIT_TURBO1; + menusub = 0; + break; + case 1: + // Joystick1 Test + menustate = MENU_8BIT_JOYTEST_A1; + menusub = 0; + break; + case 2: + // Joystick2 test + menustate = MENU_8BIT_JOYTEST_B1; + menusub = 0; + break; + case 3: + // Keyboard test + menustate = MENU_8BIT_KEYTEST1; + menusub = 0; + break; + case 4: + // USB status + menustate = MENU_8BIT_USB1; + menusub = 0; + break; + case 5: + // Exit to system menu + menustate = MENU_8BIT_SYSTEM1; + menusub = 1; + break; + /*case 6: + // character rom test + menustate=MENU_8BIT_CHRTEST1; + menusub = 0; + break; + */ + } + } + break; + + case MENU_8BIT_KEYTEST1: + helptext = helptexts[HELPTEXT_NONE]; + menumask = 1; + OsdSetTitle("Keyboard", 0); + menustate = MENU_8BIT_KEYTEST2; + parentstate = MENU_8BIT_KEYTEST1; + for (int i = 0; i < OsdGetSize() - 1; i++) OsdWrite(i, "", 0, 0); + OsdWrite(OsdGetSize() - 1, STD_COMBO_EXIT, menusub == 0, 0); + break; + + case MENU_8BIT_KEYTEST2: + m = OsdIsBig ? 4 : 0; + StateKeyboardPressed(keys); + OsdWrite(m++, " USB scancodes", 0, 0); + siprintf(s, " %2x %2x %2x %2x", keys[0], keys[1], keys[2], keys[3]); // keys[4], keys[5]); + OsdWrite(m++, s, 0, 0); + mod = StateKeyboardModifiers(); + strcpy(usb_id, " "); + siprintbinary(usb_id, sizeof(mod), &mod); + siprintf(s, " mod keys - %s ", usb_id); + OsdWrite(m++, s, 0, 0); + m++; + uint16_t keys_ps2[6] = { 0,0,0,0,0,0 }; + StateKeyboardPressedPS2(keys_ps2); + add_modifiers(mod, keys_ps2); + siprintf(s, " %s scancodes", get_keycode_table()); + OsdWrite(m++, s, 0, 0); + siprintf(s, " %4x %4x %4x %4x ", keys_ps2[0], keys_ps2[1], keys_ps2[2], keys_ps2[3]);// keys_ps2[4], keys_ps2[5]); + OsdWrite(m++, s, 0, 0); + if ((mod & 1) && menu) { // Ctrl+ESC + menustate = MENU_8BIT_SYSTEM1; + menusub = 2; + } + break; + + case MENU_8BIT_USB1: + helptext = helptexts[HELPTEXT_NONE]; + menumask = 1; + OsdSetTitle("USB", 0); + menustate = MENU_8BIT_USB2; + parentstate = MENU_8BIT_USB1; + strcpy(usb_id, " "); + get_joystick_id(usb_id, 0, 1); + siprintf(s, " Joy1 - %s", usb_id); + OsdWrite(0, "", 0, 0); + OsdWrite(1, s, 0, 0); + strcpy(usb_id, " "); + get_joystick_id(usb_id, 1, 1); + siprintf(s, " Joy2 - %s", usb_id); + OsdWrite(2, "", 0, 0); + OsdWrite(3, s, 0, 0); + OsdWrite(4, "", 0, 0); + OsdWrite(5, "", 0, 0); + OsdWrite(6, " ", 0, 0); + for (int i = 7; i < OsdGetSize() - 1; i++) OsdWrite(i, "", 0, 0); + OsdWrite(OsdGetSize() - 1, STD_EXIT, menusub == 0, 0); + break; + + case MENU_8BIT_USB2: + menumask = 1; + OsdSetTitle("USB", 0); + strcpy(usb_id, " "); + get_joystick_id(usb_id, 0, 1); + siprintf(s, " Joy1 - %s", usb_id); + OsdWrite(0, "", 0, 0); + OsdWrite(1, s, 0, 0); + strcpy(usb_id, " "); + get_joystick_id(usb_id, 1, 1); + siprintf(s, " Joy2 - %s", usb_id); + OsdWrite(2, "", 0, 0); + OsdWrite(3, s, 0, 0); + OsdWrite(OsdGetSize() - 1, STD_EXIT, menusub == 0, 0); + // menu key goes back to previous menu + if (menu) { + menustate = MENU_8BIT_CONTROLLERS1; + menusub = 4; + } + if (select) { + if (menusub == 0) { + menustate = MENU_8BIT_CONTROLLERS1; + menusub = 4; + } + } + break; + + case MENU_8BIT_JOYTEST_A1: + helptext = helptexts[HELPTEXT_NONE]; + menumask = 1; + get_joystick_id(usb_id, 0, 0); + OsdSetTitle("Joy1", 0); + menustate = MENU_8BIT_JOYTEST_A2; + parentstate = MENU_8BIT_JOYTEST_A1; + OsdWrite(0, " Test Joystick 1", 0, 0); + OsdWrite(1, usb_id, 0, 0); + OsdWrite(2, "", 0, 0); + OsdWrite(3, "", 0, 0); + OsdWrite(4, "", 0, 0); + OsdWrite(5, "", 0, 0); + OsdWrite(6, " ", 0, 0); + for (int i = 7; i < OsdGetSize() - 1; i++) OsdWrite(i, "", 0, 0); + OsdWrite(OsdGetSize() - 1, STD_SPACE_EXIT, menusub == 0, 0); + break; + + case MENU_8BIT_JOYTEST_A2: + get_joystick_state(joy_string, joy_string2, 0); //grab state of joy 0 + get_joystick_id(usb_id, 0, 0); + OsdWrite(1, usb_id, 0, 0); + OsdWrite(3, joy_string, 0, 0); + OsdWrite(4, joy_string2, 0, 0); + OsdWrite(5, " ", 0, 0); + // display raw USB input + get_joystick_state_usb(s, 0); + OsdWrite(6, s, 0, 0); + // allow allow exit when hitting space + if (c == KEY_SPACE) { + menustate = MENU_8BIT_CONTROLLERS1; + menusub = 1; + } + break; + + case MENU_8BIT_JOYTEST_B1: + helptext = helptexts[HELPTEXT_NONE]; + menumask = 1; + get_joystick_id(usb_id, 1, 0); + OsdSetTitle("Joy2", 0); + menustate = MENU_8BIT_JOYTEST_B2; + parentstate = MENU_8BIT_JOYTEST_B1; + OsdWrite(0, " Test Joystick 2", 0, 0); + OsdWrite(1, usb_id, 0, 0); + OsdWrite(2, "", 0, 0); + OsdWrite(3, "", 0, 0); + OsdWrite(4, "", 0, 0); + OsdWrite(5, "", 0, 0); + OsdWrite(6, " ", 0, 0); + for (int i = 7; i < OsdGetSize() - 1; i++) OsdWrite(i, "", 0, 0); + OsdWrite(OsdGetSize() - 1, STD_SPACE_EXIT, menusub == 0, 0); + break; + + case MENU_8BIT_JOYTEST_B2: + get_joystick_state(joy_string, joy_string2, 1); + get_joystick_id(usb_id, 1, 0); + OsdWrite(1, usb_id, 0, 0); + OsdWrite(3, joy_string, 0, 0); + OsdWrite(4, joy_string2, 0, 0); + OsdWrite(5, " ", 0, 0); + // display raw USB input + get_joystick_state_usb(s, 1); + OsdWrite(6, s, 0, 0); + // allow allow exit when hitting space + if (c == KEY_SPACE) { + menustate = MENU_8BIT_CONTROLLERS1; + menusub = 2; + } + break; + + case MENU_8BIT_TURBO1: + helptext = helptexts[HELPTEXT_NONE]; + menumask = 0x1F; + OsdSetTitle("Turbo", 0); + menustate = MENU_8BIT_TURBO2; + parentstate = MENU_8BIT_TURBO1; + //StateJoyState(0, &mist_joy[0]); + //joy0 = mist_joy[0];//StateJoyGet(0); + StateJoyState(0, &mist_joy[0]); + joy0 = mist_joy[0]; + //StateJoyState(1, &mist_joy[1]); + joy1 = mist_joy[1];//StateJoyGet(1); + OsdWrite(0, " Button Configuration", 1, 0); + OsdWrite(1, "", 0, 0); + strcpy(s, " Joy 1 Turbo : "); + strcat(s, config_button_turbo_msg[(int)joy0.turbo / OSD_TURBO_STEP]); + OsdWrite(2, s, menusub == 0, 0); + strcpy(s, " Buttons : "); + strcat(s, config_button_turbo_choice_msg[(int)joy0.turbo_mask / 16]); + OsdWrite(3, s, menusub == 1, 0); + strcpy(s, " Joy 2 Turbo : "); + strcat(s, config_button_turbo_msg[(int)joy1.turbo / OSD_TURBO_STEP]); + OsdWrite(4, s, menusub == 2, 0); + strcpy(s, " Buttons : "); + strcat(s, config_button_turbo_choice_msg[(int)joy1.turbo_mask / 16]); + OsdWrite(5, s, menusub == 3, 0); + OsdWrite(6, " ", 0, 0); + for (int i = 7; i < OsdGetSize() - 1; i++) OsdWrite(i, "", 0, 0); + OsdWrite(OsdGetSize() - 1, STD_EXIT, menusub == 4, 0); + break; + + case MENU_8BIT_TURBO2: + StateJoyState(0, &mist_joy[0]); + joy0 = mist_joy[0]; + //StateJoyState(1, &mist_joy[1]); + //joy0 = mist_joy[0];//StateJoyGet(0); + joy1 = mist_joy[1];//StateJoyGet(1); + strcpy(s, " Joy 1 Turbo : "); + strcat(s, config_button_turbo_msg[(int)(mist_joy[0].turbo / OSD_TURBO_STEP) & 0xFF]); + OsdWrite(2, s, menusub == 0, 0); + strcpy(s, " Buttons : "); + strcat(s, config_button_turbo_choice_msg[(int)(mist_joy[0].turbo_mask / 16 - 1) & 0xFF]); + OsdWrite(3, s, menusub == 1, 0); + strcpy(s, " Joy 2 Turbo : "); + strcat(s, config_button_turbo_msg[(int)(mist_joy[1].turbo / OSD_TURBO_STEP) & 0xFF]); + OsdWrite(4, s, menusub == 2, 0); + strcpy(s, " Buttons : "); + strcat(s, config_button_turbo_choice_msg[(int)(mist_joy[1].turbo_mask / 16 - 1) & 0xFF]); + OsdWrite(5, s, menusub == 3, 0); + // menu key goes back to previous menu + if (menu) { + menustate = MENU_8BIT_CONTROLLERS1; + menusub = 0; + } + if (select) { + if (menusub == 0) { + mist_joy[0].turbo += OSD_TURBO_STEP; + if (mist_joy[0].turbo>OSD_TURBO_STEP * 3) mist_joy[0].turbo = 0; + } + if (menusub == 1) { + mist_joy[0].turbo_mask += 16; + if (mist_joy[0].turbo_mask>16 * 3) mist_joy[0].turbo_mask = 16; + } + if (menusub == 2) { + mist_joy[1].turbo += OSD_TURBO_STEP; + if (mist_joy[1].turbo>OSD_TURBO_STEP * 3) mist_joy[1].turbo = 0; + } + if (menusub == 3) { + mist_joy[1].turbo_mask += 16; + if (mist_joy[1].turbo_mask>16 * 3) mist_joy[1].turbo_mask = 16; + } + if (menusub == 4) { + menustate = MENU_8BIT_CONTROLLERS1; + menusub = 0; + } + } + break; + + case MENU_8BIT_CHRTEST1: + helptext = helptexts[HELPTEXT_NONE]; + menumask = 0; + OsdSetTitle("CHR", 0); + menustate = MENU_8BIT_CHRTEST2; + parentstate = MENU_8BIT_CHRTEST1; + strcpy(usb_id, " "); + for (i = 1; i<24; i++) { + if (i<4 || i>13) + usb_id[i] = i; + else + usb_id[i] = ' '; + } + OsdWrite(0, usb_id, 0, 0); + for (i = 0; i<24; i++) usb_id[i] = i + 24; + OsdWrite(1, usb_id, 0, 0); + for (i = 0; i<24; i++) usb_id[i] = i + (24 * 2); + OsdWrite(2, usb_id, 0, 0); + for (i = 0; i<24; i++) usb_id[i] = i + (24 * 3); + OsdWrite(3, usb_id, 0, 0); + for (i = 0; i<24; i++) usb_id[i] = i + (24 * 4); + OsdWrite(4, usb_id, 0, 0); + strcpy(usb_id, " "); + for (i = 0; i<8; i++) usb_id[i] = i + (24 * 5); + OsdWrite(5, usb_id, 0, 0); + //for(i=0; i<24; i++) usb_id[i] = i+(24*6); + OsdWrite(6, "", 0, 0); + for (int i = 7; i < OsdGetSize() - 1; i++) OsdWrite(i, "", 0, 0); + OsdWrite(OsdGetSize() - 1, STD_SPACE_EXIT, menusub == 0, 0); + break; + + case MENU_8BIT_CHRTEST2: + if (c == KEY_SPACE) { + menustate = MENU_8BIT_CONTROLLERS1; + menusub = 1; + } + break; + + /******************************************************************/ + /* mist main menu */ + /******************************************************************/ + + case MENU_MIST_MAIN1: + menumask = 0xff; + OsdSetTitle("Mist", 0); + + // most important: main page has setup for floppy A: and screen + strcpy(s, " A: "); + strcat(s, tos_get_disk_name(0)); + if (tos_system_ctrl() & TOS_CONTROL_FDC_WR_PROT_A) strcat(s, " \x17"); + OsdWrite(0, s, menusub == 0, 0); + + strcpy(s, " Screen: "); + if (tos_system_ctrl() & TOS_CONTROL_VIDEO_COLOR) strcat(s, "Color"); + else strcat(s, "Mono"); + OsdWrite(1, s, menusub == 1, 0); + + /* everything else is in submenus */ + OsdWrite(2, " Storage \x16", menusub == 2, 0); + OsdWrite(3, " System \x16", menusub == 3, 0); + OsdWrite(4, " Audio / Video \x16", menusub == 4, 0); + OsdWrite(5, " Firmware & Core \x16", menusub == 5, 0); + + OsdWrite(6, " Save config ", menusub == 6, 0); + + OsdWrite(7, STD_EXIT, menusub == 7, 0); + + menustate = MENU_MIST_MAIN2; + parentstate = MENU_MIST_MAIN1; + break; + + case MENU_MIST_MAIN2: + // menu key closes menu + if (menu) + menustate = MENU_NONE1; + if (select) { + switch (menusub) { + case 0: + if (tos_disk_is_inserted(0)) { + tos_insert_disk(0, NULL); + menustate = MENU_MIST_MAIN1; + } + else + SelectFile("ST ", SCAN_DIR, MENU_MIST_MAIN_FILE_SELECTED, MENU_MIST_MAIN1, 0); + break; + + case 1: + tos_update_sysctrl(tos_system_ctrl() ^ TOS_CONTROL_VIDEO_COLOR); + menustate = MENU_MIST_MAIN1; + break; + + case 2: // Storage submenu + menustate = MENU_MIST_STORAGE1; + menusub = 0; + break; + + case 3: // System submenu + menustate = MENU_MIST_SYSTEM1; + menusub = 0; + break; + + case 4: // Video submenu + menustate = MENU_MIST_VIDEO1; + menusub = 0; + break; + + case 5: // Firmware submenu + menustate = MENU_FIRMWARE1; + menusub = 0; + break; + + case 6: // Save config + menustate = MENU_NONE1; + tos_config_save(); + break; + + case 7: // Exit + menustate = MENU_NONE1; + break; + } + } + break; + + case MENU_MIST_MAIN_FILE_SELECTED: // file successfully selected + tos_insert_disk(0, SelectedPath); + menustate = MENU_MIST_MAIN1; + break; + + case MENU_MIST_STORAGE1: + menumask = tos_get_direct_hdd() ? 0x3f : 0x7f; + OsdSetTitle("Storage", 0); + // entries for both floppies + for (i = 0; i<2; i++) { + strcpy(s, " A: "); + strcat(s, tos_get_disk_name(i)); + s[1] = 'A' + i; + if (tos_system_ctrl() & (TOS_CONTROL_FDC_WR_PROT_A << i)) + strcat(s, " \x17"); + OsdWrite(i, s, menusub == i, 0); + } + strcpy(s, " Write protect: "); + strcat(s, config_tos_wrprot[(tos_system_ctrl() >> 6) & 3]); + OsdWrite(2, s, menusub == 2, 0); + OsdWrite(3, "", 0, 0); + strcpy(s, " ACSI0 direct SD: "); + strcat(s, tos_get_direct_hdd() ? "on" : "off"); + OsdWrite(4, s, menusub == 3, 0); + for (i = 0; i<2; i++) { + strcpy(s, " ACSI0: "); + s[5] = '0' + i; + + strcat(s, tos_get_disk_name(2 + i)); + OsdWrite(5 + i, s, ((i == 1) || !tos_get_direct_hdd()) ? (menusub == (!tos_get_direct_hdd() ? 4 : 3) + i) : 0, + (i == 0) && tos_get_direct_hdd()); + } + OsdWrite(7, STD_EXIT, !tos_get_direct_hdd() ? (menusub == 6) : (menusub == 5), 0); + parentstate = menustate; + menustate = MENU_MIST_STORAGE2; + break; + + + case MENU_MIST_STORAGE2: + if (menu) { + menustate = MENU_MIST_MAIN1; + menusub = 2; + } + if (select) { + if (menusub <= 1) { + if (tos_disk_is_inserted(menusub)) { + tos_insert_disk(menusub, NULL); + menustate = MENU_MIST_STORAGE1; + } + else + SelectFile("ST ", SCAN_DIR, MENU_MIST_STORAGE_FILE_SELECTED, MENU_MIST_STORAGE1, 0); + } + else if (menusub == 2) { + // remove current write protect bits and increase by one + tos_update_sysctrl((tos_system_ctrl() & ~(TOS_CONTROL_FDC_WR_PROT_A | TOS_CONTROL_FDC_WR_PROT_B)) + | (((((tos_system_ctrl() >> 6) & 3) + 1) & 3) << 6)); + menustate = MENU_MIST_STORAGE1; + + } + else if (menusub == 3) { + tos_set_direct_hdd(!tos_get_direct_hdd()); + menustate = MENU_MIST_STORAGE1; + + // no direct hhd emulation: Both ACSI entries are enabled + // or direct hhd emulation for ACSI0: Only second ACSI entry is enabled + } + else if ((menusub == 4) || (!tos_get_direct_hdd() && (menusub == 5))) { + char disk_idx = menusub - (tos_get_direct_hdd() ? 1 : 2); + iprintf("Select image for disk %d\n", disk_idx); + + if (tos_disk_is_inserted(disk_idx)) { + tos_insert_disk(disk_idx, NULL); + menustate = MENU_MIST_STORAGE1; + } + else + SelectFile("HD ", 0, MENU_MIST_STORAGE_FILE_SELECTED, MENU_MIST_STORAGE1, 0); + + } + else if (tos_get_direct_hdd() ? (menusub == 5) : (menusub == 6)) { + menustate = MENU_MIST_MAIN1; + menusub = 2; + } + } + break; + + case MENU_MIST_STORAGE_FILE_SELECTED: // file successfully selected + // floppy/hdd + if (menusub < 2) + tos_insert_disk(menusub, SelectedPath); + else { + char disk_idx = menusub - (tos_get_direct_hdd() ? 1 : 2); + iprintf("Insert image for disk %d\n", disk_idx); + tos_insert_disk(disk_idx, SelectedPath); + } + menustate = MENU_MIST_STORAGE1; + break; + + case MENU_MIST_SYSTEM1: + menumask = 0xff; + OsdSetTitle("System", 0); + + strcpy(s, " Memory: "); + strcat(s, config_tos_mem[(tos_system_ctrl() >> 1) & 7]); + OsdWrite(0, s, menusub == 0, 0); + + strcpy(s, " CPU: "); + strcat(s, config_cpu_msg[(tos_system_ctrl() >> 4) & 3]); + OsdWrite(1, s, menusub == 1, 0); + + strcpy(s, " TOS: "); + strcat(s, tos_get_image_name()); + OsdWrite(2, s, menusub == 2, 0); + + strcpy(s, " Cartridge: "); + strcat(s, tos_get_cartridge_name()); + OsdWrite(3, s, menusub == 3, 0); + + strcpy(s, " USB I/O: "); + strcat(s, "NONE"); //config_tos_usb[tos_get_cdc_control_redirect()]); + OsdWrite(4, s, menusub == 4, 0); + + OsdWrite(5, " Reset", menusub == 5, 0); + OsdWrite(6, " Cold boot", menusub == 6, 0); + + OsdWrite(7, STD_EXIT, menusub == 7, 0); + + parentstate = menustate; + menustate = MENU_MIST_SYSTEM2; + break; + + case MENU_MIST_SYSTEM2: + if (menu) { + menustate = MENU_MIST_MAIN1; + menusub = 3; + } + if (select) { + switch (menusub) { + case 0: { // RAM + int mem = (tos_system_ctrl() >> 1) & 7; // current memory config + mem++; + if (mem > 5) mem = 3; // cycle 4MB/8MB/14MB + tos_update_sysctrl((tos_system_ctrl() & ~0x0e) | (mem << 1)); + tos_reset(1); + menustate = MENU_MIST_SYSTEM1; + } break; + + case 1: { // CPU + int cpu = (tos_system_ctrl() >> 4) & 3; // current cpu config + cpu = (cpu + 1) & 3; + if (cpu == 2) cpu = 3; // skip unused config + tos_update_sysctrl((tos_system_ctrl() & ~0x30) | (cpu << 4)); + tos_reset(0); + menustate = MENU_MIST_SYSTEM1; + } break; + + case 2: // TOS + SelectFile("IMG", 0, MENU_MIST_SYSTEM_FILE_SELECTED, MENU_MIST_SYSTEM1, 0); + break; + + case 3: // Cart + // if a cart name is set, then remove it + if (tos_cartridge_is_inserted()) { + tos_load_cartridge(""); + menustate = MENU_MIST_SYSTEM1; + } + else + SelectFile("IMG", 0, MENU_MIST_SYSTEM_FILE_SELECTED, MENU_MIST_SYSTEM1, 0); + break; + + case 4: + menustate = MENU_MIST_SYSTEM1; + break; + + case 5: // Reset + tos_reset(0); + menustate = MENU_NONE1; + break; + + case 6: // Cold Boot + tos_reset(1); + menustate = MENU_NONE1; + break; + + case 7: + menustate = MENU_MIST_MAIN1; + menusub = 3; + break; + } + } + break; + + case MENU_MIST_SYSTEM_FILE_SELECTED: // file successfully selected + if (menusub == 2) { + tos_upload(SelectedPath); + menustate = MENU_MIST_SYSTEM1; + } + if (menusub == 3) { + tos_load_cartridge(SelectedPath); + menustate = MENU_MIST_SYSTEM1; + } + break; + + + case MENU_MIST_VIDEO1: + + menumask = 0x7f; + OsdSetTitle("A/V", 0); + + strcpy(s, " Screen: "); + if (tos_system_ctrl() & TOS_CONTROL_VIDEO_COLOR) strcat(s, "Color"); + else strcat(s, "Mono"); + OsdWrite(0, s, menusub == 0, 0); + + // Viking card can only be enabled with max 8MB RAM + enable = (tos_system_ctrl() & 0xe) <= TOS_MEMCONFIG_8M; + strcpy(s, " Viking/SM194: "); + strcat(s, ((tos_system_ctrl() & TOS_CONTROL_VIKING) && enable) ? "on" : "off"); + OsdWrite(1, s, menusub == 1, enable ? 0 : 1); + + // Blitter is always present in >= STE + enable = (tos_system_ctrl() & (TOS_CONTROL_STE | TOS_CONTROL_MSTE)) ? 1 : 0; + strcpy(s, " Blitter: "); + strcat(s, ((tos_system_ctrl() & TOS_CONTROL_BLITTER) || enable) ? "on" : "off"); + OsdWrite(2, s, menusub == 2, enable); + + strcpy(s, " Chipset: "); + // extract TOS_CONTROL_STE and TOS_CONTROL_MSTE bits + strcat(s, atari_chipset[(tos_system_ctrl() >> 23) & 3]); + OsdWrite(3, s, menusub == 3, 0); + + OsdWrite(4, " Video adjust \x16", menusub == 4, 0); + + strcpy(s, " YM-Audio: "); + strcat(s, stereo[(tos_system_ctrl() & TOS_CONTROL_STEREO) ? 1 : 0]); + OsdWrite(5, s, menusub == 5, 0); + OsdWrite(6, "", 0, 0); + + OsdWrite(7, STD_EXIT, menusub == 6, 0); + + parentstate = menustate; + menustate = MENU_MIST_VIDEO2; + break; + + case MENU_MIST_VIDEO2: + if (menu) { + menustate = MENU_MIST_MAIN1; + menusub = 4; + } + + if (select) { + switch (menusub) { + case 0: + tos_update_sysctrl(tos_system_ctrl() ^ TOS_CONTROL_VIDEO_COLOR); + menustate = MENU_MIST_VIDEO1; + break; + + case 1: + // viking/sm194 + tos_update_sysctrl(tos_system_ctrl() ^ TOS_CONTROL_VIKING); + menustate = MENU_MIST_VIDEO1; + break; + + case 2: + if (!(tos_system_ctrl() & TOS_CONTROL_STE)) { + tos_update_sysctrl(tos_system_ctrl() ^ TOS_CONTROL_BLITTER); + menustate = MENU_MIST_VIDEO1; + } + break; + + case 3: { + unsigned long chipset = (tos_system_ctrl() >> 23) + 1; + if (chipset == 4) chipset = 0; + tos_update_sysctrl(tos_system_ctrl() & ~(TOS_CONTROL_STE | TOS_CONTROL_MSTE) | + (chipset << 23)); + menustate = MENU_MIST_VIDEO1; + } break; + + case 4: + menustate = MENU_MIST_VIDEO_ADJUST1; + menusub = 0; + break; + + case 5: + tos_update_sysctrl(tos_system_ctrl() ^ TOS_CONTROL_STEREO); + menustate = MENU_MIST_VIDEO1; + break; + + case 6: + menustate = MENU_MIST_MAIN1; + menusub = 4; + break; + } + } + break; + + case MENU_MIST_VIDEO_ADJUST1: + + menumask = 0x1f; + OsdSetTitle("V-adjust", 0); + + OsdWrite(0, "", 0, 0); + + strcpy(s, " PAL mode: "); + if (tos_system_ctrl() & TOS_CONTROL_PAL50HZ) strcat(s, "50Hz"); + else strcat(s, "56Hz"); + OsdWrite(1, s, menusub == 0, 0); + + strcpy(s, " Scanlines: "); + strcat(s, scanlines[(tos_system_ctrl() >> 20) & 3]); + OsdWrite(2, s, menusub == 1, 0); + + OsdWrite(3, "", 0, 0); + + siprintf(s, " Horizontal: %d", tos_get_video_adjust(0)); + OsdWrite(4, s, menusub == 2, 0); + + siprintf(s, " Vertical: %d", tos_get_video_adjust(1)); + OsdWrite(5, s, menusub == 3, 0); + + OsdWrite(6, "", 0, 0); + + OsdWrite(7, STD_EXIT, menusub == 4, 0); + + parentstate = menustate; + menustate = MENU_MIST_VIDEO_ADJUST2; + break; + + case MENU_MIST_VIDEO_ADJUST2: + if (menu) { + menustate = MENU_MIST_VIDEO1; + menusub = 4; + } + + // use left/right to adjust video position + if (left || right) { + if ((menusub == 2) || (menusub == 3)) { + if (left && (tos_get_video_adjust(menusub - 2) > -100)) + tos_set_video_adjust(menusub - 2, -1); + + if (right && (tos_get_video_adjust(menusub - 2) < 100)) + tos_set_video_adjust(menusub - 2, +1); + + menustate = MENU_MIST_VIDEO_ADJUST1; + } + } + + if (select) { + switch (menusub) { + case 0: + tos_update_sysctrl(tos_system_ctrl() ^ TOS_CONTROL_PAL50HZ); + menustate = MENU_MIST_VIDEO_ADJUST1; + break; + + case 1: { + // next scanline state + int scan = ((tos_system_ctrl() >> 20) + 1) & 3; + tos_update_sysctrl((tos_system_ctrl() & ~TOS_CONTROL_SCANLINES) | (scan << 20)); + menustate = MENU_MIST_VIDEO_ADJUST1; + } break; + + // entries 2 and 3 use left/right + + case 4: + menustate = MENU_MIST_VIDEO1; + menusub = 4; + break; + } + } + break; + + /******************************************************************/ + /* minimig main menu */ + /******************************************************************/ + case MENU_MAIN1: + menumask = 0xFF0; // b01110000 Floppy turbo, Harddisk options & Exit. + OsdSetTitle("Minimig", OSD_ARROW_RIGHT); + helptext = helptexts[HELPTEXT_MAIN]; + + OsdWrite(0, "", 0, 0); + + // floppy drive info + // We display a line for each drive that's active + // in the config file, but grey out any that the FPGA doesn't think are active. + // We also print a help text in place of the last drive if it's inactive. + for (i = 0; i < 4; i++) + { + if (i == config.floppy.drives + 1) + OsdWrite(i+1, " KP +/- to add/remove drives", 0, 1); + else + { + strcpy(s, " dfx: "); + s[3] = i + '0'; + if (i <= drives) + { + menumask |= (1 << i); // Make enabled drives selectable + + if (df[i].status & DSK_INSERTED) // floppy disk is inserted + { + char *p; + if (p = strrchr(df[i].name, '/')) + { + p++; + } + else + { + p = df[i].name; + } + + int len = strlen(p); + if (len > 22) len = 21; + strncpy(&s[6], p, len); + s[6 + len] = ' '; + s[6 + len + 1] = 0; + s[6 + len + 2] = 0; + if (!(df[i].status & DSK_WRITABLE)) s[6 + len + 1] = '\x17'; // padlock icon for write-protected disks + } + else // no floppy disk + { + strcat(s, "* no disk *"); + } + } + else if (i <= config.floppy.drives) + { + strcat(s, "* active after reset *"); + } + else + strcpy(s, ""); + OsdWrite(i+1, s, menusub == i, (i>drives) || (i>config.floppy.drives)); + } + } + siprintf(s, " Floppy disk turbo : %s", config.floppy.speed ? "on" : "off"); + OsdWrite(5, s, menusub == 4, 0); + OsdWrite(6, "", 0, 0); + + OsdWrite(7, " Hard disk settings \x16", menusub == 5, 0); + OsdWrite(8, " chipset settings \x16", menusub == 6, 0); + OsdWrite(9, " memory settings \x16", menusub == 7, 0); + OsdWrite(10, " video settings \x16", menusub == 8, 0); + OsdWrite(11, "", 0, 0); + + OsdWrite(12, " save configuration", menusub == 9, 0); + OsdWrite(13, " load configuration", menusub == 10, 0); + OsdWrite(14, "", 0, 0); + + OsdWrite(15, STD_EXIT, menusub == 11, 0); + + menustate = MENU_MAIN2; + parentstate = MENU_MAIN1; + break; + + case MENU_MAIN2: + if (menu) + menustate = MENU_NONE1; + else if (plus && (config.floppy.drives<3)) + { + config.floppy.drives++; + ConfigFloppy(config.floppy.drives, config.floppy.speed); + menustate = MENU_MAIN1; + } + else if (minus && (config.floppy.drives>0)) + { + config.floppy.drives--; + ConfigFloppy(config.floppy.drives, config.floppy.speed); + menustate = MENU_MAIN1; + } + else if (select) + { + if (menusub < 4) + { + if (df[menusub].status & DSK_INSERTED) // eject selected floppy + { + df[menusub].status = 0; + menustate = MENU_MAIN1; + } + else + { + df[menusub].status = 0; + SelectFile("ADF", SCAN_DIR, MENU_FILE_SELECTED, MENU_MAIN1, 0); + } + } + else if (menusub == 4) // Toggle floppy turbo + { + config.floppy.speed ^= 1; + ConfigFloppy(config.floppy.drives, config.floppy.speed); + menustate = MENU_MAIN1; + } + else if (menusub == 5) // Go to harddrives page. + { + t_hardfile[0] = config.hardfile[0]; + t_hardfile[1] = config.hardfile[1]; + menustate = MENU_SETTINGS_HARDFILE1; + menusub = 0; + } + else if (menusub == 6) + { + menustate = MENU_SETTINGS_CHIPSET1; + menusub = 0; + } + else if (menusub == 7) + { + menustate = MENU_SETTINGS_MEMORY1; + menusub = 0; + } + else if (menusub == 8) + { + menustate = MENU_SETTINGS_VIDEO1; + menusub = 0; + } + else if (menusub == 9) + { + menusub = 0; + menustate = MENU_SAVECONFIG_1; + } + else if (menusub == 10) + { + menusub = 0; + menustate = MENU_LOADCONFIG_1; + } + else if (menusub == 11) + menustate = MENU_NONE1; + } + else if (c == KEY_BACK) // eject all floppies + { + for (i = 0; i <= drives; i++) + df[i].status = 0; + + menustate = MENU_MAIN1; + } + else if (right) + { + menustate = MENU_8BIT_SYSTEM1; + menusub = 0; + } + break; + + case MENU_FILE_SELECTED: // file successfully selected + + InsertFloppy(&df[menusub], SelectedPath); + menustate = MENU_MAIN1; + menusub++; + if (menusub > drives) + menusub = 6; + + break; + + case MENU_LOADCONFIG_1: + helptext = helptexts[HELPTEXT_NONE]; + if (parentstate != menustate) // First run? + { + menumask = 0x20; + SetConfigurationFilename(0); if (ConfigurationExists(0)) menumask |= 0x01; + SetConfigurationFilename(1); if (ConfigurationExists(0)) menumask |= 0x02; + SetConfigurationFilename(2); if (ConfigurationExists(0)) menumask |= 0x04; + SetConfigurationFilename(3); if (ConfigurationExists(0)) menumask |= 0x08; + SetConfigurationFilename(4); if (ConfigurationExists(0)) menumask |= 0x10; + } + parentstate = menustate; + OsdSetTitle("Load", 0); + + OsdWrite(0, "", 0, 0); + OsdWrite(1, " Default", menusub == 0, (menumask & 1) == 0); + OsdWrite(2, " 1", menusub == 1, (menumask & 2) == 0); + OsdWrite(3, " 2", menusub == 2, (menumask & 4) == 0); + OsdWrite(4, " 3", menusub == 3, (menumask & 8) == 0); + OsdWrite(5, " 4", menusub == 4, (menumask & 0x10) == 0); + OsdWrite(6, "", 0, 0); + for (int i = 7; i < OsdGetSize() - 1; i++) OsdWrite(i, "", 0, 0); + OsdWrite(OsdGetSize() - 1, STD_EXIT, menusub == 5, 0); + + menustate = MENU_LOADCONFIG_2; + break; + + case MENU_LOADCONFIG_2: + + if (down) + { + // if (menusub < 3) + if (menusub < 5) + menusub++; + menustate = MENU_LOADCONFIG_1; + } + else if (select) + { + if (menusub<5) + { + OsdDisable(); + SetConfigurationFilename(menusub); + LoadConfiguration(NULL); + // OsdReset(RESET_NORMAL); + menustate = MENU_NONE1; + } + else + { + menustate = MENU_MAIN1; + menusub = 10; + } + } + if (menu) // exit menu + { + menustate = MENU_MAIN1; + menusub = 10; + } + break; + + /******************************************************************/ + /* file selection menu */ + /******************************************************************/ + case MENU_FILE_SELECT1: + helptext = helptexts[HELPTEXT_NONE]; + OsdSetTitle("Select", 0); + PrintDirectory(); + menustate = MENU_FILE_SELECT2; + break; + + case MENU_FILE_SELECT2: + menumask = 0; + + ScrollLongName(); // scrolls file name if longer than display line + + if (c == KEY_HOME) + { + ScanDirectory(SelectedPath, SCAN_INIT, fs_pFileExt, fs_Options); + menustate = MENU_FILE_SELECT1; + } + + if (c == KEY_BACK) + { + changeDir(".."); + menustate = MENU_FILE_SELECT1; + } + + if ((c == KEY_PGUP) || (c == KEY_LEFT)) + { + ScanDirectory(SelectedPath, SCAN_PREV_PAGE, fs_pFileExt, fs_Options); + menustate = MENU_FILE_SELECT1; + } + + if ((c == KEY_PGDN) || (c == KEY_RIGHT)) + { + ScanDirectory(SelectedPath, SCAN_NEXT_PAGE, fs_pFileExt, fs_Options); + menustate = MENU_FILE_SELECT1; + } + + if (down) // scroll down one entry + { + ScanDirectory(SelectedPath, SCAN_NEXT, fs_pFileExt, fs_Options); + menustate = MENU_FILE_SELECT1; + } + + if (up) // scroll up one entry + { + ScanDirectory(SelectedPath, SCAN_PREV, fs_pFileExt, fs_Options); + menustate = MENU_FILE_SELECT1; + } + + if ((i = GetASCIIKey(c))) + { + // find an entry beginning with given character + ScanDirectory(SelectedPath, i, fs_pFileExt, fs_Options); + menustate = MENU_FILE_SELECT1; + } + + if (select) + { + if(DirItem[iSelectedEntry].d_type == DT_DIR) + { + changeDir(DirItem[iSelectedEntry].d_name); + menustate = MENU_FILE_SELECT1; + } + else + { + if (nDirEntries) + { + if(strlen(SelectedPath)) strcat(SelectedPath, "/"); + strcat(SelectedPath, DirItem[iSelectedEntry].d_name); + + menustate = fs_MenuSelect; + } + } + } + + if (menu) + { + menustate = fs_MenuCancel; + } + + break; + + /******************************************************************/ + /* reset menu */ + /******************************************************************/ + case MENU_RESET1: + m = 0; + if (user_io_core_type() == CORE_TYPE_MINIMIG2) m = 1; + helptext = helptexts[HELPTEXT_NONE]; + OsdSetTitle("Reset", 0); + menumask = 0x03; // Yes / No + parentstate = menustate; + + OsdWrite(0, "", 0, 0); + OsdWrite(1, m ? " Reset MiST?" : " Reset settings?", 0, 0); + OsdWrite(2, "", 0, 0); + OsdWrite(3, " yes", menusub == 0, 0); + OsdWrite(4, " no", menusub == 1, 0); + OsdWrite(5, "", 0, 0); + OsdWrite(6, "", 0, 0); + for (int i = 7; i < OsdGetSize(); i++) OsdWrite(i, "", 0, 0); + + menustate = MENU_RESET2; + break; + + case MENU_RESET2: + + m = 0; + if (user_io_core_type() == CORE_TYPE_MINIMIG2) m = 1; + + if (select && menusub == 0) + { + if (m) { + menustate = MENU_NONE1; + OsdReset(RESET_NORMAL); + } + else { + char *filename = user_io_create_config_name(); + unsigned long status = user_io_8bit_set_status(0, 0xffffffff); + iprintf("Saving config to %s\n", filename); + FileSave(filename, &status, 4); + menustate = MENU_8BIT_MAIN1; + menusub = 0; + } + } + + if (menu || (select && (menusub == 1))) // exit menu + { + menustate = MENU_8BIT_SYSTEM1; + menusub = 3; + } + break; + + case MENU_SAVECONFIG_1: + helptext = helptexts[HELPTEXT_NONE]; + menumask = 0x3f; + parentstate = menustate; + OsdSetTitle("Save", 0); + + OsdWrite(0, "", 0, 0); + OsdWrite(1, " Default", menusub == 0, 0); + OsdWrite(2, " 1", menusub == 1, 0); + OsdWrite(3, " 2", menusub == 2, 0); + OsdWrite(4, " 3", menusub == 3, 0); + OsdWrite(5, " 4", menusub == 4, 0); + OsdWrite(6, "", 0, 0); + for (int i = 7; i < OsdGetSize() - 1; i++) OsdWrite(i, "", 0, 0); + OsdWrite(OsdGetSize() - 1, STD_EXIT, menusub == 5, 0); + + menustate = MENU_SAVECONFIG_2; + break; + + case MENU_SAVECONFIG_2: + + if (menu) + { + menustate = MENU_MAIN1; + menusub = 9; + } + + else if (up) + { + if (menusub > 0) + menusub--; + menustate = MENU_SAVECONFIG_1; + } + else if (down) + { + // if (menusub < 3) + if (menusub < 5) + menusub++; + menustate = MENU_SAVECONFIG_1; + } + else if (select) + { + if (menusub<5) + { + SetConfigurationFilename(menusub); + SaveConfiguration(NULL); + menustate = MENU_NONE1; + } + else + { + menustate = MENU_MAIN1; + menusub = 9; + } + } + if (menu) // exit menu + { + menustate = MENU_MAIN1; + menusub = 9; + } + break; + + + + /******************************************************************/ + /* chipset settings menu */ + /******************************************************************/ + case MENU_SETTINGS_CHIPSET1: + helptext = helptexts[HELPTEXT_CHIPSET]; + menumask = 0; + OsdSetTitle("Chipset", OSD_ARROW_LEFT | OSD_ARROW_RIGHT); + + OsdWrite(0, "", 0, 0); + strcpy(s, " CPU : "); + strcat(s, config_cpu_msg[config.cpu & 0x03]); + OsdWrite(1, s, menusub == 0, 0); + strcpy(s, " Turbo : "); + strcat(s, config_turbo_msg[(config.cpu >> 2) & 0x03]); + OsdWrite(2, s, menusub == 1, 0); + strcpy(s, " Video : "); + strcat(s, config.chipset & CONFIG_NTSC ? "NTSC" : "PAL"); + OsdWrite(3, s, menusub == 2, 0); + strcpy(s, " Chipset : "); + strcat(s, config_chipset_msg[(config.chipset >> 2) & 7]); + OsdWrite(4, s, menusub == 3, 0); + strcpy(s, " CD32Pad : "); + strcat(s, config_cd32pad_msg[(config.autofire >> 2) & 1]); + OsdWrite(5, s, menusub == 4, 0); + OsdWrite(6, "", 0, 0); + for (int i = 7; i < OsdGetSize() - 1; i++) OsdWrite(i, "", 0, 0); + OsdWrite(OsdGetSize() - 1, STD_EXIT, menusub == 5, 0); + + menustate = MENU_SETTINGS_CHIPSET2; + break; + + case MENU_SETTINGS_CHIPSET2: + + if (down && menusub < 5) + { + menusub++; + menustate = MENU_SETTINGS_CHIPSET1; + } + + if (up && menusub > 0) + { + menusub--; + menustate = MENU_SETTINGS_CHIPSET1; + } + + if (select) + { + if (menusub == 0) + { + menustate = MENU_SETTINGS_CHIPSET1; + int _config_cpu = config.cpu & 0x3; + _config_cpu += 1; + if (_config_cpu == 0x02) _config_cpu += 1; + config.cpu = (config.cpu & 0xfc) | (_config_cpu & 0x3); + ConfigCPU(config.cpu); + } + else if (menusub == 1) + { + menustate = MENU_SETTINGS_CHIPSET1; + int _config_turbo = (config.cpu >> 2) & 0x3; + _config_turbo += 1; + config.cpu = (config.cpu & 0x3) | ((_config_turbo & 0x3) << 2); + ConfigCPU(config.cpu); + } + else if (menusub == 2) + { + config.chipset ^= CONFIG_NTSC; + menustate = MENU_SETTINGS_CHIPSET1; + ConfigChipset(config.chipset); + } + else if (menusub == 3) + { + switch (config.chipset & 0x1c) { + case 0: + config.chipset = (config.chipset & 3) | CONFIG_A1000; + break; + case CONFIG_A1000: + config.chipset = (config.chipset & 3) | CONFIG_ECS; + break; + case CONFIG_ECS: + config.chipset = (config.chipset & 3) | CONFIG_AGA | CONFIG_ECS; + break; + case (CONFIG_AGA | CONFIG_ECS) : + config.chipset = (config.chipset & 3) | 0; + break; + } + + menustate = MENU_SETTINGS_CHIPSET1; + ConfigChipset(config.chipset); + } + else if (menusub == 4) + { + //config.autofire = ((((config.autofire >> 2) + 1) & 1) << 2) || (config.autofire & 3); + config.autofire = (config.autofire + 4) & 0x7; + menustate = MENU_SETTINGS_CHIPSET1; + ConfigAutofire(config.autofire); + } + else if (menusub == 5) + { + menustate = MENU_MAIN1; + menusub = 6; + } + } + + if (menu) + { + menustate = MENU_MAIN1; + menusub = 6; + } + else if (right) + { + menustate = MENU_SETTINGS_MEMORY1; + menusub = 0; + } + else if (left) + { + menustate = MENU_SETTINGS_VIDEO1; + menusub = 0; + } + break; + + /******************************************************************/ + /* memory settings menu */ + /******************************************************************/ + case MENU_SETTINGS_MEMORY1: + helptext = helptexts[HELPTEXT_MEMORY]; + menumask = 0x3f; + parentstate = menustate; + + OsdSetTitle("Memory", OSD_ARROW_LEFT | OSD_ARROW_RIGHT); + + OsdWrite(0, "", 0, 0); + strcpy(s, " CHIP : "); + strcat(s, config_memory_chip_msg[config.memory & 0x03]); + OsdWrite(1, s, menusub == 0, 0); + strcpy(s, " SLOW : "); + strcat(s, config_memory_slow_msg[config.memory >> 2 & 0x03]); + OsdWrite(2, s, menusub == 1, 0); + strcpy(s, " FAST : "); + strcat(s, config_memory_fast_msg[config.memory >> 4 & 0x03]); + OsdWrite(3, s, menusub == 2, 0); + + OsdWrite(4, "", 0, 0); + + strcpy(s, " ROM : "); + strncat(s, config.kickstart, 15); + OsdWrite(5, s, menusub == 3, 0); + + strcpy(s, " HRTmon: "); + strcat(s, (config.memory & 0x40) ? "enabled " : "disabled"); + OsdWrite(6, s, menusub == 4, 0); + + for (int i = 7; i < OsdGetSize() - 1; i++) OsdWrite(i, "", 0, 0); + OsdWrite(OsdGetSize() - 1, STD_EXIT, menusub == 5, 0); + + menustate = MENU_SETTINGS_MEMORY2; + break; + + case MENU_SETTINGS_MEMORY2: + if (select) + { + if (menusub == 0) + { + config.memory = ((config.memory + 1) & 0x03) | (config.memory & ~0x03); + menustate = MENU_SETTINGS_MEMORY1; + ConfigMemory(config.memory); + } + else if (menusub == 1) + { + config.memory = ((config.memory + 4) & 0x0C) | (config.memory & ~0x0C); + menustate = MENU_SETTINGS_MEMORY1; + ConfigMemory(config.memory); + } + else if (menusub == 2) + { + config.memory = ((config.memory + 0x10) & 0x30) | (config.memory & ~0x30); + // if ((config.memory & 0x30) == 0x30) + // config.memory -= 0x30; + // if (!(config.disable_ar3 & 0x01)&&(config.memory & 0x20)) + // config.memory &= ~0x30; + menustate = MENU_SETTINGS_MEMORY1; + ConfigMemory(config.memory); + } + else if (menusub == 3) + { + SelectFile("ROM", 0, MENU_ROMFILE_SELECTED, MENU_SETTINGS_MEMORY1, 0); + } + else if (menusub == 4) + { + config.memory ^= 0x40; + ConfigMemory(config.memory); + //if (!(config.disable_ar3 & 0x01)||(config.memory & 0x20)) + // config.disable_ar3 |= 0x01; + //else + // config.disable_ar3 &= 0xFE; + menustate = MENU_SETTINGS_MEMORY1; + } + else if (menusub == 5) + { + menustate = MENU_MAIN1; + menusub = 7; + } + } + + if (menu) + { + menustate = MENU_MAIN1; + menusub = 7; + } + else if (right) + { + menustate = MENU_SETTINGS_VIDEO1; + menusub = 0; + } + else if (left) + { + menustate = MENU_SETTINGS_CHIPSET1; + menusub = 0; + } + break; + + /******************************************************************/ + /* hardfile settings menu */ + /******************************************************************/ + + // FIXME! Nasty race condition here. Changing HDF type has immediate effect + // which could be disastrous if the user's writing to the drive at the time! + // Make the menu work on the copy, not the original, and copy on acceptance, + // not on rejection. + case MENU_SETTINGS_HARDFILE1: + helptext = helptexts[HELPTEXT_HARDFILE]; + OsdSetTitle("Harddisks", 0); + + parentstate = menustate; + menumask = 0x21; // b00100001 - On/off & exit enabled by default... + if (config.enable_ide) + menumask |= 0x0a; // b00001010 - HD0 and HD1 type + strcpy(s, " A600/A1200 IDE : "); + strcat(s, config.enable_ide ? "on " : "off"); + OsdWrite(1, s, menusub == 0, 0); + OsdWrite(2, "", 0, 0); + + strcpy(s, " Master : "); + if (config.hardfile[0].enabled == (HDF_FILE | HDF_SYNTHRDB)) + strcat(s, "Hardfile (filesys)"); + else + strcat(s, config_hdf_msg[config.hardfile[0].enabled & HDF_TYPEMASK]); + OsdWrite(3, s, config.enable_ide ? (menusub == 1) : 0, config.enable_ide == 0); + if (config.hardfile[0].present) + { + strcpy(s, " "); + strncpy(&s[14], config.hardfile[0].long_name, 15); + } + else + strcpy(s, " ** file not found **"); + + enable = config.enable_ide && ((config.hardfile[0].enabled&HDF_TYPEMASK) == HDF_FILE); + if (enable) + menumask |= 0x04; // Make hardfile selectable + OsdWrite(4, s, enable ? (menusub == 2) : 0, enable == 0); + + OsdWrite(5, "", 0, 0); + strcpy(s, " Slave : "); + if (config.hardfile[1].enabled == (HDF_FILE | HDF_SYNTHRDB)) + strcat(s, "Hardfile (filesys)"); + else + strcat(s, config_hdf_msg[config.hardfile[1].enabled & HDF_TYPEMASK]); + OsdWrite(6, s, config.enable_ide ? (menusub == 3) : 0, config.enable_ide == 0); + if (config.hardfile[1].present) { + strcpy(s, " "); + strncpy(&s[14], config.hardfile[1].long_name, 15); + } + else + strcpy(s, " ** file not found **"); + enable = config.enable_ide && ((config.hardfile[1].enabled&HDF_TYPEMASK) == HDF_FILE); + if (enable) + menumask |= 0x10; // Make hardfile selectable + OsdWrite(7, s, enable ? (menusub == 4) : 0, enable == 0); + + for (int i = 8; i < OsdGetSize() - 1; i++) OsdWrite(i, "", 0, 0); + OsdWrite(OsdGetSize() - 1, STD_EXIT, menusub == 5, 0); + + menustate = MENU_SETTINGS_HARDFILE2; + + break; + + case MENU_SETTINGS_HARDFILE2: + if (select) + { + if (menusub == 0) + { + config.enable_ide = (config.enable_ide == 0); + menustate = MENU_SETTINGS_HARDFILE1; + } + if (menusub == 1) + { + if (config.hardfile[0].enabled == HDF_FILE) + { + config.hardfile[0].enabled |= HDF_SYNTHRDB; + } + else if (config.hardfile[0].enabled == (HDF_FILE | HDF_SYNTHRDB)) + { + config.hardfile[0].enabled = 0; + } + else + { + config.hardfile[0].enabled = HDF_FILE; + } + menustate = MENU_SETTINGS_HARDFILE1; + } + else if (menusub == 2) + { + SelectFile("HDF", 0, MENU_HARDFILE_SELECTED, MENU_SETTINGS_HARDFILE1, 0); + } + else if (menusub == 3) + { + if (config.hardfile[1].enabled == HDF_FILE) + { + config.hardfile[1].enabled |= HDF_SYNTHRDB; + } + else if (config.hardfile[1].enabled == (HDF_FILE | HDF_SYNTHRDB)) + { + config.hardfile[1].enabled = 0; + } + else + { + config.hardfile[1].enabled = HDF_FILE; + } + menustate = MENU_SETTINGS_HARDFILE1; + } + else if (menusub == 4) + { + SelectFile("HDF", 0, MENU_HARDFILE_SELECTED, MENU_SETTINGS_HARDFILE1, 0); + } + else if (menusub == 5) // return to previous menu + { + menustate = MENU_HARDFILE_EXIT; + } + } + + if (menu) // return to previous menu + { + menustate = MENU_HARDFILE_EXIT; + } + break; + + /******************************************************************/ + /* hardfile selected menu */ + /******************************************************************/ + case MENU_HARDFILE_SELECTED: + if (menusub == 2) // master drive selected + { + // Read RDB from selected drive and determine type... + memcpy((void*)config.hardfile[0].long_name, SelectedPath, sizeof(config.hardfile[0].long_name)); + switch (GetHDFFileType(SelectedPath)) + { + case HDF_FILETYPE_RDB: + config.hardfile[0].enabled = HDF_FILE; + config.hardfile[0].present = 1; + menustate = MENU_SETTINGS_HARDFILE1; + break; + case HDF_FILETYPE_DOS: + config.hardfile[0].enabled = HDF_FILE | HDF_SYNTHRDB; + config.hardfile[0].present = 1; + menustate = MENU_SETTINGS_HARDFILE1; + break; + case HDF_FILETYPE_UNKNOWN: + config.hardfile[0].present = 1; + if (config.hardfile[0].enabled == HDF_FILE) // Warn if we can't detect the type + menustate = MENU_SYNTHRDB1; + else + menustate = MENU_SYNTHRDB2_1; + menusub = 0; + break; + case HDF_FILETYPE_NOTFOUND: + default: + config.hardfile[0].present = 0; + menustate = MENU_SETTINGS_HARDFILE1; + break; + } + } + + if (menusub == 4) // slave drive selected + { + memcpy((void*)config.hardfile[1].long_name, SelectedPath, sizeof(config.hardfile[1].long_name)); + switch (GetHDFFileType(SelectedPath)) + { + case HDF_FILETYPE_RDB: + config.hardfile[1].enabled = HDF_FILE; + config.hardfile[1].present = 1; + menustate = MENU_SETTINGS_HARDFILE1; + break; + case HDF_FILETYPE_DOS: + config.hardfile[1].enabled = HDF_FILE | HDF_SYNTHRDB; + config.hardfile[1].present = 1; + menustate = MENU_SETTINGS_HARDFILE1; + break; + case HDF_FILETYPE_UNKNOWN: + config.hardfile[1].present = 1; + if (config.hardfile[1].enabled == HDF_FILE) // Warn if we can't detect the type... + menustate = MENU_SYNTHRDB1; + else + menustate = MENU_SYNTHRDB2_1; + menusub = 0; + break; + case HDF_FILETYPE_NOTFOUND: + default: + config.hardfile[1].present = 0; + menustate = MENU_SETTINGS_HARDFILE1; + break; + } + } + break; + + // check if hardfile configuration has changed + case MENU_HARDFILE_EXIT: + + if (memcmp(config.hardfile, t_hardfile, sizeof(t_hardfile)) != 0) + { + menustate = MENU_HARDFILE_CHANGED1; + menusub = 1; + } + else + { + menustate = MENU_MAIN1; + menusub = 5; + } + + break; + + // hardfile configuration has changed, ask user if he wants to use the new settings + case MENU_HARDFILE_CHANGED1: + menumask = 0x03; + parentstate = menustate; + OsdSetTitle("Confirm", 0); + + OsdWrite(0, "", 0, 0); + OsdWrite(1, " Changing configuration", 0, 0); + OsdWrite(2, " requires reset.", 0, 0); + OsdWrite(3, "", 0, 0); + OsdWrite(4, " Reset Minimig?", 0, 0); + OsdWrite(5, "", 0, 0); + OsdWrite(6, " yes", menusub == 0, 0); + OsdWrite(7, " no", menusub == 1, 0); + + for (int i = 8; i < OsdGetSize(); i++) OsdWrite(i, "", 0, 0); + + menustate = MENU_HARDFILE_CHANGED2; + break; + + case MENU_HARDFILE_CHANGED2: + if (select) + { + if (menusub == 0) // yes + { + // FIXME - waiting for user-confirmation increases the window of opportunity for file corruption! + + if ((config.hardfile[0].enabled != t_hardfile[0].enabled) + || (strcmp(config.hardfile[0].long_name, t_hardfile[0].long_name) != 0)) + { + OpenHardfile(0); + // if((config.hardfile[0].enabled == HDF_FILE) && !FindRDB(0)) + // menustate = MENU_SYNTHRDB1; + } + if (config.hardfile[1].enabled != t_hardfile[1].enabled + || (strcmp(config.hardfile[1].long_name, t_hardfile[1].long_name) != 0)) + { + OpenHardfile(1); + // if((config.hardfile[1].enabled == HDF_FILE) && !FindRDB(1)) + // menustate = MENU_SYNTHRDB2_1; + } + + if (menustate == MENU_HARDFILE_CHANGED2) + { + ConfigIDE(config.enable_ide, config.hardfile[0].present && config.hardfile[0].enabled, config.hardfile[1].present && config.hardfile[1].enabled); + OsdReset(RESET_NORMAL); + + menustate = MENU_NONE1; + } + } + else if (menusub == 1) // no + { + memcpy(config.hardfile, t_hardfile, sizeof(t_hardfile)); // restore configuration + menustate = MENU_MAIN1; + menusub = 3; + } + } + + if (menu) + { + memcpy(config.hardfile, t_hardfile, sizeof(t_hardfile)); // restore configuration + menustate = MENU_MAIN1; + menusub = 3; + } + break; + + case MENU_SYNTHRDB1: + menumask = 0x01; + parentstate = menustate; + OsdSetTitle("Warning", 0); + OsdWrite(0, "", 0, 0); + OsdWrite(1, " No partition table found -", 0, 0); + OsdWrite(2, " Hardfile image may need", 0, 0); + OsdWrite(3, " to be prepped with HDToolbox,", 0, 0); + OsdWrite(4, " then formatted.", 0, 0); + OsdWrite(5, "", 0, 0); + OsdWrite(6, "", 0, 0); + OsdWrite(7, " OK", menusub == 0, 0); + + for (int i = 8; i < OsdGetSize(); i++) OsdWrite(i, "", 0, 0); + + menustate = MENU_SYNTHRDB2; + break; + + + case MENU_SYNTHRDB2_1: + + menumask = 0x01; + parentstate = menustate; + OsdSetTitle("Warning", 0); + OsdWrite(0, "", 0, 0); + OsdWrite(1, " No filesystem recognised.", 0, 0); + OsdWrite(2, " Hardfile may need formatting", 0, 0); + OsdWrite(3, " (or may simply be an", 0, 0); + OsdWrite(4, " unrecognised filesystem)", 0, 0); + OsdWrite(5, "", 0, 0); + OsdWrite(6, "", 0, 0); + OsdWrite(7, " OK", menusub == 0, 0); + + for (int i = 8; i < OsdGetSize(); i++) OsdWrite(i, "", 0, 0); + + menustate = MENU_SYNTHRDB2; + break; + + + case MENU_SYNTHRDB2: + if (select || menu) + { + if (menusub == 0) // OK + menustate = MENU_SETTINGS_HARDFILE1; + } + break; + + + /******************************************************************/ + /* video settings menu */ + /******************************************************************/ + case MENU_SETTINGS_VIDEO1: + menumask = 0xf; + parentstate = menustate; + helptext = helptexts[HELPTEXT_VIDEO]; + + OsdSetTitle("Video", OSD_ARROW_LEFT | OSD_ARROW_RIGHT); + OsdWrite(0, "", 0, 0); + OsdWrite(1, "", 0, 0); + strcpy(s, " Scanlines : "); + strcat(s, config_scanlines_msg[config.scanlines & 0x3]); + OsdWrite(2, s, menusub == 0, 0); + strcpy(s, " Video area by : "); + strcat(s, config_blank_msg[(config.scanlines >> 6) & 3]); + OsdWrite(3, s, menusub == 1, 0); + strcpy(s, " Aspect Ratio : "); + strcat(s, config_ar_msg[(config.scanlines >> 4) & 1]); + OsdWrite(4, s, menusub == 2, 0); + OsdWrite(5, "", 0, 0); + OsdWrite(6, "", 0, 0); + for (int i = 7; i < OsdGetSize() - 1; i++) OsdWrite(i, "", 0, 0); + OsdWrite(OsdGetSize() - 1, STD_EXIT, menusub == 3, 0); + + menustate = MENU_SETTINGS_VIDEO2; + break; + + case MENU_SETTINGS_VIDEO2: + if (select) + { + if (menusub == 0) + { + config.scanlines = ((config.scanlines + 1) & 0x03) | (config.scanlines & 0xfc); + if ((config.scanlines & 0x03) > 2) + config.scanlines = config.scanlines & 0xfc; + menustate = MENU_SETTINGS_VIDEO1; + ConfigVideo(config.filter.hires, config.filter.lores, config.scanlines); + } + else if (menusub == 1) + { + config.scanlines &= ~0x80; + config.scanlines ^= 0x40; + menustate = MENU_SETTINGS_VIDEO1; + ConfigVideo(config.filter.hires, config.filter.lores, config.scanlines); + } + else if (menusub == 2) + { + config.scanlines &= ~0x20; // reserved for auto-ar + config.scanlines ^= 0x10; + menustate = MENU_SETTINGS_VIDEO1; + ConfigVideo(config.filter.hires, config.filter.lores, config.scanlines); + } + else if (menusub == 3) + { + menustate = MENU_MAIN1; + menusub = 8; + } + } + + if (menu) + { + menustate = MENU_MAIN1; + menusub = 8; + } + else if (right) + { + menustate = MENU_SETTINGS_CHIPSET1; + menusub = 0; + } + else if (left) + { + menustate = MENU_SETTINGS_MEMORY1; + menusub = 0; + } + break; + + /******************************************************************/ + /* rom file selected menu */ + /******************************************************************/ + case MENU_ROMFILE_SELECTED: + menusub = 1; + menustate = MENU_ROMFILE_SELECTED1; + // no break intended + + case MENU_ROMFILE_SELECTED1: + menumask = 0x03; + parentstate = menustate; + OsdSetTitle("Confirm", 0); + OsdWrite(0, "", 0, 0); + OsdWrite(1, " Reload Kickstart?", 0, 0); + OsdWrite(2, "", 0, 0); + OsdWrite(3, " yes", menusub == 0, 0); + OsdWrite(4, " no", menusub == 1, 0); + OsdWrite(5, "", 0, 0); + OsdWrite(6, "", 0, 0); + OsdWrite(7, "", 0, 0); + for (int i = 8; i < OsdGetSize(); i++) OsdWrite(i, "", 0, 0); + + menustate = MENU_ROMFILE_SELECTED2; + break; + + case MENU_ROMFILE_SELECTED2: + + if (select) + { + if (menusub == 0) + { + memcpy((void*)config.kickstart, SelectedPath, sizeof(config.kickstart)); + // reset bootscreen cursor position + BootHome(); + OsdDisable(); + EnableOsd(); + spi8(OSD_CMD_RST); + rstval = (SPI_RST_CPU | SPI_CPU_HLT); + spi8(rstval); + DisableOsd(); + UploadKickstart(config.kickstart); + 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(); + + menustate = MENU_NONE1; + } + else if (menusub == 1) + { + menustate = MENU_SETTINGS_MEMORY1; + menusub = 2; + } + } + + if (menu) + { + menustate = MENU_SETTINGS_MEMORY1; + menusub = 2; + } + break; + + /******************************************************************/ + /* firmware menu */ + /******************************************************************/ + case MENU_FIRMWARE1: + helptext = helptexts[HELPTEXT_NONE]; + parentstate = menustate; + + menumask = 3; + + OsdSetTitle("FW & Core", 0); + //OsdWrite(0, "", 0, 0); + siprintf(s, " ARM s/w ver. %s", version + 5); + OsdWrite(0, "", 0, 0); + OsdWrite(1, s, 0, 0); + + if (is_menu_core()) + { + OsdWrite(2, "", 0, 0); + if (getStorage(0)) + { + OsdWrite(3, " Using USB storage", 0, 0); + OsdWrite(5, " Switch to SD card", menusub == 0, 0); + } + else + { + if (getStorage(1)) + { + OsdWrite(3, " No USB found, using SD card", 0, 0); + OsdWrite(5, " Switch to SD card", menusub == 0, 0); + } + else + { + OsdWrite(3, " Using SD card", 0, 0); + OsdWrite(5, " Switch to USB storage", menusub == 0, !isUSBMounted()); + } + } + OsdWrite(4, "", 0, 0); + OsdWrite(6, "", 0, 0); + OsdWrite(7, " NOTE:", 0, 0); + OsdWrite(8, " USB storage takes longer", 0, 0); + OsdWrite(9, " time to initialize", 0, 0); + OsdWrite(10, " upon cold boot.", 0, 0); + OsdWrite(11, " Use OSD or USER button to", 0, 0); + OsdWrite(12, " cancel USB waiting", 0, 0); + OsdWrite(13, " and use SD instead.", 0, 0); + OsdWrite(14, "", 0, 0); + menustate = MENU_STORAGE; + } + else + { + OsdWrite(2, "", 0, 0); + OsdWrite(3, "", 0, 0); + int len = strlen(OsdCoreName()); + if (len > 30) len = 30; + int sp = (30 - len) / 2; + s[0] = 0; + for (int i = 0; i < sp; i++) strcat(s, " "); + char *s2 = s + strlen(s); + char *s3 = OsdCoreName(); + for (int i = 0; i < len; i++) *s2++ = *s3++; + *s2++ = 0; + OsdWrite(4, s, 0, 0); + OsdWrite(5, "", 0, 0); + OsdWrite(6, " Change FPGA core", menusub == 0, 0); + for (int i = 7; i < OsdGetSize() - 1; i++) OsdWrite(i, "", 0, 0); + menustate = MENU_FIRMWARE2; + } + OsdWrite(OsdGetSize() - 1, STD_EXIT, menusub == 1, 0); + break; + + case MENU_STORAGE: + if (menu || ((menusub == 1) && select)) + { + switch (user_io_core_type()) { + case CORE_TYPE_MIST: + menusub = 5; + menustate = MENU_MIST_MAIN1; + break; + case CORE_TYPE_ARCHIE: + menusub = 3; + menustate = MENU_ARCHIE_MAIN1; + break; + default: + menusub = 0; + menustate = MENU_NONE1; + break; + } + } + else if (select) + { + if (menusub == 0) + { + if(getStorage(1) || isUSBMounted()) + { + setStorage(!getStorage(1)); + } + } + } + break; + + case MENU_FIRMWARE2: + if (menu) { + switch (user_io_core_type()) { + case CORE_TYPE_MIST: + menusub = 5; + menustate = MENU_MIST_MAIN1; + break; + case CORE_TYPE_ARCHIE: + menusub = 3; + menustate = MENU_ARCHIE_MAIN1; + break; + default: + menusub = 0; + menustate = (is_menu_core()) ? MENU_NONE1 : MENU_8BIT_SYSTEM1; + break; + } + } + else if (select) { + if (menusub == 0) { + SelectFile("RBF", 0, MENU_FIRMWARE_CORE_FILE_SELECTED, MENU_FIRMWARE1, 0); + } + else if (menusub == 1) { + switch (user_io_core_type()) { + case CORE_TYPE_MIST: + menusub = 5; + menustate = MENU_MIST_MAIN1; + break; + case CORE_TYPE_ARCHIE: + menusub = 3; + menustate = MENU_ARCHIE_MAIN1; + break; + default: + menusub = 0; + menustate = (is_menu_core()) ? MENU_NONE1 : MENU_8BIT_SYSTEM1; + break; + } + } + } + break; + + case MENU_FIRMWARE_CORE_FILE_SELECTED: + // close OSD now as the new core may not even have one + OsdDisable(); + + fpga_load_rbf(SelectedPath); + + menustate = MENU_NONE1; + break; + + /******************************************************************/ + /* error message menu */ + /******************************************************************/ + case MENU_ERROR: + if (menu) + menustate = MENU_NONE1; + break; + + /******************************************************************/ + /* popup info menu */ + /******************************************************************/ + case MENU_INFO: + + if (menu) + menustate = MENU_NONE1; + else if (CheckTimer(menu_timer)) + menustate = MENU_NONE1; + + break; + + /******************************************************************/ + /* we should never come here */ + /******************************************************************/ + default: + break; + } +} + +void ScrollLongName(void) +{ + // this function is called periodically when file selection window is displayed + // it checks if predefined period of time has elapsed and scrolls the name if necessary + + static int len; + int max_len; + + len = strlen(DirItem[iSelectedEntry].d_name); // get name length + if (DirItem[iSelectedEntry].d_type == DT_REG) // if a file + { + if (fs_ExtLen <= 3) + { + char e[5]; + memcpy(e + 1, fs_pFileExt, 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(DirItem[iSelectedEntry].d_name + len - l, e, l)) len -= l; + } + } + + max_len = 30; // number of file name characters to display (one more required for scrolling) + if (DirItem[iSelectedEntry].d_type == DT_DIR) + max_len = 25; // number of directory name characters to display + + ScrollText(iSelectedEntry-iFirstEntry, DirItem[iSelectedEntry].d_name, 2, len, max_len, 1); +} + +char* GetDiskInfo(char* lfn, long len) +{ + // extracts disk number substring form file name + // if file name contains "X of Y" substring where X and Y are one or two digit number + // then the number substrings are extracted and put into the temporary buffer for further processing + // comparision is case sensitive + + short i, k; + static char info[] = "XX/XX"; // temporary buffer + static char template[4] = " of "; // template substring to search for + char *ptr1, *ptr2, c; + unsigned char cmp; + + if (len > 20) // scan only names which can't be fully displayed + { + for (i = (unsigned short)len - 1 - sizeof(template); i > 0; i--) // scan through the file name starting from its end + { + ptr1 = &lfn[i]; // current start position + ptr2 = template; + cmp = 0; + for (k = 0; k < sizeof(template); k++) // scan through template + { + cmp |= *ptr1++ ^ *ptr2++; // compare substrings' characters one by one + if (cmp) + break; // stop further comparing if difference already found + } + + if (!cmp) // match found + { + k = i - 1; // no need to check if k is valid since i is greater than zero + + c = lfn[k]; // get the first character to the left of the matched template substring + if (c >= '0' && c <= '9') // check if a digit + { + info[1] = c; // copy to buffer + info[0] = ' '; // clear previous character + k--; // go to the preceding character + if (k >= 0) // check if index is valid + { + c = lfn[k]; + if (c >= '0' && c <= '9') // check if a digit + info[0] = c; // copy to buffer + } + + k = i + sizeof(template); // get first character to the right of the mached template substring + c = lfn[k]; // no need to check if index is valid + if (c >= '0' && c <= '9') // check if a digit + { + info[3] = c; // copy to buffer + info[4] = ' '; // clear next char + k++; // go to the followwing character + if (k < len) // check if index is valid + { + c = lfn[k]; + if (c >= '0' && c <= '9') // check if a digit + info[4] = c; // copy to buffer + } + return info; + } + } + } + } + } + return NULL; +} + +// print directory contents +void PrintDirectory(void) +{ + int k; + int len; + + char s[40]; + s[32] = 0; // set temporary string length to OSD line length + + ScrollReset(); + + for(int i = 0; i < OsdGetSize(); i++) + { + char leftchar = 0; + memset(s, ' ', 32); // clear line buffer + if (i < nDirEntries) + { + k = iFirstEntry + i; + + len = strlen(DirItem[k].d_name); // get name length + + if (!(DirItem[k].d_type == DT_DIR)) // if a file + { + if (fs_ExtLen <= 3) + { + char e[5]; + memcpy(e + 1, fs_pFileExt, 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(DirItem[k].d_name + len - l, e, l)) + { + len -= l; + } + } + } + + if (len > 28) + { + len = 27; // trim display length if longer than 30 characters + s[28] = 22; + } + + strncpy(s + 1, DirItem[k].d_name, len); // display only name + + if (DirItem[k].d_type == DT_DIR) // mark directory with suffix + { + if (!strcmp(DirItem[k].d_name, "..")) + { + strcpy(&s[19], " "); + } + else + { + strcpy(&s[22], " "); + } + } + + if (!i && k) leftchar = 17; + if ((i == OsdGetSize() - 1) && (k < nDirEntries - 1)) leftchar = 16; + } + else + { + if (i == 0 && nDirEntries == 0) // selected directory is empty + strcpy(s, " No files!"); + } + + OsdWriteOffset(i, s, i == (iSelectedEntry - iFirstEntry), 0, 0, leftchar); + } +} + +void _strncpy(char* pStr1, const char* pStr2, size_t nCount) +{ + // customized strncpy() function to fill remaing destination string part with spaces + + while (*pStr2 && nCount) + { + *pStr1++ = *pStr2++; // copy strings + nCount--; + } + + while (nCount--) + *pStr1++ = ' '; // fill remaining space with spaces +} + +// insert floppy image pointed to to by global into +void InsertFloppy(adfTYPE *drive, char* path) +{ + int writable = FileCanWrite(path); + + if (!FileOpenEx(&drive->file, path, writable ? O_RDWR | O_SYNC : O_RDONLY)) + { + return; + } + + unsigned char i, j; + unsigned long tracks; + + // calculate number of tracks in the ADF image file + tracks = drive->file.size / (512 * 11); + if (tracks > MAX_TRACKS) + { + menu_debugf("UNSUPPORTED ADF SIZE!!! Too many tracks: %lu\n", tracks); + tracks = MAX_TRACKS; + } + drive->tracks = (unsigned char)tracks; + + strcpy(drive->name, path); + + // initialize the rest of drive struct + drive->status = DSK_INSERTED; + if(writable) // read-only attribute + drive->status |= DSK_WRITABLE; + + drive->sector_offset = 0; + drive->track = 0; + drive->track_prev = -1; + + menu_debugf("Inserting floppy: \"%s\"\n", path); + menu_debugf("file writable: %d\n", writable); + menu_debugf("file size: %lu (%lu KB)\n", drive->file.size, drive->file.size >> 10); + menu_debugf("drive tracks: %u\n", drive->tracks); + menu_debugf("drive status: 0x%02X\n", drive->status); +} + +static void set_text(const char *message, unsigned char code) +{ + char s[40]; + char i = 0, l = 1; + + OsdWrite(0, "", 0, 0); + + do + { + s[i++] = *message; + + // line full or line break + if ((i == 29) || (*message == '\n') || !*message) + { + + s[i] = 0; + OsdWrite(l++, s, 0, 0); + i = 0; // start next line + } + } while (*message++); + + if (code && (l <= 7)) + { + siprintf(s, " Code: #%d", code); + OsdWrite(l++, s, 0, 0); + } + + while (l <= 7) OsdWrite(l++, "", 0, 0); +} + +/* Error Message */ +void ErrorMessage(const char *message, unsigned char code) +{ + menustate = MENU_ERROR; + + OsdSetTitle("Error", 0); + set_text(message, code); + OsdEnable(0); // do not disable KEYBOARD +} + +void InfoMessage(char *message) +{ + if (menustate != MENU_INFO) + { + OsdSetTitle("Message", 0); + OsdEnable(0); // do not disable keyboard + } + + set_text(message, 0); + + menu_timer = GetTimer(2000); + menustate = MENU_INFO; +} + +void EjectAllFloppies() +{ + char i; + for (i = 0; i +#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)) +}; diff --git a/mist_cfg.h b/mist_cfg.h new file mode 100644 index 00000000..8958efa1 --- /dev/null +++ b/mist_cfg.h @@ -0,0 +1,37 @@ +// mist_cfg.h +// 2015, rok.krajnc@gmail.com + + +#ifndef __MIST_CFG_H__ +#define __MIST_CFG_H__ + + +//// includes //// +#include +#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__ + diff --git a/osd.c b/osd.c new file mode 100644 index 00000000..228231e7 --- /dev/null +++ b/osd.c @@ -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 . + +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 +#include +#include +#include + +#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 && (outp63) + break; + } + } + else + break; + } + for (i = outp; i to the OSD buffer starting at line +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 to the OSD buffer starting at line +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; +} diff --git a/osd.h b/osd.h new file mode 100644 index 00000000..3f3ece96 --- /dev/null +++ b/osd.h @@ -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 + +/*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 + diff --git a/spi.c b/spi.c new file mode 100644 index 00000000..7e8636de --- /dev/null +++ b/spi.c @@ -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); + } +} diff --git a/spi.h b/spi.h new file mode 100644 index 00000000..97c8f292 --- /dev/null +++ b/spi.h @@ -0,0 +1,59 @@ +#ifndef SPI_H +#define SPI_H + +#include + +/* 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 diff --git a/state.c b/state.c new file mode 100644 index 00000000..904b5059 --- /dev/null +++ b/state.c @@ -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 . + +This code keeps status of MiST state + +*/ + +#include +#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]); +} \ No newline at end of file diff --git a/state.h b/state.h new file mode 100644 index 00000000..de6eede7 --- /dev/null +++ b/state.h @@ -0,0 +1,82 @@ +#ifndef STATE_H_INCLUDED +#define STATE_H_INCLUDED + +#include + +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 + diff --git a/tos.c b/tos.c new file mode 100644 index 00000000..4c464330 --- /dev/null +++ b/tos.c @@ -0,0 +1,1153 @@ +#include +#include +#include + +#include "hardware.h" +#include "menu.h" +#include "tos.h" +#include "file_io.h" +#include "debug.h" +#include "user_io.h" +#include "ikbd.h" +#include "fpga_io.h" + +#define CONFIG_FILENAME "MIST.CFG" + +typedef struct { + unsigned long system_ctrl; // system control word + char tos_img[1024]; + char cart_img[1024]; + char acsi_img[2][1024]; + char video_adjust[2]; + char sd_direct; +} tos_config_t; + +static tos_config_t config; + +#define TOS_BASE_ADDRESS_192k 0xfc0000 +#define TOS_BASE_ADDRESS_256k 0xe00000 +#define CART_BASE_ADDRESS 0xfa0000 +#define VIDEO_BASE_ADDRESS 0x010000 + +static unsigned char font[2048]; // buffer for 8x16 atari font + + // two floppies +static struct { + fileTYPE file; + unsigned char sides; + unsigned char spt; +} fdd_image[2]; + +// one harddisk +fileTYPE hdd_image[2]; +unsigned long hdd_direct = 0; + +static unsigned char dma_buffer[512]; + +static const char *acsi_cmd_name(int cmd) { + static const char *cmdname[] = { + "Test Drive Ready", "Restore to Zero", "Cmd $2", "Request Sense", + "Format Drive", "Read Block limits", "Reassign Blocks", "Cmd $7", + "Read Sector", "Cmd $9", "Write Sector", "Seek Block", + "Cmd $C", "Cmd $D", "Cmd $E", "Cmd $F", + "Cmd $10", "Cmd $11", "Inquiry", "Verify", + "Cmd $14", "Mode Select", "Cmd $16", "Cmd $17", + "Cmd $18", "Cmd $19", "Mode Sense", "Start/Stop Unit", + "Cmd $1C", "Cmd $1D", "Cmd $1E", "Cmd $1F", + // extended commands supported by ICD feature: + "Cmd $20", "Cmd $21", "Cmd $22", + "Read Format Capacities", "Cmd $24", "Read Capacity (10)", + "Cmd $26", "Cmd $27", "Read (10)", "Read Generation", + "Write (10)", "Seek (10)" + }; + + if (cmd > 0x2b) return NULL; + + return cmdname[cmd]; +} + +void tos_set_video_adjust(char axis, char value) { + config.video_adjust[axis] += value; + + EnableFpga(); + spi8(MIST_SET_VADJ); + spi8(config.video_adjust[0]); + spi8(config.video_adjust[1]); + DisableFpga(); +} + +char tos_get_video_adjust(char axis) { + return config.video_adjust[axis]; +} + +static void mist_memory_set_address(unsigned long a, unsigned char s, char rw) { + // iprintf("set addr = %x, %d, %d\n", a, s, rw); + + a |= rw ? 0x1000000 : 0; + a >>= 1; + + EnableFpga(); + spi8(MIST_SET_ADDRESS); + spi8(s); + spi8((a >> 16) & 0xff); + spi8((a >> 8) & 0xff); + spi8((a >> 0) & 0xff); + DisableFpga(); +} + +static void mist_set_control(unsigned long ctrl) { + EnableFpga(); + spi8(MIST_SET_CONTROL); + spi8((ctrl >> 24) & 0xff); + spi8((ctrl >> 16) & 0xff); + spi8((ctrl >> 8) & 0xff); + spi8((ctrl >> 0) & 0xff); + DisableFpga(); +} + +static void mist_memory_read(char *data, unsigned long words) { + EnableFpga(); + spi8(MIST_READ_MEMORY); + + // transmitted bytes must be multiple of 2 (-> words) + while (words--) { + *data++ = spi_in(); + *data++ = spi_in(); + } + + DisableFpga(); +} + +static void mist_memory_write(char *data, unsigned long words) { + EnableFpga(); + spi8(MIST_WRITE_MEMORY); + + while (words--) { + spi8(*data++); + spi8(*data++); + } + + DisableFpga(); +} + +static void mist_memory_read_block(char *data) { + EnableFpga(); + spi8(MIST_READ_MEMORY); + + spi_block_read(data,0); + + DisableFpga(); +} + +static void mist_memory_write_block(char *data) { + EnableFpga(); + spi8(MIST_WRITE_MEMORY); + + spi_block_write(data,0); + + DisableFpga(); +} + +void mist_memory_set(char data, unsigned long words) { + EnableFpga(); + spi8(MIST_WRITE_MEMORY); + + while (words--) { + spi8(data); + spi8(data); + } + + DisableFpga(); +} + +// enable direct sd card access on acsi0 +void tos_set_direct_hdd(char on) { + config.sd_direct = 0; + + tos_debugf("ACSI: disable direct sd access"); + config.system_ctrl &= ~TOS_ACSI0_ENABLE; + hdd_direct = 0; + + // check if image access should be enabled instead + if (hdd_image[0].size) { + tos_debugf("ACSI: re-enabling image on ACSI0"); + config.system_ctrl |= TOS_ACSI0_ENABLE; + } + + mist_set_control(config.system_ctrl); +} + +char tos_get_direct_hdd() { + return 0; +} + +static void dma_ack(unsigned char status) { + EnableFpga(); + spi8(MIST_ACK_DMA); + spi8(status); + DisableFpga(); +} + +static void dma_nak(void) { + EnableFpga(); + spi8(MIST_NAK_DMA); + DisableFpga(); +} + +static void handle_acsi(unsigned char *buffer) { + static unsigned char asc[2] = { 0,0 }; + unsigned char target = buffer[19] >> 5; + unsigned char device = buffer[10] >> 5; + unsigned char cmd = buffer[9]; + unsigned int dma_address = 256 * 256 * buffer[0] + + 256 * buffer[1] + (buffer[2] & 0xfe); + unsigned char scnt = buffer[3]; + unsigned long lba = 256 * 256 * (buffer[10] & 0x1f) + + 256 * buffer[11] + buffer[12]; + unsigned short length = buffer[13]; + if (length == 0) length = 256; + + if (user_io_dip_switch1()) { + tos_debugf("ACSI: target %d.%d, \"%s\" (%02x)", target, device, acsi_cmd_name(cmd), cmd); + tos_debugf("ACSI: lba %lu (%lx), length %u", lba, lba, length); + tos_debugf("DMA: scnt %u, addr %p", scnt, dma_address); + + if (buffer[20] == 0xa5) { + tos_debugf("DMA: fifo %d/%d %x %s", + (buffer[21] >> 4) & 0x0f, buffer[21] & 0x0f, + buffer[22], (buffer[2] & 1) ? "OUT" : "IN"); + tos_debugf("DMA stat=%x, mode=%x, fdc_irq=%d, acsi_irq=%d", + buffer[23], buffer[24], buffer[25], buffer[26]); + } + } + + // only a harddisk on ACSI 0/1 is supported + // ACSI 0/1 is only supported if a image is loaded + // ACSI 0 is only supported for direct IO + if (((target < 2) && (hdd_image[target].size != 0)) || + ((target == 0) && hdd_direct)) { + unsigned long blocks = hdd_image[target].size / 512; + + // if in hdd direct mode then hdd_direct contains device sizee + if (hdd_direct && target == 0) blocks = hdd_direct; + + // only lun0 is fully supported + switch (cmd) { + case 0x25: + if (device == 0) { + bzero(dma_buffer, 512); + dma_buffer[0] = (blocks - 1) >> 24; + dma_buffer[1] = (blocks - 1) >> 16; + dma_buffer[2] = (blocks - 1) >> 8; + dma_buffer[3] = (blocks - 1) >> 0; + dma_buffer[6] = 2; // 512 bytes per block + + mist_memory_write(dma_buffer, 4); + + dma_ack(0x00); + asc[target] = 0x00; + } + else { + dma_ack(0x02); + asc[target] = 0x25; + } + break; + + case 0x00: // test drive ready + case 0x04: // format + if (device == 0) { + asc[target] = 0x00; + dma_ack(0x00); + } + else { + asc[target] = 0x25; + dma_ack(0x02); + } + break; + + case 0x03: // request sense + if (device != 0) + asc[target] = 0x25; + + bzero(dma_buffer, 512); + dma_buffer[7] = 0x0b; + if (asc[target] != 0) { + dma_buffer[2] = 0x05; + dma_buffer[12] = asc[target]; + } + mist_memory_write(dma_buffer, 9); // 18 bytes + dma_ack(0x00); + asc[target] = 0x00; + break; + + case 0x08: // read sector + case 0x28: // read (10) + if (device == 0) { + if (cmd == 0x28) { + lba = + 256 * 256 * 256 * buffer[11] + + 256 * 256 * buffer[12] + + 256 * buffer[13] + + buffer[14]; + + length = 256 * buffer[16] + buffer[17]; + // iprintf("READ(10) %d, %d\n", lba, length); + } + + if (lba + length <= blocks) { + DISKLED_ON; + while (length) { + FileSeekLBA(&hdd_image[target], lba++); + FileRead(&hdd_image[target], dma_buffer); + // hexdump(dma_buffer, 32, 0); + mist_memory_write_block(dma_buffer); + length--; + } + DISKLED_OFF; + dma_ack(0x00); + asc[target] = 0x00; + } + else { + tos_debugf("ACSI: read (%d+%d) exceeds device limits (%d)", + lba, length, blocks); + dma_ack(0x02); + asc[target] = 0x21; + } + } + else { + dma_ack(0x02); + asc[target] = 0x25; + } + break; + + case 0x0a: // write sector + case 0x2a: // write (10) + if (device == 0) { + if (cmd == 0x2a) { + lba = + 256 * 256 * 256 * buffer[11] + + 256 * 256 * buffer[12] + + 256 * buffer[13] + + buffer[14]; + + length = 256 * buffer[16] + buffer[17]; + + // iprintf("WRITE(10) %d, %d\n", lba, length); + } + + if (lba + length <= blocks) { + DISKLED_ON; + while (length) { + mist_memory_read_block(dma_buffer); + FileSeekLBA(&hdd_image[target], lba++); + FileWrite(&hdd_image[target], dma_buffer); + length--; + } + DISKLED_OFF; + dma_ack(0x00); + asc[target] = 0x00; + } + else { + tos_debugf("ACSI: write (%d+%d) exceeds device limits (%d)", + lba, length, blocks); + dma_ack(0x02); + asc[target] = 0x21; + } + } + else { + dma_ack(0x02); + asc[target] = 0x25; + } + break; + + case 0x12: // inquiry + if (hdd_direct && target == 0) tos_debugf("ACSI: Inquiry DIRECT"); + else tos_debugf("ACSI: Inquiry %s", hdd_image[target].name); + bzero(dma_buffer, 512); + dma_buffer[2] = 2; // SCSI-2 + dma_buffer[4] = length - 5; // len + memcpy(dma_buffer + 8, "MIST ", 8); // Vendor + memcpy(dma_buffer + 16, " ", 16); // Clear device entry + if (hdd_direct && target == 0) memcpy(dma_buffer + 16, "SD DIRECT", 9);// Device + else memcpy(dma_buffer + 16, hdd_image[target].name, 11); + memcpy(dma_buffer + 32, "ATH ", 4); // Product revision + memcpy(dma_buffer + 36, VDATE " ", 8); // Serial number + if (device != 0) dma_buffer[0] = 0x7f; + mist_memory_write(dma_buffer, length / 2); + dma_ack(0x00); + asc[target] = 0x00; + break; + + case 0x1a: // mode sense + if (device == 0) { + tos_debugf("ACSI: mode sense, blocks = %u", blocks); + bzero(dma_buffer, 512); + dma_buffer[3] = 8; // size of extent descriptor list + dma_buffer[5] = blocks >> 16; + dma_buffer[6] = blocks >> 8; + dma_buffer[7] = blocks; + dma_buffer[10] = 2; // byte 1 of block size in bytes (512) + mist_memory_write(dma_buffer, length / 2); + dma_ack(0x00); + asc[target] = 0x00; + } + else { + asc[target] = 0x25; + dma_ack(0x02); + } + break; + +#if 0 + case 0x1f: // ICD command? + tos_debugf("ACSI: ICD command %s ($%02x)", + acsi_cmd_name(buffer[10] & 0x1f), buffer[10] & 0x1f); + asc[target] = 0x05; + dma_ack(0x02); + break; +#endif + + default: + tos_debugf("ACSI: >>>>>>>>>>>> Unsupported command <<<<<<<<<<<<<<<<"); + asc[target] = 0x20; + dma_ack(0x02); + break; + } + } + else { + tos_debugf("ACSI: Request for unsupported target"); + + // tell acsi state machine that io controller is done + // but don't generate a acsi irq + dma_nak(); + } +} + +static void handle_fdc(unsigned char *buffer) { + // extract contents + unsigned int dma_address = 256 * 256 * buffer[0] + + 256 * buffer[1] + (buffer[2] & 0xfe); + unsigned char scnt = buffer[3]; + unsigned char fdc_cmd = buffer[4]; + unsigned char fdc_track = buffer[5]; + unsigned char fdc_sector = buffer[6]; + unsigned char fdc_data = buffer[7]; + unsigned char drv_sel = 3 - ((buffer[8] >> 2) & 3); + unsigned char drv_side = 1 - ((buffer[8] >> 1) & 1); + + // tos_debugf("FDC: sel %d, cmd %x", drv_sel, fdc_cmd); + + // check if a matching disk image has been inserted + if (drv_sel && fdd_image[drv_sel - 1].file.size) { + // if the fdc has been asked to write protect the disks, then + // write sector commands should never reach the oi controller + + // read/write sector command + if ((fdc_cmd & 0xc0) == 0x80) { + // convert track/sector/side into disk offset + unsigned int offset = drv_side; + offset += fdc_track * fdd_image[drv_sel - 1].sides; + offset *= fdd_image[drv_sel - 1].spt; + offset += fdc_sector - 1; + + if (user_io_dip_switch1()) { + tos_debugf("FDC %s req %d sec (%c, SD:%d, T:%d, S:%d = %d) -> %p", + (fdc_cmd & 0x10) ? "multi" : "single", scnt, + 'A' + drv_sel - 1, drv_side, fdc_track, fdc_sector, offset, + dma_address); + } + + while (scnt) { + // check if requested sector is in range + if ((fdc_sector > 0) && (fdc_sector <= fdd_image[drv_sel - 1].spt)) { + + DISKLED_ON; + + FileSeek(&fdd_image[drv_sel - 1].file, offset, SEEK_SET); + + if ((fdc_cmd & 0xe0) == 0x80) { + // read from disk ... + FileRead(&fdd_image[drv_sel - 1].file, dma_buffer); + // ... and copy to ram + mist_memory_write_block(dma_buffer); + } + else { + // read from ram ... + mist_memory_read_block(dma_buffer); + // ... and write to disk + FileWrite(&(fdd_image[drv_sel - 1].file), dma_buffer); + } + + DISKLED_OFF; + } + else + tos_debugf("sector out of range"); + + scnt--; + dma_address += 512; + offset += 1; + } + dma_ack(0x00); + } + else if ((fdc_cmd & 0xc0) == 0xc0) { + char msg[32]; + + if ((fdc_cmd & 0xe0) == 0xc0) iprintf("READ ADDRESS\n"); + + if ((fdc_cmd & 0xf0) == 0xe0) { + iprintf("READ TRACK %d SIDE %d\n", fdc_track, drv_side); + siprintf(msg, "RD TRK %d S %d", fdc_track, drv_side); + InfoMessage(msg); + } + + if ((fdc_cmd & 0xf0) == 0xf0) { + iprintf("WRITE TRACK %d SIDE %d\n", fdc_track, drv_side); + siprintf(msg, "WR TRK %d S %d", fdc_track, drv_side); + InfoMessage(msg); + } + + iprintf("scnt = %d\n", scnt); + + dma_ack(0x00); + } + } +} + +static void mist_get_dmastate() { + unsigned char buffer[32]; + + EnableFpga(); + spi8(MIST_GET_DMASTATE); + spi_read(buffer, 32,0); + DisableFpga(); + + // check if acsi is busy + if (buffer[19] & 0x01) + handle_acsi(buffer); + + // check if fdc is busy + if (buffer[8] & 0x01) + handle_fdc(buffer); +} + +// color test, used to test the shifter without CPU/TOS +#define COLORS 20 +#define PLANES 4 + +static void tos_write(char *str); +static void tos_color_test() { + unsigned short buffer[COLORS][PLANES]; + + int y; + for (y = 0; y<13; y++) { + int i, j; + for (i = 0; i> n; + + *(d + 1) = *d; + } + } +} + +void tos_load_cartridge(char *name) { + fileTYPE file; + + if (name) + strncpy(config.cart_img, name, 11); + + // upload cartridge + if (config.cart_img[0] && FileOpen(&file, config.cart_img)) { + int i; + char buffer[512]; + + tos_debugf("%s:\n size = %d", config.cart_img, file.size); + + int blocks = file.size / 512; + tos_debugf(" blocks = %d", blocks); + + + DISKLED_ON; + for (i = 0; i= 256 * 1024) + tos_base = TOS_BASE_ADDRESS_256k; + else if (file.size != 192 * 1024) + tos_debugf("WARNING: Unexpected TOS size!"); + + int blocks = file.size / 512; + tos_debugf(" blocks = %d", blocks); + + tos_debugf(" address = $%08x", tos_base); + + // clear first 16k + mist_memory_set_address(0, 16384 / 512, 0); + mist_memory_set(0x00, 8192); + + time = GetTimer(0); + tos_debugf("Uploading ..."); + + for (i = 0; i= 0) { + iprintf("Failed in block %d/%x (%x != %x)\n", i, ok, 0xff & buffer[ok], 0xff & b2[ok]); + + hexdump(buffer, 512, 0); + puts(""); + hexdump(b2, 512, 0); + + // re-read to check whether read or write failed + bzero(buffer, 512); + mist_memory_set_address(tos_base + i * 512, 1, 1); + mist_memory_read_block(buffer); + + ok = -1; + for (j = 0; j<512; j++) + if (buffer[j] != b2[j]) + if (ok < 0) + ok = j; + + if (ok >= 0) { + iprintf("Re-read failed in block %d/%x (%x != %x)\n", i, ok, 0xff & buffer[ok], 0xff & b2[ok]); + hexdump(buffer, 512, 0); + } + else + iprintf("Re-read ok!\n"); + + for (;;); + } + + if (i != blocks - 1) + FileNextSector(&file); + } + iprintf("Verify: %s\n", ok ? "ok" : "failed"); + } +#endif + + time = GetTimer(0) - time; + tos_debugf("TOS.IMG uploaded in %lu ms (%d kB/s / %d kBit/s)", + time >> 20, file.size / (time >> 20), 8 * file.size / (time >> 20)); + + } + else { + tos_debugf("Unable to find tos.img"); + tos_write("Unable to find tos.img"); + + DISKLED_OFF; + return; + } + + DISKLED_OFF; + + // This is the initial boot if no name was given. Otherwise the + // user reloaded a new os + if (!name) { + // load + tos_load_cartridge(NULL); + + // try to open both floppies + int i; + for (i = 0; i<2; i++) { + char msg[] = "Found floppy disk image for drive X: "; + char name[] = "DISK_A.ST"; + msg[34] = name[5] = 'A' + i; + + tos_insert_disk(i, name); + } + + if (config.sd_direct) { + tos_set_direct_hdd(1); + tos_write("Enabling direct SD card access via ACSI0"); + } + else { + // try to open harddisk image + for (i = 0; i<2; i++) { + if (FileOpen(&file, config.acsi_img[i])) + { + FileClose(&file); + char msg[] = "Found hard disk image for ACSIX"; + msg[30] = '0' + i; + tos_write(msg); + tos_select_hdd_image(i, config.acsi_img[i]); + } + } + } + } + + tos_write("Booting ... "); + + // clear sector count register -> stop DMA + mist_memory_set_address(0, 0, 0); + + ikbd_reset(); + + // let cpu run (release reset) + config.system_ctrl &= ~TOS_CONTROL_CPU_RESET; + mist_set_control(config.system_ctrl); +} + +static unsigned long get_long(char *buffer, int offset) { + unsigned long retval = 0; + int i; + + for (i = 0; i<4; i++) + retval = (retval << 8) + *(unsigned char*)(buffer + offset + i); + + return retval; +} + +void tos_poll() { + // 1 == button not pressed, 2 = 1 sec exceeded, else timer running + static unsigned long timer = 1; + + mist_get_dmastate(); + + // check the user button + if (user_io_user_button()) { + if (timer == 1) + timer = GetTimer(1000); + else if (timer != 2) + if (CheckTimer(timer)) { + tos_reset(1); + timer = 2; + } + } + else { + // released while still running (< 1 sec) + if (!(timer & 3)) + tos_reset(0); + + timer = 1; + } +} + +void tos_update_sysctrl(unsigned long n) { + // iprintf(">>>>>>>>>>>> set sys %x, eth is %s\n", n, (n&TOS_CONTROL_ETHERNET)?"on":"off"); + + // some of the usb drivers also call this without knowing which + // core is running. So make sure this only happens if the Atari ST (MIST) + // core is running + if (user_io_core_type() == CORE_TYPE_MIST) { + config.system_ctrl = n; + mist_set_control(config.system_ctrl); + } +} + +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+2) + +char *tos_get_disk_name(char index) { + fileTYPE file; + char *c; + + if (index <= 1) + file = fdd_image[index].file; + else + file = hdd_image[index - 2]; + + if (!file.size) { + strcpy(buffer, "* no disk *"); + return buffer; + } + + nice_name(buffer, file.name); + + return buffer; +} + +char *tos_get_image_name() { + nice_name(buffer, config.tos_img); + return buffer; +} + +char *tos_get_cartridge_name() { + if (!config.cart_img[0]) // no cart name set + strcpy(buffer, "* no cartridge *"); + else + nice_name(buffer, config.cart_img); + + return buffer; +} + +char tos_disk_is_inserted(char index) { + if (index <= 1) + return (fdd_image[index].file.size != 0); + + return hdd_image[index - 2].size != 0; +} + +void tos_select_hdd_image(char i, char *name) +{ + tos_debugf("Select ACSI%c image %s", '0' + i, name); + + if(name) strcpy(config.acsi_img[i], name); + else config.acsi_img[i][0] = 0; + + if (!name) + { + FileClose(&hdd_image[i]); + hdd_image[i].size = 0; + config.system_ctrl &= ~(TOS_ACSI0_ENABLE << i); + } + else + { + if (FileOpen(&hdd_image[i], name)) + { + config.system_ctrl |= (TOS_ACSI0_ENABLE << i); + } + } + + // update system control + mist_set_control(config.system_ctrl); +} + +void tos_insert_disk(char i, char *name) +{ + if (i > 1) + { + tos_select_hdd_image(i - 2, name); + return; + } + + tos_debugf("%c: eject", i + 'A'); + + // toggle write protect bit to help tos detect a media change + int wp_bit = (!i) ? TOS_CONTROL_FDC_WR_PROT_A : TOS_CONTROL_FDC_WR_PROT_B; + + // any disk ejected is "write protected" (as nothing covers the write protect mechanism) + mist_set_control(config.system_ctrl | wp_bit); + + // first "eject" disk + fdd_image[i].file.size = 0; + fdd_image[i].sides = 1; + fdd_image[i].spt = 0; + FileClose(&fdd_image[i].file); + + // no new disk given? + if (!name) return; + + // open floppy + if (!FileOpen(&fdd_image[i].file, name)) return; + + tos_debugf("%c: insert %s", i + 'A', name); + + // check image size and parameters + + // check if image size suggests it's a two sided disk + if (fdd_image[i].file.size > 85 * 11 * 512) + fdd_image[i].sides = 2; + + // try common sector/track values + int m, s, t; + for (m = 0; m <= 2; m++) // multiplier for hd/ed disks + for (s = 9; s <= 12; s++) + for (t = 78; t <= 85; t++) + if (512 * (1 << m)*s*t*fdd_image[i].sides == fdd_image[i].file.size) + fdd_image[i].spt = s*(1 << m); + + + + if (!fdd_image[i].spt) { + // read first sector from disk + /* + if (MMC_Read(0, dma_buffer)) { + fdd_image[i].spt = dma_buffer[24] + 256 * dma_buffer[25]; + fdd_image[i].sides = dma_buffer[26] + 256 * dma_buffer[27]; + } + else + */ + fdd_image[i].file.size = 0; + } + + if (fdd_image[i].file.size) { + // restore state of write protect bit + tos_update_sysctrl(config.system_ctrl); + tos_debugf("%c: detected %d sides with %d sectors per track", + i + 'A', fdd_image[i].sides, fdd_image[i].spt); + } +} + +// force ejection of all disks (SD card has been removed) +void tos_eject_all() { + int i; + for (i = 0; i<2; i++) + tos_insert_disk(i, NULL); + + // ejecting an SD card while a hdd image is mounted may be a bad idea + for (i = 0; i<2; i++) { + if (hdd_direct) + hdd_direct = 0; + + if (hdd_image[i].size) { + InfoMessage("Card removed:\nDisabling Harddisk!"); + hdd_image[i].size = 0; + } + } +} + +void tos_reset(char cold) { + ikbd_reset(); + + tos_update_sysctrl(config.system_ctrl | TOS_CONTROL_CPU_RESET); // set reset + + if (cold) { +#if 0 // clearing mem should be sifficient. But currently we upload TOS as it may be damaged + // clear first 16k + mist_memory_set_address(8); + mist_memory_set(0x00, 8192 - 4); +#else + tos_upload(NULL); +#endif + } + + tos_update_sysctrl(config.system_ctrl & ~TOS_CONTROL_CPU_RESET); // release reset +} + +unsigned long tos_system_ctrl(void) +{ + return config.system_ctrl; +} + +void tos_config_init(void) +{ + fileTYPE file; + + // set default values + config.system_ctrl = TOS_MEMCONFIG_4M | TOS_CONTROL_BLITTER; + memcpy(config.tos_img, "TOS.IMG", 12); + config.cart_img[0] = 0; + memcpy(config.acsi_img[0], "HARDDISK.HD", 12); + config.acsi_img[1][0] = 0; + config.video_adjust[0] = config.video_adjust[1] = 0; + + // try to load config + if (FileOpen(&file, CONFIG_FILENAME)) + { + tos_debugf("Configuration file size: %lu (should be %lu)", file.size, sizeof(tos_config_t)); + if (file.size == sizeof(tos_config_t)) + { + FileReadAdv(&file, &config, file.size); + } + FileClose(&file); + } + + // ethernet is auto detected later + config.system_ctrl &= ~TOS_CONTROL_ETHERNET; +} + +// save configuration +void tos_config_save(void) +{ + FileSave(CONFIG_FILENAME, &config, sizeof(tos_config_t)); +} diff --git a/tos.h b/tos.h new file mode 100644 index 00000000..fc26ca5c --- /dev/null +++ b/tos.h @@ -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 diff --git a/user_io.c b/user_io.c new file mode 100644 index 00000000..73666cd9 --- /dev/null +++ b/user_io.c @@ -0,0 +1,1956 @@ +#include +#include +#include +#include +#include +#include + +#include "hardware.h" +#include "osd.h" +#include "state.h" +#include "state.h" +#include "user_io.h" +#include "archie.h" +#include "debug.h" +#include "keycodes.h" +#include "ikbd.h" +#include "spi.h" +#include "mist_cfg.h" +#include "tos.h" +#include "errors.h" +#include "input.h" +#include "fpga_io.h" +#include "file_io.h" +#include "config.h" + +// up to 16 key can be remapped +#define MAX_REMAP 16 +unsigned char key_remap_table[MAX_REMAP][2]; + +#define BREAK 0x8000 + +fileTYPE sd_image; + +// mouse and keyboard emulation state +typedef enum { EMU_NONE, EMU_MOUSE, EMU_JOY0, EMU_JOY1 } emu_mode_t; +static emu_mode_t emu_mode = EMU_NONE; +static unsigned char emu_state = 0; +static unsigned long emu_timer = 0; +#define EMU_MOUSE_FREQ 5 + +// keep state over core type and its capabilities +static unsigned char core_type = CORE_TYPE_UNKNOWN; +static char core_type_8bit_with_config_string = 0; + +static int fio_size = 0; +static int io_ver = 0; + +// keep state of caps lock +static char caps_lock_toggle = 0; + +// mouse position storage for ps2 and minimig rate limitation +#define X 0 +#define Y 1 +#define MOUSE_FREQ 20 // 20 ms -> 50hz +static int16_t mouse_pos[2] = { 0, 0 }; +static uint8_t mouse_flags = 0; +static unsigned long mouse_timer; + +#define LED_FREQ 100 // 100 ms +static unsigned long led_timer; +char keyboard_leds = 0; +bool caps_status = 0; +bool num_status = 0; +bool scrl_status = 0; + +// set by OSD code to suppress forwarding of those keys to the core which +// may be in use by an active OSD +static char osd_is_visible = false; + +char user_io_osd_is_visible() +{ + return osd_is_visible; +} + +void user_io_init() +{ + // no sd card image selected, SD card accesses will go directly + // to the card + sd_image.size = 0; + + // mark remap table as unused + memset(key_remap_table, 0, sizeof(key_remap_table)); + + ikbd_init(); +} + +unsigned char user_io_core_type() +{ + return core_type; +} + +char is_minimig() +{ + return(core_type == CORE_TYPE_MINIMIG2); +} + +char* user_io_create_config_name() +{ + static char str[40]; + str[0] = 0; + char *p = user_io_get_core_name(); + if (p[0]) + { + strcpy(str, p); + strcat(str, ".CFG"); + } + return str; +} + +char user_io_is_8bit_with_config_string() +{ + return core_type_8bit_with_config_string; +} + +static char core_name[16 + 1]; // max 16 bytes for core name + +char *user_io_get_core_name() +{ + return core_name; +} + +char is_menu_core() +{ + return !strcasecmp(core_name, "MENU"); +} + +static void user_io_read_core_name() +{ + core_name[0] = 0; + + if (user_io_is_8bit_with_config_string()) + { + char *p = user_io_8bit_get_string(0); // get core name + if (p && p[0]) strcpy(core_name, p); + } + + iprintf("Core name is \"%s\"\n", core_name); +} + +void user_io_detect_core_type() +{ + char *name; + core_name[0] = 0; + + core_type = (fpga_core_id() & 0xFF); + fio_size = fpga_get_fio_size(); + io_ver = fpga_get_io_version(); + + if ((core_type != CORE_TYPE_DUMB) && + (core_type != CORE_TYPE_MINIMIG2) && + (core_type != CORE_TYPE_PACE) && + (core_type != CORE_TYPE_MIST) && + (core_type != CORE_TYPE_ARCHIE) && + (core_type != CORE_TYPE_8BIT)) + { + core_type = CORE_TYPE_UNKNOWN; + fio_size = 0; + io_ver = 0; + } + + spi_init(core_type != CORE_TYPE_UNKNOWN); + OsdSetSize(8); + + switch (core_type) + { + case CORE_TYPE_UNKNOWN: + iprintf("Unable to identify core (%x)!\n", core_type); + break; + + case CORE_TYPE_DUMB: + puts("Identified core without user interface"); + break; + + case CORE_TYPE_MINIMIG2: + OsdSetSize(16); + puts("Identified Minimig V2 core"); + break; + + case CORE_TYPE_PACE: + puts("Identified PACE core"); + break; + + case CORE_TYPE_MIST: + puts("Identified MiST core"); + break; + + case CORE_TYPE_ARCHIE: + puts("Identified Archimedes core"); + archie_init(); + break; + + case CORE_TYPE_8BIT: + puts("Identified 8BIT core"); + + // forward SD card config to core in case it uses the local + // SD card implementation + user_io_sd_set_config(); + + // check if core has a config string + core_type_8bit_with_config_string = (user_io_8bit_get_string(0) != NULL); + + // set core name. This currently only sets a name for the 8 bit cores + user_io_read_core_name(); + + // send a reset + user_io_8bit_set_status(UIO_STATUS_RESET, UIO_STATUS_RESET); + + // try to load config + name = user_io_create_config_name(); + if(strlen(name) > 0) + { + iprintf("Loading config %s\n", name); + unsigned long status = 0; + if (FileLoad(name, &status, 4)) + { + iprintf("Found config\n"); + user_io_8bit_set_status(status, 0xffffffff); + } + + // check if there's a .rom present + strcpy(name + strlen(name) - 3, "ROM"); + user_io_file_tx(name, 0); + + // check if there's a .vhd present + strcpy(name + strlen(name) - 3, "VHD"); + user_io_file_mount(name); + } + + // release reset + user_io_8bit_set_status(0, UIO_STATUS_RESET); + break; + } +} + +unsigned short usb2amiga(unsigned char k) +{ + // replace MENU key by RGUI to allow using Right Amiga on reduced keyboards + // (it also disables the use of Menu for OSD) + if (mist_cfg.key_menu_as_rgui && k == 0x65) + { + return 0x67; + } + return usb2ami[k]; +} + +unsigned short usb2ps2code(unsigned char k) +{ + // replace MENU key by RGUI e.g. to allow using RGUI on reduced keyboards without physical key + // (it also disables the use of Menu for OSD) + if (mist_cfg.key_menu_as_rgui && k == 0x65) + { + return EXT | 0x27; + } + return usb2ps2[k]; +} + +void user_io_analog_joystick(unsigned char joystick, char valueX, char valueY) +{ + if (core_type == CORE_TYPE_8BIT) + { + uint16_t pos = valueX; + spi_uio_cmd8_cont(UIO_ASTICK, joystick); + if(io_ver) spi_w((pos<<8) | (uint8_t)(valueY)); + else + { + spi8(valueX); + spi8(valueY); + } + DisableIO(); + } +} + +void user_io_digital_joystick(unsigned char joystick, unsigned char map) +{ + uint8_t state = map; + // "only" 6 joysticks are supported + if (joystick >= 6) return; + + // the physical joysticks (db9 ports at the right device side) + // as well as the joystick emulation are renumbered if usb joysticks + // are present in the system. The USB joystick(s) replace joystick 1 + // and 0 and the physical joysticks are "shifted up". + // Since the primary joystick is in port 1 the first usb joystick + // becomes joystick 1 and only the second one becomes joystick 0 + // (mouse port) + + StateJoySet(state, joystick == 0 ? 1 : 0); + if (joystick == 1) + { + //StateJoyUpdateTurboStructure(0); + //map = (unsigned char) StateJoyStructureState(0) & 0xFF; + } + else if (joystick == 0) + {// WARNING: 0 is the second joystick, either USB or DB9 + //StateJoyUpdateTurboStructure(1); + //map = (unsigned char) StateJoyStructureState(1) & 0xFF; + } + + // if osd is open control it via joystick + if (osd_is_visible) + { + static const uint8_t joy2kbd[] = { + OSDCTRLMENU, OSDCTRLMENU, OSDCTRLMENU, OSDCTRLSELECT, + OSDCTRLUP, OSDCTRLDOWN, OSDCTRLLEFT, OSDCTRLRIGHT }; + + // iprintf("joy to osd\n"); + + // OsdKeySet(0x80 | usb2ami[pressed[i]]); + + return; + } + + // iprintf("j%d: %x\n", joystick, map); + + + // atari ST handles joystick 0 and 1 through the ikbd emulated by the io controller + // but only for joystick 1 and 2 + if ((core_type == CORE_TYPE_MIST) && (joystick < 2)) + { + ikbd_joystick(joystick, map); + return; + } + + // every other core else uses this + // (even MIST, joystick 3 and 4 were introduced later) + spi_uio_cmd8((joystick < 2) ? (UIO_JOYSTICK0 + joystick) : ((UIO_JOYSTICK2 + joystick - 2)), map); +} + +static char dig2ana(char min, char max) +{ + if (min && !max) return -128; + if (max && !min) return 127; + return 0; +} + +void user_io_joystick(unsigned char joystick, unsigned char map) +{ + // digital joysticks also send analog signals + user_io_digital_joystick(joystick, map); + user_io_analog_joystick(joystick, + dig2ana(map&JOY_LEFT, map&JOY_RIGHT), + dig2ana(map&JOY_UP, map&JOY_DOWN)); +} + +// transmit serial/rs232 data into core +void user_io_serial_tx(char *chr, uint16_t cnt) +{ + spi_uio_cmd_cont(UIO_SERIAL_OUT); + while (cnt--) spi8(*chr++); + DisableIO(); +} + +char user_io_serial_status(serial_status_t *status_in, uint8_t status_out) +{ + uint8_t i, *p = (uint8_t*)status_in; + + spi_uio_cmd_cont(UIO_SERIAL_STAT); + + // first byte returned by core must be "magic". otherwise the + // core doesn't support this request + if (spi_b(status_out) != 0xa5) + { + DisableIO(); + return 0; + } + + // read the whole structure + for (i = 0; i> 9); + CSD[7] = (uint8_t)(sd_image.size >> 17); + CSD[8] = (uint8_t)(sd_image.size >> 25); + + // forward it to the FPGA + spi_uio_cmd_cont(UIO_SET_SDCONF); + spi_write(CID, sizeof(CID), fio_size); + spi_write(CSD, sizeof(CSD), fio_size); + spi8(1); //SDHC permanently + + DisableIO(); + + // hexdump(data, sizeof(data), 0); +} + +// read 8+32 bit sd card status word from FPGA +uint16_t user_io_sd_get_status(uint32_t *lba) +{ + uint32_t s; + uint16_t c; + + spi_uio_cmd_cont(UIO_GET_SDSTAT); + if (io_ver) + { + c = spi_w(0); + s = spi_w(0); + s = (s & 0xFFFF) | (((uint32_t)spi_w(0))<<16); + } + else + { + //note: using 32bit big-endian transfer! + c = spi_in(); + s = spi_in(); + s = (s << 8) | spi_in(); + s = (s << 8) | spi_in(); + s = (s << 8) | spi_in(); + } + DisableIO(); + + if (lba) + *lba = s; + + return c; +} + +// read 8 bit keyboard LEDs status from FPGA +uint8_t user_io_kbdled_get_status(void) +{ + uint8_t c; + + spi_uio_cmd_cont(UIO_GET_KBD_LED); + c = spi_in(); + DisableIO(); + + return c; +} + +// read 32 bit ethernet status word from FPGA +uint32_t user_io_eth_get_status(void) +{ + uint32_t s; + + spi_uio_cmd_cont(UIO_ETH_STATUS); + s = spi_in(); + s = (s << 8) | spi_in(); + s = (s << 8) | spi_in(); + s = (s << 8) | spi_in(); + DisableIO(); + + return s; +} + +// read ethernet frame from FPGAs ethernet tx buffer +void user_io_eth_receive_tx_frame(uint8_t *d, uint16_t len) +{ + spi_uio_cmd_cont(UIO_ETH_FRM_IN); + while (len--) *d++ = spi_in(); + DisableIO(); +} + +// write ethernet frame to FPGAs rx buffer +void user_io_eth_send_rx_frame(uint8_t *s, uint16_t len) +{ + spi_uio_cmd_cont(UIO_ETH_FRM_OUT); + spi_write(s, len, 0); + spi8(0); // one additional byte to allow fpga to store the previous one + DisableIO(); +} + +// the physical joysticks (db9 ports at the right device side) +// as well as the joystick emulation are renumbered if usb joysticks +// are present in the system. The USB joystick(s) replace joystick 1 +// and 0 and the physical joysticks are "shifted up". +// +// Since the primary joystick is in port 1 the first usb joystick +// becomes joystick 1 and only the second one becomes joystick 0 +// (mouse port) + +static uint8_t joystick_renumber(uint8_t j) +{ + uint8_t usb_sticks = 0; //hid_get_joysticks(); + + // no usb sticks present: no changes are being made + if (!usb_sticks) return j; + + if (j == 0) { + // if usb joysticks are present, then physical joystick 0 (mouse port) + // becomes becomes 2,3,... + j = usb_sticks + 1; + } + else { + // if one usb joystick is present, then physical joystick 1 (joystick port) + // becomes physical joystick 0 (mouse) port. If more than 1 usb joystick + // is present it becomes 2,3,... + if (usb_sticks == 1) j = 0; + else j = usb_sticks; + } + + return j; +} + +// 16 byte fifo for amiga key codes to limit max key rate sent into the core +#define KBD_FIFO_SIZE 16 // must be power of 2 +static unsigned short kbd_fifo[KBD_FIFO_SIZE]; +static unsigned char kbd_fifo_r = 0, kbd_fifo_w = 0; +static long kbd_timer = 0; + +static void kbd_fifo_minimig_send(unsigned short code) +{ + spi_uio_cmd8((code&OSD) ? UIO_KBD_OSD : UIO_KEYBOARD, code & 0xff); + kbd_timer = GetTimer(10); // next key after 10ms earliest +} + +static void kbd_fifo_enqueue(unsigned short code) +{ + // if fifo full just drop the value. This should never happen + if (((kbd_fifo_w + 1)&(KBD_FIFO_SIZE - 1)) == kbd_fifo_r) + return; + + // store in queue + kbd_fifo[kbd_fifo_w] = code; + kbd_fifo_w = (kbd_fifo_w + 1)&(KBD_FIFO_SIZE - 1); +} + +// send pending bytes if timer has run up +static void kbd_fifo_poll() +{ + // timer enabled and runnig? + if (kbd_timer && !CheckTimer(kbd_timer)) + return; + + kbd_timer = 0; // timer == 0 means timer is not running anymore + + if (kbd_fifo_w == kbd_fifo_r) + return; + + kbd_fifo_minimig_send(kbd_fifo[kbd_fifo_r]); + kbd_fifo_r = (kbd_fifo_r + 1)&(KBD_FIFO_SIZE - 1); +} + +void user_io_set_index(unsigned char index) +{ + EnableFpga(); + spi8(UIO_FILE_INDEX); + spi8(index); + DisableFpga(); +} + +void user_io_file_mount(char *name) +{ + int writable = FileCanWrite(name); + printf("Mount %s as %s\n", name, writable ? "read-write" : "read-only"); + + int ret = FileOpenEx(&sd_image, name, writable ? (O_RDWR | O_SYNC) : O_RDONLY); + if (!ret) + { + sd_image.size = 0; + printf("Failed to open file %s\n", name); + return; + } + + // send mounted image size first then notify about mounting + EnableIO(); + spi8(UIO_SET_SDINFO); + if (io_ver) + { + spi_w((uint16_t)(sd_image.size)); + spi_w((uint16_t)(sd_image.size>>16)); + spi_w((uint16_t)(sd_image.size>>32)); + spi_w((uint16_t)(sd_image.size>>48)); + } + else + { + spi32le(sd_image.size); + spi32le(sd_image.size>>32); + } + DisableIO(); + + // notify core of possible sd image change + spi_uio_cmd8(UIO_SET_SDSTAT, 0); +} + +void user_io_file_tx(char* name, unsigned char index) +{ + fileTYPE f; + static uint8_t buf[512]; + + if (!FileOpen(&f, name)) return; + + unsigned long bytes2send = f.size; + + /* transmit the entire file using one transfer */ + iprintf("Selected file %s with %lu bytes to send for index %d.%d\n", name, bytes2send, index&0x3F, index>>6); + + // set index byte (0=bios rom, 1-n=OSD entry index) + user_io_set_index(index); + + // send directory entry (for alpha amstrad core) + //EnableFpga(); + //spi8(UIO_FILE_INFO); + //spi_write((void*)(DirEntry + sort_table[iSelectedEntry]), sizeof(DIRENTRY)); + //DisableFpga(); + + // hexdump(DirEntry+sort_table[iSelectedEntry], sizeof(DIRENTRY), 0); + + // prepare transmission of new file + EnableFpga(); + spi8(UIO_FILE_TX); + spi8(0xff); + DisableFpga(); + + while (bytes2send) + { + iprintf("."); + + uint16_t chunk = (bytes2send>512) ? 512 : bytes2send; + + FileReadSec(&f, buf); + + EnableFpga(); + spi8(UIO_FILE_TX_DAT); + spi_write(buf, chunk, fio_size); + DisableFpga(); + + bytes2send -= chunk; + } + + FileClose(&f); + + // signal end of transmission + EnableFpga(); + spi8(UIO_FILE_TX); + spi8(0x00); + DisableFpga(); + + iprintf("\n"); +} + +// 8 bit cores have a config string telling the firmware how +// to treat it +char *user_io_8bit_get_string(char index) +{ + unsigned char i, lidx = 0, j = 0; + static char buffer[128 + 1]; // max 128 bytes per config item + + // clear buffer + buffer[0] = 0; + + spi_uio_cmd_cont(UIO_GET_STRING); + i = spi_in(); + // the first char returned will be 0xff if the core doesn't support + // config strings. atari 800 returns 0xa4 which is the status byte + if ((i == 0xff) || (i == 0xa4)) + { + DisableIO(); + return NULL; + } + + // iprintf("String: "); + while ((i != 0) && (i != 0xff) && (j 127) + { + spi8(127); + mouse_pos[X] -= 127; + } + else + { + spi8(mouse_pos[X]); + mouse_pos[X] = 0; + } + + // ----- Y axis ------- + if (mouse_pos[Y] < -128) + { + spi8(-128); + mouse_pos[Y] += 128; + } + else if (mouse_pos[Y] > 127) + { + spi8(127); + mouse_pos[Y] -= 127; + } + else + { + spi8(mouse_pos[Y]); + mouse_pos[Y] = 0; + } + + spi8(mouse_flags & 0x03); + DisableIO(); + + // reset flags + mouse_flags = 0; + } + } + } + + if (core_type == CORE_TYPE_MIST) + { + // do some tos specific monitoring here + tos_poll(); + } + + if (core_type == CORE_TYPE_8BIT) + { + unsigned char c = 1, f, p = 0; + + // check for serial data to be sent + + // check for incoming serial data. this is directly forwarded to the + // arm rs232 and mixes with debug output. + spi_uio_cmd_cont(UIO_SIO_IN); + // status byte is 1000000A with A=1 if data is available + if ((f = spi_in(0)) == 0x81) + { + iprintf("\033[1;36m"); + + // character 0xff is returned if FPGA isn't configured + while ((f == 0x81) && (c != 0xff) && (c != 0x00) && (p < 8)) + { + c = spi_in(); + if (c != 0xff && c != 0x00) iprintf("%c", c); + + f = spi_in(); + p++; + } + iprintf("\033[0m"); + } + DisableIO(); + + // sd card emulation + { + static char buffer[512]; + static uint32_t buffer_lba = 0xffffffff; + uint32_t lba; + uint16_t c = user_io_sd_get_status(&lba); + //if(c&3) printf("user_io_sd_get_status: cmd=%02x, lba=%08x\n", c, lba); + + // valid sd commands start with "5x" to avoid problems with + // cores that don't implement this command + if ((c & 0xf0) == 0x50) + { + // check if core requests configuration + if (c & 0x08) + { + iprintf("core requests SD config\n"); + user_io_sd_set_config(); + } + + // check if system is trying to access a sdhc card from + // a sd/mmc setup + + // check if an SDHC card is inserted + static char using_sdhc = 1; + + // SD request and + if ((c & 0x03) && !(c & 0x04)) + { + if (using_sdhc) + { + // we have not been using sdhc so far? + // -> complain! + ErrorMessage(" This core does not support\n" + " SDHC cards. Using them may\n" + " lead to data corruption.\n\n" + " Please use an SD card <2GB!", 0); + using_sdhc = 0; + } + } + else + { + // SDHC request from core is always ok + using_sdhc = 1; + } + + if ((c & 0x03) == 0x02) + { + // only write if the inserted card is not sdhc or + // if the core uses sdhc + if(c & 0x04) + { + uint8_t wr_buf[512]; + //iprintf("SD WR %d\n", lba); + + // if we write the sector stored in the read buffer, then + // update the read buffer with the new contents + if (buffer_lba == lba) memcpy(buffer, wr_buf, 512); + + buffer_lba = 0xffffffff; + + // Fetch sector data from FPGA ... + spi_uio_cmd_cont(UIO_SECTOR_WR); + spi_block_read(wr_buf, fio_size); + DisableIO(); + + // ... and write it to disk + diskled_on(); + + if (sd_image.size) + { + if (FileSeekLBA(&sd_image, lba)) + { + FileWriteSec(&sd_image, wr_buf); + } + } + } + } + + if ((c & 0x03) == 0x01) + { + //iprintf("SD RD %d\n", lba); + + // are we using a file as the sd card image? + // (C64 floppy does that ...) + if (buffer_lba != lba) + { + diskled_on(); + + if (sd_image.size) + { + if (FileSeekLBA(&sd_image, lba)) + { + FileReadSec(&sd_image, buffer); + } + } + buffer_lba = lba; + } + + if(buffer_lba == lba) + { + //hexdump(buffer, 32, 0); + + // data is now stored in buffer. send it to fpga + spi_uio_cmd_cont(UIO_SECTOR_RD); + spi_block_write(buffer, fio_size); + DisableIO(); + + // the end of this transfer acknowledges the FPGA internal + // sd card emulation + } + + // just load the next sector now, so it may be prefetched + // for the next request already + diskled_on(); + + if (sd_image.size) + { + if (FileSeekLBA(&sd_image, lba + 1)) + { + FileReadSec(&sd_image, buffer); + } + } + buffer_lba = lba + 1; + } + } + + if(diskled_is_on && CheckTimer(diskled_timer)) + { + DISKLED_OFF; + diskled_is_on = 0; + } + } + + // frequently check ps2 mouse for events + if (CheckTimer(mouse_timer)) + { + mouse_timer = GetTimer(MOUSE_FREQ); + + // has ps2 mouse data been updated in the meantime + if (mouse_flags & 0x08) + { + unsigned char ps2_mouse[3]; + + // PS2 format: + // YOvfl, XOvfl, dy8, dx8, 1, mbtn, rbtn, lbtn + // dx[7:0] + // dy[7:0] + ps2_mouse[0] = mouse_flags; + + // ------ X axis ----------- + // store sign bit in first byte + ps2_mouse[0] |= (mouse_pos[X] < 0) ? 0x10 : 0x00; + if (mouse_pos[X] < -255) + { + // min possible value + overflow flag + ps2_mouse[0] |= 0x40; + ps2_mouse[1] = -128; + } + else if (mouse_pos[X] > 255) + { + // max possible value + overflow flag + ps2_mouse[0] |= 0x40; + ps2_mouse[1] = 255; + } + else + { + ps2_mouse[1] = mouse_pos[X]; + } + + // ------ Y axis ----------- + // store sign bit in first byte + ps2_mouse[0] |= (mouse_pos[Y] < 0) ? 0x20 : 0x00; + if (mouse_pos[Y] < -255) + { + // min possible value + overflow flag + ps2_mouse[0] |= 0x80; + ps2_mouse[2] = -128; + } + else if (mouse_pos[Y] > 255) + { + // max possible value + overflow flag + ps2_mouse[0] |= 0x80; + ps2_mouse[2] = 255; + } + else + { + ps2_mouse[2] = mouse_pos[Y]; + } + + // collect movement info and send at predefined rate + if (!(ps2_mouse[0] == 0x08 && ps2_mouse[1] == 0 && ps2_mouse[2] == 0)) + iprintf("PS2 MOUSE: %x %d %d\n", ps2_mouse[0], ps2_mouse[1], ps2_mouse[2]); + + spi_uio_cmd_cont(UIO_MOUSE); + spi8(ps2_mouse[0]); + spi8(ps2_mouse[1]); + spi8(ps2_mouse[2]); + DisableIO(); + + // reset counters + mouse_flags = 0; + mouse_pos[X] = mouse_pos[Y] = 0; + } + } + + // --------------- THE FOLLOWING IS DEPRECATED AND WILL BE REMOVED ------------ + // ------------------------ USE SD CARD EMULATION INSTEAD --------------------- + + // raw sector io for the atari800 core which include a full + // file system driver usually implemented using a second cpu + static unsigned long bit8_status = 0; + unsigned long status; + + /* read status byte */ + EnableFpga(); + spi8(UIO_GET_STATUS); + status = spi_in(); + status = (status << 8) | spi_in(); + status = (status << 8) | spi_in(); + status = (status << 8) | spi_in(); + DisableFpga(); + /* + if (status != bit8_status) + { + unsigned long sector = (status >> 8) & 0xffffff; + char buffer[512]; + + bit8_status = status; + + // sector read testing + DISKLED_ON; + + // sector read + if (((status & 0xff) == 0xa5) || ((status & 0x3f) == 0x29)) + { + + // extended command with 26 bits (for 32GB SDHC) + if ((status & 0x3f) == 0x29) sector = (status >> 6) & 0x3ffffff; + + bit8_debugf("SECIO rd %ld", sector); + + if (MMC_Read(sector, buffer)) + { + // data is now stored in buffer. send it to fpga + EnableFpga(); + spi8(UIO_SECTOR_SND); // send sector data IO->FPGA + spi_block_write(buffer); + DisableFpga(); + } + else + { + bit8_debugf("rd %ld fail", sector); + } + } + + // sector write + if (((status & 0xff) == 0xa6) || ((status & 0x3f) == 0x2a)) + { + // extended command with 26 bits (for 32GB SDHC) + if ((status & 0x3f) == 0x2a) sector = (status >> 6) & 0x3ffffff; + + bit8_debugf("SECIO wr %ld", sector); + + // read sector from FPGA + EnableFpga(); + spi8(UIO_SECTOR_RCV); // receive sector data FPGA->IO + spi_block_read(buffer); + DisableFpga(); + + if (!MMC_Write(sector, buffer)) bit8_debugf("wr %ld fail", sector); + } + + DISKLED_OFF; + } + */ + } + + if (core_type == CORE_TYPE_ARCHIE) archie_poll(); + + if (CheckTimer(led_timer)) + { + led_timer = GetTimer(LED_FREQ); + uint8_t leds = user_io_kbdled_get_status(); + if ((leds & KBD_LED_FLAG_MASK) != KBD_LED_FLAG_STATUS) leds = 0; + + if ((keyboard_leds & KBD_LED_CAPS_MASK) != (leds & KBD_LED_CAPS_MASK)) + set_kbdled(HID_LED_CAPS_LOCK, (leds & KBD_LED_CAPS_CONTROL) ? leds & KBD_LED_CAPS_STATUS : caps_status); + + if ((keyboard_leds & KBD_LED_NUM_MASK) != (leds & KBD_LED_NUM_MASK)) + set_kbdled(HID_LED_NUM_LOCK, (leds & KBD_LED_NUM_CONTROL) ? leds & KBD_LED_NUM_STATUS : num_status); + + if ((keyboard_leds & KBD_LED_SCRL_MASK) != (leds & KBD_LED_SCRL_MASK)) + set_kbdled(HID_LED_SCROLL_LOCK, (leds & KBD_LED_SCRL_CONTROL) ? leds & KBD_LED_SCRL_STATUS : scrl_status); + keyboard_leds = leds; + } + +} + +char user_io_dip_switch1() +{ + return 0; +} + +char user_io_menu_button() +{ + return((fpga_get_buttons() & BUTTON_OSD) ? 1 : 0); +} + +char user_io_user_button() +{ + return((!user_io_menu_button() && (fpga_get_buttons() & BUTTON_USR)) ? 1 : 0); +} + +static void send_keycode(unsigned short code) +{ + if (core_type == CORE_TYPE_MINIMIG2) + { + // amiga has "break" marker in msb + if (code & BREAK) code = (code & 0xff) | 0x80; + + // send immediately if possible + if (CheckTimer(kbd_timer) && (kbd_fifo_w == kbd_fifo_r)) + { + kbd_fifo_minimig_send(code); + } + else + { + kbd_fifo_enqueue(code); + } + } + + if (core_type == CORE_TYPE_MIST) + { + // atari has "break" marker in msb + if (code & BREAK) code = (code & 0xff) | 0x80; + ikbd_keyboard(code); + } + + if (core_type == CORE_TYPE_8BIT) + { + // send ps2 keycodes for those cores that prefer ps2 + spi_uio_cmd_cont(UIO_KEYBOARD); + + // "pause" has a complex code + if ((code & 0xff) == 0x77) + { + // pause does not have a break code + if (!(code & BREAK)) + { + // Pause key sends E11477E1F014E077 + static const unsigned char c[] = { + 0xe1, 0x14, 0x77, 0xe1, 0xf0, 0x14, 0xf0, 0x77, 0x00 }; + const unsigned char *p = c; + + iprintf("PS2 KBD "); + while (*p) + { + iprintf("%x ", *p); + spi8(*p++); + } + iprintf("\n"); + } + } + else + { + /* + iprintf("PS2 KBD "); + if (code & EXT) iprintf("e0 "); + if (code & BREAK) iprintf("f0 "); + iprintf("%x\n", code & 0xff); + */ + + // prepend extended code flag if required + if (code & EXT) spi8(0xe0); + + // prepend break code if required + if (code & BREAK) spi8(0xf0); + + // send code itself + spi8(code & 0xff); + } + + DisableIO(); + } + + if (core_type == CORE_TYPE_ARCHIE) archie_kbd(code); +} + +void user_io_mouse(unsigned char b, int16_t x, int16_t y) +{ + // send mouse data as minimig expects it + if (core_type == CORE_TYPE_MINIMIG2) + { + mouse_pos[X] += x; + mouse_pos[Y] += y; + mouse_flags |= 0x80 | (b & 3); + } + + // 8 bit core expects ps2 like data + if (core_type == CORE_TYPE_8BIT) + { + mouse_pos[X] += x; + mouse_pos[Y] -= y; // ps2 y axis is reversed over usb + mouse_flags |= 0x08 | (b & 3); + } + + // send mouse data as mist expects it + if (core_type == CORE_TYPE_MIST) ikbd_mouse(b, x, y); + if (core_type == CORE_TYPE_ARCHIE) archie_mouse(b, x, y); +} + +// check if this is a key that's supposed to be suppressed +// when emulation is active +static unsigned char is_emu_key(unsigned char c, unsigned alt) { + static const unsigned char m[] = { JOY_RIGHT, JOY_LEFT, JOY_DOWN, JOY_UP }; + static const unsigned char m2[] = + { + 0x5A, JOY_DOWN, + 0x5C, JOY_LEFT, + 0x5D, JOY_DOWN, + 0x5E, JOY_RIGHT, + 0x60, JOY_UP, + 0x5F, JOY_BTN1, + 0x61, JOY_BTN2 + }; + + if (emu_mode == EMU_NONE) return 0; + + if (alt) + { + for (int i = 0; i<(sizeof(m2) / sizeof(m2[0])); i += 2) if (c == m2[i]) return m2[i + 1]; + } + else + { + // direction keys R/L/D/U + if (c >= 0x4f && c <= 0x52) return m[c - 0x4f]; + } + + return 0; +} + +/* usb modifer bits: +0 1 2 3 4 5 6 7 +LCTRL LSHIFT LALT LGUI RCTRL RSHIFT RALT RGUI +*/ +#define EMU_BTN1 (0+(keyrah*4)) // left control +#define EMU_BTN2 (1+(keyrah*4)) // left shift +#define EMU_BTN3 (2+(keyrah*4)) // left alt +#define EMU_BTN4 (3+(keyrah*4)) // left gui (usually windows key) + +unsigned short keycode(unsigned char in) +{ + if (core_type == CORE_TYPE_MINIMIG2) return usb2amiga(in); + + // atari st and the 8 bit core (currently only used for atari 800) + // use the same key codes + if (core_type == CORE_TYPE_MIST) return usb2atari[in]; + if (core_type == CORE_TYPE_ARCHIE) return usb2archie[in]; + if (core_type == CORE_TYPE_8BIT) return usb2ps2code(in); + + return MISS; +} + +extern configTYPE config; + +void check_reset(unsigned short modifiers, char useKeys) +{ + unsigned short combo[] = + { + 0x45, // lctrl+lalt+ralt + 0x89, // lctrl+lgui+rgui + 0x105, // lctrl+lalt+del + }; + + if ((modifiers & ~2) == combo[useKeys]) + { + if (modifiers & 2) // with lshift - MiST reset + { + reboot(1); + } + + switch (core_type) + { + case CORE_TYPE_MINIMIG2: + ConfigIDE(config.enable_ide, config.hardfile[0].present && config.hardfile[0].enabled, config.hardfile[1].present && config.hardfile[1].enabled); + OsdReset(RESET_NORMAL); + break; + + case CORE_TYPE_8BIT: + kbd_reset = 1; + break; + } + } + else + { + kbd_reset = 0; + } +} + +unsigned short modifier_keycode(unsigned char index) +{ + /* usb modifer bits: + 0 1 2 3 4 5 6 7 + LCTRL LSHIFT LALT LGUI RCTRL RSHIFT RALT RGUI + */ + + if (core_type == CORE_TYPE_MINIMIG2) + { + static const unsigned short amiga_modifier[] = { 0x63, 0x60, 0x64, 0x66, 0x63, 0x61, 0x65, 0x67 }; + return amiga_modifier[index]; + } + + if (core_type == CORE_TYPE_MIST) + { + static const unsigned short atari_modifier[] = { 0x1d, 0x2a, 0x38, MISS, 0x1d, 0x36, 0x38, MISS }; + return atari_modifier[index]; + } + + if (core_type == CORE_TYPE_8BIT) + { + static const unsigned short ps2_modifier[] = { 0x14, 0x12, 0x11, EXT | 0x1f, EXT | 0x14, 0x59, EXT | 0x11, EXT | 0x27 }; + return ps2_modifier[index]; + } + + if (core_type == CORE_TYPE_ARCHIE) + { + static const unsigned short archie_modifier[] = { 0x36, 0x4c, 0x5e, MISS, 0x61, 0x58, 0x60, MISS }; + return archie_modifier[index]; + } + + return MISS; +} + +void user_io_osd_key_enable(char on) +{ + iprintf("OSD is now %s\n", on ? "visible" : "invisible"); + osd_is_visible = on; +} + +static char key_used_by_osd(unsigned short s) +{ + // this key is only used to open the OSD and has no keycode + if ((s & OSD_OPEN) && !(s & 0xff)) return true; + + // no keys are suppressed if the OSD is inactive + if (!osd_is_visible) return false; + + // in atari mode eat all keys if the OSD is online, + // else none as it's up to the core to forward keys + // to the OSD + return((core_type == CORE_TYPE_MIST) || (core_type == CORE_TYPE_ARCHIE) || (core_type == CORE_TYPE_8BIT)); +} + +static char kr_fn_table[] = +{ + 0x54, 0x48, // pause/break + 0x55, 0x46, // prnscr + 0x50, 0x4a, // home + 0x4f, 0x4d, // end + 0x52, 0x4b, // pgup + 0x51, 0x4e, // pgdown + 0x3a, 0x44, // f11 + 0x3b, 0x45, // f12 + + 0x3c, 0x6c, // EMU_MOUSE + 0x3d, 0x6d, // EMU_JOY0 + 0x3e, 0x6e, // EMU_JOY1 + 0x3f, 0x6f, // EMU_NONE + + //Emulate keypad for A600 + 0x1E, 0x59, //KP1 + 0x1F, 0x5A, //KP2 + 0x20, 0x5B, //KP3 + 0x21, 0x5C, //KP4 + 0x22, 0x5D, //KP5 + 0x23, 0x5E, //KP6 + 0x24, 0x5F, //KP7 + 0x25, 0x60, //KP8 + 0x26, 0x61, //KP9 + 0x27, 0x62, //KP0 + 0x2D, 0x56, //KP- + 0x2E, 0x57, //KP+ + 0x31, 0x55, //KP* + 0x2F, 0x68, //KP( + 0x30, 0x69, //KP) + 0x37, 0x63, //KP. + 0x28, 0x58 //KP Enter +}; + +static void keyrah_trans(unsigned char *m, unsigned char *k) +{ + static char keyrah_fn_state = 0; + char fn = 0; + char empty = 1; + char rctrl = 0; + int i = 0; + while (i<6) + { + if ((k[i] == 0x64) || (k[i] == 0x32)) + { + if (k[i] == 0x64) fn = 1; + if (k[i] == 0x32) rctrl = 1; + for (int n = i; n<5; n++) k[n] = k[n + 1]; + k[5] = 0; + } + else + { + if (k[i]) empty = 0; + i++; + } + } + + if (fn) + { + for (i = 0; i<6; i++) + { + for (int n = 0; n<(sizeof(kr_fn_table) / (2 * sizeof(kr_fn_table[0]))); n++) + { + if (k[i] == kr_fn_table[n * 2]) k[i] = kr_fn_table[(n * 2) + 1]; + } + } + } + else + { + // free these keys for core usage + for (i = 0; i<6; i++) + { + if (k[i] == 0x53) k[i] = 0x68; + if (k[i] == 0x47) k[i] = 0x69; + if (k[i] == 0x49) k[i] = 0x6b; // workaround! + } + } + + *m = rctrl ? (*m) | 0x10 : (*m) & ~0x10; + if (fn) + { + keyrah_fn_state |= 1; + if (*m || !empty) keyrah_fn_state |= 2; + } + else + { + if (keyrah_fn_state == 1) + { + if (core_type == CORE_TYPE_MINIMIG2) + { + send_keycode(KEY_MENU); + send_keycode(BREAK | KEY_MENU); + } + else + { + OsdKeySet(KEY_MENU); + } + } + keyrah_fn_state = 0; + } +} + +//Keyrah v2: USB\VID_18D8&PID_0002\A600/A1200_MULTIMEDIA_EXTENSION_VERSION +#define KEYRAH_ID (mist_cfg.keyrah_mode && (((((uint32_t)vid)<<16) | pid) == mist_cfg.keyrah_mode)) + +void user_io_kbd(unsigned char m, unsigned char *k, unsigned short vid, unsigned short pid) +{ + char keyrah = KEYRAH_ID ? 1 : 0; + if (emu_mode == EMU_MOUSE) keyrah <<= 1; + + if (keyrah) keyrah_trans(&m, k); + + unsigned short reset_m = m; + for (char i = 0; i<6; i++) if (k[i] == 0x4c) reset_m |= 0x100; + check_reset(reset_m, KEYRAH_ID ? 1 : mist_cfg.reset_combo); + + if ((core_type == CORE_TYPE_MINIMIG2) || + (core_type == CORE_TYPE_MIST) || + (core_type == CORE_TYPE_ARCHIE) || + (core_type == CORE_TYPE_8BIT)) + { + //iprintf("KBD: %d\n", m); + //hexdump(k, 6, 0); + + static unsigned char modifier = 0, pressed[6] = { 0,0,0,0,0,0 }; + char keycodes[6] = { 0,0,0,0,0,0 }; + uint16_t keycodes_ps2[6] = { 0,0,0,0,0,0 }; + char i, j; + + // remap keycodes if requested + for (i = 0; (i<6) && k[i]; i++) + { + for (j = 0; j3) && *ext) + { + if (!strncasecmp(name + strlen(name) - 3, ext, 3)) + { + printf("%d\n", idx); + return idx; + } + if (strlen(ext) <= 3) break; + idx++; + ext += 3; + } + + printf("0\n", name, ext, 0); + return 0; +} diff --git a/user_io.h b/user_io.h new file mode 100644 index 00000000..643dd1ed --- /dev/null +++ b/user_io.h @@ -0,0 +1,192 @@ +/* +* user_io.h +* +*/ + +#ifndef USER_IO_H +#define USER_IO_H + +#include +#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