diff --git a/Vcc.cpp b/Vcc.cpp
index 8d8178fc..8938ae34 100644
--- a/Vcc.cpp
+++ b/Vcc.cpp
@@ -1,27 +1,27 @@
-/*
-Copyright 2015 by Joseph Forgione
-This file is part of VCC (Virtual Color Computer).
-
- VCC (Virtual Color Computer) 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.
-
- VCC (Virtual Color Computer) 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 VCC (Virtual Color Computer). If not, see .
-*/
-
-/*---------------------------------------------------------------
-
----------------------------------------------------------------*/
+//#define USE_LOGGING
+//======================================================================
+// This file is part of VCC (Virtual Color Computer).
+// Vcc is Copyright 2015 by Joseph Forgione
+//
+// VCC (Virtual Color Computer) is free software, you can redistribute
+// 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.
+//
+// VCC (Virtual Color Computer) 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 VCC (Virtual Color Computer). If not, see
+// .
+//======================================================================
+
+/*---------------------------------------------------------------------*/
//#define STRICT
//#define WIN32_LEAN_AND_MEAN
-/*--------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------*/
// FIXME: This should be defined on the command line
#define DIRECTINPUT_VERSION 0x0800
@@ -604,6 +604,11 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
return 0;
break;
+ // Don't send click that activates window to emulation
+ case WM_MOUSEACTIVATE:
+ return MA_ACTIVATEANDEAT;
+ break;
+
case WM_LBUTTONDOWN: //0 = Left 1=right
SetButtonStatus(0,1);
break;
diff --git a/docs/namespaces.txt b/docs/namespaces.txt
new file mode 100644
index 00000000..c7c9949b
--- /dev/null
+++ b/docs/namespaces.txt
@@ -0,0 +1,25 @@
+We let namspaces get a bit overboard. Namespaces are for preventing
+name collisions, not to fully classify sources or to define directory
+or common library structures.
+
+Also we let the case of the VCC namespace change. This gains nothing
+but confusion. I see two choices here. a) go all lowercase or
+b) use CamelCase. Since we already had CamelCase I suggest we stick
+with it:
+
+Valid VCC namespaces
+
+VCC // The VCC core emulator
+VCC::UI // The VCC core user interface (future)
+VCC::Util // Generic utilities used by VCC
+VCC::Debugger // Coco Debugger code
+VCC::Debugger::UI // Coco Debugger user interface
+
+The following are invalid and should be changed when touched:
+
+namespace vcc -> namespace VCC
+namespace vcc::core -> namespace VCC
+namespace vcc::core::cartridges -> namespace VCC
+namespace vcc::core::utils -> namespace VCC::Util
+namespace vcc::devices::rtc -> namespace VCC
+namespace vcc::modules::mpi -> namespace VCC
diff --git a/joystickinput.cpp b/joystickinput.cpp
index 1994b7c2..a2c8633b 100644
--- a/joystickinput.cpp
+++ b/joystickinput.cpp
@@ -105,22 +105,14 @@ static unsigned char RightButton1Status = 0;
static unsigned char LeftButton2Status = 0;
static unsigned char RightButton2Status = 0;
-// FIXME Direct input not working for ARM - disable joysticks for arm builds
-#ifdef _M_ARM
-unsigned int Joysticks[MAXSTICKS] = {nullptr};
-#else
static LPDIRECTINPUTDEVICE8 Joysticks[MAXSTICKS];
-#endif
char StickName[MAXSTICKS][STRLEN];
static unsigned char JoyStickIndex=0;
-#ifdef _M_ARM
-#else
static LPDIRECTINPUT8 di;
BOOL CALLBACK enumCallback(const DIDEVICEINSTANCE* , VOID* );
BOOL CALLBACK enumAxesCallback(const DIDEVICEOBJECTINSTANCE* , VOID* );
-#endif
static unsigned char CurrentStick;
@@ -131,9 +123,6 @@ inline int vccJoystickType();
// Locate connected joysticks. Called by config.c
int EnumerateJoysticks()
{
-#ifdef _M_ARM
- return(0);
-#else
HRESULT hr;
JoyStickIndex=0;
if (FAILED(hr = DirectInput8Create(GetModuleHandle(nullptr),
@@ -145,11 +134,9 @@ int EnumerateJoysticks()
return 0;
return JoyStickIndex;
-#endif
}
/*****************************************************************************/
-#ifndef _M_ARM
BOOL CALLBACK enumCallback(const DIDEVICEINSTANCE* instance, VOID* /*context*/)
{
HRESULT hr;
@@ -158,13 +145,11 @@ BOOL CALLBACK enumCallback(const DIDEVICEINSTANCE* instance, VOID* /*context*/)
JoyStickIndex++;
return(JoyStickIndex>7;
RightButton2Status= Stick1.rgbButtons[1]>>7;
}
-#endif
switch (pot) {
case 0:
diff --git a/keyboard.cpp b/keyboard.cpp
index 239b61e2..24e00327 100644
--- a/keyboard.cpp
+++ b/keyboard.cpp
@@ -1,21 +1,23 @@
-/*****************************************************************************/
-/*
-Copyright 2015 by Joseph Forgione
-This file is part of VCC (Virtual Color Computer).
-
- VCC (Virtual Color Computer) 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.
-
- VCC (Virtual Color Computer) 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.
+//#define USE_LOGGING
+//======================================================================
+// This file is part of VCC (Virtual Color Computer).
+// Vcc is Copyright 2015 by Joseph Forgione
+//
+// VCC (Virtual Color Computer) is free software, you can redistribute
+// 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.
+//
+// VCC (Virtual Color Computer) 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 VCC (Virtual Color Computer). If not, see
+// .
+//======================================================================
- You should have received a copy of the GNU General Public License
- along with VCC (Virtual Color Computer). If not, see .
-*/
/*****************************************************************************/
/*
Keyboard handling / translation - system -> emulator
@@ -50,7 +52,7 @@ This file is part of VCC (Virtual Color Computer).
#include
#include
-
+#include
/*****************************************************************************/
/*
@@ -244,6 +246,9 @@ void _vccKeyboardUpdateRolloverTable()
void vccKeyboardHandleKey(unsigned char ScanCode, keyevent_e keyState)
{
+
+ DLOG_C("HandleKey %d %d\n",ScanCode,keyState);
+
//If requested, abort pasting operation.
if (ScanCode == 0x01 || ScanCode == 0x43 || ScanCode == 0x3F) { pasting = false; }
@@ -253,6 +258,7 @@ void vccKeyboardHandleKey(unsigned char ScanCode, keyevent_e keyState)
{
ScanCode = DIK_LSHIFT;
}
+
#if 0 // TODO: CTRL and/or ALT?
// CTRL key - right -> left
if (ScanCode == DIK_RCONTROL)
diff --git a/libcommon/include/vcc/core/filesystem.h b/libcommon/include/vcc/core/filesystem.h
index cfbab3fe..742f0ceb 100644
--- a/libcommon/include/vcc/core/filesystem.h
+++ b/libcommon/include/vcc/core/filesystem.h
@@ -20,13 +20,11 @@
#include
#include
+//TODO replace get_directory_from_path and get_filename with fileutil functions
+//TODO move find_pak_module_path to point of use
namespace vcc::core::utils
{
-
- LIBCOMMON_EXPORT std::string get_module_path(HMODULE module_handle = nullptr);
LIBCOMMON_EXPORT std::string find_pak_module_path(std::string path);
LIBCOMMON_EXPORT std::string get_directory_from_path(std::string path);
LIBCOMMON_EXPORT std::string get_filename(std::string path);
- LIBCOMMON_EXPORT std::string strip_application_path(std::string path);
-
}
diff --git a/libcommon/include/vcc/core/fileutil.h b/libcommon/include/vcc/core/fileutil.h
new file mode 100644
index 00000000..857a7bd2
--- /dev/null
+++ b/libcommon/include/vcc/core/fileutil.h
@@ -0,0 +1,62 @@
+//======================================================================
+// General purpose Host file utilities. EJ Jaquay 2026
+//
+// This file is part of VCC (Virtual Color Computer).
+// Vcc is Copyright 2015 by Joseph Forgione
+//
+// VCC (Virtual Color Computer) is free software, you can redistribute
+// 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.
+//
+// VCC (Virtual Color Computer) 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 VCC (Virtual Color Computer). If not, see
+// .
+//
+//======================================================================
+#pragma once
+
+#include
+#include
+#include
+
+// FIXME: directory names in libcommon are wrong
+// libcommon/include/vcc/core should be libcommon/include/vcc/util
+// libcommon/src/core should be libcommon/include/util
+
+//=========================================================================
+// Host file utilities. Most of these are general purpose
+//=========================================================================
+
+namespace VCC::Util {
+
+ // Get most recent windows error text
+ std::string LastErrorString();
+ const char * LastErrorTxt();
+
+ // Convert backslashes to slashes in directory string
+ void FixDirSlashes(std::string &dir);
+
+ // Return copy of string with spaces trimmed from end of a string
+ std::string trim_right_spaces(const std::string &s);
+
+ // Return slash normalized directory part of a path
+ std::string GetDirectoryPart(const std::string& input);
+
+ // Return filename part of a path
+ std::string GetFileNamePart(const std::string& input);
+
+ // Determine if path is a direcory
+ bool IsDirectory(const std::string& path);
+
+ // Get path of loaded module or current application
+ std::string get_module_path(HMODULE module_handle);
+
+ // If path is in the application directory strip directory
+ std::string strip_application_path(std::string path);
+}
diff --git a/libcommon/libcommon.vcxproj b/libcommon/libcommon.vcxproj
index 2d8dbec1..39752eb3 100644
--- a/libcommon/libcommon.vcxproj
+++ b/libcommon/libcommon.vcxproj
@@ -118,6 +118,7 @@
+
@@ -138,6 +139,7 @@
+
diff --git a/libcommon/src/core/FileOps.cpp b/libcommon/src/core/FileOps.cpp
index 7b5a9326..10e0a941 100644
--- a/libcommon/src/core/FileOps.cpp
+++ b/libcommon/src/core/FileOps.cpp
@@ -15,6 +15,9 @@
// You should have received a copy of the GNU General Public License along with
// VCC (Virtual Color Computer). If not, see .
////////////////////////////////////////////////////////////////////////////////
+
+// TODO: FileOps should be depreciated and functions moved to fileutil.cpp
+
#include
#include
#include
diff --git a/libcommon/src/core/filesystem.cpp b/libcommon/src/core/filesystem.cpp
index 6de559e0..2fe18171 100644
--- a/libcommon/src/core/filesystem.cpp
+++ b/libcommon/src/core/filesystem.cpp
@@ -15,39 +15,17 @@
// You should have received a copy of the GNU General Public License along with
// VCC (Virtual Color Computer). If not, see .
////////////////////////////////////////////////////////////////////////////////
+#include
#include
#include
+//TODO filesystem.cpp should be depreciated
+//TODO replace get_directory_from_path and get_filename with fileutil functions
+//TODO move find_pak_module_path to point of use
namespace vcc::core::utils
{
-
-
- LIBCOMMON_EXPORT std::string get_module_path(HMODULE module_handle)
- {
- std::string text(MAX_PATH, 0);
-
- for (;;)
- {
- DWORD ret = GetModuleFileName(module_handle, &text[0], text.size());
- if (ret == 0)
- {
- // An error occurred; return an empty string
- return {};
- }
-
- if (ret < text.size())
- {
- text.resize(ret);
-
- return text;
- }
-
- // Buffer was too small, double its size and try again
- text.resize(text.size() * 2);
- }
- }
-
+ // TODO: move this
LIBCOMMON_EXPORT std::string find_pak_module_path(std::string path)
{
if (path.empty())
@@ -58,7 +36,7 @@ namespace vcc::core::utils
auto file_handle(CreateFile(path.c_str(), 0, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
if (file_handle == INVALID_HANDLE_VALUE)
{
- const auto application_path = get_directory_from_path(::vcc::core::utils::get_module_path());
+ const auto application_path = get_directory_from_path(::VCC::Util::get_module_path(NULL));
const auto alternate_path = application_path + path;
file_handle = CreateFile(alternate_path.c_str(), 0, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (file_handle == INVALID_HANDLE_VALUE)
@@ -72,6 +50,7 @@ namespace vcc::core::utils
return path;
}
+ // TODO: replace with ::VCC::Util::GetDirectoryPart(const std::string& input)
LIBCOMMON_EXPORT std::string get_directory_from_path(std::string path)
{
const auto last_separator(path.find_last_of('\\'));
@@ -83,18 +62,7 @@ namespace vcc::core::utils
return path;
}
- LIBCOMMON_EXPORT std::string strip_application_path(std::string path)
- {
- const auto module_path = get_directory_from_path(vcc::core::utils::get_module_path(nullptr));
- auto temp_path(get_directory_from_path(path));
- if (module_path == temp_path) // If they match remove the Path
- {
- path = get_filename(path);
- }
-
- return path;
- }
-
+ //TODO: replace with ::VCC::Util::GetFileNamePart(const std::string& input)
LIBCOMMON_EXPORT std::string get_filename(std::string path)
{
const auto last_seperator = path.find_last_of('\\');
@@ -107,5 +75,4 @@ namespace vcc::core::utils
return path;
}
-
}
diff --git a/libcommon/src/core/fileutil.cpp b/libcommon/src/core/fileutil.cpp
new file mode 100644
index 00000000..67bd7603
--- /dev/null
+++ b/libcommon/src/core/fileutil.cpp
@@ -0,0 +1,146 @@
+//#define USE_LOGGING
+//======================================================================
+// General purpose Host file utilities. EJ Jaquay 2026
+//
+// This file is part of VCC (Virtual Color Computer).
+// Vcc is Copyright 2015 by Joseph Forgione
+//
+// VCC (Virtual Color Computer) is free software, you can redistribute
+// 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.
+//
+// VCC (Virtual Color Computer) 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 VCC (Virtual Color Computer). If not, see
+// .
+//
+//======================================================================
+
+#include
+#include
+#include
+#include
+#include
+
+// FIXME: directory names in libcommon are wrong
+// libcommon/include/vcc/core should be libcommon/include/vcc/util
+// libcommon/src/core should be libcommon/include/util
+
+namespace VCC::Util
+{
+ //----------------------------------------------------------------------
+ // Get most recent windows error text
+ //----------------------------------------------------------------------
+ std::string LastErrorString()
+ {
+ DWORD error_code = GetLastError();
+ char msg[256];
+ DWORD len = FormatMessageA(
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr, error_code,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ msg, sizeof(msg), nullptr
+ );
+ if (len == 0) return "Unknown error";
+ return std::string(msg, len);
+ }
+
+ // Only use this overload as argument to debug logger (DLOG)
+ const char* LastErrorTxt()
+ {
+ thread_local static std::string buffer;
+ buffer = LastErrorString();
+ return buffer.c_str();
+ }
+
+ //TODO: This is generic, move to something for generic utils
+ //----------------------------------------------------------------------
+ // Return copy of string with spaces trimmed from end of a string
+ //----------------------------------------------------------------------
+ std::string trim_right_spaces(const std::string& s)
+ {
+ size_t end = s.find_last_not_of(' ');
+ if (end == std::string::npos)
+ return {};
+ return s.substr(0, end + 1);
+ }
+
+ //----------------------------------------------------------------------
+ // Return slash normalized directory part of a path
+ //----------------------------------------------------------------------
+ std::string GetDirectoryPart(const std::string& input)
+ {
+ std::filesystem::path p(input);
+ std::string out = p.parent_path().string();
+ FixDirSlashes(out);
+ return out;
+ }
+
+ //----------------------------------------------------------------------
+ // Return filename part of a path
+ //----------------------------------------------------------------------
+ std::string GetFileNamePart(const std::string& input)
+ {
+ std::filesystem::path p(input);
+ return p.filename().string();
+ }
+
+ //----------------------------------------------------------------------
+ // Determine if path is a direcory
+ //----------------------------------------------------------------------
+ bool IsDirectory(const std::string& path)
+ {
+ std::error_code ec;
+ return std::filesystem::is_directory(path, ec) && !ec;
+ }
+
+ //-------------------------------------------------------------------
+ // Convert path directory backslashes to forward slashes
+ //-------------------------------------------------------------------
+ void FixDirSlashes(std::string &dir)
+ {
+ if (dir.empty()) return;
+ std::replace(dir.begin(), dir.end(), '\\', '/');
+ if (dir.back() == '/') dir.pop_back();
+ }
+
+ //-------------------------------------------------------------------
+ // Get the file path of a loaded module
+ // Current exe path is returned if module_handle is null
+ //-------------------------------------------------------------------
+ std::string get_module_path(HMODULE module_handle)
+ {
+ std::string text(MAX_PATH, '\0');
+ for (;;) {
+ DWORD ret = GetModuleFileName(module_handle, &text[0], text.size());
+ if (ret == 0)
+ return {}; // error
+
+ if (ret < text.size() - 1) {
+ text.resize(ret);
+ return text;
+ }
+ // truncated grow and retry
+ text.resize(text.size() * 2);
+ }
+ }
+
+ //-------------------------------------------------------------------
+ // If path directory matches application directory strip directory
+ //-------------------------------------------------------------------
+ std::string strip_application_path(std::string path)
+ {
+ auto app = get_module_path(nullptr);
+ auto app_dir = GetDirectoryPart(app);
+ auto path_dir = GetDirectoryPart(path);
+ if (path_dir == app_dir) {
+ path = GetFileNamePart(path);
+ }
+ return path;
+ }
+}
diff --git a/mpi/configuration_dialog.cpp b/mpi/configuration_dialog.cpp
index b405adc5..858a6b29 100644
--- a/mpi/configuration_dialog.cpp
+++ b/mpi/configuration_dialog.cpp
@@ -1,3 +1,4 @@
+//#define USE_LOGGING
////////////////////////////////////////////////////////////////////////////////
// Copyright 2015 by Joseph Forgione
// This file is part of VCC (Virtual Color Computer).
@@ -180,6 +181,7 @@ void configuration_dialog::cart_type_menu(unsigned int Button)
void configuration_dialog::eject_or_select_new_cartridge(unsigned int Button)
{
+
// Disable Slot changes if parent is disabled. This prevents user using the
// config dialog to eject a cartridge while VCC main is using a modal dialog
// Otherwise user can crash VCC by unloading a disk cart while inserting a disk
@@ -295,6 +297,10 @@ INT_PTR configuration_dialog::process_message(
return TRUE;
case IDC_RESET:
SendMessage(gVccWnd,WM_COMMAND,(WPARAM) ID_FILE_RESET,(LPARAM) 0);
+ close();
+ return TRUE;
+ case IDOK:
+ close();
return TRUE;
} // End switch LOWORD
break;
diff --git a/mpi/mpi.rc b/mpi/mpi.rc
index 9bd5a7d9..36fa3d9d 100644
--- a/mpi/mpi.rc
+++ b/mpi/mpi.rc
@@ -81,12 +81,13 @@ BEGIN
LTEXT "> Load cartridge.", IDC_STATIC, 124,128,60,10
LTEXT "X Eject cartridge.", IDC_STATIC, 194,128,60,10
- LTEXT "Reset VCC for startup slot change to take effect", IDC_STATIC, 74,144,230,10
- PUSHBUTTON "Reset VCC",IDC_RESET, 10,142,58,14
+ PUSHBUTTON "Reset VCC",IDC_RESET, 6,142,55,15
+ LTEXT "Reset VCC for startup slot change to take effect", IDC_STATIC, 68,145,230,10
+
+ DEFPUSHBUTTON "OK",IDOK, 240,142,50,15
// CHECKBOX "Persistent ProgramPak Images",IDC_PERSIST_PAK, 15,165,110,10
// CHECKBOX "Disable Cart Select Signal",IDC_SCS_DISABLE, 140,165,100,10
-// DEFPUSHBUTTON "OK",IDOK, 245,163, 40,14
END
IDD_POPUP_MENU MENU
diff --git a/mpi/multipak_cartridge.cpp b/mpi/multipak_cartridge.cpp
index 0d4a8906..f254744e 100644
--- a/mpi/multipak_cartridge.cpp
+++ b/mpi/multipak_cartridge.cpp
@@ -1,3 +1,4 @@
+//#define USE_LOGGING
////////////////////////////////////////////////////////////////////////////////
// Copyright 2015 by Joseph Forgione
// This file is part of VCC (Virtual Color Computer).
@@ -15,7 +16,6 @@
// You should have received a copy of the GNU General Public License along with
// VCC (Virtual Color Computer). If not, see .
////////////////////////////////////////////////////////////////////////////////
-//#define USE_LOGGING
#include "multipak_cartridge.h"
#include "multipak_cartridge_context.h"
#include "mpi.h"
@@ -108,7 +108,10 @@ void multipak_cartridge::start()
const auto path(vcc::core::utils::find_pak_module_path(configuration_.slot_cartridge_path(slot)));
if (!path.empty())
{
- mount_cartridge(slot, path);
+ if (mount_cartridge(slot, path) != vcc::core::cartridge_loader_status::success) {
+ DLOG_C("Clearing configured slot path %d\n",slot);
+ configuration_.slot_cartridge_path(slot,"");
+ }
}
}
@@ -339,6 +342,8 @@ multipak_cartridge::mount_status_type multipak_cartridge::mount_cartridge(
*context_,
*multipakHost);
+ DLOG_C("load cart %d %s\n",slot,filename);
+
auto loadedCartridge = vcc::core::load_cartridge(
filename,
std::move(ctx),
diff --git a/sdc/sdc.cpp b/sdc/sdc.cpp
index 76ef1697..af3d8746 100644
--- a/sdc/sdc.cpp
+++ b/sdc/sdc.cpp
@@ -1,6 +1,6 @@
-// SDC simulator DLL
-//
-// By E J Jaquay 2025
+//#define USE_LOGGING
+//======================================================================
+// SDC simulator. EJ Jaquay 2025
//
// This file is part of VCC (Virtual Color Computer).
// Vcc is Copyright 2015 by Joseph Forgione
@@ -19,22 +19,21 @@
// along with VCC (Virtual Color Computer). If not, see
// .
//
-//----------------------------------------------------------------------
+//======================================================================
//
-// SDC Floppy port conflicts
-// -------------------------
-// The SDC interface shares ports with the FD502 floppy controller.
+// SDC Floppy port
+// ---------------
+// The SDC interface shares ports with the FDC floppy emulator.
//
-// $FF40 ; controller latch (write)
+// $FF40 ; SDC latch / FDC control
// $FF42 ; flash data register
// $FF43 ; flash control register
-// $FF48 ; command register (write)
-// $FF48 ; status register (read)
-// $FF49 ; param register 1
-// $FF4A ; param register 2
-// $FF4B ; param register 3
+// $FF48 ; command / status
+// $FF49 ; param register 1 / FDC Track
+// $FF4A ; param register 2 / FDC Sector
+// $FF4B ; param register 3 / FDC Data
//
-// The FD502 interface uses following ports;
+// The FDC emulation uses following ports;
//
// $FF40 ; Control register (write)
// Bit 7 halt flag 0 = disabled 1 = enabled
@@ -46,6 +45,9 @@
// Bit 1 drive select 1
// Bit 0 drive select 0
//
+// SDC latch code is 0x43 which is unlikey to be used for FDC
+// control because that would be multiple drives selected.
+//
// $FF48 ; Command register (write)
// high order nibble; low order nibble type; command
// 0x0 ; I ; Restore
@@ -54,9 +56,9 @@
// 0x4 ; I ; Step in
// 0x5 ; I ; Step out
// 0x8 ; II ; Read sector
-// 0x9 ; II ; Read sector multiple
+// 0x9 ; II ; Read sector mfm
// 0xA ; II ; write sector
-// 0xB ; II ; write sector multiple
+// 0xB ; II ; write sector mfm
// 0xC ; III ; read address
// 0xD ; III ; force interrupt
// 0xE ; III ; read track
@@ -67,22 +69,11 @@
// II ; b3 side compare enable, b2 delay, b1 side, b0 0
// III ; b2 delay others 0
// IV ; interrupt control b3 immediate, b2 index pulse, b1 notready, b0 ready
-// $FF48 ; Status register (read)
-// $FF49 ; Track register (read/write)
-// $FF4A ; Sector register (read/write)
-// $FF4B ; Data register (read/write)
//
-// Port conflicts are resolved in the MPI by using the SCS (select
-// cart signal) to direct the floppy ports ($FF40-$FF5F) to the
-// selected cartridge slot, either SDC or FD502.
+// The becker port (drivewire) uses port $FF41 for status and port $FF42
+// for data so these must be always allowed for becker.dll to work.
//
-// Sometime in the past the VCC cart select was disabled in mmi.cpp. This
-// had to be be re-enabled for for sdc to co-exist with FD502. Additionally
-// the becker port (drivewire) uses port $FF41 for status and port $FF42
-// for data so these must be always alowed for becker.dll to work. This
-// means sdc.dll requires the new version of mmi.dll to work properly.
-//
-// NOTE: Stock SDCDOS does not in support the becker ports, it expects
+// NOTE: Stock SDCDOS does not support the becker ports, it expects
// drivewire to be on the bitbanger ports. RGBDOS does, however, and will
// still work with sdc.dll installed.
//
@@ -123,218 +114,97 @@
// bank is stored. When a bank is selected the file is read into ROM.
// The SDC-DOS RUN@n command can be used to select a bank.
//
-// This simulator has no provision for writing to the banks or the
-// associated files. These are easily managed using the host system.
-//
// Data written to the flash data port (0x42) can be read back.
// When the flash data port (0x43) is written the three low bits
// select the ROM from the corresponding flash bank (0-7). When
// read the flash control port returns the currently selected bank
// in the three low bits and the five bits from the Flash Data port.
//
-// SDC-DOS is typically in bank zero and disk basic in bank one. These
-// ROMS require their respective .DLL's to be installed to function,
-// these are typically in MMI slot 3 and 4 respectively.
-//
-//----------------------------------------------------------------------
-//#define USE_LOGGING
+// SDC-DOS is typically in bank zero.
+//======================================================================
-#include
-#include
-#include
-#include
-#include
-#include
+#include
+#include
+#include
+#include
#include
#pragma warning(push)
#pragma warning(disable:4091)
#include
#pragma warning(pop)
-#include
-#include
-#include
+#include
+
#include
#include
#include
#include "../CartridgeMenu.h"
#include
#include
-#include "sdc.h"
-
-//======================================================================
-//======================================================================
-static ::vcc::devices::rtc::cloud9 cloud9_rtc;
+//#include "fileutil.h"
+#include
-//======================================================================
-// Private functions
-//======================================================================
+#include "sdc.h"
-LRESULT CALLBACK SDC_Configure(HWND, UINT, WPARAM, LPARAM);
-void LoadConfig();
-bool SaveConfig(HWND);
-void BuildCartridgeMenu();
-void SelectCardBox();
-void UpdateFlashItem(int);
-void InitCardBox();
-void InitFlashBoxes();
-void FitEditTextPath(HWND, int, const char *);
+#ifdef VCC
+#pragma message("VCC is defined as a macro!")
+#endif
-void AppendPathChar(char *,char c);
-bool IsDirectory(const char *);
-char * LastErrorTxt();
-void ConvertSlashes(char *);
+#ifdef Util
+#pragma message("Util is defined as a macro!")
+#endif
-void SDCInit();
-void LoadRom(unsigned char);
-void SDCWrite(unsigned char,unsigned char);
-unsigned char SDCRead(unsigned char port);
+namespace util = VCC::Util;
-void ParseStartup();
-void SDCCommand();
-void ReadSector();
-void StreamImage();
-void WriteSector();
-bool SeekSector(unsigned char,unsigned int);
-bool ReadDrive(unsigned char,unsigned int);
-void GetDriveInfo();
-void SDCControl();
-void UpdateSD();
-bool LoadFoundFile(struct FileRecord *);
-void FixSDCPath(char *,const char *);
-void MountDisk(int,const char *,int);
-void MountNewDisk(int,const char *,int);
-bool MountNext(int);
-void OpenNew(int,const char *,int);
-void CloseDrive(int);
-void OpenFound(int,int);
-void LoadReply(const void *, int);
-void BlockReceive(unsigned char);
-void FlashControl(unsigned char);
-void LoadDirPage();
-void SetCurDir(const char *);
-bool SearchFile(const char *);
-bool InitiateDir(const char *);
-void GetFullPath(char *,const char *);
-void RenameFile(const char *);
-void KillFile(const char *);
-void MakeDirectory(const char *);
-void GetMountedImageRec();
-void GetSectorCount();
-void GetDirectoryLeaf();
-unsigned char PickReplyByte(unsigned char);
-unsigned char WriteFlashBank(unsigned short);
-
-void FloppyCommand(unsigned char);
-void FloppyRestore();
-void FloppySeek();
-void FloppyReadDisk();
-void FloppyWriteDisk();
-void FloppyTrack(unsigned char);
-void FloppySector(unsigned char);
-void FloppyWriteData(unsigned char);
-unsigned int FloppyLSN(unsigned int,unsigned int,unsigned int);
-unsigned char FloppyStatus();
-unsigned char FloppyReadData();
+static ::vcc::devices::rtc::cloud9 cloud9_rtc;
//======================================================================
// Globals
//======================================================================
-// Idle Status counter
-int idle_ctr = 0;
+static HINSTANCE gModuleInstance; // Dll handle
+static int idle_ctr = 0; // Idle Status counter
-// SDC CoCo Interface
-struct Interface
-{
- int sdclatch;
- unsigned char cmdcode;
- unsigned char status;
- unsigned char reply1;
- unsigned char reply2;
- unsigned char reply3;
- unsigned char param1;
- unsigned char param2;
- unsigned char param3;
- unsigned char reply_mode; // 0=words, 1=bytes
- unsigned char reply_status;
- unsigned char half_sent;
- int bufcnt;
- char *bufptr;
- char blkbuf[600];
-};
-static Interface IF = {};
-
-// Cart ROM
-char PakRom[0x4000];
-
-// Host paths for SDC
+// Callback pointers
+static void* gCallbackContext = nullptr;
+static PakAssertInteruptHostCallback AssertIntCallback = nullptr;
+static PakAppendCartridgeMenuHostCallback CartMenuCallback = nullptr;
static char IniFile[MAX_PATH] = {}; // Vcc ini file name
-static char SDCard[MAX_PATH] = {}; // SD card root directory
-static char CurDir[256] = {}; // SDC current directory
-static char SeaDir[MAX_PATH] = {}; // Last directory searched
-// Packed file records for interface
-#pragma pack(1)
-struct FileRecord {
- char name[8];
- char type[3];
- char attrib;
- char hihi_size;
- char lohi_size;
- char hilo_size;
- char lolo_size;
-};
-#pragma pack()
-static struct FileRecord DirPage[16];
-
-// Mounted image data
-struct SDC_disk_t {
- HANDLE hFile;
- unsigned int size;
- unsigned int headersize;
- DWORD sectorsize;
- DWORD tracksectors;
- char doublesided;
- char name[MAX_PATH];
- char fullpath[MAX_PATH];
- struct FileRecord filerec;
-};
-SDC_disk_t SDC_disk[2] = {};
+static HWND gVccWindow = nullptr;
+static HWND hConfigureDlg = nullptr;
+static HWND hSDCardBox = nullptr;
+static HWND hStartupBank = nullptr;
+
+static int ClockEnable = 0;
+static char SDC_Status[16] = {};
+static char PakRom[0x4000] = {};
+static unsigned char CurrentBank = 0xff;
+static unsigned char EnableBankWrite = 0;
+static unsigned char BankWriteState = 0;
+
+static std::string gSDRoot {}; // SD card root directory
+static std::string gCurDir {}; // Current directory relative to root
+
+// SDC CoCo Interface
+static FileList gFileList {};
+static struct FileRecord gDirPage[16] {};
+static CocoDisk gCocoDisk[2] {};
+static Interface IFace {};
// Flash banks
static char FlashFile[8][MAX_PATH];
static FILE *h_RomFile = nullptr;
static unsigned char StartupBank = 0;
-static unsigned char CurrentBank = 0xff;
-static unsigned char EnableBankWrite = 0;
static unsigned char BankWriteNum = 0;
-static unsigned char BankWriteState = 0;
static unsigned char BankDirty = 0;
static unsigned char BankData = 0;
-// Dll handle
-static HINSTANCE gModuleInstance;
-
-// Clock enable IDC_CLOCK
-static int ClockEnable;
-
-// Windows file lookup handle and data
-static HANDLE hFind = INVALID_HANDLE_VALUE;
-static WIN32_FIND_DATAA dFound;
-
-// config control handles
-static HWND hConfigureDlg = nullptr;
-static HWND hSDCardBox = nullptr;
-static HWND hStartupBank = nullptr;
-
// Streaming control
static int streaming;
static unsigned char stream_cmdcode;
static unsigned int stream_lsn;
-static char Status[16] = {};
-
// Floppy I/O
static char FlopDrive = 0;
static char FlopData = 0;
@@ -351,18 +221,100 @@ static int EDBOXES[8] = {ID_TEXT0,ID_TEXT1,ID_TEXT2,ID_TEXT3,
static int UPDBTNS[8] = {ID_UPDATE0,ID_UPDATE1,ID_UPDATE2,ID_UPDATE3,
ID_UPDATE4,ID_UPDATE5,ID_UPDATE6,ID_UPDATE7};
-static char MPIPath[MAX_PATH];
+static char ROMPath[MAX_PATH];
+
+//======================================================================
+// Functions
+//======================================================================
+
+LRESULT CALLBACK SDC_Configure(HWND, UINT, WPARAM, LPARAM);
+void LoadConfig();
+bool SaveConfig(HWND);
+void BuildCartridgeMenu();
+void SelectCardBox();
+void UpdateFlashItem(int);
+void InitCardBox();
+void InitFlashBoxes();
+void FitEditTextPath(HWND, int, const std::string&);
+void InitSDC();
+void LoadRom(unsigned char);
+void ParseStartup();
+bool SearchFile(const std::string&);
+void UnloadDisk(int);
+void GetFileList(const std::string&);
+void SortFileList();
+std::string FixFATPath(const std::string&);
+std::string lfn_from_sfn(const char (&name)[8], const char (&ext)[3]);
+void sfn_from_lfn(char (&name)[8], char (&ext)[3], const std::string& lfn);
+template void copy_to_fixed_char(char (&dest)[N], const std::string& src);
+
+void SDCReadSector();
+void SDCStreamImage();
+void SDCWriteSector();
+void SDCGetDriveInfo();
+void SDCUpdateSD();
+void SDCSetCurDir(const char *);
+bool SDCInitiateDir(const char *);
+void SDCRenameFile(const char *);
+void SDCKillFile(const char *);
+void SDCMakeDirectory(const char *);
+void SDCGetMountedImageRec();
+void SDCGetSectorCount();
+void SDCGetDirectoryLeaf();
+void SDCMountDisk(int,const char *,int);
+void SDCMountNewDisk(int,const char *,int);
+bool SDCMountNext(int);
+unsigned char SDCRead(unsigned char port);
+void SDCOpenFound(int,int);
+void SDCOpenNew(int,const char *,int);
+void SDCWrite(unsigned char,unsigned char);
+void SDCControl();
+void SDCCommand();
+void SDCBlockReceive(unsigned char);
+void SDCFlashControl(unsigned char);
+void SDCFloppyCommand(unsigned char);
+void SDCFloppyRestore();
+void SDCFloppySeek();
+void SDCFloppyReadDisk();
+void SDCFloppyWriteDisk();
+void SDCFloppyTrack(unsigned char);
+void SDCFloppySector(unsigned char);
+void SDCFloppyWriteData(unsigned char);
+unsigned int SDCFloppyLSN(unsigned int,unsigned int,unsigned int);
+unsigned char SDCFloppyStatus();
+unsigned char SDCFloppyReadData();
+unsigned char SDCPickReplyByte(unsigned char);
+unsigned char SDCWriteFlashBank(unsigned short);
+void SDCLoadReply(const void *, int);
+bool SDCSeekSector(unsigned char,unsigned int);
+bool SDCReadDrive(unsigned char,unsigned int);
+bool SDCLoadNextDirPage();
+std::string SDCGetFullPath(const std::string&);
-// DLL Callback pointers
-static void* gCallbackContext = nullptr;
-static PakAssertInteruptHostCallback AssertIntCallback = nullptr;
-static PakAppendCartridgeMenuHostCallback CartMenuCallback = nullptr;
//======================================================================
-// DLL exports
+// DLL interface
//======================================================================
extern "C"
{
+ // PakInitialize gets called first, sets up dynamic menues and captures callbacks
+ __declspec(dllexport) void PakInitialize(
+ void* const callback_context,
+ const char* const configuration_path,
+ HWND hVccWnd,
+ const cpak_callbacks* const callbacks)
+ {
+ gVccWindow = hVccWnd;
+ DLOG_C("SDC %p %p %p %p %p\n",*callbacks);
+ gCallbackContext = callback_context;
+ CartMenuCallback = callbacks->add_menu_item;
+ AssertIntCallback = callbacks->assert_interrupt;
+ strcpy(IniFile, configuration_path);
+
+ LoadConfig();
+ BuildCartridgeMenu();
+ }
+
__declspec(dllexport) const char* PakGetName()
{
static char string_buffer[MAX_LOADSTRING];
@@ -384,29 +336,13 @@ extern "C"
return string_buffer;
}
- __declspec(dllexport) void PakInitialize(
- void* const callback_context,
- const char* const configuration_path,
- HWND hVccWnd,
- const cpak_callbacks* const callbacks)
- {
- DLOG_C("SDC %p %p %p %p %p\n",*callbacks);
- gCallbackContext = callback_context;
- CartMenuCallback = callbacks->add_menu_item;
- AssertIntCallback = callbacks->assert_interrupt;
- strcpy(IniFile, configuration_path);
-
- LoadConfig();
- BuildCartridgeMenu();
- }
-
// Clean up must also be done on DLL_UNLOAD incase VCC is closed!
__declspec(dllexport) void PakTerminate()
{
CloseCartDialog(hConfigureDlg);
hConfigureDlg = nullptr;
- CloseDrive(0);
- CloseDrive(1);
+ UnloadDisk(0);
+ UnloadDisk(1);
}
// Write to port
@@ -432,7 +368,7 @@ extern "C"
__declspec(dllexport) void PakReset()
{
DLOG_C("PakReset\n");
- SDCInit();
+ InitSDC();
}
// Dll export run config dialog
@@ -447,7 +383,7 @@ extern "C"
ShowWindow(hConfigureDlg,1);
break;
case 11:
- MountNext (0);
+ SDCMountNext (0);
break;
}
BuildCartridgeMenu();
@@ -457,13 +393,13 @@ extern "C"
// Return SDC status.
__declspec(dllexport) void PakGetStatus(char* text_buffer, size_t buffer_size)
{
- strncpy(text_buffer,Status,buffer_size);
if (idle_ctr < 100) {
idle_ctr++;
} else {
idle_ctr = 0;
- snprintf(Status,16,"SDC:%d idle",CurrentBank);
+ snprintf(SDC_Status,16,"SDC:%d idle",CurrentBank);
}
+ strncpy(text_buffer,SDC_Status,buffer_size);
}
// Return a byte from the current PAK ROM
@@ -471,7 +407,7 @@ extern "C"
{
adr &= 0x3FFF;
if (EnableBankWrite) {
- return WriteFlashBank(adr);
+ return SDCWriteFlashBank(adr);
} else {
BankWriteState = 0; // Any read resets write state
return(PakRom[adr]);
@@ -479,9 +415,6 @@ extern "C"
}
}
-//-------------------------------------------------------------
-// Dll Main here so it can use PakTerminate
-//-------------------------------------------------------------
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID rsvd)
{
if (reason == DLL_PROCESS_ATTACH) {
@@ -505,11 +438,15 @@ void BuildCartridgeMenu()
CartMenuCallback(gCallbackContext, "", MID_ENTRY, MIT_Seperator);
CartMenuCallback(gCallbackContext, "SDC Drive 0",MID_ENTRY,MIT_Head);
char tmp[64]={};
- if (strcmp(SDC_disk[0].name,"") == 0) {
+ if (strcmp(gCocoDisk[0].name,"") == 0) {
strcpy(tmp,"empty");
} else {
- strcpy(tmp,SDC_disk[0].name);
- strcat(tmp," (load next)");
+ strcpy(tmp,gCocoDisk[0].name);
+ if (gFileList.nextload_flag) {
+ strcat(tmp," (load next)");
+ } else {
+ strcat(tmp," (no next)");
+ }
}
CartMenuCallback(gCallbackContext, tmp, ControlId(11),MIT_Slave);
CartMenuCallback(gCallbackContext, "SDC Config", ControlId(10), MIT_StandAlone);
@@ -549,7 +486,11 @@ SDC_Configure(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lParam*/)
if (HIWORD(wParam) == EN_CHANGE) {
char tmp[MAX_PATH];
GetWindowText(hSDCardBox,tmp,MAX_PATH);
- if (*tmp != '\0') strncpy(SDCard,tmp,MAX_PATH);
+ if (*tmp != '\0') {
+ gCurDir = tmp;
+ util::FixDirSlashes(gCurDir);
+ //strncpy(SDCard,tmp,MAX_PATH);
+ }
}
return TRUE;
case ID_UPDATE0:
@@ -604,13 +545,23 @@ SDC_Configure(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lParam*/)
//------------------------------------------------------------
void LoadConfig()
{
+ char tmp[MAX_PATH];
+
+ // FIXME should be "SDCRomPath" and saved when changed
GetPrivateProfileString
- ("DefaultPaths", "MPIPath", "", MPIPath, MAX_PATH, IniFile);
+ ("DefaultPaths", "MPIPath", "", ROMPath, MAX_PATH, IniFile);
+
GetPrivateProfileString
- ("SDC", "SDCardPath", "", SDCard, MAX_PATH, IniFile);
+ ("SDC", "SDCardPath", "", tmp, MAX_PATH, IniFile);
+
+ gSDRoot = tmp;
+ util::FixDirSlashes(gSDRoot);
- if (!IsDirectory(SDCard)) {
- MessageBox (nullptr,"Invalid SDCard Path in VCC init","Error",0);
+ DLOG_C("LoadConfig ROMPath %s\n",ROMPath);
+ DLOG_C("LoadConfig gSDRoot %s\n",gSDRoot);
+
+ if (!util::IsDirectory(gSDRoot)) {
+ MessageBox (gVccWindow,"Invalid SDCard Path in VCC init","Error",0);
}
for (int i=0;i<8;i++) {
@@ -629,11 +580,11 @@ void LoadConfig()
//------------------------------------------------------------
bool SaveConfig(HWND hDlg)
{
- if (!IsDirectory(SDCard)) {
- MessageBox(nullptr,"Invalid SDCard Path\n","Error",0);
+ if (!util::IsDirectory(gSDRoot)) {
+ MessageBox(gVccWindow,"Invalid SDCard Path\n","Error",0);
return false;
}
- WritePrivateProfileString("SDC","SDCardPath",SDCard,IniFile);
+ WritePrivateProfileString("SDC","SDCardPath",gSDRoot.c_str(),IniFile);
for (int i=0;i<8;i++) {
char txt[32];
@@ -652,21 +603,21 @@ bool SaveConfig(HWND hDlg)
return true;
}
-//------------------------------------------------------------
-// Fit path in edit text box (Box must be ES_READONLY)
-//------------------------------------------------------------
-void FitEditTextPath(HWND hDlg, int ID, const char * path) {
+void FitEditTextPath(HWND hDlg, int ID, const std::string& path)
+{
HDC c;
HWND h;
RECT r;
- char p[MAX_PATH];
+
if ((c = GetDC(hDlg)) == NULL) return;
if ((h = GetDlgItem(hDlg, ID)) == NULL) return;
+
GetClientRect(h, &r);
- strncpy(p, path, MAX_PATH);
- PathCompactPath(c, p, r.right);
- ConvertSlashes(p);
- SetWindowText(h, p);
+ std::string p = path;
+ p.resize(MAX_PATH);
+ PathCompactPathA(c, p.data(), r.right);
+ util::FixDirSlashes(p);
+ SetWindowTextA(h, p.c_str());
ReleaseDC(hDlg, c);
}
@@ -693,7 +644,7 @@ void InitFlashBoxes()
void InitCardBox()
{
hSDCardBox = GetDlgItem(hConfigureDlg,ID_SD_BOX);
- SendMessage(hSDCardBox, WM_SETTEXT, 0, (LPARAM)SDCard);
+ SendMessage(hSDCardBox, WM_SETTEXT, 0, (LPARAM) gSDRoot.c_str());
}
//------------------------------------------------------------
@@ -715,7 +666,7 @@ void UpdateFlashItem(int index)
dlg.setDefExt("rom");
dlg.setFilter("Rom File\0*.rom\0All Files\0*.*\0\0");
dlg.setTitle(title);
- dlg.setInitialDir(MPIPath); // FIXME should be SDC rom path
+ dlg.setInitialDir(ROMPath);
if (dlg.show(0,hConfigureDlg)) {
dlg.getupath(filename,MAX_PATH); // cvt to unix style
strncpy(FlashFile[index],filename,MAX_PATH);
@@ -732,79 +683,143 @@ void UpdateFlashItem(int index)
//------------------------------------------------------------
// Dialog to select SD card path in user home directory
//------------------------------------------------------------
+
+// TODO: Replace Win32 browse dialog with the modern IFileDialog API (Vista+)
void SelectCardBox()
{
- // Prompt user for path
- BROWSEINFO bi = { nullptr };
+ namespace fs = std::filesystem;
+
+ // Prepare browse dialog
+ BROWSEINFO bi = {};
bi.hwndOwner = GetActiveWindow();
bi.lpszTitle = "Set the SD card path";
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NONEWFOLDERBUTTON;
- // Start from user home diretory
- SHGetSpecialFolderLocation
- (nullptr,CSIDL_PROFILE, const_cast(& bi.pidlRoot));
+ // Set initial folder to user profile
+ LPITEMIDLIST pidlRoot = nullptr;
+ if (SUCCEEDED(SHGetSpecialFolderLocation(nullptr, CSIDL_PROFILE, &pidlRoot)))
+ bi.pidlRoot = pidlRoot;
+ // Show dialog
LPITEMIDLIST pidl = SHBrowseForFolder(&bi);
- if (pidl != nullptr) {
- SHGetPathFromIDList(pidl,SDCard);
+
+ // Free root PIDL if allocated
+ if (pidlRoot)
+ CoTaskMemFree(pidlRoot);
+
+ if (pidl)
+ {
+ char tmp[MAX_PATH] = {};
+ if (SHGetPathFromIDList(pidl, tmp))
+ {
+ gSDRoot = tmp;
+ util::FixDirSlashes(gSDRoot);
+ SendMessage(hSDCardBox, WM_SETTEXT, 0, (LPARAM)gSDRoot.c_str());
+ }
+
CoTaskMemFree(pidl);
}
-
- ConvertSlashes(SDCard);
- SendMessage(hSDCardBox, WM_SETTEXT, 0, (LPARAM)SDCard);
}
//=====================================================================
-// SDC Simulation
+// SDC startup
//======================================================================
//----------------------------------------------------------------------
// Init the controller. This gets called by PakReset
//----------------------------------------------------------------------
-void SDCInit()
+void InitSDC()
{
- DLOG_C("\nSDCInit\n");
#ifdef USE_LOGGING
- MoveWindow(GetConsoleWindow(),0,0,300,800,TRUE);
+ DLOG_C("\nInitSDC\n");
+ MoveWindow(GetConsoleWindow(),0,0,400,800,TRUE);
#endif
- // Init the hFind handle (otherwise could crash on dll load)
- hFind = INVALID_HANDLE_VALUE;
-
// Make sure drives are unloaded
- MountDisk (0,"",0);
- MountDisk (1,"",0);
+ SDCMountDisk (0,"",0);
+ SDCMountDisk (1,"",0);
// Load SDC settings
LoadConfig();
LoadRom(StartupBank);
- SetCurDir(""); // May be changed by ParseStartup()
+ SDCSetCurDir(""); // May be changed by ParseStartup()
- SDC_disk[0] = {};
- SDC_disk[1] = {};
+ gCocoDisk[0] = {};
+ gCocoDisk[1] = {};
+
+ gFileList = {};
// Process the startup config file
ParseStartup();
// init the interface
- IF = {};
+ IFace = {};
return;
}
+//----------------------------------------------------------------------
+// Parse the startup.cfg file
+//----------------------------------------------------------------------
+void ParseStartup()
+{
+ namespace fs = std::filesystem;
+ fs::path sd = fs::path(gSDRoot);
+
+ if (!fs::is_directory(sd)) {
+ DLOG_C("ParseStartup SDCard path invalid\n");
+ return;
+ }
+
+ fs::path cfg = sd / "startup.cfg";
+ FILE* su = std::fopen(cfg.string().c_str(), "r");
+ if (su == nullptr) {
+ DLOG_C("ParseStartup file not found, %s\n", cfg.string().c_str());
+ return;
+ }
+
+ // Strict single char followed by '=' then path
+ char buf[MAX_PATH];
+ while (fgets(buf,sizeof(buf),su) != nullptr) {
+ //Chomp line ending
+ buf[strcspn(buf,"\r\n")] = 0;
+ // Skip line if less than 3 chars;
+ if (strlen(buf) < 3) continue;
+ // Skip line if second char is not '='
+ if (buf[1] != '=') continue;
+ // Grab drive num char
+ char drv = buf[0];
+ // Attempt to mount drive
+ switch (drv) {
+ case '0':
+ SDCMountDisk(0,&buf[2],0);
+ break;
+ case '1':
+ SDCMountDisk(1,&buf[2],0);
+ break;
+ case 'D':
+ SDCSetCurDir(&buf[2]);
+ break;
+ }
+ }
+ fclose(su);
+}
+
//-------------------------------------------------------------
// Load rom from flash bank
//-------------------------------------------------------------
void LoadRom(unsigned char bank)
{
-
unsigned char ch;
int ctr = 0;
char *p_rom;
char *RomFile;
+ DLOG_C("LoadRom load flash bank %d\n",bank);
+
+ // Skip if bank is already active
if (bank == CurrentBank) return;
// Make sure flash file is closed
@@ -813,12 +828,13 @@ void LoadRom(unsigned char bank)
h_RomFile = nullptr;
}
+ // If bank contents have been changed write the flash file
if (BankDirty) {
RomFile = FlashFile[CurrentBank];
DLOG_C("LoadRom switching out dirty bank %d %s\n",CurrentBank,RomFile);
h_RomFile = fopen(RomFile,"wb");
- if (h_RomFile == nullptr) {
- DLOG_C("LoadRom failed to open bank file%d\n",bank);
+ if (!h_RomFile) {
+ MessageBox (gVccWindow,"Can not write Rom file","SDC Rom Save Failed",0);
} else {
ctr = 0;
p_rom = PakRom;
@@ -829,7 +845,6 @@ void LoadRom(unsigned char bank)
BankDirty = 0;
}
- DLOG_C("LoadRom load flash bank %d\n",bank);
RomFile = FlashFile[bank];
CurrentBank = bank;
@@ -842,13 +857,16 @@ void LoadRom(unsigned char bank)
}
}
- // Open romfile for read or write if not startup bank
- h_RomFile = fopen(RomFile,"rb");
- if (h_RomFile == nullptr) {
- if (CurrentBank != StartupBank) h_RomFile = fopen(RomFile,"wb");
- }
+ // Open romfile for write if not startup bank
+ if (CurrentBank != StartupBank)
+ h_RomFile = fopen(RomFile,"wb");
+
+ if (CurrentBank == StartupBank || h_RomFile == nullptr)
+ h_RomFile = fopen(RomFile,"rb");
+
if (h_RomFile == nullptr) {
- DLOG_C("LoadRom '%s' failed %s \n",RomFile,LastErrorTxt());
+ std::string msg="Check Rom Path and SDC Config\n"+std::string(RomFile);
+ MessageBox (gVccWindow,msg.c_str(),"SDC Startup Rom Load Failed",0);
return;
}
@@ -865,89 +883,46 @@ void LoadRom(unsigned char bank)
return;
}
-//----------------------------------------------------------------------
-// Parse the startup.cfg file
-//----------------------------------------------------------------------
-void ParseStartup()
-{
- char buf[MAX_PATH+10];
- if (!IsDirectory(SDCard)) {
- DLOG_C("ParseStartup SDCard path invalid\n");
- return;
- }
-
- strncpy(buf,SDCard,MAX_PATH);
- AppendPathChar(buf,'/');
- strncat(buf,"startup.cfg",MAX_PATH);
-
- FILE *su = fopen(buf,"r");
- if (su == nullptr) {
- DLOG_C("ParseStartup file not found,%s\n",buf);
- return;
- }
-
- // Strict single char followed by '=' then path
- while (fgets(buf,sizeof(buf),su) != nullptr) {
- //Chomp line ending
- buf[strcspn(buf,"\r\n")] = 0;
- // Skip line if less than 3 chars;
- if (strlen(buf) < 3) continue;
- // Skip line if second char is not '='
- if (buf[1] != '=') continue;
- // Grab drive num char
- char drv = buf[0];
- // Attempt to mount drive
- switch (drv) {
- case '0':
- MountDisk(0,&buf[2],0);
- break;
- case '1':
- MountDisk(1,&buf[2],0);
- break;
- case 'D':
- SetCurDir(&buf[2]);
- break;
- }
- }
- fclose(su);
-}
+//=====================================================================
+// SDC Interface
+//=====================================================================
//----------------------------------------------------------------------
// Write port. If a command needs a data block to complete it
-// will put a count (256 or 512) in IF.bufcnt.
+// will put a count (256 or 512) in IFace.bufcnt.
//----------------------------------------------------------------------
void SDCWrite(unsigned char data,unsigned char port)
{
if (port < 0x40 || port > 0x4F) return;
- if (IF.sdclatch) {
+ if (IFace.sdclatch) {
switch (port) {
- // Control Latch
+ // Toggle Control Latch
case 0x40:
- if (IF.sdclatch) IF = {};
+ if (IFace.sdclatch) IFace = {};
break;
// Command registor
case 0x48:
- IF.cmdcode = data;
+ IFace.cmdcode = data;
SDCCommand();
break;
// Command param #1
case 0x49:
- IF.param1 = data;
+ IFace.param1 = data;
break;
// Command param #2 or block data receive
case 0x4A:
- if (IF.bufcnt > 0)
- BlockReceive(data);
+ if (IFace.bufcnt > 0)
+ SDCBlockReceive(data);
else
- IF.param2 = data;
+ IFace.param2 = data;
break;
// Command param #3 or block data receive
case 0x4B:
- if (IF.bufcnt > 0)
- BlockReceive(data);
+ if (IFace.bufcnt > 0)
+ SDCBlockReceive(data);
else
- IF.param3 = data;
+ IFace.param3 = data;
break;
// Unhandled
default:
@@ -961,10 +936,10 @@ void SDCWrite(unsigned char data,unsigned char port)
// Mask out halt, density, precomp, and motor
switch (data & 0x43) { // 0b01000111
case 0:
- IF.sdclatch = false;
+ IFace.sdclatch = false;
break;
case 0x43:
- IF.sdclatch = true;
+ IFace.sdclatch = true;
break;
case 0b00000001:
FlopDrive = 0;
@@ -989,23 +964,23 @@ void SDCWrite(unsigned char data,unsigned char port)
break;
// Flash Control
case 0x43:
- FlashControl(data);
+ SDCFlashControl(data);
break;
// floppy command
case 0x48:
- FloppyCommand(data);
+ SDCFloppyCommand(data);
break;
// floppy set track
case 0x49:
- FloppyTrack(data);
+ SDCFloppyTrack(data);
break;
// floppy set sector
case 0x4A:
- FloppySector(data);
+ SDCFloppySector(data);
break;
// floppy write data
case 0x4B:
- FloppyWriteData(data);
+ SDCFloppyWriteData(data);
break;
// Unhandled
default:
@@ -1023,33 +998,33 @@ unsigned char SDCRead(unsigned char port)
{
unsigned char rpy = 0;
- if (IF.sdclatch) {
+ if (IFace.sdclatch) {
switch (port) {
case 0x48:
- if (IF.bufcnt > 0) {
- rpy = (IF.reply_status != 0) ? IF.reply_status:STA_BUSY|STA_READY;
+ if (IFace.bufcnt > 0) {
+ rpy = (IFace.reply_status != 0) ? IFace.reply_status:STA_BUSY|STA_READY;
} else {
- rpy = IF.status;
+ rpy = IFace.status;
}
break;
// Reply data 1
case 0x49:
- rpy = IF.reply1;
+ rpy = IFace.reply1;
break;
// Reply data 2 or block reply
case 0x4A:
- if (IF.bufcnt > 0) {
- rpy = PickReplyByte(port);
+ if (IFace.bufcnt > 0) {
+ rpy = SDCPickReplyByte(port);
} else {
- rpy = IF.reply2;
+ rpy = IFace.reply2;
}
break;
// Reply data 3 or block reply
case 0x4B:
- if (IF.bufcnt > 0) {
- rpy = PickReplyByte(port);
+ if (IFace.bufcnt > 0) {
+ rpy = SDCPickReplyByte(port);
} else {
- rpy = IF.reply3;
+ rpy = IFace.reply3;
}
break;
default:
@@ -1057,19 +1032,37 @@ unsigned char SDCRead(unsigned char port)
rpy = 0;
break;
}
+ // If not SDC latched do floppy controller simulation
} else {
switch (port) {
+ case 0x40:
+ //Nitros9 driver reads this
+ DLOG_C("SDCRead floppy port 40?\n");
+ rpy = 0;
+ break;
// Flash control read is used by SDCDOS to detect the SDC
case 0x43:
rpy = CurrentBank | (BankData & 0xF8);
break;
// Floppy read status
case 0x48:
- rpy = FloppyStatus();
+ rpy = SDCFloppyStatus();
+ break;
+ // Current Track
+ case 0x49:
+ //Nitros9 driver reads this every sector
+ //DLOG_C("SDCRead floppy track?\n");
+ rpy = 0;
+ break;
+ // Current Sector
+ case 0x4A:
+ //Nitros9 driver reads this every sector
+ //DLOG_C("SDCRead floppy sector?\n");
+ rpy = 0;
break;
// Floppy read data
case 0x4B:
- rpy = FloppyReadData();
+ rpy = SDCFloppyReadData();
break;
default:
DLOG_C("SDCRead U %02x\n",port);
@@ -1081,45 +1074,191 @@ unsigned char SDCRead(unsigned char port)
}
//----------------------------------------------------------------------
-// Floppy I/O
+// Dispatch SDC commands
//----------------------------------------------------------------------
-
-void FloppyCommand(unsigned char data)
+void SDCCommand()
{
- unsigned char cmd = data >> 4;
- switch (cmd) {
- case 0: //RESTORE
- FloppyRestore();
+ switch (IFace.cmdcode & 0xF0) {
+ // Read sector
+ case 0x80:
+ SDCReadSector();
break;
- case 1: //SEEK
- FloppySeek();
+ // Stream 512 byte sectors
+ case 0x90:
+ SDCStreamImage();
break;
- //case 2: //STEP
- //case 3: //STEPUPD
- //case 4: //STEPIN
- //case 5: //STEPINUPD
+ // Get drive info
+ case 0xC0:
+ SDCGetDriveInfo();
+ break;
+ // Control SDC
+ case 0xD0:
+ SDCControl();
+ break;
+ // Next two are block receive commands
+ case 0xA0:
+ case 0xE0:
+ IFace.status = STA_READY | STA_BUSY;
+ IFace.bufptr = IFace.blkbuf;
+ IFace.bufcnt = 256;
+ IFace.half_sent = 0;
+ break;
+ }
+ return;
+}
+
+//----------------------------------------------------------------------
+// Floppy I/O
+//----------------------------------------------------------------------
+
+void SDCFloppyCommand(unsigned char data)
+{
+ unsigned char cmd = data >> 4;
+ switch (cmd) {
+ case 0: //RESTORE
+ SDCFloppyRestore();
+ break;
+ case 1: //SEEK
+ SDCFloppySeek();
+ break;
+ //case 2: //STEP
+ //case 3: //STEPUPD
+ //case 4: //STEPIN
+ //case 5: //STEPINUPD
//case 6: //STEFOUT
//case 7: //STEPOUTUPD
case 8: //READSECTOR
- FloppyReadDisk();
+ case 9: //READSECTORM
+ SDCFloppyReadDisk();
break;
- //case 9: //READSECTORM
case 10: //WRITESECTOR
- FloppyWriteDisk();
+ case 11: //WRITESECTORM
+ SDCFloppyWriteDisk();
+ break;
+ case 12: //READADDRESS
+ //Nitros9 driver does this
+ DLOG_C("SDCFloppyCommand read address?\n");
+ break;
+ case 13: //FORCEINTERUPT
+ //Nitros9 driver does this
+ DLOG_C("SDCFloppyCommand force interrupt?\n");
break;
- //case 11: //WRITESECTORM
- //case 12: //READADDRESS
- //case 13: //FORCEINTERUPT
//case 14: //READTRACK
//case 15: //WRITETRACK
default:
- DLOG_C("Floppy cmd not implemented %d\n",cmd);
+ DLOG_C("SDCFloppyCommand %d not implemented\n",cmd);
break;
}
}
+//----------------------------------------------------------------------
+// Get drive information
+//----------------------------------------------------------------------
+void SDCGetDriveInfo()
+{
+ int drive = IFace.cmdcode & 1;
+ switch (IFace.param1) {
+ case 0x49:
+ // 'I' - return drive information in block
+ SDCGetMountedImageRec();
+ break;
+ case 0x43:
+ // 'C' Return current directory leaf in block
+ SDCGetDirectoryLeaf();
+ break;
+ case 0x51:
+ // 'Q' Return the size of disk image in p1,p2,p3
+ SDCGetSectorCount();
+ break;
+ case 0x3E:
+ // '>' Get directory page
+ SDCLoadNextDirPage();
+ IFace.reply_mode=0;
+ SDCLoadReply(gDirPage,256);
+ break;
+ case 0x2B:
+ // '+' Mount next next disk in set.
+ SDCMountNext(drive);
+ break;
+ case 0x56:
+ // 'V' Get BCD firmware version number in p2, p3.
+ IFace.reply2 = 0x00;
+ IFace.reply3 = 0x01;
+ break;
+ }
+}
+
+//----------------------------------------------------------------------
+// Update SD Commands.
+//----------------------------------------------------------------------
+void SDCUpdateSD()
+{
+ switch (IFace.blkbuf[0]) {
+ case 0x4D: //M
+ SDCMountDisk(IFace.cmdcode&1,&IFace.blkbuf[2],0);
+ break;
+ case 0x6D: //m
+ SDCMountDisk(IFace.cmdcode&1,&IFace.blkbuf[2],1);
+ break;
+ case 0x4E: //N
+ SDCMountNewDisk(IFace.cmdcode&1,&IFace.blkbuf[2],0);
+ break;
+ case 0x6E: //n
+ SDCMountNewDisk(IFace.cmdcode&1,&IFace.blkbuf[2],1);
+ break;
+ case 0x44: //D
+ SDCSetCurDir(&IFace.blkbuf[2]);
+ break;
+ case 0x4C: //L
+ SDCInitiateDir(&IFace.blkbuf[2]);
+ break;
+ case 0x4B: //K
+ SDCMakeDirectory(&IFace.blkbuf[2]);
+ break;
+ case 0x52: //R
+ SDCRenameFile(&IFace.blkbuf[2]);
+ break;
+ case 0x58: //X
+ SDCKillFile(&IFace.blkbuf[2]);
+ break;
+ default:
+ DLOG_C("SDCUpdateSD %02x not Supported\n",IFace.blkbuf[0]);
+ IFace.status = STA_FAIL;
+ break;
+ }
+}
+
+//-------------------------------------------------------------------
+// Load a DirPage from FileList. Called until list is exhausted
+//-------------------------------------------------------------------
+bool SDCLoadNextDirPage()
+{
+ DLOG_C("SDCLoadNextDirPage cur:%d siz:%d\n",
+ gFileList.cursor,gFileList.files.size());
+
+ std::memset(gDirPage, 0, sizeof gDirPage);
+
+ if (gFileList.cursor >= gFileList.files.size()) {
+ DLOG_C("SDCLoadNextDirPage no files left\n");
+ return false;
+ }
+
+ size_t count = 0;
+ while (count < 16 && gFileList.cursor < gFileList.files.size()) {
+ gDirPage[count++] = FileRecord(gFileList.files[gFileList.cursor]);
+ ++gFileList.cursor;
+ }
+
+ // gFileList.cursor not valid for next file loading
+ gFileList.nextload_flag = false;
+
+ return true;
+}
+
+//----------------------------------------------------------------------
// floppy restore
-void FloppyRestore()
+//----------------------------------------------------------------------
+void SDCFloppyRestore()
{
DLOG_C("FloppyRestore\n");
FlopTrack = 0;
@@ -1130,17 +1269,23 @@ void FloppyRestore()
AssertIntCallback(gCallbackContext, INT_NMI, IS_NMI);
}
-// floppy seek
-void FloppySeek()
+//----------------------------------------------------------------------
+// floppy seek. No attempt is made to simulate seek times here.
+//----------------------------------------------------------------------
+void SDCFloppySeek()
{
DLOG_C("FloppySeek %d %d\n",FlopTrack,FlopData);
FlopTrack = FlopData;
}
-// Convert floppy drive, track, sector, to LSN
-//TODO: Side select
-//TODO: Support floppy formats other than raw
-unsigned int FloppyLSN(
+//----------------------------------------------------------------------
+// convert side (drive), track, sector, to LSN. The floppy controller
+// uses CHS addressing while hard drives, and the SDC, use LBA addressing.
+// FIXME:
+// Nine sector tracks, (512 byte sectors)
+// disk type: gCocoDisk[drive].type
+//----------------------------------------------------------------------
+unsigned int SDCFloppyLSN(
unsigned int drive, // 0 or 1
unsigned int track, // 0 to num tracks
unsigned int sector // 1 to 18
@@ -1149,15 +1294,17 @@ unsigned int FloppyLSN(
return track * 18 + sector - 1;
}
+//----------------------------------------------------------------------
// floppy read sector
-void FloppyReadDisk()
+//----------------------------------------------------------------------
+void SDCFloppyReadDisk()
{
- auto lsn = FloppyLSN(FlopDrive,FlopTrack,FlopSector);
+ auto lsn = SDCFloppyLSN(FlopDrive,FlopTrack,FlopSector);
//DLOG_C("FloppyReadDisk %d %d\n",FlopDrive,lsn);
- snprintf(Status,16,"SDC:%d Rd %d,%d",CurrentBank,FlopDrive,lsn);
- if (SeekSector(FlopDrive,lsn)) {
- if (ReadFile(SDC_disk[FlopDrive].hFile,FlopRdBuf,256,&FlopRdCnt,nullptr)) {
+ snprintf(SDC_Status,16,"SDC:%d Rd %d,%d",CurrentBank,FlopDrive,lsn);
+ if (SDCSeekSector(FlopDrive,lsn)) {
+ if (ReadFile(gCocoDisk[FlopDrive].hFile,FlopRdBuf,256,&FlopRdCnt,nullptr)) {
DLOG_C("FloppyReadDisk %d %d\n",FlopDrive,lsn);
FlopStatus = FLP_DATAREQ;
} else {
@@ -1170,8 +1317,10 @@ void FloppyReadDisk()
}
}
+//----------------------------------------------------------------------
// floppy write sector
-void FloppyWriteDisk()
+//----------------------------------------------------------------------
+void SDCFloppyWriteDisk()
{
// write not implemented
int lsn = FlopTrack * 18 + FlopSector - 1;
@@ -1179,14 +1328,18 @@ void FloppyWriteDisk()
FlopStatus = FLP_READONLY;
}
+//----------------------------------------------------------------------
// floppy set track
-void FloppyTrack(unsigned char data)
+//----------------------------------------------------------------------
+void SDCFloppyTrack(unsigned char data)
{
FlopTrack = data;
}
+//----------------------------------------------------------------------
// floppy set sector
-void FloppySector(unsigned char data)
+//----------------------------------------------------------------------
+void SDCFloppySector(unsigned char data)
{
FlopSector = data; // Sector num in track (1-18)
//int lsn = FlopTrack * 18 + FlopSector - 1;
@@ -1194,8 +1347,10 @@ void FloppySector(unsigned char data)
FlopStatus = FLP_NORMAL;
}
+//----------------------------------------------------------------------
// floppy write data
-void FloppyWriteData(unsigned char data)
+//----------------------------------------------------------------------
+void SDCFloppyWriteData(unsigned char data)
{
//DLOG_C("FloppyWriteData %d\n",data);
if (FlopWrCnt<256) {
@@ -1209,15 +1364,19 @@ void FloppyWriteData(unsigned char data)
FlopData = data;
}
+//----------------------------------------------------------------------
// floppy get status
-unsigned char FloppyStatus()
+//----------------------------------------------------------------------
+unsigned char SDCFloppyStatus()
{
//DLOG_C("FloppyStatus %02x\n",FlopStatus);
return FlopStatus;
}
+//----------------------------------------------------------------------
// floppy read data
-unsigned char FloppyReadData()
+//----------------------------------------------------------------------
+unsigned char SDCFloppyReadData()
{
unsigned char rpy;
if (FlopRdCnt>0) {
@@ -1237,225 +1396,97 @@ unsigned char FloppyReadData()
// has most replies in words and the order the word bytes are read can
// vary so we play games to send the right ones
//----------------------------------------------------------------------
-unsigned char PickReplyByte(unsigned char port)
+unsigned char SDCPickReplyByte(unsigned char port)
{
unsigned char rpy = 0;
// Byte mode bytes come on port 0x4B
- if (IF.reply_mode == 1) {
- if (IF.bufcnt > 0) {
- rpy = *IF.bufptr++;
- IF.bufcnt--;
+ if (IFace.reply_mode == 1) {
+ if (IFace.bufcnt > 0) {
+ rpy = *IFace.bufptr++;
+ IFace.bufcnt--;
}
// Word mode bytes come on port 0x4A and 0x4B
} else {
if (port == 0x4A) {
- rpy = IF.bufptr[0];
+ rpy = IFace.bufptr[0];
} else {
- rpy = IF.bufptr[1];
+ rpy = IFace.bufptr[1];
}
- if (IF.half_sent) {
- IF.bufcnt -= 2;
- IF.bufptr += 2;
- IF.half_sent = 0;
+ if (IFace.half_sent) {
+ IFace.bufcnt -= 2;
+ IFace.bufptr += 2;
+ IFace.half_sent = 0;
} else {
- IF.half_sent = 1;
+ IFace.half_sent = 1;
}
}
// Keep stream going until stopped
- if ((IF.bufcnt < 1) && streaming) StreamImage();
+ if ((IFace.bufcnt < 1) && streaming) SDCStreamImage();
return rpy;
}
-//----------------------------------------------------------------------
-// Dispatch SDC commands
-//----------------------------------------------------------------------
-void SDCCommand()
-{
-
- switch (IF.cmdcode & 0xF0) {
- // Read sector
- case 0x80:
- ReadSector();
- break;
- // Stream 512 byte sectors
- case 0x90:
- StreamImage();
- break;
- // Get drive info
- case 0xC0:
- GetDriveInfo();
- break;
- // Control SDC
- case 0xD0:
- SDCControl();
- break;
- // Next two are block receive commands
- case 0xA0:
- case 0xE0:
- IF.status = STA_READY | STA_BUSY;
- IF.bufptr = IF.blkbuf;
- IF.bufcnt = 256;
- IF.half_sent = 0;
- break;
- }
- return;
-}
-
//----------------------------------------------------------------------
// Receive block data
//----------------------------------------------------------------------
-void BlockReceive(unsigned char byte)
+void SDCBlockReceive(unsigned char byte)
{
- if (IF.bufcnt > 0) {
- IF.bufcnt--;
- *IF.bufptr++ = byte;
+ if (IFace.bufcnt > 0) {
+ IFace.bufcnt--;
+ *IFace.bufptr++ = byte;
}
// Done receiving block
- if (IF.bufcnt < 1) {
- switch (IF.cmdcode & 0xF0) {
+ if (IFace.bufcnt < 1) {
+ switch (IFace.cmdcode & 0xF0) {
case 0xA0:
- WriteSector();
+ SDCWriteSector();
break;
case 0xE0:
- UpdateSD();
+ SDCUpdateSD();
break;
default:
- DLOG_C("BlockReceive invalid cmd %d\n",IF.cmdcode);
- IF.status = STA_FAIL;
+ DLOG_C("SDCBlockReceive invalid cmd %d\n",IFace.cmdcode);
+ IFace.status = STA_FAIL;
break;
}
}
}
//----------------------------------------------------------------------
-// Get drive information
-//----------------------------------------------------------------------
-void GetDriveInfo()
-{
- int drive = IF.cmdcode & 1;
- switch (IF.param1) {
- case 0x49:
- // 'I' - return drive information in block
- GetMountedImageRec();
- break;
- case 0x43:
- // 'C' Return current directory leaf in block
- GetDirectoryLeaf();
- break;
- case 0x51:
- // 'Q' Return the size of disk image in p1,p2,p3
- GetSectorCount();
- break;
- case 0x3E:
- // '>' Get directory page
- LoadDirPage();
- IF.reply_mode=0;
- LoadReply(DirPage,256);
- break;
- case 0x2B:
- // '+' Mount next next disk in set.
- MountNext(drive);
- break;
- case 0x56:
- // 'V' Get BCD firmware version number in p2, p3.
- IF.reply2 = 0x00;
- IF.reply3 = 0x01;
- break;
- }
-}
-
-//----------------------------------------------------------------------
-// Get directory leaf. This is the leaf name of the current directory,
-// not it's full path. SDCEXP uses this with the set directory '..'
-// command to learn the full path when restore last session is active.
-// The full path is saved in SDCX.CFG for the next session.
+// Reply with directory leaf. Reply is 32 byte directory record. The first
+// 8 bytes are the name of the leaf, the next 3 are blanks, and the
+// remainder is undefined ("Private" is SDC docs)
+// SDCEXP uses this with the set directory '..' command to learn the full
+// path when restore last session is active. The full path is saved in
+// SDCX.CFG for the next session.
//----------------------------------------------------------------------
-void GetDirectoryLeaf()
+void SDCGetDirectoryLeaf()
{
- DLOG_C("GetDirectoryLeaf CurDir '%s'\n",CurDir);
+ namespace fs = std::filesystem;
- char leaf[32];
- memset(leaf,0,32);
-
- // Strip trailing '/' from current directory. There should not
- // be one there but the slash is so bothersome best to check.
- int n = strlen(CurDir);
- if (n > 0) {
- n -= 1;
- if (CurDir[n] == '/') CurDir[n] = '\0';
- }
+ char leaf[32] = {};
- // If at least one leaf find the last one
- if (n > 0) {
- const char *p = strrchr(CurDir,'/');
- if (p == nullptr) {
- p = CurDir;
- } else {
- p += 1;
- }
- // Build reply
- memset(leaf,32,12);
- strncpy(leaf,p,12);
- n = strlen(p);
- // SDC filenames are fixed 8 chars so blank any terminator
- if (n < 12) leaf[n] = ' ';
- // If current directory is SDCard root reply with zeros and status
+ if (!gCurDir.empty()) {
+ std::string leafName = fs::path(gCurDir).filename().string();
+ memset(leaf, ' ', 11);
+ size_t copyLen = std::min(8,leafName.size());
+ memcpy(leaf, leafName.data(), copyLen);
} else {
- IF.reply_status = STA_FAIL | STA_NOTFOUND;
+ IFace.reply_status = STA_FAIL | STA_NOTFOUND;
}
+ DLOG_C("SDCGetDirectoryLeaf CurDir '%s' leaf '%s' \n", gCurDir.c_str(),leaf);
- IF.reply_mode=0;
- LoadReply(leaf,32);
-}
-
-//----------------------------------------------------------------------
-// Update SD Commands.
-//----------------------------------------------------------------------
-void UpdateSD()
-{
- switch (IF.blkbuf[0]) {
- case 0x4D: //M
- MountDisk(IF.cmdcode&1,&IF.blkbuf[2],0);
- break;
- case 0x6D: //m
- MountDisk(IF.cmdcode&1,&IF.blkbuf[2],1);
- break;
- case 0x4E: //N
- MountNewDisk(IF.cmdcode&1,&IF.blkbuf[2],0);
- break;
- case 0x6E: //n
- MountNewDisk(IF.cmdcode&1,&IF.blkbuf[2],1);
- break;
- case 0x44: //D
- SetCurDir(&IF.blkbuf[2]);
- break;
- case 0x4C: //L
- InitiateDir(&IF.blkbuf[2]);
- break;
- case 0x4B: //K
- MakeDirectory(&IF.blkbuf[2]);
- break;
- case 0x52: //R
- RenameFile(&IF.blkbuf[2]);
- break;
- case 0x58: //X
- KillFile(&IF.blkbuf[2]);
- break;
- default:
- DLOG_C("UpdateSD %02x not Supported\n",IF.blkbuf[0]);
- IF.status = STA_FAIL;
- break;
- }
+ IFace.reply_mode = 0;
+ SDCLoadReply(leaf, sizeof(leaf));
}
//----------------------------------------------------------------------
// Flash control
//----------------------------------------------------------------------
-void FlashControl(unsigned char data)
+void SDCFlashControl(unsigned char data)
{
unsigned char bank = data & 7;
EnableBankWrite = data & 0x80;
@@ -1488,9 +1519,9 @@ void FlashControl(unsigned char data)
// state 6 write bank # adr sect val 30 kill bank # sect (adr & 0x3000)
//
//----------------------------------------------------------------------
-unsigned char WriteFlashBank(unsigned short adr)
+unsigned char SDCWriteFlashBank(unsigned short adr)
{
- DLOG_C("WriteFlashBank %d %d %04X %02X\n",
+ DLOG_C("SDCWriteFlashBank %d %d %04X %02X\n",
BankWriteState,BankWriteNum,adr,BankData);
// BankWriteState controls the write or kill
@@ -1537,79 +1568,6 @@ unsigned char WriteFlashBank(unsigned short adr)
return BankData;
}
-//----------------------------------------------------------------------
-// Seek sector in drive image
-// cmdcode:
-// b0 drive number
-// b1 single sided LSN flag
-// b2 eight bit transfer flag
-//
-//----------------------------------------------------------------------
-bool SeekSector(unsigned char cmdcode, unsigned int lsn)
-{
- int drive = cmdcode & 1; // Drive number 0 or 1
- int sside = cmdcode & 2; // Single sided LSN flag
-
- int trk = lsn / SDC_disk[drive].tracksectors;
- int sec = lsn % SDC_disk[drive].tracksectors;
-
- // The single sided LSN flag tells the controller that the lsn
- // assumes the disk image is a single-sided floppy disk. If the
- // disk is actually double-sided the LSN must be adjusted.
- if (sside && SDC_disk[drive].doublesided) {
- DLOG_C("SeekSector sside %d %d\n",drive,lsn);
- lsn = 2 * SDC_disk[drive].tracksectors * trk + sec;
- }
-
- // Allow seek to expand a writable file to a resonable limit
- if (lsn > MAX_DSK_SECTORS) {
- DLOG_C("SeekSector exceed max image %d %d\n",drive,lsn);
- return false;
- }
-
- // Seek to logical sector on drive.
- LARGE_INTEGER pos{0};
- pos.QuadPart = lsn * SDC_disk[drive].sectorsize + SDC_disk[drive].headersize;
-
- if (!SetFilePointerEx(SDC_disk[drive].hFile,pos,nullptr,FILE_BEGIN)) {
- DLOG_C("SeekSector error %s\n",LastErrorTxt());
- return false;
- }
- return true;
-}
-
-//----------------------------------------------------------------------
-// Read a sector from drive image and load reply
-//----------------------------------------------------------------------
-bool ReadDrive(unsigned char cmdcode, unsigned int lsn)
-{
- char buf[520];
- DWORD cnt = 0;
- int drive = cmdcode & 1;
- if (SDC_disk[drive].hFile == nullptr) {
- DLOG_C("ReadDrive %d not open\n");
- return false;
- }
-
- if (!SeekSector(cmdcode,lsn)) {
- return false;
- }
-
- if (!ReadFile(SDC_disk[drive].hFile,buf,SDC_disk[drive].sectorsize,&cnt,nullptr)) {
- DLOG_C("ReadDrive %d %s\n",drive,LastErrorTxt());
- return false;
- }
-
- if (cnt != SDC_disk[drive].sectorsize) {
- DLOG_C("ReadDrive %d short read\n",drive);
- return false;
- }
-
- snprintf(Status,16,"SDC:%d Rd %d,%d",CurrentBank,drive,lsn);
- LoadReply(buf,cnt);
- return true;
-}
-
//----------------------------------------------------------------------
// Read logical sector
// cmdcode:
@@ -1617,49 +1575,49 @@ bool ReadDrive(unsigned char cmdcode, unsigned int lsn)
// b1 single sided flag
// b2 eight bit transfer flag
//----------------------------------------------------------------------
-void ReadSector()
+void SDCReadSector()
{
- unsigned int lsn = (IF.param1 << 16) + (IF.param2 << 8) + IF.param3;
+ unsigned int lsn = (IFace.param1 << 16) + (IFace.param2 << 8) + IFace.param3;
- DLOG_C("R%d\n",lsn);
+ //DLOG_C("R%d\n",lsn);
- IF.reply_mode = ((IF.cmdcode & 4) == 0) ? 0 : 1; // words : bytes
- if (!ReadDrive(IF.cmdcode,lsn))
- IF.status = STA_FAIL | STA_READERROR;
+ IFace.reply_mode = ((IFace.cmdcode & 4) == 0) ? 0 : 1; // words : bytes
+ if (!SDCReadDrive(IFace.cmdcode,lsn))
+ IFace.status = STA_FAIL | STA_READERROR;
else
- IF.status = STA_NORMAL;
+ IFace.status = STA_NORMAL;
}
//----------------------------------------------------------------------
// Stream image data
//----------------------------------------------------------------------
-void StreamImage()
+void SDCStreamImage()
{
// If already streaming continue
if (streaming) {
stream_lsn++;
// Else start streaming
} else {
- stream_cmdcode = IF.cmdcode;
- IF.reply_mode = ((IF.cmdcode & 4) == 0) ? 0 : 1;
- stream_lsn = (IF.param1 << 16) + (IF.param2 << 8) + IF.param3;
- DLOG_C("StreamImage lsn %d\n",stream_lsn);
+ stream_cmdcode = IFace.cmdcode;
+ IFace.reply_mode = ((IFace.cmdcode & 4) == 0) ? 0 : 1;
+ stream_lsn = (IFace.param1 << 16) + (IFace.param2 << 8) + IFace.param3;
+ DLOG_C("SDCStreamImage lsn %d\n",stream_lsn);
}
// For now can only stream 512 byte sectors
int drive = stream_cmdcode & 1;
- SDC_disk[drive].sectorsize = 512;
- SDC_disk[drive].tracksectors = 9;
+ gCocoDisk[drive].sectorsize = 512;
+ gCocoDisk[drive].tracksectors = 9;
- if (stream_lsn > (SDC_disk[drive].size/SDC_disk[drive].sectorsize)) {
- DLOG_C("StreamImage done\n");
+ if (stream_lsn > (gCocoDisk[drive].size/gCocoDisk[drive].sectorsize)) {
+ DLOG_C("SDCStreamImage done\n");
streaming = 0;
return;
}
- if (!ReadDrive(stream_cmdcode,stream_lsn)) {
- DLOG_C("StreamImage read error %s\n",LastErrorTxt());
- IF.status = STA_FAIL;
+ if (!SDCReadDrive(stream_cmdcode,stream_lsn)) {
+ DLOG_C("SDCStreamImage read error %s\n",util::LastErrorTxt());
+ IFace.status = STA_FAIL;
streaming = 0;
return;
}
@@ -1669,71 +1627,71 @@ void StreamImage()
//----------------------------------------------------------------------
// Write logical sector
//----------------------------------------------------------------------
-void WriteSector()
+void SDCWriteSector()
{
DWORD cnt = 0;
- int drive = IF.cmdcode & 1;
- unsigned int lsn = (IF.param1 << 16) + (IF.param2 << 8) + IF.param3;
- snprintf(Status,16,"SDC:%d Wr %d,%d",CurrentBank,drive,lsn);
+ int drive = IFace.cmdcode & 1;
+ unsigned int lsn = (IFace.param1 << 16) + (IFace.param2 << 8) + IFace.param3;
+ snprintf(SDC_Status,16,"SDC:%d Wr %d,%d",CurrentBank,drive,lsn);
- if (SDC_disk[drive].hFile == nullptr) {
- IF.status = STA_FAIL;
+ if (gCocoDisk[drive].hFile == nullptr) {
+ IFace.status = STA_FAIL;
return;
}
- if (!SeekSector(drive,lsn)) {
- IF.status = STA_FAIL;
+ if (!SDCSeekSector(drive,lsn)) {
+ IFace.status = STA_FAIL;
return;
}
- if (!WriteFile(SDC_disk[drive].hFile,IF.blkbuf,
- SDC_disk[drive].sectorsize,&cnt,nullptr)) {
- DLOG_C("WriteSector %d %s\n",drive,LastErrorTxt());
- IF.status = STA_FAIL;
+ if (!WriteFile(gCocoDisk[drive].hFile,IFace.blkbuf,
+ gCocoDisk[drive].sectorsize,&cnt,nullptr)) {
+ DLOG_C("SDCWriteSector %d %s\n",drive,util::LastErrorTxt());
+ IFace.status = STA_FAIL;
return;
}
- if (cnt != SDC_disk[drive].sectorsize) {
- DLOG_C("WriteSector %d short write\n",drive);
- IF.status = STA_FAIL;
+ if (cnt != gCocoDisk[drive].sectorsize) {
+ DLOG_C("SDCWriteSector %d short write\n",drive);
+ IFace.status = STA_FAIL;
return;
}
- IF.status = 0;
+ IFace.status = 0;
return;
}
//----------------------------------------------------------------------
// Return sector count for mounted disk image
//----------------------------------------------------------------------
-void GetSectorCount() {
+void SDCGetSectorCount() {
- int drive = IF.cmdcode & 1;
- unsigned int numsec = SDC_disk[drive].size/SDC_disk[drive].sectorsize;
- IF.reply3 = numsec & 0xFF;
+ int drive = IFace.cmdcode & 1;
+ unsigned int numsec = gCocoDisk[drive].size/gCocoDisk[drive].sectorsize;
+ IFace.reply3 = numsec & 0xFF;
numsec = numsec >> 8;
- IF.reply2 = numsec & 0xFF;
+ IFace.reply2 = numsec & 0xFF;
numsec = numsec >> 8;
- IF.reply1 = numsec & 0xFF;
- IF.status = STA_READY;
+ IFace.reply1 = numsec & 0xFF;
+ IFace.status = STA_READY;
}
//----------------------------------------------------------------------
// Return file record for mounted disk image
//----------------------------------------------------------------------
-void GetMountedImageRec()
+void SDCGetMountedImageRec()
{
- int drive = IF.cmdcode & 1;
- //DLOG_C("GetMountedImageRec %d %s\n",drive,SDC_disk[drive].fullpath);
- if (strlen(SDC_disk[drive].fullpath) == 0) {
- IF.status = STA_FAIL;
+ int drive = IFace.cmdcode & 1;
+ //DLOG_C("SDCGetMountedImageRec %d %s\n",drive,gCocoDisk[drive].fullpath);
+ if (strlen(gCocoDisk[drive].fullpath) == 0) {
+ IFace.status = STA_FAIL;
} else {
- IF.reply_mode = 0;
- LoadReply(&SDC_disk[drive].filerec,sizeof(FileRecord));
+ IFace.reply_mode = 0;
+ SDCLoadReply(&gCocoDisk[drive].filerec,sizeof(FileRecord));
}
}
//----------------------------------------------------------------------
// $DO Abort stream and mount disk in a set of disks.
-// IF.param1 0: Next disk 1-9: specific disk.
-// IF.param2 b0: Blink Enable
+// IFace.param1 0: Next disk 1-9: specific disk.
+// IFace.param2 b0: Blink Enable
//----------------------------------------------------------------------
void SDCControl()
{
@@ -1741,172 +1699,154 @@ void SDCControl()
if (streaming) {
DLOG_C ("Streaming abort");
streaming = 0;
- IF.status = STA_READY;
- IF.bufcnt = 0;
+ IFace.status = STA_READY;
+ IFace.bufcnt = 0;
} else {
// TODO: Mount in set
DLOG_C("SDCControl Mount in set unsupported %d %d %d \n",
- IF.cmdcode,IF.param1,IF.param2);
- IF.status = STA_FAIL | STA_NOTFOUND;
+ IFace.cmdcode,IFace.param1,IFace.param2);
+ IFace.status = STA_FAIL | STA_NOTFOUND;
}
}
//----------------------------------------------------------------------
-// Load reply. Count is bytes, 512 max.
+// Seek sector in drive image
+// cmdcode:
+// b0 drive number
+// b1 single sided LSN flag
+// b2 eight bit transfer flag
+//
//----------------------------------------------------------------------
-void LoadReply(const void *data, int count)
+bool SDCSeekSector(unsigned char cmdcode, unsigned int lsn)
{
- if ((count < 2) | (count > 512)) {
- DLOG_C("LoadReply bad count %d\n",count);
- return;
- }
+ int drive = cmdcode & 1; // Drive number 0 or 1
+ int sside = cmdcode & 2; // Single sided LSN flag
- memcpy(IF.blkbuf,data,count);
+ int trk = lsn / gCocoDisk[drive].tracksectors;
+ int sec = lsn % gCocoDisk[drive].tracksectors;
- IF.bufptr = IF.blkbuf;
- IF.bufcnt = count;
- IF.half_sent = 0;
+ // The single sided LSN flag tells the controller that the lsn
+ // assumes the disk image is a single-sided floppy disk. If the
+ // disk is actually double-sided the LSN must be adjusted.
+ if (sside && gCocoDisk[drive].doublesided) {
+ DLOG_C("SDCSeekSector sside %d %d\n",drive,lsn);
+ lsn = 2 * gCocoDisk[drive].tracksectors * trk + sec;
+ }
- // If port reads exceed the count zeros will be returned
- IF.reply2 = 0;
- IF.reply3 = 0;
+ // Allow seek to expand a writable file to a resonable limit
+ if (lsn > MAX_DSK_SECTORS) {
+ DLOG_C("SDCSeekSector exceed max image %d %d\n",drive,lsn);
+ return false;
+ }
- return;
-}
+ // Seek to logical sector on drive.
+ LARGE_INTEGER pos{0};
+ pos.QuadPart = lsn * gCocoDisk[drive].sectorsize + gCocoDisk[drive].headersize;
-//----------------------------------------------------------------------
-// A file path may be in SDC format which does not use a dot to seperate
-// the name from the extension. User is free to use standard dot format.
-// "FOODIR/FOO.DSK" = FOODIR/FOO.DSK
-// "FOODIR/FOO DSK" = FOODIR/FOO.DSK
-// "FOODIR/ALONGFOODSK" = FOODIR/ALONGFOO.DSK
-//----------------------------------------------------------------------
-void FixSDCPath(char *path, const char *fpath8)
-{
- const char *pname8 = strrchr(fpath8,'/');
- // Copy Directory portion
- if (pname8 != nullptr) {
- pname8++;
- memcpy(path,fpath8,pname8-fpath8);
- } else {
- pname8 = fpath8;
- }
- path[pname8-fpath8]='\0'; // terminate directory portion
-
- // Copy Name portion
- char c;
- char name[16];
- int namlen=0;
- while(c = *pname8++) {
- if ((c == '.')||(c == ' ')) break;
- name[namlen++] = c;
- if (namlen > 7) break;
- }
-
- // Copy extension if any thing is left
- if (c) {
- name[namlen++] = '.';
- int extlen=0;
- while(c = *pname8++) {
- if (c == '.' || c == ' ') continue;
- name[namlen++] = c;
- extlen++;
- if (extlen > 2) break;
- }
+ if (!SetFilePointerEx(gCocoDisk[drive].hFile,pos,nullptr,FILE_BEGIN)) {
+ DLOG_C("SDCSeekSector error %s\n",util::LastErrorTxt());
+ return false;
}
- name[namlen] = '\0'; // terminate name
- strncat(path,name,MAX_PATH); // append it to directory
+ return true;
}
//----------------------------------------------------------------------
-// Load a file record with the file found by Find File
+// Read a sector from drive image and load reply
//----------------------------------------------------------------------
-bool LoadFoundFile(struct FileRecord * rec)
+bool SDCReadDrive(unsigned char cmdcode, unsigned int lsn)
{
- memset(rec,0,sizeof(rec));
- memset(rec->name,' ',8);
- memset(rec->type,' ',3);
-
- // Special case filename starts with a dot
- if (dFound.cFileName[0] == '.' ) {
- // Don't load if current directory is SD root,
- // is only one dot, or if more than two chars
- if ((*CurDir=='\0') |
- (dFound.cFileName[1] != '.' ) |
- (dFound.cFileName[2] != '\0'))
- return false;
- rec->name[0]='.';
- rec->name[1]='.';
- rec->attrib = ATTR_DIR;
- return true;
+ char buf[520];
+ DWORD cnt = 0;
+ int drive = cmdcode & 1;
+ if (gCocoDisk[drive].hFile == nullptr) {
+ DLOG_C("SDCReadDrive %d not open\n");
+ return false;
}
- // File type
- const char * pdot = strrchr(dFound.cFileName,'.');
- if (pdot) {
- const char * ptyp = pdot + 1;
- for (int cnt = 0; cnt<3; cnt++) {
- if (*ptyp == '\0') break;
- rec->type[cnt] = *ptyp++;
- }
+ if (!SDCSeekSector(cmdcode,lsn)) {
+ return false;
}
- // File name
- const char * pnam = dFound.cFileName;
- for (int cnt = 0; cnt < 8; cnt++) {
- if (*pnam == '\0') break;
- if (pdot && (pnam == pdot)) break;
- rec->name[cnt] = *pnam++;
+ if (!ReadFile(gCocoDisk[drive].hFile,buf,gCocoDisk[drive].sectorsize,&cnt,nullptr)) {
+ DLOG_C("SDCReadDrive %d %s\n",drive,util::LastErrorTxt());
+ return false;
}
- // Attributes
- if (dFound.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
- rec->attrib |= ATTR_RDONLY;
+ if (cnt != gCocoDisk[drive].sectorsize) {
+ DLOG_C("SDCReadDrive %d short read\n",drive);
+ return false;
}
- if (dFound.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
- rec->attrib |= ATTR_DIR;
+
+ snprintf(SDC_Status,16,"SDC:%d Rd %d,%d",CurrentBank,drive,lsn);
+ SDCLoadReply(buf,cnt);
+ return true;
+}
+
+//----------------------------------------------------------------------
+// Load reply. Count is bytes, 512 max.
+//----------------------------------------------------------------------
+void SDCLoadReply(const void *data, int count)
+{
+ if ((count < 2) | (count > 512)) {
+ DLOG_C("SDCLoadReply bad count %d\n",count);
+ return;
}
- // Filesize, sssume < 4G (dFound.nFileSizeHigh == 0)
- rec->lolo_size = (dFound.nFileSizeLow) & 0xFF;
- rec->hilo_size = (dFound.nFileSizeLow >> 8) & 0xFF;
- rec->lohi_size = (dFound.nFileSizeLow >> 16) & 0xFF;
- rec->hihi_size = (dFound.nFileSizeLow >> 24) & 0xFF;
+ memcpy(IFace.blkbuf,data,count);
- return true;
+ IFace.bufptr = IFace.blkbuf;
+ IFace.bufcnt = count;
+ IFace.half_sent = 0;
+
+ // If port reads exceed the count zeros will be returned
+ IFace.reply2 = 0;
+ IFace.reply3 = 0;
+
+ return;
}
//----------------------------------------------------------------------
// Create and mount a new disk image
-// IF.param1 == 0 create 161280 byte JVC file
-// IF.param1 SDF image number of cylinders
-// IF.param2 SDC image number of sides
+// IFace.param1 == 0 create 161280 byte JVC file
+// IFace.param1 SDF image number of cylinders
+// IFace.param2 SDC image number of sides
//----------------------------------------------------------------------
-void MountNewDisk (int drive, const char * path, int raw)
+void SDCMountNewDisk (int drive, const char * path, int raw)
{
- //DLOG_C("MountNewDisk %d %s %d\n",drive,path,raw);
-
+ DLOG_C("SDCMountNewDisk %d %s %d\n",drive,path,raw);
// limit drive to 0 or 1
drive &= 1;
// Close and clear previous entry
- CloseDrive(drive);
- SDC_disk[drive] = {};
+ UnloadDisk(drive);
// Convert from SDC format
- char file[MAX_PATH];
- FixSDCPath(file,path);
+ char file[MAX_PATH] {};
+ strncpy(file,FixFATPath(path).c_str(),MAX_PATH-1);
// Look for pre-existing file
if (SearchFile(file)) {
- OpenFound(drive,raw);
+ SDCOpenFound(drive,raw);
return;
}
- OpenNew(drive,path,raw);
+ SDCOpenNew(drive,path,raw);
return;
}
+//----------------------------------------------------------------------
+// Unload disk in drive
+//----------------------------------------------------------------------
+void UnloadDisk(int drive) {
+ int d = drive & 1;
+ if ( gCocoDisk[d].hFile &&
+ gCocoDisk[d].hFile != INVALID_HANDLE_VALUE) {
+ CloseHandle(gCocoDisk[d].hFile);
+ DLOG_C("UnloadDisk %d %s\n",drive,gCocoDisk[d].name);
+ }
+ gCocoDisk[d] = {};
+}
+
//----------------------------------------------------------------------
// Mount Disk. If image path starts with '/' load drive relative
// to SDRoot, else load drive relative to the current directory.
@@ -1915,33 +1855,32 @@ void MountNewDisk (int drive, const char * path, int raw)
// the 'Next Disk' function.
// TODO: Sets of type SOMEAPPn.DSK
//----------------------------------------------------------------------
-void MountDisk (int drive, const char * path, int raw)
+void SDCMountDisk (int drive, const char * path, int raw)
{
- DLOG_C("MountDisk %d %s %d\n",drive,path,raw);
+ DLOG_C("SDCMountDisk %d %s %d\n",drive,path,raw);
drive &= 1;
- // Close and clear previous entry
- CloseDrive(drive);
- SDC_disk[drive] = {};
+ // Unload previous dsk
+ UnloadDisk(drive);
// Check for UNLOAD. Path will be an empty string.
if (*path == '\0') {
- DLOG_C("MountDisk unload %d %s\n",drive,path);
- IF.status = STA_NORMAL;
+ DLOG_C("SDCMountDisk unload %d %s\n",drive,path);
+ IFace.status = STA_NORMAL;
if (drive == 0) BuildCartridgeMenu();
return;
}
- char file[MAX_PATH];
- char tmp[MAX_PATH];
-
// Convert from SDC format
- FixSDCPath(file,path);
+ char file[MAX_PATH] {};
+ strncpy(file,FixFATPath(path).c_str(),MAX_PATH-1);
// Look for the file
bool found = SearchFile(file);
+ char tmp[MAX_PATH];
+
// if no '.' in the name try appending .DSK or wildcard
if (!found && (strchr(file,'.') == nullptr)) {
strncpy(tmp,file,MAX_PATH);
@@ -1956,125 +1895,133 @@ void MountDisk (int drive, const char * path, int raw)
// Give up
if (!found) {
- DLOG_C("MountDisk not found '%s'\n",file);
- IF.status = STA_FAIL | STA_NOTFOUND;
+ DLOG_C("SDCMountDisk not found '%s'\n",file);
+ IFace.status = STA_FAIL | STA_NOTFOUND;
return;
}
// Mount first image found
- OpenFound(drive,raw);
+ SDCOpenFound(drive,raw);
return;
}
//----------------------------------------------------------------------
// Mount Next Disk from found set
//----------------------------------------------------------------------
-bool MountNext (int drive)
+bool SDCMountNext (int drive)
{
- if (FindNextFile(hFind,&dFound) == 0) {
- DLOG_C("MountNext no more\n");
- FindClose(hFind);
- hFind = INVALID_HANDLE_VALUE;
+
+ // Can't mount next unless disk set has been loaded
+ if (!gFileList.nextload_flag) {
+ DLOG_C("SDCMountNext disabled\n");
return false;
}
+ if (gFileList.files.size() <= 1) {
+ DLOG_C("SDCMountNext List empty or only one file\n");
+ return false;
+ }
+
+ gFileList.cursor++;
+
+ if (gFileList.cursor >= gFileList.files.size()) {
+ gFileList.cursor = 0;
+ DLOG_C("SDCMountNext reset cursor\n");
+ }
+
+ DLOG_C("SDCMountNext %d %d\n",
+ gFileList.cursor, gFileList.files.size());
+
// Open next image found on the drive
- OpenFound(drive,0);
+ SDCOpenFound(drive,0);
+
return true;
}
//----------------------------------------------------------------------
// Open new disk image
//
-// IF.Param1: $FF49 B number of cylinders for SDF
-// IF.Param2: $FF4A X.H number of sides for SDF image
+// IFace.Param1: $FF49 B number of cylinders for SDF
+// IFace.Param2: $FF4A X.H number of sides for SDF image
//
-// OpenNew 0 'A.DSK' 0 40 0 NEW
-// OpenNew 0 'B.DSK' 0 40 1 NEW++ one side
-// OpenNew 0 'C.DSK' 0 40 2 NEW++ two sides
+// SDCOpenNew 0 'A.DSK' 0 40 0 NEW
+// SDCOpenNew 0 'B.DSK' 0 40 1 NEW++ one side
+// SDCOpenNew 0 'C.DSK' 0 40 2 NEW++ two sides
//
// Currently new JVC files are 35 cylinders, one sided
// Possibly future num cylinders could specify 40 or more
// cylinders with num cyl controlling num sides
//
//----------------------------------------------------------------------
-void OpenNew( int drive, const char * path, int raw)
+void SDCOpenNew( int drive, const char * path, int raw)
{
- DLOG_C("OpenNew %d '%s' %d %d %d\n",
- drive,path,raw,IF.param1,IF.param2);
+ DLOG_C("SDCOpenNew %d '%s' %d %d %d\n",
+ drive,path,raw,IFace.param1,IFace.param2);
// Number of sides controls file type
- switch (IF.param2) {
+ switch (IFace.param2) {
case 0: //NEW
// create JVC DSK file
break;
case 1: //NEW+
case 2: //NEW++
- DLOG_C("OpenNew SDF file not supported\n");
- IF.status = STA_FAIL | STA_INVALID;
+ DLOG_C("SDCOpenNew SDF file not supported\n");
+ IFace.status = STA_FAIL | STA_INVALID;
return;
}
- // Contruct fully qualified file name
- char fqn[MAX_PATH]={};
- strncpy(fqn,SDCard,MAX_PATH);
- AppendPathChar(fqn,'/');
- strncat(fqn,CurDir,MAX_PATH);
- AppendPathChar(fqn,'/');
- strncat(fqn,path,MAX_PATH);
+ namespace fs = std::filesystem;
+ fs::path fqn = fs::path(gSDRoot) / gCurDir / path;
- // Trying to mount a directory. Find .DSK files in the ending
- // with a digit and mount the first one.
- if (IsDirectory(fqn)) {
- DLOG_C("OpenNew %s is a directory\n",fqn);
- IF.status = STA_FAIL | STA_INVALID;
+ if (fs::is_directory(fqn)) {
+ DLOG_C("SDCOpenNew %s is a directory\n",path);
+ IFace.status = STA_FAIL | STA_INVALID;
return;
}
+ UnloadDisk(drive);
- // Try to create file
- CloseDrive(drive);
- strncpy(SDC_disk[drive].fullpath,fqn,MAX_PATH);
+ strncpy(gCocoDisk[drive].fullpath,fqn.string().c_str(),MAX_PATH);
// Open file for write
- SDC_disk[drive].hFile = CreateFile(
- SDC_disk[drive].fullpath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,
+ gCocoDisk[drive].hFile = CreateFile(
+ gCocoDisk[drive].fullpath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,
nullptr,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,nullptr);
- if (SDC_disk[drive].hFile == INVALID_HANDLE_VALUE) {
- DLOG_C("OpenNew fail %d file %s\n",drive,SDC_disk[drive].fullpath);
- DLOG_C("... %s\n",LastErrorTxt());
- IF.status = STA_FAIL | STA_WIN_ERROR;
+ if (gCocoDisk[drive].hFile == INVALID_HANDLE_VALUE) {
+ DLOG_C("SDCOpenNew fail %d file %s\n",drive,gCocoDisk[drive].fullpath);
+ DLOG_C("... %s\n",util::LastErrorTxt());
+ IFace.status = STA_FAIL | STA_WIN_ERROR;
return;
}
// Sectorsize and sectors per track
- SDC_disk[drive].sectorsize = 256;
- SDC_disk[drive].tracksectors = 18;
+ gCocoDisk[drive].sectorsize = 256;
+ gCocoDisk[drive].tracksectors = 18;
- IF.status = STA_FAIL;
+ IFace.status = STA_FAIL;
if (raw) {
// New raw file is empty - can be any format
- IF.status = STA_NORMAL;
- SDC_disk[drive].doublesided = 0;
- SDC_disk[drive].headersize = 0;
- SDC_disk[drive].size = 0;
+ IFace.status = STA_NORMAL;
+ gCocoDisk[drive].doublesided = 0;
+ gCocoDisk[drive].headersize = 0;
+ gCocoDisk[drive].size = 0;
} else {
// TODO: handle SDF
// Create headerless 35 track JVC file
- SDC_disk[drive].doublesided = 0;
- SDC_disk[drive].headersize = 0;
- SDC_disk[drive].size = 35
- * SDC_disk[drive].sectorsize
- * SDC_disk[drive].tracksectors
- + SDC_disk[drive].headersize;
+ gCocoDisk[drive].doublesided = 0;
+ gCocoDisk[drive].headersize = 0;
+ gCocoDisk[drive].size = 35
+ * gCocoDisk[drive].sectorsize
+ * gCocoDisk[drive].tracksectors
+ + gCocoDisk[drive].headersize;
// Extend file to size
LARGE_INTEGER l_siz;
- l_siz.QuadPart = SDC_disk[drive].size;
- if (SetFilePointerEx(SDC_disk[drive].hFile,l_siz,nullptr,FILE_BEGIN)) {
- if (SetEndOfFile(SDC_disk[drive].hFile)) {
- IF.status = STA_NORMAL;
+ l_siz.QuadPart = gCocoDisk[drive].size;
+ if (SetFilePointerEx(gCocoDisk[drive].hFile,l_siz,nullptr,FILE_BEGIN)) {
+ if (SetEndOfFile(gCocoDisk[drive].hFile)) {
+ IFace.status = STA_NORMAL;
} else {
- IF.status = STA_FAIL | STA_WIN_ERROR;
+ IFace.status = STA_FAIL | STA_WIN_ERROR;
}
}
}
@@ -2084,75 +2031,85 @@ void OpenNew( int drive, const char * path, int raw)
//----------------------------------------------------------------------
// Open disk image found. Raw flag skips file type checks
//----------------------------------------------------------------------
-void OpenFound (int drive,int raw)
+void SDCOpenFound (int drive,int raw)
{
drive &= 1;
int writeprotect = 0;
- DLOG_C("OpenFound drive %d %s hfile %d\n",
- drive, dFound.cFileName, SDC_disk[drive].hFile);
-
- CloseDrive(drive);
- *SDC_disk[drive].name = '\0';
-
- // Open a directory of containing DSK files
- if (dFound.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
- char path[MAX_PATH];
- strncpy(path,dFound.cFileName,MAX_PATH);
- AppendPathChar(path,'/');
- strncat(path,"*.DSK",MAX_PATH);
- // Open disk in directory if .DSK is found
- if (InitiateDir(path)) {
- DLOG_C("OpenFound %s in directory\n",dFound.cFileName);
- OpenFound(drive,0);
+ if (gFileList.cursor >= gFileList.files.size()) {
+ DLOG_C("SDCOpenFound no file %d %d %d\n",
+ drive, gFileList.cursor, gFileList.files.size());
+ return;
+ }
+
+ UnloadDisk(drive);
+
+ std::string name = gFileList.files[gFileList.cursor].name.c_str();
+ DLOG_C("SDCOpenFound drive %d %d %s\n",
+ drive, gFileList.cursor, name.c_str());
+
+ if (gFileList.files[gFileList.cursor].isDir)
+ {
+ std::string pattern = name + "/*.DSK";
+ DLOG_C("SDCOpenFound %s directory initiate\n",pattern.c_str());
+ if (SDCInitiateDir(pattern.c_str())) {
+ SDCOpenFound(drive, 0);
if (drive == 0) BuildCartridgeMenu();
}
return;
}
- // Fully qualify name of found file and try to open it
- char fqn[MAX_PATH]={};
- strncpy(fqn,SeaDir,MAX_PATH);
- strncat(fqn,dFound.cFileName,MAX_PATH);
+ std::string file = gFileList.directory + "/" + name;
+ DLOG_C("SDCOpenFound %s\n", file.c_str());
- // Open file for read
- SDC_disk[drive].hFile = CreateFile(
- fqn, GENERIC_READ, FILE_SHARE_READ,
- nullptr,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,nullptr);
+ gCocoDisk[drive].hFile = CreateFileA(
+ file.c_str(),
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ nullptr
+ );
- if (SDC_disk[drive].hFile == INVALID_HANDLE_VALUE) {
- DLOG_C("OpenFound fail %d file %s\n",drive,fqn);
- DLOG_C("... %s\n",LastErrorTxt());
+ if (gCocoDisk[drive].hFile == INVALID_HANDLE_VALUE) {
+ DLOG_C("SDCOpenFound fail %d file %s\n",drive,file.c_str());
+ DLOG_C("... %s\n",util::LastErrorTxt());
int ecode = GetLastError();
if (ecode == ERROR_SHARING_VIOLATION) {
- IF.status = STA_FAIL | STA_INUSE;
+ IFace.status = STA_FAIL | STA_INUSE;
} else {
- IF.status = STA_FAIL | STA_WIN_ERROR;
+ IFace.status = STA_FAIL | STA_WIN_ERROR;
}
return;
}
- strncpy(SDC_disk[drive].fullpath,fqn,MAX_PATH);
- strncpy(SDC_disk[drive].name,dFound.cFileName,MAX_PATH);
+ // If more than one file in list enable nextload function
+ if (gFileList.files.size() > 1) gFileList.nextload_flag = true;
+
+ strncpy(gCocoDisk[drive].fullpath,file.c_str(),MAX_PATH);
+ strncpy(gCocoDisk[drive].name,name.c_str(),MAX_PATH);
// Default sectorsize and sectors per track
- SDC_disk[drive].sectorsize = 256;
- SDC_disk[drive].tracksectors = 18;
+ gCocoDisk[drive].sectorsize = 256;
+ gCocoDisk[drive].tracksectors = 18;
- // Grab filesize from found record
- SDC_disk[drive].size = dFound.nFileSizeLow;
+ // Grab filesize
+ gCocoDisk[drive].size = gFileList.files[gFileList.cursor].size;
+ // Determine file type (RAW,DSK,JVC,VDK,SDF)
if (raw) {
writeprotect = 0;
- SDC_disk[drive].headersize = 0;
- SDC_disk[drive].doublesided = 0;
+ gCocoDisk[drive].type = DTYPE_RAW;
+ gCocoDisk[drive].headersize = 0;
+ gCocoDisk[drive].doublesided = 0;
} else {
// Read a few bytes of the file to determine it's type
unsigned char header[16];
- if (ReadFile(SDC_disk[drive].hFile,header,12,nullptr,nullptr) == 0) {
- DLOG_C("OpenFound header read error\n");
- IF.status = STA_FAIL | STA_INVALID;
+ if (ReadFile(gCocoDisk[drive].hFile,header,12,nullptr,nullptr) == 0) {
+ DLOG_C("SDCOpenFound header read error\n");
+ IFace.status = STA_FAIL | STA_INVALID;
return;
}
@@ -2160,87 +2117,91 @@ void OpenFound (int drive,int raw)
// track record is 6250 bytes. Assume at least 35 tracks so
// minimum size of a SDF file is 219262 bytes. The first four
// bytes of the header contains "SDF1"
- if ((SDC_disk[drive].size >= 219262) &&
+ if ((gCocoDisk[drive].size >= 219262) && // is this reasonable?
(strncmp("SDF1",(const char *) header,4) == 0)) {
- DLOG_C("OpenFound SDF file unsupported\n");
- IF.status = STA_FAIL | STA_INVALID;
+ gCocoDisk[drive].type = DTYPE_SDF;
+ DLOG_C("SDCOpenFound SDF file unsupported\n");
+ IFace.status = STA_FAIL | STA_INVALID;
return;
}
unsigned int numsec;
- SDC_disk[drive].headersize = SDC_disk[drive].size & 255;
- DLOG_C("OpenFound headersize %d\n",SDC_disk[drive].headersize);
- switch (SDC_disk[drive].headersize) {
+ gCocoDisk[drive].headersize = gCocoDisk[drive].size & 255;
+ DLOG_C("SDCOpenFound headersize %d\n",gCocoDisk[drive].headersize);
+ switch (gCocoDisk[drive].headersize) {
// JVC optional header bytes
case 4: // First Sector = header[3] 1 assumed
case 3: // Sector Size code = header[2] 256 assumed {128,256,512,1024}
case 2: // Number of sides = header[1] 1 or 2
case 1: // Sectors per trk = header[0] 18 assumed
- SDC_disk[drive].doublesided = (header[1] == 2);
+ gCocoDisk[drive].doublesided = (header[1] == 2);
+ gCocoDisk[drive].type = DTYPE_JVC;
break;
// No apparant header
// JVC or OS9 disk if no header, side count per file size
case 0:
- numsec = SDC_disk[drive].size >> 8;
- DLOG_C("OpenFound JVC/OS9 sectors %d\n",numsec);
- SDC_disk[drive].doublesided = ((numsec > 720) && (numsec <= 2880));
+ numsec = gCocoDisk[drive].size >> 8;
+ DLOG_C("SDCOpenFound JVC/OS9 sectors %d\n",numsec);
+ gCocoDisk[drive].doublesided = ((numsec > 720) && (numsec <= 2880));
+ gCocoDisk[drive].type = DTYPE_JVC;
break;
// VDK
case 12:
- SDC_disk[drive].doublesided = (header[8] == 2);
+ gCocoDisk[drive].doublesided = (header[8] == 2);
writeprotect = header[9] & 1;
+ gCocoDisk[drive].type = DTYPE_VDK;
break;
// Unknown or unsupported
default: // More than 4 byte header is not supported
- DLOG_C("OpenFound unsuported image type %d %d\n",
- drive, SDC_disk[drive].headersize);
- IF.status = STA_FAIL | STA_INVALID;
+ DLOG_C("SDCOpenFound unsuported image type %d %d\n",
+ drive, gCocoDisk[drive].headersize);
+ IFace.status = STA_FAIL | STA_INVALID;
return;
break;
}
}
- // Fill in image info.
- LoadFoundFile(&SDC_disk[drive].filerec);
+ gCocoDisk[drive].hf = gFileList.files[gFileList.cursor];
+ gCocoDisk[drive].filerec = FileRecord(gCocoDisk[drive].hf);
// Set readonly attrib per find status or file header
- if ((SDC_disk[drive].filerec.attrib & ATTR_RDONLY) != 0) {
+ if ((gCocoDisk[drive].filerec.FR_attrib & ATTR_RDONLY) != 0) {
writeprotect = 1;
} else if (writeprotect) {
- SDC_disk[drive].filerec.attrib |= ATTR_RDONLY;
+ gCocoDisk[drive].filerec.FR_attrib |= ATTR_RDONLY;
}
// If file is writeable reopen read/write
if (!writeprotect) {
- CloseHandle(SDC_disk[drive].hFile);
- SDC_disk[drive].hFile = CreateFile(
- SDC_disk[drive].fullpath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,
+ CloseHandle(gCocoDisk[drive].hFile);
+ gCocoDisk[drive].hFile = CreateFile(
+ gCocoDisk[drive].fullpath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,
nullptr,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,nullptr);
- if (SDC_disk[drive].hFile == INVALID_HANDLE_VALUE) {
- DLOG_C("OpenFound reopen fail %d\n",drive);
- DLOG_C("... %s\n",LastErrorTxt());
- IF.status = STA_FAIL | STA_WIN_ERROR;
+ if (gCocoDisk[drive].hFile == INVALID_HANDLE_VALUE) {
+ DLOG_C("SDCOpenFound reopen fail %d\n",drive);
+ DLOG_C("... %s\n",util::LastErrorTxt());
+ IFace.status = STA_FAIL | STA_WIN_ERROR;
return;
}
}
if (drive == 0) BuildCartridgeMenu();
- IF.status = STA_NORMAL;
+ IFace.status = STA_NORMAL;
return;
}
//----------------------------------------------------------------------
-// Convert file name from SDC format and prepend current dir.
+// Convert file name from FAT format and prepend current dir.
//----------------------------------------------------------------------
-void GetFullPath(char * path, const char * file) {
- char tmp[MAX_PATH];
- strncpy(path,SDCard,MAX_PATH);
- AppendPathChar(path,'/');
- strncat(path,CurDir,MAX_PATH);
- AppendPathChar(path,'/');
- FixSDCPath(tmp,file);
- strncat(path,tmp,MAX_PATH);
+std::string SDCGetFullPath(const std::string& file)
+{
+ std::string out = gSDRoot;
+ if (gCurDir.size() > 0) out += '/' + gCurDir;
+ out += '/' + FixFATPath(file);
+
+ DLOG_C("SDCGetFullPath in %s out %s\n",file.c_str(),out.c_str());
+ return (out);
}
//----------------------------------------------------------------------
@@ -2248,21 +2209,22 @@ void GetFullPath(char * path, const char * file) {
// names contains two consecutive null terminated name strings
// first is file or directory to rename, second is target name
//----------------------------------------------------------------------
-void RenameFile(const char *names)
+void SDCRenameFile(const char *names)
{
- char from[MAX_PATH];
- char target[MAX_PATH];
-
- GetFullPath(from,names);
- GetFullPath(target,1+strchr(names,'\0'));
-
- DLOG_C("UpdateSD rename %s %s\n",from,target);
-
- if (std::rename(from,target)) {
- DLOG_C("RenameFile %s\n", strerror(errno));
- IF.status = STA_FAIL | STA_WIN_ERROR;
+ const char* p = names;
+ std::string sfrom = p;
+ p += sfrom.size() + 1;
+ std::string starget = p;
+
+ DLOG_C("SDCRenameFile %s to %s\n",sfrom.c_str(),starget.c_str());
+ std::string from = SDCGetFullPath(sfrom);
+ std::string target = SDCGetFullPath(starget);
+
+ if (std::rename(from.c_str(),target.c_str())) {
+ DLOG_C("SDCRenameFile %s\n", strerror(errno));
+ IFace.status = STA_FAIL | STA_WIN_ERROR;
} else {
- IF.status = STA_NORMAL;
+ IFace.status = STA_NORMAL;
}
return;
}
@@ -2270,69 +2232,76 @@ void RenameFile(const char *names)
//----------------------------------------------------------------------
// Delete disk or directory
//----------------------------------------------------------------------
-void KillFile(const char *file)
+void SDCKillFile(const char* file)
{
- char path[MAX_PATH];
- GetFullPath(path,file);
- DLOG_C("KillFile delete %s\n",path);
+ namespace fs = std::filesystem;
- if (IsDirectory(path)) {
- if (PathIsDirectoryEmpty(path)) {
- if (RemoveDirectory(path)) {
- IF.status = STA_NORMAL;
- } else {
- DLOG_C("Deletefile %s\n", strerror(errno));
- IF.status = STA_FAIL | STA_NOTFOUND;
- }
+ fs::path p = SDCGetFullPath(std::string(file));
+ std::error_code ec;
+
+ DLOG_C("SDCKillFile %s\n",file);
+
+ // Does it exist?
+ if (!fs::exists(p, ec)) {
+ DLOG_C("SDCKillFile does not exist\n");
+ IFace.status = STA_FAIL | STA_NOTFOUND;
+ return;
+ }
+ // Regular file
+ if (fs::is_regular_file(p, ec)) {
+ if (fs::remove(p, ec)) {
+ IFace.status = STA_NORMAL;
} else {
- IF.status = STA_FAIL | STA_NOTEMPTY;
+ DLOG_C("SDCKillFile error: %s\n", ec.message().c_str());
+ IFace.status = STA_FAIL | STA_WPROTECT;
}
- } else {
- if (DeleteFile(path)) {
- IF.status = STA_NORMAL;
+ return;
+ }
+ // Directory
+ if (fs::is_directory(p, ec)) {
+ if (fs::is_empty(p, ec)) {
+ if (fs::remove(p, ec)) {
+ IFace.status = STA_NORMAL;
+ } else {
+ IFace.status = STA_FAIL | STA_WPROTECT;
+ DLOG_C("SDCKillFile dir error: %s\n", ec.message().c_str());
+ }
} else {
- DLOG_C("Deletefile %s\n", strerror(errno));
- IF.status = STA_FAIL | STA_NOTFOUND;
+ DLOG_C("SDCKillFile directory not empty\n");
+ IFace.status = STA_FAIL | STA_NOTEMPTY;
}
+ return;
}
- return;
+ // Not a file or directory (symlink, device, etc.)
+ IFace.status = STA_FAIL;
}
//----------------------------------------------------------------------
// Create directory
//----------------------------------------------------------------------
-void MakeDirectory(const char *name)
+
+void SDCMakeDirectory(const char* name)
{
- char path[MAX_PATH];
- GetFullPath(path,name);
- DLOG_C("MakeDirectory %s\n",path);
+ namespace fs = std::filesystem;
+
+ std::string s = SDCGetFullPath(std::string(name));
+ fs::path p(s);
+
+ DLOG_C("SDCMakeDirectory %s\n", s.c_str());
+
+ std::error_code ec;
- // Make sure directory is not in use
- struct _stat file_stat;
- int result = _stat(path,&file_stat);
- if (result == 0) {
- IF.status = STA_FAIL | STA_INVALID;
+ if (fs::exists(p, ec)) {
+ DLOG_C("SDCMakeDirectory already exists\n");
+ IFace.status = STA_FAIL | STA_INVALID;
return;
}
- if (CreateDirectory(path,nullptr)) {
- IF.status = STA_NORMAL;
+ if (fs::create_directory(p, ec)) {
+ IFace.status = STA_NORMAL;
} else {
- DLOG_C("MakeDirectory %s\n", strerror(errno));
- IF.status = STA_FAIL | STA_WIN_ERROR;
- }
- return;
-}
-
-//----------------------------------------------------------------------
-// Close virtual disk
-//----------------------------------------------------------------------
-void CloseDrive (int drive)
-{
- drive &= 1;
- if (SDC_disk[drive].hFile && SDC_disk[drive].hFile != INVALID_HANDLE_VALUE) {
- CloseHandle(SDC_disk[drive].hFile);
- SDC_disk[drive].hFile = INVALID_HANDLE_VALUE;
+ DLOG_C("SDCMakeDirectory error: %s\n", ec.message().c_str());
+ IFace.status = STA_FAIL | STA_WIN_ERROR;
}
}
@@ -2340,221 +2309,333 @@ void CloseDrive (int drive)
// Set the Current Directory relative to previous current or to SDRoot
// This is complicated by the many ways a user can change the directory
//----------------------------------------------------------------------
-void SetCurDir(const char * branch)
+
+void SDCSetCurDir(const char* branch)
{
- DLOG_C("SetCurdir '%s'\n",branch);
+ namespace fs = std::filesystem;
+
+ DLOG_C("SetCurdir '%s'\n", branch);
- // If branch is "." or "" do nothing
- if ((*branch == '\0') || (strcmp(branch,".") == 0)) {
+ fs::path cur = gCurDir; // current relative directory
+ fs::path root = gSDRoot; // SD card root
+
+ // "." or "" no change
+ if (*branch == '\0' || strcmp(branch, ".") == 0) {
DLOG_C("SetCurdir no change\n");
- IF.status = STA_NORMAL;
+ IFace.status = STA_NORMAL;
return;
}
- // If branch is "/" set CurDir to root
- if (strcmp(branch,"/") == 0) {
+ // "/" go to root
+ if (strcmp(branch, "/") == 0) {
+ gCurDir = "";
DLOG_C("SetCurdir to root\n");
- *CurDir = '\0';
- IF.status = STA_NORMAL;
+ IFace.status = STA_NORMAL;
return;
}
- // If branch is ".." go back a directory
- if (strcmp(branch,"..") == 0) {
- char *p = strrchr(CurDir,'/');
- if (p != nullptr) {
- *p = '\0';
- } else {
- *CurDir = '\0';
- }
- DLOG_C("SetCurdir back %s\n",CurDir);
- IF.status = STA_NORMAL;
+ // ".." parent directory
+ if (strcmp(branch, "..") == 0) {
+ cur = cur.parent_path();
+ gCurDir = cur.string();
+ util::FixDirSlashes(gCurDir);
+ DLOG_C("SetCurdir back %s\n", gCurDir.c_str());
+ IFace.status = STA_NORMAL;
return;
}
- // Disallow branch start with "//"
- if (strncmp(branch,"//",2) == 0) {
+ // Reject "//"
+ if (strncmp(branch, "//", 2) == 0) {
DLOG_C("SetCurdir // invalid\n");
- IF.status = STA_FAIL | STA_INVALID;
+ IFace.status = STA_FAIL | STA_INVALID;
return;
}
- // Test for CurDir relative branch
- int relative = (*branch != '/');
+ // Build the candidate directory
+ bool relative = (branch[0] != '/');
- // Test the full directory path
- char test[MAX_PATH];
- strncpy(test,SDCard,MAX_PATH);
- AppendPathChar(test,'/');
+ fs::path candidate;
if (relative) {
- strncat(test,CurDir,MAX_PATH);
- AppendPathChar(test,'/');
- strncat(test,branch,MAX_PATH);
+ candidate = root / cur / branch;
} else {
- strncat(test,branch+1,MAX_PATH);
+ candidate = root / fs::path(branch).relative_path();
}
- if (!IsDirectory(test)) {
- DLOG_C("SetCurdir not a directory %s\n",test);
- IF.status = STA_FAIL | STA_NOTFOUND;
+
+ // Validate directory
+ if (!fs::is_directory(candidate)) {
+ DLOG_C("SetCurdir not a directory %s\n", candidate.string().c_str());
+ IFace.status = STA_FAIL | STA_NOTFOUND;
return;
}
- // Set current directory
+ // Update CurDir
+ fs::path newCur;
if (relative) {
- if (*CurDir != '\0') AppendPathChar(CurDir,'/');
- strncat(CurDir,branch,MAX_PATH);
+ newCur = cur / branch;
} else {
- strncpy(CurDir,branch+1,MAX_PATH);
+ newCur = fs::path(branch).relative_path();
}
- // Trim trailing '/'
- int l = strlen(CurDir);
- while (l > 0 && CurDir[l-1] == '/') l--;
- CurDir[l] = '\0';
+ // String based host files
+ gCurDir = newCur.string();
+ util::FixDirSlashes(gCurDir);
- DLOG_C("SetCurdir set to '%s'\n",CurDir);
-
- IF.status = STA_NORMAL;
- return;
+ DLOG_C("SetCurdir set to '%s'\n", gCurDir.c_str());
+ IFace.status = STA_NORMAL;
}
//----------------------------------------------------------------------
-// Start File search. Searches start from the root of the SDCard.
+// SDCInitiateDir command.
//----------------------------------------------------------------------
-bool SearchFile(const char * pattern)
-{
- // Path always starts with SDCard
- char path[MAX_PATH];
- strncpy(path,SDCard,MAX_PATH);
- AppendPathChar(path,'/');
-
- if (*pattern == '/') {
- strncat(path,&pattern[1],MAX_PATH);
- } else {
- strncat(path,CurDir,MAX_PATH);
- AppendPathChar(path,'/');
- strncat(path,pattern,MAX_PATH);
- }
-
- DLOG_C("SearchFile %s\n",path);
-
- // Close previous search
- if (hFind != INVALID_HANDLE_VALUE) {
- FindClose(hFind);
- hFind = INVALID_HANDLE_VALUE;
- }
-
- // Search
- hFind = FindFirstFile(path, &dFound);
-
- if (hFind == INVALID_HANDLE_VALUE) {
- *SeaDir = '\0';
- return false;
- } else {
- // Save directory portion for prepending to results
- char * pnam = strrchr(path,'/');
- if (pnam != nullptr) pnam[1] = '\0';
- strncpy(SeaDir,path,MAX_PATH);
- return true;
- }
-
- return (hFind != INVALID_HANDLE_VALUE);
-}
-
-//----------------------------------------------------------------------
-// InitiateDir command.
-//----------------------------------------------------------------------
-bool InitiateDir(const char * path)
+bool SDCInitiateDir(const char * path)
{
bool rc;
- // Append "*.*" if last char in path was '/';
- int l = strlen(path);
+ DLOG_C("SDCInitiateDir '%s'\n",path);
+
+ // Append "*.*" if last char in path is '/';
+ size_t l = strlen(path);
if (path[l-1] == '/') {
- char tmp[MAX_PATH];
- strncpy(tmp,path,MAX_PATH);
- strncat(tmp,"*.*",MAX_PATH);
- rc = SearchFile(tmp);
+ rc = SearchFile(std::string(path) + "*.*");
} else {
rc = SearchFile(path);
}
+
if (rc) {
- IF.status = STA_NORMAL;
+ IFace.status = STA_NORMAL;
return true;
} else {
- IF.status = STA_FAIL | STA_INVALID;
+ IFace.status = STA_FAIL | STA_INVALID;
return false;
}
}
-//----------------------------------------------------------------------
-// Load directory page containing up to 16 file records that match
-// the pattern used in SearchFile. Can be called multiple times until
-// there are no more matches.
-//----------------------------------------------------------------------
-void LoadDirPage()
+//=========================================================================
+// Host file handling
+//=========================================================================
+
+// Construct FileRecord from a HostFile object.
+FileRecord::FileRecord(const HostFile& hf) noexcept
+{
+ memset(this, 0, sizeof(*this));
+ // name and type
+ sfn_from_lfn(FR_name, FR_type, hf.name);
+ // Attributes
+ FR_attrib = 0;
+ if (hf.isDir) FR_attrib |= 0x10;
+ if (hf.isRdOnly) FR_attrib |= 0x01;
+ // Size -> 4 reversed endian bytes
+ DWORD s = hf.size;
+ FR_hihi_size = (s >> 24) & 0xFF;
+ FR_lohi_size = (s >> 16) & 0xFF;
+ FR_hilo_size = (s >> 8) & 0xFF;
+ FR_lolo_size = (s >> 0) & 0xFF;
+}
+
+//-------------------------------------------------------------------
+// Get list of files matching pattern starting from CurDir or SDRoot
+//-------------------------------------------------------------------
+void GetFileList(const std::string& pattern)
{
- memset(DirPage,0,sizeof(DirPage));
+ DLOG_C("GetFileList %s\n",pattern.c_str());
+
+ // Init search results
+ gFileList = {};
+
+ // None if pattern is empty
+ if (pattern.empty()) return;
+
+ // Set up search path.
+ bool not_at_root = !gCurDir.empty();
+ bool rel_pattern = pattern.front() != '/';
+ std::string search_path = gSDRoot;
+ if (rel_pattern) {
+ search_path += "/";
+ if (not_at_root)
+ search_path += gCurDir + "/";
+ }
+ search_path += pattern;
+
+ // A directory lookup if pattern name is "*" or "*.*";
+ std::string fnpat = util::GetFileNamePart(pattern);
+ bool dir_lookup = (fnpat == "*" || fnpat == "*.*");
+
+ std::string search_dir = util::GetDirectoryPart(search_path);
+
+ // Include ".." if dir_lookup and search_dir is not SDRoot
+ if (dir_lookup && util::GetDirectoryPart(search_path) != gSDRoot) {
+ gFileList.append({"..", 0, true, false});
+ }
+
+ // Add matching files to the list
+ WIN32_FIND_DATAA fd;
+ HANDLE hFind = FindFirstFileA(search_path.c_str(), &fd);
if (hFind == INVALID_HANDLE_VALUE) {
- DLOG_C("LoadDirPage Search fail\n");
+ gFileList = {};
return;
}
+ do {
+ if (strcmp(fd.cFileName,".") == 0) continue; // exclude single dot file
+ if (PathIsLFNFileSpecA(fd.cFileName)) continue; // exclude ugly or long filenames
+ if (fd.nFileSizeHigh != 0) continue; // exclude files > 4 GB
+ gFileList.append({fd});
+ } while (FindNextFileA(hFind, &fd));
+ FindClose(hFind);
- int cnt = 0;
- while (cnt < 16) {
- if (LoadFoundFile(&DirPage[cnt])) cnt++;
- if (FindNextFile(hFind,&dFound) == 0) {
- FindClose(hFind);
- hFind = INVALID_HANDLE_VALUE;
- break;
+ // Save directory
+ gFileList.directory = search_dir;
+
+ DLOG_C("GetFileList found %d\n",gFileList.files.size());
+}
+
+//-------------------------------------------------------------------
+// SortFileList() may be needed when mounting a directory
+//-------------------------------------------------------------------
+void SortFileList()
+{
+ std::sort(
+ gFileList.files.begin(),
+ gFileList.files.end(),
+ [] (const HostFile& a, const HostFile& b) {
+ return a.name < b.name;
}
- }
+ );
+ gFileList.cursor = 0;
}
//----------------------------------------------------------------------
-// Append char to path if not there
+// Host File search.
//----------------------------------------------------------------------
-void AppendPathChar(char * path, char c)
+bool SearchFile(const std::string& pat)
{
- int l = strlen(path);
- if ((l > 0) && (path[l-1] == c)) return;
- if (l > (MAX_PATH-2)) return;
- path[l] = c;
- path[l+1] = '\0';
+ // Fill gFileList with files found.
+ GetFileList(pat);
+ // Update menu to show next disk status
+ BuildCartridgeMenu();
+ // Return true if something found
+ return (gFileList.files.size() > 0);
}
//----------------------------------------------------------------------
-// Determine if path is a direcory
+// A file path may use 11 char FAT format which does not use a separater
+// between name and extension. User is free to use standard dot format.
+// "FOODIR/FOO.DSK" = FOODIR/FOO.DSK
+// "FOODIR/FOO DSK" = FOODIR/FOO.DSK
+// "FOODIR/ALONGFOODSK" = FOODIR/ALONGFOO.DSK
//----------------------------------------------------------------------
-bool IsDirectory(const char * path)
+std::string FixFATPath(const std::string& sdcpath)
{
- struct _stat file_stat;
- int result = _stat(path,&file_stat);
- if (result != 0) return false;
- return ((file_stat.st_mode & _S_IFDIR) != 0);
+ std::filesystem::path p(sdcpath);
+
+ auto chop = [](std::string s) {
+ size_t space = s.find(' ');
+ if (space != std::string::npos) s.erase(space);
+ return s;
+ };
+
+ std::string fname = p.filename().string();
+ if (fname.length() == 11 && fname.find('.') == std::string::npos) {
+ auto nam = chop(fname.substr(0,8));
+ auto ext = chop(fname.substr(8,3));
+ fname = ext.empty() ? nam : nam + "." + ext;
+ }
+
+ std::filesystem::path out = p.parent_path() / fname;
+
+ DLOG_C("FixFATPath in %s out %s\n",sdcpath.c_str(),out.generic_string().c_str());
+ return out.generic_string();
}
-//----------------------------------------------------------------------
-// Get most recent windows error text
-//----------------------------------------------------------------------
-char * LastErrorTxt() {
- static char msg[200];
- DWORD error_code = GetLastError();
- FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS,
- nullptr, error_code,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- msg, 200, nullptr );
- return msg;
+
+//-------------------------------------------------------------------
+// Convert string containing possible FAT name and extension to an
+// LFN string. Returns empty string if invalid LFN
+//-------------------------------------------------------------------
+std::string NormalizeInputToLFN(const std::string& s)
+{
+ if (s.empty()) return {};
+ if (s.size() > 11) return {};
+ if (s == "..") return "..";
+
+ // LFN candidate
+ if (s.find('.') != std::string::npos) {
+ if (!PathIsLFNFileSpecA(s.c_str()))
+ return {}; // invalid
+ return s;
+ }
+
+ // SFN candidate
+ char name[8];
+ char ext[3];
+ sfn_from_lfn(name,ext,s);
+ return lfn_from_sfn(name,ext);
}
-//------------------------------------------------------------
-// Convert path slashes
-//------------------------------------------------------------
-void ConvertSlashes(char * path)
+//-------------------------------------------------------------------
+// Convert LFN to FAT filename parts, 8 char name, 3 char ext
+// A LNF file is less than 4GB and has a short (8.3) name.
+//-------------------------------------------------------------------
+void sfn_from_lfn(char (&name)[8], char (&ext)[3], const std::string& lfn)
{
- for(size_t i=0; i < strlen(path); i++) {
- if (path[i] == '\\') path[i] = '/';
+ // Special case: parent directory
+ if (lfn == "..") {
+ copy_to_fixed_char(name, "..");
+ std::fill(ext, ext + 3, ' ');
+ return;
+ }
+
+ size_t dot = lfn.find('.');
+ std::string base, extension;
+
+ if (dot == std::string::npos) {
+ base = lfn;
+ } else {
+ base = lfn.substr(0, dot);
+ extension = lfn.substr(dot + 1);
}
+
+ copy_to_fixed_char(name, base);
+ copy_to_fixed_char(ext, extension);
+}
+
+//-------------------------------------------------------------------
+// Convert FAT filename parts to LFN. Returns empty string if invalid
+//-------------------------------------------------------------------
+std::string lfn_from_sfn(const char (&name)[8], const char (&ext)[3])
+{
+ std::string base(name, 8);
+ std::string extension(ext, 3);
+
+ base = util::trim_right_spaces(base);
+ extension = util::trim_right_spaces(extension);
+
+ if (base == ".." && extension.empty())
+ return "..";
+
+ std::string lfn = base;
+
+ if (!extension.empty())
+ lfn += "." + extension;
+
+ if (lfn.empty())
+ return {};
+
+ if (!PathIsLFNFileSpecA(lfn.c_str()))
+ return {};
+
+ return lfn;
+}
+
+//-------------------------------------------------------------------
+// Copy string to fixed size char array (non terminated)
+//-------------------------------------------------------------------
+template
+void copy_to_fixed_char(char (&dest)[N], const std::string& src)
+{
+ size_t i = 0;
+ for (; i < src.size() && i < N; ++i)
+ dest[i] = src[i];
+ for (; i < N; ++i)
+ dest[i] = ' ';
}
diff --git a/sdc/sdc.h b/sdc/sdc.h
index 2316f406..56159534 100644
--- a/sdc/sdc.h
+++ b/sdc/sdc.h
@@ -19,7 +19,11 @@
// along with VCC (Virtual Color Computer). If not, see
// .
//
+//======================================================================
+#pragma once
+
+#include
#include
#include
@@ -58,6 +62,109 @@
#define ATTR_SDF 0x04
#define ATTR_DIR 0x10
-// Self imposed limit on maximum dsk file size.
+// Disk Types
+enum DiskType {
+ DTYPE_RAW = 0,
+ DTYPE_DSK,
+ DTYPE_JVC,
+ DTYPE_VDK,
+ DTYPE_SDF
+};
+
+// Limit maximum dsk file size.
#define MAX_DSK_SECTORS 2097152
+// HostFile contains a file name, it's size, and directory and readonly flags
+struct HostFile
+{
+ std::string name; // LFN 8.3 name
+ DWORD size; // < 4GB
+ bool isDir; // is a directory
+ bool isRdOnly; // is read only
+ HostFile() {}
+ // Construct a HostFile from an argument list
+ HostFile(const char* cName, DWORD nSize, bool isDir, bool isRdOnly) :
+ name(cName), size(nSize), isDir(isDir), isRdOnly(isRdOnly) {}
+ // Construct a HostFile from a WIN32_FIND_DATAA record.
+ HostFile(WIN32_FIND_DATAA fd) :
+ name(fd.cFileName), size(fd.nFileSizeLow),
+ isDir((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0),
+ isRdOnly((fd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0) {}
+};
+
+// FileList contains a list of HostFiles
+struct FileList
+{
+ std::vector files; // files
+ std::string directory; // directory files are in
+ size_t cursor = 0; // current file curspr
+ bool nextload_flag = false; // enable next disk loading
+ // append a host file to the list
+ void append(const HostFile& hf) { files.push_back(hf); }
+};
+
+// FileRecord is 16 packed bytes file name, type, attrib, and size that can
+// be passed directly via the SDC interface.
+#pragma pack(push, 1)
+struct FileRecord
+{
+ char FR_name[8];
+ char FR_type[3];
+ char FR_attrib;
+ char FR_hihi_size;
+ char FR_lohi_size;
+ char FR_hilo_size;
+ char FR_lolo_size;
+ FileRecord() noexcept {};
+ FileRecord(const HostFile& hf) noexcept;
+};
+#pragma pack(pop)
+
+// CocoDisk contains info about mounted disk files 0 and 1
+struct CocoDisk
+{
+ HANDLE hFile; // file handle
+ unsigned int size; // number of bytes total
+ unsigned int headersize; // number of bytes in header
+ DWORD sectorsize; // number of bytes per sector
+ DWORD tracksectors; // number of sectors per side
+ DiskType type; // Disk image type (RAW,DSK,JVC,VDK,SDF)
+ char doublesided; // false:1 side, true:2 sides
+ char name[MAX_PATH]; // name of file (8.3)
+ char fullpath[MAX_PATH]; // full file path
+ struct HostFile hf{"",0,false,false}; //name,size,isDir,isRdOnly
+ struct FileRecord filerec;
+ // Constructor
+ CocoDisk() noexcept
+ : hFile(INVALID_HANDLE_VALUE),
+ size(0),
+ headersize(0),
+ sectorsize(256),
+ tracksectors(18),
+ type(DiskType::DTYPE_RAW),
+ doublesided(1) {
+ name[0] = '\0';
+ fullpath[0] = '\0';
+ };
+};
+
+// SDC CoCo Interface
+struct Interface
+{
+ int sdclatch;
+ unsigned char cmdcode;
+ unsigned char status;
+ unsigned char reply1;
+ unsigned char reply2;
+ unsigned char reply3;
+ unsigned char param1;
+ unsigned char param2;
+ unsigned char param3;
+ unsigned char reply_mode; // 0=words, 1=bytes
+ unsigned char reply_status;
+ unsigned char half_sent;
+ int bufcnt;
+ char *bufptr;
+ char blkbuf[600];
+};
+
diff --git a/sdc/sdc.vcxproj b/sdc/sdc.vcxproj
index 04bf9198..063f2b88 100644
--- a/sdc/sdc.vcxproj
+++ b/sdc/sdc.vcxproj
@@ -113,4 +113,4 @@
-
\ No newline at end of file
+