--- /dev/null
+diff -Naru fuse-1.1.1/fuse.c fuse-1.1.1.fdd3000/fuse.c
+--- fuse-1.1.1/fuse.c 2013-05-16 22:26:23.000000000 +0200
++++ fuse-1.1.1.fdd3000/fuse.c 2014-02-14 08:28:21.228997802 +0100
+@@ -274,6 +274,7 @@
+ rzx_init();
+ psg_init();
+ beta_init();
++ tim397_init();
+ opus_init();
+ plusd_init();
+ disciple_init();
+diff -Naru fuse-1.1.1/machines/machines_periph.c fuse-1.1.1.fdd3000/machines/machines_periph.c
+--- fuse-1.1.1/machines/machines_periph.c 2013-05-16 22:26:11.000000000 +0200
++++ fuse-1.1.1.fdd3000/machines/machines_periph.c 2014-02-14 08:30:34.228014927 +0100
+@@ -201,6 +201,7 @@
+ periph_set_present( PERIPH_TYPE_FULLER, PERIPH_PRESENT_OPTIONAL );
+ periph_set_present( PERIPH_TYPE_ZXPRINTER, PERIPH_PRESENT_OPTIONAL );
+ periph_set_present( PERIPH_TYPE_DISCIPLE, PERIPH_PRESENT_OPTIONAL );
++ periph_set_present( PERIPH_TYPE_TIM397, PERIPH_PRESENT_OPTIONAL );
+ }
+
+ /* The set of peripherals available on the 128K and similar machines */
+diff -Naru fuse-1.1.1/Makefile.in fuse-1.1.1.fdd3000/Makefile.in
+--- fuse-1.1.1/Makefile.in 2013-05-30 00:21:33.000000000 +0200
++++ fuse-1.1.1.fdd3000/Makefile.in 2014-02-14 08:33:02.997237928 +0100
+@@ -232,7 +232,7 @@
+ LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+ LIBOBJS = @LIBOBJS@
+ LIBPNG_CONFIG = @LIBPNG_CONFIG@
+-LIBS = @LIBS@
++LIBS = @LIBS@ -lrt
+ LIBSPEC_CFLAGS = @LIBSPEC_CFLAGS@
+ LIBSPEC_HAVE_AUDIOFILE = @LIBSPEC_HAVE_AUDIOFILE@
+ LIBSPEC_HAVE_BZ2 = @LIBSPEC_HAVE_BZ2@
+diff -Naru fuse-1.1.1/memory.c fuse-1.1.1.fdd3000/memory.c
+--- fuse-1.1.1/memory.c 2013-05-16 22:26:23.000000000 +0200
++++ fuse-1.1.1.fdd3000/memory.c 2014-02-14 08:21:36.769616238 +0100
+@@ -37,6 +37,7 @@
+ #include "memory.h"
+ #include "module.h"
+ #include "peripherals/disk/opus.h"
++#include "peripherals/disk/ti_m397.h"
+ #include "peripherals/spectranet.h"
+ #include "peripherals/ula.h"
+ #include "settings.h"
+diff -Naru fuse-1.1.1/menu.c fuse-1.1.1.fdd3000/menu.c
+--- fuse-1.1.1/menu.c 2013-05-16 22:26:23.000000000 +0200
++++ fuse-1.1.1.fdd3000/menu.c 2014-02-14 08:21:36.769616238 +0100
+@@ -36,6 +36,7 @@
+ #include "peripherals/disk/beta.h"
+ #include "peripherals/disk/disciple.h"
+ #include "peripherals/disk/opus.h"
++#include "peripherals/disk/ti_m397.h"
+ #include "peripherals/disk/plusd.h"
+ #include "peripherals/ide/divide.h"
+ #include "peripherals/ide/simpleide.h"
+@@ -224,7 +225,8 @@
+ case 18: menu_select_roms_with_title( "+D", 42, 1 ); return;
+ case 19: menu_select_roms_with_title( "DISCiPLE", 43, 1 ); return;
+ case 20: menu_select_roms_with_title( "Opus Discovery", 44, 1 ); return;
+- case 21: menu_select_roms_with_title( "SpeccyBoot", 45, 1 ); return;
++ case 21: menu_select_roms_with_title( "Timex M397", 45, 1 ); return;
++ case 22: menu_select_roms_with_title( "SpeccyBoot", 46, 1 ); return;
+
+ }
+
+diff -Naru fuse-1.1.1/menu_data.dat fuse-1.1.1.fdd3000/menu_data.dat
+--- fuse-1.1.1/menu_data.dat 2013-05-16 22:26:23.000000000 +0200
++++ fuse-1.1.1.fdd3000/menu_data.dat 2014-02-14 08:21:36.769616238 +0100
+@@ -101,7 +101,8 @@
+ Options/Select ROMs/+_D..., Item,, menu_options_selectroms_select,, 18
+ Options/Select ROMs/DISCiP_LE..., Item,, menu_options_selectroms_select,, 19
+ Options/Select ROMs/_Opus Discovery..., Item,, menu_options_selectroms_select,, 20
+-Options/Select ROMs/Specc_yBoot..., Item,, menu_options_selectroms_select,, 21
++Options/Select ROMs/_Timex M397..., Item,, menu_options_selectroms_select,, 21
++Options/Select ROMs/Specc_yBoot..., Item,, menu_options_selectroms_select,, 22
+
+ Options/_Filter..., Item,,, menu_filter_detail
+
+diff -Naru fuse-1.1.1/peripherals/disk/Makefile.am fuse-1.1.1.fdd3000/peripherals/disk/Makefile.am
+--- fuse-1.1.1/peripherals/disk/Makefile.am 2013-05-16 22:26:03.000000000 +0200
++++ fuse-1.1.1.fdd3000/peripherals/disk/Makefile.am 2014-02-14 08:21:36.769616238 +0100
+@@ -37,6 +37,7 @@
+ opus.c \
+ plusd.c \
+ upd_fdc.c \
++ ti_m397.c \
+ wd_fdc.c
+
+ noinst_HEADERS = beta.h \
+@@ -47,4 +48,5 @@
+ opus.h \
+ plusd.h \
+ upd_fdc.h \
++ ti_m397.h \
+ wd_fdc.h
+diff -Naru fuse-1.1.1/peripherals/disk/Makefile.in fuse-1.1.1.fdd3000/peripherals/disk/Makefile.in
+--- fuse-1.1.1/peripherals/disk/Makefile.in 2013-05-24 22:56:08.000000000 +0200
++++ fuse-1.1.1.fdd3000/peripherals/disk/Makefile.in 2014-02-14 08:32:46.900294664 +0100
+@@ -75,7 +75,7 @@
+ libdisk_a_LIBADD =
+ am_libdisk_a_OBJECTS = beta.$(OBJEXT) crc.$(OBJEXT) disciple.$(OBJEXT) \
+ disk.$(OBJEXT) fdd.$(OBJEXT) opus.$(OBJEXT) plusd.$(OBJEXT) \
+- upd_fdc.$(OBJEXT) wd_fdc.$(OBJEXT)
++ upd_fdc.$(OBJEXT) ti_m397.$(OBJEXT) wd_fdc.$(OBJEXT)
+ libdisk_a_OBJECTS = $(am_libdisk_a_OBJECTS)
+ DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+ depcomp = $(SHELL) $(top_srcdir)/depcomp
+@@ -281,6 +281,7 @@
+ opus.c \
+ plusd.c \
+ upd_fdc.c \
++ ti_m397.c \
+ wd_fdc.c
+
+ noinst_HEADERS = beta.h \
+@@ -291,6 +292,7 @@
+ opus.h \
+ plusd.h \
+ upd_fdc.h \
++ ti_m397.h \
+ wd_fdc.h
+
+ all: all-am
+@@ -349,6 +351,7 @@
+ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/opus.Po@am__quote@
+ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plusd.Po@am__quote@
+ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upd_fdc.Po@am__quote@
++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ti_m397.Po@am__quote@
+ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wd_fdc.Po@am__quote@
+
+ .c.o:
+diff -Naru fuse-1.1.1/peripherals/disk/ti_m397.c fuse-1.1.1.fdd3000/peripherals/disk/ti_m397.c
+--- fuse-1.1.1/peripherals/disk/ti_m397.c 1970-01-01 01:00:00.000000000 +0100
++++ fuse-1.1.1.fdd3000/peripherals/disk/ti_m397.c 2014-02-14 08:53:54.159789884 +0100
+@@ -0,0 +1,562 @@
++/******************************************************************************
++ This file is a part od FDD3000 Emulator
++
++ Copyright (C) 2013 by SÅ‚awomir Szczyrba <sszczyrba@gmail.com>
++ Copyright (C) 2014 by Stefano Bodrato <stefano_bodrato hotmail dt com>
++
++ This program is free software: you can redistribute it and/or modify it under
++ the terms of the GNU Lesser General Public License as published by the Free
++ Software Foundation, either version 2 of the License, or (at your option) any
++ later version.
++
++ This program is distributed in the hope that it will be useful, but WITHOUT
++ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
++ FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
++ details.
++
++ You should have received a copy of the GNU Lesser General Public License
++ along with this program. If not, see <http://www.gnu.org/licenses/lgpl.html>
++******************************************************************************/
++
++
++#include <config.h>
++
++#include <libspectrum.h>
++
++#include <string.h>
++
++#include "compat.h"
++#include "machine.h"
++#include "module.h"
++#include "ti_m397.h"
++#include "peripherals/printer.h"
++#include "settings.h"
++#include "ui/ui.h"
++#include "unittests/unittests.h"
++//#include "utils.h"
++#include "wd_fdc.h"
++#include "options.h" /* needed for get combo options */
++#include "z80/z80.h"
++
++
++#include <errno.h>
++#include <fcntl.h>
++#include <signal.h>
++#include <stdio.h>
++#include <string.h>
++#include <sys/stat.h> /* For mode constants */
++#include <sys/types.h>
++#include <unistd.h>
++
++
++#if defined (__MINGW32__) || defined (_WIN32) || defined (_WIN32_) || defined (__WIN32__)
++
++#include <windows.h>
++#include <tchar.h>
++
++#else
++
++#include <semaphore.h>
++#ifndef SEM_FAILED
++#define SEM_FAILED -1
++#endif
++#include <sys/mman.h>
++#include <sys/ipc.h>
++#include <sys/msg.h>
++#include <sys/socket.h>
++
++#endif
++
++
++
++/* IPC */
++
++typedef struct tFdd2TiLink {
++ uint8_t fdd2ti;
++ uint8_t ti2fdd;
++ uint8_t fddAlive;
++ uint8_t tiAlive;
++} fdd2TiLink;
++
++fdd2TiLink * fddlink;
++
++
++uint8_t data;
++int ipc, connected;
++char * path;
++
++
++
++#if defined (__MINGW32__) || defined (_WIN32) || defined (_WIN32_) || defined (__WIN32__)
++
++HANDLE fdd2tiF, fdd2tiE, ti2fddF, ti2fddE, fddAlive;
++HANDLE shm, shmf;
++DWORD dwRetVal = 0;
++
++TCHAR *shmId = _T("\\fdd.data");
++//TCHAR shmId[]=TEXT("Global\\fdd.data");
++TCHAR szTempFileName[MAX_PATH];
++TCHAR lpTempPathBuffer[MAX_PATH];
++
++TCHAR fdd2tiE_Id[]=TEXT("Global\\fdd.fdd2ti.empty");
++TCHAR fdd2tiF_Id[]=TEXT("Global\\fdd.fdd2ti.full");
++TCHAR ti2fddE_Id[]=TEXT("Global\\fdd.ti2fdd.empty");
++TCHAR ti2fddF_Id[]=TEXT("Global\\fdd.ti2fdd.full");
++TCHAR fddAlive_Id[]=TEXT("Global\\fdd.alive");
++/*
++TCHAR * fddAlive_Id = "fdd.alive";
++TCHAR * fdd2tiE_Id = "fdd.fdd2ti.empty";
++TCHAR * fdd2tiF_Id = "fdd.fdd2ti.full";
++TCHAR * ti2fddE_Id = "fdd.ti2fdd.empty";
++TCHAR * ti2fddF_Id = "fdd.ti2fdd.full";
++*/
++#else
++
++sem_t *fdd2tiF,*fdd2tiE,*ti2fddF,*ti2fddE,*fddAlive;
++int shm;
++const char * shmId = "fdd.data";
++
++const char * fddAlive_Id = "fdd.alive";
++const char * fdd2tiE_Id = "fdd.fdd2ti.empty";
++const char * fdd2tiF_Id = "fdd.fdd2ti.full";
++const char * ti2fddE_Id = "fdd.ti2fdd.empty";
++const char * ti2fddF_Id = "fdd.ti2fdd.full";
++
++#endif
++
++int8_t fddRead() {
++ uint8_t data=0;
++#if defined (__MINGW32__) || defined (_WIN32) || defined (_WIN32_) || defined (__WIN32__)
++ WaitForSingleObject(ti2fddF,INFINITE);
++ data=fddlink->fdd2ti;
++ ReleaseSemaphore(ti2fddE,1,NULL);
++#else
++ sem_wait(fdd2tiF);
++ data=fddlink->fdd2ti;
++ sem_post(fdd2tiE);
++#endif
++ return data;
++}
++
++void fddWrite(int8_t d) {
++#if defined (__MINGW32__) || defined (_WIN32) || defined (_WIN32_) || defined (__WIN32__)
++ WaitForSingleObject(ti2fddE,INFINITE);
++ fddlink->ti2fdd=d;
++ ReleaseSemaphore(ti2fddF,1,NULL);
++#else
++ sem_wait(ti2fddE);
++ fddlink->ti2fdd=d;
++ sem_post(ti2fddF);
++#endif
++}
++
++uint8_t fddIsAlive() {
++ uint8_t ok;
++#if defined (__MINGW32__) || defined (_WIN32) || defined (_WIN32_) || defined (__WIN32__)
++ WaitForSingleObject(fddAlive,INFINITE);
++ ok=fddlink->fddAlive&fddlink->tiAlive;
++ ReleaseSemaphore(fddAlive,1,NULL);
++#else
++ sem_wait(fddAlive);
++ ok=fddlink->fddAlive&fddlink->tiAlive;
++ sem_post(fddAlive);
++#endif
++ return ok;
++}
++
++void cleanUp() {
++ int i;
++ if(!ipc) { return; }
++ ipc=0;
++ connected=0;
++
++#if defined (__MINGW32__) || defined (_WIN32) || defined (_WIN32_) || defined (__WIN32__)
++
++ WaitForSingleObject(fddAlive,INFINITE);
++ fddlink->tiAlive=0;
++ i=fddlink->fddAlive;
++ ReleaseSemaphore(fddAlive,1,NULL);
++
++// semResetAll();
++ CloseHandle(fddAlive);
++ CloseHandle(ti2fddF);
++ CloseHandle(ti2fddE);
++ CloseHandle(fdd2tiF);
++ CloseHandle(fdd2tiE);
++
++ UnmapViewOfFile(fddlink);
++ if(!i) {
++ CloseHandle(shm);
++ CloseHandle(shmf);
++ }
++
++#else
++
++ sem_wait(fddAlive);
++ fddlink->tiAlive=0;
++ i=fddlink->fddAlive;
++ sem_post(fddAlive);
++
++// semResetAll();
++ sem_close(fddAlive);
++ sem_close(ti2fddF);
++ sem_close(ti2fddE);
++ sem_close(fdd2tiF);
++ sem_close(fdd2tiE);
++
++ munmap(fddlink,sizeof(fdd2TiLink));
++ if(!i) {
++ sem_unlink(fdd2tiE_Id);
++ sem_unlink(fdd2tiF_Id);
++ sem_unlink(ti2fddE_Id);
++ sem_unlink(ti2fddF_Id);
++ sem_unlink(fddAlive_Id);
++ shm_unlink(shmId);
++ }
++
++#endif
++
++}
++
++int openShm() {
++ int trunc;
++ if(!ipc) {
++#if defined (__MINGW32__) || defined (_WIN32) || defined (_WIN32_) || defined (__WIN32__)
++ //shmfile=ev.value("TMP",ev.value("TEMP","."))+"\\fdd.data";
++
++ // Gets the temp path env string (no guarantee it's a valid path).
++ dwRetVal = GetTempPath(MAX_PATH, lpTempPathBuffer); // buffer for path
++ if (dwRetVal > MAX_PATH || (dwRetVal == 0))
++ {
++// fprintf(stderr,"tim397_openShm: GetTempPath failed\n");
++ return (-1);
++ }
++ snprintf(szTempFileName,MAX_PATH,"%s%s",lpTempPathBuffer,shmId);
++
++ shmf = CreateFile(szTempFileName, GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL);
++
++ if (shmf == INVALID_HANDLE_VALUE)
++ {
++ //ui_error( UI_ERROR_ERROR, "tim397_openShm: CreateFile failed\n");
++ return (-1);
++ }
++
++ shm = CreateFileMapping(
++ shmf,//INVALID_HANDLE_VALUE, // use paging file
++ NULL, // default security
++ PAGE_READWRITE, // read/write access
++ 0, // maximum object size (high-order DWORD)
++ 256, // maximum object size (low-order DWORD)
++ NULL);//TEXT("Global\\shmFdd")); // name of mapping object
++ if(shm==NULL){
++ //ui_error( UI_ERROR_ERROR, "tim397_openShm: CreateFileMapping failed\n");
++ CloseHandle(shmf);
++ return (-1);
++ }
++ //fddlink=(fdd2TiLink*)mmap(0,sizeof(fdd2TiLink),PROT_READ|PROT_WRITE,MAP_SHARED,shm,0);
++ fddlink = (fdd2TiLink*)MapViewOfFile(shm, // handle to map object
++ FILE_MAP_ALL_ACCESS, // read/write permission
++ 0,
++ 0,
++ sizeof(fdd2TiLink));
++ if(fddlink==NULL){
++ //ui_error( UI_ERROR_ERROR, "tim397_openShm: MapViewOfFile (mmap) failed\n");
++ CloseHandle(shm);
++ CloseHandle(shmf);
++ return (-1);
++ }
++
++ fdd2tiE=OpenSemaphore(SEMAPHORE_MODIFY_STATE|SYNCHRONIZE, TRUE, fdd2tiE_Id);
++ fdd2tiF=OpenSemaphore(SEMAPHORE_MODIFY_STATE|SYNCHRONIZE, TRUE, fdd2tiF_Id);
++ ti2fddE=OpenSemaphore(SEMAPHORE_MODIFY_STATE|SYNCHRONIZE, TRUE, ti2fddE_Id);
++ ti2fddF=OpenSemaphore(SEMAPHORE_MODIFY_STATE|SYNCHRONIZE, TRUE, ti2fddF_Id);
++ fddAlive=OpenSemaphore(SEMAPHORE_MODIFY_STATE|SYNCHRONIZE, TRUE, fddAlive_Id);
++
++ if ((fdd2tiE==NULL) || (fdd2tiF==NULL) ||
++ (ti2fddE==NULL) || (ti2fddF==NULL) || (fddAlive==NULL)) {
++ //ui_error( UI_ERROR_ERROR, "tim397_openShm: semaphore creation failed\n");
++ return (-1);
++ }
++
++#else
++ shm = shm_open(shmId, O_RDWR, 0666);
++ if (-1==shm) {
++ //ui_error( UI_ERROR_ERROR, "tim397_openShm: shm_open failed\n");
++ return (-1);
++ }
++ trunc=ftruncate(shm,sizeof(fdd2TiLink));
++ if (-1==trunc) {
++ //ui_error( UI_ERROR_ERROR, "tim397_openShm: ftruncate failed\n");
++ return (-1);
++ }
++ fddlink=(fdd2TiLink*)mmap(0,sizeof(fdd2TiLink),PROT_READ|PROT_WRITE,MAP_SHARED,shm,0);
++ if(fddlink==MAP_FAILED) {
++ //ui_error( UI_ERROR_ERROR, "tim397_openShm: mmap failed\n");
++ return (-1);
++ }
++ close(shm);
++
++ fdd2tiE=sem_open(fdd2tiE_Id,O_RDWR,0666,1);
++ fdd2tiF=sem_open(fdd2tiF_Id,O_RDWR,0666,0);
++ ti2fddE=sem_open(ti2fddE_Id,O_RDWR,0666,1);
++ ti2fddF=sem_open(ti2fddF_Id,O_RDWR,0666,0);
++ fddAlive=sem_open(fddAlive_Id,O_RDWR,0666,1);
++ if ((fdd2tiE==SEM_FAILED) || (fdd2tiF==SEM_FAILED) ||
++ (ti2fddE==SEM_FAILED) || (ti2fddF==SEM_FAILED) || (fddAlive==SEM_FAILED)) {
++ //ui_error( UI_ERROR_ERROR, "tim397_openShm: semaphore creation failed\n");
++ return (-1);
++ }
++#endif
++
++ ipc=1;
++ }
++ if(ipc&&(!connected)) {
++#if defined (__MINGW32__) || defined (_WIN32) || defined (_WIN32_) || defined (__WIN32__)
++ WaitForSingleObject(fddAlive,INFINITE);
++ if(fddlink->fddAlive){ fddlink->tiAlive=1; connected=1; }
++ ReleaseSemaphore(fddAlive,1,NULL);
++#else
++ sem_wait(fddAlive);
++ if(fddlink->fddAlive){ fddlink->tiAlive=1; connected=1; }
++ sem_post(fddAlive);
++#endif
++ }
++ return (0);
++}
++
++
++
++/*************************************************************************/
++
++libspectrum_byte tim397_io_read( libspectrum_word port, int *attached ) {
++ libspectrum_byte b = 0;
++//ui_error( UI_ERROR_ERROR, "Reading from port: %u\n",port);
++ if(ipc&&connected) {
++//ui_error( UI_ERROR_ERROR, "Reading from port: %u\n",port);
++ if(fddIsAlive()) { b=fddRead(); } else { connected=0; /*semResetAll();*/ return 0; }
++ *attached = 1;
++ }
++ return b;
++}
++
++void tim397_io_write( libspectrum_word port, libspectrum_byte b ) {
++//ui_error( UI_ERROR_ERROR, "Writing %u to port: %u\n",b,port);
++ if(ipc&&connected) {
++//ui_error( UI_ERROR_ERROR, "Writing %u to port: %u\n",b,port);
++// if ((port&&0xff) == 0xef)
++ if(fddIsAlive()) { fddWrite(b); } else { connected=0; /*semResetAll();*/ }
++ }
++}
++
++/*************************************************************************/
++
++static void tim397_reset( int hard_reset );
++static void tim397_memory_map( void );
++static void tim397_enabled_snapshot( libspectrum_snap *snap );
++static void tim397_from_snapshot( libspectrum_snap *snap );
++static void tim397_to_snapshot( libspectrum_snap *snap );
++static void tim397_activate( void );
++
++
++/* Two 8Kb memory chunks accessible by the Z80 when /ROMCS is low */
++static memory_page tim397_memory_map_romcs_rom[ MEMORY_PAGES_IN_8K ];
++static memory_page tim397_memory_map_romcs_ram[ MEMORY_PAGES_IN_8K ];
++static int tim397_memory_source;
++
++int tim397_available = 0;
++int tim397_active = 0;
++
++static libspectrum_byte *tim397_ram;
++static int memory_allocated = 0;
++
++static module_info_t tim397_module_info = {
++ tim397_reset,
++ tim397_memory_map,
++ tim397_enabled_snapshot,
++ tim397_from_snapshot,
++ tim397_to_snapshot
++};
++
++void
++tim397_page( void )
++{
++ tim397_active = 1;
++ machine_current->ram.romcs = 1;
++ machine_current->memory_map();
++ //ui_error( UI_ERROR_ERROR, "tim397_page, PC: $%x\n",m397_pageflag);
++ ui_statusbar_update( UI_STATUSBAR_ITEM_DISK, UI_STATUSBAR_STATE_ACTIVE );
++}
++
++void
++tim397_unpage( void )
++{
++ tim397_active = 0;
++ machine_current->ram.romcs = 0;
++ machine_current->memory_map();
++ //ui_error( UI_ERROR_ERROR, "tim397_unpage, PC: $%x\n",z80.pc.w);
++ ui_statusbar_update( UI_STATUSBAR_ITEM_DISK, UI_STATUSBAR_STATE_INACTIVE );
++}
++
++static void
++tim397_memory_map( void )
++{
++ if( !tim397_active ) return;
++ memory_map_romcs_8k( 0x0000, tim397_memory_map_romcs_rom );
++ memory_map_romcs_8k( 0x2000, tim397_memory_map_romcs_ram );
++// ui_error( UI_ERROR_ERROR, "tim397_memory_map, PC: $%x\n",z80.pc.w);
++}
++
++static const periph_port_t tim397_ports[] = {
++ { 0x00ff, 0x00ef, tim397_io_read, tim397_io_write },
++ { 0, 0, NULL, NULL }
++};
++
++static const periph_t tim397_periph = {
++ &settings_current.ti_m397,
++ tim397_ports,
++ 1,
++ tim397_activate
++};
++
++int
++tim397_init( void )
++{
++ int i;
++ module_register( &tim397_module_info );
++
++ tim397_memory_source = memory_source_register( "TI_M397" );
++
++ for( i = 0; i < MEMORY_PAGES_IN_8K; i++ )
++ tim397_memory_map_romcs_rom[ i ].source = tim397_memory_source;
++ for( i = 0; i < MEMORY_PAGES_IN_8K; i++ )
++ tim397_memory_map_romcs_ram[ i ].source = tim397_memory_source;
++
++ periph_register( PERIPH_TYPE_TIM397, &tim397_periph );
++ ipc=0;
++
++ fprintf(stderr,"tim397_init\n");
++
++ return 0;
++}
++
++
++static void
++tim397_reset( int hard_reset )
++{
++
++ tim397_active = 0;
++ tim397_available = 0;
++ int i;
++
++ if( !periph_is_active( PERIPH_TYPE_TIM397 ) ) {
++ ui_statusbar_update( UI_STATUSBAR_ITEM_DISK,
++ UI_STATUSBAR_STATE_NOT_AVAILABLE );
++ return;
++ }
++
++ if (openShm()!=0) {
++ settings_current.ti_m397 = 0;
++ periph_activate_type( PERIPH_TYPE_TIM397, 0 );
++// ui_error( UI_ERROR_ERROR, "Link to FDD3000 emulator failed\n");
++ return;
++ }
++
++ if( machine_load_rom_bank( tim397_memory_map_romcs_rom, 0,
++ settings_current.rom_tim397,
++ settings_default.rom_tim397, 0x2000 ) ) {
++ settings_current.ti_m397 = 0;
++ periph_activate_type( PERIPH_TYPE_TIM397, 0 );
++ ui_statusbar_update( UI_STATUSBAR_ITEM_DISK,
++ UI_STATUSBAR_STATE_NOT_AVAILABLE );
++ return;
++ }
++
++ machine_current->ram.romcs = 0;
++
++ for( i = 0; i < MEMORY_PAGES_IN_8K; i++ ) {
++ tim397_memory_map_romcs_ram[ i ].page = &tim397_ram[ i * MEMORY_PAGE_SIZE ];
++ tim397_memory_map_romcs_ram[ i ].writable = 1;
++ }
++
++ tim397_available = 1;
++ tim397_active = 0;
++
++ if( hard_reset ) {
++ memset( tim397_ram, 0, 0x2000 );
++ }
++
++ machine_current->memory_map();
++ ui_statusbar_update( UI_STATUSBAR_ITEM_DISK, UI_STATUSBAR_STATE_INACTIVE );
++ fprintf(stderr,"tim397_reset\n");
++}
++
++void
++tim397_end( void )
++{
++ tim397_available = 0;
++
++ if(ipc&&connected) {
++#if defined (__MINGW32__) || defined (_WIN32) || defined (_WIN32_) || defined (__WIN32__)
++ WaitForSingleObject(fddAlive,INFINITE);
++ fddlink->tiAlive=0;
++ ReleaseSemaphore(fddAlive,1,NULL);
++#else
++ sem_wait(fddAlive);
++ fddlink->tiAlive=0;
++ sem_post(fddAlive);
++#endif
++ }
++ ipc=0;
++ connected=0;
++ cleanUp();
++ fprintf(stderr,"tim397_end\n");
++}
++
++static void
++tim397_activate( void )
++{
++ if( !memory_allocated ) {
++ tim397_ram = memory_pool_allocate_persistent( 0x2000, 1 );
++ memory_allocated = 1;
++ }
++}
++
++int
++tim397_unittest( void )
++{
++ int r = 0;
++
++ tim397_page();
++
++ r += unittests_assert_16k_page( 0x0000, tim397_memory_source, 0 );
++ r += unittests_assert_16k_ram_page( 0x4000, 5 );
++ r += unittests_assert_16k_ram_page( 0x8000, 2 );
++ r += unittests_assert_16k_ram_page( 0xc000, 0 );
++
++ tim397_unpage();
++
++ r += unittests_paging_test_48( 2 );
++
++ return r;
++}
++
++
++static void
++tim397_enabled_snapshot( libspectrum_snap *snap )
++{
++ return;
++}
++
++static void
++tim397_from_snapshot( libspectrum_snap *snap )
++{
++ return;
++}
++
++static void
++tim397_to_snapshot( libspectrum_snap *snap GCC_UNUSED )
++{
++ //if( !periph_is_active( PERIPH_TYPE_TIM397 ) ) return;
++ return;
++}
++
+diff -Naru fuse-1.1.1/peripherals/disk/ti_m397.h fuse-1.1.1.fdd3000/peripherals/disk/ti_m397.h
+--- fuse-1.1.1/peripherals/disk/ti_m397.h 1970-01-01 01:00:00.000000000 +0100
++++ fuse-1.1.1.fdd3000/peripherals/disk/ti_m397.h 2014-02-14 08:31:43.972546695 +0100
+@@ -0,0 +1,28 @@
++/*
++ TIM397.h: Routines for handling the Timex TIM397
++*/
++
++#ifndef FUSE_TIM397_H
++#define FUSE_TIM397_H
++
++#include <config.h>
++
++#include <libspectrum.h>
++
++#include "fdd.h"
++
++extern int tim397_available; /* Is the tim397 available for use? */
++extern int tim397_active; /* tim397 enabled? */
++
++int tim397_init( void );
++void tim397_end( void );
++
++void tim397_page( void );
++void tim397_unpage( void );
++
++libspectrum_byte tim397_io_read( libspectrum_word port, int *attached );
++void tim397_io_write( libspectrum_word port, libspectrum_byte b );
++
++int tim397_unittest( void );
++
++#endif /* #ifndef FUSE_tim397_H */
+diff -Naru fuse-1.1.1/periph.h fuse-1.1.1.fdd3000/periph.h
+--- fuse-1.1.1/periph.h 2013-05-24 22:42:22.000000000 +0200
++++ fuse-1.1.1.fdd3000/periph.h 2014-02-14 08:21:36.769616238 +0100
+@@ -65,6 +65,7 @@
+ PERIPH_TYPE_SPECCYBOOT, /* SpeccyBoot interface */
+ PERIPH_TYPE_SPECDRUM, /* SpecDrum interface */
+ PERIPH_TYPE_SPECTRANET, /* Spectranet interface */
++ PERIPH_TYPE_TIM397, /* FDD3000 disk interface */
+ PERIPH_TYPE_ULA, /* Standard ULA */
+ PERIPH_TYPE_ULA_FULL_DECODE,/* Standard ULA responding only to 0xfe */
+ PERIPH_TYPE_UPD765, /* +3 uPD765 FDC */
+diff -Naru fuse-1.1.1/settings.dat fuse-1.1.1.fdd3000/settings.dat
+--- fuse-1.1.1/settings.dat 2013-05-16 22:26:23.000000000 +0200
++++ fuse-1.1.1.fdd3000/settings.dat 2014-02-14 08:27:14.978179040 +0100
+@@ -64,6 +64,7 @@
+ interface2, boolean, 1
+ snapsasz80, null, 0
+ opus, boolean, 0
++ti_m397, boolean, 0
+ pal_tv2x, boolean, 0
+ movie_compr, string, NULL
+ movie_start, string, NULL
+@@ -235,6 +236,7 @@
+ rom_plusd, string, "plusd.rom",
+ rom_disciple, string, "disciple.rom",
+ rom_opus, string, "opus.rom",
++rom_tim397, string, "ti_m397.rom",
+ rom_speccyboot, string, "speccyboot-1.4.rom",
+
+ drive_plus3a_type, string, NULL
+diff -Naru fuse-1.1.1/settings.pl fuse-1.1.1.fdd3000/settings.pl
+--- fuse-1.1.1/settings.pl 2013-05-24 22:42:22.000000000 +0200
++++ fuse-1.1.1.fdd3000/settings.pl 2014-02-14 08:25:30.881179995 +0100
+@@ -762,7 +762,8 @@
+ case 42: return &( settings->rom_plusd );
+ case 43: return &( settings->rom_disciple );
+ case 44: return &( settings->rom_opus );
+- case 45: return &( settings->rom_speccyboot );
++ case 45: return &( settings->rom_tim397 );
++ case 46: return &( settings->rom_speccyboot );
+ default: return NULL;
+ }
+ }
+diff -Naru fuse-1.1.1/ui/options.dat fuse-1.1.1.fdd3000/ui/options.dat
+--- fuse-1.1.1/ui/options.dat 2013-05-24 22:42:22.000000000 +0200
++++ fuse-1.1.1.fdd3000/ui/options.dat 2014-02-14 08:21:36.769616238 +0100
+@@ -64,6 +64,7 @@
+ Checkbox, (B)eta 128 interface, beta128, INPUT_KEY_b
+ Checkbox, Beta 128 (a)uto-boot in 48K machines, beta128_48boot, INPUT_KEY_a
+ Checkbox, (O)pus Discovery interface, opus, INPUT_KEY_o
++Checkbox, FDD(3)000 / M397 interface, ti_m397, INPUT_KEY_3
+ Postcheck, periph_postcheck
+ Posthook, periph_posthook
+
+diff -Naru fuse-1.1.1/ui/ui.h fuse-1.1.1.fdd3000/ui/ui.h
+--- fuse-1.1.1/ui/ui.h 2013-05-16 22:26:23.000000000 +0200
++++ fuse-1.1.1.fdd3000/ui/ui.h 2014-02-14 08:21:36.769616238 +0100
+@@ -39,6 +39,7 @@
+ #include "peripherals/disk/beta.h"
+ #include "peripherals/disk/disciple.h"
+ #include "peripherals/disk/opus.h"
++#include "peripherals/disk/ti_m397.h"
+ #include "peripherals/disk/plusd.h"
+ #include "ui/scaler/scaler.h"
+
+@@ -211,6 +212,7 @@
+ UI_MENU_ITEM_MEDIA_DISK_OPUS_2_EJECT,
+ UI_MENU_ITEM_MEDIA_DISK_OPUS_2_FLIP_SET,
+ UI_MENU_ITEM_MEDIA_DISK_OPUS_2_WP_SET,
++ UI_MENU_ITEM_MEDIA_DISK_TIM397,
+ UI_MENU_ITEM_MEDIA_IDE,
+ UI_MENU_ITEM_MEDIA_IDE_SIMPLE8BIT,
+ UI_MENU_ITEM_MEDIA_IDE_SIMPLE8BIT_MASTER_EJECT,
+diff -Naru fuse-1.1.1/ui.c fuse-1.1.1.fdd3000/ui.c
+--- fuse-1.1.1/ui.c 2013-05-16 22:26:23.000000000 +0200
++++ fuse-1.1.1.fdd3000/ui.c 2014-02-14 08:51:58.576646341 +0100
+@@ -548,6 +549,8 @@
+ "/Media/Disk/Opus/Drive 2/Write protect/Enable",
+ "/Media/Disk/Opus/Drive 2/Write protect/Disable", 1 },
+
++ { UI_MENU_ITEM_MEDIA_DISK_TIM397, "/Media/Disk/Timex M397" },
++
+ { UI_MENU_ITEM_MEDIA_IDE, "/Media/IDE" },
+
+ { UI_MENU_ITEM_MEDIA_IDE_SIMPLE8BIT, "/Media/IDE/Simple 8-bit" },
+diff -Naru fuse-1.1.1/z80/coretest.c fuse-1.1.1.fdd3000/z80/coretest.c
+--- fuse-1.1.1/z80/coretest.c 2013-05-16 22:26:16.000000000 +0200
++++ fuse-1.1.1.fdd3000/z80/coretest.c 2014-02-14 08:21:36.769616238 +0100
+@@ -459,6 +459,21 @@
+ abort();
+ }
+
++int tim397_available = 0;
++int tim397_active = 0;
++
++void
++tim397_page( void )
++{
++ abort();
++}
++
++void
++tim397_unpage( void )
++{
++ abort();
++}
++
+ int plusd_available = 0;
+ int plusd_active = 0;
+
+diff -Naru fuse-1.1.1/z80/z80_checks.h fuse-1.1.1.fdd3000/z80/z80_checks.h
+--- fuse-1.1.1/z80/z80_checks.h 2013-05-16 22:26:16.000000000 +0200
++++ fuse-1.1.1.fdd3000/z80/z80_checks.h 2014-02-14 08:23:42.063410052 +0100
+@@ -2,6 +2,7 @@
+ SETUP_CHECK( rzx, rzx_playback )
+ SETUP_CHECK( debugger, debugger_mode != DEBUGGER_MODE_INACTIVE )
+ SETUP_CHECK( beta, beta_available )
++SETUP_CHECK( tim397, tim397_available )
+ SETUP_CHECK( plusd, plusd_available )
+ SETUP_CHECK( disciple, disciple_available )
+ SETUP_CHECK( if1p, if1_available )
+diff -Naru fuse-1.1.1/z80/z80_ops.c fuse-1.1.1.fdd3000/z80/z80_ops.c
+--- fuse-1.1.1/z80/z80_ops.c 2013-05-16 22:26:16.000000000 +0200
++++ fuse-1.1.1.fdd3000/z80/z80_ops.c 2014-02-14 08:23:33.485342586 +0100
+@@ -36,6 +36,7 @@
+ #include "peripherals/disk/disciple.h"
+ #include "peripherals/disk/opus.h"
+ #include "peripherals/disk/plusd.h"
++#include "peripherals/disk/ti_m397.h"
+ #include "peripherals/ide/divide.h"
+ #include "peripherals/if1.h"
+ #include "peripherals/spectranet.h"
+@@ -176,6 +177,18 @@
+ }
+
+ END_CHECK
++
++ CHECK( tim397, tim397_available )
++
++ if( tim397_active ) {
++ if( PC == 0x0604 ) {
++ tim397_unpage();
++ }
++ } else if( PC == 0x0000 || PC == 0x0008 ) {
++ tim397_page();
++ }
++
++ END_CHECK
+
+ CHECK( plusd, plusd_available )
+