floppyemu/floppy_emu_arduino

This commit is contained in:
Sin Shimozono 2014-07-15 08:48:56 +09:00
parent 0a8a67e352
commit b58b3da080
28 changed files with 386 additions and 2632 deletions

View File

@ -1,7 +1,8 @@
avrtarget/ClockFrequency=16000000
avrtarget/ExtRAMSize=0
avrtarget/ExtendedRAM=false
avrtarget/MCUType=atmega1284p
avrtarget/MCUType=atmega2560
avrtarget/UseEEPROM=false
avrtarget/UseExtendedRAMforHeap=true
avrtarget/perConfig=false
eclipse.preferences.version=1

View File

@ -4,5 +4,4 @@ avrtarget/ExtendedRAM=false
avrtarget/MCUType=atmega1284p
avrtarget/UseEEPROM=false
avrtarget/UseExtendedRAMforHeap=true
avrtarget/perConfig=false
eclipse.preferences.version=1

View File

@ -1,67 +0,0 @@
eclipse.preferences.version=1
org.eclipse.cdt.codan.checkers.errnoreturn=Warning
org.eclipse.cdt.codan.checkers.errnoreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false}
org.eclipse.cdt.codan.checkers.errreturnvalue=Error
org.eclipse.cdt.codan.checkers.errreturnvalue.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.checkers.noreturn=Error
org.eclipse.cdt.codan.checkers.noreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false}
org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation=Error
org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem=Error
org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem=Warning
org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem=Error
org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem=Warning
org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},no_break_comment\=>"no break",last_case_param\=>false,empty_case_param\=>false}
org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning
org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},unknown\=>false,exceptions\=>()}
org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=Error
org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization=Warning
org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},skip\=>true}
org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=Error
org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem=Error
org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.InvalidArguments=Error
org.eclipse.cdt.codan.internal.checkers.InvalidArguments.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem=Error
org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem=Error
org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem=Error
org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=Error
org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info
org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},pattern\=>"^[a-z]",macro\=>true,exceptions\=>()}
org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning
org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.OverloadProblem=Error
org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem=Error
org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem=Error
org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem=-Warning
org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem=-Warning
org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem=Warning
org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>()}
org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem=Warning
org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},paramNot\=>false}
org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem=Warning
org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},else\=>false,afterelse\=>false}
org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem=Error
org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}
org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem=Warning
org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true}
org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem=Warning
org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true}
org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning
org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>("@(\#)","$Id")}
org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=Error
org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}}

View File

@ -1,21 +1,16 @@
eclipse.preferences.version=1
environment/project/de.innot.avreclipse.configuration.app.debug.225132627/LANG/delimiter=\:
environment/project/de.innot.avreclipse.configuration.app.debug.225132627/LANG/operation=append
environment/project/de.innot.avreclipse.configuration.app.debug.225132627/LANG/value=en-US
environment/project/de.innot.avreclipse.configuration.app.debug.225132627/append=true
environment/project/de.innot.avreclipse.configuration.app.debug.225132627/appendContributed=true
environment/project/de.innot.avreclipse.configuration.app.debug.276225876/LANG/delimiter=\:
environment/project/de.innot.avreclipse.configuration.app.debug.276225876/LANG/operation=append
environment/project/de.innot.avreclipse.configuration.app.debug.276225876/LANG/value=en-US
environment/project/de.innot.avreclipse.configuration.app.debug.276225876/append=true
environment/project/de.innot.avreclipse.configuration.app.debug.276225876/appendContributed=true
environment/project/de.innot.avreclipse.configuration.app.release.472085903/LANG/delimiter=\:
environment/project/de.innot.avreclipse.configuration.app.release.472085903/LANG/operation=append
environment/project/de.innot.avreclipse.configuration.app.release.472085903/LANG/value=en-US
environment/project/de.innot.avreclipse.configuration.app.release.472085903/append=true
environment/project/de.innot.avreclipse.configuration.app.release.472085903/appendContributed=true
environment/project/de.innot.avreclipse.configuration.app.release.924927447/LANG/delimiter=\:
environment/project/de.innot.avreclipse.configuration.app.release.924927447/LANG/operation=append
environment/project/de.innot.avreclipse.configuration.app.release.924927447/LANG/value=en-US
environment/project/de.innot.avreclipse.configuration.app.release.924927447/append=true
environment/project/de.innot.avreclipse.configuration.app.release.924927447/appendContributed=true
environment/project/de.innot.avreclipse.configuration.app.debug.1718737082/LANG/delimiter=\:
environment/project/de.innot.avreclipse.configuration.app.debug.1718737082/LANG/operation=append
environment/project/de.innot.avreclipse.configuration.app.debug.1718737082/LANG/value=en-US
environment/project/de.innot.avreclipse.configuration.app.debug.1718737082/append=true
environment/project/de.innot.avreclipse.configuration.app.debug.1718737082/appendContributed=true
environment/project/de.innot.avreclipse.configuration.app.debug.674828633/LANG/delimiter=\:
environment/project/de.innot.avreclipse.configuration.app.debug.674828633/LANG/operation=append
environment/project/de.innot.avreclipse.configuration.app.debug.674828633/LANG/value=en-US
environment/project/de.innot.avreclipse.configuration.app.debug.674828633/append=true
environment/project/de.innot.avreclipse.configuration.app.debug.674828633/appendContributed=true
environment/project/de.innot.avreclipse.configuration.app.release.1024470171/LANG/delimiter=\:
environment/project/de.innot.avreclipse.configuration.app.release.1024470171/LANG/operation=append
environment/project/de.innot.avreclipse.configuration.app.release.1024470171/LANG/value=en-US
environment/project/de.innot.avreclipse.configuration.app.release.1024470171/append=true
environment/project/de.innot.avreclipse.configuration.app.release.1024470171/appendContributed=true

View File

@ -38,7 +38,6 @@
#define PSTR(s) (__extension__({static prog_char __c[] PROGMEM = (s); &__c[0];}))
#endif
#define STATUS_LED_PORT B
#define STATUS_LED_PIN 3

736
floppy_emu_arduino/diskmenu.cpp Normal file → Executable file
View File

@ -1,368 +1,368 @@
/*
Floppy Emu, copyright 2013 Steve Chamberlin, "Big Mess o' Wires". All rights reserved.
Floppy Emu is licensed under a Creative Commons Attribution-NonCommercial 3.0 Unported
license. (CC BY-NC 3.0) The terms of the license may be viewed at
http://creativecommons.org/licenses/by-nc/3.0/
Based on a work at http://www.bigmessowires.com/macintosh-floppy-emu/
Permissions beyond the scope of this license may be available at www.bigmessowires.com
or from mailto:steve@bigmessowires.com.
*/
#include <math.h>
#include <avr/pgmspace.h>
#include <util/atomic.h>
#include <util/delay.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include "diskmenu.h"
#include "SdFat.h"
#include "SdBaseFile.h"
#include "noklcd.h"
#define SECTORBUF_SIZE (23 * 512) // use the 24th buffer for directory breadcrumbs
extern uint8_t sectorBuf[24][512];
extern uint8_t extraBuf[512];
typedef struct FileEntry
{
char longName[FILENAME_LEN+1];
char shortName[SHORTFILENAME_LEN+1];
eImageType imageFileType;
} FileEntry;
bool dirLfnNext(SdFat& sd, dir_t& dir, char* lfn)
{
uint8_t offset[] = {1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30};
uint8_t lfnIn = 130;
uint8_t i;
uint8_t ndir=0;
uint8_t sum;
uint8_t test=0;
bool haveLong = false;
while( sd.vwd()->read( &dir, 32 ) == 32 )
{
if( DIR_IS_LONG_NAME( &dir ) )
{
if( ! haveLong )
{
if(( dir.name[0] & 0XE0 ) != 0X40 )
continue;
ndir = dir.name[0] & 0X1F;
test = dir.creationTimeTenths;
haveLong = true;
lfnIn = 130;
lfn[ lfnIn ] = 0;
}
else if( dir.name[0] != --ndir || test != dir.creationTimeTenths )
{
haveLong = false;
continue;
}
char *p = (char*) & dir;
if( lfnIn > 0 )
{
lfnIn -= 13;
for( i = 0; i < 13; i++ )
lfn[lfnIn + i] = p[offset[i]];
}
}
else if( DIR_IS_FILE_OR_SUBDIR( &dir )
&& dir.name[0] != DIR_NAME_DELETED
&& dir.name[0] != DIR_NAME_FREE
&& dir.name[0] != '.')
{
if( haveLong )
{
for( sum = i = 0; i < 11; i++ )
sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + dir.name[i];
if( sum != test || ndir != 1 )
haveLong = false;
}
if( haveLong )
{
for( i = 0; lfnIn + i <= 130 ; i++ )
lfn[i] = lfn[lfnIn + i];
return true;
}
// else if( dir.reservedNT )
// return "Reserved NT";
else
{
SdBaseFile::dirName( dir, lfn );
return true;
}
}
else
{
if( dir.name[0] == DIR_NAME_FREE )
break;
haveLong = false;
}
}
lfn[ 0 ] = 0;
return false;
}
eImageType DiskImageFileType(dir_t& dir, const char *filename)
{
if (filename[0] == '.')
return DISK_IMAGE_NONE;
if (DIR_IS_SUBDIR(&dir))
return DISK_IMAGE_DIRECTORY;
if (!DIR_IS_FILE(&dir))
return DISK_IMAGE_NONE;
uint32_t size = dir.fileSize;
if (size == (unsigned long)1024 * 400)
return DISK_IMAGE_400K;
else if (size == (unsigned long)1024 * 800)
return DISK_IMAGE_800K;
else if (size == (unsigned long)1024 * 1440)
return DISK_IMAGE_1440K;
else if (size > (unsigned long)1024 * 400 &&
size < (unsigned long)1024 * 1500)
{
// get the 8.3 filename
char shortName[SHORTFILENAME_LEN+1];
SdBaseFile::dirName(dir, shortName);
// read the first sector of the file
SdBaseFile f;
if (f.open(shortName, O_RDONLY))
{
f.read(extraBuf, 512);
f.close();
// is it a DiskCopy 4.2 image?
if (extraBuf[0x52] == 0x01 &&
extraBuf[0x53] == 0x00)
{
size = ((unsigned long)extraBuf[0x41] * 65536 + (unsigned long)extraBuf[0x42] * 256 + (unsigned long)extraBuf[0x43]) / 1024;
if (size == 400)
return DISK_IMAGE_DISKCOPY_400K;
else if (size == 800)
return DISK_IMAGE_DISKCOPY_800K;
else if (size == 1440)
return DISK_IMAGE_DISKCOPY_1440K;
}
}
}
return DISK_IMAGE_NONE;
}
uint16_t diskMenuEntryCount;
uint16_t diskMenuOffset = 0;
uint16_t diskMenuSelection = 0;
char selectedFile[FILENAME_LEN+1];
char selectedLongFile[FILENAME_LEN+1];
eImageType selectedFileType;
uint8_t subdirDepth = 0;
#define LONGFILENAME_LEN 130
void InitDiskMenu(SdFat& sd)
{
dir_t dir;
char name[LONGFILENAME_LEN+1];
diskMenuEntryCount = 0;
// use the sector buffers to hold the filenames
uint16_t maxEntries = SECTORBUF_SIZE / sizeof(FileEntry);
FileEntry* pFileEntries = (FileEntry*)sectorBuf;
sd.vwd()->rewind();
while (dirLfnNext(sd, dir, name) && diskMenuEntryCount < maxEntries)
{
eImageType imageType;
if ((imageType = DiskImageFileType(dir, name)) != DISK_IMAGE_NONE)
{
strncpy(pFileEntries[diskMenuEntryCount].longName, name, FILENAME_LEN+1);
SdBaseFile::dirName(dir, pFileEntries[diskMenuEntryCount].shortName);
pFileEntries[diskMenuEntryCount].imageFileType = imageType;
diskMenuEntryCount++;
}
}
// add up directory, if not at the root
if (!sd.vwd()->isRoot())
{
strncpy(pFileEntries[diskMenuEntryCount].longName, "..", FILENAME_LEN+1);
strncpy(pFileEntries[diskMenuEntryCount].shortName, "..", SHORTFILENAME_LEN+1);
pFileEntries[diskMenuEntryCount].imageFileType = DISK_IMAGE_UP_DIRECTORY;
diskMenuEntryCount++;
}
char file1[FILENAME_LEN+1], file2[FILENAME_LEN+1], temp[FILENAME_LEN+1];
eImageType tempType;
// sort the names by longname
for (uint16_t i=0; i<diskMenuEntryCount; i++)
{
for (uint16_t j=i+1; j<diskMenuEntryCount; j++)
{
// convert filenames to upper case, for comparison purposes
strncpy(file1, pFileEntries[i].longName, FILENAME_LEN+1);
for (uint8_t x=0; x<strlen(file1); x++)
file1[x] = toupper(file1[x]);
strncpy(file2, pFileEntries[j].longName, FILENAME_LEN+1);
for (uint8_t x=0; x<strlen(file2); x++)
file2[x] = toupper(file2[x]);
// sort directories before regular files
int diff = strncmp(file1, file2, FILENAME_LEN+1);
if (pFileEntries[i].imageFileType == DISK_IMAGE_DIRECTORY ||
pFileEntries[i].imageFileType == DISK_IMAGE_UP_DIRECTORY)
diff -= 1000;
if (pFileEntries[j].imageFileType == DISK_IMAGE_DIRECTORY ||
pFileEntries[j].imageFileType == DISK_IMAGE_UP_DIRECTORY)
diff += 1000;
// if file1 > file2, swap them
if (diff > 0)
{
strncpy(temp, pFileEntries[i].longName, FILENAME_LEN+1);
strncpy(pFileEntries[i].longName, pFileEntries[j].longName, FILENAME_LEN+1);
strncpy(pFileEntries[j].longName, temp, FILENAME_LEN+1);
strncpy(temp, pFileEntries[i].shortName, SHORTFILENAME_LEN+1);
strncpy(pFileEntries[i].shortName, pFileEntries[j].shortName, SHORTFILENAME_LEN+1);
strncpy(pFileEntries[j].shortName, temp, SHORTFILENAME_LEN+1);
tempType = pFileEntries[i].imageFileType;
pFileEntries[i].imageFileType = pFileEntries[j].imageFileType;
pFileEntries[j].imageFileType = tempType;
}
}
}
}
void DrawDiskMenu(SdFat& sd)
{
// scroll menu if necessary
if (diskMenuSelection < diskMenuOffset)
diskMenuOffset = diskMenuSelection;
if (diskMenuSelection > diskMenuOffset+4)
diskMenuOffset = diskMenuSelection-4;
LcdGoto(0,0);
LcdWrite(LCD_DATA, 0x7F);
for (int i=0; i<19; i++)
LcdWrite(LCD_DATA, 0x40);
LcdTinyStringFramed("Select Disk");
for (int i=0; i<19; i++)
LcdWrite(LCD_DATA, 0x40);
LcdWrite(LCD_DATA, 0x7F);
if (diskMenuEntryCount == 0)
{
LcdGoto(0, 1);
LcdTinyString("no image files found", TEXT_NORMAL);
}
else
{
FileEntry* pFileEntries = (FileEntry*)sectorBuf;
int row = 0;
for (uint16_t i=diskMenuOffset; i<diskMenuOffset+5 && i<diskMenuEntryCount; i++)
{
bool selected = (i == diskMenuSelection);
LcdGoto(0, row+1);
for (int j=0; j<LCD_WIDTH; j++)
{
LcdWrite(LCD_DATA, selected ? 0x7F : 0x00);
}
// show the image name
LcdGoto(1, row+1);
LcdTinyString(pFileEntries[i].longName, selected ? TEXT_INVERSE : TEXT_NORMAL, LCD_WIDTH-1);
// draw a folder icon for subdirectories
if (pFileEntries[i].imageFileType == DISK_IMAGE_DIRECTORY ||
pFileEntries[i].imageFileType == DISK_IMAGE_UP_DIRECTORY)
{
LcdGoto(73, row+1);
LcdWrite(LCD_DATA, selected ? (0x7F ^ 0x00): 0x00);
LcdWrite(LCD_DATA, selected ? (0x7F ^ 0x3C): 0x3C);
LcdWrite(LCD_DATA, selected ? (0x7F ^ 0x22): 0x22);
LcdWrite(LCD_DATA, selected ? (0x7F ^ 0x22): 0x22);
LcdWrite(LCD_DATA, selected ? (0x7F ^ 0x22): 0x22);
LcdWrite(LCD_DATA, selected ? (0x7F ^ 0x24): 0x24);
LcdWrite(LCD_DATA, selected ? (0x7F ^ 0x24): 0x24);
LcdWrite(LCD_DATA, selected ? (0x7F ^ 0x3C): 0x3C);
LcdWrite(LCD_DATA, selected ? (0x7F ^ 0x00): 0x00);
}
if (selected)
{
strncpy(selectedLongFile, pFileEntries[i].longName, FILENAME_LEN+1);
strncpy(selectedFile, pFileEntries[i].shortName, SHORTFILENAME_LEN+1);
selectedFileType = pFileEntries[i].imageFileType;
}
row++;
}
// draw the scrollbar
if (diskMenuEntryCount <= 5)
{
// no scrollbar
for (uint8_t r=1; r<6; r++)
{
LcdGoto(82, r);
LcdWrite(LCD_DATA, 0x00);
LcdWrite(LCD_DATA, 0x00);
}
}
else
{
uint8_t barEnd = 8 + (uint16_t)39 * (diskMenuOffset + 5) / diskMenuEntryCount;
uint8_t barSize = (uint16_t)39 * 5 / diskMenuEntryCount;
uint8_t barStart = barEnd - barSize;
for (uint8_t r=1; r<6; r++)
{
LcdGoto(82, r);
LcdWrite(LCD_DATA, 0x00);
uint8_t b = 0;
for (uint8_t y=r*8; y<=r*8+7; y++)
{
if (y >= barStart && y <= barEnd)
b = 0x80 | (b >> 1);
else
b = (b >> 1);
}
LcdWrite(LCD_DATA, b);
}
}
// prevent moving selection past end of list
if (diskMenuSelection >= diskMenuOffset + row)
{
diskMenuSelection = diskMenuOffset + row - 1;
if (row == 4 && diskMenuOffset > 0)
{
diskMenuOffset--; // scroll backwards
}
DrawDiskMenu(sd); // draw again
}
}
}
/*
Floppy Emu, copyright 2013 Steve Chamberlin, "Big Mess o' Wires". All rights reserved.
Floppy Emu is licensed under a Creative Commons Attribution-NonCommercial 3.0 Unported
license. (CC BY-NC 3.0) The terms of the license may be viewed at
http://creativecommons.org/licenses/by-nc/3.0/
Based on a work at http://www.bigmessowires.com/macintosh-floppy-emu/
Permissions beyond the scope of this license may be available at www.bigmessowires.com
or from mailto:steve@bigmessowires.com.
*/
#include <math.h>
#include <avr/pgmspace.h>
#include <util/atomic.h>
#include <util/delay.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include "diskmenu.h"
#include "SdFat.h"
#include "SdBaseFile.h"
#include "noklcd.h"
#define SECTORBUF_SIZE (23 * 512) // use the 24th buffer for directory breadcrumbs
extern uint8_t sectorBuf[24][512];
extern uint8_t extraBuf[512];
typedef struct FileEntry
{
char longName[FILENAME_LEN+1];
char shortName[SHORTFILENAME_LEN+1];
eImageType imageFileType;
} FileEntry;
bool dirLfnNext(SdFat& sd, dir_t& dir, char* lfn)
{
uint8_t offset[] = {1, 3, 5, 7, 9, 14, 16, 18, 20, 22, 24, 28, 30};
uint8_t lfnIn = 130;
uint8_t i;
uint8_t ndir=0;
uint8_t sum;
uint8_t test=0;
bool haveLong = false;
while( sd.vwd()->read( &dir, 32 ) == 32 )
{
if( DIR_IS_LONG_NAME( &dir ) )
{
if( ! haveLong )
{
if(( dir.name[0] & 0XE0 ) != 0X40 )
continue;
ndir = dir.name[0] & 0X1F;
test = dir.creationTimeTenths;
haveLong = true;
lfnIn = 130;
lfn[ lfnIn ] = 0;
}
else if( dir.name[0] != --ndir || test != dir.creationTimeTenths )
{
haveLong = false;
continue;
}
char *p = (char*) & dir;
if( lfnIn > 0 )
{
lfnIn -= 13;
for( i = 0; i < 13; i++ )
lfn[lfnIn + i] = p[offset[i]];
}
}
else if( DIR_IS_FILE_OR_SUBDIR( &dir )
&& dir.name[0] != DIR_NAME_DELETED
&& dir.name[0] != DIR_NAME_FREE
&& dir.name[0] != '.')
{
if( haveLong )
{
for( sum = i = 0; i < 11; i++ )
sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + dir.name[i];
if( sum != test || ndir != 1 )
haveLong = false;
}
if( haveLong )
{
for( i = 0; lfnIn + i <= 130 ; i++ )
lfn[i] = lfn[lfnIn + i];
return true;
}
// else if( dir.reservedNT )
// return "Reserved NT";
else
{
SdBaseFile::dirName( dir, lfn );
return true;
}
}
else
{
if( dir.name[0] == DIR_NAME_FREE )
break;
haveLong = false;
}
}
lfn[ 0 ] = 0;
return false;
}
eImageType DiskImageFileType(dir_t& dir, const char *filename)
{
if (filename[0] == '.')
return DISK_IMAGE_NONE;
if (DIR_IS_SUBDIR(&dir))
return DISK_IMAGE_DIRECTORY;
if (!DIR_IS_FILE(&dir))
return DISK_IMAGE_NONE;
uint32_t size = dir.fileSize;
if (size == (unsigned long)1024 * 400)
return DISK_IMAGE_400K;
else if (size == (unsigned long)1024 * 800)
return DISK_IMAGE_800K;
else if (size == (unsigned long)1024 * 1440)
return DISK_IMAGE_1440K;
else if (size > (unsigned long)1024 * 400 &&
size < (unsigned long)1024 * 1500)
{
// get the 8.3 filename
char shortName[SHORTFILENAME_LEN+1];
SdBaseFile::dirName(dir, shortName);
// read the first sector of the file
SdBaseFile f;
if (f.open(shortName, O_RDONLY))
{
f.read(extraBuf, 512);
f.close();
// is it a DiskCopy 4.2 image?
if (extraBuf[0x52] == 0x01 &&
extraBuf[0x53] == 0x00)
{
size = ((unsigned long)extraBuf[0x41] * 65536 + (unsigned long)extraBuf[0x42] * 256 + (unsigned long)extraBuf[0x43]) / 1024;
if (size == 400)
return DISK_IMAGE_DISKCOPY_400K;
else if (size == 800)
return DISK_IMAGE_DISKCOPY_800K;
else if (size == 1440)
return DISK_IMAGE_DISKCOPY_1440K;
}
}
}
return DISK_IMAGE_NONE;
}
uint16_t diskMenuEntryCount;
uint16_t diskMenuOffset = 0;
uint16_t diskMenuSelection = 0;
char selectedFile[FILENAME_LEN+1];
char selectedLongFile[FILENAME_LEN+1];
eImageType selectedFileType;
uint8_t subdirDepth = 0;
#define LONGFILENAME_LEN 130
void InitDiskMenu(SdFat& sd)
{
dir_t dir;
char name[LONGFILENAME_LEN+1];
diskMenuEntryCount = 0;
// use the sector buffers to hold the filenames
uint16_t maxEntries = SECTORBUF_SIZE / sizeof(FileEntry);
FileEntry* pFileEntries = (FileEntry*)sectorBuf;
sd.vwd()->rewind();
while (dirLfnNext(sd, dir, name) && diskMenuEntryCount < maxEntries)
{
eImageType imageType;
if ((imageType = DiskImageFileType(dir, name)) != DISK_IMAGE_NONE)
{
strncpy(pFileEntries[diskMenuEntryCount].longName, name, FILENAME_LEN+1);
SdBaseFile::dirName(dir, pFileEntries[diskMenuEntryCount].shortName);
pFileEntries[diskMenuEntryCount].imageFileType = imageType;
diskMenuEntryCount++;
}
}
// add up directory, if not at the root
if (!sd.vwd()->isRoot())
{
strncpy(pFileEntries[diskMenuEntryCount].longName, "..", FILENAME_LEN+1);
strncpy(pFileEntries[diskMenuEntryCount].shortName, "..", SHORTFILENAME_LEN+1);
pFileEntries[diskMenuEntryCount].imageFileType = DISK_IMAGE_UP_DIRECTORY;
diskMenuEntryCount++;
}
char file1[FILENAME_LEN+1], file2[FILENAME_LEN+1], temp[FILENAME_LEN+1];
eImageType tempType;
// sort the names by longname
for (uint16_t i=0; i<diskMenuEntryCount; i++)
{
for (uint16_t j=i+1; j<diskMenuEntryCount; j++)
{
// convert filenames to upper case, for comparison purposes
strncpy(file1, pFileEntries[i].longName, FILENAME_LEN+1);
for (uint8_t x=0; x<strlen(file1); x++)
file1[x] = toupper(file1[x]);
strncpy(file2, pFileEntries[j].longName, FILENAME_LEN+1);
for (uint8_t x=0; x<strlen(file2); x++)
file2[x] = toupper(file2[x]);
// sort directories before regular files
int diff = strncmp(file1, file2, FILENAME_LEN+1);
if (pFileEntries[i].imageFileType == DISK_IMAGE_DIRECTORY ||
pFileEntries[i].imageFileType == DISK_IMAGE_UP_DIRECTORY)
diff -= 1000;
if (pFileEntries[j].imageFileType == DISK_IMAGE_DIRECTORY ||
pFileEntries[j].imageFileType == DISK_IMAGE_UP_DIRECTORY)
diff += 1000;
// if file1 > file2, swap them
if (diff > 0)
{
strncpy(temp, pFileEntries[i].longName, FILENAME_LEN+1);
strncpy(pFileEntries[i].longName, pFileEntries[j].longName, FILENAME_LEN+1);
strncpy(pFileEntries[j].longName, temp, FILENAME_LEN+1);
strncpy(temp, pFileEntries[i].shortName, SHORTFILENAME_LEN+1);
strncpy(pFileEntries[i].shortName, pFileEntries[j].shortName, SHORTFILENAME_LEN+1);
strncpy(pFileEntries[j].shortName, temp, SHORTFILENAME_LEN+1);
tempType = pFileEntries[i].imageFileType;
pFileEntries[i].imageFileType = pFileEntries[j].imageFileType;
pFileEntries[j].imageFileType = tempType;
}
}
}
}
void DrawDiskMenu(SdFat& sd)
{
// scroll menu if necessary
if (diskMenuSelection < diskMenuOffset)
diskMenuOffset = diskMenuSelection;
if (diskMenuSelection > diskMenuOffset+4)
diskMenuOffset = diskMenuSelection-4;
LcdGoto(0,0);
LcdWrite(LCD_DATA, 0x7F);
for (int i=0; i<19; i++)
LcdWrite(LCD_DATA, 0x40);
LcdTinyStringFramed("Select Disk");
for (int i=0; i<19; i++)
LcdWrite(LCD_DATA, 0x40);
LcdWrite(LCD_DATA, 0x7F);
if (diskMenuEntryCount == 0)
{
LcdGoto(0, 1);
LcdTinyString("no image files found", TEXT_NORMAL);
}
else
{
FileEntry* pFileEntries = (FileEntry*)sectorBuf;
int row = 0;
for (uint16_t i=diskMenuOffset; i<diskMenuOffset+5 && i<diskMenuEntryCount; i++)
{
bool selected = (i == diskMenuSelection);
LcdGoto(0, row+1);
for (int j=0; j<LCD_WIDTH; j++)
{
LcdWrite(LCD_DATA, selected ? 0x7F : 0x00);
}
// show the image name
LcdGoto(1, row+1);
LcdTinyString(pFileEntries[i].longName, selected ? TEXT_INVERSE : TEXT_NORMAL, LCD_WIDTH-1);
// draw a folder icon for subdirectories
if (pFileEntries[i].imageFileType == DISK_IMAGE_DIRECTORY ||
pFileEntries[i].imageFileType == DISK_IMAGE_UP_DIRECTORY)
{
LcdGoto(73, row+1);
LcdWrite(LCD_DATA, selected ? (0x7F ^ 0x00): 0x00);
LcdWrite(LCD_DATA, selected ? (0x7F ^ 0x3C): 0x3C);
LcdWrite(LCD_DATA, selected ? (0x7F ^ 0x22): 0x22);
LcdWrite(LCD_DATA, selected ? (0x7F ^ 0x22): 0x22);
LcdWrite(LCD_DATA, selected ? (0x7F ^ 0x22): 0x22);
LcdWrite(LCD_DATA, selected ? (0x7F ^ 0x24): 0x24);
LcdWrite(LCD_DATA, selected ? (0x7F ^ 0x24): 0x24);
LcdWrite(LCD_DATA, selected ? (0x7F ^ 0x3C): 0x3C);
LcdWrite(LCD_DATA, selected ? (0x7F ^ 0x00): 0x00);
}
if (selected)
{
strncpy(selectedLongFile, pFileEntries[i].longName, FILENAME_LEN+1);
strncpy(selectedFile, pFileEntries[i].shortName, SHORTFILENAME_LEN+1);
selectedFileType = pFileEntries[i].imageFileType;
}
row++;
}
// draw the scrollbar
if (diskMenuEntryCount <= 5)
{
// no scrollbar
for (uint8_t r=1; r<6; r++)
{
LcdGoto(82, r);
LcdWrite(LCD_DATA, 0x00);
LcdWrite(LCD_DATA, 0x00);
}
}
else
{
uint8_t barEnd = 8 + (uint16_t)39 * (diskMenuOffset + 5) / diskMenuEntryCount;
uint8_t barSize = (uint16_t)39 * 5 / diskMenuEntryCount;
uint8_t barStart = barEnd - barSize;
for (uint8_t r=1; r<6; r++)
{
LcdGoto(82, r);
LcdWrite(LCD_DATA, 0x00);
uint8_t b = 0;
for (uint8_t y=r*8; y<=r*8+7; y++)
{
if (y >= barStart && y <= barEnd)
b = 0x80 | (b >> 1);
else
b = (b >> 1);
}
LcdWrite(LCD_DATA, b);
}
}
// prevent moving selection past end of list
if (diskMenuSelection >= diskMenuOffset + row)
{
diskMenuSelection = diskMenuOffset + row - 1;
if (row == 4 && diskMenuOffset > 0)
{
diskMenuOffset--; // scroll backwards
}
DrawDiskMenu(sd); // draw again
}
}
}

View File

@ -1,471 +0,0 @@
/*
* floppy_emu_arduino.ino
*
* Created on: 2014/07/12
* Author: sin
*/
#include <util/delay.h>
#include <SPI.h>
#include <SdFat.h>
#include "portmacros.h"
#include "millitimer.h"
#include "diskmenu.h"
#include "noklcd.h"
#include "floppyemu.h"
SdFat sd;
void setup() {
millitimerInit();
ResetDiskState();
ShowVersion();
LcdClear();
sei();
_delay_ms(100); // wait for pending interrupts??
millitimerOn();
_delay_ms(100); // wait for pending interrupts??
// if select and next are both held down, enter contrast adjust mode
if (bit_is_set(PIN(PREV_BUTTON_PORT), PREV_BUTTON_PIN) &&
bit_is_clear(PIN(NEXT_BUTTON_PORT), NEXT_BUTTON_PIN) &&
bit_is_clear(PIN(SELECT_BUTTON_PORT),SELECT_BUTTON_PIN))
{
AdjustContrast();
}
//SdFat sd;
if (!sd.init(SPI_FULL_SPEED))
{
snprintf(textBuf, TEXTBUF_SIZE, "SD card error %d:%d", sd.card()->errorCode(), sd.card()->errorData());
error(textBuf);
}
millitimerOff();
// if prev and next are both held down, enter firmware update mode
if (bit_is_clear(PIN(PREV_BUTTON_PORT), PREV_BUTTON_PIN) &&
bit_is_clear(PIN(NEXT_BUTTON_PORT), NEXT_BUTTON_PIN) &&
bit_is_set(PIN(SELECT_BUTTON_PORT),SELECT_BUTTON_PIN))
{
PromptForFirmwareUpdate();
}
InitDiskMenu(sd);
DrawDiskMenu(sd);
}
// main loop
void loop() {
if (writeError)
{
// report error encounted in the interrupt routine
error(textBuf);
}
cli();
uint8_t trackNumber = currentTrack; // save track in a local var, since currentTrack is volatile
uint8_t sideNumber = currentSide; // save side in a local var, since currentSide is volatile
restartDisk = false;
sei();
if (diskInserted)
{
// show the current track and side
snprintf(textBuf, TEXTBUF_SIZE, "%02d", trackNumber);
LcdGoto(24,4);
LcdTinyString(textBuf, TEXT_NORMAL);
snprintf(textBuf, TEXTBUF_SIZE, "%d ", sideNumber);
LcdGoto(56,4);
LcdTinyString(textBuf, TEXT_NORMAL);
// sync RAM buffer with SD card when switching tracks, or also when switching sides for mfmMode
if (prevTrack != trackNumber || (mfmMode && (prevSide != sideNumber)))
{
// write any dirty sectors from the previous track/side back to the SD card
FlushDirtySectors(sd, prevTrack);
prevTrack = trackNumber;
prevSide = sideNumber;
// Also mark all the buffers on this track as invalid, since they don't contain valid data for the new track.
for (uint8_t i=0; i<NUM_BUFFERS; i++)
bufferState[i] &= ~BUFFER_DATA_VALID;
}
// continuously replay sectors from this track/side until interrupted
while (!restartDisk)
{
if (writeMode)
{
// do nothing: incoming data is processed by the interrupt handler
// show write state
LcdGoto(64,4);
LcdTinyStringP(PSTR("Write"), TEXT_NORMAL);
while (!restartDisk)
{
}
}
else if (trackNumber <= 79)
{
uint8_t trackLen = trackLength(trackNumber);
// when stepping from a track with more sectors to one with fewer, current sector number could potentially
// end up out of range
if (currentSector >= trackLen)
currentSector = 0;
bool prevMotorOn = !bit_is_clear(PIN(CPLD_STEP_DIR_MOTOR_ON_PORT), CPLD_STEP_DIR_MOTOR_ON_PIN);
while (true)
{
// check for disk eject
if (bit_is_set(PIN(CPLD_EJECT_REQ_PORT), CPLD_EJECT_REQ_PIN))
{
PORT(CPLD_RD_READY_TK0_PORT) &= ~(1<<CPLD_RD_READY_TK0_PIN);
PORT(CPLD_STEP_ACK_DISK_IN_PORT) |= (1<<CPLD_STEP_ACK_DISK_IN_PIN);
diskInserted = false;
// write any dirty sectors from the current track
FlushDirtySectors(sd, trackNumber);
_delay_ms(100);
ResetDiskState();
InitDiskMenu(sd);
DrawDiskMenu(sd);
goto restart;
}
// show read/idle state
bool motorOn = bit_is_clear(PIN(CPLD_STEP_DIR_MOTOR_ON_PORT), CPLD_STEP_DIR_MOTOR_ON_PIN);
if (!motorOn)
{
// write any dirty sectors from the current track, when idle
FlushDirtySectors(sd, trackNumber);
}
if (prevMotorOn != motorOn)
{
prevMotorOn = motorOn;
LcdGoto(64,4);
if (motorOn)
LcdTinyStringP(PSTR(" Read"), TEXT_NORMAL);
else
{
LcdTinyStringP(PSTR(" Idle"), TEXT_NORMAL);
// turn LED off when idle
PORT(STATUS_LED_PORT) |= (1<<STATUS_LED_PIN);
writeDisplayTimer = 1; // clear old write alerts when going idle
}
}
// remove old write alerts when writeDisplayTimer reaches 1
if (writeDisplayTimer == 1)
{
writeDisplayTimer = 0;
LcdGoto(0,5);
LcdTinyStringP(PSTR(" "), TEXT_NORMAL);
}
uint8_t bufferNumber = mfmMode ? currentSector : (sideNumber * trackLen + currentSector);
bool shouldReadSector = false;
// atomic check and acquire of buffer lock
cli();
if ((bufferState[bufferNumber] & BUFFER_DATA_VALID) == 0 &&
(bufferState[bufferNumber] & BUFFER_LOCKED) == 0)
{
// lock the buffer, so the Mac won't write to it while we're reading it from SD
bufferState[bufferNumber] |= BUFFER_LOCKED;
shouldReadSector = true;
}
sei();
// read the sector from the SD card, if necessary
if (shouldReadSector)
{
uint32_t blockToRead = imageFirstBlock + ((uint32_t)trackStart(trackNumber) * numberOfDiskSides + sideNumber * trackLen + currentSector);
millitimerOn();
if (selectedFileIsDiskCopyFormat)
{
ReadDiskCopy42Block(sd, blockToRead, bufferNumber);
}
else
{
if (!sd.card()->readBlock(blockToRead, sectorBuf[bufferNumber]))
error("SD read error R");
}
millitimerOff();
bufferState[bufferNumber] |= BUFFER_DATA_VALID;
bufferState[bufferNumber] &= ~BUFFER_LOCKED;
}
if (currentSector == 0)
{
if (motorOn)
{
// toggle LED during drive activity
PORT(STATUS_LED_PORT) ^= (1<<STATUS_LED_PIN);
if (writeDisplayTimer > 1)
writeDisplayTimer--;
}
// "Flutter" the drive's TACH speed slightly, every time we pass sector 0 (about every 100-150ms). This avoids a bug
// in P_Sony_MakeSpdTbl in the 64K ROM (used in the Mac 128K and Mac 512K) where
// the Mac will crash if two successive TACH measurements see the exact same speed.
tachFlutter += 25;
if (tachFlutter >= 125)
tachFlutter = 0;
// Set the timeout. OC1A will toggle after this many counts. New timeout threshold won't take effect until the next timeout.
OCR1A = driveTachHalfPeriod - tachFlutter;
}
if (mfmMode)
{
// insert sector-to-sector gap bytes
for (uint8_t i=0; i<50; i++)
{
SendMFMAndCheckRestart(0x4E);
}
// insert sync bytes
for (uint8_t i=0; i<12; i++)
{
SendMFMAndCheckRestart(0x00);
}
// send the address block
crc = 0xFFFF; // reset CRC
SendMFMSync();
SendMFMSync();
SendMFMSync();
SendMFMAndCheckRestart(0xFE);
SendMFMAndCheckRestart(trackNumber);
SendMFMAndCheckRestart(sideNumber);
SendMFMAndCheckRestart(currentSector+1); // MFM sector numbers are 1-based
SendMFMAndCheckRestart(2); // size = 128 * 2^N bytes, so 2 means 512
uint8_t crc0 = (crc >> 8) & 0xFF;
uint8_t crc1 = crc & 0xFF;
SendMFMAndCheckRestart(crc0);
SendMFMAndCheckRestart(crc1);
// insert Address to Data gap bytes
for (uint8_t i=0; i<22; i++)
{
SendMFMAndCheckRestart(0x4E);
}
// insert sync bytes
for (uint8_t i=0; i<12; i++)
{
SendMFMAndCheckRestart(0x00);
}
// send the data block
crc = 0xFFFF; // reset CRC
SendMFMSync();
SendMFMSync();
SendMFMSync();
SendMFMAndCheckRestart(0xFB);
for (uint16_t i=0; i<SECTOR_DATA_SIZE; i++)
{
uint8_t d = sectorBuf[bufferNumber][i];
SendMFMAndCheckRestart(d);
}
crc0 = (crc >> 8) & 0xFF;
crc1 = crc & 0xFF;
SendMFMAndCheckRestart(crc0);
SendMFMAndCheckRestart(crc1);
}
else
{
// ensure a short gap between sectors - otherwise once they're all cached, one sector will appear
// to immediately follow another on disk, which may cause problems for the Mac.
// Bad voodoo here:
// 1. In the Finder StuffIt copy test that sometimes dies after the first 18 tracks, the length of delay here
// seems to affect what track it will freeze on.
// 2. With a longer delay here, the first ~10 sectors of copying seem to have fewer or no "long writes".
// 3. Depending on the delay here, the Transcend 2GB SD card sometimes gets "writeStop fail" when saving tracks.
for (uint16_t i=0; i<INTER_SECTOR_GAP_SIZE; i++)
{
SendByteAndCheckRestart(0xFF);
}
// send the address block
uint8_t format = (numberOfDiskSides == 2) ? 0x22 : 0x02; // 0x22 = MacOS double-sided, 0x02 = single sided
uint8_t trackLow = (uint8_t)(trackNumber & 0x3F);
uint8_t trackHigh = (uint8_t)((sideNumber << 5) | (trackNumber >> 6));
uint8_t checksum = (uint8_t)((trackLow ^ currentSector ^ trackHigh ^ format) & 0x3F);
SendByteAndCheckRestart(0xD5);
SendByteAndCheckRestart(0xAA);
SendByteAndCheckRestart(0x96);
SendByteAndCheckRestart(pgm_read_byte(&sony_to_disk_byte[trackLow]));
SendByteAndCheckRestart(pgm_read_byte(&sony_to_disk_byte[currentSector]));
SendByteAndCheckRestart(pgm_read_byte(&sony_to_disk_byte[trackHigh]));
SendByteAndCheckRestart(pgm_read_byte(&sony_to_disk_byte[format]));
SendByteAndCheckRestart(pgm_read_byte(&sony_to_disk_byte[checksum]));
SendByteAndCheckRestart(0xDE);
SendByteAndCheckRestart(0xAA);
// insert sync bytes between the address and data blocks
for (uint8_t i=0; i<ADDRESS_DATA_GAP_SIZE; i++)
{
SendByteAndCheckRestart(0xFF);
}
// send the data block
SendByteAndCheckRestart(0xD5);
SendByteAndCheckRestart(0xAA);
SendByteAndCheckRestart(0xAD);
SendByteAndCheckRestart(pgm_read_byte(&sony_to_disk_byte[currentSector]));
SendGCRSectorData((const uint8_t*)sectorBuf[bufferNumber]);
SendByteAndCheckRestart(0xDE);
SendByteAndCheckRestart(0xAA);
SendByteAndCheckRestart(0xFF);
}
currentSector = NextInterleavedSector(trackNumber, currentSector);
}
}
}
restart:
;
}
else
{
while (!restartDisk)
{
// check for disk eject. This shouldn't normally happen, but if the CPLD and AVR get out of sync
// and the CPLD thinks there's a disk while the AVR doesn't, it could.
// But don't do this is ALL the CPLD inputs are asserted - that means you're probably midway
// through building the hardware, and the CPLD isn't there yet.
if (bit_is_set(PIN(CPLD_EJECT_REQ_PORT), CPLD_EJECT_REQ_PIN) &&
!(bit_is_set(PIN(CPLD_RD_ACK_WR_TICK_PORT), CPLD_RD_ACK_WR_TICK_PIN) &&
bit_is_set(PIN(CPLD_STEP_REQ_PORT), CPLD_STEP_REQ_PIN) &&
bit_is_set(PIN(CPLD_WR_REQ_PORT), CPLD_WR_REQ_PIN) &&
bit_is_set(PIN(CPLD_CURRENT_SIDE_PORT), CPLD_CURRENT_SIDE_PIN)))
{
// reboot
((void(*)(void))0)();
}
if (bit_is_clear(PIN(PREV_BUTTON_PORT), PREV_BUTTON_PIN))
{
if (diskMenuSelection > 0)
{
diskMenuSelection--;
DrawDiskMenu(sd);
_delay_ms(200);
}
}
else if (bit_is_clear(PIN(NEXT_BUTTON_PORT), NEXT_BUTTON_PIN))
{
diskMenuSelection++;
DrawDiskMenu(sd);
_delay_ms(200);
}
else if (bit_is_clear(PIN(SELECT_BUTTON_PORT), SELECT_BUTTON_PIN))
{
if (selectedFileType == DISK_IMAGE_DIRECTORY)
{
// remember where we came from, so we can get back later
char* pSubdirName = ((char*)sectorBuf[23]) + ((SHORTFILENAME_LEN+1) * subdirDepth);
strncpy(pSubdirName, selectedFile, SHORTFILENAME_LEN+1);
subdirDepth++;
sd.chdir(selectedFile, true);
diskMenuSelection = 0;
LcdClear();
InitDiskMenu(sd);
DrawDiskMenu(sd);
_delay_ms(400);
}
else if (selectedFileType == DISK_IMAGE_UP_DIRECTORY)
{
subdirDepth--;
sd.chdir(true); // go to root directory
for (uint8_t i=0; i<subdirDepth; i++)
{
char* pSubdirName = ((char*)sectorBuf[23]) + ((SHORTFILENAME_LEN+1) * i);
sd.chdir(pSubdirName, true);
}
diskMenuSelection = 0;
LcdClear();
InitDiskMenu(sd);
DrawDiskMenu(sd);
_delay_ms(400);
}
else
{
// enable the interrupts
PCICR |= (1<<CPLD_STEP_REQ_INT_ENABLE);
PCICR |= (1<<CPLD_CURRENT_SIDE_INT_ENABLE);
PCICR |= (1<<CPLD_WR_REQ_INT_ENABLE);
PCICR |= (1<<CPLD_RD_ACK_WR_TICK_INT_ENABLE);
currentTrack = 0;
LcdReset(); // also resets the CPLD, ensures it knows we're now at track 0
LcdClear();
// "insert" the disk
if (OpenImageFile())
{
// tell the CPLD whether the disk is read-only (bit 0, active low)
uint8_t configByte = 0;
if (!readOnly)
configByte |= 0x01;
if (!mfmMode)
configByte |= 0x02;
PORT(CPLD_DATA_PORT) = configByte;
// asserting RD_READY without DISK_IN causes the CPLD to load config options from the data bus
PORT(CPLD_RD_READY_TK0_PORT) |= (1<<CPLD_RD_READY_TK0_PIN);
_delay_ms(1);
PORT(CPLD_RD_READY_TK0_PORT) &= ~(1<<CPLD_RD_READY_TK0_PIN);
_delay_us(10);
// tell the CPLD there is a disk
PORT(CPLD_STEP_ACK_DISK_IN_PORT) &= ~(1<<CPLD_STEP_ACK_DISK_IN_PIN);
diskInserted = true;
}
}
break;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,110 +0,0 @@
/*
* floppyemu.h
*
* Created on: 2014/07/12
* Author: sin
*/
#ifndef FLOPPYEMU_H_
#define FLOPPYEMU_H_
// I/O pin assignments
#define CPLD_RESET_PORT B
#define CPLD_RESET_PIN 0
#define CPLD_STEP_DIR_MOTOR_ON_PORT C
#define CPLD_STEP_DIR_MOTOR_ON_PIN 7
#define CPLD_STEP_REQ_PORT D
#define CPLD_STEP_REQ_PIN 0 // PCINT24
#define CPLD_STEP_REQ_INT_MSK PCMSK3
#define CPLD_STEP_REQ_INT_PIN PCINT24
#define CPLD_STEP_REQ_INT_ENABLE PCIE3
#define CPLD_CURRENT_SIDE_PORT C
#define CPLD_CURRENT_SIDE_PIN 1 // PCINT17
#define CPLD_CURRENT_SIDE_INT_MSK PCMSK2
#define CPLD_CURRENT_SIDE_INT_PIN PCINT17
#define CPLD_CURRENT_SIDE_INT_ENABLE PCIE2
#define CPLD_EJECT_REQ_PORT D
#define CPLD_EJECT_REQ_PIN 3
#define CPLD_STEP_ACK_DISK_IN_PORT C
#define CPLD_STEP_ACK_DISK_IN_PIN 2
#define CPLD_WR_REQ_PORT C
#define CPLD_WR_REQ_PIN 0 // PCINT16
#define CPLD_WR_REQ_INT_MSK PCMSK2
#define CPLD_WR_REQ_INT_PIN PCINT16
#define CPLD_WR_REQ_INT_ENABLE PCIE2
#define CPLD_RD_READY_TK0_PORT C
#define CPLD_RD_READY_TK0_PIN 5
#define CPLD_RD_ACK_WR_TICK_PORT A
#define CPLD_RD_ACK_WR_TICK_PIN 7 // PCINT7
#define CPLD_RD_ACK_WR_TICK_INT_MSK PCMSK0
#define CPLD_RD_ACK_WR_TICK_INT_PIN PCINT7
#define CPLD_RD_ACK_WR_TICK_INT_ENABLE PCIE0
#define CPLD_DATA_PORT A
#define CPLD_DATA_HIZ_PORT C
#define CPLD_DATA_HIZ_PIN 6
#define CPLD_TACH_PORT D
#define CPLD_TACH_PIN 5
#define CPLD_TMS_PORT C
#define CPLD_TMS_PIN 3
#define SELECT_BUTTON_PORT D
#define SELECT_BUTTON_PIN 4
#define PREV_BUTTON_PORT D
#define PREV_BUTTON_PIN 1
#define NEXT_BUTTON_PORT D
#define NEXT_BUTTON_PIN 2
#define STATUS_LED_PORT B
#define STATUS_LED_PIN 3
#define CARD_WPROT_PORT D
#define CARD_WPROT_PIN 7
// global variables predefinitions
#define TEXTBUF_SIZE 22
char textBuf[TEXTBUF_SIZE];
volatile uint8_t currentTrack;
volatile uint8_t prevTrack;
volatile uint8_t currentSide;
volatile uint8_t prevSide;
volatile uint8_t writeMode;
volatile bool restartDisk;
volatile bool writeError;
bool diskInserted;
bool readOnly;
bool mfmMode;
uint16_t crc;
uint8_t numberOfDiskSides;
uint8_t currentSector;
uint16_t driveTachHalfPeriod;
uint8_t tachFlutter;
uint8_t writeDisplayTimer;
uint8_t cpldFirmwareVersion;
// public functions predefinitions
void ResetDiskState();
void ShowVersion();
void AdjustContrast(void);
void error(const char* msg);
void PromptForFirmwareUpdate();
#endif /* FLOPPYEMU_H_ */

View File

@ -306,7 +306,7 @@ void LcdClear(void)
void LcdReset(void)
{
lcd_vop = 0xBF;
lcd_vop = 0xa4; //0xBF;
lcd_bias = 0x14;
lcd_tempCoef = 0x04;