From aab2d81671377c1df6eef13098e951a90dbeda60 Mon Sep 17 00:00:00 2001 From: icabod Date: Fri, 30 Jul 2021 22:03:26 +0100 Subject: [PATCH] First drop of 3eExplorer code --- .gitignore | 96 + 3eExplorer.clw | 334 ++ 3eExplorer.cpp | 263 ++ 3eExplorer.dsp | 385 ++ 3eExplorer.dsw | 29 + 3eExplorer.h | 67 + 3eExplorer.rc | 565 +++ 3eExplorer.txt | 90 + 3eExplorerDoc.cpp | 347 ++ 3eExplorerDoc.h | 96 + 3eExplorerView.cpp | 433 +++ 3eExplorerView.h | 88 + ChildFrm.cpp | 119 + ChildFrm.h | 59 + ContentView.cpp | 59 + ContentView.h | 53 + DetailView.cpp | 91 + DetailView.h | 63 + DiskAllocationView.cpp | 159 + DiskAllocationView.h | 65 + DocumentStatus.cpp | 107 + DocumentStatus.h | 52 + ExplorerFile.cpp | 137 + ExplorerFile.h | 71 + HdfHeader.cpp | 204 + HdfHeader.h | 73 + IDEDOSPartitionDefinition.cpp | 49 + IDEDOSPartitionDefinition.h | 69 + IDEDOS_Plus3DOSPartition.cpp | 201 + IDEDOS_Plus3DOSPartition.h | 89 + IDEDOS_SystemPartition.cpp | 111 + IDEDOS_SystemPartition.h | 43 + MainFrm.cpp | 117 + MainFrm.h | 59 + OptionsDialog.cpp | 45 + OptionsDialog.h | 47 + Plus3DOS_DirectoryEntry.cpp | 391 ++ Plus3DOS_DirectoryEntry.h | 105 + Plus3DOS_UserArea.cpp | 216 ++ Plus3DOS_UserArea.h | 38 + ReadMe.txt | 117 + Resource.h | 37 + StatusView.cpp | 59 + StatusView.h | 53 + StdAfx.cpp | 8 + StdAfx.h | 34 + TreeObject.cpp | 58 + TreeObject.h | 68 + descript.ion | 0 memdc.h | 110 + plus3dos.zsm | 6836 +++++++++++++++++++++++++++++++++ plus3dos_file_headers.txt | 53 + registry.h | 11 + res/3eExplorer.ico | Bin 0 -> 1078 bytes res/3eExplorer.rc2 | 13 + res/3eExplorerDoc.ico | Bin 0 -> 1078 bytes res/Toolbar.bmp | Bin 0 -> 1078 bytes res/app.ico | Bin 0 -> 17006 bytes res/app_ico_large copy.ico | Bin 0 -> 12862 bytes res/app_ico_large.psd | Bin 0 -> 62887 bytes res/app_ico_medium copy.ico | Bin 0 -> 3262 bytes res/app_ico_medium.psd | Bin 0 -> 52068 bytes res/app_ico_small copy.ico | Bin 0 -> 894 bytes res/app_ico_small.psd | Bin 0 -> 47389 bytes res/descript.ion | 0 res/icobundl.exe | Bin 0 -> 15872 bytes res/icocheck.exe | Bin 0 -> 13824 bytes res/tree_ico.bmp | Bin 0 -> 12344 bytes res/tree_ico.psd | Bin 0 -> 37673 bytes res/tree_ico_mask.bmp | Bin 0 -> 12344 bytes tapfile_format.txt | 118 + 71 files changed, 13260 insertions(+) create mode 100644 .gitignore create mode 100644 3eExplorer.clw create mode 100644 3eExplorer.cpp create mode 100644 3eExplorer.dsp create mode 100644 3eExplorer.dsw create mode 100644 3eExplorer.h create mode 100644 3eExplorer.rc create mode 100644 3eExplorer.txt create mode 100644 3eExplorerDoc.cpp create mode 100644 3eExplorerDoc.h create mode 100644 3eExplorerView.cpp create mode 100644 3eExplorerView.h create mode 100644 ChildFrm.cpp create mode 100644 ChildFrm.h create mode 100644 ContentView.cpp create mode 100644 ContentView.h create mode 100644 DetailView.cpp create mode 100644 DetailView.h create mode 100644 DiskAllocationView.cpp create mode 100644 DiskAllocationView.h create mode 100644 DocumentStatus.cpp create mode 100644 DocumentStatus.h create mode 100644 ExplorerFile.cpp create mode 100644 ExplorerFile.h create mode 100644 HdfHeader.cpp create mode 100644 HdfHeader.h create mode 100644 IDEDOSPartitionDefinition.cpp create mode 100644 IDEDOSPartitionDefinition.h create mode 100644 IDEDOS_Plus3DOSPartition.cpp create mode 100644 IDEDOS_Plus3DOSPartition.h create mode 100644 IDEDOS_SystemPartition.cpp create mode 100644 IDEDOS_SystemPartition.h create mode 100644 MainFrm.cpp create mode 100644 MainFrm.h create mode 100644 OptionsDialog.cpp create mode 100644 OptionsDialog.h create mode 100644 Plus3DOS_DirectoryEntry.cpp create mode 100644 Plus3DOS_DirectoryEntry.h create mode 100644 Plus3DOS_UserArea.cpp create mode 100644 Plus3DOS_UserArea.h create mode 100644 ReadMe.txt create mode 100644 Resource.h create mode 100644 StatusView.cpp create mode 100644 StatusView.h create mode 100644 StdAfx.cpp create mode 100644 StdAfx.h create mode 100644 TreeObject.cpp create mode 100644 TreeObject.h create mode 100644 descript.ion create mode 100644 memdc.h create mode 100644 plus3dos.zsm create mode 100644 plus3dos_file_headers.txt create mode 100644 registry.h create mode 100644 res/3eExplorer.ico create mode 100644 res/3eExplorer.rc2 create mode 100644 res/3eExplorerDoc.ico create mode 100644 res/Toolbar.bmp create mode 100644 res/app.ico create mode 100644 res/app_ico_large copy.ico create mode 100644 res/app_ico_large.psd create mode 100644 res/app_ico_medium copy.ico create mode 100644 res/app_ico_medium.psd create mode 100644 res/app_ico_small copy.ico create mode 100644 res/app_ico_small.psd create mode 100644 res/descript.ion create mode 100644 res/icobundl.exe create mode 100644 res/icocheck.exe create mode 100644 res/tree_ico.bmp create mode 100644 res/tree_ico.psd create mode 100644 res/tree_ico_mask.bmp create mode 100644 tapfile_format.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..90c38d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,96 @@ +actual_releases/ + +.idea/ +*.iml +*.pem +*.zip +*.rar + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +box-aspnet-mvc-skeleton-app/[Bb]in/ +[Oo]bj/ + +# Visual Studo 2015 cache/options directory +.vs/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Logs +logs +*.log + diff --git a/3eExplorer.clw b/3eExplorer.clw new file mode 100644 index 0000000..495a080 --- /dev/null +++ b/3eExplorer.clw @@ -0,0 +1,334 @@ +; CLW file contains information for the MFC ClassWizard + +[General Info] +Version=1 +LastClass=CDocumentStatus +LastTemplate=CListView +NewFileInclude1=#include "stdafx.h" +NewFileInclude2=#include "3eExplorer.h" +LastPage=0 + +ClassCount=12 +Class1=CMy3eExplorerApp +Class2=CMy3eExplorerDoc +Class3=CMy3eExplorerView +Class4=CMainFrame + +ResourceCount=10 +Resource1=IDD_ABOUTBOX +Resource2=IDR_MAINFRAME +Resource3=IDR_MY3EEXTYPE +Class5=CChildFrame +Class6=CAboutDlg +Resource4=IDD_ABOUTBOX (English (U.S.)) +Resource5=IDR_EXPLORER_POPUP_FILE +Class7=CDetailView +Class8=CContentView +Resource6=IDR_EXPLORER_POPUP +Resource7=IDR_MY3EEXTYPE (English (U.S.)) +Class9=COptionsDialog +Resource8=IDR_MAINFRAME (English (U.S.)) +Class10=CDiskAllocationView +Resource9=IDD_OPTIONS +Class11=CDocumentStatus +Class12=CStatusView +Resource10=IDD_STATUS + +[CLS:CMy3eExplorerApp] +Type=0 +HeaderFile=3eExplorer.h +ImplementationFile=3eExplorer.cpp +Filter=N +BaseClass=CWinApp +VirtualFilter=AC +LastObject=CMy3eExplorerApp + +[CLS:CMy3eExplorerDoc] +Type=0 +HeaderFile=3eExplorerDoc.h +ImplementationFile=3eExplorerDoc.cpp +Filter=N +LastObject=ID_FILE_FILESTATUS +BaseClass=CDocument +VirtualFilter=DC + +[CLS:CMy3eExplorerView] +Type=0 +HeaderFile=3eExplorerView.h +ImplementationFile=3eExplorerView.cpp +Filter=C +LastObject=CMy3eExplorerView +BaseClass=CMy3eExplorerViewBase +VirtualFilter=VWC + + +[CLS:CMainFrame] +Type=0 +HeaderFile=MainFrm.h +ImplementationFile=MainFrm.cpp +Filter=T +BaseClass=CMDIFrameWnd +VirtualFilter=fWC +LastObject=CMainFrame + + +[CLS:CChildFrame] +Type=0 +HeaderFile=ChildFrm.h +ImplementationFile=ChildFrm.cpp +Filter=M +LastObject=CChildFrame +BaseClass=CMDIChildWnd +VirtualFilter=mfWC + + +[CLS:CAboutDlg] +Type=0 +HeaderFile=3eExplorer.cpp +ImplementationFile=3eExplorer.cpp +Filter=D +LastObject=CAboutDlg + +[DLG:IDD_ABOUTBOX] +Type=1 +ControlCount=4 +Control1=IDC_STATIC,static,1342177283 +Control2=IDC_STATIC,static,1342308352 +Control3=IDC_STATIC,static,1342308352 +Control4=IDOK,button,1342373889 +Class=CAboutDlg + +[MNU:IDR_MAINFRAME] +Type=1 +Class=CMainFrame +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command4=ID_APP_EXIT +Command5=ID_VIEW_TOOLBAR +Command6=ID_VIEW_STATUS_BAR +Command7=ID_APP_ABOUT +CommandCount=7 +Command3=ID_FILE_MRU_FILE1 + +[TB:IDR_MAINFRAME] +Type=1 +Class=CMainFrame +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_SAVE +Command4=ID_EDIT_CUT +Command5=ID_EDIT_COPY +Command6=ID_EDIT_PASTE +Command7=ID_FILE_PRINT +CommandCount=8 +Command8=ID_APP_ABOUT + +[MNU:IDR_MY3EEXTYPE] +Type=1 +Class=CMy3eExplorerView +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_CLOSE +Command4=ID_FILE_SAVE +Command5=ID_FILE_SAVE_AS +Command9=ID_EDIT_CUT +Command10=ID_EDIT_COPY +Command11=ID_EDIT_PASTE +Command12=ID_VIEW_TOOLBAR +Command13=ID_VIEW_STATUS_BAR +Command14=ID_WINDOW_NEW +CommandCount=18 +Command6=ID_FILE_MRU_FILE1 +Command7=ID_APP_EXIT +Command8=ID_EDIT_UNDO +Command15=ID_WINDOW_CASCADE +Command16=ID_WINDOW_TILE_HORZ +Command17=ID_WINDOW_ARRANGE +Command18=ID_APP_ABOUT + +[ACL:IDR_MAINFRAME] +Type=1 +Class=CMainFrame +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_SAVE +Command5=ID_EDIT_CUT +Command6=ID_EDIT_COPY +Command7=ID_EDIT_PASTE +Command8=ID_EDIT_UNDO +Command9=ID_EDIT_CUT +Command10=ID_EDIT_COPY +Command11=ID_EDIT_PASTE +Command12=ID_NEXT_PANE +CommandCount=13 +Command4=ID_EDIT_UNDO +Command13=ID_PREV_PANE + + +[TB:IDR_MAINFRAME (English (U.S.))] +Type=1 +Class=? +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_SAVE +Command4=ID_EDIT_CUT +Command5=ID_EDIT_COPY +Command6=ID_EDIT_PASTE +Command7=ID_FILE_PRINT +Command8=ID_APP_ABOUT +CommandCount=8 + +[MNU:IDR_MAINFRAME (English (U.S.))] +Type=1 +Class=? +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_MRU_FILE1 +Command4=ID_APP_EXIT +Command5=ID_VIEW_TOOLBAR +Command6=ID_VIEW_STATUS_BAR +Command7=ID_VIEW_OPTIONS +Command8=ID_APP_ABOUT +CommandCount=8 + +[MNU:IDR_MY3EEXTYPE (English (U.S.))] +Type=1 +Class=? +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_CLOSE +Command4=ID_FILE_SAVE +Command5=ID_FILE_SAVE_AS +Command6=ID_FILE_FILESTATUS +Command7=ID_FILE_MRU_FILE1 +Command8=ID_APP_EXIT +Command9=ID_EXPLORER_EXTRACT_TO_TAP +Command10=ID_EXPLORER_EXTRACT_TO_BIN +Command11=ID_EXPLORER_EXTRACT_TO_BIN_WITH_HEADER +Command12=ID_EDIT_UNDO +Command13=ID_EDIT_CUT +Command14=ID_EDIT_COPY +Command15=ID_EDIT_PASTE +Command16=ID_VIEW_TOOLBAR +Command17=ID_VIEW_STATUS_BAR +Command18=ID_VIEW_OPTIONS +Command19=ID_WINDOW_NEW +Command20=ID_WINDOW_CASCADE +Command21=ID_WINDOW_TILE_HORZ +Command22=ID_WINDOW_ARRANGE +Command23=ID_WINDOW_SPLIT +Command24=ID_APP_ABOUT +CommandCount=24 + +[ACL:IDR_MAINFRAME (English (U.S.))] +Type=1 +Class=? +Command1=ID_FILE_NEW +Command2=ID_FILE_OPEN +Command3=ID_FILE_SAVE +Command4=ID_EDIT_UNDO +Command5=ID_EDIT_CUT +Command6=ID_EDIT_COPY +Command7=ID_EDIT_PASTE +Command8=ID_EDIT_UNDO +Command9=ID_EDIT_CUT +Command10=ID_EDIT_COPY +Command11=ID_EDIT_PASTE +Command12=ID_NEXT_PANE +Command13=ID_PREV_PANE +CommandCount=13 + +[DLG:IDD_ABOUTBOX (English (U.S.))] +Type=1 +Class=CAboutDlg +ControlCount=2 +Control1=IDOK,button,1342373889 +Control2=IDC_ABOUT_TEXT,edit,1342245060 + +[DLG:IDR_MAINFRAME (English (U.S.))] +Type=1 +Class=? +ControlCount=0 + +[CLS:CDetailView] +Type=0 +HeaderFile=DetailView.h +ImplementationFile=DetailView.cpp +BaseClass=CEditView +Filter=C +VirtualFilter=VWC +LastObject=CDetailView + +[CLS:CContentView] +Type=0 +HeaderFile=ContentView.h +ImplementationFile=ContentView.cpp +BaseClass=CListView +Filter=C + +[DLG:IDD_OPTIONS] +Type=1 +Class=COptionsDialog +ControlCount=4 +Control1=IDOK,button,1342242817 +Control2=IDCANCEL,button,1342242816 +Control3=IDC_PRECACHE_TRACK,button,1342242819 +Control4=IDC_PRE_READ_HEADER,button,1342242819 + +[CLS:COptionsDialog] +Type=0 +HeaderFile=OptionsDialog.h +ImplementationFile=OptionsDialog.cpp +BaseClass=CDialog +Filter=D +VirtualFilter=dWC +LastObject=COptionsDialog + +[MNU:IDR_EXPLORER_POPUP_FILE] +Type=1 +Class=? +Command1=ID_EXPLORER_FILENAME +Command2=ID_EXPLORER_EXTRACT_TO_TAP +Command3=ID_EXPLORER_EXTRACT_TO_BIN +Command4=ID_EXPLORER_EXTRACT_TO_BIN_WITH_HEADER +CommandCount=4 + +[MNU:IDR_EXPLORER_POPUP] +Type=1 +Class=? +Command1=ID_EXPLORER_FILENAME +CommandCount=1 + +[CLS:CDiskAllocationView] +Type=0 +HeaderFile=DiskAllocationView.h +ImplementationFile=DiskAllocationView.cpp +BaseClass=CScrollView +Filter=C +LastObject=CDiskAllocationView +VirtualFilter=VWC + +[DLG:IDD_STATUS] +Type=1 +Class=CDocumentStatus +ControlCount=3 +Control1=IDOK,button,1342242817 +Control2=IDC_FLUSH_CACHE,button,1342242816 +Control3=IDC_STATUS_LISTBOX,listbox,1352728961 + +[CLS:CDocumentStatus] +Type=0 +HeaderFile=DocumentStatus.h +ImplementationFile=DocumentStatus.cpp +BaseClass=CDialog +Filter=D +VirtualFilter=dWC +LastObject=IDC_FLUSH_CACHE + +[CLS:CStatusView] +Type=0 +HeaderFile=StatusView.h +ImplementationFile=StatusView.cpp +BaseClass=CListView +Filter=C + diff --git a/3eExplorer.cpp b/3eExplorer.cpp new file mode 100644 index 0000000..07dfddb --- /dev/null +++ b/3eExplorer.cpp @@ -0,0 +1,263 @@ +// 3eExplorer.cpp : Defines the class behaviors for the application. +// + +#include "stdafx.h" +#include "3eExplorer.h" + +#include "ChildFrm.h" +#include "3eExplorerDoc.h" +#include "3eExplorerView.h" +#include "registry.h" +#include "OptionsDialog.h" + + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CMy3eExplorerApp + +BEGIN_MESSAGE_MAP(CMy3eExplorerApp, CWinApp) + //{{AFX_MSG_MAP(CMy3eExplorerApp) + ON_COMMAND(ID_APP_ABOUT, OnAppAbout) + ON_UPDATE_COMMAND_UI(ID_VIEW_OPTIONS, OnUpdateViewOptions) + ON_UPDATE_COMMAND_UI(ID_FILE_NEW, OnUpdateFileNew) + ON_COMMAND(ID_VIEW_OPTIONS, OnViewOptions) + //}}AFX_MSG_MAP + // Standard file based document commands + ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) + ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CMy3eExplorerApp construction + +CMy3eExplorerApp::CMy3eExplorerApp() +: m_bHeadCache(FALSE) +, m_bPreReadFileInfo(TRUE) +{ + // TODO: add construction code here, + // Place all significant initialization in InitInstance +} + +///////////////////////////////////////////////////////////////////////////// +// The one and only CMy3eExplorerApp object + +CMy3eExplorerApp theApp; + +///////////////////////////////////////////////////////////////////////////// +// CMy3eExplorerApp initialization + +BOOL CMy3eExplorerApp::InitInstance() +{ + AfxEnableControlContainer(); + + // Standard initialization + // If you are not using these features and wish to reduce the size + // of your final executable, you should remove from the following + // the specific initialization routines you do not need. + +#ifdef _AFXDLL + Enable3dControls(); // Call this when using MFC in a shared DLL +#else + Enable3dControlsStatic(); // Call this when linking to MFC statically +#endif + + // Change the registry key under which our settings are stored. + // TODO: You should modify this string to be something appropriate + // such as the name of your company or organization. + SetRegistryKey(REG_ROOT); + + LoadStdProfileSettings(); // Load standard INI file options (including MRU) + + LoadGlobalSettings(); + + // Register the application's document templates. Document templates + // serve as the connection between documents, frame windows and views. + + CMultiDocTemplate* pDocTemplate; + pDocTemplate = new CMultiDocTemplate( + IDR_MY3EEXTYPE, + RUNTIME_CLASS(CMy3eExplorerDoc), + RUNTIME_CLASS(CChildFrame), // custom MDI child frame + RUNTIME_CLASS(CMy3eExplorerView)); + AddDocTemplate(pDocTemplate); + + // create main MDI Frame window + CMainFrame* pMainFrame = new CMainFrame; + if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) + return FALSE; + m_pMainWnd = pMainFrame; + + // Parse command line for standard shell commands, DDE, file open + CCommandLineInfo cmdInfo; + ParseCommandLine(cmdInfo); + + // Dispatch commands specified on the command line + if (!ProcessShellCommand(cmdInfo)) + return FALSE; + + m_pMainFrame = pMainFrame; + + // The main window has been initialized, so show and update it. + pMainFrame->ShowWindow(m_nCmdShow); + pMainFrame->UpdateWindow(); + + return TRUE; +} + + +///////////////////////////////////////////////////////////////////////////// +// CAboutDlg dialog used for App About + +class CAboutDlg : public CDialog +{ +public: + CString m_strAbout; + CAboutDlg(); + +// Dialog Data + //{{AFX_DATA(CAboutDlg) + enum { IDD = IDD_ABOUTBOX }; + //}}AFX_DATA + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CAboutDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + //{{AFX_MSG(CAboutDlg) + // No message handlers + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) +{ + //{{AFX_DATA_INIT(CAboutDlg) + m_strAbout = _T(""); + //}}AFX_DATA_INIT +} + +void CAboutDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CAboutDlg) + DDX_Text(pDX, IDC_ABOUT_TEXT, m_strAbout); + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) + //{{AFX_MSG_MAP(CAboutDlg) + // No message handlers + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +// App command to run the dialog +void CMy3eExplorerApp::OnAppAbout() +{ + // build some information about this application into a string + std::ostringstream ostr; + char filename[_MAX_PATH]; + GetModuleFileName(NULL, filename, _MAX_PATH); + std::string fnstr = filename; + int last_pos = fnstr.find_last_of(_T("\\/")); + if (std::string::npos == last_pos) + last_pos = 0; + else + last_pos ++; + ostr << fnstr.substr(last_pos) << "\r\n"; + + DWORD dwTmp; + DWORD dwSize = GetFileVersionInfoSize(filename, &dwTmp); + void *versionInfo = new char[dwSize]; + + if (GetFileVersionInfo(filename, dwTmp, dwSize, versionInfo)) + { + LPVOID lpBuffer; + UINT uiBuffer; + CString strtmp; + + VerQueryValue(versionInfo, _T("\\StringFileInfo\\040904B0\\FileDescription"), &lpBuffer, &uiBuffer); + ostr << (LPCTSTR)lpBuffer << _T("\r\n"); + + VerQueryValue(versionInfo, _T("\\StringFileInfo\\040904B0\\ProductVersion"), &lpBuffer, &uiBuffer); + strtmp = (LPCTSTR)lpBuffer; + strtmp.Replace(_T(", "), _T(".")); + ostr << _T("Version: ") << (LPCTSTR)strtmp; + #ifdef _DEBUG + ostr << "_DEBUG"; + #endif _DEBUG + ostr << _T("\r\n"); + + VerQueryValue(versionInfo, _T("\\StringFileInfo\\040904B0\\LegalCopyright"), &lpBuffer, &uiBuffer); + ostr << (LPCTSTR)lpBuffer; + } + else + { + ostr << "Error - cannot get version information"; + } + + delete [] versionInfo; + + CAboutDlg aboutDlg; + aboutDlg.m_strAbout = ostr.str().c_str(); + aboutDlg.DoModal(); +} + +///////////////////////////////////////////////////////////////////////////// +// CMy3eExplorerApp message handlers + +void CMy3eExplorerApp::LoadGlobalSettings() +{ + // this method loads the global settings from the Registry + if (GetProfileInt(REG_SETTINGS, REG_SETTINGS_HEADCACHE, 0)) + m_bHeadCache = TRUE; + else + m_bHeadCache = FALSE; + + if (GetProfileInt(REG_SETTINGS, REG_SETTINGS_PREREADFILEINFO, 1)) + m_bPreReadFileInfo = TRUE; + else + m_bPreReadFileInfo = FALSE; +} + +void CMy3eExplorerApp::SaveGlobalSettings() +{ + // this method saves the global settings to the Registry + WriteProfileInt(REG_SETTINGS, REG_SETTINGS_HEADCACHE, m_bHeadCache ? 1 : 0); + WriteProfileInt(REG_SETTINGS, REG_SETTINGS_PREREADFILEINFO, m_bPreReadFileInfo ? 1 : 0); +} + +int CMy3eExplorerApp::ExitInstance() +{ + SaveGlobalSettings(); + return CWinApp::ExitInstance(); +} + +void CMy3eExplorerApp::OnUpdateViewOptions(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(); +} + +void CMy3eExplorerApp::OnUpdateFileNew(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(FALSE); +} + +void CMy3eExplorerApp::OnViewOptions() +{ + // Display the options dialog + COptionsDialog optionsDlg; + optionsDlg.m_bPreCacheHeader = m_bPreReadFileInfo; + optionsDlg.m_bPreCacheTrack = m_bHeadCache; + optionsDlg.DoModal(); + m_bPreReadFileInfo = optionsDlg.m_bPreCacheHeader; + m_bHeadCache = optionsDlg.m_bPreCacheTrack; +} diff --git a/3eExplorer.dsp b/3eExplorer.dsp new file mode 100644 index 0000000..e019422 --- /dev/null +++ b/3eExplorer.dsp @@ -0,0 +1,385 @@ +# Microsoft Developer Studio Project File - Name="3eExplorer" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=3eExplorer - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "3eExplorer.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "3eExplorer.mak" CFG="3eExplorer - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "3eExplorer - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "3eExplorer - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE "3eExplorer - Win32 Release Static" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "3eExplorer - Win32 Release" + +# PROP BASE Use_MFC 6 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 6 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release_Obj" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" /d "_AFXDLL" +# ADD RSC /l 0x809 /d "NDEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /machine:I386 +# ADD LINK32 version.lib /nologo /subsystem:windows /machine:I386 +# SUBTRACT LINK32 /profile +# Begin Custom Build +OutDir=.\Release +ProjDir=. +InputPath=.\Release\3eExplorer.exe +SOURCE="$(InputPath)" + +"$(OutDir)\3eExplorer.txt" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy "$(ProjDir)\3eExplorer.txt" "$(OutDir)" /y + date /t >> "$(OutDir)\3eExplorer.txt" + +# End Custom Build + +!ELSEIF "$(CFG)" == "3eExplorer - Win32 Debug" + +# PROP BASE Use_MFC 6 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 6 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug_Obj" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /FR /Yu"stdafx.h" /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" /d "_AFXDLL" +# ADD RSC /l 0x809 /d "_DEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo /o"Debug_Obj/3eExplorer.bsc" +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 version.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# SUBTRACT LINK32 /profile + +!ELSEIF "$(CFG)" == "3eExplorer - Win32 Release Static" + +# PROP BASE Use_MFC 6 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "3eExplorer___Win32_Release_Static" +# PROP BASE Intermediate_Dir "3eExplorer___Win32_Release_Static" +# PROP BASE Target_Dir "" +# PROP Use_MFC 5 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release_Static" +# PROP Intermediate_Dir "Release_Static_Obj" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" /d "_AFXDLL" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /machine:I386 +# ADD LINK32 version.lib /nologo /subsystem:windows /machine:I386 +# SUBTRACT LINK32 /profile +# Begin Custom Build +OutDir=.\Release_Static +ProjDir=. +InputPath=.\Release_Static\3eExplorer.exe +SOURCE="$(InputPath)" + +"$(OutDir)\3eExplorer.txt" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + copy "$(ProjDir)\3eExplorer.txt" "$(OutDir)" /y + date /t >> "$(OutDir)\3eExplorer.txt" + +# End Custom Build + +!ENDIF + +# Begin Target + +# Name "3eExplorer - Win32 Release" +# Name "3eExplorer - Win32 Debug" +# Name "3eExplorer - Win32 Release Static" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\3eExplorer.cpp +# End Source File +# Begin Source File + +SOURCE=.\3eExplorer.rc +# End Source File +# Begin Source File + +SOURCE=.\3eExplorerDoc.cpp +# End Source File +# Begin Source File + +SOURCE=.\3eExplorerView.cpp +# End Source File +# Begin Source File + +SOURCE=.\ChildFrm.cpp +# End Source File +# Begin Source File + +SOURCE=.\ContentView.cpp +# End Source File +# Begin Source File + +SOURCE=.\DetailView.cpp +# End Source File +# Begin Source File + +SOURCE=.\DiskAllocationView.cpp +# End Source File +# Begin Source File + +SOURCE=.\DocumentStatus.cpp +# End Source File +# Begin Source File + +SOURCE=.\ExplorerFile.cpp +# End Source File +# Begin Source File + +SOURCE=.\HdfHeader.cpp +# End Source File +# Begin Source File + +SOURCE=.\IDEDOS_Plus3DosPartition.cpp +# End Source File +# Begin Source File + +SOURCE=.\IDEDOS_SystemPartition.cpp +# End Source File +# Begin Source File + +SOURCE=.\IDEDOSPartitionDefinition.cpp +# End Source File +# Begin Source File + +SOURCE=.\MainFrm.cpp +# End Source File +# Begin Source File + +SOURCE=.\OptionsDialog.cpp +# End Source File +# Begin Source File + +SOURCE=.\Plus3DOS_DirectoryEntry.cpp +# End Source File +# Begin Source File + +SOURCE=.\Plus3DOS_UserArea.cpp +# End Source File +# Begin Source File + +SOURCE=.\StatusView.cpp +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.cpp +# ADD CPP /Yc"stdafx.h" +# End Source File +# Begin Source File + +SOURCE=.\TreeObject.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\3eExplorer.h +# End Source File +# Begin Source File + +SOURCE=.\3eExplorerDoc.h +# End Source File +# Begin Source File + +SOURCE=.\3eExplorerView.h +# End Source File +# Begin Source File + +SOURCE=.\ChildFrm.h +# End Source File +# Begin Source File + +SOURCE=.\ContentView.h +# End Source File +# Begin Source File + +SOURCE=.\DetailView.h +# End Source File +# Begin Source File + +SOURCE=.\DiskAllocationView.h +# End Source File +# Begin Source File + +SOURCE=.\DocumentStatus.h +# End Source File +# Begin Source File + +SOURCE=.\ExplorerFile.h +# End Source File +# Begin Source File + +SOURCE=.\HdfHeader.h +# End Source File +# Begin Source File + +SOURCE=.\IDEDOS_Plus3DosPartition.h +# End Source File +# Begin Source File + +SOURCE=.\IDEDOS_SystemPartition.h +# End Source File +# Begin Source File + +SOURCE=.\IDEDOSPartitionDefinition.h +# End Source File +# Begin Source File + +SOURCE=.\MainFrm.h +# End Source File +# Begin Source File + +SOURCE=.\memdc.h +# End Source File +# Begin Source File + +SOURCE=.\OptionsDialog.h +# End Source File +# Begin Source File + +SOURCE=.\Plus3DOS_DirectoryEntry.h +# End Source File +# Begin Source File + +SOURCE=.\Plus3DOS_UserArea.h +# End Source File +# Begin Source File + +SOURCE=.\registry.h +# End Source File +# Begin Source File + +SOURCE=.\Resource.h +# End Source File +# Begin Source File + +SOURCE=.\StatusView.h +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.h +# End Source File +# Begin Source File + +SOURCE=.\TreeObject.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\res\3eExplorer.ico +# End Source File +# Begin Source File + +SOURCE=.\res\3eExplorer.rc2 +# End Source File +# Begin Source File + +SOURCE=.\res\3eExplorerDoc.ico +# End Source File +# Begin Source File + +SOURCE=.\res\app.ico +# End Source File +# Begin Source File + +SOURCE=.\res\bitmap1.bmp +# End Source File +# Begin Source File + +SOURCE=.\res\Toolbar.bmp +# End Source File +# Begin Source File + +SOURCE=.\res\tree_ico.bmp +# End Source File +# Begin Source File + +SOURCE=.\res\tree_ico_mask.bmp +# End Source File +# End Group +# Begin Source File + +SOURCE=.\3eExplorer.txt +# End Source File +# Begin Source File + +SOURCE=.\plus3dos.zsm +# End Source File +# Begin Source File + +SOURCE=.\plus3dos_file_headers.txt +# End Source File +# Begin Source File + +SOURCE=.\ReadMe.txt +# End Source File +# Begin Source File + +SOURCE=.\tapfile_format.txt +# End Source File +# End Target +# End Project diff --git a/3eExplorer.dsw b/3eExplorer.dsw new file mode 100644 index 0000000..c5d3a34 --- /dev/null +++ b/3eExplorer.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "3eExplorer"=.\3eExplorer.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/3eExplorer.h b/3eExplorer.h new file mode 100644 index 0000000..8519ac5 --- /dev/null +++ b/3eExplorer.h @@ -0,0 +1,67 @@ +// 3eExplorer.h : main header file for the 3EEXPLORER application +// + +#if !defined(AFX_3EEXPLORER_H__917076D4_9D4D_45D0_9DE1_40AAB7FE0C40__INCLUDED_) +#define AFX_3EEXPLORER_H__917076D4_9D4D_45D0_9DE1_40AAB7FE0C40__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#ifndef __AFXWIN_H__ + #error include 'stdafx.h' before including this file for PCH +#endif + +#include "resource.h" // main symbols +#include "MainFrm.h" + +///////////////////////////////////////////////////////////////////////////// +// CMy3eExplorerApp: +// See 3eExplorer.cpp for the implementation of this class +// + +class CMy3eExplorerApp : public CWinApp +{ +public: + CMy3eExplorerApp(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CMy3eExplorerApp) + public: + virtual BOOL InitInstance(); + virtual int ExitInstance(); + //}}AFX_VIRTUAL + +// Implementation + //{{AFX_MSG(CMy3eExplorerApp) + afx_msg void OnAppAbout(); + afx_msg void OnUpdateViewOptions(CCmdUI* pCmdUI); + afx_msg void OnUpdateFileNew(CCmdUI* pCmdUI); + afx_msg void OnViewOptions(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +protected: + BOOL m_bHeadCache; + BOOL m_bPreReadFileInfo; + void SaveGlobalSettings(); + void LoadGlobalSettings(); + +public: + CMainFrame *m_pMainFrame; + +public: + inline BOOL HeadCache() {return m_bHeadCache;}; + inline BOOL PreReadFileInfo() {return m_bPreReadFileInfo;}; +}; + + +extern CMy3eExplorerApp theApp; + + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_3EEXPLORER_H__917076D4_9D4D_45D0_9DE1_40AAB7FE0C40__INCLUDED_) diff --git a/3eExplorer.rc b/3eExplorer.rc new file mode 100644 index 0000000..4ec3f9c --- /dev/null +++ b/3eExplorer.rc @@ -0,0 +1,565 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAINFRAME ICON DISCARDABLE "res\\app.ico" +IDR_MY3EEXTYPE ICON DISCARDABLE "res\\3eExplorerDoc.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDR_MAINFRAME BITMAP MOVEABLE PURE "res\\Toolbar.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Toolbar +// + +IDR_MAINFRAME TOOLBAR DISCARDABLE 16, 15 +BEGIN + BUTTON ID_FILE_NEW + BUTTON ID_FILE_OPEN + BUTTON ID_FILE_SAVE + SEPARATOR + BUTTON ID_EDIT_CUT + BUTTON ID_EDIT_COPY + BUTTON ID_EDIT_PASTE + SEPARATOR + BUTTON ID_FILE_PRINT + SEPARATOR + BUTTON ID_APP_ABOUT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MAINFRAME MENU PRELOAD DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New\tCtrl+N", ID_FILE_NEW, GRAYED + MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN + MENUITEM SEPARATOR + MENUITEM "Recent File", ID_FILE_MRU_FILE1, GRAYED + MENUITEM SEPARATOR + MENUITEM "E&xit", ID_APP_EXIT + END + POPUP "&View" + BEGIN + MENUITEM "&Toolbar", ID_VIEW_TOOLBAR + MENUITEM "&Status Bar", ID_VIEW_STATUS_BAR + MENUITEM "&Options...", ID_VIEW_OPTIONS + END + POPUP "&Help" + BEGIN + MENUITEM "&About 3eExplorer...", ID_APP_ABOUT + END +END + +IDR_MY3EEXTYPE MENU PRELOAD DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New\tCtrl+N", ID_FILE_NEW, GRAYED + MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN + MENUITEM "&Close", ID_FILE_CLOSE + MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE, GRAYED + MENUITEM "Save &As...", ID_FILE_SAVE_AS, GRAYED + MENUITEM SEPARATOR + MENUITEM "File S&tatus", ID_FILE_FILESTATUS + MENUITEM SEPARATOR + MENUITEM "Recent File", ID_FILE_MRU_FILE1, GRAYED + MENUITEM SEPARATOR + MENUITEM "E&xit", ID_APP_EXIT + END + POPUP "E&xtract" + BEGIN + MENUITEM "Extract to &TAP", ID_EXPLORER_EXTRACT_TO_TAP + MENUITEM "Extract to &BIN", ID_EXPLORER_EXTRACT_TO_BIN + MENUITEM "Extract to BIN with &Header", ID_EXPLORER_EXTRACT_TO_BIN_WITH_HEADER + + END + POPUP "&Edit" + BEGIN + MENUITEM "&Undo\tCtrl+Z", ID_EDIT_UNDO, GRAYED + MENUITEM SEPARATOR + MENUITEM "Cu&t\tCtrl+X", ID_EDIT_CUT, GRAYED + MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY, GRAYED + MENUITEM "&Paste\tCtrl+V", ID_EDIT_PASTE, GRAYED + END + POPUP "&View" + BEGIN + MENUITEM "&Toolbar", ID_VIEW_TOOLBAR + MENUITEM "&Status Bar", ID_VIEW_STATUS_BAR + MENUITEM "&Options...", ID_VIEW_OPTIONS + END + POPUP "&Window" + BEGIN + MENUITEM "&New Window", ID_WINDOW_NEW + MENUITEM "&Cascade", ID_WINDOW_CASCADE + MENUITEM "&Tile", ID_WINDOW_TILE_HORZ + MENUITEM "&Arrange Icons", ID_WINDOW_ARRANGE + MENUITEM "S&plit", ID_WINDOW_SPLIT + END + POPUP "&Help" + BEGIN + MENUITEM "&About 3eExplorer...", ID_APP_ABOUT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE +BEGIN + "N", ID_FILE_NEW, VIRTKEY, CONTROL + "O", ID_FILE_OPEN, VIRTKEY, CONTROL + "S", ID_FILE_SAVE, VIRTKEY, CONTROL + "Z", ID_EDIT_UNDO, VIRTKEY, CONTROL + "X", ID_EDIT_CUT, VIRTKEY, CONTROL + "C", ID_EDIT_COPY, VIRTKEY, CONTROL + "V", ID_EDIT_PASTE, VIRTKEY, CONTROL + VK_BACK, ID_EDIT_UNDO, VIRTKEY, ALT + VK_DELETE, ID_EDIT_CUT, VIRTKEY, SHIFT + VK_INSERT, ID_EDIT_COPY, VIRTKEY, CONTROL + VK_INSERT, ID_EDIT_PASTE, VIRTKEY, SHIFT + VK_F6, ID_NEXT_PANE, VIRTKEY + VK_F6, ID_PREV_PANE, VIRTKEY, SHIFT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOG DISCARDABLE 0, 0, 235, 55 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About 3eExplorer" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,178,7,50,14,WS_GROUP + EDITTEXT IDC_ABOUT_TEXT,7,7,167,41,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER +END + +IDR_MAINFRAME DIALOG DISCARDABLE 0, 0, 330, 16 +STYLE WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN +END + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,1,0,2 + PRODUCTVERSION 0,1,0,2 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "\0" + VALUE "CompanyName", "raww.org\0" + VALUE "FileDescription", "3eExplorer +3e File Extractor\0" + VALUE "FileVersion", "0, 1, 0, 2\0" + VALUE "InternalName", "3eExplorer\0" + VALUE "LegalCopyright", "Copyright (C) 2007 by icabod\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "3eExplorer.exe\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "3eExplorer\0" + VALUE "ProductVersion", "0, 1, 0, 2\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ABOUTBOX, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 228 + TOPMARGIN, 7 + BOTTOMMARGIN, 48 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + IDR_MAINFRAME "3eExplorer" + IDR_MY3EEXTYPE "\nhdf\nHDF Image File\nImage Files (*.hdf)\n.hdf\nMy3eExplorer.Document\nHDF Image File" +END + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + AFX_IDS_APP_TITLE "3eExplorer" + AFX_IDS_IDLEMESSAGE "Ready" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_INDICATOR_EXT "EXT" + ID_INDICATOR_CAPS "CAP" + ID_INDICATOR_NUM "NUM" + ID_INDICATOR_SCRL "SCRL" + ID_INDICATOR_OVR "OVR" + ID_INDICATOR_REC "REC" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_NEW "Create a new document\nNew" + ID_FILE_OPEN "Open an existing document\nOpen" + ID_FILE_CLOSE "Close the active document\nClose" + ID_FILE_SAVE "Save the active document\nSave" + ID_FILE_SAVE_AS "Save the active document with a new name\nSave As" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_APP_ABOUT "Display program information, version number and copyright\nAbout" + ID_APP_EXIT "Quit the application; prompts to save documents\nExit" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_MRU_FILE1 "Open this document" + ID_FILE_MRU_FILE2 "Open this document" + ID_FILE_MRU_FILE3 "Open this document" + ID_FILE_MRU_FILE4 "Open this document" + ID_FILE_MRU_FILE5 "Open this document" + ID_FILE_MRU_FILE6 "Open this document" + ID_FILE_MRU_FILE7 "Open this document" + ID_FILE_MRU_FILE8 "Open this document" + ID_FILE_MRU_FILE9 "Open this document" + ID_FILE_MRU_FILE10 "Open this document" + ID_FILE_MRU_FILE11 "Open this document" + ID_FILE_MRU_FILE12 "Open this document" + ID_FILE_MRU_FILE13 "Open this document" + ID_FILE_MRU_FILE14 "Open this document" + ID_FILE_MRU_FILE15 "Open this document" + ID_FILE_MRU_FILE16 "Open this document" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_NEXT_PANE "Switch to the next window pane\nNext Pane" + ID_PREV_PANE "Switch back to the previous window pane\nPrevious Pane" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_WINDOW_NEW "Open another window for the active document\nNew Window" + ID_WINDOW_ARRANGE "Arrange icons at the bottom of the window\nArrange Icons" + ID_WINDOW_CASCADE "Arrange windows so they overlap\nCascade Windows" + ID_WINDOW_TILE_HORZ "Arrange windows as non-overlapping tiles\nTile Windows" + ID_WINDOW_TILE_VERT "Arrange windows as non-overlapping tiles\nTile Windows" + ID_WINDOW_SPLIT "Split the active window into panes\nSplit" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_EDIT_CLEAR "Erase the selection\nErase" + ID_EDIT_CLEAR_ALL "Erase everything\nErase All" + ID_EDIT_COPY "Copy the selection and put it on the Clipboard\nCopy" + ID_EDIT_CUT "Cut the selection and put it on the Clipboard\nCut" + ID_EDIT_FIND "Find the specified text\nFind" + ID_EDIT_PASTE "Insert Clipboard contents\nPaste" + ID_EDIT_REPEAT "Repeat the last action\nRepeat" + ID_EDIT_REPLACE "Replace specific text with different text\nReplace" + ID_EDIT_SELECT_ALL "Select the entire document\nSelect All" + ID_EDIT_UNDO "Undo the last action\nUndo" + ID_EDIT_REDO "Redo the previously undone action\nRedo" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_VIEW_TOOLBAR "Show or hide the toolbar\nToggle ToolBar" + ID_VIEW_STATUS_BAR "Show or hide the status bar\nToggle StatusBar" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_SCSIZE "Change the window size" + AFX_IDS_SCMOVE "Change the window position" + AFX_IDS_SCMINIMIZE "Reduce the window to an icon" + AFX_IDS_SCMAXIMIZE "Enlarge the window to full size" + AFX_IDS_SCNEXTWINDOW "Switch to the next document window" + AFX_IDS_SCPREVWINDOW "Switch to the previous document window" + AFX_IDS_SCCLOSE "Close the active window and prompts to save the documents" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_SCRESTORE "Restore the window to normal size" + AFX_IDS_SCTASKLIST "Activate Task List" + AFX_IDS_MDICHILD "Activate this window" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_FILE_ACCESS_ERROR "There was an error accessing the file." +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_EXPLORER_EXTRACT_TO_TAP + "Extract selected file to a .tap file\nTap Extract" + ID_EXPLORER_EXTRACT_TO_BIN "Extract file to raw binary\nBinary Extract" + ID_EXPLORER_EXTRACT_TO_BIN_WITH_HEADER + "Extract file to raw binary with +3dos header\nBinary Header Extract" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.K.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_TREE_ICONS BITMAP DISCARDABLE "res\\tree_ico.bmp" +IDB_TREE_ICONS_MASK BITMAP DISCARDABLE "res\\tree_ico_mask.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_EXPLORER_POPUP_FILE MENU DISCARDABLE +BEGIN + POPUP "ExplorerPopup" + BEGIN + MENUITEM "{filename}", ID_EXPLORER_FILENAME, GRAYED + MENUITEM SEPARATOR + MENUITEM "Extract to &TAP", ID_EXPLORER_EXTRACT_TO_TAP + MENUITEM "Extract to &BIN", ID_EXPLORER_EXTRACT_TO_BIN + MENUITEM "Extract to BIN with &Header", ID_EXPLORER_EXTRACT_TO_BIN_WITH_HEADER + + END +END + +IDR_EXPLORER_POPUP MENU DISCARDABLE +BEGIN + POPUP "ExplorerPopup" + BEGIN + MENUITEM "{filename}", ID_EXPLORER_FILENAME, GRAYED + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_OPTIONS DIALOG DISCARDABLE 0, 0, 123, 54 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Dialog" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,7,33,50,14 + PUSHBUTTON "Cancel",IDCANCEL,66,33,50,14 + CONTROL "Pre-cache Entire Track",IDC_PRECACHE_TRACK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,7,89,10 + CONTROL "Pre-read File Header",IDC_PRE_READ_HEADER,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,19,81,10 +END + +IDD_STATUS DIALOGEX 0, 0, 209, 95 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_TOOLWINDOW +CAPTION "StatusDialog" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,152,74,50,14 + PUSHBUTTON "Flush Cache",IDC_FLUSH_CACHE,7,74,50,14 + LISTBOX IDC_STATUS_LISTBOX,7,7,195,60,LBS_USETABSTOPS | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_OPTIONS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 116 + TOPMARGIN, 7 + BOTTOMMARGIN, 47 + END + + IDD_STATUS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 202 + TOPMARGIN, 7 + BOTTOMMARGIN, 88 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" + "#ifdef _WIN32\r\n" + "LANGUAGE 9, 1\r\n" + "#pragma code_page(1252)\r\n" + "#endif //_WIN32\r\n" + "#include ""res\\3eExplorer.rc2"" // non-Microsoft Visual C++ edited resources\r\n" + "#include ""afxres.rc"" // Standard components\r\n" + "#endif\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + ID_EXPLORER_EXTRACT_TO_TAP + "Extract selected file to a .tap file\nTap Extract" + ID_EXPLORER_EXTRACT_TO_BIN "Extract file to raw binary\nBinary Extract" + ID_EXPLORER_EXTRACT_TO_BIN_WITH_HEADER + "Extract file to raw binary with +3dos header\nBinary Header Extract" +END + +#endif // English (U.K.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE 9, 1 +#pragma code_page(1252) +#endif //_WIN32 +#include "res\3eExplorer.rc2" // non-Microsoft Visual C++ edited resources +#include "afxres.rc" // Standard components +#endif + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/3eExplorer.txt b/3eExplorer.txt new file mode 100644 index 0000000..4b66a2e --- /dev/null +++ b/3eExplorer.txt @@ -0,0 +1,90 @@ +3eExplorer v0.1.0.2 +=-----------------= + + +Introduction: +============= + +3eExplorer is a utility for Windows to allow the examination and manipulation of +images of drives used by the +3e. The +3e, as developed by Garry Lancaster, is a +modification of the standard ROMs of the Sinclair ZX Spectrum +2a/+3 to allow +them to use large capacity storage interfaces in the same way as a normal 3" +floppy disc. There are several storage devices supported, including the DivIDE, +ZXCF, ZXATASP, with each device requiring a different set of ROMs. There are +additional alterations to the ROMs including bugfixes and "windowed" BASIC text +output. It's a really useful modification, and easy to implement (with only two +EPROMs). + +Currently the +3e uses a custom format for the discs, PLUSIDEDOS, which allows +a single disk to be partitioned into several distinct areas conforming to the +theoretical +3DOS limit of a 32Mb partition size. Because of this it can be +tricky to swap files between the ZX Spectrum and a PC. This is where 3eExplorer +comes in. It allows you to open an HDF image file of a PLUSIDEDOS disk and +browse the files stored on it. Currently it is read-only, allowing you to +extract files from the image into a number of formats on your PC. Importing new +files is a planned addition. It is also planned to support more image-types such +as .dsk. + + + + +Supported formats: +================== + +Image Formats: +-------------- ++ HDF v1.0/v1.1 + + PLUSIDEDOS + + 0x03 : +3DOS Partitions + +Export Formats: +--------------- ++ TAP / Headerless TAP ++ BIN + +Import Formats: +--------------- +None. Yet. + +Notes: +------ +While the image file is opened read-only, it wouldn't hurt to back it up first. +This is a beta application after all. + + + +Contact: +======== +Author: Robert 'icabod' Cooper +Email: icabod@raww.org +WWW: http://www.icabod.org/3eExplorer/ + + + +History: +======== + +v0.1.0.2 - 06/08/2007 +------ +- Fixed reading 16-bit images from 8-bit devices. + + +v0.1.0.1 - 03/08/2007 - first public beta release +------ ++ Added additional menu items for extraction. ++ Added extract to binary file. ++ Added option to extract with/without +3dos header. ++ Added File Status dialog, allowing you to flush the cache. ++ Added some code that's not complete so you should never see it :] ++ Added an application icon. ++ Much code shuffling. + + +v0.1.0.0 - 31/07/2007 - non-public beta release +------ ++ First release. + + + + +Documentation produced on: \ No newline at end of file diff --git a/3eExplorerDoc.cpp b/3eExplorerDoc.cpp new file mode 100644 index 0000000..436fbfa --- /dev/null +++ b/3eExplorerDoc.cpp @@ -0,0 +1,347 @@ +// 3eExplorerDoc.cpp : implementation of the CMy3eExplorerDoc class +// + +#include "stdafx.h" +#include "3eExplorer.h" + +#include "3eExplorerDoc.h" +#include "DocumentStatus.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + + +CCHS::CCHS(unsigned int _c, unsigned int _h, unsigned int _s) +: c(_c), h(_h), s(_s) +{ +} + + + +///////////////////////////////////////////////////////////////////////////// +// CMy3eExplorerDoc + +IMPLEMENT_DYNCREATE(CMy3eExplorerDoc, CDocument) + +BEGIN_MESSAGE_MAP(CMy3eExplorerDoc, CDocument) + //{{AFX_MSG_MAP(CMy3eExplorerDoc) + ON_COMMAND(ID_FILE_FILESTATUS, OnFileFilestatus) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CMy3eExplorerDoc construction/destruction + +CMy3eExplorerDoc::CMy3eExplorerDoc() +: m_pFile(NULL) +, m_pHdfHeader(NULL) +{ + // TODO: add one-time construction code here + +} + +CMy3eExplorerDoc::~CMy3eExplorerDoc() +{ +} + +BOOL CMy3eExplorerDoc::OnNewDocument() +{ + //if (!CDocument::OnNewDocument()) + // return FALSE; + + // TODO: add reinitialization code here + // (SDI documents will reuse this document) + + return FALSE; +} + + + +///////////////////////////////////////////////////////////////////////////// +// CMy3eExplorerDoc serialization + +void CMy3eExplorerDoc::Serialize(CArchive& ar) +{ + if (ar.IsStoring()) + { + // TODO: add storing code here + } + else + { + // TODO: add loading code here + } +} + +///////////////////////////////////////////////////////////////////////////// +// CMy3eExplorerDoc diagnostics + +#ifdef _DEBUG +void CMy3eExplorerDoc::AssertValid() const +{ + CDocument::AssertValid(); +} + +void CMy3eExplorerDoc::Dump(CDumpContext& dc) const +{ + CDocument::Dump(dc); +} +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CMy3eExplorerDoc commands + +BOOL CMy3eExplorerDoc::OnOpenDocument(LPCTSTR lpszPathName) +{ + // we want to override the default stuff, as we want to keep the file open and cache it as we go + + // so we'll do pretty much the default handling + if (IsModified()) + TRACE0("Warning: OnOpenDocument replaces an unsaved document.\n"); + + CFileException fe; + CFile* pFile = GetFile(lpszPathName, + CFile::modeRead|CFile::shareDenyWrite|CFile::typeBinary, &fe); + if (pFile == NULL) + { + ReportSaveLoadException(lpszPathName, &fe, + FALSE, AFX_IDP_FAILED_TO_OPEN_DOC); + return FALSE; + } + + DeleteContents(); + SetModifiedFlag(); // dirty during de-serialize + + // store the filename + std::string filename = lpszPathName; + std::string::size_type pos = filename.find_last_of("\\/"); + if (std::string::npos == pos) + pos = 0; + else + pos++; + + m_sFileName = filename.substr(pos); + + // store the file pointer + m_pFile = pFile; + + ASSERT (m_pHdfHeader == NULL); + // read in a block of the image and verify it + + try + { + char first_block[0x216]; + UINT read = pFile->Read(first_block, 0x216); + m_pHdfHeader = new CHdfHeader(this, first_block, read, m_sFileName.c_str()); + } + catch (CHdfHeader::EHdfInvalid &e) + { + ::AfxMessageBox(e.what(), MB_ICONEXCLAMATION | MB_OK); + return FALSE; + } + catch (CHdfHeader::EHdfUnsupported &e) + { + ::AfxMessageBox(e.what(), MB_ICONINFORMATION | MB_OK); + return FALSE; + } + catch (CFileException *e) + { + ReportSaveLoadException(lpszPathName, e, FALSE, IDS_FILE_ACCESS_ERROR); + return FALSE; + } + + SetModifiedFlag(FALSE); // start off with unmodified + + return TRUE; +} + +void CMy3eExplorerDoc::DeleteContents() +{ + // Here we need to release the file and delete any allocated memory + if (NULL != m_pFile) + { + m_pFile->Close(); + ReleaseFile(m_pFile, TRUE); + m_pFile = NULL; + } + + // step through the cache and free up memory + for (TCacheIterator it = m_sector_cache.begin(); it != m_sector_cache.end(); it++) + { + delete [] (*it).second; + } + + m_sector_cache.clear(); + + // now delete the object tree we have created. + if (NULL != m_pHdfHeader) + { + delete m_pHdfHeader; + m_pHdfHeader = NULL; + } + + CDocument::DeleteContents(); +} + +BOOL CMy3eExplorerDoc::OnSaveDocument(LPCTSTR lpszPathName) +{ + // TODO: Add your specialized code here and/or call the base class + + //return CDocument::OnSaveDocument(lpszPathName); + + // DON'T SAVE! + return 0; +} + +unsigned int CMy3eExplorerDoc::ReadSector(const unsigned int _zz, char ** _buff) +{ + ASSERT (m_pFile != NULL); + ASSERT (m_pHdfHeader != NULL); + + unsigned int sector_size = m_pHdfHeader->getSectorSize(); + + // first see if this sector is in our cache... + TCacheIterator it = m_sector_cache.find(_zz); + + if (it == m_sector_cache.end()) + { + // we could be reading a word and using a byte, you know... + unsigned int read_size = sector_size; + if (m_pHdfHeader->UsingHalfWord()) + read_size *= 2; + + // need to allocate another sector in memory + char * temp_buffer = new char[read_size]; + + // read from the file into the buffer + LONG byte_offset = m_pHdfHeader->getOffset() + (_zz * read_size); + + try + { + m_pFile->Seek(byte_offset, CFile::begin); + if (read_size != m_pFile->Read(temp_buffer, read_size)) + { + // couldn't read everything - throw an exception + std::ostringstream except; + except << "Error reading sector " << _zz << ". File may be truncated"; + throw EInvalidSector(except.str()); + } + } + catch (CFileException *e) + { + // for now just rethrow + throw e; + } + + if (m_pHdfHeader->UsingHalfWord()) + { + char * copy_sector = new char[sector_size]; + // now copy every other byte + for (int ccount = 0; ccount < sector_size; ccount++) + { + copy_sector[ccount] = temp_buffer[2 * ccount]; + } + m_sector_cache[_zz] = copy_sector; + delete [] temp_buffer; + } + else + { + m_sector_cache[_zz] = temp_buffer; + } + it = m_sector_cache.find(_zz); + + ASSERT (it != m_sector_cache.end()); + } + + // just for fun, see what happens if we try to set the status... + + *_buff = (*it).second; + + return sector_size; +} + +unsigned int CMy3eExplorerDoc::ReadSector(CCHS _chs, char ** _buff) +{ + // work out the sector number and call the other method + ASSERT (m_pHdfHeader != NULL); + + unsigned int cc = m_pHdfHeader->getCylinders(); + unsigned int hh = m_pHdfHeader->getHeads(); + unsigned int ss = m_pHdfHeader->getSectors(); + + if (_chs.c > m_pHdfHeader->getCylinders()) + { + std::ostringstream except; + except << "Cylinder count error : " << _chs.c << " > " << m_pHdfHeader->getCylinders(); + throw EInvalidSector(except.str()); + } + if (_chs.h > m_pHdfHeader->getHeads()) + { + std::ostringstream except; + except << "Head count error : " << _chs.h << " > " << m_pHdfHeader->getHeads(); + throw EInvalidSector(except.str()); + } + if (_chs.s > m_pHdfHeader->getSectors()) + { + std::ostringstream except; + except << "Sector count error : " << _chs.s << " > " << m_pHdfHeader->getSectors(); + throw EInvalidSector(except.str()); + } + + // see if we're doing a cache of a head a time... + if (theApp.HeadCache()) + { + // do a precache of all the sectors in this head + unsigned int presec = (_chs.h * ss) + (_chs.c * hh); + unsigned int presec_max = (presec + ss); + for (; presec < presec_max; presec++) + { + ReadSector(presec, _buff); + } + } + + unsigned int sector = (_chs.s) + (_chs.h * ss) + (_chs.c * (hh * ss)); + + m_cur_chs = _chs; + + return ReadSector(sector, _buff); +} + +unsigned int CMy3eExplorerDoc::ReadNextSector(char ** _buff) +{ + if (++m_cur_chs.s >= m_pHdfHeader->getSectors()) + { + m_cur_chs.s = 0; + if (++m_cur_chs.h >= m_pHdfHeader->getHeads()) + { + m_cur_chs.h = 0; + if (++m_cur_chs.c >= m_pHdfHeader->getCylinders()) + { + m_cur_chs.c = 0; + } + } + } + + return ReadSector(m_cur_chs, _buff); +} + +void CMy3eExplorerDoc::OnFileFilestatus() +{ + // here we want to open the dialog that gives us some status information about this document + CDocumentStatus docStatus; + docStatus.SetExplorerDoc(this); + docStatus.DoModal(); +} + +void CMy3eExplorerDoc::FlushCache() +{ + // step through the cache and free up memory + for (TCacheIterator it = m_sector_cache.begin(); it != m_sector_cache.end(); it++) + { + delete [] (*it).second; + } + + m_sector_cache.clear(); +} diff --git a/3eExplorerDoc.h b/3eExplorerDoc.h new file mode 100644 index 0000000..cf1e24c --- /dev/null +++ b/3eExplorerDoc.h @@ -0,0 +1,96 @@ +// 3eExplorerDoc.h : interface of the CMy3eExplorerDoc class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_3EEXPLORERDOC_H__1D2D4AB6_9635_483C_AAA3_F36ADAC7439F__INCLUDED_) +#define AFX_3EEXPLORERDOC_H__1D2D4AB6_9635_483C_AAA3_F36ADAC7439F__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "HdfHeader.h" + + +class CCHS +{ +public: + CCHS(unsigned int _c = 0, unsigned int _h = 0, unsigned int _s = 0); + unsigned int c; + unsigned int h; + unsigned int s; +}; + +class CMy3eExplorerDoc : public CDocument +{ +public: + typedef std::map TCache; + typedef std::map::iterator TCacheIterator; + +protected: // create from serialization only + CMy3eExplorerDoc(); + DECLARE_DYNCREATE(CMy3eExplorerDoc) + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CMy3eExplorerDoc) + public: + virtual BOOL OnNewDocument(); + virtual void Serialize(CArchive& ar); + virtual BOOL OnOpenDocument(LPCTSTR lpszPathName); + virtual void DeleteContents(); + virtual BOOL OnSaveDocument(LPCTSTR lpszPathName); + //}}AFX_VIRTUAL + +// Implementation +protected: + unsigned int ReadSector(const unsigned int _zz, char ** _buff); +public: + unsigned int ReadSector(CCHS _chs, char ** _buff); +public: + virtual ~CMy3eExplorerDoc(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +protected: + + class EInvalidSector : public std::runtime_error {public: EInvalidSector(const std::string &what_arg) : std::runtime_error(what_arg) {};}; + +public: + void FlushCache(); + unsigned int ReadNextSector(char ** _buff); + const std::string Filename() const {return m_sFileName;}; + CHdfHeader * GetRoot() {return m_pHdfHeader;}; + + unsigned long int GetCacheSize() {return m_sector_cache.size();}; + +// Generated message map functions +protected: + std::string m_sFileName; + CHdfHeader * m_pHdfHeader; + // This is the cache used to read in the sectors of the .hdf file + TCache m_sector_cache; + CFile * m_pFile; + + CCHS m_cur_chs; + + //{{AFX_MSG(CMy3eExplorerDoc) + afx_msg void OnFileFilestatus(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_3EEXPLORERDOC_H__1D2D4AB6_9635_483C_AAA3_F36ADAC7439F__INCLUDED_) diff --git a/3eExplorerView.cpp b/3eExplorerView.cpp new file mode 100644 index 0000000..5e3bc13 --- /dev/null +++ b/3eExplorerView.cpp @@ -0,0 +1,433 @@ +// 3eExplorerView.cpp : implementation of the CMy3eExplorerView class +// + +#include "stdafx.h" +#include "3eExplorer.h" + +#include "3eExplorerDoc.h" +#include "3eExplorerView.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CMy3eExplorerView + +IMPLEMENT_DYNCREATE(CMy3eExplorerView, CMy3eExplorerViewBase) + +BEGIN_MESSAGE_MAP(CMy3eExplorerView, CMy3eExplorerViewBase) + //{{AFX_MSG_MAP(CMy3eExplorerView) + ON_NOTIFY_REFLECT(TVN_ITEMEXPANDING, OnItemExpanding) + ON_NOTIFY_REFLECT(TVN_SELCHANGED, OnSelectionChanged) + ON_WM_CONTEXTMENU() + ON_COMMAND(ID_EXPLORER_EXTRACT_TO_TAP, OnExplorerExtractToTap) + ON_UPDATE_COMMAND_UI(ID_EXPLORER_EXTRACT_TO_TAP, OnUpdateExplorerExtractToTap) + ON_UPDATE_COMMAND_UI(ID_EXPLORER_FILENAME, OnUpdateExplorerFilename) + ON_UPDATE_COMMAND_UI(ID_EXPLORER_EXTRACT_TO_BIN, OnUpdateExplorerExtractToBin) + ON_COMMAND(ID_EXPLORER_EXTRACT_TO_BIN, OnExplorerExtractToBin) + ON_NOTIFY_REFLECT(NM_RCLICK, OnRClick) + ON_UPDATE_COMMAND_UI(ID_EXPLORER_EXTRACT_TO_BIN_WITH_HEADER, OnUpdateExplorerExtractToBinWithHeader) + ON_COMMAND(ID_EXPLORER_EXTRACT_TO_BIN_WITH_HEADER, OnExplorerExtractToBinWithHeader) + //}}AFX_MSG_MAP + ON_WM_INITMENUPOPUP() +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CMy3eExplorerView construction/destruction + +CMy3eExplorerView::CMy3eExplorerView() +: m_objActive(NULL) +, m_htActive(0) +{ + // map the possible type to their respective images + m_ilmap[CTreeObject::INVALID] = 0; + m_ilmap[CTreeObject::HDF] = 1; + m_ilmap[CTreeObject::PARTITION_PLUSIDEDOS] = 2; + m_ilmap[CTreeObject::PARTITION_PLUS3DOS] = 3; + m_ilmap[CTreeObject::PLUS3DOS_USER_DIRECTORY] = 4; + m_ilmap[CTreeObject::PLUS3DOS_USER_DIRECTORY_ERASED] = 5; + m_ilmap[CTreeObject::PLUS3DOS_FILE] = 6; + m_ilmap[CTreeObject::PLUS3DOS_FILE_ERASED] = 7; + m_ilmap[CTreeObject::PLUS3DOS_FILE_PROGRAM] = 8; + m_ilmap[CTreeObject::PLUS3DOS_FILE_NUMARRAY] = 9; + m_ilmap[CTreeObject::PLUS3DOS_FILE_CHARARRAY] = 10; + m_ilmap[CTreeObject::PLUS3DOS_FILE_CODE] = 11; + m_ilmap[CTreeObject::PLUS3DOS_FILE_HEADERLESS] = 12; + m_ilmap[CTreeObject::RESERVED] = 0; +} + +CMy3eExplorerView::~CMy3eExplorerView() +{ +} + +BOOL CMy3eExplorerView::PreCreateWindow(CREATESTRUCT& cs) +{ + // TODO: Modify the Window class or styles here by modifying + // the CREATESTRUCT cs + + if (!CMy3eExplorerViewBase::PreCreateWindow(cs)) + return FALSE; + + cs.style |= TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS | TVS_SHOWSELALWAYS | TVS_DISABLEDRAGDROP; + + return TRUE; +} + +///////////////////////////////////////////////////////////////////////////// +// CMy3eExplorerView drawing + +void CMy3eExplorerView::OnDraw(CDC* pDC) +{ + CMy3eExplorerDoc* pDoc = GetDocument(); + ASSERT_VALID(pDoc); + // TODO: add draw code for native data here +} + +///////////////////////////////////////////////////////////////////////////// +// CMy3eExplorerView diagnostics + +#ifdef _DEBUG +void CMy3eExplorerView::AssertValid() const +{ + CView::AssertValid(); +} + +void CMy3eExplorerView::Dump(CDumpContext& dc) const +{ + CView::Dump(dc); +} + +CMy3eExplorerDoc* CMy3eExplorerView::GetDocument() // non-debug version is inline +{ + ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMy3eExplorerDoc))); + return (CMy3eExplorerDoc*)m_pDocument; +} +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CMy3eExplorerView message handlers + +void CMy3eExplorerView::OnInitialUpdate() +{ + CMy3eExplorerViewBase::OnInitialUpdate(); + + // set up the images + m_ilImageList.Create(16, 15, ILC_COLOR24 | ILC_MASK, 1, 1); + m_bmpImageList.LoadBitmap(IDB_TREE_ICONS); + m_bmpImageListMask.LoadBitmap(IDB_TREE_ICONS_MASK); + m_ilImageList.Add(&m_bmpImageList, &m_bmpImageListMask); + + GetTreeCtrl().SetImageList(&m_ilImageList, TVSIL_NORMAL); + + CMy3eExplorerDoc *pDoc = GetDocument(); + + CHdfHeader *pHdf = pDoc->GetRoot(); + + HTREEITEM hItem = GetTreeCtrl().InsertItem(pHdf->GetName().c_str(), m_ilmap[pHdf->Type()], m_ilmap[pHdf->Type()]); + + // set a couple things... + GetTreeCtrl().SetItemData(hItem, (DWORD)pHdf); + + GetTreeCtrl().Select(hItem, TVGN_CARET); + + if (pHdf->IsExpandable()) + { + // add a null item so we get the "expand" box... + hItem = GetTreeCtrl().InsertItem(_T(""), m_ilmap[CTreeObject::INVALID], m_ilmap[CTreeObject::INVALID], hItem); + GetTreeCtrl().SetItemData(hItem, 0); + } + + pDoc->UpdateAllViews(this, 0x01, pHdf); +} + +void CMy3eExplorerView::OnItemExpanding(NMHDR* pNMHDR, LRESULT* pResult) +{ + NM_TREEVIEW *pNMTV = (NM_TREEVIEW*) pNMHDR; + HTREEITEM hItem = pNMTV->itemNew.hItem; + CTreeObject *pObj = (CTreeObject*)(GetTreeCtrl().GetItemData(hItem)); + + ASSERT (NULL != pObj); + + // if we're expanding the tree, check to see if we need to parse the contents... + if (NULL != pObj && TVE_EXPAND == pNMTV->action) + { + // if the first child has NULL data it means we've not parsed it yet. + HTREEITEM hChildItem = GetTreeCtrl().GetChildItem(hItem); + if (NULL != hChildItem) + { + DWORD item_data = GetTreeCtrl().GetItemData(hChildItem); + if (NULL == item_data) + { + GetTreeCtrl().DeleteItem(hChildItem); + + // force the object to parse for any children + pObj->ParseForChildren(); + + // now loop through the child objects and add them... + std::vector::iterator it = pObj->GetChildren(); + while (pObj->GetChildrenEnd() != it) + { + CTreeObject * pChild = (*it); + HTREEITEM hChild = GetTreeCtrl().InsertItem(pChild->GetName().c_str(), m_ilmap[pChild->Type()], m_ilmap[pChild->Type()], hItem); + GetTreeCtrl().SetItemData(hChild, (DWORD)pChild); + + if (pChild->IsExpandable()) + { + // add an expander. we should alter this to have an "expandable" flag + hChild = GetTreeCtrl().InsertItem(_T(""), m_ilmap[CTreeObject::INVALID], m_ilmap[CTreeObject::INVALID], hChild); + GetTreeCtrl().SetItemData(hChild, 0); + } + ++it; + } + } + } + + } +} + +void CMy3eExplorerView::OnSelectionChanged(NMHDR* pNMHDR, LRESULT* pResult) +{ + // the selection has changed, so update all the views with the new object. + NM_TREEVIEW *pNMTV = (NM_TREEVIEW*) pNMHDR; + + // update the current item + m_htActive = pNMTV->itemNew.hItem; + m_objActive = (CTreeObject*)(GetTreeCtrl().GetItemData(m_htActive)); + + GetDocument()->UpdateAllViews(this, 0x01, m_objActive); +} + +void CMy3eExplorerView::OnContextMenu(CWnd* pWnd, CPoint point) +{ + // check to see if this was generated from the kb (Shift-F10) + HTREEITEM htItem = NULL; + + if (-1 == point.x && -1 == point.y) + { + //point = (CPoint) GetMessagePos(); + // work out which item is selected... + htItem = m_htActive; + + // ok, where is it on the screen? + CRect rc; + if (!GetTreeCtrl().GetItemRect(htItem, &rc, TRUE)) + { + rc.SetRect(0, 0, 0, 0); + } + + // ok, ensure the rect we have at least intersects the client area + CRect client_rc; + GetClientRect(&client_rc); + + if (rc.top < client_rc.top) rc.top = client_rc.top; + if (rc.left < client_rc.left) rc.left = client_rc.left; + + if (rc.left > client_rc.Width()) rc.left = 0; + if (rc.top > client_rc.Height()) rc.top = 0; + + // ok, we have a position in the control, now set the menu to pop up in the middle. + point.x = rc.left; + point.y = rc.top; + } + else + { + ScreenToClient(&point); + + UINT uFlags; + htItem = GetTreeCtrl().HitTest(point, &uFlags); + } + + if (NULL != htItem) + { + m_htActive = htItem; + m_objActive = (CTreeObject*)(GetTreeCtrl().GetItemData(m_htActive)); + + if (NULL != m_objActive) + { + CMenu menu; + CMenu* pPopup; + + // which menu do we want? + switch (m_objActive->Type()) + { + case CTreeObject::PLUS3DOS_FILE: + case CTreeObject::PLUS3DOS_FILE_PROGRAM: + case CTreeObject::PLUS3DOS_FILE_NUMARRAY: + case CTreeObject::PLUS3DOS_FILE_CHARARRAY: + case CTreeObject::PLUS3DOS_FILE_CODE: + case CTreeObject::PLUS3DOS_FILE_HEADERLESS: + { + menu.LoadMenu(IDR_EXPLORER_POPUP_FILE); + } + break; + //case CTreeObject::PLUS3DOS_USER_DIRECTORY: + //case CTreeObject::PLUS3DOS_USER_DIRECTORY_ERASED: + // { + // menu.LoadMenu(IDR_EXPLORER_POPUP_FILE); + // } + // break; + default: + { + menu.LoadMenu(IDR_EXPLORER_POPUP); + } + break; + } + + pPopup = menu.GetSubMenu(0); + + ClientToScreen(&point); + pPopup->TrackPopupMenu(TPM_LEFTALIGN, point.x, point.y, this); + } + } +} + +void CMy3eExplorerView::OnRClick(NMHDR *pNMHDR, LRESULT *pResult) +{ + // generate the context-menu message to self + SendMessage(WM_CONTEXTMENU, (WPARAM) m_hWnd, GetMessagePos()); + // stop this being handled again + *pResult = 1; +} + +void CMy3eExplorerView::OnExplorerExtractToTap() +{ + if (NULL != m_objActive) + { + m_objActive->ExtractObject(CExplorerFile::TAPFILE); + } +} + + +void CMy3eExplorerView::OnUpdateExplorerExtractToTap(CCmdUI* pCmdUI) +{ + if (NULL != m_objActive && m_objActive->IsExtractable()) + pCmdUI->Enable(TRUE); + else + pCmdUI->Enable(FALSE); +} + +void CMy3eExplorerView::OnInitMenuPopup(CMenu *pPopupMenu, UINT nIndex, BOOL bSysMenu) +{ + // Annoyingly, we need to handle this and do what the FrameWnd would do, + // otherwise we won't get the ON_UPDATE_COMMAND_UI calls. + + ASSERT(NULL != pPopupMenu); + + CCmdUI state; + state.m_pMenu = pPopupMenu; + ASSERT (NULL == state.m_pOther); + ASSERT (NULL == state.m_pParentMenu); + + // is this a popup in top-level menu? + HMENU hParentMenu; + if (AfxGetThreadState()->m_hTrackingMenu == pPopupMenu->m_hMenu) + { + // set parent = child + state.m_pParentMenu = pPopupMenu; + } + else if (NULL != (hParentMenu = ::GetMenu(m_hWnd))) + { + CWnd *pParent = this; + if (NULL != pParent && NULL != (hParentMenu = ::GetMenu(pParent->m_hWnd))) + { + int nIndexMax = ::GetMenuItemCount(hParentMenu); + for (int nIndex = 0; nIndex < nIndexMax; nIndex++) + { + if (::GetSubMenu(hParentMenu, nIndex) == pPopupMenu->m_hMenu) + { + state.m_pParentMenu = CMenu::FromHandle(hParentMenu); + break; + } + } + } + } + + // ok, done that stuff... + // now let's go through the actual menu items + state.m_nIndexMax = pPopupMenu->GetMenuItemCount(); + + for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax; state.m_nIndex++) + { + state.m_nID = pPopupMenu->GetMenuItemID(state.m_nIndex); + if (0 == state.m_nID) + continue; + + ASSERT(NULL == state.m_pOther); + ASSERT(NULL != state.m_pMenu); + + if ((UINT)-1 == state.m_nID) + { + // possibly a popup + state.m_pSubMenu = pPopupMenu->GetSubMenu(state.m_nIndex); + if (NULL == state.m_pSubMenu || + 0 == (state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) || + (UINT)-1 == state.m_nID) + { + continue; + } + state.DoUpdate(this, TRUE); + } + else + { + // normal menu item + state.m_pSubMenu = NULL; + state.DoUpdate(this, FALSE); + } + + // adjust for menu additions and deletions + UINT nCount = pPopupMenu->GetMenuItemCount(); + if (nCount < state.m_nIndexMax) + { + state.m_nIndex -= (state.m_nIndexMax - nCount); + while (state.m_nIndex < nCount && + pPopupMenu->GetMenuItemID(state.m_nIndex) == state.m_nID) + { + state.m_nIndex++; + } + } + + state.m_nIndexMax = nCount; + } +} + +void CMy3eExplorerView::OnUpdateExplorerFilename(CCmdUI* pCmdUI) +{ + if (NULL != m_objActive) + pCmdUI->SetText(m_objActive->GetName().c_str()); + pCmdUI->Enable(FALSE); +} + +void CMy3eExplorerView::OnUpdateExplorerExtractToBin(CCmdUI* pCmdUI) +{ + if (NULL != m_objActive && m_objActive->IsExtractable()) + pCmdUI->Enable(TRUE); + else + pCmdUI->Enable(FALSE); +} + +void CMy3eExplorerView::OnExplorerExtractToBin() +{ + if (NULL != m_objActive) + { + m_objActive->ExtractObject(CExplorerFile::BINFILE); + } +} + +void CMy3eExplorerView::OnUpdateExplorerExtractToBinWithHeader(CCmdUI* pCmdUI) +{ + if (NULL != m_objActive && m_objActive->IsExtractable() && CTreeObject::PLUS3DOS_FILE_HEADERLESS != m_objActive->Type()) + pCmdUI->Enable(TRUE); + else + pCmdUI->Enable(FALSE); +} + +void CMy3eExplorerView::OnExplorerExtractToBinWithHeader() +{ + if (NULL != m_objActive) + { + m_objActive->ExtractObject(CExplorerFile::BINFILE, true); + } +} diff --git a/3eExplorerView.h b/3eExplorerView.h new file mode 100644 index 0000000..b9a8f61 --- /dev/null +++ b/3eExplorerView.h @@ -0,0 +1,88 @@ +// 3eExplorerView.h : interface of the CMy3eExplorerView class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_3EEXPLORERVIEW_H__5BADFA8E_4E79_41B7_84F2_F99FA85F1E1E__INCLUDED_) +#define AFX_3EEXPLORERVIEW_H__5BADFA8E_4E79_41B7_84F2_F99FA85F1E1E__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "3eExplorerDoc.h" + + +#define CMy3eExplorerViewBase CTreeView + + +class CMy3eExplorerView : public CMy3eExplorerViewBase +{ +protected: // create from serialization only + CMy3eExplorerView(); + DECLARE_DYNCREATE(CMy3eExplorerView) + +// Attributes +public: + CMy3eExplorerDoc* GetDocument(); + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CMy3eExplorerView) + public: + virtual void OnDraw(CDC* pDC); // overridden to draw this view + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + virtual void OnInitialUpdate(); + //}}AFX_VIRTUAL + +// Implementation +public: + void OnInitMenuPopup(CMenu *pPopupMenu, UINT nIndex,BOOL bSysMenu); + void OnRClick(NMHDR* pNMHDR, LRESULT* pResult); + virtual ~CMy3eExplorerView(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +protected: + +// Generated message map functions +protected: + CImageList m_ilImageList; + CBitmap m_bmpImageList; + CBitmap m_bmpImageListMask; + CBitmap m_bmpImageList_Selected; + std::map m_ilmap; + + HTREEITEM m_htActive; + CTreeObject *m_objActive; + + //{{AFX_MSG(CMy3eExplorerView) + afx_msg void OnItemExpanding(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnSelectionChanged(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnContextMenu(CWnd* pWnd, CPoint point); + afx_msg void OnExplorerExtractToTap(); + afx_msg void OnUpdateExplorerExtractToTap(CCmdUI* pCmdUI); + afx_msg void OnUpdateExplorerFilename(CCmdUI* pCmdUI); + afx_msg void OnUpdateExplorerExtractToBin(CCmdUI* pCmdUI); + afx_msg void OnExplorerExtractToBin(); + afx_msg void OnUpdateExplorerExtractToBinWithHeader(CCmdUI* pCmdUI); + afx_msg void OnExplorerExtractToBinWithHeader(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +#ifndef _DEBUG // debug version in 3eExplorerView.cpp +inline CMy3eExplorerDoc* CMy3eExplorerView::GetDocument() + { return (CMy3eExplorerDoc*)m_pDocument; } +#endif + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_3EEXPLORERVIEW_H__5BADFA8E_4E79_41B7_84F2_F99FA85F1E1E__INCLUDED_) diff --git a/ChildFrm.cpp b/ChildFrm.cpp new file mode 100644 index 0000000..ce1df4c --- /dev/null +++ b/ChildFrm.cpp @@ -0,0 +1,119 @@ +// ChildFrm.cpp : implementation of the CChildFrame class +// + +#include "stdafx.h" +#include "3eExplorer.h" + +#include "ChildFrm.h" + +#include "3eExplorerView.h" +#include "DetailView.h" +#include "ContentView.h" +#include "StatusView.h" +#include "DiskAllocationView.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CChildFrame + +IMPLEMENT_DYNCREATE(CChildFrame, CMDIChildWnd) + +BEGIN_MESSAGE_MAP(CChildFrame, CMDIChildWnd) + //{{AFX_MSG_MAP(CChildFrame) + ON_WM_SIZE() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CChildFrame construction/destruction + +CChildFrame::CChildFrame() +{ + // TODO: add member initialization code here + +} + +CChildFrame::~CChildFrame() +{ +} + +BOOL CChildFrame::OnCreateClient( LPCREATESTRUCT lpcs, + CCreateContext* pContext) +{ + // work out the sizes according to the client size. + int explorerview_cx = 256; + int contentview_cy = lpcs->cy / 2; + + if (lpcs->cx < 512) + { + explorerview_cx = lpcs->cx / 2; + } + + if (!m_wndSplitter.CreateStatic(this, 1, 2) || + !m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CMy3eExplorerView), CSize(explorerview_cx, 0), pContext) || + !m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CDetailView), CSize(0, 0), pContext)) + return FALSE; + else + return TRUE; + + //if (!m_wndSplitter.CreateStatic(this, 1, 2) || + // !m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CMy3eExplorerView), CSize(explorerview_cx, 0), pContext) || + // !m_wndSplitterSub1.CreateStatic(&m_wndSplitter, 2, 1, WS_CHILD | WS_VISIBLE, m_wndSplitter.IdFromRowCol(0, 1)) || + // !m_wndSplitterSub1.CreateView(0, 0, RUNTIME_CLASS(CDetailView), CSize(0, contentview_cy), pContext) || + // !m_wndSplitterSub1.CreateView(1, 0, RUNTIME_CLASS(CStatusView), CSize(0, contentview_cy), pContext)) + // return FALSE; + //else + // return TRUE; +} + +BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs) +{ + // TODO: Modify the Window class or styles here by modifying + // the CREATESTRUCT cs + + if( !CMDIChildWnd::PreCreateWindow(cs) ) + return FALSE; + + cs.style = WS_CHILD | WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU + | FWS_ADDTOTITLE | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; + + return TRUE; +} + +void CChildFrame::ActivateFrame(int nCmdShow) +{ + // TODO: Modify this function to change how the frame is activated. + + nCmdShow = SW_SHOWMAXIMIZED; + CMDIChildWnd::ActivateFrame(nCmdShow); +} + + +///////////////////////////////////////////////////////////////////////////// +// CChildFrame diagnostics + +#ifdef _DEBUG +void CChildFrame::AssertValid() const +{ + CMDIChildWnd::AssertValid(); +} + +void CChildFrame::Dump(CDumpContext& dc) const +{ + CMDIChildWnd::Dump(dc); +} + +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CChildFrame message handlers + +void CChildFrame::OnSize(UINT nType, int cx, int cy) +{ + CMDIChildWnd::OnSize(nType, cx, cy); +} diff --git a/ChildFrm.h b/ChildFrm.h new file mode 100644 index 0000000..1b74429 --- /dev/null +++ b/ChildFrm.h @@ -0,0 +1,59 @@ +// ChildFrm.h : interface of the CChildFrame class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_CHILDFRM_H__DC9AEAAA_C3CE_4B0F_9919_ED35D863CAF2__INCLUDED_) +#define AFX_CHILDFRM_H__DC9AEAAA_C3CE_4B0F_9919_ED35D863CAF2__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + + +class CChildFrame : public CMDIChildWnd +{ + DECLARE_DYNCREATE(CChildFrame) +public: + CChildFrame(); + +// Attributes +protected: + CSplitterWnd m_wndSplitter; + CSplitterWnd m_wndSplitterSub1; + CSplitterWnd m_wndSplitterSub2; +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CChildFrame) + public: + virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext); + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + virtual void ActivateFrame(int nCmdShow); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CChildFrame(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +// Generated message map functions +protected: + //{{AFX_MSG(CChildFrame) + afx_msg void OnSize(UINT nType, int cx, int cy); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_CHILDFRM_H__DC9AEAAA_C3CE_4B0F_9919_ED35D863CAF2__INCLUDED_) diff --git a/ContentView.cpp b/ContentView.cpp new file mode 100644 index 0000000..4f4ad36 --- /dev/null +++ b/ContentView.cpp @@ -0,0 +1,59 @@ +// ContentView.cpp : implementation file +// + +#include "stdafx.h" +#include "3eExplorer.h" +#include "ContentView.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CContentView + +IMPLEMENT_DYNCREATE(CContentView, CListView) + +CContentView::CContentView() +{ +} + +CContentView::~CContentView() +{ +} + + +BEGIN_MESSAGE_MAP(CContentView, CListView) + //{{AFX_MSG_MAP(CContentView) + // NOTE - the ClassWizard will add and remove mapping macros here. + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CContentView drawing + +void CContentView::OnDraw(CDC* pDC) +{ + CDocument* pDoc = GetDocument(); + // TODO: add draw code here +} + +///////////////////////////////////////////////////////////////////////////// +// CContentView diagnostics + +#ifdef _DEBUG +void CContentView::AssertValid() const +{ + CListView::AssertValid(); +} + +void CContentView::Dump(CDumpContext& dc) const +{ + CListView::Dump(dc); +} +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CContentView message handlers diff --git a/ContentView.h b/ContentView.h new file mode 100644 index 0000000..fa3c8b8 --- /dev/null +++ b/ContentView.h @@ -0,0 +1,53 @@ +#if !defined(AFX_CONTENTVIEW_H__97FE80B9_799E_4AA4_B920_B8076B565A23__INCLUDED_) +#define AFX_CONTENTVIEW_H__97FE80B9_799E_4AA4_B920_B8076B565A23__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// ContentView.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CContentView view + +class CContentView : public CListView +{ +protected: + CContentView(); // protected constructor used by dynamic creation + DECLARE_DYNCREATE(CContentView) + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CContentView) + protected: + virtual void OnDraw(CDC* pDC); // overridden to draw this view + //}}AFX_VIRTUAL + +// Implementation +protected: + virtual ~CContentView(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + + // Generated message map functions +protected: + //{{AFX_MSG(CContentView) + // NOTE - the ClassWizard will add and remove member functions here. + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_CONTENTVIEW_H__97FE80B9_799E_4AA4_B920_B8076B565A23__INCLUDED_) diff --git a/DetailView.cpp b/DetailView.cpp new file mode 100644 index 0000000..39fca19 --- /dev/null +++ b/DetailView.cpp @@ -0,0 +1,91 @@ +// DetailView.cpp : implementation file +// + +#include "stdafx.h" +#include "3eExplorer.h" +#include "DetailView.h" + +#include "TreeObject.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CDetailView + +IMPLEMENT_DYNCREATE(CDetailView, CEditView) + +CDetailView::CDetailView() +{ +} + +CDetailView::~CDetailView() +{ +} + + +BEGIN_MESSAGE_MAP(CDetailView, CEditView) + //{{AFX_MSG_MAP(CDetailView) + ON_WM_CREATE() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CDetailView drawing + +void CDetailView::OnDraw(CDC* pDC) +{ + CDocument* pDoc = GetDocument(); + // TODO: add draw code here +} + +///////////////////////////////////////////////////////////////////////////// +// CDetailView diagnostics + +#ifdef _DEBUG +void CDetailView::AssertValid() const +{ + CEditView::AssertValid(); +} + +void CDetailView::Dump(CDumpContext& dc) const +{ + CEditView::Dump(dc); +} +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CDetailView message handlers + +void CDetailView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) +{ + if (lHint) + { + CTreeObject *pObj = (CTreeObject*)pHint; + + // wipe the text. + GetEditCtrl().SetWindowText(_T("No Information Available")); + + if (NULL != pObj) + { + GetEditCtrl().SetWindowText(pObj->GetInformation().c_str()); + } + } +} + +int CDetailView::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CEditView::OnCreate(lpCreateStruct) == -1) + return -1; + + // set the editbox to be fixed-width text for now + LOGFONT lf; + ::GetObject(GetStockObject(ANSI_FIXED_FONT), sizeof(LOGFONT), &lf); + m_cFont.CreateFontIndirect(&lf); + SetFont(&m_cFont); + + return 0; +} diff --git a/DetailView.h b/DetailView.h new file mode 100644 index 0000000..882b036 --- /dev/null +++ b/DetailView.h @@ -0,0 +1,63 @@ +#if !defined(AFX_DETAILVIEW_H__595342D4_1BA8_4E65_82E4_537126CB8A84__INCLUDED_) +#define AFX_DETAILVIEW_H__595342D4_1BA8_4E65_82E4_537126CB8A84__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// DetailView.h : header file +// + +#include "TreeObject.h" +#include "HdfHeader.h" +#include "IDEDOS_SystemPartition.h" + + +///////////////////////////////////////////////////////////////////////////// +// CDetailView view + +#define CDetailViewBase CEditView + +class CDetailView : public CDetailViewBase +{ +protected: + CDetailView(); // protected constructor used by dynamic creation + DECLARE_DYNCREATE(CDetailView) + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CDetailView) + protected: + virtual void OnDraw(CDC* pDC); // overridden to draw this view + virtual void OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint); + //}}AFX_VIRTUAL + +// Implementation +protected: + virtual ~CDetailView(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + + // Generated message map functions +protected: + CFont m_cFont; + + //{{AFX_MSG(CDetailView) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_DETAILVIEW_H__595342D4_1BA8_4E65_82E4_537126CB8A84__INCLUDED_) diff --git a/DiskAllocationView.cpp b/DiskAllocationView.cpp new file mode 100644 index 0000000..a13be0e --- /dev/null +++ b/DiskAllocationView.cpp @@ -0,0 +1,159 @@ +// DiskAllocationView.cpp : implementation file +// + +#include "stdafx.h" +#include "3eExplorer.h" +#include "DiskAllocationView.h" +#include "3eExplorerDoc.h" +#include "memdc.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CDiskAllocationView + +IMPLEMENT_DYNCREATE(CDiskAllocationView, CScrollView) + +CDiskAllocationView::CDiskAllocationView() +: m_objActive(NULL) +, m_csBlockScale(3,4) +{ +} + +CDiskAllocationView::~CDiskAllocationView() +{ +} + + +BEGIN_MESSAGE_MAP(CDiskAllocationView, CScrollView) + //{{AFX_MSG_MAP(CDiskAllocationView) + ON_WM_ERASEBKGND() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CDiskAllocationView drawing + +void CDiskAllocationView::OnDraw(CDC* pDC) +{ + //CMemDC DC(pDC); + + CMy3eExplorerDoc* pDoc = GetDocument(); + ASSERT_VALID(pDoc); + + // work out how big our grid should be (from the CHS) + CCHS chs; + pDoc->GetRoot()->getCHS(chs); + + // how much are we updating? + CRect rcClip; + pDC->GetClipBox(&rcClip); + //rcClip.OffsetRect(-m_ptBlockOffset); + + unsigned int cyl_start = rcClip.top / m_csBlockScale.cy; + unsigned int cyl_end = (rcClip.top + rcClip.Height()) / m_csBlockScale.cy; + + unsigned int blk_start = rcClip.left / m_csBlockScale.cx; + unsigned int blk_end = (rcClip.left + rcClip.Width()) / m_csBlockScale.cx; + + // alter the end to ensure we're drawing everything. + // it really doesn't matter if we overshoot... better than under, eh? + //cyl_end += 8; + //blk_end += 8; + + // check to see if we're drawing everything... + if (cyl_end > chs.c) + cyl_end = chs.c; + if (blk_end > (chs.h * chs.s)) + blk_end = (chs.h * chs.s); + + // set up our brushes... + CBrush block_empty(RGB(0xf0, 0xf0, 0xf0)); + CBrush block_full(RGB(0xf0, 0x00, 0x00)); + CPen border_empty(PS_SOLID, 1, RGB(0xe0, 0xe0, 0xe0)); + CPen border_full(PS_SOLID, 1, RGB(0xe0, 0x00, 0x00)); + CBrush *pold_brush; + CPen *pold_pen; + + // now step through each block and draw it... + for (int cur_cyl = cyl_start; cur_cyl <= cyl_end; cur_cyl++) + { + for (int cur_blk = blk_start; cur_blk <= blk_end; cur_blk++) + { + // draw the rectangle + CPoint pt(m_csBlockScale.cx * cur_blk, m_csBlockScale.cy * cur_cyl); + CRect rc(pt, m_csBlockScale); + pold_brush = pDC->SelectObject(&block_empty); + pold_pen = pDC->SelectObject(&border_empty); + + pDC->FillRect(rc, &block_empty); + pDC->MoveTo(rc.right - 1, rc.top); + pDC->LineTo(rc.right - 1, rc.bottom - 1); + pDC->LineTo(rc.left, rc.bottom - 1); + + pDC->SelectObject(pold_brush); + pDC->SelectObject(pold_pen); + } + } + + block_empty.DeleteObject(); + block_full.DeleteObject(); + border_empty.DeleteObject(); + border_full.DeleteObject(); +} + +///////////////////////////////////////////////////////////////////////////// +// CDiskAllocationView diagnostics + +#ifdef _DEBUG +void CDiskAllocationView::AssertValid() const +{ + CScrollView::AssertValid(); +} + +void CDiskAllocationView::Dump(CDumpContext& dc) const +{ + CScrollView::Dump(dc); +} + +CMy3eExplorerDoc* CDiskAllocationView::GetDocument() // non-debug version is inline +{ + ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMy3eExplorerDoc))); + return (CMy3eExplorerDoc*)m_pDocument; +} +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CDiskAllocationView message handlers + +void CDiskAllocationView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) +{ + if (lHint) + { + CTreeObject *pObj = (CTreeObject*)pHint; + + if (NULL != pObj) + { + m_objActive = pObj; + + // set the scroll sizes... + CMy3eExplorerDoc* pDoc = GetDocument(); + CCHS chs; + pDoc->GetRoot()->getCHS(chs); + SetScrollSizes(MM_TEXT, CSize((m_csBlockScale.cx * ((chs.h * chs.s))), (m_csBlockScale.cy * (chs.c)))); + + Invalidate(FALSE); + } + } +} + + +BOOL CDiskAllocationView::OnEraseBkgnd(CDC* pDC) +{ + return CScrollView::OnEraseBkgnd(pDC); + //return FALSE; +} diff --git a/DiskAllocationView.h b/DiskAllocationView.h new file mode 100644 index 0000000..465cd48 --- /dev/null +++ b/DiskAllocationView.h @@ -0,0 +1,65 @@ +#if !defined(AFX_DISKALLOCATIONVIEW_H__51F9CB92_E1DC_4950_9652_26D0A956EC8D__INCLUDED_) +#define AFX_DISKALLOCATIONVIEW_H__51F9CB92_E1DC_4950_9652_26D0A956EC8D__INCLUDED_ + +#include "TreeObject.h" // Added by ClassView +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// DiskAllocationView.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CDiskAllocationView view + +class CDiskAllocationView : public CScrollView +{ +protected: + CDiskAllocationView(); // protected constructor used by dynamic creation + DECLARE_DYNCREATE(CDiskAllocationView) + +// Attributes +public: + CMy3eExplorerDoc* GetDocument(); + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CDiskAllocationView) + protected: + virtual void OnDraw(CDC* pDC); // overridden to draw this view + virtual void OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint); + //}}AFX_VIRTUAL + +// Implementation +protected: + virtual ~CDiskAllocationView(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + + // Generated message map functions +protected: + const CSize m_csBlockScale; + CTreeObject *m_objActive; + //{{AFX_MSG(CDiskAllocationView) + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + +#ifndef _DEBUG // debug version in CDiskAllocationView.cpp +inline CMy3eExplorerDoc* CDiskAllocationView::GetDocument() + { return (CMy3eExplorerDoc*)m_pDocument; } +#endif + + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_DISKALLOCATIONVIEW_H__51F9CB92_E1DC_4950_9652_26D0A956EC8D__INCLUDED_) diff --git a/DocumentStatus.cpp b/DocumentStatus.cpp new file mode 100644 index 0000000..eb00b95 --- /dev/null +++ b/DocumentStatus.cpp @@ -0,0 +1,107 @@ +// DocumentStatus.cpp : implementation file +// + +#include "stdafx.h" +#include "3eExplorer.h" +#include "DocumentStatus.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CDocumentStatus dialog + + +CDocumentStatus::CDocumentStatus(CWnd* pParent /*=NULL*/) + : CDialog(CDocumentStatus::IDD, pParent) + , m_pExplorerDoc(NULL) +{ + //{{AFX_DATA_INIT(CDocumentStatus) + //}}AFX_DATA_INIT +} + + +void CDocumentStatus::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CDocumentStatus) + DDX_Control(pDX, IDC_STATUS_LISTBOX, m_Listbox); + //}}AFX_DATA_MAP + + SetStatusInformation(); +} + + +BEGIN_MESSAGE_MAP(CDocumentStatus, CDialog) + //{{AFX_MSG_MAP(CDocumentStatus) + ON_WM_CREATE() + ON_BN_CLICKED(IDC_FLUSH_CACHE, OnFlushCache) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CDocumentStatus message handlers + + +int CDocumentStatus::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CDialog::OnCreate(lpCreateStruct) == -1) + return -1; + + if (NULL != m_pExplorerDoc) + { + SetWindowText(m_pExplorerDoc->GetRoot()->GetName().c_str()); + } + + return 0; +} + +void CDocumentStatus::SetStatusInformation() +{ + // remove the items from the listbox... + while (m_Listbox.GetCount()) + { + m_Listbox.DeleteString(0); + } + + if (NULL != m_pExplorerDoc) + { + m_Listbox.SetTabStops(100); + + std::ostringstream ostr; + ostr << "Cylinders\t" << m_pExplorerDoc->GetRoot()->getCylinders(); + m_Listbox.AddString(ostr.str().c_str()); + ostr.str(""); + + ostr << "Heads/Tracks\t" << m_pExplorerDoc->GetRoot()->getHeads(); + m_Listbox.AddString(ostr.str().c_str()); + ostr.str(""); + + ostr << "Sectors\t" << m_pExplorerDoc->GetRoot()->getSectors(); + m_Listbox.AddString(ostr.str().c_str()); + ostr.str(""); + + ostr << "Sector Size\t" << m_pExplorerDoc->GetRoot()->getSectorSize(); + m_Listbox.AddString(ostr.str().c_str()); + ostr.str(""); + + ostr << "Sectors Cached\t" << m_pExplorerDoc->GetCacheSize(); + m_Listbox.AddString(ostr.str().c_str()); + ostr.str(""); + + ostr << "Cache Memory\t" << (m_pExplorerDoc->GetCacheSize() * m_pExplorerDoc->GetRoot()->getSectorSize()) << "b"; + m_Listbox.AddString(ostr.str().c_str()); + } +} + +void CDocumentStatus::OnFlushCache() +{ + if (NULL != m_pExplorerDoc) + { + m_pExplorerDoc->FlushCache(); + SetStatusInformation(); + } +} diff --git a/DocumentStatus.h b/DocumentStatus.h new file mode 100644 index 0000000..2eced6c --- /dev/null +++ b/DocumentStatus.h @@ -0,0 +1,52 @@ +#if !defined(AFX_DOCUMENTSTATUS_H__B118BD26_9EB6_4F26_8075_C93218EE4BDE__INCLUDED_) +#define AFX_DOCUMENTSTATUS_H__B118BD26_9EB6_4F26_8075_C93218EE4BDE__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// DocumentStatus.h : header file +// + +#include "3eExplorerDoc.h" + +///////////////////////////////////////////////////////////////////////////// +// CDocumentStatus dialog + +class CDocumentStatus : public CDialog +{ +// Construction +public: + void SetExplorerDoc(CMy3eExplorerDoc *_pDoc) {m_pExplorerDoc = _pDoc;}; + CDocumentStatus(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CDocumentStatus) + enum { IDD = IDD_STATUS }; + CListBox m_Listbox; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CDocumentStatus) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + void SetStatusInformation(); + CMy3eExplorerDoc * m_pExplorerDoc; + + // Generated message map functions + //{{AFX_MSG(CDocumentStatus) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnFlushCache(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_DOCUMENTSTATUS_H__B118BD26_9EB6_4F26_8075_C93218EE4BDE__INCLUDED_) diff --git a/ExplorerFile.cpp b/ExplorerFile.cpp new file mode 100644 index 0000000..0af8b02 --- /dev/null +++ b/ExplorerFile.cpp @@ -0,0 +1,137 @@ +// ExplorerFile.cpp: implementation of the CExplorerFile class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "3eExplorer.h" +#include "ExplorerFile.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CExplorerFile::CExplorerFile(std::string _filename, char * _data, int _size, TFILETYPE _type, WORD _param1, WORD _param2) +: m_strFilename(_filename) +, m_param1(_param1) +, m_param2(_param2) +, m_type(_type) +, m_vData(_size) +, m_tapfile(NULL) +{ + if (NULL != _data) + { + // copy this data into the vector... + ::CopyMemory(&m_vData[0], _data, _size); + } +} + +CExplorerFile::~CExplorerFile() +{ + if (NULL != m_tapfile) + { + delete [] m_tapfile; + } +} + +void CExplorerFile::AddData(char *_data, int _datasize) +{ + if (NULL != _data && 0 < _datasize) + { + int curr_size = m_vData.size(); + m_vData.resize(curr_size + _datasize); + ::CopyMemory(&m_vData[curr_size], _data, _datasize); + } +} + +int CExplorerFile::GenerateTapfile(char ** _buff, bool headerless) +{ + // here we'll allocate some memory for a tapfile, and return the pointer. + if (NULL != m_tapfile) + { + delete [] m_tapfile; + m_tapfile = NULL; + } + + unsigned int data_size = m_vData.size(); + + if (data_size > 0xffff) + { + throw EExplorerFile("File is too large to export to a .tap"); + } + + // how much memory do we need? + unsigned int total_mem = data_size; + total_mem += 4; + + if (!headerless) + total_mem += 21; + + m_tapfile = new char [total_mem]; + + unsigned int current_pointer = 0; + + if (!headerless) + { + // set up the header with the information we have... + T_TAPFILE_HEADER *header = reinterpret_cast(m_tapfile); + header->base.blocksize = sizeof(T_TAPFILE_HEADER) - 2; + header->base.flag = 0x00; + header->type = m_type; + std::string fn = m_strFilename; + fn.resize(10, ' '); + strncpy(header->filename, fn.c_str(), 10); + header->filesize = data_size; + header->param1 = m_param1; + header->param2 = m_param2; + + // set checksum... + BYTE chk = 0; + for (int ii = 2; ii < 20; ++ii) + { + chk ^= m_tapfile[ii]; + } + header->checksum = chk; + current_pointer += 21; + } + + + int chk_ptr = current_pointer; + + // now add in the actual data... + T_TAPFILE_BASE *tape = reinterpret_cast(&m_tapfile[current_pointer]); + tape->blocksize = data_size + 2; + tape->flag = 0xff; + current_pointer += sizeof(T_TAPFILE_BASE); + + if (data_size) + { + ::CopyMemory(&m_tapfile[current_pointer], &m_vData[0], data_size); + } + + current_pointer += data_size; + + // now work out the checksum + BYTE chk = 0; + chk_ptr += 2; + while (chk_ptr < current_pointer) + { + chk ^= m_tapfile[chk_ptr++]; + } + m_tapfile[chk_ptr] = chk; + + *_buff = m_tapfile; + return total_mem; +} + +int CExplorerFile::GenerateBinfile(char ** _buff) +{ + // for the moment just return a pointer to the first element + *_buff = m_vData.size() ? &m_vData[0] : NULL; + return m_vData.size(); +} diff --git a/ExplorerFile.h b/ExplorerFile.h new file mode 100644 index 0000000..9b92f6a --- /dev/null +++ b/ExplorerFile.h @@ -0,0 +1,71 @@ +// ExplorerFile.h: interface for the CExplorerFile class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_EXPLORERFILE_H__EFA8EFCE_76FE_4767_95EB_D107D5FC14B4__INCLUDED_) +#define AFX_EXPLORERFILE_H__EFA8EFCE_76FE_4767_95EB_D107D5FC14B4__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +class CExplorerFile +{ +public: + int GenerateTapfile(char ** _buff, bool headerless = false); + int GenerateBinfile(char ** _buff); + + typedef enum + { + TAPFILE, + TAPFILE_HEADERLESS, + BINFILE + } TEXTRACTABLE_FORMATS; + + typedef enum + { + PROGRAM = 0, + NUMBER_ARRAY = 1, + CHARACTER_ARRAY = 2, + CODE = 3 + } TFILETYPE; + +#pragma pack(push, 1) + typedef struct { + WORD blocksize; + BYTE flag; + } T_TAPFILE_BASE; + + typedef struct { + T_TAPFILE_BASE base; + BYTE type; + char filename[10]; + WORD filesize; + WORD param1; + WORD param2; + BYTE checksum; + } T_TAPFILE_HEADER; +#pragma pack(pop) + + + void AddData(char * _data, int _datasize); + CExplorerFile(std::string _filename, char * _data = NULL, int _size = 0, TFILETYPE _type = CODE, WORD _param1 = 0, WORD _param2 = 0); + virtual ~CExplorerFile(); + + void SetType(TFILETYPE _type) {m_type = _type;}; + void SetParam1(TFILETYPE _p1) {m_param1 = _p1;}; + void SetParam2(TFILETYPE _p2) {m_param2 = _p2;}; + void SetFilename(std::string _fn) {m_strFilename = _fn;}; + + class EExplorerFile : public std::runtime_error {public: EExplorerFile(const std::string &what_arg) : std::runtime_error(what_arg) {};}; + +protected: + std::string m_strFilename; + std::vector m_vData; + TFILETYPE m_type; + WORD m_param1; + WORD m_param2; + char * m_tapfile; +}; + +#endif // !defined(AFX_EXPLORERFILE_H__EFA8EFCE_76FE_4767_95EB_D107D5FC14B4__INCLUDED_) diff --git a/HdfHeader.cpp b/HdfHeader.cpp new file mode 100644 index 0000000..b1e7d67 --- /dev/null +++ b/HdfHeader.cpp @@ -0,0 +1,204 @@ +// HdfHeader.cpp: implementation of the CHdfHeader class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "3eExplorer.h" +#include "HdfHeader.h" + +#include "3eExplorerDoc.h" + +#include "IDEDOS_SystemPartition.h" + + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif + +std::string CHdfHeader::HDF_Signature("RS-IDE"); + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CHdfHeader::CHdfHeader(CMy3eExplorerDoc * _p3eDoc, char * block, const int blocksize, const char * _name) +: CTreeObject(_p3eDoc, HDF, _name, true) +, bHalvedSectorData(FALSE) +, bHalfWordUsed(FALSE) +{ + if (0x80 > blocksize) + { + // we should throw an exception here + throw EHdfInvalid("File is invalid"); + } + else + { + // check that it's the right filetype from the first few bytes + std::string header; + header.append(block, 6); + if (!HDF_Signature.compare(header) && 0x1a == block[0x06]) + { + // check the revision + revision = block[0x07]; + if (0x10 != revision && 0x11 != revision) + { + // it's not an acceptable revision of the HDF format + std::ostringstream except; + int major = (revision >> 4); + int minor = (revision & 0x0f); + except << "Revision " << major << "." << minor << " of HDF format not supported"; + throw EHdfUnsupported(except.str()); + } + else + { + // we're ok so get more values... + bHalvedSectorData = block[0x08] & 0x01; + bATAPIDevice = block[0x08] & 0x02; + + // for the moment ATAPI is not supported - sorry + if (0x11 == revision && bATAPIDevice) + { + throw EHdfUnsupported("ATAPI Devices are not currently supported, sorry."); + } + + image_offset = *(reinterpret_cast(&block[0x09])); + bOffset_valid = FALSE; + + // verify this offset, tho' it doesn't actually matter + if ((0x10 == revision && 0x0080 == image_offset) || + (0x11 == revision && 0x0216 == image_offset)) + { + bOffset_valid = TRUE; + } + + // now copy (some of) the rest of the block into the ATA Device ID output + ::CopyMemory(&ata_device_id, &block[0x16], sizeof(T_ATA_Device_ID)); + } + } + else + { + // throw an exception + throw EHdfInvalid("Not an HDF image - incorrect magic"); + } + } +} + +CHdfHeader::~CHdfHeader() +{ + +} + +void CHdfHeader::getCHS(CCHS &_chs) const +{ + _chs.c = getCylinders(); + _chs.h = getHeads(); + _chs.s = getSectors(); +} + +unsigned int CHdfHeader::getOffset() const +{ + return image_offset; +} + +unsigned int CHdfHeader::getCylinders() const +{ + return ata_device_id.cylinders; +} + +unsigned int CHdfHeader::getHeads() const +{ + return ata_device_id.heads; +} + +unsigned int CHdfHeader::getSectors() const +{ + return ata_device_id.sectors; +} + +unsigned int CHdfHeader::getSectorSize() +{ + // need to parse this now, or the sector size could be wrong + ParseForChildren(); + return bHalvedSectorData || bHalfWordUsed ? 256 : 512; +} + +void CHdfHeader::ParseForChildren() +{ + if (NULL != m_p3eDoc && !m_bParsedForChildren) + { + m_bParsedForChildren = TRUE; + // this is an HDF, so parse the first sector... + // the first item _must_ be a PLUSIDEDOS partition. + // or... it could be on the second track :] + // so we'll validate it... + try + { + CCHS chs(0,0,0); + char * ss_buff; + std::string header = "PLUSIDEDOS "; + std::string block; + + m_p3eDoc->ReadSector(chs, &ss_buff); + block.append(ss_buff, 0x10); + if (header.compare(block) || CIDEDOSPartitionDefinition::SYSTEM != ss_buff[0x10]) + { + // try the next track + ++chs.h; + m_p3eDoc->ReadSector(chs, &ss_buff); + block = ""; + block.append(ss_buff, 0x10); + if (header.compare(block) || CIDEDOSPartitionDefinition::SYSTEM != ss_buff[0x10]) + { + // ok, it's still not valid. + // we'll try the same thing but with half-word data... + m_p3eDoc->FlushCache(); + bHalfWordUsed = TRUE; + --chs.h; + m_p3eDoc->ReadSector(chs, &ss_buff); + block = ""; + block.append(ss_buff, 0x10); + if (header.compare(block) || CIDEDOSPartitionDefinition::SYSTEM != ss_buff[0x10]) + { + ++chs.h; + m_p3eDoc->ReadSector(chs, &ss_buff); + block = ""; + block.append(ss_buff, 0x10); + } + } + } + + if (!header.compare(block) && CIDEDOSPartitionDefinition::SYSTEM == ss_buff[0x10]) + { + // it's a valid PLUSIDEDOS image + // create a new object (system partition) and add it as a child + CIDEDOS_SystemPartition *cide_sys = new CIDEDOS_SystemPartition(m_p3eDoc, ss_buff, chs.h); + m_vChildren.push_back(cide_sys); + } + else + { + throw EHdfInvalid("Not a PLUSIDEDOS image"); + } + } + catch (CFileException *e) + { + m_p3eDoc->ReportSaveLoadException(m_p3eDoc->Filename().c_str(), e, FALSE, IDS_FILE_ACCESS_ERROR); + } + catch (EHdfInvalid &e) + { + ::AfxMessageBox(e.what(), MB_ICONEXCLAMATION | MB_OK); + } + } +} + +std::string CHdfHeader::GetInformation() +{ + std::ostringstream ostr; + ostr << "HDF: " << GetName() << "\r\n"; + ostr << "CHS: " << getCylinders() << "/" << getHeads() << "/" << getSectors() << "\r\n"; + ostr << "Sec: " << getSectorSize() << " bytes"; + if (bHalfWordUsed) + ostr << " : half-word mode"; + return(ostr.str()); +} diff --git a/HdfHeader.h b/HdfHeader.h new file mode 100644 index 0000000..d50af79 --- /dev/null +++ b/HdfHeader.h @@ -0,0 +1,73 @@ +// HdfHeader.h: interface for the CHdfHeader class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_HDFHEADER_H__74BF451F_C49E_41A3_A5E9_65D15205AD2F__INCLUDED_) +#define AFX_HDFHEADER_H__74BF451F_C49E_41A3_A5E9_65D15205AD2F__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "TreeObject.h" + +class CCHS; + +class CHdfHeader : public CTreeObject +{ +public: + CHdfHeader(CMy3eExplorerDoc * _p3eDoc, char * block, const int blocksize, const char * _name); + virtual ~CHdfHeader(); + +#pragma pack(push, 1) + typedef struct { + WORD general_config; + WORD cylinders; + WORD _reserved_1; + WORD heads; + WORD _obsolete_1; + WORD _obsolete_2; + WORD sectors; + WORD _vender_1; + WORD _vender_2; + WORD _vender_3; + WORD serial[10]; + WORD _obsolete_3; + WORD _obsolete_4; + WORD bytes_on_rw_long_cmds; + CHAR firmware[8]; + CHAR model[40]; + WORD _vendor_4; + WORD _reserved_2; + WORD capabilities; + } T_ATA_Device_ID; +#pragma pack(pop) + + void getCHS(CCHS &_chs) const; + unsigned int getOffset() const; + unsigned int getCylinders() const; + unsigned int getHeads() const; + unsigned int getSectors() const; + unsigned int getSectorSize(); + BOOL UsingHalfWord() const {return bHalfWordUsed;}; + virtual void ParseForChildren(); + +protected: + static std::string HDF_Signature; + BYTE revision; + BOOL bHalvedSectorData; + BOOL bHalfWordUsed; + BOOL bATAPIDevice; + unsigned int image_offset; + BOOL bOffset_valid; + BYTE _reserved[11]; + T_ATA_Device_ID ata_device_id; + +public: + // define an exception that could occur + class EHdfInvalid : public std::runtime_error {public: EHdfInvalid(const std::string &what_arg) : std::runtime_error(what_arg) {};}; + class EHdfUnsupported : public std::runtime_error {public: EHdfUnsupported(const std::string &what_arg) : std::runtime_error(what_arg) {};}; + virtual std::string GetInformation(); +}; + +#endif // !defined(AFX_HDFHEADER_H__74BF451F_C49E_41A3_A5E9_65D15205AD2F__INCLUDED_) diff --git a/IDEDOSPartitionDefinition.cpp b/IDEDOSPartitionDefinition.cpp new file mode 100644 index 0000000..c1069b4 --- /dev/null +++ b/IDEDOSPartitionDefinition.cpp @@ -0,0 +1,49 @@ +// IDEDOSPartitionDefinition.cpp: implementation of the CIDEDOSPartitionDefinition class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "3eExplorer.h" +#include "IDEDOSPartitionDefinition.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CIDEDOSPartitionDefinition::CIDEDOSPartitionDefinition(CMy3eExplorerDoc * _p3eDoc, TreeObjectType _type, char * block) +: CTreeObject(_p3eDoc, _type) +, m_partition_entry_size(0x40) +{ + // for now simply copy the block onto the definition + ::CopyMemory(&m_idedos_partition, block, sizeof(m_idedos_partition)); + + // set the name + m_strName = GetPartitionName(); +} + +CIDEDOSPartitionDefinition::~CIDEDOSPartitionDefinition() +{ + +} + +std::string CIDEDOSPartitionDefinition::GetPartitionName() +{ + std::string str; + str.append(reinterpret_cast(m_idedos_partition.name), 16); + return str; +} + +std::string CIDEDOSPartitionDefinition::GetInformation() +{ + std::ostringstream ostr; + ostr << "from: " << m_idedos_partition.g_cylinder_start() << "/" << m_idedos_partition.g_head_start() << "\r\n"; + ostr << "to : " << m_idedos_partition.g_cylinder_end() << "/" << m_idedos_partition.g_head_end() << "\r\n"; + ostr << " " << m_idedos_partition.g_largest_logical_sector() << " is largest logical sector"; + return (ostr.str()); +} diff --git a/IDEDOSPartitionDefinition.h b/IDEDOSPartitionDefinition.h new file mode 100644 index 0000000..27d080f --- /dev/null +++ b/IDEDOSPartitionDefinition.h @@ -0,0 +1,69 @@ +// IDEDOSPartitionDefinition.h: interface for the CIDEDOSPartitionDefinition class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_IDEDOSPARTITIONDEFINITION_H__99998564_1B63_4B07_B30C_23B9C03BEECD__INCLUDED_) +#define AFX_IDEDOSPARTITIONDEFINITION_H__99998564_1B63_4B07_B30C_23B9C03BEECD__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "TreeObject.h" + +class CIDEDOSPartitionDefinition : public CTreeObject +{ +public: + CIDEDOSPartitionDefinition(CMy3eExplorerDoc * _p3eDoc, TreeObjectType _type, char * block); + virtual ~CIDEDOSPartitionDefinition(); + +#pragma pack(push, 1) + typedef struct { + BYTE name[16]; + BYTE type; + WORD cylinder_start; + BYTE head_start; + WORD cylinder_end; + BYTE head_end; + DWORD largest_logical_sector; + // type-specific? + + int g_cylinder_start() const {return (int)cylinder_start;}; + int g_cylinder_end() const {return (int)cylinder_end;}; + int g_head_start() const {return (int)head_start;}; + int g_head_end() const {return (int)head_end;}; + int g_largest_logical_sector() const {return (int)largest_logical_sector;}; + int g_type() const {return (int)type;}; + } T_IDEDOS_PARTITION_BASE; +#pragma pack(pop) + + typedef enum + { + UNUSED = 0x00, + SYSTEM = 0x01, + SWAP = 0x02, + PLUS3DOS = 0x03, + CPM = 0x04, + BOOT = 0x05, + MOVIE = 0x0f, + FAT16 = 0x10, + UZIX = 0x20, + TRDOS = 0x30, + SAMDOS = 0x31, + MB02 = 0x32, + BAD_SPACE = 0xfe, + FREE_SPACE = 0xff + } T_PARTITION_TYPES; + +public: + virtual std::string GetInformation(); + +protected: + std::string GetPartitionName(); + +protected: + unsigned int m_partition_entry_size; + T_IDEDOS_PARTITION_BASE m_idedos_partition; +}; + +#endif // !defined(AFX_IDEDOSPARTITIONDEFINITION_H__99998564_1B63_4B07_B30C_23B9C03BEECD__INCLUDED_) diff --git a/IDEDOS_Plus3DOSPartition.cpp b/IDEDOS_Plus3DOSPartition.cpp new file mode 100644 index 0000000..dcb5d99 --- /dev/null +++ b/IDEDOS_Plus3DOSPartition.cpp @@ -0,0 +1,201 @@ +// IDEDOS_Plus3DOSPartition.cpp: implementation of the CIDEDOS_Plus3DOSPartition class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "3eExplorer.h" +#include "IDEDOS_Plus3DOSPartition.h" +#include "3eExplorerDoc.h" +#include "Plus3DOS_UserArea.h" +#include "Plus3DOS_DirectoryEntry.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CIDEDOS_Plus3DOSPartition::CIDEDOS_Plus3DOSPartition(CMy3eExplorerDoc * _p3eDoc, char * block) +: CIDEDOSPartitionDefinition(_p3eDoc, PARTITION_PLUS3DOS, block) +, m_block_size(0) +{ + ::CopyMemory(&m_plus3dos_partition, &block[0x20], sizeof(m_plus3dos_partition)); + m_block_size = 0x80 << (m_plus3dos_partition.bsh); +} + +CIDEDOS_Plus3DOSPartition::~CIDEDOS_Plus3DOSPartition() +{ + +} + +void CIDEDOS_Plus3DOSPartition::ParseForChildren() +{ + //CTreeObject::ParseForChildren(p3eDoc); + + // here we want to go through the directory entries checking for + // files in various user areas (0-f and e5). + + // first work out what the values are to pass into user-area constructors... + // this is: area_id, partition_name, start cyl/track/sector, and max_dir_entries + unsigned int start_cyl = m_idedos_partition.g_cylinder_start(); + unsigned int start_trk = m_idedos_partition.g_head_start() + m_plus3dos_partition.g_off(); + unsigned int start_sec = 0; + unsigned int max_dir_entries = m_plus3dos_partition.g_drm() + 1; + const unsigned int max_heads_per_cylinder = m_p3eDoc->GetRoot()->getHeads(); + + // we should also check the allocation mask to see how many blocks (128 << bsh) the directory takes up. + // for now we'll assume the mask will be all in the top bits. + // this may not be the case, but we can fix that later. + //WORD directory_mask = AllocationMask(); + //int block_count = 0; + //while (directory_mask & 0x8000) + //{ + // block_count++; + // directory_mask <<= 1; + //} + + // now work out how many sectors that would be... + //const int sectors_of_directory = ((block_count * BlockSize()) / m_p3eDoc->GetRoot()->getSectorSize()); + + // ensure start cylinder and track is valid + start_cyl += (start_trk / max_heads_per_cylinder); + start_trk %= max_heads_per_cylinder; + + // now we want to go through the whole directory area, searching for files in the various user areas. + // as soon as we find a file, we stop searching, create that user area, and search for the next. + const unsigned int dir_entry_size = sizeof(CPlus3DOS_DirectoryEntry::T_IDEDOS_PLUS3DOS_DIRECTORY_ENTRY); + + + for (BYTE user_id = 0x00; user_id <= 0xe5; user_id++) + { + // a bit hacky, but... + if ((0x00 <= user_id && 0x0f >= user_id) || 0xe5 == user_id) + { + // go through the directory entries and find an entry of this user id + char * ss_buff = NULL; + unsigned int dir_pointer = 0; + unsigned int sector_size = m_p3eDoc->ReadSector(CCHS(start_cyl, start_trk, start_sec), &ss_buff); + + bool found = false; + unsigned int cur_dir_entry = 0; + + do + { + // map the current directory entry + CPlus3DOS_DirectoryEntry::T_IDEDOS_PLUS3DOS_DIRECTORY_ENTRY *dir_entry; + dir_entry = reinterpret_cast(&ss_buff[dir_pointer]); + + // check the type + if (dir_entry->user_number == user_id) + { + // we may have an entry... if it's 0xe5 it could simply be blank, so check the remaining bytes... + if (0xe5 == user_id) + { + // for now, just check the first byte of the filename... + if (0xe5 != dir_entry->filename[0]) + { + found = true; + } + } + else + { + found = true; + } + + if (found) + { + // create a new user-area and add it here. + // note, we're passing in this partition, so we can get xdpb information + CPlus3DOS_UserArea *user_area = new CPlus3DOS_UserArea(m_p3eDoc, user_id, GetName(), start_cyl, start_trk, start_sec, max_dir_entries, this); + m_vChildren.push_back(user_area); + } + } + + // done... + if (!found) + { + // select next directory entry + cur_dir_entry++; + if (cur_dir_entry < max_dir_entries) + { + dir_pointer += dir_entry_size; + if (dir_pointer >= sector_size) + { + dir_pointer = 0; + // read the next sector + sector_size = m_p3eDoc->ReadNextSector(&ss_buff); + } + } + } + } while (!found && cur_dir_entry < max_dir_entries); + } + // else it's not a valid user area code so ignore + } + + CTreeObject::ParseForChildren(); +} + +std::string CIDEDOS_Plus3DOSPartition::GetInformation() +{ + std::ostringstream ostr; + ostr << "+3DOS: " << GetName() << "\r\n"; + ostr << CIDEDOSPartitionDefinition::GetInformation() << "\r\n\r\n"; + + ostr << "XDPB Structure:\r\n"; + ostr << "spt : " << m_plus3dos_partition.g_spt() << "\r\n"; + ostr << "bsh : " << m_plus3dos_partition.g_bsh() << "\r\n"; + ostr << "blm : " << m_plus3dos_partition.g_blm() << "\r\n"; + ostr << "exm : " << m_plus3dos_partition.g_exm() << "\r\n"; + ostr << "dsm : " << m_plus3dos_partition.g_dsm() << "\r\n"; + ostr << "drm : " << m_plus3dos_partition.g_drm() << "\r\n"; + ostr << "al0 : " << m_plus3dos_partition.g_al0() << "\r\n"; + ostr << "al1 : " << m_plus3dos_partition.g_al1() << "\r\n"; + ostr << "cks : " << m_plus3dos_partition.g_cks() << "\r\n"; + ostr << "off : " << m_plus3dos_partition.g_off() << "\r\n"; + ostr << "psh : " << m_plus3dos_partition.g_psh() << "\r\n"; + ostr << "phm : " << m_plus3dos_partition.g_phm() << "\r\n"; + ostr << "sidedness : " << m_plus3dos_partition.g_sidedness() << "\r\n"; + ostr << "tracks_per_side : " << m_plus3dos_partition.g_tracks_per_side() << "\r\n"; + ostr << "sectors_per_track : " << m_plus3dos_partition.g_sectors_per_track() << "\r\n"; + ostr << "first_physical_sector_number : " << m_plus3dos_partition.g_first_physical_sector_number() << "\r\n"; + ostr << "sector_size : " << m_plus3dos_partition.g_sector_size() << "\r\n"; + ostr << "upd765a_rw_gap : " << m_plus3dos_partition.g_upd765a_rw_gap() << "\r\n"; + ostr << "upd765a_format_gap : " << m_plus3dos_partition.g_upd765a_format_gap() << "\r\n"; + ostr << "multitrack_flags : " << m_plus3dos_partition.g_multitrack_flags() << "\r\n"; + ostr << "freeze_flag : " << m_plus3dos_partition.g_freeze_flag(); + + return(ostr.str()); +} + + +bool CIDEDOS_Plus3DOSPartition::BlockToCHS(WORD _block, CCHS &_chs) const +{ + // this method translates a block to a chs. + // the return value is true if the value is safe to use, false if it lies outside this partition + + // first get the sector number... + unsigned int full_sector = _block * (BlockSize() / m_p3eDoc->GetRoot()->getSectorSize()); + + // now apply the offsets due to the position of this partition + unsigned int start_cyl = m_idedos_partition.g_cylinder_start(); + unsigned int start_trk = m_idedos_partition.g_head_start(); + + full_sector += (start_trk * m_p3eDoc->GetRoot()->getSectors()); + full_sector += (start_cyl * (m_p3eDoc->GetRoot()->getSectors() * m_p3eDoc->GetRoot()->getHeads())); + + // now full_sector is the sector number... + // we want to convert this to a CHS... + _chs.c = full_sector / (m_p3eDoc->GetRoot()->getSectors() * m_p3eDoc->GetRoot()->getHeads()); + full_sector -= _chs.c * (m_p3eDoc->GetRoot()->getSectors() * m_p3eDoc->GetRoot()->getHeads()); + + _chs.h = full_sector / m_p3eDoc->GetRoot()->getSectors(); + full_sector -= _chs.h * m_p3eDoc->GetRoot()->getSectors(); + + _chs.s = full_sector; + + return true; +} diff --git a/IDEDOS_Plus3DOSPartition.h b/IDEDOS_Plus3DOSPartition.h new file mode 100644 index 0000000..3521a60 --- /dev/null +++ b/IDEDOS_Plus3DOSPartition.h @@ -0,0 +1,89 @@ +// IDEDOS_Plus3DOSPartition.h: interface for the CIDEDOS_Plus3DOSPartition class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_IDEDOS_PLUS3DOSPARTITION_H__9F47A008_2139_4F90_9BC0_77D7D4B5F4CE__INCLUDED_) +#define AFX_IDEDOS_PLUS3DOSPARTITION_H__9F47A008_2139_4F90_9BC0_77D7D4B5F4CE__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "IDEDOSPartitionDefinition.h" +#include "3eExplorerDoc.h" // Added by ClassView + +class CIDEDOS_Plus3DOSPartition : public CIDEDOSPartitionDefinition +{ +public: + CIDEDOS_Plus3DOSPartition(CMy3eExplorerDoc * _p3eDoc, char * block); + virtual ~CIDEDOS_Plus3DOSPartition(); + +#pragma pack(push, 1) + typedef struct { + WORD spt; // records_per_track_128 + BYTE bsh; // block_shift + BYTE blm; // block_mask + BYTE exm; // extent_mask + WORD dsm; // blocks_on_disk + WORD drm; // directory_entries + BYTE al0; // directory allocation bitmap first byte + BYTE al1; // directory allocation bitmap second byte + WORD cks; // checksum vector size + WORD off; // offset, number of reserved tracks + BYTE psh; // physical sector shift (0 = 128b, 1 = 256b, 2 = 512b, ...) + BYTE phm; // physical sector mask (0 = 128b, 1 = 256b, 3 = 512b, ...) + + // now come the amstrad extensions... + BYTE sidedness; + BYTE tracks_per_side; + BYTE sectors_per_track; + BYTE first_physical_sector_number; + WORD sector_size; + BYTE upd765a_rw_gap; + BYTE upd765a_format_gap; + BYTE multitrack_flags; + BYTE freeze_flag; + + + inline int g_spt() const {return (int)spt;}; + inline int g_bsh() const {return (int)bsh;}; + inline int g_blm() const {return (int)blm;}; + inline int g_exm() const {return (int)exm;}; + inline int g_dsm() const {return (int)dsm;}; + inline int g_drm() const {return (int)drm;}; + inline int g_al0() const {return (int)al0;}; + inline int g_al1() const {return (int)al1;}; + inline int g_cks() const {return (int)cks;}; + inline int g_off() const {return (int)off;}; + inline int g_psh() const {return (int)psh;}; + inline int g_phm() const {return (int)phm;}; + inline int g_sidedness() const {return (int)sidedness;}; + inline int g_tracks_per_side() const {return (int)tracks_per_side;}; + inline int g_sectors_per_track() const {return (int)sectors_per_track;}; + inline int g_first_physical_sector_number() const {return (int)first_physical_sector_number;}; + inline int g_sector_size() const {return (int)sector_size;}; + inline int g_upd765a_rw_gap() const {return (int)upd765a_rw_gap;}; + inline int g_upd765a_format_gap() const {return (int)upd765a_format_gap;}; + inline int g_multitrack_flags() const {return (int)multitrack_flags;}; + inline int g_freeze_flag() const {return (int)freeze_flag;}; + } T_IDEDOS_PLUS3DOS_PARTITION; +#pragma pack(pop) + +public: + bool BlockToCHS(WORD _block, CCHS &_chs) const; + virtual void ParseForChildren(); + virtual std::string GetInformation(); + + BYTE ExtentMask() const {return m_plus3dos_partition.exm;}; + WORD AllocationMask() const {return (WORD)(m_plus3dos_partition.al1 + (m_plus3dos_partition.al0 * 256));}; + int BlockSize() const {return (128 << (m_plus3dos_partition.g_bsh()));}; + int SectorsInABlock() const {return (BlockSize() / m_p3eDoc->GetRoot()->getSectorSize());}; + bool ALRecordsAre16bit() const {return (m_plus3dos_partition.g_dsm() > 255);}; + +protected: + T_IDEDOS_PLUS3DOS_PARTITION m_plus3dos_partition; + unsigned int m_block_size; + +}; + +#endif // !defined(AFX_IDEDOS_PLUS3DOSPARTITION_H__9F47A008_2139_4F90_9BC0_77D7D4B5F4CE__INCLUDED_) diff --git a/IDEDOS_SystemPartition.cpp b/IDEDOS_SystemPartition.cpp new file mode 100644 index 0000000..f71be77 --- /dev/null +++ b/IDEDOS_SystemPartition.cpp @@ -0,0 +1,111 @@ +// IDEDOS_SystemPartition.cpp: implementation of the CIDEDOS_SystemPartition class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "3eExplorer.h" +#include "IDEDOS_SystemPartition.h" +#include "3eExplorerDoc.h" +#include "IDEDOS_Plus3DOSPartition.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CIDEDOS_SystemPartition::CIDEDOS_SystemPartition(CMy3eExplorerDoc * _p3eDoc, char * block, unsigned int _tt) +: CIDEDOSPartitionDefinition(_p3eDoc, PARTITION_PLUSIDEDOS, block) +, m_start_track(_tt) +{ + // copy the additional part of the definition + ::CopyMemory(&m_system_partition, &block[0x20], sizeof(m_system_partition)); +} + +CIDEDOS_SystemPartition::~CIDEDOS_SystemPartition() +{ + +} + +std::string CIDEDOS_SystemPartition::GetInformation() +{ + std::ostringstream ostr; + ostr << "PLUSIDEDOS System Partition" << "\r\n"; + ostr << CIDEDOSPartitionDefinition::GetInformation() << "\r\n"; + ostr << "CHT : " << m_system_partition.g_cylinders() << "/" << m_system_partition.g_heads() << "/" << m_system_partition.g_tracks() << "\r\n"; + ostr << " " << m_system_partition.g_sectors_per_cylinder() << " sectors per cylinder\r\n"; + ostr << "max : " << m_system_partition.g_partitions_allowed() << " partitions"; + return(ostr.str()); +} + +void CIDEDOS_SystemPartition::ParseForChildren() +{ + // here we want to parse the system partition to find any _real_ partitions. + + if (NULL != m_p3eDoc && !m_bParsedForChildren) + { + m_bParsedForChildren = TRUE; + + try + { + char * ss_buff; + unsigned int curr_sect = 0; + const unsigned int max_sect = m_p3eDoc->GetRoot()->getSectors(); + unsigned int sect_offset = m_partition_entry_size; + const unsigned int max_offset = m_p3eDoc->GetRoot()->getSectorSize(); + + // *** to do - system partition taking more than one track *** + + // for the moment we're only supporting type 0x03 (+3dos) + // for other partitions we'll create a childless CTreeObject + m_p3eDoc->ReadSector(CCHS(0, m_start_track, curr_sect), &ss_buff); + + while (curr_sect < max_sect) + { + char * block = &ss_buff[sect_offset]; + switch ((unsigned char)block[0x10]) + { + case CIDEDOSPartitionDefinition::PLUS3DOS: + { + // it's a +3dos partition + CIDEDOS_Plus3DOSPartition *cplus3dos = new CIDEDOS_Plus3DOSPartition(m_p3eDoc, block); + m_vChildren.push_back(cplus3dos); + } + break; + case CIDEDOSPartitionDefinition::UNUSED: + case CIDEDOSPartitionDefinition::BAD_SPACE: + case CIDEDOSPartitionDefinition::FREE_SPACE: + // we can ignore these + break; + default: + { + // it's an unsupported partition + CIDEDOSPartitionDefinition *partition = new CIDEDOSPartitionDefinition(m_p3eDoc, CTreeObject::INVALID, block); + m_vChildren.push_back(partition); + } + break; + } + + sect_offset += m_partition_entry_size; + if (sect_offset >= max_offset) + { + // reached the end of this sector, so see if we read in the next... + sect_offset = 0x00; + ++curr_sect; + if (curr_sect < max_sect) + { + m_p3eDoc->ReadSector(CCHS(0, m_start_track, curr_sect), &ss_buff); + } + } + } + } + catch (CFileException *e) + { + m_p3eDoc->ReportSaveLoadException(m_p3eDoc->Filename().c_str(), e, FALSE, IDS_FILE_ACCESS_ERROR); + } + } +} diff --git a/IDEDOS_SystemPartition.h b/IDEDOS_SystemPartition.h new file mode 100644 index 0000000..523b72e --- /dev/null +++ b/IDEDOS_SystemPartition.h @@ -0,0 +1,43 @@ +// IDEDOS_SystemPartition.h: interface for the CIDEDOS_SystemPartition class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_IDEDOS_SYSTEMPARTITION_H__DEB9F53D_4E27_4718_9C4F_2127FD1B7BA1__INCLUDED_) +#define AFX_IDEDOS_SYSTEMPARTITION_H__DEB9F53D_4E27_4718_9C4F_2127FD1B7BA1__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "IDEDOSPartitionDefinition.h" + +class CIDEDOS_SystemPartition : public CIDEDOSPartitionDefinition +{ +public: + CIDEDOS_SystemPartition(CMy3eExplorerDoc * _p3eDoc, char * block, unsigned int _tt); + virtual ~CIDEDOS_SystemPartition(); + +#pragma pack(push, 1) + typedef struct { + WORD cylinders; + BYTE heads; + BYTE tracks; + WORD sectors_per_cylinder; + WORD partitions_allowed; + int g_cylinders() const {return (int)cylinders;}; + int g_heads() const {return (int)heads;}; + int g_tracks() const {return (int)tracks;}; + int g_sectors_per_cylinder() const {return (int)sectors_per_cylinder;}; + int g_partitions_allowed() const {return (int)partitions_allowed;}; + } T_IDEDOS_SYSTEM_PARTITION; +#pragma pack(pop) + +protected: + unsigned int m_start_track; + T_IDEDOS_SYSTEM_PARTITION m_system_partition; +public: + virtual void ParseForChildren(); + virtual std::string GetInformation(); +}; + +#endif // !defined(AFX_IDEDOS_SYSTEMPARTITION_H__DEB9F53D_4E27_4718_9C4F_2127FD1B7BA1__INCLUDED_) diff --git a/MainFrm.cpp b/MainFrm.cpp new file mode 100644 index 0000000..f428124 --- /dev/null +++ b/MainFrm.cpp @@ -0,0 +1,117 @@ +// MainFrm.cpp : implementation of the CMainFrame class +// + +#include "stdafx.h" +#include "3eExplorer.h" + +#include "MainFrm.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame + +IMPLEMENT_DYNAMIC(CMainFrame, CMDIFrameWnd) + +BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd) + //{{AFX_MSG_MAP(CMainFrame) + ON_WM_CREATE() + ON_COMMAND(ID_FILE_NEW, OnFileNew) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +static UINT indicators[] = +{ + ID_SEPARATOR // status line indicator +}; + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame construction/destruction + +CMainFrame::CMainFrame() +{ +} + +CMainFrame::~CMainFrame() +{ +} + +int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1) + return -1; + + if (!m_wndToolBar.CreateEx(this) || + !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) + { + TRACE0("Failed to create toolbar\n"); + return -1; // fail to create + } + if (!m_wndDlgBar.Create(this, IDR_MAINFRAME, + CBRS_ALIGN_TOP, AFX_IDW_DIALOGBAR)) + { + TRACE0("Failed to create dialogbar\n"); + return -1; // fail to create + } + + if (!m_wndReBar.Create(this) || + !m_wndReBar.AddBar(&m_wndToolBar) || + !m_wndReBar.AddBar(&m_wndDlgBar)) + { + TRACE0("Failed to create rebar\n"); + return -1; // fail to create + } + + if (!m_wndStatusBar.Create(this) || + !m_wndStatusBar.SetIndicators(indicators, + sizeof(indicators)/sizeof(UINT))) + { + TRACE0("Failed to create status bar\n"); + return -1; // fail to create + } + + // TODO: Remove this if you don't want tool tips + m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() | + CBRS_TOOLTIPS | CBRS_FLYBY); + + return 0; +} + +BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) +{ + if( !CMDIFrameWnd::PreCreateWindow(cs) ) + return FALSE; + // TODO: Modify the Window class or styles here by modifying + // the CREATESTRUCT cs + + return TRUE; +} + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame diagnostics + +#ifdef _DEBUG +void CMainFrame::AssertValid() const +{ + CMDIFrameWnd::AssertValid(); +} + +void CMainFrame::Dump(CDumpContext& dc) const +{ + CMDIFrameWnd::Dump(dc); +} + +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame message handlers + + +void CMainFrame::OnFileNew() +{ + // we don't want to be able to create a new document. +} diff --git a/MainFrm.h b/MainFrm.h new file mode 100644 index 0000000..2dfff26 --- /dev/null +++ b/MainFrm.h @@ -0,0 +1,59 @@ +// MainFrm.h : interface of the CMainFrame class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_MAINFRM_H__6A5C6FA8_41F2_4CC1_AB86_BA685AAC0CD7__INCLUDED_) +#define AFX_MAINFRM_H__6A5C6FA8_41F2_4CC1_AB86_BA685AAC0CD7__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +class CMainFrame : public CMDIFrameWnd +{ + DECLARE_DYNAMIC(CMainFrame) +public: + CMainFrame(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CMainFrame) + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CMainFrame(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +public: // control bar embedded members + CStatusBar m_wndStatusBar; +protected: + CToolBar m_wndToolBar; + CReBar m_wndReBar; + CDialogBar m_wndDlgBar; + +// Generated message map functions +protected: + //{{AFX_MSG(CMainFrame) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnFileNew(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_MAINFRM_H__6A5C6FA8_41F2_4CC1_AB86_BA685AAC0CD7__INCLUDED_) diff --git a/OptionsDialog.cpp b/OptionsDialog.cpp new file mode 100644 index 0000000..8d7e39a --- /dev/null +++ b/OptionsDialog.cpp @@ -0,0 +1,45 @@ +// OptionsDialog.cpp : implementation file +// + +#include "stdafx.h" +#include "3eExplorer.h" +#include "OptionsDialog.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// COptionsDialog dialog + + +COptionsDialog::COptionsDialog(CWnd* pParent /*=NULL*/) + : CDialog(COptionsDialog::IDD, pParent) +{ + //{{AFX_DATA_INIT(COptionsDialog) + m_bPreCacheHeader = FALSE; + m_bPreCacheTrack = FALSE; + //}}AFX_DATA_INIT +} + + +void COptionsDialog::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COptionsDialog) + DDX_Check(pDX, IDC_PRE_READ_HEADER, m_bPreCacheHeader); + DDX_Check(pDX, IDC_PRECACHE_TRACK, m_bPreCacheTrack); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COptionsDialog, CDialog) + //{{AFX_MSG_MAP(COptionsDialog) + // NOTE: the ClassWizard will add message map macros here + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COptionsDialog message handlers diff --git a/OptionsDialog.h b/OptionsDialog.h new file mode 100644 index 0000000..328c921 --- /dev/null +++ b/OptionsDialog.h @@ -0,0 +1,47 @@ +#if !defined(AFX_OPTIONSDIALOG_H__2A8B6CA6_6DB1_4976_9B48_BE1349026EFF__INCLUDED_) +#define AFX_OPTIONSDIALOG_H__2A8B6CA6_6DB1_4976_9B48_BE1349026EFF__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// OptionsDialog.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// COptionsDialog dialog + +class COptionsDialog : public CDialog +{ +// Construction +public: + COptionsDialog(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(COptionsDialog) + enum { IDD = IDD_OPTIONS }; + BOOL m_bPreCacheHeader; + BOOL m_bPreCacheTrack; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COptionsDialog) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COptionsDialog) + // NOTE: the ClassWizard will add member functions here + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_OPTIONSDIALOG_H__2A8B6CA6_6DB1_4976_9B48_BE1349026EFF__INCLUDED_) diff --git a/Plus3DOS_DirectoryEntry.cpp b/Plus3DOS_DirectoryEntry.cpp new file mode 100644 index 0000000..e9fc28f --- /dev/null +++ b/Plus3DOS_DirectoryEntry.cpp @@ -0,0 +1,391 @@ +// Plus3DOS_DirectoryEntry.cpp: implementation of the CPlus3DOS_DirectoryEntry class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "3eExplorer.h" +#include "3eExplorerDoc.h" +#include "Plus3DOS_DirectoryEntry.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif + +std::string CPlus3DOS_DirectoryEntry::PLUS3DOS_Signature("PLUS3DOS"); + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CPlus3DOS_DirectoryEntry::CPlus3DOS_DirectoryEntry(CMy3eExplorerDoc * _p3eDoc, TreeObjectType _type, char * block, const CIDEDOS_Plus3DOSPartition *_partition) +:CTreeObject(_p3eDoc, _type, _T(""), false, true) +, m_pPartition(_partition) +, m_bHeaderExamined(false) +, m_bHeaderless(true) // assume headerless until we know otherwise +, m_filetype(CExplorerFile::CODE) +, m_param1(0x0000) +, m_param2(0x8000) +, m_disk_file_length(0x00000080) +, m_filesize(0x0000) +, m_issue(0x00) +, m_version(0x00) +{ + const T_IDEDOS_PLUS3DOS_DIRECTORY_ENTRY *directory_entry = reinterpret_cast(block); + + // sort out the filename... + int fn_count = 8; + while (0x20 == directory_entry->filename[fn_count - 1] && fn_count) + --fn_count; + + m_strName.append(reinterpret_cast(directory_entry->filename), fn_count); + + fn_count = 0; + std::string extension; + // note that the extension is the top 7 bits + while (0x20 != (0x7f & directory_entry->ext.extension[fn_count]) && fn_count < 3) + { + char ext_c = 0x7f & directory_entry->ext.extension[fn_count]; + extension.append(&ext_c, 1); + fn_count++; + } + if (extension.size()) + { + m_strName += "."; + m_strName += extension; + } + + // create this extent (it may not be the first, you know) + AddExtent(block); +} + +CPlus3DOS_DirectoryEntry::~CPlus3DOS_DirectoryEntry() +{ + +} + +void CPlus3DOS_DirectoryEntry::AddExtent(char *block) +{ + // this method allows us to add an extent, should there be one, to this directory entry + // an extent is an additional directory entry which lists additional blocks for this file. + + const T_IDEDOS_PLUS3DOS_DIRECTORY_ENTRY *directory_entry = reinterpret_cast(block); + + // get the sequence number + const int extent_sequence = directory_entry->g_extent(m_pPartition->ExtentMask()); + + // are we using 8-bit or 16-bit allocation records? + + int max_al_records = 16; + if (m_pPartition->ALRecordsAre16bit()) + { + max_al_records = 8; + } + + + // now add each of the allocation things to this extent... + int records_in_this_extent = ((directory_entry->extent_low & m_pPartition->ExtentMask()) * 128) + directory_entry->records_used; + + int blocks_in_this_extent = (128 * records_in_this_extent) / m_pPartition->BlockSize(); + if ((128 * records_in_this_extent) % m_pPartition->BlockSize()) + blocks_in_this_extent++; + + int this_allocator = 0; + // now, blocks_in_this_extent is the number of AL records in use here. + while ((this_allocator < blocks_in_this_extent) && (this_allocator < max_al_records)) + { + WORD this_alloc; + if (m_pPartition->ALRecordsAre16bit()) + { + this_alloc = directory_entry->alloc.al_16.al[this_allocator]; + } + else + { + this_alloc = directory_entry->alloc.al_8.al[this_allocator]; + } + extent_map[(extent_sequence * max_al_records) + this_allocator] = this_alloc; + ++ this_allocator; + } +} + +std::string CPlus3DOS_DirectoryEntry::GetInformation() +{ + ExamineHeader(); + + int block_count = extent_map.size(); + + std::ostringstream ostr; + if (m_bHeaderless) + { + ostr << "Headerless File: " << GetName() << "\r\n"; + } + else + { + switch (m_filetype) + { + case CExplorerFile::PROGRAM: + { + ostr << "Program: " << GetName() << "\r\n"; + ostr << "Line: " << m_param1 << "\t "; + ostr << "Vars: +" << m_param2 << "\r\n"; + } + break; + case CExplorerFile::NUMBER_ARRAY: + { + ostr << "Number Array: " << GetName() << "\r\n"; + char var = (HIBYTE(m_param1) & 0x1f) | 0x40; + ostr << "Var: " << var << "\r\n"; + } + break; + case CExplorerFile::CHARACTER_ARRAY: + { + ostr << "Character Array: " << GetName() << "\r\n"; + char var = (HIBYTE(m_param1) & 0x1f) | 0x40; + ostr << "Var: " << var << "$\r\n"; + } + break; + case CExplorerFile::CODE: + { + ostr << "Bytes: " << GetName() << "\r\n"; + ostr << "Addr: " << m_param1 << "\r\n"; + } + break; + } + + ostr << "Size: " << m_filesize << "\r\n"; + } + + ostr << "Disk: " << m_disk_file_length << "\r\n"; + ostr << "BlkC: " << block_count << "\r\n"; + + ostr << "Using Blocks:"; + + // we'll verify that we have all extents by just counting the map entries + bool missing_entries = false; + unsigned int map_counter = 0; + + TExtentMapIterator map_it = extent_map.begin(); + int line_count = 0; + while (map_it != extent_map.end()) + { + if (map_counter != (*map_it).first) + { + missing_entries = true; + } + map_counter++; + + line_count++; + if (16 == line_count) + { + ostr << "\r\n" << (*map_it).second; + line_count = 0; + } + else + { + ostr << "," << (*map_it).second; + } + ++map_it; + } + + if (missing_entries) + ostr << "\r\n\r\nWARNING - MAY BE MISSING DIRECTORY ENTRIES!"; + + return (ostr.str()); +} + +void CPlus3DOS_DirectoryEntry::ExtractObject(CExplorerFile::TEXTRACTABLE_FORMATS _format, bool _include_header) +{ + // here we want to read in the blocks of the file + // and add them to a new CExplorerFile object. + + ExamineHeader(); + + if (extent_map.size()) + { + int sector_offset = (m_bHeaderless || _include_header) ? 0 : sizeof(T_PLUS3DOS_HEADER); + + // now create a new file and fill it with data... + CExplorerFile the_file(m_strName, NULL, 0, m_filetype, m_param1, m_param2); + + ReadFile(&the_file, sector_offset); + + // right, we've done that... + // now we want to write it out to a file... + + try + { + std::string filename = m_strName; + int result = 0; + char * the_data; + + switch(_format) + { + case CExplorerFile::TAPFILE: + filename += ".tap"; + result = the_file.GenerateTapfile(&the_data, false); + break; + case CExplorerFile::TAPFILE_HEADERLESS: + filename += ".tap"; + result = the_file.GenerateTapfile(&the_data, true); + break; + case CExplorerFile::BINFILE: + filename += ".bin"; + result = the_file.GenerateBinfile(&the_data); + break; + default: + break; + } + + if (result) + { + HANDLE xxx = CreateFile(filename.c_str(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); + DWORD written; + WriteFile(xxx, the_data, result, &written, NULL); + CloseHandle(xxx); + } + } + catch (CExplorerFile::EExplorerFile &e) + { + ::AfxMessageBox(e.what(), 0, 0); + } + } +} + +void CPlus3DOS_DirectoryEntry::ExamineHeader() +{ + // this method looks at the first sector of the file to see if it's got a header. + // if it has, it rips the information out. + // if it hasn't... it makes it up :) + + // only do this if we've not done it already, and the file has any data + if (extent_map.size() && !m_bHeaderExamined) + { + CCHS chs; + + // before we do anything else, work out if we're headerless... + WORD first_block = (*extent_map.begin()).second; + m_pPartition->BlockToCHS(first_block, chs); + + char * one_sector; + unsigned int sec_size = m_p3eDoc->ReadSector(chs, &one_sector); + + T_PLUS3DOS_HEADER *plus3dos_header = reinterpret_cast(one_sector); + + std::string header_magic; + header_magic.append(plus3dos_header->magic, 8); + if (!PLUS3DOS_Signature.compare(header_magic) && 0x1a == plus3dos_header->soft_eof) + { + // looks good so far... work out the checksum... + unsigned int chk = 0; + for (int chk_count = 0; chk_count < sizeof(T_PLUS3DOS_HEADER) - 1; chk_count++) + { + chk += one_sector[chk_count]; + } + + chk %= 0x100; + + if (chk == plus3dos_header->checksum) + { + m_bHeaderless = false; + } + } + + if (m_bHeaderless) + { + // it's a headerless file so fill in the gaps + m_disk_file_length = extent_map.size() * m_pPartition->BlockSize(); + m_filesize = m_disk_file_length > 0xffff ? 0xffff : m_disk_file_length; + + if (CTreeObject::PLUS3DOS_FILE_ERASED != m_tType) + { + m_tType = CTreeObject::PLUS3DOS_FILE_HEADERLESS; + } + } + else + { + // take the data we want out of the header + m_filetype = static_cast(plus3dos_header->type); + m_param1 = plus3dos_header->param1; + m_param2 = plus3dos_header->param2; + m_disk_file_length = plus3dos_header->length; + m_filesize = plus3dos_header->filesize; + m_issue = plus3dos_header->issue; + m_version = plus3dos_header->version; + + if (CTreeObject::PLUS3DOS_FILE_ERASED != m_tType) + { + // alter the file type + switch (m_filetype) + { + case CExplorerFile::PROGRAM: + m_tType = CTreeObject::PLUS3DOS_FILE_PROGRAM; + break; + case CExplorerFile::NUMBER_ARRAY: + m_tType = CTreeObject::PLUS3DOS_FILE_NUMARRAY; + break; + case CExplorerFile::CHARACTER_ARRAY: + m_tType = CTreeObject::PLUS3DOS_FILE_CHARARRAY; + break; + case CExplorerFile::CODE: + m_tType = CTreeObject::PLUS3DOS_FILE_CODE; + break; + } + } + } + } + + m_bHeaderExamined = true; +} + +void CPlus3DOS_DirectoryEntry::ReadFile(CExplorerFile *_file, unsigned int _skip) +{ + TExtentMapIterator map_it = extent_map.begin(); + + unsigned int filesize_left = m_disk_file_length - _skip; + + // now loop through the blocks and add them. + bool carry_on = true; + while (0 < filesize_left && carry_on) + { + if (map_it != extent_map.end()) + { + // ok, get the block id... + WORD block = (*map_it).second; + CCHS chs; + m_pPartition->BlockToCHS(block, chs); + + int sectors_in_block = m_pPartition->SectorsInABlock() - 1; + + // read in this sector... + char * sector; + int sec_size = m_p3eDoc->ReadSector(chs, §or); + + do + { + // handle this sector + sec_size -= _skip; + if (sec_size > filesize_left) + sec_size = filesize_left; + + _file->AddData(§or[_skip], sec_size); + + filesize_left -= sec_size; + + if (filesize_left) + { + sec_size = m_p3eDoc->ReadNextSector(§or); + } + + // it's only the first block we want to skip the start of... + _skip = 0; + } while (sectors_in_block-- && 0 < filesize_left); + + map_it++; + } + else + { + carry_on = false; + } + } +} diff --git a/Plus3DOS_DirectoryEntry.h b/Plus3DOS_DirectoryEntry.h new file mode 100644 index 0000000..a487c26 --- /dev/null +++ b/Plus3DOS_DirectoryEntry.h @@ -0,0 +1,105 @@ +// Plus3DOS_DirectoryEntry.h: interface for the CPlus3DOS_DirectoryEntry class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_PLUS3DOS_DIRECTORYENTRY_H__8A127574_D8EA_4E50_B57C_9A0DA2F65AF3__INCLUDED_) +#define AFX_PLUS3DOS_DIRECTORYENTRY_H__8A127574_D8EA_4E50_B57C_9A0DA2F65AF3__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "TreeObject.h" +#include "IDEDOS_Plus3DOSPartition.h" +#include "ExplorerFile.h" + +class CPlus3DOS_DirectoryEntry : public CTreeObject +{ +public: + CPlus3DOS_DirectoryEntry(CMy3eExplorerDoc * _p3eDoc, TreeObjectType _type, char * block, const CIDEDOS_Plus3DOSPartition *_partition); + virtual ~CPlus3DOS_DirectoryEntry(); + + virtual void ExtractObject(CExplorerFile::TEXTRACTABLE_FORMATS _format, bool _include_header = false); + + // this type allows us to map an extent to a vector of blocks + typedef std::map TExtentMap; + typedef std::map::iterator TExtentMapIterator; + +#pragma pack(push, 1) + typedef struct { + CHAR magic[8]; + CHAR soft_eof; + CHAR issue; + CHAR version; + DWORD length; // usually it's the filesize + 128 + BYTE type; + WORD filesize; + WORD param1; + WORD param2; + BYTE _reserved1; + CHAR _reserved2[104]; + BYTE checksum; + } T_PLUS3DOS_HEADER; + + + typedef struct { + BYTE user_number; + BYTE filename[8]; + union ext_t + { + struct { + BYTE read_only; + BYTE system_file; + BYTE archived; + } flags; + BYTE extension[3]; + } ext; + BYTE extent_low; + BYTE s1_reserved; + BYTE extent_high; + BYTE records_used; + union alloc_t + { + struct { + WORD al[8]; + } al_16; + struct { + BYTE al[16]; + } al_8; + } alloc; + + int g_extent(BYTE _exm) const {return (((int)extent_low + (32 * (int)extent_high)) / (_exm + 1));}; + bool g_readonly() const {return ((ext.flags.read_only & 0x80) == 0x80);}; + bool g_systemfile() const {return ((ext.flags.system_file & 0x80) == 0x80);}; + bool g_archived() const {return ((ext.flags.archived & 0x80) == 0x80);}; + } T_IDEDOS_PLUS3DOS_DIRECTORY_ENTRY; +#pragma pack(pop) + + +public: + void ReadFile(CExplorerFile *_file, unsigned int _skip = 0); + void ExamineHeader(); + void AddExtent(char * block); + virtual std::string GetInformation(); + +protected: + //T_IDEDOS_PLUS3DOS_DIRECTORY_ENTRY m_directory_entry; + const CIDEDOS_Plus3DOSPartition *m_pPartition; + TExtentMap extent_map; + static std::string PLUS3DOS_Signature; + +protected: + bool m_bHeaderless; + CExplorerFile::TFILETYPE m_filetype; + WORD m_param1; + WORD m_param2; + DWORD m_disk_file_length; // usually filesize + 128 + WORD m_filesize; + CHAR m_issue; + CHAR m_version; + +private: + bool m_bHeaderExamined; +}; + +#endif // !defined(AFX_PLUS3DOS_DIRECTORYENTRY_H__8A127574_D8EA_4E50_B57C_9A0DA2F65AF3__INCLUDED_) diff --git a/Plus3DOS_UserArea.cpp b/Plus3DOS_UserArea.cpp new file mode 100644 index 0000000..ff2c16a --- /dev/null +++ b/Plus3DOS_UserArea.cpp @@ -0,0 +1,216 @@ +// Plus3DOS_UserArea.cpp: implementation of the CPlus3DOS_UserArea class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "3eExplorer.h" +#include "Plus3DOS_UserArea.h" +#include "Plus3DOS_DirectoryEntry.h" +#include "3eExplorerDoc.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +std::map CPlus3DOS_UserArea::user_area_id; + +CPlus3DOS_UserArea::CPlus3DOS_UserArea(CMy3eExplorerDoc * _p3eDoc, BYTE _id, std::string _partition_name, unsigned int _cc, unsigned int _tt, unsigned int _ss, unsigned int _max_dir_entries, const CIDEDOS_Plus3DOSPartition *_partition) +: CTreeObject(_p3eDoc) +, m_start_cc(_cc) +, m_start_tt(_tt) +, m_start_ss(_ss) +, m_max_dir_entries(_max_dir_entries) +, m_user_id(_id) +, m_partition(_partition) +{ + if (0 == user_area_id.size()) + { + // initialised the static map + user_area_id[0x00] = "0:"; + user_area_id[0x01] = "1:"; + user_area_id[0x02] = "2:"; + user_area_id[0x03] = "3:"; + user_area_id[0x04] = "4:"; + user_area_id[0x05] = "5:"; + user_area_id[0x06] = "6:"; + user_area_id[0x07] = "7:"; + user_area_id[0x08] = "8:"; + user_area_id[0x09] = "9:"; + user_area_id[0x0a] = "a:"; + user_area_id[0x0b] = "b:"; + user_area_id[0x0c] = "c:"; + user_area_id[0x0d] = "d:"; + user_area_id[0x0e] = "e:"; + user_area_id[0x0f] = "f:"; + user_area_id[0xe5] = "x:"; + } + + // first a little bit of "protection" + // check that the user-area if valid. 0x00..0x0f, or 0xe5 + // if _id isn't one of those, throw an exception. + + std::map::iterator it = user_area_id.find(_id); + + if (it != user_area_id.end()) + { + // we're ok + if ((BYTE)0xe5 == _id) + { + m_tType = CTreeObject::PLUS3DOS_USER_DIRECTORY_ERASED; + } + else + { + m_tType = CTreeObject::PLUS3DOS_USER_DIRECTORY; + } + + m_strName = (*it).second; + m_strName += _partition_name; + } + else + { + // not a valid user area, so throw an exception + std::ostringstream except; + except << std::ios::hex << _id << " is not a valid user area"; + throw EUserAreaInvalid(except.str()); + } + + // allow extraction of the contents of this user area... + //m_bExtractable = TRUE; +} + +CPlus3DOS_UserArea::~CPlus3DOS_UserArea() +{ + +} + +std::string CPlus3DOS_UserArea::GetInformation() +{ + std::ostringstream ostr; + ostr << "User Partition: " << (int)m_user_id << "\r\n"; + ostr << "You can call me " << GetName(); + return (ostr.str()); +} + +void CPlus3DOS_UserArea::ParseForChildren() +{ + // here we want to go through the directory entries finding any from our user-area + // and adding them. + // note that if our user-area of 0xe5 it means we are "deleted files". + + // mostly this is very similar to the code in CIDEDOS_Plus3DOSPartition::ParseForChildren + // however, we need to remember that a single file can take up several directory entries. + // so... we'll temporarily store new CPlus3DOS_DirectoryEntry objects in a map based on name. + + // go through the directory entries and find an entry of this user id + char * ss_buff = NULL; + unsigned int dir_pointer = 0; + unsigned int sector_size = m_p3eDoc->ReadSector(CCHS(m_start_cc, m_start_tt, m_start_ss), &ss_buff); + + unsigned int cur_dir_entry = 0; + + const unsigned int dir_entry_size = sizeof(CPlus3DOS_DirectoryEntry::T_IDEDOS_PLUS3DOS_DIRECTORY_ENTRY); + + std::map files; + + CTreeObject::TreeObjectType file_type = CTreeObject::PLUS3DOS_FILE; + if (0xe5 == m_user_id) + { + file_type = CTreeObject::PLUS3DOS_FILE_ERASED; + } + + do + { + // map the current directory entry + CPlus3DOS_DirectoryEntry::T_IDEDOS_PLUS3DOS_DIRECTORY_ENTRY *dir_entry; + dir_entry = reinterpret_cast(&ss_buff[dir_pointer]); + + bool valid = false; + + // check the type + if (dir_entry->user_number == m_user_id) + { + // we may have an entry... if it's 0xe5 it could simply be blank, so check the remaining bytes... + if (0xe5 == m_user_id) + { + // for now, just check the first byte of the filename... + if (0xe5 != dir_entry->filename[0]) + { + valid = true; + } + } + else + { + valid = true; + } + + if (valid) + { + // ok. first up, check to see if this filename already exists... + std::string filename; + filename.append(reinterpret_cast(dir_entry->filename), 8); + filename.append(reinterpret_cast(dir_entry->ext.extension), 3); + + std::map::iterator it = files.find(filename); + + if (it == files.end()) + { + // it's a new entry so create it + // note, we're passing in the parent partition, so we can get xdpb information + CPlus3DOS_DirectoryEntry *obj = new CPlus3DOS_DirectoryEntry(m_p3eDoc, file_type, &ss_buff[dir_pointer], m_partition); + files[filename] = obj; + } + else + { + // it's most likely a continuation, so we need to add it to an existing object + (*it).second->AddExtent(&ss_buff[dir_pointer]); + } + + // create a new directory entry and add it here. + //CPlus3DOS_DirectoryEntry *directory = new CPlus3DOS_UserArea(user_id, GetName(), start_cyl, start_trk, start_sec, max_dir_entries); + //m_vChildren.push_back(user_area); + } + } + + // select next directory entry + cur_dir_entry++; + if (cur_dir_entry < m_max_dir_entries) + { + dir_pointer += dir_entry_size; + if (dir_pointer >= sector_size) + { + dir_pointer = 0; + // read the next sector + sector_size = m_p3eDoc->ReadNextSector(&ss_buff); + } + } + } while (cur_dir_entry < m_max_dir_entries); + + + // now we've done that, we want to step through the map we made and push them into m_vChildren + std::map::iterator it = files.begin(); + + while (it != files.end()) + { + // should we pre-read the file info? + if (theApp.PreReadFileInfo()) + { + (*it).second->ExamineHeader(); + } + m_vChildren.push_back((*it).second); + ++it; + } + + // phew, now we can ensure this doesn't happen again... + CTreeObject::ParseForChildren(); +} + +void CPlus3DOS_UserArea::ExtractObject(CExplorerFile::TEXTRACTABLE_FORMATS _format, bool _include_header) +{ + // here we should create a new directory and set that as our "current" dir. +} diff --git a/Plus3DOS_UserArea.h b/Plus3DOS_UserArea.h new file mode 100644 index 0000000..0e3d397 --- /dev/null +++ b/Plus3DOS_UserArea.h @@ -0,0 +1,38 @@ +// Plus3DOS_UserArea.h: interface for the CPlus3DOS_UserArea class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_PLUS3DOS_USERAREA_H__6B31F7E1_171E_41C0_9942_A381C50586D2__INCLUDED_) +#define AFX_PLUS3DOS_USERAREA_H__6B31F7E1_171E_41C0_9942_A381C50586D2__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "TreeObject.h" +#include "IDEDOS_Plus3DOSPartition.h" + +class CPlus3DOS_UserArea : public CTreeObject +{ +public: + class EUserAreaInvalid : public std::runtime_error {public: EUserAreaInvalid(const std::string &what_arg) : std::runtime_error(what_arg) {};}; + virtual void ExtractObject(CExplorerFile::TEXTRACTABLE_FORMATS _format, bool _include_header = false); + virtual void ParseForChildren(); + virtual std::string GetInformation(); + CPlus3DOS_UserArea(CMy3eExplorerDoc * _p3eDoc, BYTE _id, std::string _partition_name, unsigned int _cc, unsigned int _tt, unsigned int _ss, unsigned int _max_dir_entries, const CIDEDOS_Plus3DOSPartition *_partition); + virtual ~CPlus3DOS_UserArea(); + +protected: + unsigned int m_start_cc; // start cylinder of directory records + unsigned int m_start_tt; // start track of directory records + unsigned int m_start_ss; // start sector of directory records (probably 0) + unsigned int m_max_dir_entries; // maximum number of directory entries. each entry is 32b + BYTE m_user_id; + + const CIDEDOS_Plus3DOSPartition *m_partition; + +private: + static std::map user_area_id; +}; + +#endif // !defined(AFX_PLUS3DOS_USERAREA_H__6B31F7E1_171E_41C0_9942_A381C50586D2__INCLUDED_) diff --git a/ReadMe.txt b/ReadMe.txt new file mode 100644 index 0000000..dceb25b --- /dev/null +++ b/ReadMe.txt @@ -0,0 +1,117 @@ +======================================================================== + MICROSOFT FOUNDATION CLASS LIBRARY : 3eExplorer +======================================================================== + + +AppWizard has created this 3eExplorer application for you. This application +not only demonstrates the basics of using the Microsoft Foundation classes +but is also a starting point for writing your application. + +This file contains a summary of what you will find in each of the files that +make up your 3eExplorer application. + +3eExplorer.dsp + This file (the project file) contains information at the project level and + is used to build a single project or subproject. Other users can share the + project (.dsp) file, but they should export the makefiles locally. + +3eExplorer.h + This is the main header file for the application. It includes other + project specific headers (including Resource.h) and declares the + CMy3eExplorerApp application class. + +3eExplorer.cpp + This is the main application source file that contains the application + class CMy3eExplorerApp. + +3eExplorer.rc + This is a listing of all of the Microsoft Windows resources that the + program uses. It includes the icons, bitmaps, and cursors that are stored + in the RES subdirectory. This file can be directly edited in Microsoft + Visual C++. + +3eExplorer.clw + This file contains information used by ClassWizard to edit existing + classes or add new classes. ClassWizard also uses this file to store + information needed to create and edit message maps and dialog data + maps and to create prototype member functions. + +res\3eExplorer.ico + This is an icon file, which is used as the application's icon. This + icon is included by the main resource file 3eExplorer.rc. + +res\3eExplorer.rc2 + This file contains resources that are not edited by Microsoft + Visual C++. You should place all resources not editable by + the resource editor in this file. + + + +///////////////////////////////////////////////////////////////////////////// + +For the main frame window: + +MainFrm.h, MainFrm.cpp + These files contain the frame class CMainFrame, which is derived from + CMDIFrameWnd and controls all MDI frame features. + +res\Toolbar.bmp + This bitmap file is used to create tiled images for the toolbar. + The initial toolbar and status bar are constructed in the CMainFrame + class. Edit this toolbar bitmap using the resource editor, and + update the IDR_MAINFRAME TOOLBAR array in 3eExplorer.rc to add + toolbar buttons. +///////////////////////////////////////////////////////////////////////////// + +For the child frame window: + +ChildFrm.h, ChildFrm.cpp + These files define and implement the CChildFrame class, which + supports the child windows in an MDI application. + +///////////////////////////////////////////////////////////////////////////// + +AppWizard creates one document type and one view: + +3eExplorerDoc.h, 3eExplorerDoc.cpp - the document + These files contain your CMy3eExplorerDoc class. Edit these files to + add your special document data and to implement file saving and loading + (via CMy3eExplorerDoc::Serialize). + +3eExplorerView.h, 3eExplorerView.cpp - the view of the document + These files contain your CMy3eExplorerView class. + CMy3eExplorerView objects are used to view CMy3eExplorerDoc objects. + +res\3eExplorerDoc.ico + This is an icon file, which is used as the icon for MDI child windows + for the CMy3eExplorerDoc class. This icon is included by the main + resource file 3eExplorer.rc. + + +///////////////////////////////////////////////////////////////////////////// +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named 3eExplorer.pch and a precompiled types file named StdAfx.obj. + +Resource.h + This is the standard header file, which defines new resource IDs. + Microsoft Visual C++ reads and updates this file. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" to indicate parts of the source code you +should add to or customize. + +If your application uses MFC in a shared DLL, and your application is +in a language other than the operating system's current language, you +will need to copy the corresponding localized resources MFC42XXX.DLL +from the Microsoft Visual C++ CD-ROM onto the system or system32 directory, +and rename it to be MFCLOC.DLL. ("XXX" stands for the language abbreviation. +For example, MFC42DEU.DLL contains resources translated to German.) If you +don't do this, some of the UI elements of your application will remain in the +language of the operating system. + +///////////////////////////////////////////////////////////////////////////// diff --git a/Resource.h b/Resource.h new file mode 100644 index 0000000..5900320 --- /dev/null +++ b/Resource.h @@ -0,0 +1,37 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by 3eExplorer.rc +// +#define ID_EXPLORER_FILENAME 1 +#define IDD_ABOUTBOX 100 +#define IDR_MAINFRAME 128 +#define IDR_MY3EEXTYPE 129 +#define IDB_TREE_ICONS 130 +#define IDB_TREE_ICONS_MASK 131 +#define IDR_EXPLORER_POPUP_FILE 133 +#define IDD_OPTIONS 134 +#define IDR_EXPLORER_POPUP 134 +#define IDD_STATUS 135 +#define IDC_ABOUT_TEXT 1000 +#define IDC_PRECACHE_TRACK 1001 +#define IDC_PRE_READ_HEADER 1002 +#define IDC_FLUSH_CACHE 1005 +#define IDC_STATUS_LISTBOX 1007 +#define ID_EXPLORER_EXTRACT_TO_TAP 32771 +#define ID_VIEW_OPTIONS 32773 +#define ID_EXPLORER_EXTRACT_TO_BIN 32775 +#define ID_EXPLORER_EXTRACT_TO_BIN_WITH_HEADER 32776 +#define ID_FILE_FILESTATUS 32777 +#define IDS_FILE_ACCESS_ERROR 61216 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_3D_CONTROLS 1 +#define _APS_NEXT_RESOURCE_VALUE 138 +#define _APS_NEXT_COMMAND_VALUE 32778 +#define _APS_NEXT_CONTROL_VALUE 1008 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/StatusView.cpp b/StatusView.cpp new file mode 100644 index 0000000..efa548f --- /dev/null +++ b/StatusView.cpp @@ -0,0 +1,59 @@ +// StatusView.cpp : implementation file +// + +#include "stdafx.h" +#include "3eExplorer.h" +#include "StatusView.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CStatusView + +IMPLEMENT_DYNCREATE(CStatusView, CListView) + +CStatusView::CStatusView() +{ +} + +CStatusView::~CStatusView() +{ +} + + +BEGIN_MESSAGE_MAP(CStatusView, CListView) + //{{AFX_MSG_MAP(CStatusView) + // NOTE - the ClassWizard will add and remove mapping macros here. + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CStatusView drawing + +void CStatusView::OnDraw(CDC* pDC) +{ + CDocument* pDoc = GetDocument(); + // TODO: add draw code here +} + +///////////////////////////////////////////////////////////////////////////// +// CStatusView diagnostics + +#ifdef _DEBUG +void CStatusView::AssertValid() const +{ + CListView::AssertValid(); +} + +void CStatusView::Dump(CDumpContext& dc) const +{ + CListView::Dump(dc); +} +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CStatusView message handlers diff --git a/StatusView.h b/StatusView.h new file mode 100644 index 0000000..570ac17 --- /dev/null +++ b/StatusView.h @@ -0,0 +1,53 @@ +#if !defined(AFX_STATUSVIEW_H__A6B764BD_7164_46ED_A961_EFCE5F68C69A__INCLUDED_) +#define AFX_STATUSVIEW_H__A6B764BD_7164_46ED_A961_EFCE5F68C69A__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// StatusView.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CStatusView view + +class CStatusView : public CListView +{ +protected: + CStatusView(); // protected constructor used by dynamic creation + DECLARE_DYNCREATE(CStatusView) + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CStatusView) + protected: + virtual void OnDraw(CDC* pDC); // overridden to draw this view + //}}AFX_VIRTUAL + +// Implementation +protected: + virtual ~CStatusView(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + + // Generated message map functions +protected: + //{{AFX_MSG(CStatusView) + // NOTE - the ClassWizard will add and remove member functions here. + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STATUSVIEW_H__A6B764BD_7164_46ED_A961_EFCE5F68C69A__INCLUDED_) diff --git a/StdAfx.cpp b/StdAfx.cpp new file mode 100644 index 0000000..05140bd --- /dev/null +++ b/StdAfx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// 3eExplorer.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + + + diff --git a/StdAfx.h b/StdAfx.h new file mode 100644 index 0000000..7661aea --- /dev/null +++ b/StdAfx.h @@ -0,0 +1,34 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__F54A4594_4414_43DD_B007_E5AB5431086C__INCLUDED_) +#define AFX_STDAFX_H__F54A4594_4414_43DD_B007_E5AB5431086C__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +#include // MFC core and standard components +#include // MFC extensions +#include // MFC Automation classes +#include // MFC support for Internet Explorer 4 Common Controls +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT + +#pragma warning(disable:4786) +#include +#include +#include +#include +#include + + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__F54A4594_4414_43DD_B007_E5AB5431086C__INCLUDED_) diff --git a/TreeObject.cpp b/TreeObject.cpp new file mode 100644 index 0000000..0e1e3ee --- /dev/null +++ b/TreeObject.cpp @@ -0,0 +1,58 @@ +// TreeObject.cpp: implementation of the CTreeObject class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "3eExplorer.h" +#include "TreeObject.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CTreeObject::CTreeObject(CMy3eExplorerDoc * _p3eDoc, CTreeObject::TreeObjectType _type, const char * _name, bool _expand, bool _extract) +: m_tType(_type) +, m_strName(_name) +, m_bParsedForChildren(FALSE) +, m_bExpandable(_expand) +, m_bExtractable(_extract) +, m_p3eDoc(_p3eDoc) +{ + ASSERT (NULL != _p3eDoc); +} + +CTreeObject::~CTreeObject() +{ + // iterate through the children and delete them + std::vector::iterator it = GetChildren(); + while (it != GetChildrenEnd()) + { + delete (*it); + it++; + } + m_vChildren.clear(); +} + +void CTreeObject::ParseForChildren() +{ + // a default object has no children + m_bParsedForChildren = TRUE; +} + +std::string CTreeObject::GetInformation() +{ + std::ostringstream ostr; + ostr << "INVALID: " << GetName(); + return (ostr.str()); +} + +void CTreeObject::ExtractObject(CExplorerFile::TEXTRACTABLE_FORMATS _format, bool _include_header) +{ + // default to doing nothing +} diff --git a/TreeObject.h b/TreeObject.h new file mode 100644 index 0000000..754dd94 --- /dev/null +++ b/TreeObject.h @@ -0,0 +1,68 @@ +// TreeObject.h: interface for the CTreeObject class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_TREEOBJECT_H__3FAB6305_33FD_42EE_80C1_1FA3A5598C3A__INCLUDED_) +#define AFX_TREEOBJECT_H__3FAB6305_33FD_42EE_80C1_1FA3A5598C3A__INCLUDED_ + + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +class CMy3eExplorerDoc; +#include "ExplorerFile.h" + +class CTreeObject : public CObject +{ +public: + // _format : what format are we outputting to? + // _include_header : do we include the, for example, +3dos header in the output? + virtual void ExtractObject(CExplorerFile::TEXTRACTABLE_FORMATS _format, bool _include_header = false); + typedef enum + { + INVALID = 0, + HDF = 1, + PARTITION_PLUSIDEDOS = 2, + PARTITION_PLUS3DOS = 3, + PLUS3DOS_USER_DIRECTORY = 0x100 + 0x00, + PLUS3DOS_FILE = 0x100 + 0x01, + PLUS3DOS_FILE_PROGRAM = 0x100 + 0x02, + PLUS3DOS_FILE_NUMARRAY = 0x100 + 0x03, + PLUS3DOS_FILE_CHARARRAY = 0x100 + 0x04, + PLUS3DOS_FILE_CODE = 0x100 + 0x05, + PLUS3DOS_FILE_HEADERLESS = 0x100 + 0x06, + PLUS3DOS_USER_DIRECTORY_ERASED = 0x100 + 0x10, + PLUS3DOS_FILE_ERASED = 0x100 + 0x11, + RESERVED = 65535 + } TreeObjectType; + + typedef std::vector TTree; + typedef std::vector::iterator TTreeIterator; + + CTreeObject(CMy3eExplorerDoc * _p3eDoc, TreeObjectType _type = INVALID, const char * _name = _T(""), bool _expand = true, bool _extract = false); + virtual ~CTreeObject(); + const TreeObjectType Type() const {return m_tType;}; + + std::vector::iterator GetChildren() {return m_vChildren.begin();}; + std::vector::iterator GetChildrenEnd() {return m_vChildren.end();}; + virtual void ParseForChildren(); + + std::string GetName() const {return m_strName;}; + BOOL IsExpandable() const {return m_bExpandable;}; + BOOL IsExtractable() const {return m_bExtractable;}; + + virtual std::string GetInformation(); + +protected: + TreeObjectType m_tType; + BOOL m_bParsedForChildren; + std::vector m_vChildren; + std::string m_strName; + BOOL m_bExpandable; + BOOL m_bExtractable; + CMy3eExplorerDoc *m_p3eDoc; + +}; + +#endif // !defined(AFX_TREEOBJECT_H__3FAB6305_33FD_42EE_80C1_1FA3A5598C3A__INCLUDED_) diff --git a/descript.ion b/descript.ion new file mode 100644 index 0000000..e69de29 diff --git a/memdc.h b/memdc.h new file mode 100644 index 0000000..be3bdda --- /dev/null +++ b/memdc.h @@ -0,0 +1,110 @@ +#ifndef _MEMDC_H_ +#define _MEMDC_H_ + +////////////////////////////////////////////////// +// CMemDC - memory DC +// +// Author: Keith Rule +// Email: keithr@europa.com +// Copyright 1996-2002, Keith Rule +// +// You may freely use or modify this code provided this +// Copyright is included in all derived versions. +// +// History - 10/3/97 Fixed scrolling bug. +// Added print support. - KR +// +// 11/3/99 Fixed most common complaint. Added +// background color fill. - KR +// +// 11/3/99 Added support for mapping modes other than +// MM_TEXT as suggested by Lee Sang Hun. - KR +// +// 02/11/02 Added support for CScrollView as supplied +// by Gary Kirkham. - KR +// +// This class implements a memory Device Context which allows +// flicker free drawing. + +class CMemDC : public CDC { +private: + CBitmap m_bitmap; // Offscreen bitmap + CBitmap* m_oldBitmap; // bitmap originally found in CMemDC + CDC* m_pDC; // Saves CDC passed in constructor + CRect m_rect; // Rectangle of drawing area. + BOOL m_bMemDC; // TRUE if CDC really is a Memory DC. +public: + + CMemDC(CDC* pDC, const CRect* pRect = NULL) : CDC() + { + ASSERT(pDC != NULL); + + // Some initialization + m_pDC = pDC; + m_oldBitmap = NULL; + m_bMemDC = !pDC->IsPrinting(); + + // Get the rectangle to draw + if (pRect == NULL) { + pDC->GetClipBox(&m_rect); + } else { + m_rect = *pRect; + } + + if (m_bMemDC) { + // Create a Memory DC + CreateCompatibleDC(pDC); + pDC->LPtoDP(&m_rect); + + m_bitmap.CreateCompatibleBitmap(pDC, m_rect.Width(), m_rect.Height()); + m_oldBitmap = SelectObject(&m_bitmap); + + SetMapMode(pDC->GetMapMode()); + + SetWindowExt(pDC->GetWindowExt()); + SetViewportExt(pDC->GetViewportExt()); + + pDC->DPtoLP(&m_rect); + SetWindowOrg(m_rect.left, m_rect.top); + } else { + // Make a copy of the relevent parts of the current DC for printing + m_bPrinting = pDC->m_bPrinting; + m_hDC = pDC->m_hDC; + m_hAttribDC = pDC->m_hAttribDC; + } + + // Fill background + FillSolidRect(m_rect, pDC->GetBkColor()); + } + + ~CMemDC() + { + if (m_bMemDC) { + // Copy the offscreen bitmap onto the screen. + m_pDC->BitBlt(m_rect.left, m_rect.top, m_rect.Width(), m_rect.Height(), + this, m_rect.left, m_rect.top, SRCCOPY); + + //Swap back the original bitmap. + SelectObject(m_oldBitmap); + } else { + // All we need to do is replace the DC with an illegal value, + // this keeps us from accidently deleting the handles associated with + // the CDC that was passed to the constructor. + m_hDC = m_hAttribDC = NULL; + } + } + + // Allow usage as a pointer + CMemDC* operator->() + { + return this; + } + + // Allow usage as a pointer + operator CMemDC*() + { + return this; + } +}; + +#endif \ No newline at end of file diff --git a/plus3dos.zsm b/plus3dos.zsm new file mode 100644 index 0000000..0ada93a --- /dev/null +++ b/plus3dos.zsm @@ -0,0 +1,6836 @@ +; +; Disassembled by Garry Lancaster with the aid of dZ80 v1.10 +; Mail comments to dharkhig@menaxus.demon.co.uk +; +; Thanks to John Elliott for a partial disassembly +; +; This file can be assembled to produce a binary image of the ROM +; with Interlogic's Z80ASM assembler (available for Z88, QL, DOS +; and Linux) + + + + module rom2 + + + org $0000 + + + include "sysvar48.def" + include "sysvarp3.def" + include "sysvarp7.def" + include "p3dos.def" + + +.l0000 defs 8 +.l0008 defm "PLUS3DOS" +.l0010 defs 40 + + + +; The maskable interrupt routine + + +.l0038 push af + push hl + ld hl,(FRAMES) + inc hl ; increment FRAMES + ld (FRAMES),hl + ld a,h + or l + jr nz,l0048 + inc (iy+$40) ; increment high byte of FRAMES +.l0048 push bc + push de + call l2358 ; scan the keyboard + call l0068 ; test for disk motor timeout + pop de + pop bc + pop hl + pop af + ei + ret + + defs 16 + +; The Non-maskable interrupt + +.l0066 retn ; do nothing + +; The disk motor timeout subroutine + +.l0068 ld bc,$7ffd + ld a,(BANKM) + or $07 + out (c),a ; page in page 7 + ld a,(timeout) + or a + jr z,l0095 ; exit if motor already off + ld a,(FRAMES) + bit 0,a + jr nz,l0095 ; only decrement count every other frame + ld a,(timeout) + dec a ; decrement timeout count + ld (timeout),a + jr nz,l0095 ; exit if not yet zero + ld bc,$1ffd + ld a,(BANK678) + and $f7 + ld (BANK678),a + out (c),a ; turn motor off +.l0095 ld bc,$7ffd + ld a,(BANKM) + out (c),a ; restore memory configuration + ret + + defs 98 + + +; The DOS routines jump block + +.l0100 jp l019f ; DOS_INITIALISE +.l0103 jp l01cd ; DOS_VERSION +.l0106 jp l062d ; DOS_OPEN +.l0109 jp l0740 ; DOS_CLOSE +.l010c jp l0761 ; DOS_ABANDON +.l010f jp l08b1 ; DOS_REF_HEAD +.l0112 jp l10ea ; DOS_READ +.l0115 jp l11fe ; DOS_WRITE +.l0118 jp l11a8 ; DOS_BYTE_READ +.l011b jp l1298 ; DOS_BYTE_WRITE +.l011e jp l0a19 ; DOS_CATALOG +.l0121 jp l08f2 ; DOS_FREE_SPACE +.l0124 jp l0924 ; DOS_DELETE +.l0127 jp l096f ; DOS_RENAME +.l012a jp l1ace ; DOS_BOOT +.l012d jp l090f ; DOS_SET_DRIVE +.l0130 jp l08fc ; DOS_SET_USER +.l0133 jp l1070 ; DOS_GET_POSITION +.l0136 jp l108c ; DOS_SET_POSITION +.l0139 jp l1079 ; DOS_GET_EOF +.l013c jp l01d8 ; DOS_GET_1346 +.l013f jp l01de ; DOS_SET_1346 +.l0142 jp l05c2 ; DOS_FLUSH +.l0145 jp l08c3 ; DOS_SET_ACCESS +.l0148 jp l0959 ; DOS_SET_ATTRIBUTES +.l014b jp l0706 ; DOS_OPEN_DRIVE +.l014e jp l02e8 ; DOS_SET_MESSAGE +.l0151 jp l1847 ; DOS_REF_XDPB +.l0154 jp l1943 ; DOS_MAP_B +.l0157 jp l1f27 ; DD_INTERFACE +.l015a jp l1f32 ; DD_INIT +.l015d jp l1f47 ; DD_SETUP +.l0160 jp l1e7c ; DD_SET_RETRY +.l0163 jp l1bff ; DD_READ_SECTOR +.l0166 jp l1c0d ; DD_WRITE_SECTOR +.l0169 jp l1c16 ; DD_CHECK_SECTOR +.l016c jp l1c24 ; DD_FORMAT +.l016f jp l1c36 ; DD_READ_ID +.l0172 jp l1e65 ; DD_TEST_UNSUITABLE +.l0175 jp l1c80 ; DD_LOGIN +.l0178 jp l1cdb ; DD_SEL_FORMAT +.l017b jp l1edd ; DD_ASK_1 +.l017e jp l1ee9 ; DD_DRIVE_STATUS +.l0181 jp l1e75 ; DD_EQUIPMENT +.l0184 jp l1bda ; DD_ENCODE +.l0187 jp l1cee ; DD_L_XDPB +.l018a jp l1d30 ; DD_L_DPB +.l018d jp l1f76 ; DD_L_SEEK +.l0190 jp l20c3 ; DD_L_READ +.l0193 jp l20cc ; DD_L_WRITE +.l0196 jp l212b ; DD_L_ON_MOTOR +.l0199 jp l2150 ; DD_L_T_OFF_MOTOR +.l019c jp l2164 ; DD_L_OFF_MOTOR + + +; DOS_INITIALISE + +.l019f ld hl,pg_buffer + ld de,pg_buffer+1 + ld bc,$09ff + ld (hl),$00 + ldir ; clear DOS workspace variables + call l1f27 ; DD_INTERFACE + ld hl,$0080 ; max RAMdisk, zero cache + ld d,h + ld e,h + jr nc,l01c2 ; move on if no interface + call l1f32 ; DD_INIT + call l17d0 ; initialise A: & B: extended XDPBs + ld hl,$0878 ; $78 RAMdisk buffers, $08 cache buffers + ld de,$0008 +.l01c2 push de + call l1820 ; initialise RAMdisk + pop de + call l1539 ; setup cache + jp l0500 ; set default drive and exit + + +; DOS_VERSION + +.l01cd xor a + ld b,a + ld c,a ; A=BC=0 + ld de,$0100 ; DE=version info + ld hl,$0069 ; HL=? + scf ; signal success + ret + +; DOS_GET_1346 + +.l01d8 call l1a48 ; get RAMdisk info + jp l1530 ; get cache info & exit + +; DOS_SET_1346 + +.l01de push de ; save new cache info + ex de,hl ; HL=new RAMdisk info + call l1a48 ; get HL=old RAMdisk info + or a + sbc hl,de ; set Z if no change in RAMdisk + ex de,hl + scf ; set success flag + call nz,l1a52 ; change RAMdisk if necessary + pop de ; restore new cache info + ret nc ; exit if error changing RAMdisk + ex de,hl + call l1530 ; get old cache info + ex de,hl + or a + sbc hl,de ; set Z if no change in cache + add hl,de + scf + call nz,l1535 ; change cache if necessary + ret + +; Subroutine to copy BC bytes from HL to DE within page A, then page +; back original page + +.l01fb inc c + dec c + jr nz,l0202 + inc b + dec b + ret z ; exit if no bytes to copy +.l0202 call l0207 ; page in A + ldir ; copy bytes, then following routine pages + ; back original bank & exits + +; Subroutine to page in bank A (gives A=previous bank) + +.l0207 push hl + push bc + ld b,a + ld hl,BANKM + ld a,(hl) + and $07 + push af ; stack previous bank + cp b + jr z,l0227 ; exit if no change + ld a,(hl) + and $f8 + or b ; new value + ld b,a + ld a,r ; interrupt status to P/V + ld a,b + ld bc,$7ffd + di + ld (hl),a + out (c),a ; page in new bank + jp po,l0227 + ei ; restore interrupts if necessary +.l0227 pop af ; restore previous bank + pop bc + pop hl + ret + +; Subroutine to find address of buffer A in 1346 area +; On exit, HL=address, A=bank + +.l022b add a,a ; A=offset/256 + ld l,a + or $c0 + ld h,a ; H=high byte + ld a,l + ld l,$00 ; L=start of buffer + rlca + rlca + rlca + and $06 ; A=bank 0,2,4 or 6 + cp $04 + ret nc ; exit if 4 or 6 + inc a ; else use 1 or 3 + ret + +; Subroutine to copy IX bytes from HL in page C to DE in page B + +.l023d ld a,c + cp b + jr nz,l0247 ; move on if pages different + push ix + pop bc + jp l01fb ; else just copy within page (now in A) +.l0247 push bc + call l02c4 ; get #bytes to move if page C below $c000 + call l01fb ; move them + pop bc + push bc + ld a,b ; get #bytes to move if page B below $c000 + ex de,hl ; opposite direction + call l02c4 ; calculate number + ex de,hl + call l01fb ; move them + push ix + pop bc + ld a,b + or c + pop bc + ret z ; exit if all moved + ld a,r ; get interrupt status + di ; disable interrupts + push af + or a + call l0273 ; save $20 bytes at $bfe0 + call l0288 ; copy bytes via buffer at $bfe0 + scf + call l0273 ; restore $20 bytes at $bfe0 + pop af + ret po + ei ; restore interrupts if necessary + ret + +; Subroutine to copy $20 bytes from pg_buffer to $bfe0 (or vice-versa if carry +; set) + +.l0273 push hl + push de + push bc + ld bc,$0020 + ld de,pg_buffer + ld hl,$bfe0 + jr nc,l0282 + ex de,hl ; exchange if required +.l0282 ldir ; copy bytes + pop bc + pop de + pop hl + ret + +; Subroutine to copy IX bytes from HL in page C to DE in page B, using +; a $20-byte buffer at $bfe0 + +.l0288 push ix + ex (sp),hl + ld a,h + or a + jr nz,l0294 ; move on if >=$100 bytes left + ld a,l + cp $20 + jr c,l0296 ; move up to $20 bytes at a time +.l0294 ld a,$20 +.l0296 push bc ; save page numbers + ld c,a + ld b,$00 + or a + sbc hl,bc ; reduce #bytes left to copy + pop bc ; restore page numbers + ex (sp),hl + pop ix + or a + ret z ; exit if no more bytes to copy + push de + push bc + push af + ld b,a + ld a,c + ld c,b + ld b,$00 ; A=page to copy from, BC=#bytes + ld de,$bfe0 + call l01fb ; copy bytes to $bfe0 + pop af + pop bc + pop de + push hl + push bc + ld c,a + ld a,b + ld b,$00 ; A=page to copy to, BC=#bytes + ld hl,$bfe0 + call l01fb ; copy bytes from $bfe0 + pop bc + pop hl + jr l0288 ; loop back until copied all + +; Subroutine to calculate BC=#bytes that can be moved to DE +; If DE is in the top segment, no bytes can be moved +; IX initially contains total #bytes to move, and on exit contains #bytes +; that will be left after this move + +.l02c4 push hl + ld bc,$0000 ; move zero bytes if dest in top segment + ld hl,$c000 + or a + sbc hl,de + jr c,l02e6 ; exit if destination in top segment + jr z,l02e6 + push ix + pop bc ; BC=#bytes + or a + sbc hl,bc + add hl,bc + jr nc,l02dd ; move on if space for all bytes + ld b,h ; else use space available + ld c,l +.l02dd push ix + pop hl + or a + sbc hl,bc + push hl + pop ix ; IX=bytes left to move after this +.l02e6 pop hl + ret + +; DOS_SET_MESSAGE + +.l02e8 or a + jr nz,l02ee + ld hl,$0000 ; use $0000 to disable +.l02ee ld de,(rt_alert) ; get old routine address + ld (rt_alert),hl ; set new routine address + ex de,hl + ret + +; Subroutine to do an ALERT message for error A on drive C + +.l02f7 ld b,a + ld hl,(rt_alert) + ld a,h + or l + ld a,b + jr nz,l0302 ; if ALERT routine exists, go to do it + inc l ; otherwise exit with HL=1 + ret +.l0302 push bc ; save drive + call l033a ; generate error message + push hl ; save address + ld de,al_resp + push de + ld hl,l04cc + ld bc,$0007 + ldir ; copy response key list + pop de + pop hl + push de + call l0332 ; call the ALERT routine + pop de + ld hl,l04d3 ; address of reply value string + ld b,a +.l031e ld a,(de) + cp $ff + jr z,l032b ; move on if end of reply string + cp b + ld a,(hl) ; get reply value + jr z,l032c ; move on if match + inc de + inc hl + jr l031e ; loop back +.l032b xor a ; use "Cancel" +.l032c sub $01 ; carry set for "Cancel", Z set for "Retry" + ccf ; invert carry + pop bc ; restore drive + ld a,b ; restore error + ret + +; Subroutine to call ALERT subroutine with error message in HL + +.l0332 push hl + ld hl,(rt_alert) + ex (sp),hl + ret + +; Subroutine to generate change disk message + +.l0338 ld a,$0a ; message 10 + +; Subroutine to generate recoverable error message A, returns address in HL + +.l033a ld ix,al_mess ; address to place message + push ix + call l0349 ; generate it + ld (ix+$00),$ff ; add a terminator + pop hl ; restore address + ret + +; Subroutine to put recoverable error message address for error A at IX + +.l0349 and $7f ; mask off bit 7 + ld hl,l03ae ; address of message 0 + ld b,a + inc b + jr l0357 ; go to find address +.l0352 ld a,(hl) + inc hl + inc a + jr nz,l0352 ; skip next message +.l0357 djnz l0352 ; back until at correct message +.l0359 ld a,(hl) ; get next char + inc hl + cp $ff + ret z ; exit if end of message + push hl + call l0365 ; process next char + pop hl + jr l0359 ; back for more chars + +; Subroutine to process a character in the error message generator + +.l0365 or a + jp p,l03a8 ; move on if standard ASCII value + cp $fe + jr z,l03a7 ; go to insert drive letter + cp $fd + jr z,l0378 ; go to insert track number + cp $fc + jr nz,l0349 ; if not sector, go to include submessage + ld a,e ; sector=E + jr l0379 +.l0378 ld a,d ; track=D +.l0379 push de + push bc + ld l,a + ld h,$00 + ld d,h + ld bc,$ff9c + call l0392 ; generate 100s digit + ld bc,$fff6 + call l0392 ; generate 10s digit + ld a,l + add a,'0' + pop bc + pop de + jr l03a8 ; insert units digit +.l0392 ld a,$ff +.l0394 push hl + inc a + add hl,bc + jr nc,l039d + ex (sp),hl + pop hl + jr l0394 ; loop back until value in A +.l039d pop hl + or a + jr z,l03a3 ; move on if zero + ld d,'0' +.l03a3 add a,d ; form character + ret z ; exit if none + jr l03a8 +.l03a7 ld a,c ; drive=C +.l03a8 ld (ix+$00),a ; add character to message + inc ix + ret + +; Recoverable error message table + +.l03ae defm $8b&"not ready"&$8f&$ff + defm $8c&"write protected"&$8f&$ff + defm $8d&"seek fail"&$8f&$ff + defm $8e&"data error"&$8f&$ff + defm $8e&"no data"&$8f&$ff + defm $8e&"missing address mark"&$8f&$ff + defm $8b&"bad format"&$8f&$ff + defm $8e&"unknown error"&$8f&$ff + defm $8c&"changed, please replace"&$8f&$ff + defm $8c&"unsuitable"&$8f&$ff + defm "Please put the disk for "&$fe&": into the drive then press " + defm "any key"&$ff + defm "Drive "&$fe&": "&$ff + defm $8b&"disk "&$ff + defm $8b&"track "&$fd&", "&$ff + defm $8d&"sector "&$fc&", "&$ff + defm " - Retry, Ignore or Cancel? "&$ff +.l04cc defm "rRiIcC"&$ff +.l04d3 defb 1,1,2,2,0,0 + + +; Subroutine to shift DE right A times + +.l04d9 or a + ret z ; exit if A=0 +.l04db srl d ; shift right + rr e + dec a + jr nz,l04db ; back for more + ret + +; Subroutine to shift DE left A times + +.l04e3 or a + ret z ; exit if A=0 + ex de,hl +.l04e6 add hl,hl ; shift left + dec a + jr nz,l04e6 ; back for more + ex de,hl + ret + +; Subroutine to call routine address in HL + +.l04ec jp (hl) + +; Subroutine to convert lowercase drive letters to uppercase + +.l04ed cp 'a' +.l04ef ret c + cp 'z'+1 + ret nc + add a,$e0 ; if lowercase, convert to uppercase + ret + + defs 10 + +; Sets default drive to first found with XDPB +; If none, A: is set as default + +.l0500 ld bc,$1041 ; 16 drives, A: to P: +.l0503 ld a,c + ld (def_drv),a ; set drive as default +.l0507 call l184d ; get XDPB for drive + ret c ; exit if XDPB available + inc c ; increment drive letter + djnz l0503 ; back for more + ld a,'A' + ld (def_drv),a ; set default drive A: + ret + +; Subroutine to get FCB for file B & test if open for reading + +.l0514 call l0525 ; get FCB & test if open + ret nc ; exit if error + rra ; test if open for read + ld a,$1d ; error "file not open" + ret + +; Subroutine to get FCB for file B & test if open for writing + +.l051c call l0525 ; get FCB & test if open + ret nc ; exit if error + rra + rra ; test if open for write + ld a,$1d ; error "file not open" + ret + +; Subroutine to get FCB for file B & test if open + +.l0525 call l054b ; get FCB & access mode + ret nc ; exit if error + rlca + rra ; bit 7 to carry + ret c ; return with success if open + ld a,$1d ; file not open error + ret + +; Subroutine to clear FCB for closed file B +; *BUG* Clears one byte too many; this can result in user area for the +; next FCB being erroneously set to 0 + +.l052f call l054b ; get FCB & access mode + ret nc ; exit if error + rla + ccf ; inverted bit 7 to carry + ld a,$1d ; file not open + ret nc ; return with error if open + push hl ; save registers + push de + push bc + ld h,b + ld l,c + ld (hl),$00 + ld d,b + ld e,c + inc de + ld bc,$0038 ; *BUG* should be $0037 + ldir ; clear FCB + pop bc + pop de + pop hl + ret + +; Subroutine to get FCB (in BC) of file B. Returns access mode/flags in A. + +.l054b push hl ; save registers + push de + ld a,b ; get file number + cp $10 + ld a,$15 + jr nc,l0566 ; if >$0f, exit with bad parameter error + ld hl,fcbs-$0038 + ld de,$0038 + inc b +.l055b add hl,de + djnz l055b + ld b,h + ld c,l ; BC=address of FCB + ld hl,$0020 + add hl,bc + ld a,(hl) ; get access mode + scf ; success +.l0566 pop de ; restore registers + pop hl + ret + + +; Subroutine to check if file in BC is opened by any other FCB + +.l0569 ld hl,$0020 + add hl,bc + ld e,(hl) ; save access mode + ld (hl),$00 ; signal "not opened" + push hl + push de + call l0579 ; check if file opened in any other FCB + pop de + pop hl + ld (hl),e ; restore access mode + ret + +; Subroutine to check if file in BC can be opened in required access mode + +.l0579 ld hl,fcbs + ld e,$12 ; 18 files to check +.l057e push hl + push bc + ld bc,$0020 + add hl,bc + ld d,(hl) ; D=file access mode + inc hl + ld a,(hl) ; A=file drive + pop bc + pop hl + bit 7,d + jr z,l05b7 ; move on if file not open + push hl + ld hl,$0021 + add hl,bc + cp (hl) + pop hl + jr nz,l05b7 ; move on if different drive + ld a,(bc) + cp $22 ; check if current file is open drive + jr z,l05a3 + ld a,(hl) + cp $22 ; or file we are checking + call nz,l0d8a ; if not, check if files are the same + jr nz,l05b7 ; if not, move on +.l05a3 push hl + ld hl,$0020 + add hl,bc + ld a,(hl) ; get access mode of current file + rrca + rrca + and $03 + ld h,a ; H=1 if open shared + ld a,d + and $03 + or h + xor h ; Z set if legal access + ld a,$1e ; access denied error + pop hl + ret nz +.l05b7 push de + ld de,$0038 + add hl,de + pop de + dec e + jr nz,l057e ; loop back to test more FCBs + scf + ret + +; DOS_FLUSH + +.l05c2 call l04ed ; make drive uppercase + call l0c27 ; ensure disk logged in + ret nc ; exit if error +.l05c9 push bc + ld bc,fcbs ; first FCB + ld e,$12 ; 18 files +.l05cf ld hl,$0020 + add hl,bc + bit 7,(hl) + jr z,l05e4 ; skip if file not open + inc hl + ld a,(hl) + cp (ix+$1c) ; is it on same drive? + scf + push de + call z,l074c ; ensure file header & directory up to date + pop de + jr nc,l05ee ; exit if error +.l05e4 ld hl,$0038 + add hl,bc + ld b,h + ld c,l ; get to next FCB + dec e + jr nz,l05cf ; back for more + scf ; success +.l05ee pop bc + ret + +; Subroutine to get sector HL to buffer, ensuring all FCBs on same +; drive referencing this sector are up to date +; On entry, D=drive, HL=abs logical sector of a buffer + +.l05f0 ld bc,fcbs ; first FCB + ld e,$12 ; check 18 FCBs (16 user+2 system) +.l05f5 push hl + push de + ld a,d ; A=drive + ex de,hl + ld hl,$0020 + add hl,bc + bit 7,(hl) ; is file open? + jr z,l061f ; move on if not + inc hl + cp (hl) ; is drive the same? + jr nz,l061f ; move on if not + inc hl + bit 3,(hl) ; is a sector number in +$2b? + jr z,l061f ; move on if not + ld hl,$002b + add hl,bc + ld a,e + cp (hl) ; check low byte of absolute sector + jr nz,l061f ; move on if different + inc hl + ld a,d + cp (hl) ; check high byte of absolute sector + jr nz,l061f ; move on if different + call l0c20 ; ensure correct disk logged in + call c,l132a ; if no error, get sector to buffer + jr nc,l0626 +.l061f ld hl,$0038 + add hl,bc + ld b,h ; BC=next FCB + ld c,l + scf ; success +.l0626 pop de + pop hl + ret nc ; exit if error + dec e + jr nz,l05f5 ; loop back for more FCBs + ret + +; DOS_OPEN + +.l062d push de + push bc + call l052f ; clear FCB for file B (& ensure closed) + call c,l0adf ; parse filename to FCB,disallowing wildcards + call c,l0c20 ; ensure correct disk logged in + pop hl + pop de + ret nc ; exit if error + push de + ld a,l + ld hl,$0020 + add hl,bc + ld (hl),a ; set access mode + call l0579 ; ensure file can be opened in this mode + ld hl,l0d8a + call c,l0dae ; find first extent of file + pop de + ret nc ; exit if error + jr nz,l067f ; move on if not found + ld a,e ; check open action + or a + ld a,$18 + ret z ; "file exists" error for open action 0 + dec e + jr nz,l065f ; move on for actions 2-4 + call l06c4 ; find first entry (open action 1) + call c,l0801 ; open file, reading any header + jr l069b ; mark file as open & exit +.l065f dec e + jr nz,l066a ; move on for actions 3 & 4 + call l06c4 ; find first entry (open action 2) + call c,l0859 ; open file, skipping any header + jr l069b ; mark file as open & exit +.l066a push de + dec e + jr nz,l0676 ; move on for action 4 + call l06e0 ; for open action 3, erase any existing .BAK + call c,l0983 ; and rename file to .BAK + jr l067d ; go to follow create action +.l0676 or a + ld a,$15 ; bad parameter if not action 4 + dec e + call z,l092e ; for action 4, erase file then do create +.l067d pop de + ret nc ; exit if error +.l067f ld a,d + or a + ld a,$17 + ret z ; error "file not found" for create action 0 + dec d + jr nz,l068f ; move on for create action 2 + call l06a9 ; for action 1, create an entry + call c,l07dc ; and add a header + jr l0696 +.l068f or a + ld a,$15 ; "bad parameter" for create actions > 2 + dec d + call z,l06a9 ; for action 2, just create an entry +.l0696 ret nc ; exit if error + xor a ; A=0 if file was created + scf + jr l069d +.l069b ret nc ; exit if error + sbc a,a ; A=$ff if file was opened +.l069d push af + ld hl,$0020 + add hl,bc + set 7,(hl) ; mark FCB as containing an open file + inc (ix+$21) ; increment # open files on drive + pop af + ret + +; Subroutine to set up clean directory entry for new file + +.l06a9 ld hl,$0020 + add hl,bc + ld a,(hl) + rra + rra ; check bit 1 of FCB flags + ld a,$1e ; "access denied" error + call c,l18f3 ; check disk can be written to + ld hl,$0000 + call c,l0cbe ; set up clean extent + ret nc ; exit if error + ld hl,$0022 + add hl,bc + set 0,(hl) ; signal "directory valid" + scf + ret + +; Subroutine to find entry for required extent of file + +.l06c4 call l0d4a ; find directory entry of required extent + jr nc,l06d8 ; move on if not found + ld hl,$0020 + add hl,bc + bit 1,(hl) + scf + ret z ; exit with success if new entry not needed + call l0ebe ; check file can be written to + call c,l18f3 ; check disk can be written to + ret +.l06d8 cp $19 ; error "end of file" + scf + ccf + ret nz ; exit unless finding first extent + ld a,$17 ; error "file not found" + ret + +; Subroutine to erase any .BAK file existing for file in current FCB + +.l06e0 push bc + ld h,b + ld l,c + ld de,sysfcb0 + ld bc,$0038 + ldir ; copy FCB to SYSFCB0 + pop bc + ld a,'B' + ld (sysfcb0+9),a + ld hl,$4b41 + ld (sysfcb0+$0a),hl ; set extension in SYSFCB0 to "BAK" + push bc + ld bc,sysfcb0 + call l092e ; erase any existing .BAK file + pop bc + ret c ; exit if success + cp $17 + scf + ret z ; or with success if error was "file not found" + or a + ret + +; DOS_OPEN_DRIVE + +.l0706 call l04ed ; make drive letter uppercase + ld d,a ; save letter + ld e,c ; & access mode + call l052f ; clear FCB for file B (ensure closed) + ret nc ; exit if error + ld a,$22 + ld (bc),a ; "open drive" + ld hl,$0020 + add hl,bc + ld (hl),e ; access mode + inc hl + ld (hl),d ; drive letter + ld a,d + call l184d ; get XDPB for drive + call c,l0579 ; check can open in required access mode + ret nc ; exit if error + ld e,(ix+$05) + ld d,(ix+$06) + inc de + ld a,(ix+$02) + call l04e3 + call l19c0 + sla e + rl d ; DE="file" length (high bytes) + ld hl,$0024 + add hl,bc + ld (hl),e + inc hl + ld (hl),d ; set file length + scf + jp l069d ; set file open & exit + +; DOS_CLOSE + +.l0740 call l0525 ; get FCB & see if open + call c,l0c20 ; ensure correct disk logged in + call c,l074c ; ensure file header & directory up-to-date + ret nc ; exit if error + jr l077f ; go to abandon FCB + +; Subroutine to ensure file's +3DOS header & directory entry are up-to-date + +.l074c ld hl,$0020 + add hl,bc + bit 1,(hl) + scf + ret z ; exit with success if file not in write mode + call l0797 ; update any +3DOS header with file length + call c,l132a ; get sector to buffer + call c,l1719 ; write all changed data on this disk + call c,l0cca ; ensure directory up to date for this file + ret + +; DOS_ABANDON + +.l0761 call l0525 ; get FCB & see if open + call c,l0c20 ; ensure correct disk logged in + ret nc ; exit if error + ld hl,$0020 + add hl,bc + bit 1,(hl) + jr z,l077f ; move on if not open in write mode + inc hl + inc hl + bit 1,(hl) + jr z,l077c ; move on if new directory entry not needed + call l1038 ; clear flag & increment free entries + call l0f40 ; deallocate blocks in FCB +.l077c call l16c6 ; move all inuse BCBs for file to free list +.l077f ld hl,$0020 + add hl,bc + ld (hl),$00 ; signal "file not open" + dec (ix+$21) ; decrement files open on disk + call z,l189d ; low-level logout disk if none left + scf ; success + ret + +; +3DOS file header signature (issue 1) + +.l078d defm "PLUS3DOS"&$1a&$01 + + +; Subroutine to update any +3DOS file header with filelength if necessary +; File position is unaffected + +.l0797 ld hl,$0022 + add hl,bc + bit 6,(hl) + scf + ret z ; exit with success if file has no header + call l1074 ; get DEHL=file position + push hl + push de + call l07af ; update header with filelength + pop de + pop hl + push af + call l1090 ; restore file position + pop af + ret + +; Subroutine to update +3DOS file header with filelength & +3 BASIC header +; Leaves filepointer positioned after header + +.l07af ld hl,$000b + ld e,h + call l1090 ; set file position to header file length + ld hl,$0023 + add hl,bc + ld e,$03 + call l07d2 ; copy filelength from FCB to file header + ret nc ; exit if error + xor a + call l12a5 ; MSB of filelength is zero + ret nc ; exit if error + ld hl,$0030 + add hl,bc + ld e,$08 + call l07d2 ; copy +3 BASIC header data from FCB to file + ret nc ; exit if error + jp l12e6 ; update header checksum & exit + +; Subroutine to copy E bytes from HL into file at current filepointer + +.l07d2 ld a,(hl) ; get next byte + inc hl + call l12a5 ; copy byte into file + ret nc ; exit if error + dec e + jr nz,l07d2 ; back for more + ret + +; Subroutine to create a header for a newly-created file + +.l07dc ld e,$0a + ld hl,l078d ; header signature + call l07d2 ; copy header signature into file + ret nc + ld a,$00 + call l12a5 ; version 0 + ret nc + ld e,$74 +.l07ed xor a + call l12a5 ; fill rest of header with nulls + ret nc + dec e + jr nz,l07ed + call l12e6 ; set checksum & place filepointer after it + ret nc +.l07f9 ld hl,$0022 + add hl,bc + set 6,(hl) ; set "file has header" flag + scf + ret + +; Subroutine to open file & read any header + +.l0801 call l12de ; check if valid header checksum + jr nc,l084d ; move on if error + jr nz,l0852 ; if not, move on (no header) + ld e,$0a ; header signature length + ld hl,l078d ; header signature +.l080d call l11cb ; get byte from file + jr nc,l084d ; move on if error + cp (hl) + inc hl + jr nz,l0852 ; move on if doesn't match header signature + dec e + jr nz,l080d ; back for more chars + call l11cb ; get version + jr nc,l084d ; move on if error + cp $01 + jr nc,l0852 ; no header if higher version than 0 + ld hl,$0023 + add hl,bc + ld e,$03 +.l0828 call l11cb + ret nc + ld (hl),a ; copy filelength from header + inc hl + dec e + jr nz,l0828 + call l11cb ; skip high byte of length + ret nc + ld hl,$0030 + add hl,bc + ld e,$08 +.l083b call l11cb + ret nc + ld (hl),a ; copy +3 BASIC data from header + inc hl + dec e + jr nz,l083b + ld hl,$0080 + ld e,h + call l1090 ; set filepointer past header record + jr l07f9 ; exit, setting "file has header" flag +.l084d cp $19 + scf + ccf + ret nz ; error unless "end of file" +.l0852 ld hl,$0000 + ld e,l + call l1090 ; set filepointer to start of file +.l0859 ld hl,$0000 + ld (filerecs+1),hl + xor a + ld (filerecs+2),a + ld hl,l088d + call l0dae ; get last record+1 in file + ret nc + ld de,(filerecs+1) + ld hl,(filerecs-1) + ld l,$00 ; DEHL=2*filelength + srl d + rr e + rr h + rr l ; DEHL=filelength + ld a,d + or a + ld a,$22 ; "file too big" error if >8M + ret nz + push hl + ld hl,$0025 + add hl,bc + ld (hl),e ; store filelength in FCB + pop de + dec hl + ld (hl),d + dec hl + ld (hl),e + scf + ret + +; Subroutine to update largest rec# with one from current extent if larger + +.l088d call l0d8a + ret nz ; exit if filenames don't match + push bc + ld b,h + ld c,l + call l14c2 ; get ADE=last record number+1 + ld b,a + ex de,hl ; test against largest found so far + ld hl,(filerecs) + or a + sbc hl,de + ld a,(filerecs+2) + sbc a,b + jr nc,l08ad ; move on unless larger + ld (filerecs),de + ld a,b + ld (filerecs+2),a ; set size from current extent +.l08ad pop bc + scf + sbc a,a ; success + ret + +; DOS_REF_HEAD + +.l08b1 call l0525 ; get FCB & check file is open + ret nc ; exit if not + ld ix,$0030 + add ix,bc ; IX points to header data + ld hl,$0022 + add hl,bc + bit 6,(hl) ; does file have header? + scf ; success + ret + +; DOS_SET_ACCESS + +.l08c3 ld e,c ; E=required access mode + push de + call l0525 ; get FCB & current access mode + call c,l0c20 ; if ok, ensure correct disk logged in + call c,l074c ; ensure file header & directory up-to-date + pop de + ret nc ; exit if error + ld hl,$0020 + add hl,bc + ld d,(hl) ; get old access mode + ld (hl),e ; store new (as closed) + push hl + push de + call l0579 ; check if can open in this mode + pop de + pop hl + jr nc,l08ef ; if not, go to restore old mode & exit + bit 1,e + jr z,l08eb ; move on if not opening for write + call l0ebe ; check file can be written to + call c,l18f3 ; check disk can be written to + jr nc,l08ef ; exit, restoring mode, if error +.l08eb set 7,(hl) ; set file open + scf ; success + ret +.l08ef ld (hl),d ; restore old mode + or a + ret + +; DOS_FREE_SPACE + +.l08f2 call l04ed ; make drive letter uppercase + call l0c27 ; ensure allocation bitmap up-to-date + ret nc ; exit if error + jp l0fa9 ; move on to calculate free space + +; DOS_SET_USER + +.l08fc cp $ff + jr z,l090a ; move on to return current default user area + cp $10 ; check in range 0-15 + ld b,a + ld a,$15 + ret nc ; error 21 - bad parameter if not + ld a,b + ld (def_user),a ; set default user area +.l090a ld a,(def_user) ; get default user area + scf ; success + ret + +; DOS_SET_DRIVE + +.l090f call l04ed ; make letter uppercase + cp $ff + jr z,l091f ; move on if current default drive required + ld b,a + call l184d ; check drive has an XDPB + ret nc ; exit with error if not + ld a,b + ld (def_drv),a ; set default drive +.l091f ld a,(def_drv) ; get default drive + scf ; success + ret + +; DOS_DELETE + +.l0924 ld bc,sysfcb0 + call l0af5 ; parse filespec, allowing wildcards + call c,l0c20 ; ensure correct disk logged in + ret nc +.l092e call l0569 ; check no source files open by any FCBs + call c,l18f3 ; check disk can be written to + ret nc ; exit if error + ld hl,l093b ; routine to delete extents + jp l09ad + +; Subroutine to delete any directory entry matching one in FCB + +.l093b call l0d84 + ret nz ; exit if entry doesn't match + call l0ebe + ret nc ; or if file read-only + push hl + push de + xor a + call l0f43 ; deallocate blocks in entry + pop de + pop hl + ld (hl),$e5 ; set "deleted" mark in FCB + call l0e34 ; copy "direntry" to directory entry DE + ret nc + call l1040 ; increment #free entries + sbc a,a + ld (extchg),a ; set "success" flag + ret + +; DOS_SET_ATTRIBUTES + +.l0959 ld (att_clr),de ; store attribs to set/clear + ld bc,sysfcb0 + call l0af5 ; parse filespec, allowing wildcards + call c,l0c20 ; ensure correct disk logged in + call c,l18f3 ; check disk can be written to + ret nc ; exit if error + ld hl,l09bf ; routine to change attributes + jr l09a7 + +; DOS_RENAME + +.l096f push de + ld bc,sysfcb1 + call l0adf ; parse source filespec, ensuring no wildcards + call c,l0c20 ; ensure correct disk logged in + pop hl + push bc + ld bc,sysfcb0 + call c,l0adf ; parse dest filespec, ensuring no wildcards + pop bc + ret nc ; exit if error +.l0983 ld hl,$0021 + add hl,bc + ld a,(sysfcb0+$21) + xor (hl) ; check drives are the same + ld a,$1f + ret nz ; error "cannot rename between drives" + call l18f3 ; check disk can be written to + push bc + ld bc,sysfcb0 + call c,l0569 ; check dest file not open by any FCB + ld hl,l0d84 + call c,l0dae ; check if dest file exists in directory + pop bc + ret nc ; exit if error + ccf + ld a,$18 + ret z ; file already exists error + ld hl,l09f4 ; rename routine +.l09a7 push hl + call l0569 ; check source file not open by any FCB + pop hl + ret nc ; exit if error +.l09ad xor a + ld (extchg),a ; set "no extents changed" + call l0dae ; rename/delete all extents of file + ret nc + ld a,(extchg) + or a ; check if any extents changed + ld a,$17 ; file not found error + call nz,l1719 + ret + +; Subroutine to change attributes for directory entries matching +; filespec + +.l09bf call l0d84 + ret nz ; exit if no match + push bc + ld a,(att_set) ; get attribs to set + ld c,$ff ; set mask + push hl + call l09d8 ; set them + pop hl + ld a,(att_clr) ; get attribs to clear + inc c ; clear mask + call l09d8 ; clear them + pop bc + jr l0a10 ; go to update directory entry + +; Subroutine to set/clear attributes. HL points to directory entry, A +; contains attributes to set/clear & C contains set/clear mask + +.l09d8 rla ; discard bit 7 + ld b,$04 + inc hl + call l09e5 ; bits 6->3 on first four chars of filename + inc hl + inc hl + inc hl + inc hl + ld b,$03 ; bits 2->0 on extension +.l09e5 rla + jr nc,l09f0 ; move on if attribute not to be affected + res 7,(hl) ; reset it + inc c + dec c + jr z,l09f0 + set 7,(hl) ; set it if mask=$ff +.l09f0 inc hl + djnz l09e5 ; back for more + ret + +; Subroutine to rename any directory entry matching filename in SYSFCB1 +; to name in SYSFCB0 + +.l09f4 call l0d84 + ret nz ; exit if names don't match + call l0ebe + ret nc ; exit if file readonly + push de + ex de,hl + ld hl,sysfcb0 + ld a,(de) + and $10 ; also rename password control entries + or (hl) + ld (de),a + inc de + inc hl + push bc + ld bc,$000b + ldir ; copy new filename to direntry + pop bc + pop de +.l0a10 call l0e34 ; copy "direntry" to directory entry DE + ret nc + sbc a,a + ld (extchg),a ; if no error, set successful rename flag + ret + +; DOS_CATALOG + +.l0a19 ld (cat_buff),de ; store buffer address + ld (cat_filt),bc ; store filter & buffer size + ld a,$01 + ld (cat_ents),a ; store "1 entry completed" + ld bc,sysfcb0 + call l0af5 ; parse filespec to SYSFCB0, wildcards allowed + call c,l0c20 ; ensure correct disk logged in + call c,l05c9 ; flush drive + ld hl,l0a3d + call c,l0dae ; generate catalog + ld bc,(cat_size) ; get B=#completed entries + ret + +; Subroutine to add directory entry to catalog if suitable + +.l0a3d push bc + call l0a45 ; process the entry + pop bc + scf + sbc a,a ; set A=$ff, set carry for success + ret + +; Subroutine to process a directory entry & add to catalog if suitable + +.l0a45 call l0d8a + ret nz ; exit if doesn't match filespec + ld a,(cat_filt) + rra + jr c,l0a58 ; move on if we should include system files + push hl + ld bc,$000a + add hl,bc + bit 7,(hl) + pop hl + ret nz ; exit if system file +.l0a58 ld de,(cat_buff) + call l0ac9 ; is it alphabetically less than preloaded? + ret nc ; exit if so + ld bc,(cat_size) ; C=buffer size, B=#completed entries +.l0a64 push hl + ld hl,$000d + add hl,de ; get to next entry in buffer + ex de,hl + pop hl + dec c ; decrement buffer size + djnz l0a71 ; move on if more entries to check + ret z ; exit if no space left in buffer + jr l0aa1 ; else move to add +.l0a71 call l0ac9 ; does filespec match next buffer entry? + jr c,l0a64 ; loop back if alphabetically greater + jr z,l0ab9 ; move on if the same + push hl + push de + ld hl,(cat_size) + ld h,$00 + dec hl ; HL=#catalog buffer entries-1 + ld b,h + ld c,l + add hl,hl + add hl,bc + add hl,hl + add hl,hl + add hl,bc ; HL=13*(#catalog buffer entries-1) + ld bc,(cat_buff) + add hl,bc ; HL=address of last entry in catalog buffer + ld a,l + sub e + ld c,a + ld a,h + sbc a,d + ld b,a ; BC=distance between last & current entries + dec hl + ld de,$000d + ex de,hl + add hl,de + ex de,hl ; DE=address of end of catalog buffer + ld a,b + or c + jr z,l0a9f + lddr ; shift catalog entries down one (may lose one) +.l0a9f pop de + pop hl +.l0aa1 push hl + push de + inc hl + ld bc,$000b + ldir ; copy entry into buffer + xor a + ld (de),a + inc de + ld (de),a ; set zero size + ld hl,(cat_size) + ld a,h + cp l + adc a,$00 + ld (cat_ents),a ; increment # completed entries (max=bufsize) + pop de + pop hl +.l0ab9 call l0f75 ; calculate extent size in K + ex de,hl + ld bc,$000b + add hl,bc + ld a,(hl) + add a,e ; add extent size into directory entry size + ld (hl),a + inc hl + ld a,(hl) + adc a,d + ld (hl),a + ret + +; Subroutine to compare filenames at DE & HL (ignoring attributes) +; and setting Z if they match. Carry set if filename at HL > one at DE + +.l0ac9 push hl + push de + push bc + ld b,$0b ; 11 chars to check + inc hl +.l0acf ld a,(hl) + add a,a + ld c,a + ld a,(de) + add a,a + cp c ; compare chars without attribute bits + jr nz,l0adb ; if different, exit + inc de + inc hl + djnz l0acf ; back for more +.l0adb pop bc + pop de + pop hl + ret + +; Subroutine to parse filespec at HL into FCB at BC, giving +; error if filespec illegal or contains wildcards + +.l0adf call l0b3f ; parse filespec + ret nc ; exit if error + ld hl,$0001 + add hl,bc + ld e,$0b +.l0ae9 ld a,(hl) + inc hl + cp '?' ; check for wildcard characters + ld a,$14 + ret z ; exit with "bad filename" if found + dec e + jr nz,l0ae9 ; back for more + scf + ret + +; Subroutine to parse filespecs, allowing wildcards + +.l0af5 jp l0b3f ; jump to the routine + +; Subroutine to set user area & drive letter specified in filename +; Should exit with HL pointing to ":" +; On exit, carry reset if neither found + +.l0af8 call l0b02 ; set user area if specified + jr nc,l0b21 + call l0b21 ; set drive letter if specified + scf + ret + +; Subroutine to check if chars at HL are a user area +; If so, exits with user area set, carry set & HL pointing after user area +; in filename + +.l0b02 call l0b38 ; check if char is digit + ret nc ; exit if not + ld e,a ; save digit + call l0bd3 ; get next char + call c,l0b38 ; check if its a digit + jr nc,l0b1b ; if not, use single digit + ld d,a ; save ls digit + ld a,e + add a,a + ld e,a + add a,a + add a,a + add a,e + add a,d + ld e,a ; E=user area + call l0bd3 ; get next char +.l0b1b ld a,e + cp $10 + ret nc ; exit if invalid user area + ld (bc),a ; save user area in FCB + ret + +; Subroutine to check if char at HL is a drive letter +; If so, exits with drive letter set, carry set & HL pointing after drive +; letter in filename + +.l0b21 call l0bc9 ; get next char + ret nc ; exit if none + cp 'A' + ccf + ret nc ; or if <"A" + cp 'Q' + ret nc ; or if >"P" + push hl + ld hl,$0021 + add hl,bc + ld (hl),a ; save as drive letter in FCB + pop hl + call l0bd3 ; get next char + scf + ret + +; Subroutine to test if A is a digit ('0'-'9') +; If so, exits with carry set & A=value + +.l0b38 sub '0' + ccf ; error if <"0" + ret nc + cp $0a ; set carry if user area + ret + +; Subroutine to parse filename at HL to FCB in BC (preserved) +; Carry reset & error $14 if illegal filespec + +.l0b3f push bc + call l0b47 ; parse filename + pop bc + ld a,$14 ; error "bad filename" + ret + +; Subroutine to parse filename at HL to FCB in BC (not preserved) +; Exits with carry set if legal filespec + +.l0b47 push hl + ld hl,$0021 + add hl,bc + ld a,(def_drv) + ld (hl),a ; set drive as default + pop hl + ld a,(def_user) + ld (bc),a ; set user area as default + call l0bc9 ; get next filename char + jr nc,l0b7a ; move on if none + ld e,a ; save char + push hl +.l0b5c cp ':' + scf + jr z,l0b66 ; if drive/user spec found, move on + call l0bd3 ; get next char + jr c,l0b5c ; back if found +.l0b66 pop hl + ld a,e ; A=last drive/user char + jr nc,l0b80 ; but skip if no drive/user spec + call l0af8 ; set user area and/or drive in FCB + ret nc ; exit if not found + call l0bc9 ; get char + ret nc + xor ':' + ret nz ; exit if not ":" + call l0bc5 ; get char after ":" + jr c,l0b80 +.l0b7a inc bc ; if no filename, point to start + ld e,$0b ; with 11 chars to blank + scf + jr l0bbc +.l0b80 inc bc ; move to filename in FCB + cp '.' + ret z ; exit if first char is "." + ld e,$08 + call l0b96 ; get up to 8 filename chars + ccf + ld e,$03 ; and up to 3 extension chars + jr nc,l0bb3 + xor '.' + ret nz ; exit if no extension + call l0bc5 ; skip "." and get next char + jr nc,l0bb3 ; move on if none +.l0b96 push hl + cp ' ' + ld hl,l0bef + call nc,l0be5 ; check if char legal + pop hl + jr c,l0bb3 ; if not, fill rest with spaces + dec e + ret m ; exit with error if filename too long + cp '*' + call z,l0bbc ; fill rest with ? if "*" wildcard + ld (bc),a ; else, store character + inc bc + call l0bd3 ; get next + jr nz,l0b96 ; loop back if more + call c,l0bc5 ; skip any spaces +.l0bb3 push af + ld a,' ' + call l0bbe ; fill rest with spaces + pop af + ccf ; set carry (success) + ret + +; Subroutine to fill E+1 chars in filename with "?" wildcard chars +; Or enter at l0bbe to fill with char in A + +.l0bbc ld a,'?' +.l0bbe inc e +.l0bbf dec e + ret z ; exit if done + ld (bc),a ; fill + inc bc + jr l0bbf + +; Subroutine to skip char in filename & get next one + +.l0bc5 call l0bd3 ; get next char + ret nc ; exit if end + +; Subroutine to get next filename char in A, Z set if end-of-filename + +.l0bc9 call l0bd8 ; check for end of filename +.l0bcc ret nz ; exit if not + call l0bd3 ; check if ended by space or $ff + jr c,l0bcc ; skip until $ff encountered + ret + +; Subroutine to get next filename char (exits with C reset +; if already end of filename) + +.l0bd3 ld a,(hl) + cp $ff + ret z ; exit if end of filename + inc hl ; next character + +; Subroutine to get current filename char as uppercase in A, +; checking for end of filename ($ff or space), setting Z if so + +.l0bd8 ld a,(hl) ; get next char + cp $ff + ret z ; exit if end of filename + and $7f ; mask to ASCII code + call l04ed ; convert to uppercase + cp ' ' ; space? + scf ; set "not $ff" + ret + +; Subroutine to check for illegal chars in filenames +; On entry, A=char & on exit carry & Z set if illegal + +.l0be5 cp (hl) ; test against next illegal char + scf + ret z ; exit if illegal + inc hl ; get to next char in list + bit 7,(hl) + jr z,l0be5 ; move back for more + or a ; clear carry & reset Z (character legal) + ret + +; List of illegal chars in filenames + +.l0bef defm "!&()+,-./:;<=>[\]|"&$80 + defs 30 + +; Subroutine to ensure disk for FCB in BC is logged in, if +; necessary building checksum vector, allocation bitmap & directory +; entry information for a drive + +.l0c20 push hl + ld hl,$0021 + add hl,bc + ld a,(hl) ; get drive from FCB + pop hl +.l0c27 call l1871 ; possibly re-log drive + ret nc ; exit if error + bit 0,(ix+$1b) + scf + ret nz ; exit if disk logged in + push hl + push de + push bc + call l0ecc ; initialise allocation bitmap + set 1,(ix+$1b) ; set "collecting sector checksums" + xor a + ld (ix+$22),a + ld (ix+$23),a ; set no free directory entries + call l0c74 ; copy last dir entry number to ext XDPB info + ld bc,$0000 + ld hl,l0c61 + call l0dae ; generate allocation bitmap & get free entries + ld (ix+$24),c + ld (ix+$25),b ; set last used directory entry number + pop bc + pop de + pop hl + ret nc ; exit if error + set 0,(ix+$1b) ; drive logged in + res 1,(ix+$1b) ; just check sector checksums + ret + +; Subroutine to process a directory entry, either incrementing the number +; of free entries, or updating the allocation bitmap for the files blocks + +.l0c61 call l0c67 + scf ; success + sbc a,a ; A=$ff + ret +.l0c67 ld a,(hl) + cp $e5 ; is entry unused? + jp z,l1040 ; if so, inc #free directory entries & exit + ld b,d ; BC=directory entry number + ld c,e + ld a,$ff ; add to allocation bitmap + jp l0f43 ; add entry's blocks into bitmap + +; Subroutine to copy last directory entry number to extended XDPB info + +.l0c74 ld a,(ix+$07) + ld (ix+$24),a + ld a,(ix+$08) + ld (ix+$25),a + ret + +; Subroutine to find extent HL for current file + +.l0c81 call l0d19 ; is correct extent in FCB? + ret c ; exit if so + push hl + call l0cca ; ensure directory up to date for this file + pop hl + ret nc ; exit if error +.l0c8b ex de,hl + ld hl,$000c + add hl,bc + ld (hl),d ; store low 5 bits of extent counter in FCB + inc hl + inc hl + ld (hl),e ; store high 8 bits of extent counter in FCB + ld e,$11 + xor a +.l0c97 inc hl + ld (hl),a ; clear allocation list & records in extent + dec e + jr nz,l0c97 + call l0d4a ; find entry + ret c ; exit if success + ld hl,$0022 + add hl,bc + set 2,(hl) ; signal "new extent required" + or a + ret + +; Subroutine to get new extent for file if required + +.l0ca8 call l0d19 ; check if correct extent in FCB + jr nc,l0cb8 ; move on if not + ld hl,$0022 + add hl,bc + bit 2,(hl) + jp nz,l0fd6 ; move on if new extent required + scf ; else success + ret +.l0cb8 push hl + call l0cca ; ensure directory up to date for this file + pop hl + ret nc +.l0cbe call l0c8b ; set up FCB with clean extent + ret c + cp $19 ; error "end of file" + scf + ccf + ret nz ; exit with error except new extent required + jp l0fd6 ; get new directory entry + +; Subroutine to ensure directory is up-to-date for current file + +.l0cca ld hl,$0022 + add hl,bc + ld a,(hl) ; get FCB flags + and $03 + scf + ret z ; exit with success if directory up to date + cp $02 + jp z,l1038 ; move on if new entry flag set but not needed + and $02 + jr nz,l0ce9 ; move on if need new entry + ld hl,l0d60 + call l0dae ; search directory for correct extent + ret nc ; exit if error + ld a,$20 ; extent missing error + ccf + ret nz ; exit with error if extent not found + jr l0cff ; update directory from FCB & exit +.l0ce9 call l0ff6 ; get new directory entry number (DE) + ret nc ; exit if error + push hl + push de + push bc + call l1049 ; check if datestamps in use + jr nz,l0cfc ; move on if not + ld b,$0a +.l0cf7 ld (hl),$00 ; else zeroise date stamp + inc hl + djnz l0cf7 +.l0cfc pop bc + pop de + pop hl + +; Subroutine to update directory with current FCB info + +.l0cff push de + push bc + ex de,hl + ld h,b + ld l,c + ld bc,$0020 + ldir ; copy directory entry from FCB to "direntry" + pop bc + pop de + call l0e34 ; copy "direntry" to directory entry DE + call c,l1719 ; write all changed buffers back on this disk + ld hl,$0022 + add hl,bc + res 0,(hl) ; flag "directory up to date" + scf + ret + +; Subroutine to check if correct extent for record is in FCB +; On entry, DE=record number +; On exit, carry set if record is within this extent + +.l0d19 push bc + ld a,(ix+$04) ; A=EXM extent mask + cpl + and $1f + ld b,a ; B=low 5 bits of extent mask (inverted) + ld a,d + rra + rra + rra + rra + and $0f + ld l,a ; L=high 8 bits of required extent counter + ld a,e + add a,a + ld a,d + adc a,a + and b + ld h,a ; H=low 5 bits of required extent counter + ld a,b + pop bc + push hl + push de + push bc + ex de,hl + ld hl,$000e + add hl,bc + ld b,a + ld a,(hl) ; A=high 8 bits of extent counter from FCB + xor e + jr nz,l0d46 ; if not same, exit + dec hl + dec hl + ld a,(hl) ; A=low 5 bits of extent counter from FCB + xor d + and b + jr nz,l0d46 ; if not same, exit + scf ; signal "correct extent" +.l0d46 pop bc + pop de + pop hl + ret + +; Subroutine to find directory entry with filename & extent counter in FCB + +.l0d4a ld hl,l0d60 + call l0dae ; find entry with correct extent counter + ret nc ; exit if error + ccf + ld a,$19 + ret nz ; error "end of file" if not found + push bc + ld d,b + ld e,c + ld bc,$0020 + ldir ; copy entry into FCB + pop bc + scf + ret + +; Subroutine to check if directory entry found (HL) matches one in FCB +; FCB can contain wildcards & $ff for extent bytes (match any extent) +; Z set if match found + +.l0d60 push hl + push de + push bc + ld a,(bc) + xor (hl) ; check user area + call z,l0d97 ; and filename + jr nz,l0d82 ; move on if no match + ld a,(de) ; get EX + inc a + jr z,l0d78 ; move on if don't care about extent + ld a,(ix+$04) + cpl + ld b,a ; B=inverse EXM + ld a,(de) + xor (hl) + and b + jr nz,l0d82 ; move on if wrong extent +.l0d78 inc de + inc hl + inc de + inc hl + ld a,(de) ; get S2 + cp $ff + jr z,l0d82 ; move on if don't care about extent + xor (hl) +.l0d82 jr l0d92 ; exit with Z set if match + +; Subroutine to check if current file (BC) and file HL are the same +; Enter at l0d84 if user area flag $20 should be ignored, or at +; l0d8a if user areas must exactly match. Z set if match. + +.l0d84 ld a,(bc) + xor (hl) + and $ef ; mask off user area flag $20 + jr l0d8c +.l0d8a ld a,(bc) + xor (hl) ; check if user areas match +.l0d8c push hl + push de + push bc + call z,l0d97 ; check if filenames match +.l0d92 pop bc + pop de + pop hl + scf + ret + +; Subroutine to check if filename in directory entry at HL matches one +; in FCB at BC (may contain ? wildcards). Z set if match. +; On exit, HL points to directory entry after filename & DE points to +; FCB after filename (or at failed chars) + +.l0d97 push bc + ld d,b + ld e,c + inc de ; DE points to FCB's name + inc hl ; HL points to directory entry's name + ld b,$0b ; 11 bytes to check +.l0d9e ld a,(de) ; get next char from FCB + cp '?' + jr z,l0da8 ; ? matches anything + xor (hl) ; compare chars + and $7f ; mask off attributes + jr nz,l0dac ; move on if no match +.l0da8 inc de + inc hl + djnz l0d9e ; loop back for more chars +.l0dac pop bc + ret + +; Subroutine to call subroutine HL with every directory entry in turn +; The subroutine it calls should set Z if it doesn't require any more +; entries. On exit, this routine leaves address of last-accessed entry +; in HL. + +.l0dae ld (rt_dirent),hl ; store subroutine address + call l1653 ; free buffers referencing directory sectors + ld de,$0000 ; DE=first directory entry + push af +.l0db8 ld a,e + and $0f + jr nz,l0dc3 ; only get a sector every 16th entry + pop af + call l0dff ; get sector & get/set checksum + ret nc ; exit if error + push af +.l0dc3 pop af + push af + push hl + push ix + push de + push bc + ld c,a + ld b,$07 + ld a,e + call l0e6a ; calc offset of entry in sector + call l023d ; copy entry to page 7 + pop bc + pop de + pop ix + push de + call l0ded ; call subroutine rt_dirent + pop de + pop hl + jr nc,l0de7 ; move on if error + jr z,l0de7 ; or if done + call l0df5 ; increment dir entry number + jr nc,l0db8 ; loop back if more +.l0de7 ld hl,direntry ; HL points to last entry obtained + inc sp + inc sp ; drop original AF from stack + ret + +; Subroutine to call the subroutine whose address is at rt_dirent. +; It enters with HL=address of directory entry (direntry) + +.l0ded ld hl,(rt_dirent) + push hl ; stack routine address + ld hl,direntry ; address of directory entry + ret ; "return" to call routine + + +; Subroutine to increment DE (current directory entry) +; On exit, carry is set if no more entries + +.l0df5 inc de ; increment + ld a,(ix+$24) + sub e + ld a,(ix+$25) + sbc a,d ; test against last + ret + +; Subroutine to get sector for directory entry DE, and get/set checksum +; On exit, AHL=sector buffer address. Error if checksums didn't match + +.l0dff push bc + push de + ld a,$02 + call l04d9 ; DE=logical usable record number + call l19c0 ; DE=absolute logical sector number +.l0e09 call l15f4 ; get AHL=page & address of buffer + jr nc,l0e31 ; exit if error + ld b,a ; save buffer details + push hl + call l0e7d ; get sector checksum & address to place + jr c,l0e2d ; skip reserved sectors + bit 1,(ix+$1b) + jr z,l0e1c ; move on if just checking + ld (hl),a ; save checksum +.l0e1c cp (hl) ; set Z if checksum matches + scf ; set success + jr z,l0e2d ; if checksums match, exit + call l166c ; free buffers referencing unchanged sectors + ld a,$08 + call l1a34 ; recoverable error 8 - disk changed + jr nz,l0e2d ; exit if didn't recover + pop hl + jr l0e09 ; else loop back to retry +.l0e2d pop hl ; restore buffer address to AHL + jr nc,l0e31 ; but don't overwrite an error code + ld a,b +.l0e31 pop de + pop bc + ret + +; Subroutine to copy "direntry" to directory entry (DE), marking +; buffer as changed & updating checksum + +.l0e34 push hl + push de + push bc + ld c,e ; save low byte of directory entry number + ld a,$02 + call l04d9 ; DE=directory record number + call l19c0 ; get DE=absolute logical sector + push bc + ld bc,$0001 ; dummy FCB address + call l1624 ; get buffer to AHL, and flag changed + pop bc + jr nc,l0e66 ; exit if error + ld b,a ; save buffer page + push ix + push hl + push de + push bc + ld a,c + call l0e6a ; get address of directory entry + ex de,hl + ld c,$07 + call l023d ; copy "direntry" into directory + pop bc + pop de + pop hl + pop ix + call l0e7d ; calculate sector checksum + jr c,l0e66 + ld (hl),a ; update checksum vector + scf +.l0e66 pop bc + pop de + pop hl + ret + +; Subroutine to calculate an offset into a sector buffer for a +; directory entry +; On entry, A=lowbyte of entry number, HL=buffer address +; On exit, DE=direntry, HL=entry address, IX=$20 + +.l0e6a and $0f ; 16 entries per sector + jr z,l0e75 + ld de,$0020 +.l0e71 add hl,de + dec a + jr nz,l0e71 +.l0e75 ld de,direntry + ld ix,$0020 + ret + +; Subroutine to find address of sector DE (abs log) within checksum table +; (returned in HL) returning sector checksum in B +; On entry, BHL=sector buffer address + +.l0e7d push hl + push de + ex de,hl ; HL=abs logical sector + ld e,(ix+$0b) + ld a,(ix+$0c) + and $7f + ld d,a ; DE=#directory records + call l19c0 ; DE=first non-directory sector + sbc hl,de ; test logical sector + ccf + pop de + pop hl + ret c ; exit if not after directory + push bc + ld a,b + call l0207 ; page in bank containing buffer + push af + xor a + ld bc,$0002 +.l0e9c add a,(hl) ; A=8-bit checksum of sector + inc hl + djnz l0e9c + dec c + jr nz,l0e9c + ld b,a ; save checksum + pop af + call l0207 ; page back original bank + ld l,(ix+$26) + ld h,(ix+$27) ; HL=address of checksum table + add hl,de ; +abs logical sector + push de + ld de,$0000 + call l19c0 ; DE=first non-reserved sector + or a + sbc hl,de ; HL=address of sector checksum + pop de + ld a,b + or a + pop bc ; restore checksum + ret + +; Subroutine to check if file (HL=FCB) is read-only, giving error if so + +.l0ebe push de + ex de,hl + ld hl,$0009 + add hl,de + ld a,(hl) ; get read-only bit + add a,a ; move to carry + ex de,hl + pop de + ccf ; carry is inverse of read-only bit + ld a,$1c ; "read-only file" error + ret + +; Subroutine to initialise the allocation bitmap of a drive with the +; directory bitmaps + +.l0ecc call l0fc9 ; get HL=allocation bitmap, DE=last block # + ld a,$03 + call l04d9 + inc de ; DE=(last block #)/8+1 + push hl +.l0ed6 ld (hl),$00 ; zero allocation for 8 blocks + inc hl + dec de + ld a,d + or e + jr nz,l0ed6 ; loop back for more + pop hl + ld a,(ix+$09) ; store directory bitmaps + ld (hl),a + inc hl + ld a,(ix+$0a) + ld (hl),a + ret + + +; Subroutine to allocate/deallocate a block +; On entry, DE=block number and C=$00 to remove block from allocation bitmap +; or $ff to add block to allocation bitmap + +.l0ee9 push bc + push hl + push de + ld a,$03 + call l04d9 ; DE=block/8 + push de + call l0fc9 ; get HL=address of allocation bitmap + pop de + add hl,de ; HL points to correct allocation byte + pop de + ld a,e + and $07 + ld b,a ; B=bit within byte + ld a,$01 + inc b +.l0eff rrca + djnz l0eff + ld b,a ; B=bitmask + and c + ld c,a ; C=bit if allocating, 0 if deallocating + ld a,b + cpl + and (hl) + or c + ld (hl),a ; update bit in allocation bitmap + pop hl + pop bc + ret + +; Subroutine to allocate a new block for a file (returned in DE) + +.l0f0d push hl + push bc + call l0fc9 ; get HL=allocation bitmap & DE=last block +.l0f12 ld bc,$0880 ; bit counter & mask +.l0f15 ld a,(hl) ; get byte from allocation bitmap + and c + jr z,l0f27 ; move on if found free block + rrc c ; shift bit mask + ld a,d + or e + ld a,$1a + jr z,l0f3d ; move on if no blocks left + dec de + djnz l0f15 ; loop back for more blocks in allocation byte + inc hl + jr l0f12 ; back for more allocation bytes +.l0f27 ld a,(hl) + or c + ld (hl),a ; allocate this block + ld a,(ix+$05) + sub e + ld e,a + ld a,(ix+$06) + sbc a,d + ld d,a ; calculate DE=allocated block + pop bc + push bc + ld hl,$0022 + add hl,bc + set 0,(hl) ; signal directory contains valid FCB data + scf +.l0f3d pop bc + pop hl + ret + +; Subroutine to deallocate all blocks in current FCB + +.l0f40 ld h,b ; HL=FCB address + ld l,c + xor a ; deallocate + +; Subroutine to add/remove all the blocks in a directory entry to +; the allocation bitmap +; On entry, HL=address of directory entry, A=$ff to add or $00 to remove + +.l0f43 push bc ; save BC + ld c,a ; C=allocate/deallocate flag + ld a,$0f + cp (hl) + jr c,l0f72 ; exit if not a file entry + ld de,$0010 + add hl,de ; HL points to allocation list + ld b,$10 ; 16 allocation entries to consider + inc b + jr l0f70 ; go to start loop +.l0f53 ld e,(hl) ; E=next allocation entry + inc hl + ld a,(ix+$06) ; more than 256 blocks on disk? + or a + ld d,a ; DE=block number if 8-bit block numbers + jr z,l0f5f + dec b + ld d,(hl) ; DE=16-bit block number + inc hl +.l0f5f ld a,d + or e + jr z,l0f70 ; move on if allocation entry null + push hl + ld a,(ix+$05) + sub e + ld a,(ix+$06) + sbc a,d ; test against last block number + call nc,l0ee9 ; if in range, allocate/deallocate block + pop hl +.l0f70 djnz l0f53 ; loop back +.l0f72 pop bc + scf + ret + +; Subroutine to calculate extent size in K (to HL) + +.l0f75 push de + ex de,hl + ld a,(de) + cp $10 ; test user area + ld hl,$0000 ; zero blocks so far + jr nc,l0f99 ; skip calculation if password entry + ld hl,$0010 + add hl,de ; point to allocation list + ld de,$1000 ; D=#bytes to test,E=#blocks found +.l0f86 ld a,(ix+$06) + or a ; more than 255 blocks? + ld a,(hl) ; get block number + inc hl + jr z,l0f91 ; if not move on + or (hl) ; else incorporate 2nd byte + dec d ; and move over + inc hl +.l0f91 or a + jr z,l0f95 + inc e ; increment #blocks if not null +.l0f95 dec d + jr nz,l0f86 ; loop back for rest of allocation list + ex de,hl ; HL=#blocks, to be converted to K next +.l0f99 pop de + +; Subroutine to convert HL=blocks to K + +.l0f9a ld a,(ix+$02) ; get block size + dec a + dec a +.l0f9f dec a + jr z,l0fa5 ; move on when HL=size in K + add hl,hl ; double it + jr l0f9f ; loop back +.l0fa5 ld a,h + or l ; set Z if HL=0 + scf ; success + ret + +; Subroutine to calculate HL=free space (in K) on current drive + +.l0fa9 ld hl,$0000 ; 0 free blocks so far + push hl ; stack it + call l0fc9 ; HL=alloc bitmap, DE=last block number +.l0fb0 ld bc,$0880 ; bit count & mask +.l0fb3 ld a,(hl) + and c ; check if block free + jr nz,l0fba + ex (sp),hl + inc hl ; increment #free if so + ex (sp),hl +.l0fba rrc c ; shift bit mask + ld a,d + or e + jr z,l0fc6 ; move on if no more blocks + dec de + djnz l0fb3 ; loop back for rest of allocation byte + inc hl + jr l0fb0 ; loop back +.l0fc6 pop hl ; HL=#free blocks + jr l0f9a ; convert blocks to K & exit + +; Subroutine to get HL=allocation bitmap & DE=last block number from XDPB + +.l0fc9 ld l,(ix+$28) + ld h,(ix+$29) ; HL=add of allocation bitmap + ld e,(ix+$05) + ld d,(ix+$06) ; DE=last block number + ret + +; Subroutine to check if new directory entry available + +.l0fd6 ld a,(ix+$22) + or (ix+$23) + ld a,$1b + ret z ; error "directory full" if no new entries + ld hl,$0022 + add hl,bc + set 1,(hl) ; new directory entry needed + res 2,(hl) ; extent now created + ld a,(ix+$22) + sub $01 ; decrement #free directory entries + ld (ix+$22),a + jr nc,l0ff4 + dec (ix+$23) +.l0ff4 scf + ret + +; Subroutine to get a new directory entry (DE) + +.l0ff6 push bc + ld c,(ix+$24) + ld b,(ix+$25) ; BC=last used directory entry number + call l0c74 ; set last used=last entry + ld hl,l1033 + call l0dae ; find first unused entry + jr nc,l1031 ; exit if error + ex (sp),hl + push hl + push de + push af + ld de,$0022 + add hl,de + res 1,(hl) ; reset "new directory entry" flag + pop af + pop de + pop hl + ex (sp),hl + jr nz,l102b ; if no free entry found, cause error + ex de,hl + or a + sbc hl,bc + add hl,bc + ex de,hl + jr c,l1022 ; move on if entry lower than last used + ld b,d + ld c,e ; else last used=this entry +.l1022 ld (ix+$24),c + ld (ix+$25),b ; set last used entry + scf ; success + jr l1031 +.l102b call l1040 ; re-increment free directory entries + ld a,$20 ; "extent missing" error + or a +.l1031 pop bc + ret + +; Subroutine to check if directory entry at HL is free + +.l1033 ld a,(hl) + xor $e5 ; set Z if free + scf ; success + ret + +; Subroutine to clear new entry flag & increment # free entries + +.l1038 push hl + ld hl,$0022 + add hl,bc + res 1,(hl) ; we don't need a new entry + pop hl +.l1040 scf ; success + inc (ix+$22) ; increment # free directory entries + ret nz + inc (ix+$23) + ret + +; Subroutine to check for datestamps, and if present to exit with Z set +; and HL pointing to address to place datestamp for entry DE + +; *BUG* This routine assumes that on entry, HL contains the address of a +; directory entry within a record, and tries to access the last entry in +; the record. However, HL points to a copy of the entry at "direntry", +; so the routine checks for a datestamp at $dfc2 (unused), $dfe2 (byte 2 of +; BCB 0) or $e002 (byte 1 of BCB 3). +; Luckily, none of these locations can ever hold a datestamp identifier, +; so the effect of the bug is that datestamps are always ignored. + +.l1049 ld a,e + and $03 + cpl + add a,$04 + jr z,l1058 + ld bc,$0020 +.l1054 add hl,bc ; generate HL=address of last entry in record + dec a + jr nz,l1054 +.l1058 ld a,(hl) + cp $21 ; check if datestamp entry + ret nz ; exit if not + ld a,e + and $03 + jr z,l1068 + ld bc,$000a +.l1064 add hl,bc ; generate address of datestamp for entry + dec a + jr nz,l1064 +.l1068 inc hl + ret + + defs 6 + +; DOS_GET_POSITION + +.l1070 call l0525 ; get FCB + ret nc ; exit if error +.l1074 ld hl,$0026 ; offset for file position + jr l1080 ; move on + +; DOS_GET_EOF + +.l1079 call l0525 ; get FCB + ret nc ; exit if error + ld hl,$0023 ; offset for EOF +.l1080 add hl,bc + ld e,(hl) + inc hl + ld d,(hl) + inc hl + ld a,(hl) + ex de,hl + ld e,a + ld d,$00 ; DEHL=file position/EOF + scf ; success + ret + +; DOS_SET_POSITION + +.l108c call l0525 ; get FCB + ret nc ; exit if error +.l1090 ld a,l + ld d,e + ld e,h ; DEA=new filepos + ld hl,$0026 + add hl,bc ; HL=add of filepos in FCB + jr l10ad ; move on + +; Subroutine to increment filepointer by 1 (l09d), $80 (l1099) or A (l109f) +; If this caused the filepointer to cross a record boundary, bit 5 of +; the FCB flags byte is reset + +.l1099 ld a,$80 + jr l109f +.l109d ld a,$01 +.l109f ld hl,$0026 + add hl,bc ; HL=add of filepos in FCB + add a,(hl) ; add in A + inc hl + ld e,(hl) + inc hl + ld d,(hl) + jr nc,l10ab + inc de +.l10ab dec hl ; move HL back to start of filepos + dec hl +.l10ad push hl ; save registers + push af + xor (hl) + jp m,l10bd ; move on if record changed + inc hl + ld a,(hl) + cp e + jr nz,l10bd + inc hl + ld a,(hl) + cp d + jr z,l10c3 ; skip next bit if record unchanged +.l10bd ld hl,$0022 + add hl,bc + res 5,(hl) ; flag "record changed" +.l10c3 pop af + pop hl + ld (hl),a + inc hl + ld (hl),e + inc hl + ld (hl),d ; set new filepointer + scf ; success + ret + +; Subroutine to ensure filelength is at least as large as current filepointer + +.l10cc push bc + push af + ld hl,$0022 + add hl,bc + ex de,hl ; DE points to filelength-1 + ld hl,$0025 + add hl,bc ; HL points to filepointer-1 + ld b,$03 + or a +.l10da inc de + inc hl + ld a,(de) + sbc a,(hl) ; compare filepointer with filelength + djnz l10da + jr nc,l10e7 ; exit if filepointer within current file + ld bc,$0003 + lddr ; make filelength=filepointer +.l10e7 pop af + pop bc + ret + +; DOS_READ + +.l10ea ld a,c + ld (rw_page),a ; save page & address to read to + ld (rw_add),hl + call l0514 ; test if file B open for reading + ret nc ; exit if not + add hl,de ; calculate final address + push hl + call l1107 ; do the read + pop hl + ret c ; exit if successful + push af + ld de,(rw_add) ; get current address + or a + sbc hl,de + ex de,hl ; DE=unread bytes + pop af + ret + +; Subroutine to read DE bytes from file + +.l1107 push bc + push de + ld hl,$0023 + add hl,bc + ld e,(hl) + inc hl + ld d,(hl) + inc hl + ld b,(hl) ; get BDE=file length + inc hl + ld a,e + scf + sbc a,(hl) + ld e,a + inc hl + ld a,d + sbc a,(hl) + ld d,a + inc hl + ld a,b + sbc a,(hl) + ex de,hl ; AHL=filelength available to read + pop de + pop bc + jr c,l1130 ; exit if none + dec de ; DE=#bytes to read-1 + sbc hl,de + add hl,de + sbc a,$00 + jr nc,l1134 ; if enough bytes in file, move on + ex de,hl + call l1134 ; else read what's available + ret nc ; exit if error +.l1130 ld a,$19 ; "end of file" error + or a + ret +.l1134 ld hl,$0026 + add hl,bc + ld a,(hl) + and $7f + jr z,l1144 ; move on if filepointer on record boundary + call l1158 ; read byte + ret nc ; exit if error + ret z ; exit if finished + jr l1134 ; loop back +.l1144 ld hl,$ff81 + add hl,de + jr nc,l1151 ; move on if don't need any more full records + call l1175 ; read a record + ret nc ; exit if error + ret z ; exit if finished + jr l1144 ; loop back +.l1151 call l1158 ; read byte + ret nc ; exit if error + ret z ; exit if finished + jr l1151 ; loop back + +; Subroutine to read a byte from the file to correct address +; On exit, Z set if finished + +.l1158 call l11cb ; read a byte + ret nc ; exit if error + push de + ld e,a + ld a,(rw_page) + ld hl,(rw_add) + call l0207 ; page in bank + ld (hl),e ; store byte + call l0207 ; page back original bank + inc hl + ld (rw_add),hl ; update address to read to + pop de + ld a,d + or e ; set Z if finished + dec de ; decrement #bytes to read + scf ; success + ret + +; Subroutine to read a record from the file to correct address +; On exit, Z set if finished + +.l1175 push de + call l137d ; get record to buffer & update FCB + pop de + ret nc ; exit if error + push de + call l1099 ; increment filepointer by $80 + ld hl,$002d + add hl,bc + ld e,(hl) + inc hl + ld d,(hl) ; DE=address of record + inc hl + push bc + ld c,(hl) ; C=page of buffer + ld a,(rw_page) + ld b,a + ld hl,(rw_add) + ex de,hl + ld ix,$0080 + call l023d ; copy record from buffer + pop bc + ld (rw_add),de ; update address to read to + pop de + ld hl,$ff81 + add hl,de + ex de,hl ; subtract $7f from bytes to read + ld a,d + or e ; set Z if done + dec de ; subtract further byte + scf ; success + ret + +; DOS_BYTE_READ + +.l11a8 call l0514 ; get FCB & test if open for reading + ret nc ; exit if error + ld hl,$0026 + add hl,bc + ex de,hl ; DE=filepointer address + ld hl,$0023 + add hl,bc ; HL=filelength address + push bc + ld b,$03 + or a +.l11b9 ld a,(de) ; test filepointer (carry must be set + sbc a,(hl) ; if pointer within file) + inc de + inc hl + djnz l11b9 + pop bc + ld a,$19 ; error "end of file" + call c,l11cb ; get a byte if within file + ret nc ; exit if error + ld c,a ; C=byte read + cp $1a ; set Z if soft-EOF + scf ; success + ret + +; Subroutine to read a byte (A) from the file + +.l11cb push hl + push de + ld hl,$0022 + add hl,bc + bit 5,(hl) ; has record been changed? + jr nz,l11da + call l137d ; if so, get new record details into FCB + jr nc,l11fb ; exit if error +.l11da ld hl,$0026 + add hl,bc + ld a,(hl) ; low byte of filepointer + and $7f ; offset into record + ld hl,$002d + add hl,bc + add a,(hl) + ld e,a + inc hl + adc a,(hl) + sub e + ld d,a ; DE=address of byte in buffer + inc hl + ld a,(hl) ; A=bank of buffer + ex de,hl + call l0207 ; page in buffer bank + ld d,(hl) ; get byte + call l0207 ; page back original bank + push de + call l109d ; increment filepointer + pop af ; A=byte + scf ; success +.l11fb pop de + pop hl + ret + +; DOS_WRITE + +.l11fe ld a,c + ld (rw_page),a ; store bank & address to write from + ld (rw_add),hl + call l051c ; test file B open for writing + ret nc ; exit if error + add hl,de ; calculate final address + push hl + call l121e ; do the write + call l10cc ; ensure filelength includes filepointer + pop hl + ret c ; exit if successful + push af + ld de,(rw_add) + or a + sbc hl,de + ex de,hl ; DE=bytes unwritten + pop af + ret + +; Subroutine to write DE bytes to file + +.l121e dec de +.l121f ld hl,$0026 + add hl,bc + ld a,(hl) + and $7f + jr z,l122f ; move on if filepointer on record boundary + call l1243 ; write a byte + ret nc ; exit if error + ret z ; exit if finished + jr l121f ; loop back +.l122f ld hl,$ff81 + add hl,de + jr nc,l123c ; move on if don't need to write full record + call l1261 ; write a record + ret nc ; exit if error + ret z ; exit if finished + jr l122f ; loop back +.l123c call l1243 ; write a byte + ret nc ; exit if error + ret z ; exit if finished + jr l123c ; loop back + +; Subroutine to write a byte to file + +.l1243 ld a,(rw_page) ; get bank & address + ld hl,(rw_add) + call l0207 ; page in bank + ld l,(hl) ; get byte + call l0207 ; page back original bank + ld a,l + call l12a5 ; write the byte + ret nc ; exit if error + ld hl,(rw_add) + inc hl + ld (rw_add),hl ; update address to write from + ld a,d + or e ; test if finished + dec de ; decrement bytes to write + scf + ret + +; Subroutine to write a record to file + +.l1261 push de + call l134e ; get record to buffer at AHL + pop de + ret nc ; exit if error + ld hl,$0022 + add hl,bc + set 4,(hl) ; signal "current record changed" + push de + call l1099 ; increment filepointer by $80 + ld hl,$002d + add hl,bc + ld e,(hl) + inc hl + ld d,(hl) + inc hl + push bc + ld b,(hl) ; get B=bank, DE=address of buffer + ld a,(rw_page) + ld c,a + ld hl,(rw_add) + ld ix,$0080 + call l023d ; copy record to buffer + pop bc + ld (rw_add),hl ; update address to write from + pop de + ld hl,$ff81 + add hl,de + ex de,hl ; subtract $7f from bytes to read + ld a,d + or e ; set Z if finished + dec de ; subtract further byte + scf + ret + +; DOS_BYTE_WRITE + +.l1298 ld e,c + call l051c ; check file open for writing + ret nc ; exit if not + ld a,e + call l12a5 ; write byte A at filepointer + call c,l10cc ; ensure filelength includes filepointer + ret + +; Subroutine to place byte A in file at current filepointer, and increment +; filepointer + +.l12a5 push hl + push de + ld e,a ; save byte in E + ld hl,$0022 + add hl,bc + bit 5,(hl) + jr nz,l12b9 ; move on if FCB contains valid record details + push hl + push de + call l134e ; get record to buffer + pop de + pop hl + jr nc,l12db ; exit if error +.l12b9 set 4,(hl) ; signal "record changed" + ld hl,$0026 + add hl,bc + ld a,(hl) + and $7f ; A=offset into current record + ld hl,$002d + add hl,bc + push de + add a,(hl) + ld e,a + inc hl + adc a,(hl) + sub e + ld d,a ; DE=address in file + inc hl + ld a,(hl) ; A=bank of file + ex de,hl + pop de + call l0207 ; page in file bank + ld (hl),e ; store byte at filepointer + call l0207 ; page back original bank + call l109d ; increment filepointer +.l12db pop de + pop hl + ret + +; Subroutine to test if file has valid header checksum (Z set if so) + +.l12de call l12f7 ; get stored & calculated checksum + ret nc ; exit if error + ld a,d + cp e ; set Z if they match + scf ; success + ret + +; Subroutine to update checksum in file header & set filepointer after it + +.l12e6 call l12f7 ; get header checksums + ret nc ; exit if error + call l0207 ; page in bank file bank + ld (hl),e ; store calculated header checksum + call l0207 ; page back original bank + call l1099 ; increment filepointer past header + jp l10cc ; ensure filelength includes filepointer + +; Subroutine to get stored header checksum (D) & calculated checksum (E) + +.l12f7 ld hl,$0000 + ld e,h + call l1090 ; set position to file start + ld hl,$0022 + add hl,bc + bit 5,(hl) + jr nz,l130a ; move on if valid record details in FCB + call l137d ; get record to buffer and update FCB + ret nc ; exit if error +.l130a ld hl,$002d + add hl,bc + ld e,(hl) + inc hl + ld d,(hl) ; DE=address of file start + inc hl + ld a,(hl) ; A=bank of file + ex de,hl + push af + call l0207 ; page in file bank + push af + xor a ; zero checksum + ld e,$7f +.l131c add a,(hl) ; form checksum + inc hl + dec e + jr nz,l131c ; back for more bytes in header + ld e,a ; E=calculated checksum + ld d,(hl) ; D=checksum stored in file header + pop af + call l0207 ; page back original bank + pop af + scf + ret + +; Subroutine to get sector in FCB to a buffer + +.l132a push hl + ld hl,$0022 + add hl,bc ; HL points to flags of FCB + bit 3,(hl) + jr z,l1347 ; move on if sector already in buffer + bit 4,(hl) + jr z,l1347 ; or if current record not changed + push hl + push de + ld hl,$002b + add hl,bc + ld e,(hl) + inc hl + ld d,(hl) ; DE=logical sector + call l1624 ; get AHL=buffer address (flag changed) + pop de + pop hl + jr nc,l134c ; move on if error +.l1347 ld a,(hl) + and $c7 ; reset bits 3,4 & 5 + ld (hl),a + scf ; success +.l134c pop hl + ret + +; Subroutine to get abs logical sector (DE) & record address (AHL) +; for record (DE) in current file, creating new record if required + +.l134e ld a,(bc) + cp $22 ; test for "drive open as file" + ld hl,l135b ; routine to use for normal file + jr nz,l1388 + ld hl,l14eb ; routine to use for drive + jr l1388 + +; Subroutine to find abs log sector (DE) and record address (AHL) +; for possibly new record DE in current file + +.l135b push de + call l0ca8 ; get new extent if required + pop de + call c,l13f0 ; get record address & sector number + ret nc ; exit if error + push hl + push de + push af + call l13d2 ; get DE=record number from FCB + call l14b0 ; check in current extent + call nc,l1493 ; if so, set FCBs extent counter & #records + pop af + pop de + pop hl + ret + +; Subroutine to get the abs log sector (DE) and address (AHL) of +; record DE in the current file + +.l1374 push de + call l0c81 ; find extent HL for file + pop de + call c,l1423 ; if found, get address of record + ret + +; Subroutine to get the current record into a buffer and update the FCB +; with its details + +.l137d ld a,(bc) + cp $22 ; test for "drive open as file" + ld hl,l1374 ; routine to use for normal file + jr nz,l1388 + ld hl,l14eb ; routine to use for drive +.l1388 call l13dc ; DE=record number for filepointer + ret nc ; exit if file too big + push hl + ld hl,$0022 + add hl,bc + bit 3,(hl) + pop hl + jr z,l13a2 ; move on if no sector currently in buffer + push hl + ex de,hl + call l13d2 ; get record number from FCB + ex de,hl + or a + sbc hl,de + pop hl + jr z,l13c8 ; move on if record numbers match +.l13a2 call l0c20 ; ensure correct disk logged in + call c,l132a ; get sector in FCB to buffer + ret nc ; exit if error + push hl + ld hl,$0029 + add hl,bc + ld (hl),e + inc hl + ld (hl),d + pop hl + call l13d2 ; DE=record number required + call l04ec ; call routine in HL + ret nc ; exit if error + push hl + ld hl,$002b + add hl,bc + ld (hl),e + inc hl + ld (hl),d ; store abs logical sector number + pop de + inc hl + ld (hl),e + inc hl + ld (hl),d ; store add of record + inc hl + ld (hl),a ; store bank for buffer +.l13c8 ld hl,$0022 + add hl,bc + ld a,(hl) + or $28 ; set bit 3 (valid sector) & bit 5 + ld (hl),a ; (valid filepointer) + scf + ret + +; Subroutine to get DE=record number from FCB + +.l13d2 push hl + ld hl,$0029 + add hl,bc + ld e,(hl) + inc hl + ld d,(hl) + pop hl + ret + +; Subroutine to get DE=record number for filepointer + +.l13dc push hl + ld hl,$0026 + add hl,bc ; HL points to filepointer + ld a,(hl) + inc hl + ld e,(hl) + inc hl + ld d,(hl) + ex de,hl ; HLA=filepointer + add a,a + adc hl,hl ; double filepointer + ex de,hl ; DE=record number + ccf ; successful if no carry + ld a,$22 ; error "file too big" + pop hl + ret + +; Subroutine to get record address (AHL) and sector (DE) for record DE, +; creating new record if required + +.l13f0 push de + call l1459 ; get block & offset + ex de,hl + ex (sp),hl + ex de,hl + jr c,l140a ; move on if not end-of-file + call l0f0d ; allocate a new block + jr nc,l1413 ; move on if error + ld (hl),e ; insert block in allocation list + ld a,(ix+$06) + or a + jr z,l1407 + inc hl + ld (hl),d +.l1407 ex de,hl ; HL=block number + jr l1411 +.l140a ld a,e + and $03 + scf + call z,l14b0 ; if first record in a sector, check in extent +.l1411 sbc a,a ; Z set if record newly created + scf +.l1413 pop de + call c,l143c ; get abs logical sector & offset + push hl + call c,l141d ; get sector to buffer + jr l1435 ; exit, getting required record to AHL + +; Subroutine to get sector DE to a buffer, don't bother reading in if +; newly created record (Z set) + +.l141d jp z,l160c ; if created, don't read sector + jp l15f4 ; get sector, reading if necessary + +; Subroutine to find abs log sector (DE) and record address (AHL) +; for record DE in current file + +.l1423 push de + call l1459 ; get block & offset + ex de,hl + ex (sp),hl + ex de,hl + call c,l14b0 ; check record is in this extent + pop de + call c,l143c ; calculate sector & offset +.l1431 push hl ; stack offset into sector + call c,l15f4 ; get AHL=address of sector DE +.l1435 ex de,hl + ex (sp),hl ; stack sector, restore offset + push af + add hl,de ; now AHL=address of record + pop af + pop de ; restore sector + ret + +; Subroutine to calculate abs logical sector (DE) and offset (HL) from +; block number (HL) offset into block (DE) + +.l143c push bc + push af + ex de,hl + ld a,(ix+$02) + call l04e3 ; DE=logical usable record + call l19c0 ; DE=abs logical sector of block start + ex de,hl + ld b,d ; save high byte of offset + ld a,d + and $01 + ld d,a + ex de,hl ; HL=offset into sector + xor b + rrca ; A=#sectors offset + add a,e + ld e,a + adc a,d + sub e + ld d,a ; DE=abs logical sector + pop af + pop bc + ret + +; Subroutine to calculate block number (HL) and offset (DE) of record +; DE in file, assuming correct extent is in FCB + +.l1459 push bc + ld h,b ; HL=FCB + ld l,c + ld a,(ix+$03) + and e ; A=record within block + rra + ld b,a + ld a,$00 + rra + ld c,a ; BC=byte offset within block + ld a,(ix+$02) + call l04d9 + ld d,$00 ; DE=block number (low byte) + ld a,(ix+$06) + or a + ld a,e + jr z,l1480 ; move on if <256 blocks on disk with A=block + and $07 + add a,a + add a,$11 + ld e,a + add hl,de ; for 256+ blocks, each uses 2 bytes in list + ld d,(hl) ; D=high byte of actual block number + dec hl ; HL points to low byte + jr l1486 +.l1480 and $0f + add a,$10 + ld e,a + add hl,de ; for <256 blocks, each uses 1 byte in list +.l1486 ld e,(hl) ; now, DE=block number on disk + ld a,d + or e + ld a,$19 + jr z,l148f ; if 0, exit with "end of file" error + ex de,hl ; HL=block number + scf ; success +.l148f ld d,b + ld e,c + pop bc + ret + +; Subroutine to set extent counter & number of records in extent + +.l1493 push hl + ld a,e + and $7f + inc a + ld hl,$000f + add hl,bc + ld (hl),a ; set number of records in extent + ld a,e + rla + ld a,d + rla + and $1f + dec hl + dec hl + dec hl + ld (hl),a ; set extent counter (low 5 bits) + ld hl,$0022 + add hl,bc + set 0,(hl) ; directory contains valid copy of FCB data + scf + pop hl + ret + +; Subroutine to check record number DE is in the current extent + +.l14b0 push de + push hl + call l14c2 ; find last record number in extent + or a + ld a,$22 + jr nz,l14bf ; "file too big" error + ex de,hl + sbc hl,de ; check record is in this extent + ld a,$19 ; "end of file" error if not +.l14bf pop hl + pop de + ret + +; Subroutine to calculate ADE=last record in extent+1 + +.l14c2 push de + ld hl,$000c + add hl,bc + ld d,(hl) + ld e,$00 + srl d + rr e + inc hl + inc hl + inc hl + ld a,(hl) + or a + jp p,l14d8 + ld a,$80 +.l14d8 add a,e + ld e,a + adc a,d + sub e + dec hl + ld l,(hl) + ld h,$00 + add hl,hl + add hl,hl + add hl,hl + add hl,hl + add a,l + ld d,a + adc a,h + sub d ; A=#records in this extent + ex de,hl ; DE=last record in extent+1 + pop de + ret + +; Subroutine to find abs log sector (DE) and record address (AHL) +; for record DE in current file (which is an open drive) + +.l14eb ld a,e + and $03 + ld h,a + ld l,$00 + srl h + rr l ; HL=offset of record into sector + ld a,$02 + call l04d9 ; DE=absolute logical sector + push hl + push de + ex de,hl + ld a,(ix+$02) + ld e,(ix+$05) + ld d,(ix+$06) + inc de + call l04e3 + call l19c0 ; DE=max sector on disk + or a + sbc hl,de ; no carry (error) if sector > max + pop de + pop hl + ld a,$19 ; error "end of file" + jp l1431 ; go to get sector & calculate address + + defs 25 + +; Subroutine to get D=first buffer for cache, E=number of cache buffers + +.l1530 ld de,(cachenum) ; get the values + ret + +; Subroutine to set cache to D=first buffer, E=# buffers + +.l1535 call l1706 ; clear current cache + ret nc ; exit if error +.l1539 ld hl,$0000 + ld (bcb_inuse),hl ; clear last in-use BCB address + ld (bcb_free),hl ; clear last free BCB address + ld h,d + ld (cachenum),hl ; set first buffer=D, no buffers yet + ld ix,bcbs ; address for first BCB + ld b,$10 ; max 16 buffers required + ld a,$07 + ld hl,cache7 ; first buffer in bank 7 + jr l1561 ; move on +.l1553 ld a,e + or a + scf ; exit if created all required BCBs + ret z + ld hl,cachenum + inc (hl) ; increment # buffers + ld a,d + inc d ; increment buffer number + dec e ; decrement buffers left to create + call l022b ; get address & bank of buffer D +.l1561 ld (ix+$08),l + ld (ix+$09),h ; store buffer address + ld (ix+$0a),a ; and bank + ld hl,(bcb_free) + ld (ix+$00),l + ld (ix+$01),h ; store add of previous BCB + ld (bcb_free),ix ; current BCB is now last + ex de,hl + ld de,$000b + add ix,de ; address of next BCB + ex de,hl + djnz l1553 ; back for more + scf ; success + ret + +; Subroutine to find BCB (address in DE) of buffer holding log sector in BC +; on current drive + +.l1582 ld de,bcb_inuse ; last in-use buffer address +.l1585 ex de,hl + call l179b ; get next in-use BCB + ccf + ld a,$21 + ret z ; fail with error "uncached" if none + push hl + ld hl,$0005 + add hl,de + ld a,(hl) + cp (ix+$1c) ; is buffer from current drive? + jr nz,l15a0 ; move on if not + inc hl + ld a,(hl) + inc hl + ld h,(hl) ; HL=abs logical sector in buffer + ld l,a + or a + sbc hl,bc +.l15a0 pop hl + jr nz,l1585 ; loop back if not required buffer + scf ; success + ret + +; Subroutine to get a buffer for logical sector BC +; On exit, DE=BCB + +.l15a5 call l1798 ; get last free BCB + jr nz,l15c4 ; move on if got one + ld de,bcb_inuse ; address of in-use BCB chain +.l15ad ex de,hl + call l179b ; get next in-use BCB + ccf + ld a,$21 + ret z ; "uncached" error if no more inuse BCBs + call l17a3 ; is this last BCB in chain? + jr nz,l15ad ; loop back if not + call l15db ; get sector for BCB to memory + call l16ec ; write back data from this buffer if required + ret nc ; exit if error + call l17aa ; move BCB to the free chain +.l15c4 push hl + ld hl,$0002 + add hl,de + xor a + ld (hl),a ; no data changed + inc hl + ld (hl),a + inc hl + ld (hl),a ; no file owner + inc hl + ld a,(ix+$1c) + ld (hl),a ; drive + inc hl + ld (hl),c + inc hl + ld (hl),b ; absolute logical sector + pop hl + scf ; success + ret + +; Subroutine to get sector for BCB into memory + +.l15db push ix + push hl + push de + push bc + ld hl,$0005 + add hl,de + ld a,(hl) + inc hl + ld e,(hl) + inc hl + ld d,(hl) + ex de,hl + ld d,a ; D=drive letter, HL=abs logical sector + call l05f0 ; get sector to buffer, updating FCBs + pop bc + pop de + pop hl + pop ix + ret + +; Subroutine to return page (A) & address (HL) for absolute logical sector DE +; reading to a new buffer if necessary + +.l15f4 bit 3,(ix+$1b) + jp nz,l1ab6 ; move on if RAMdisk + push de + push bc + ld b,d + ld c,e ; BC=abs logical sector + call l1582 ; check if sector held in a buffer + jr c,l160a ; move on if so + call l15a5 ; get a new buffer for it + call c,l1778 ; if no error, read the sector to the buffer +.l160a jr l161d ; move on + +; Subroutine to return page (A) & address (HL) for absolute logical sector DE +; creating a new buffer if necessary (but not reading sector from disk) + +.l160c bit 3,(ix+$1b) + jp nz,l1ab6 ; move on if RAMdisk + push de + push bc + ld b,d + ld c,e ; BC=abs logical sector + call l1582 ; check if sector held in a buffer + call nc,l15a5 ; get a new buffer if not +.l161d push af + call c,l17b2 ; if no error, move BCB to top of in-use chain + pop af + jr l1643 ; go to return buffer address as AHL + +; Subroutine to get page (A) and address (HL) of buffer for files +; current logical sector, & flag as changed + +.l1624 bit 3,(ix+$1b) + jp nz,l1ab6 ; move on if RAMdisk + push de + push bc + push bc + ld b,d + ld c,e + call l1582 ; find buffer holding logical sector + pop bc + jr nc,l1643 ; move on if error + push hl + ld hl,$0002 + add hl,de + set 0,(hl) ; flag "data changed" + inc hl + ld (hl),c + inc hl + ld (hl),b ; store FCB address of file using buffer + pop hl + scf ; successful so far +.l1643 jr nc,l1650 ; move on if error + ld hl,$0008 + add hl,de + ld e,(hl) + inc hl + ld d,(hl) + inc hl + ld a,(hl) ; A=buffer bank + ex de,hl ; HL=buffer address + scf ; success +.l1650 pop bc + pop de + ret + +; Subroutine to free all buffers referencing unchanged sectors on drive +; Enter at l166c, or l1653 to free only buffers referencing directory sectors + +.l1653 call l18ab ; has drive been recently accessed? + ret c ; exit if so + push hl + push de + push bc + ld e,(ix+$07) + ld d,(ix+$08) + inc de ; DE=#directory entries + ld a,$02 + call l04d9 ; DE=#directory records (32 bytes per entry) + call l19c0 + dec de ; DE=last absolute directory sector + jr l1672 +.l166c push hl + push de + push bc + ld de,$ffff ; max abs sector number +.l1672 ld b,d + ld c,e ; BC=absolute sector number + ld de,bcb_inuse ; DE=last inuse buffer address +.l1677 ex de,hl +.l1678 call l179b ; get next inuse buffer + jr z,l16e8 ; move on if no more in-use buffers + push hl + ld hl,$0005 + add hl,de + ld a,(hl) + cp (ix+$1c) + jr nz,l16a4 ; move on if buffer isn't on same drive + inc hl + ld a,c + sub (hl) + inc hl + ld a,b + sbc a,(hl) + jr c,l16a4 ; move on if larger sector than required + call l15db ; get sector for BCB into memory + dec hl + dec hl + dec hl + dec hl + dec hl + bit 0,(hl) + jr nz,l16a4 ; move on if buffer contains changed data + pop hl + push hl + call l17aa ; else move buffer to free chain + pop hl + jr l1678 ; back for more +.l16a4 pop hl + jr l1677 ; back for more + +; Subroutine to move all in-use BCBs for current drive to the free list + +.l16a7 push hl + push de + push bc + ld de,bcb_inuse ; last in-use BCB address +.l16ad ex de,hl +.l16ae call l179b ; get next BCB + jr z,l16e8 ; exit if no more + push hl + ld hl,$0005 + add hl,de + ld a,(hl) + pop hl + cp (ix+$1c) + jr nz,l16ad ; move on if buffer not for current drive + push hl + call l17aa ; move BCB from in-use chain to free chain + pop hl + jr l16ae ; loop back for more + +; Subroutine to move all in-use BCBs for current file to the free list + +.l16c6 push hl + push de + push bc + ld de,bcb_inuse ; last in-use BCB address +.l16cc ex de,hl +.l16cd call l179b ; get next BCB + jr z,l16e8 ; exit if no more + push hl + ld hl,$0003 + add hl,de + ld a,(hl) + inc hl + ld h,(hl) + ld l,a ; HL=FCB of file for current buffer + or a + sbc hl,bc + pop hl + jr nz,l16cc ; loop back if not current file + push hl + call l17aa ; move BCB from in-use chain to free chain + pop hl + jr l16cd ; loop back +.l16e8 pop bc + pop de + pop hl + ret + +; Subroutine to release in-use cache buffer (address in DE) + +.l16ec push ix + push hl + ld hl,$0002 + add hl,de + bit 0,(hl) ; test flags + scf + jr z,l1702 ; exit if no changed data in buffer + inc hl + inc hl + inc hl + ld a,(hl) ; A=drive letter for buffer + call l184d ; get IX=XDPB + call l1719 ; write data back for all changed buffers +.l1702 pop hl ; on this disk + pop ix + ret + +; Subroutine to clear current cache + +.l1706 push hl + push de + ld de,bcb_inuse ; last in-use BCB address +.l170b ex de,hl + call l179b ; get address of previous in-use BCB + jr z,l1716 ; exit if none + call l16ec ; release cache buffer + jr c,l170b ; loop back if no error +.l1716 pop de + pop hl + ret + +; Subroutine to write data back to disk +; On entry, DE=BCB and IX=XDPB +; The routine writes all changed sectors for the disk back in order +; (smallest absolute logical sector first) + +.l1719 push hl + push de + push bc +.l171c ld de,bcb_inuse + ld bc,$ffff ; max absolute logical sector number + call l173a ; get a smaller one + jr z,l1736 ; exit if none left +.l1727 push de + call l173a ; get a smaller one + jr z,l1730 ; move on if smallest found + pop af + jr l1727 ; loop back +.l1730 pop de + call l1762 ; write buffer to disk + jr c,l171c ; loop back if no error +.l1736 pop bc + pop de + pop hl + ret + +; Subroutine to find BCB on same drive with smaller logical sector number +; than BC. On entry DE contains address of current BCB. +; On exit, Z set if no BCB with smaller logical sector number found. + +.l173a ex de,hl + call l179b ; get address of previous BCB + ret z ; exit if none with Z set + ld hl,$0002 + add hl,de + bit 0,(hl) + jr z,l173a ; loop back if no data to write + inc hl + inc hl + inc hl + ld a,(hl) ; get drive letter for this buffer + cp (ix+$1c) ; same as buffer we're freeing? + jr nz,l173a ; back if not + inc hl + ld a,(hl) + inc hl + ld h,(hl) + ld l,a ; HL=absolute logical sector + or a + sbc hl,bc + add hl,bc + jr z,l175d ; if same, move on + jr nc,l173a ; if larger, loop back +.l175d ld b,h ; BC=smaller absolute logical sector + ld c,l + scf + sbc a,a ; exit with Z reset + ret + +; Subroutine to write buffer (DE=BCB) to disk + +.l1762 push de + call l1785 ; get buffer & sector details + call l1a13 ; write the sector + pop de + ret nc ; exit if error + ld hl,$0002 + add hl,de + res 0,(hl) ; signal "buffer doesnt contain unwritten data" + inc hl + xor a + ld (hl),a + inc hl + ld (hl),a ; set null FCB using this buffer + scf ; success + ret + +; Subroutine to read buffer (DE=BCB) from disk + +.l1778 push hl + push de + push bc + call l1785 ; get buffer & sector details + call l1a0a ; read the sector + pop bc + pop de + pop hl + ret + +; Subroutine to get buffer bank (B), address (HL), logical track (D) and +; logical sector (E), given BCB address in DE + +.l1785 ld hl,$000a + add hl,de + ld b,(hl) ; B=buffer bank + dec hl + ld d,(hl) + dec hl + ld e,(hl) + push de ; stack buffer address + dec hl + ld d,(hl) + dec hl + ld e,(hl) ; DE=absolute logical sector number + call l19df ; get D=logical track, E=logical sector + pop hl ; HL=buffer address + ret + +; Subroutine to get address of last free/in-use BCB (Z set if none) +; Enter at l179b with HL=bcb_inuse for last in-use BCB + +.l1798 ld hl,bcb_free ; address of last free BCB +.l179b ld e,(hl) + inc hl + ld d,(hl) ; DE=BCB address + dec hl + ld a,d + or e ; set Z if null + scf ; success + ret + +; Subroutine to check if current BCB is last in chain +; Exits with Z set if so + +.l17a3 ex de,hl + ld a,(hl) + inc hl + or (hl) + dec hl + ex de,hl + ret + +; Subroutine to move a BCB from the free chain to the in-use chain (l17b2) +; or vice-versa (l17aa) +; If the BCB is already in the chain its being moved to, this has the effect +; of moving it to the top of the chain +; On entry, HL is address of pointer to this BCB and DE holds address of BCB +; On exit, HL is address of start of other chain, and DE is unchanged + +.l17aa call l17c3 ; remove current BCB from chain + ld hl,bcb_free + jr l17b8 ; go to insert into free chain +.l17b2 call l17c3 ; remove current BCB from chain + ld hl,bcb_inuse ; address of in-use chain +.l17b8 ld a,(hl) ; store start of chain into current BCB + ld (de),a + inc hl + inc de + ld a,(hl) + ld (de),a + dec de + ld (hl),d ; store address of current BCB at list address + dec hl + ld (hl),e + ret + +; Subroutine to transfer a word value from DE to HL +; On exit, DE is unchanged, but HL is incremented + +.l17c3 ld a,(de) + ld (hl),a + inc hl + inc de + ld a,(de) + ld (hl),a + dec de + ret + + defs 5 + +; Subroutine to setup extended XDPB info for drives A: and B: + +.l17d0 ld a,'A' + ld (unit0),a ; unit 0 is drive A: + ld hl,l17f6 + ld de,xdpb_a+$1b + ld bc,$0015 + ldir ; copy extended XDPB info for A: + ld hl,xdpb_a + ld (xdpb_ptrs),hl ; set pointer to A:'s XDPB + ld hl,l180b + ld de,xdpb_b+$1b + ld bc,$0015 + ldir ; copy extended XDPB info for B: + ld c,$01 ; B: should be unit 1 or disabled + jp l1943 ; exit via DOS_MAP_B + +; The extended XDPB info for drive A: + +.l17f6 defb $04,'A',$00 ; flags,drive,unit + defb $00,$00,$00,$00 ; last access,filesopen + defw $0000,$0000 ; #free direntries,last used + defw chksm_a,alloc_a ; checksum vector,alloc bitmap + defw l1988 ; login disk + defw l197c ; read sector + defw l1982 ; write sector + +; The extended XDPB info for drive B: + +.l180b defb $04,'B',$01 ; flags,drive,unit + defb $00,$00,$00,$00 ; last access,filesopen + defw $0000,$0000 ; #free direntries,lastused + defw chksm_b,alloc_b ; checksum vector,alloc bitmap + defw l1988 ; login disk + defw l197c ; read sector + defw l1982 ; write sector + +; Subroutine to initialise RAMdisk + +.l1820 push hl + ld hl,l1830 + ld de,xdpb_m+$1b + ld bc,$0015 + ldir ; copy extended XDPB info for M: + pop hl + jp l1a5d ; setup RAMdisk & exit + +; The extended XDPB info for drive M: + +.l1830 defb $08,'M',$ff ; flags,drive,unit + defb $00,$00,$00,$00 ; last access,filesopen + defw $0000,$0000 ; #free direntries,lastused + defw $0000,alloc_m ; no checksum;alloc bitmap + defw l1845 ; login disk + defw l1845 ; read sector + defw l1845 ; write sector + +; Dummy low-level routines for RAMdisk + +.l1845 scf ; success + ret + +; DOS_REF_XDPB + +.l1847 call l04ed ; ensure drive letter uppercase + ld hl,xdpb_ptrs +.l184d sub 'A' + jr c,l186d ; error if P + push hl + add a,a + add a,xdpb_ptrs~$ff + ld l,a + adc a,xdpb_ptrs/$100 + sub l + ld h,a ; HL=xdpb_ptrs+2*drive + ld a,(hl) + inc hl + ld h,(hl) ; get address of XDPB for drive (or 0) + ld l,a + push hl + pop ix ; IX points to XDPB + ld a,h + or l + add a,$ff ; set carry unless no XDPB (success) + pop hl ; restore HL + ld a,$16 ; error 22 - drive not found if error + ret +.l186d ld a,$15 ; error 21 - bad parameter + or a + ret + +; Subroutine to possibly logout drive & low-level login another disk + +.l1871 call l184d ; get XDPB for drive + ret nc ; exit if none + push hl + push de + push bc + call l188f ; possibly logout disk + bit 0,(ix+$1b) + scf + call z,l1887 ; if disk logged out, try to log another in + pop bc + pop de + pop hl + ret + +; Subroutine to low-level login a disk + +.l1887 ld a,(ix+$1a) ; get freeze flag + rla + ret c ; exit if auto-detect not required + jp l19fe ; do a low-level login & exit + +; Subroutine to log out a disk if no files open & not recently accessed + +.l188f bit 0,(ix+$1b) + ret z ; exit if drive not logged in + ld a,(ix+$21) + or a + ret nz ; exit if files open on drive + call l18ab + ret c ; exit if accessed within last 2secs, or fixed + +; Subroutine to low-level log out a disk (IX=XDPB) + +.l189d ld a,(ix+$21) ; # files open on drive + or a + ld a,$24 ; drive in use error if so + ret nz + res 0,(ix+$1b) ; flag drive logged out + jp l16a7 ; move all BCBs for this drive to free list + +; Subroutine to see if drive has been accessed within last 2secs +; If so (or if fixed drive), carry is set + +.l18ab bit 7,(ix+$0c) + scf + ret nz ; exit if drive is fixed + push hl + push de + push bc + ld a,r ; get interrupt status + di + ld a,(FRAMES) ; get HLA=FRAMES + ld hl,(FRAMES+1) + jp po,l18c1 + ei ; re-enable interrupts if necessary +.l18c1 ld b,a ; HLB=current FRAMES + ld a,(ix+$1e) + ld e,(ix+$1f) + ld d,(ix+$20) ; DEA=last access FRAMES + add a,$64 + jr nc,l18d0 + inc de ; DEA=last access FRAMES + 2secs +.l18d0 ld c,a + ld a,b + sub c + sbc hl,de + push af ; save carry (set if accessed within last 2s) + ld hl,FRAMES + ld a,r ; get interrupt status + di + ld a,(hl) + ld (ix+$1e),a ; update last access time to current FRAMES + inc hl + ld a,(hl) + ld (ix+$1f),a + inc hl + ld a,(hl) + ld (ix+$20),a + jp po,l18ee + ei ; re-enable interrupts if necessary +.l18ee pop af + pop bc + pop de + pop hl + ret ; done + +; Subroutine to call l18fd, preserving BC/DE/HL + +.l18f3 push hl + push de + push bc + call l18fd + pop bc + pop de + pop hl + ret + +; Subroutine to check disk can be written to + +.l18fd bit 2,(ix+$1b) + scf + ret z ; exit with success if not floppy drive + call l1918 ; change disk if necessary + call l1e65 ; DD_TEST_UNSUITABLE + ret nc ; exit if error + call l1ee9 ; DD_DRIVE_STATUS + ld c,a + and $20 + ret z ; exit if not present + bit 6,c + ld a,$01 + ret nz ; error if disk write-protected + scf ; success + ret + +; Subroutine to check whether "change disk" routine should be called + +.l1918 push hl + ld c,(ix+$1d) + ld a,c + or a + jr nz,l1935 ; exit if drive not mapped to unit 0 + ld hl,unit0 + ld a,(ix+$1c) + cp (hl) + jr z,l1935 ; exit if unit 0 is currently mapped to drive + ld (hl),a ; change unit 0 mapping to this drive + push ix + push de + push bc + call l1937 ; ask user to change disk + pop bc + pop de + pop ix +.l1935 pop hl + ret + +; Change disk subroutine + +.l1937 push af + ld c,a + call l0338 ; generate change disk message + pop af + push hl + ld hl,(rt_chgdsk) + ex (sp),hl ; stack change disk routine address + ret ; exit to routine + +; DOS_MAP_B + +.l1943 ld a,'B' + call l184d ; get XDPB of drive B: + ccf + call nc,l189d ; logout drive at low-level + ret nc ; exit if error + ld a,c + or a + jr z,l1954 + ld hl,$0000 ; no change-disk routine if B: is unit 1 +.l1954 ld de,(rt_chgdsk) ; get old routine address + ld (rt_chgdsk),hl ; store new routine address + ld hl,$0000 + ld (xdpb_ptrs+2),hl ; clear XDPB pointer for B: + ld ix,xdpb_b ; IX=XDPB for B: + ld (ix+$1d),c ; set unit number + call l1f27 ; DD_INTERFACE + jr nc,l1979 ; move on if no interface + ld a,c + or a + scf + call nz,l1edd ; for unit 1 only, test if present + jr nc,l1979 ; if not, no drive B: + ld (xdpb_ptrs+2),ix ; set pointer to B:'s XDPB +.l1979 scf + ex de,hl + ret ; exit with HL=old change-disk routine + +; Low-level read sector subroutine for drives A: & B: + +.l197c call l1918 ; check if disk change required + jp l1bff ; DD_READ_SECTOR + +; Low-level write sector subroutine for drives A: & B: + +.l1982 call l1918 ; check if disk change required + jp l1c0d ; DD_WRITE_SECTOR + +; Low-level login disk subroutine for drives A: & B: + +.l1988 call l1918 ; check if disk change required + call l1c80 ; DD_LOGIN + ret nc ; exit if error + ld a,(ix+$0f) + xor $02 + ld a,$06 ; "unrecognised disk format" + ret nz ; error if sectorsize <> 512 + rr d + rr e + ld hl,$ffd2 + add hl,de + ccf + ret nc ; error if alloc vector size/2 >$2d + ld e,(ix+$0b) + ld a,(ix+$0c) + and $7f + ld d,a + ld hl,$ffbf + add hl,de + ccf + ret c ; success if chksum size <= $40 + ld (ix+$0b),$40 + ld a,(ix+$0c) + and $80 + or $00 + ld (ix+$0c),a ; else set chksum size to $40 + scf + ret + +; Subroutine to convert logical usable record number (in DE) to +; absolute logical sector number by allowing for reserved tracks + +.l19c0 push hl + push bc + ld c,(ix+$0d) + ld b,(ix+$0e) ; BC=# reserved tracks + ex de,hl + ld e,(ix+$00) + ld d,(ix+$01) ; DE=# records per track + jr l19d3 +.l19d1 add hl,de + dec bc +.l19d3 ld a,b + or c + jr nz,l19d1 + ex de,hl ; DE=origHL+#reserved records + pop bc + pop hl + ld a,$02 + jp l04d9 ; DE=absolute logical sector + +; Subroutine to find logical track (D) & sector (E) from absolute logical +; sector number (DE). IX=XDPB for disk + +.l19df push hl + push bc + ex de,hl + add hl,hl + add hl,hl ; HL=absolute logical record number + ld e,(ix+$00) + ld d,(ix+$01) ; DE=records per track + ld bc,$ffff ; BC=logical track counter + or a +.l19ee inc bc + sbc hl,de + jr nc,l19ee ; loop until found correct track in BC + add hl,de + ex de,hl ; DE=logical record number + ld a,$02 + call l04d9 ; DE=logical sector number on track + ld d,c ; D=logical track, E=logical sector + pop bc + pop hl + ret + +; Subroutine to login a disk at low-level + +.l19fe push hl + ld l,(ix+$2a) + ld h,(ix+$2b) ; HL=routine address to login a disk + ld de,$0000 ; track 0, sector 0 + jr l1a1a ; move on to perform operation + +; Subroutine to read a sector at BHL on logical track D, logical sector E + +.l1a0a push hl + ld l,(ix+$2c) + ld h,(ix+$2d) ; HL=routine address to read a sector + jr l1a1a ; move on to perform operation + +; Subroutine to write a sector at BHL on logical track D, logical sector E + +.l1a13 push hl + ld l,(ix+$2e) + ld h,(ix+$2f) ; HL=routine address to write a sector +.l1a1a ld (rt_temp),hl ; store ready to call + pop hl +.l1a1e push hl + push de + push bc + call l1a2e ; call routine to write a sector + pop bc + pop de + pop hl + ret c ; exit if no error + call l1a34 ; retry message + jr z,l1a1e ; loop back to retry + ret ; exit + +; Subroutine to call address at rt_temp + +.l1a2e push hl ; save HL + ld hl,(rt_temp) ; stack routine address + ex (sp),hl ; stack address & restore HL + ret ; return to call routine + +; Subroutine to do an ALERT message for error A (IX=XDPB) + +.l1a34 push ix + push hl + push de + push bc + call l2164 ; turn off motor + ld c,(ix+$1c) ; C=drive + call l02f7 ; run the ALERT routine + pop bc + pop de + pop hl + pop ix + ret + +; Subroutine to get H=first RAMdisk buffer, L=number of RAMdisk buffers + +.l1a48 ld a,(spec_m+5) + ld h,a ; H=first buffer + ld a,(spec_m+2) ; A=last buffer+1 + sub h + ld l,a ; L=# buffers + ret + +; Subroutine to change the RAMdisk to first buffer=H, number of buffers=L + +.l1a52 ld a,'M' + call l184d ; get XDPB of drive M: + jr nc,l1a5d ; move on if doesn't exist + call l189d ; log drive M: out + ret nc ; exit if error +.l1a5d push hl + ld hl,l1aae + ld de,spec_m + ld bc,$0008 + ldir ; copy initial disk spec for M: + pop de + ld hl,$0000 + ld (xdpb_ptrs+$18),hl ; set M:s XDPB to null + ld a,e + cp $04 + ret c ; exit with M: disabled if <2K allowed + add a,d + ld (spec_m+2),a ; #tracks=size+offset (=last buffer+1) + ld a,d + ld (spec_m+5),a ; #reserved tracks (=first buffer) +.l1a7c ld a,d + push de + call l022b ; get address of buffer + call l0207 ; page in the bank + ld d,h + ld e,l + inc de + ld (hl),$e5 + ld bc,$01ff + ldir ; fill buffer with $e5 filler bytes + call l0207 ; repage previous bank + pop de + inc d ; next buffer + dec e + jr nz,l1a7c ; back for rest + ld ix,xdpb_m + ld hl,spec_m ; disk spec for M: + call l1d30 ; initialise DPB + ld (ix+$0b),$00 + ld (ix+$0c),$80 ; M: is permanent (fixed) + ld (xdpb_ptrs+$18),ix ; set pointer to XDPB for M: + scf ; success + ret + +; Disk spec for RAMdisk + +.l1aae defb $00,$00,$00,$01 + defb $02,$00,$03,$00 + +; Subroutine to get page (A) and address (HL) of RAMdisk logical sector +; for current file + +.l1ab6 push de + call l19df ; get logical track & sector + ld a,e + or a + jr nz,l1ac9 ; RAMdisk has 1sec/trk, so error if not 0 + ld a,d + ld hl,spec_m+2 + cp (hl) + jr nc,l1ac9 ; error if track out of range + call l022b ; convert track=buffer# to address & bank + scf +.l1ac9 pop de + ret c + ld a,$02 ; seek fail error + ret + +; DOS_BOOT + +.l1ace ld a,'A' + call l1871 ; login a disk + call c,l189d ; log back out if no error + ld de,$0000 ; logical sector 0 + call c,l15f4 ; get sector to memory at AHL if no error + ret nc ; exit if error + ld c,a ; save bank + push hl + call l0207 ; page bank in + push af ; save old bank + xor a + ld b,a + ld e,$02 +.l1ae7 add a,(hl) ; form checksum of all bytes in sector + inc hl + djnz l1ae7 + dec e + jr nz,l1ae7 + ld e,a + pop af + call l0207 ; page back original bank + pop hl + ld a,e + xor $03 ; checksum must be 3 + ld a,$23 + ret nz ; if not, exit with "disk not bootable" error + di ; disable interrupts + ld b,$03 + ld de,$fe00 + ld ix,$0200 + call l023d ; copy bootsector to $fe00 in bank 3 + ld a,$03 + call l0207 ; page in bank 3 + ld hl,l1b22 + ld de,$fdfb + ld bc,$0005 + ldir ; copy routine to bank 3 + ld bc,$1ffd + ld a,$07 + ld sp,$fe00 + jp $fdfb ; jump to following routine in bank 3 +.l1b22 out (c),a ; page in RAM 4,7,6,3; keep disk motor on + jp $fe10 ; jump to boot routine + + defs 9 + + +; ********************** LOW-LEVEL ROUTINES ********************* + + +; Subroutine to setup parameter block for format + +.l1b30 ld a,(ix+$19) + and $40 ; A=modulation mode + or $0d ; format command + call l1b9c ; set up parameter block as for read/write etc + ld l,(ix+$0f) + ld h,(ix+$13) + ld (ddl_parms+8),hl ; store sector size & #sectors per track + ld h,e + ld l,(ix+$18) + ld (ddl_parms+$0a),hl ; store format gap length & filler byte + ld a,$06 + ld (ddl_parms+5),a ; store # of command bytes + ret + +; Setup parameter block for sector check + +.l1b50 ld a,(ix+$19) ; flags + or $11 ; scan equal command + call l1b69 ; set up parameter block + ld (hl),$01 ; store data length 1 + ret + +; Setup parameter block for sector read + +.l1b5b ld a,(ix+$19) ; flags + or $06 ; read data command + jr l1b69 ; set up parameter block + +; Setup parameter block for sector read + +.l1b62 ld a,(ix+$19) ; flags + and $c0 ; mask off deleted address mark bit + or $05 ; write data command + +; Subroutine to setup parameter block for low-level read/writes + +.l1b69 call l1b9c ; setup basic parameter block data + ld a,e + add a,(ix+$14) + ld e,a ; E=physical sector number + push de ; save physical track & sector numbers + ld hl,(rt_encode) + ld a,h + or l + call nz,l1ef2 ; call encode routine if required + ld a,e + ld (ddl_parms+$0a),a ; store 1st sector ID + ld l,(ix+$0f) + ld h,e + ld (ddl_parms+$0b),hl ; store sector size & last(=1st) sector ID + ld a,(ix+$17) + ld (ddl_parms+$0d),a ; store gap length + ld h,b + ld l,d + ld (ddl_parms+8),hl ; store track & side numbers + ld a,$09 + ld (ddl_parms+5),a ; store # command bytes + ld hl,ddl_parms+$0e + ld (hl),$ff ; store dummy data length + pop de + ret + +; Subroutine to setup some of the parameter block for sector read/writes +; (except # command bytes & additional command bytes) + +.l1b9c ld (ddl_parms+1),hl ; store buffer address + ld l,a + ld a,b + ld (ddl_parms),a ; store buffer page + call l1bb5 ; C=physical side & unit byte + ld h,c + ld (ddl_parms+6),hl ; store command & unit byte + ld l,(ix+$15) + ld h,(ix+$16) + ld (ddl_parms+3),hl ; store sector size as # bytes to transfer + ret + +; Subroutine to return physical side (B) and track (D) given logical track (D) +; Physical side is also ORed with unit number in C + +.l1bb5 ld a,(ix+$11) + and $7f ; A=sidedness + ld b,$00 ; side 0 + ret z ; exit if single-sided (physical=logical) + dec a + jr nz,l1bc8 ; move on if double-sided: successive sides + ld a,d + rra ; for alternate sides, halve track + ld d,a + ld a,b + rla ; with side=remainder + ld b,a + jr l1bd4 ; move on to OR into unit number +.l1bc8 ld a,d + sub (ix+$12) ; subtract # tracks + jr c,l1bd4 ; if < # tracks, physical=logical so move on + sub (ix+$12) ; on successive side, tracks count back down + cpl + ld d,a + inc b ; and use side 1 +.l1bd4 ld a,b + add a,a + add a,a + or c + ld c,a ; update unit number with side bit + ret + +; DD_ENCODE + +.l1bda or a + jr nz,l1be0 ; move on if routine supplied + ld hl,$0000 ; disable +.l1be0 ld de,(rt_encode) ; get old encode routine address + ld (rt_encode),hl ; store new encode routine address + ex de,hl ; HL=old routine + ret + +; Subroutine to read A bytes from a sector + +.l1be9 push af + call l1b5b ; setup parameter block for sector read + pop af + ld l,a + ld h,$00 + ld (ddl_parms+3),hl ; store #bytes required from sector + ld hl,l1bf9 ; the operation to try + jr l1c4f ; go to try it +.l1bf9 ld hl,ddl_parms ; address of parameter block + jp l20ba ; read bytes and exit + +; DD_READ_SECTOR + +.l1bff call l1b5b ; set up parameter block + ld hl,l1c07 ; the operation to try + jr l1c4f ; go to try it +.l1c07 ld hl,ddl_parms ; address of parameter block + jp l20c3 ; do a DD_L_READ + +; DD_WRITE_SECTOR + +.l1c0d call l1e65 ; test if suitable XDPB + ret nc ; exit if error + call l1b62 ; set up parameter block + jr l1c2b ; move on + +; DD_CHECK_SECTOR + +.l1c16 call l1b50 ; set up parameter block + call l1c2b ; scan the data + ret nc ; exit if error + ld a,(fdc_res+3) ; get ST2 + cp $08 ; set Z if scan was equal + scf ; success + ret + +; DD_FORMAT + +.l1c24 call l1e65 ; test if suitable XDPB + ret nc ; exit if not + call l1b30 ; setup parameter block +.l1c2b ld hl,l1c30 ; the operation to try + jr l1c4f ; go to try it +.l1c30 ld hl,ddl_parms ; address of parameter block + jp l20cc ; do a DD_L_WRITE + +; DD_READ_ID + +.l1c36 call l1c41 ; read a sector ID + ld hl,fdc_res ; get results buffer address + ret nc ; exit if error + ld a,(fdc_res+6) ; get sector number + ret + +; Subroutine to read a sector ID + +.l1c41 call l1bb5 ; convert logical track to physical + ld hl,l1c49 ; the operation to try + jr l1c4f ; go to do it +.l1c49 ld a,(ix+$19) ; get modulation mode + jp l2103 ; read sector ID & exit + +; Routine to turn on motor, try an operation in HL on track D multiple +; times & then start the motor off timeout + +.l1c4f call l212b ; turn on motor + call l1e80 ; try the operation multiple times + jp l2150 ; start motor off timeout & exit + + +; Tables of specifications for disk types 0-3 +; Format as on p215 of +3 Manual + +; Type 0 - Spectrum +3 format + +.l1c58 defb $00,$00,$28,$09 + defb $02,$01,$03,$02 + defb $2a,$52 + +; Type 1 - CPC system format + + defb $01,$00,$28,$09 + defb $02,$02,$03,$02 + defb $2a,$52 + +; Type 2 - CPC data format + + defb $02,$00,$28,$09 + defb $02,$00,$03,$02 + defb $2a,$52 + +; Type 3 - PCW format + + defb $03,$81,$50,$09 + defb $02,$01,$04,$04 + defb $2a,$52 + +; DD_LOGIN + +.l1c80 xor a + call l1cdb ; get IX=XDPB of +3 format disk + ld d,$00 + push bc + call c,l1c36 ; if no error, read a sector ID on track 0 + pop bc + ret nc ; exit if error + and $c0 + ld e,$01 ; CPC system format if IDs ~$40 + cp $40 + jr z,l1c99 + inc e ; CPC data format if IDs ~$c0 + cp $c0 + jr nz,l1c9f ; otherwise, +3 or PCW format +.l1c99 ld a,e + call l1cdb ; get IX=XDPB of CPC format disks + jr l1cd3 ; move on +.l1c9f push bc + ld hl,ddl_parms+$0f ; buffer for disk spec + ld de,$0000 ; track 0, sector 0 + ld b,$07 ; use page 7 + ld a,$0a ; 10 bytes required (disk spec) + push hl + call l1be9 ; read disk spec from boot sector + pop hl + pop bc + jr c,l1cba ; move on if no error + cp $08 + scf + ccf + ret nz ; exit with errors except disk changed + ld a,$06 ; substitute "unrecognised disk format" + ret +.l1cba push bc + ld d,h + ld e,l + ld c,(hl) + ld b,$0a ; check 10 bytes of specifier +.l1cc0 ld a,(de) + inc de + cp c + jr nz,l1cca ; if any differ, use this as disk spec + djnz l1cc0 + ld hl,l1c58 ; use +3 format if all bytes the same +.l1cca pop bc + ld a,(hl) + cp $04 ; test format type + ld a,$06 ; signal unrecognised disk format + call c,l1cee ; initialise XDPB if no error +.l1cd3 push hl + push bc + call c,l1dee ; if no errors, update drive equipment & + pop bc ; check suitable for disk + pop hl + ret + +; DD_SEL_FORMAT + +.l1cdb ld e,a + cp $04 ; test format type + ld a,$06 + ret nc ; error 6 if not type 0-3 + ld a,e + add a,a + ld e,a ; E=2*type + add a,a + add a,a + add a,e ; A=10*type + adc a,l1c58~$ff + ld l,a + adc a,l1c58/$100 + sub l + ld h,a ; HL=address of info for type + +; DD_L_XDPB +; Enter at this point to initialise an XDPB (HL=disk spec) + +.l1cee push hl ; save info address + push bc + ld a,(hl) ; get format type + ld b,$41 ; first sector number for CPC system + dec a + jr z,l1cfd + ld b,$c1 ; first sector number for CPC data + dec a + jr z,l1cfd + ld b,$01 ; first sector number for +3/PCW +.l1cfd ld (ix+$14),b ; set first sector number + inc hl + ld a,(hl) + ld (ix+$11),a ; set sidedness & single/double track + inc hl + ld a,(hl) + ld (ix+$12),a ; set # tracks + inc hl + ld a,(hl) + ld (ix+$13),a ; set # sectors + inc hl + ld b,(hl) ; B=sector size (0..2) + inc hl + inc hl + inc hl + inc hl + ld a,(hl) + ld (ix+$17),a ; set gap length (r/w) + inc hl + ld a,(hl) + ld (ix+$18),a ; set gap length (format) + ld hl,$0080 + call l1efd ; HL=128x2^B + ld (ix+$15),l + ld (ix+$16),h ; set sector size (bytes) + ld (ix+$19),$60 ; set multitrack,modulation flags + pop bc ; restore registers + pop hl + +; DD_L_DPB +; Enter at this point to initialise a DPB (HL=disk spec) +; On exit, DE=ALS, HL=HASH and A=format type + +.l1d30 push bc ; save registers + push hl + ex de,hl + ld hl,$0004 + add hl,de + ld a,(hl) + ld (ix+$0f),a ; set PSH=log2(sectorsize/128) + push af + call l1ef3 + ld (ix+$10),a ; set PHM=(sectorsize/128)-1 + dec hl + ld l,(hl) + ld h,$00 + pop bc + call l1efd ; HL=(#sectors)x(records per sector) + ld (ix+$00),l + ld (ix+$01),h ; set SPT (records per track) + ld hl,$0006 + add hl,de + ld a,(hl) + ld (ix+$02),a ; set BSH=log2(blocksize/128) + ld c,a ; save it + push hl + call l1ef3 + ld (ix+$03),a ; set BLM=(blocksize/128)-1 + dec hl + ld e,(hl) + ld (ix+$0d),e ; set OFF (#reserved tracks) + ld (ix+$0e),$00 + dec hl + dec hl + ld b,(hl) ; B=#sectors + dec hl + ld d,(hl) ; D=#tracks + dec hl + ld a,(hl) ; A=sidedness + ld l,d + ld h,$00 ; HL=tracks per side + ld d,h ; DE=reserved tracks + and $7f + jr z,l1d79 + add hl,hl ; HL=total tracks on disk +.l1d79 sbc hl,de + ex de,hl ; DE=total tracks-reserved tracks + ld hl,$0000 +.l1d7f add hl,de + djnz l1d7f ; HL=total non-reserved sectors + ld a,c + sub (ix+$0f) + ld b,a + call l1f04 ; HL=total non-reserved blocks + dec hl + ld (ix+$05),l + ld (ix+$06),h ; set DSM (last block number) + ld b,$03 + ld a,h + or a + jr z,l1d98 + inc b ; B=3, or 4 if >256 blocks +.l1d98 ld a,c ; A=log2(blocksize/128) + sub b ; A=log2(bs/2^7)-log2(2^B)=log2(bs/2^(B+7)) + call l1ef3 ; A=(bs/2^(B+7))-1 + ld (ix+$04),a ; set EXM (extent mask) + pop de + push hl ; save DSM + ld b,$02 + call l1f04 + inc hl + inc hl ; HL=Floor(DSM/4)+2=ALS (allocation vector size) + ex (sp),hl ; save ALS and retrieve DSM + inc de + ld a,(de) ; A=#dir blocks + or a + jr nz,l1db7 ; move on unless need to calculate it + add hl,hl + ld a,h + inc a + cp $02 + jr nc,l1db7 + inc a +.l1db7 ld b,a + ld hl,$0000 +.l1dbb scf + rr h + rr l + djnz l1dbb ; HL=2^16-2^(16-#dir blocks) + ld (ix+$09),h ; set AL0 directory bitmap + ld (ix+$0a),l ; set AL1 directory bitmap + ld h,$00 + ld l,a ; HL=#dirblocks + ld b,c ; B=log2(blocksize/128) + inc b + inc b ; B=log2(bs/128)+log2(4)=log2(bs/32) + call l1efd + push hl + dec hl ; HL=(#dirblocks*bs/32)-1=DRM + ld (ix+$07),l + ld (ix+$08),h ; set DRM (last directory entry number) + ld b,$02 + call l1f04 + inc hl ; HL=((DRM+1)/4)+1=CKS + ld (ix+$0b),l + ld (ix+$0c),h ; set CKS (checksum vector size) + pop hl + add hl,hl + add hl,hl ; HL=4*(DRM+1) (hash table size) + pop de ; DE=ALS (2bit allocation vector size) + pop bc + ld a,(bc) ; A=format type + scf ; signal success + pop bc ; restore BC + ret + +; Subroutine to update drive equipment info & check is suitable for disk + +.l1dee ld b,a + push de + push bc + call l1e10 ; test drive equipment, updating if necessary + pop bc + pop de + ret nc ; exit if error + ld a,(ix+$11) + and $03 + jr z,l1e02 ; move on if disk is single-sided + bit 1,(hl) + jr z,l1e0c ; error 9 if drive single-sided +.l1e02 ld a,b + scf + bit 7,(ix+$11) + ret z ; exit with success if disk not double-track + bit 3,(hl) + ret nz ; or if drive double-track +.l1e0c ld a,$09 ; error 9 - unsuitable media for drive + or a + ret + +; Subroutine to test drive equipment for sidedness and double/single track + +.l1e10 call l1f6a ; get equipment address + ld a,(hl) + and $0c ; check if single/double track drive + jr z,l1e24 ; move on if not known + ld a,(hl) + and $03 ; check if single/double sided drive + scf + ret nz ; exit with success if known + ld a,(ix+$11) + and $03 ; check # sides for disk + scf + ret z ; exit with success if disk single-sided +.l1e24 ld a,(ix+$11) + and $03 ; check # sides for disk + ld d,$02 ; track 2 + jr z,l1e39 ; move on if single-sided + dec a + ld d,$05 ; track 2 on side 1 if alternating + jr z,l1e39 ; move on if alternating sides + ld a,(ix+$12) ; + add a,a + sub $03 + ld d,a ; track 2 on side 1 if successive sides +.l1e39 push hl + call l1c36 ; read a sector ID + pop hl + ret nc ; exit if error + ld de,(fdc_res+4) ; get E=track, D=head detected + ld a,(ix+$11) + and $03 + jr z,l1e53 ; move on if single-sided disk + dec d ; D should now be zero + jr z,l1e51 + set 0,(hl) ; if not, set single-sided drive + jr l1e53 +.l1e51 set 1,(hl) ; set double-sided drive +.l1e53 ld a,(ix+$11) + dec e + dec e + jr z,l1e5b ; move on if track 2 was detected + cpl ; else drive has inverse d-track bit of disk +.l1e5b rla ; get double-track bit + jr nc,l1e61 + set 3,(hl) ; set double-track + ret +.l1e61 set 2,(hl) ; set single-track + scf + ret + +; DD_TEST_UNSUITABLE + +.l1e65 push hl + call l1f6a ; get equipment address + bit 3,(hl) ; test if double-track + pop hl + scf + ret z ; exit with okay if single-track + ld a,(ix+$11) + rla ; set carry (okay) if disk double-track + ld a,$09 ; error 9 - unsuitable media + ret + +; DD_EQUIPMENT + +.l1e75 call l1f6a ; get equipment address + ld a,(hl) ; get flags byte + and $0f ; mask side & track info + ret + +; DD_SET_RETRY + +.l1e7c ld (retry_cnt),a ; set it + ret + +; Subroutine to try operation in HL on track D multiple times + +.l1e80 ld a,(retry_cnt) + ld b,a ; B=retry count +.l1e84 push bc + call l1eb0 ; try the operation + pop bc + ret z ; exit for success or unrecoverable error + cp $04 + jr nz,l1eac ; move on if not "no data" error + push hl + push de + push bc + ld a,(ix+$19) ; flags (for modulation mode) + call l2103 ; read a sector ID + call l204a ; process results + pop bc + pop de + pop hl + jr nz,l1eac ; move on if recoverable error + ret nc ; exit if non-recoverable error + ld a,(fdc_res+6) ; get sector ID found + xor (ix+$14) + and $c0 ; compare to XDPB sector ID (detect if + ld a,$08 ; different CPC format than expected) + ret nz ; error 8 - disk changed + rra +.l1eac djnz l1e84 ; back for more tries + or a ; exit with error 4 - no data + ret + +; Subroutine to position head on track D and call subroutine in HL, +; processing results buffer afterwards +; B contains try number + +.l1eb0 ld a,b + and $07 + jr z,l1ec2 ; reseek to high track every 8 tries + and $03 + jr nz,l1ecc ; recalibrate every other 8 tries + push hl + call l1f6a ; get equipment address + res 6,(hl) ; signal head not positioned + pop hl + jr l1ecc +.l1ec2 push de + ld d,(ix+$12) + dec d ; D=high track number + call l1f76 ; seek to it + pop de + ret nc ; exit if error +.l1ecc call l1f76 ; seek to track D + ret nc ; exit if error + push hl + push de + push bc + call l1ef2 ; call routine address in HL + pop bc + pop de + call l204a ; process results buffer + pop hl + ret + +; DD_ASK_1 + +.l1edd push bc + ld c,$01 + call l1ee9 ; get status of unit 1 + pop bc + and $60 + ret z ; exit if not ready or write-protected + scf ; signal "unit 1 present" + ret + +; DD_DRIVE_STATUS + +.l1ee9 call l212b ; turn on motor + call l2087 ; get status in A + jp l2150 ; start motor off timeout & exit + +; Call is made here to call the address in HL + +.l1ef2 jp (hl) + +; Subroutine to calculate N/128 - 1 +; given A=log2 (N/128) + +.l1ef3 or a + ret z ; exit if zero + ld b,a + ld a,$01 +.l1ef8 add a,a ; calculate N/128 + djnz l1ef8 + dec a ; decrement + ret + +; Subroutine to multiply HL by 2^B + +.l1efd ld a,b + or a + ret z ; exit when B=0 +.l1f00 add hl,hl ; double HL + djnz l1f00 ; loop back + ret + +; Subroutine to divide HL by 2^B + +.l1f04 ld a,b + or a + ret z ; exit when B=0 +.l1f07 srl h + rr l ; halve HL + djnz l1f07 ; loop back + ret + + defs 18 + +; Default setup data, used by DD_INIT + +.l1f20 defb $0a ; motor on time + defb $32 ; motor off time + defb $af ; write off time + defb $1e ; head settle time + defb $0c ; step rate + defb $0f ; head unload time + defb $03 ; head load time x2+1 + +; DD_INTERFACE + +.l1f27 push bc + ld bc,$2ffd + in a,(c) ; read FD status register ($ff if no i/f) + add a,$01 + ccf ; carry not set if no i/f + pop bc + ret + +; DD_INIT + +.l1f32 ld hl,equipment ; zero equipment data, timing consts and + ld b,$10 ; FDC results buffer +.l1f37 ld (hl),$00 + inc hl + djnz l1f37 + ld a,$0f + ld (retry_cnt),a ; set retry count to $0f + call l2164 ; turn off motor + ld hl,l1f20 ; enter DD_SETUP with default setup data + +; DD_SETUP + +.l1f47 ld de,tm_mtron + ld bc,$0005 + ldir ; copy first 5 bytes of setup info + ld a,(tm_step) + dec a + rlca + rlca + rlca + cpl + and $f0 + or (hl) + inc hl + ld h,(hl) + ld l,a ; HL=setup info for FDC + ld a,$03 ; send setup info & exit + call l2114 + ld a,l + call l2114 + ld a,h + jp l2114 + +; Subroutine to convert unit/head byte (A) to equipment address (in HL) + +.l1f6a ld a,c + and $03 + add a,a ; A=2*unit + add a,equipment~$ff + ld l,a + adc a,equipment/$100 + sub l + ld h,a ; HL=equipment+2*unit + ret + +; DD_L_SEEK + +.l1f76 push hl + call l1f6a ; get equipment address + call l1f7f ; do the seek + pop hl + ret + +; Subroutine to seek to track D on equipment HL (XDPB=IX) + +.l1f7f ld a,(retry_cnt) + ld b,a ; B=# tries +.l1f83 bit 6,(hl) + jr nz,l1f92 ; move on if head positioned + inc hl + ld (hl),$00 ; set track zero + dec hl + call l1fb7 ; seek to track zero + jr nc,l1fa8 ; move on if error + set 6,(hl) ; signal head positioned +.l1f92 ld a,d + inc hl + cp (hl) ; compare required track to current + dec hl + scf + ret z ; exit with carry set if same + or a + jr nz,l1fa0 + call l1fb7 ; recalibrate if track 0 required + jr l1fa3 +.l1fa0 call l1fdb ; else seek to track D +.l1fa3 jr nc,l1fad ; move on if error + inc hl + ld (hl),d ; store new track number + ret ; done +.l1fa8 push de + call nz,l1fd7 ; attempt to seek to highest track + pop de +.l1fad res 6,(hl) ; signal head not positioned + ret z ; exit if FDC wasn't ready + call l206f ; wait until ready + djnz l1f83 ; loop back for retries + cp a ; exit with carry reset & error 2 - seek fail + ret + +; Subroutine to seek to track zero + +.l1fb7 call l1fbb ; try once + ret z ; and again if unsuccessful +.l1fbb push bc + ld b,(ix+$12) + dec b ; B=max track + bit 7,(ix+$11) + jr nz,l1fcd ; move on unless double-track disk + bit 3,(hl) + jr z,l1fcd ; move on if single-track drive + ld a,b + add a,a + ld b,a ; double max track # +.l1fcd ld a,$07 + call l2114 ; send recalibrate command + ld a,c + and $03 + jr l1ffe ; do the seek + +; Subroutine to seek to track D (enter at l1fd7 for highest track) + +.l1fd7 ld d,(ix+$12) + dec d ; D=high track +.l1fdb push bc + ld a,d + inc hl + sub (hl) ; A=#tracks head must move + dec hl + jr nc,l1fe4 + cpl + inc a ; ensure A is positive +.l1fe4 ld b,a + ld a,$0f + call l2114 ; send seek command + ld a,c + call l2114 ; send unit number + ld a,d ; A=track number + bit 7,(ix+$11) + jr nz,l1ffe ; move on if double-track disk + bit 3,(hl) + jr z,l1ffe ; move on unless double-track drive + ld a,b + add a,a + ld b,a ; double tracks to move + ld a,d + add a,a ; double track number required + +; Subroutine to complete a seek command. Carry set on exit if successful + +.l1ffe push hl + call l2114 ; send unit number +.l2002 ld a,(tm_step) + call l201c ; delay for step rate time + djnz l2002 ; for max tracks + ld a,(tm_hdset) + call l201c ; delay for head settle time + ld hl,fdc_res + call l2080 ; sense interrupt status + call l2025 ; wait until seek successfully completed + pop hl + pop bc + ret + +; Subroutine to delay for approx A milliseconds + +.l201c ld l,$dc +.l201e dec l + jr nz,l201e + dec a + jr nz,l201c + ret + +; Subroutine to wait for end of seek command +; On exit, carry set if seek completed successfully +; If unsuccessful, Z set if FDC was not ready + +.l2025 ld a,c ; unit number + or $20 ; seek end bit + inc hl + xor (hl) ; mask against ST0 + and $fb ; ignore side information + scf + ret z ; return with carry set if seek ended + ld a,(hl) + and $c0 ; test error code + xor $80 + jr z,l2046 ; move on if invalid command + ld a,(hl) + xor c + and $03 + jr z,l2040 ; move on if no error + call l2080 ; else sense interrupt status again + jr l2025 ; and loop back +.l2040 ld a,(hl) + and $08 + xor $08 ; test not ready bit + ret z ; exit with carry reset if so +.l2046 ld a,$02 + or a + ret ; else exit with Z reset + +; Subroutine to process results buffer, exiting with error or carry set +; Z is set for success or write-protect/not ready error + +.l204a inc hl + ld a,(hl) ; get ST0 + xor c + scf + ret z ; exit with success if no error bits + and $08 + xor $08 + ret z ; exit with error 0 if not ready + inc hl + ld a,(hl) ; get ST1 + cp $80 ; bit 7 always set on +3 + scf + ret z ; exit with success if no error + xor $02 + ld a,$01 ; error 1 if write-protected + ret z + ld a,$03 ; error 3 if CRC data error + bit 5,(hl) + ret nz + inc a ; error 4 if no data + bit 2,(hl) + ret nz + inc a ; error 5 if missing address mark + bit 0,(hl) + ret nz + inc a + inc a ; else error 7 (unknown error) + ret + +; Subroutine to wait until FDC ready for new command + +.l206f push hl + push af + ld hl,fdc_res +.l2074 call l2080 ; sense interrupt status + and $c0 + cp $80 + jr nz,l2074 ; wait for bit 7=1, bit 6=0 + pop af + pop hl + ret + +; Subroutine to perform "sense interrupt status" command + +.l2080 ld a,$08 ; Sense interrupt status command + call l2114 ; send it + jr l2093 ; get results & exit + +; Subroutine to perform "get unit status" command + +.l2087 ld a,$04 ; get unit status command + call l2114 ; send it + ld a,c ; C=unit number + call l2114 ; send it & follow in to next routine + +; Subroutine to get results string from FDC + +.l2090 ld hl,fdc_res ; HL=buffer for results +.l2093 push de + push bc + ld bc,$2ffd + ld d,$00 ; total bytes read + inc hl ; step past length byte + push hl +.l209c in a,(c) ; get FD status register + add a,a + jr nc,l209c ; loop back until ready + jp p,l20b3 ; move on if no more bytes (bit 6 reset) + ld b,$3f + in a,(c) ; get byte + ld b,$2f + ld (hl),a ; store in buffer + inc hl + inc d ; increment count + ex (sp),hl ; short delay + ex (sp),hl + ex (sp),hl + ex (sp),hl + jr l209c ; back for more +.l20b3 pop hl + ld a,(hl) ; A=first byte of results + dec hl + ld (hl),d ; store results length + pop bc + pop de + ret ; exit with HL=address of results + +; Subroutine to read E bytes only + +.l20ba call l20de ; output command except last byte + call l2185 ; read the bytes + jp l2090 ; get results string & exit + + +; DD_L_READ + +.l20c3 call l20de ; output command except last byte + call l21b7 ; read the bytes + jp l2090 ; get results string & exit + +; DD_L_WRITE + +.l20cc call l20de ; output command except last byte + call l21d4 ; write the bytes + ld a,(tm_wroff) ; get write off time value +.l20d5 dec a + inc bc + inc bc + inc bc + jr nz,l20d5 ; write off delay + jp l2090 ; get results string & exit + + +; Subroutine to get page (D) and address (HL) of buffer for low-level +; command, and output all command bytes except last (in A) +; On entry, HL=address of parameter block + +.l20de call l206f ; wait until ready for new command + ld a,(BANKM) ; get old BANKM + and $f8 + or (hl) ; set page required + ld b,a + inc hl + ld e,(hl) + inc hl + ld d,(hl) ; DE=buffer address + inc hl + ld c,(hl) ; C=# bytes to transfer (low) + push bc + inc hl + inc hl + ld b,(hl) ; B=# command bytes + inc hl + dec b +.l20f4 ld a,(hl) ; get next command byte + inc hl + call l2114 ; send it + djnz l20f4 ; back for all except last + ld a,(hl) + ex de,hl + pop de ; D=page required, E=#bytes to transfer (low) + ld bc,$7ffd + di ; turn off interrupts + ret + +; Subroutine to read a sector ID + +.l2103 call l206f ; wait for FDC ready + and $40 ; get modulation mode + or $0a ; read sector ID command + call l2114 ; send command + ld a,c + call l2114 ; send unit + jp l2090 ; get results + +; Subroutine to wait for FD ready & ouput A to data register if +; controller wants input + +.l2114 push de + push bc + ld d,a + ld bc,$2ffd +.l211a in a,(c) ; get FD status register + add a,a + jr nc,l211a ; loop back if not ready + add a,a + jr c,l2128 ; exit if controller doesn't want input + ld b,$3f + out (c),d ; output byte to data register + ex (sp),hl ; short delay + ex (sp),hl +.l2128 pop bc + pop de + ret + + +; DD_L_ON_MOTOR + +.l212b push bc + push af + xor a + ld (timeout),a ; zero timeout value + ld a,(BANK678) + bit 3,a + jr nz,l214d ; exit if motor already on + or $08 + call l2173 ; set motor on + ld a,(tm_mtron) ; get motor spinup timing value +.l2140 push af + ld bc,$3548 +.l2144 dec bc ; delay loop + ld a,b + or c + jr nz,l2144 + pop af + dec a + jr nz,l2140 +.l214d pop af ; restore registers and exit + pop bc + ret + +; DD_L_T_OFF_MOTOR + +.l2150 push af + xor a + ld (timeout),a ; zero timeout value + ld a,(BANK678) + and $08 + jr z,l2162 ; go to exit if motor already off + ld a,(tm_mtroff) + ld (timeout),a ; else set timeout value +.l2162 pop af + ret + +; DD_L_OFF_MOTOR + +.l2164 push af + xor a + ld (timeout),a ; zero timeout value + ld a,(BANK678) + and $f7 ; get current BANK678 value & mask motor bit + call l2173 ; change the value to switch off motor + pop af + ret + +; Subroutine to set the BANK678 value to A + +.l2173 push bc + ld b,a + ld a,r ; get interrupt status + ld a,b + ld bc,$1ffd + di + ld (BANK678),a + out (c),a ; set the value + pop bc + ret po + ei ; re-enable interrupts if necessary + ret + +; Subroutine to output last command byte to FDC and read E bytes to buffer + +.l2185 call l2114 ; send command + out (c),d ; page in required bank + ld bc,$2ffd + ld d,$20 + jr l219b +.l2191 ld b,$3f + ini ; read a byte + ld b,$2f + dec e + jp z,l21ac ; move on if got all bytes required +.l219b in a,(c) + jp p,l219b ; wait until FDC ready + and d + jp nz,l2191 ; loop back if more bytes on offer + jr l21ef ; page bank 7 back and exit +.l21a6 ld b,$3f + in a,(c) ; discard a byte + ld b,$2f +.l21ac in a,(c) + jp p,l21ac ; wait until FDC ready + and d + jp nz,l21a6 ; loop back if more bytes on offer + jr l21ef ; page bank 7 back and exit + +; Subroutine to output last byte of command to FDC and read bytes to buffer + +.l21b7 call l2114 ; send command + out (c),d ; page in required bank + ld bc,$2ffd + ld d,$20 + jr l21c9 +.l21c3 ld b,$3f + ini ; read a byte + ld b,$2f +.l21c9 in a,(c) + jp p,l21c9 ; wait until FDC ready + and d + jp nz,l21c3 ; loop back if more bytes to read + jr l21ef ; go to repage bank 7 & exit + +; Subroutine to output last byte of command to FDC and write bytes from buffer + +.l21d4 call l2114 ; send command + out (c),d ; page in required bank + ld bc,$2ffd + ld d,$20 + jr l21e6 +.l21e0 ld b,$40 + outi ; write a byte + ld b,$2f +.l21e6 in a,(c) + jp p,l21e6 ; wait until FDC ready + and d + jp nz,l21e0 ; loop back if more bytes to write +.l21ef ld a,(BANKM) + ld bc,$7ffd + out (c),a ; page bank 7 back in + ei + ret + + defs 165 + + +; ******************** KEYBOARD SCANNING ROUTINES ***************** + +; These are copies of the keytables from ROM 3 + +; The L-mode keytable with CAPS-SHIFT + +.l229e defm "BHY65TGV" + defm "NJU74RFC" + defm "MKI83EDX" + defm $0e&"LO92WSZ" + defm " "&$0d&"P01QA" + +; The extended-mode keytable (unshifted letters) + +.l22c5 defb $e3,$c4,$e0,$e4 + defb $b4,$bc,$bd,$bb + defb $af,$b0,$b1,$c0 + defb $a7,$a6,$be,$ad + defb $b2,$ba,$e5,$a5 + defb $c2,$e1,$b3,$b9 + defb $c1,$b8 + +; The extended mode keytable (shifted letters) + +.l22df defb $7e,$dc,$da,$5c + defb $b7,$7b,$7d,$d8 + defb $bf,$ae,$aa,$ab + defb $dd,$de,$df,$7f + defb $b5,$d6,$7c,$d5 + defb $5d,$db,$b6,$d9 + defb $5b,$d7 + +; The control code keytable (CAPS-SHIFTed digits) + +.l22f9 defb $0c,$07,$06,$04 + defb $05,$08,$0a,$0b + defb $09,$0f + +; The symbol code keytable (letters with symbol shift) + +.l2303 defb $e2,$2a,$3f,$cd + defb $c8,$cc,$cb,$5e + defb $ac,$2d,$2b,$3d + defb $2e,$2c,$3b,$22 + defb $c7,$3c,$c3,$3e + defb $c5,$2f,$c9,$60 + defb $c6,$3a + +; The extended mode keytable (SYM-SHIFTed digits) + +.l231d defb $d0,$ce,$a8,$ca + defb $d3,$d4,$d1,$d2 + defb $a9,$cf + + +; This is a copy of the "keyboard scanning" subroutine from +; $028e in ROM 3 + +.l2327 ld l,$2f + ld de,$ffff + ld bc,$fefe +.l232f in a,(c) + cpl + and $1f + jr z,l2344 + ld h,a + ld a,l +.l2338 inc d + ret nz +.l233a sub $08 + srl h + jr nc,l233a + ld d,e + ld e,a + jr nz,l2338 +.l2344 dec l + rlc b + jr c,l232f + ld a,d + inc a + ret z + cp $28 + ret z + cp $19 + ret z + ld a,e + ld e,d + ld d,a + cp $18 + ret + +; This is a copy of the "keyboard" subroutines from $02bf in ROM 3 + +.l2358 call l2327 + ret nz + ld hl,KSTATE +.l235f bit 7,(hl) + jr nz,l236a + inc hl + dec (hl) + dec hl + jr nz,l236a + ld (hl),$ff +.l236a ld a,l + ld hl,KSTATE+$04 + cp l + jr nz,l235f + call l23b7 + ret nc + ld hl,KSTATE + cp (hl) + jr z,l23a9 + ex de,hl + ld hl,KSTATE+$04 + cp (hl) + jr z,l23a9 + bit 7,(hl) + jr nz,l238a + ex de,hl + bit 7,(hl) + ret z +.l238a ld e,a + ld (hl),a + inc hl + ld (hl),$05 + inc hl + ld a,(REPDEL) + ld (hl),a + inc hl + ld c,(iy+$07) + ld d,(iy+$01) + push hl + call $23cc + pop hl + ld (hl),a +.l23a1 ld (LAST_K),a + set 5,(iy+$01) + ret +.l23a9 inc hl + ld (hl),$05 + inc hl + dec (hl) + ret nz + ld a,(REPPER) + ld (hl),a + inc hl + ld a,(hl) + jr l23a1 + +; This is a copy of the "K-Test" subroutine from $031e in ROM 3 + +.l23b7 ld b,d + ld d,$00 + ld a,e + cp $27 + ret nc + cp $18 + jr nz,l23c5 + bit 7,b + ret nz +.l23c5 ld hl,l229e ; the main keytable + add hl,de + ld a,(hl) + scf + ret + +; This is a copy of the "keyboard decoding" subroutine from $0333 in +; ROM 3 + +.l23cc ld a,e + cp $3a + jr c,l2400 + dec c + jp m,l23e8 + jr z,l23da + add a,$4f + ret +.l23da ld hl,l22c5-'A' + inc b + jr z,l23e3 + ld hl,l22df-'A' +.l23e3 ld d,$00 + add hl,de + ld a,(hl) + ret +.l23e8 ld hl,l2303-'A' + bit 0,b + jr z,l23e3 + bit 3,d + jr z,l23fd + bit 3,(iy+$30) + ret nz + inc b + ret nz + add a,$20 + ret +.l23fd add a,$a5 + ret +.l2400 cp $30 + ret c + dec c + jp m,l2436 + jr nz,l2422 + ld hl,l231d-'0' + bit 5,b + jr z,l23e3 + cp $38 + jr nc,l241b + sub $20 + inc b + ret z + add a,$08 + ret +.l241b sub $36 + inc b + ret z + add a,$fe + ret +.l2422 ld hl,l22f9-'0' + cp $39 + jr z,l23e3 + cp $30 + jr z,l23e3 + and $07 + add a,$80 + inc b + ret z + xor $0f + ret +.l2436 inc b + ret z + bit 5,b + ld hl,l22f9-'0' + jr nz,l23e3 + sub $10 + cp $22 + jr z,l244b + cp $20 + ret nz + ld a,$5f + ret +.l244b ld a,$40 + ret + +; These routines display error messages (in RAM at HL) using ROM 0. +; The first requires a response (DE=list of possible keys on entry) + +.l244e xor a ; we need a response + call l3e00 + defw $3ff0 ; display message + ret + +.l2455 or a ; no response required + call l3e00 + defw $3ff0 ; display message + ret + +; ******************* SELF-TEST PROGRAM SECTION START ******************* +; This routine is copied to RAM at $6000 and executed there with ROM 1 +; paged in. A total of $C00 bytes is copied from here ($245c-$305b). + + defc stst=$6000-ASMPC + +.l6000 call l683a+stst ; page ROM 1/bank 0 + rst $28 + defw $0d6b ; cls + ld a,$02 + rst $28 + defw $1601 ; open stream 2 + call l6038+stst ; do the tests + push af + ld hl,l6544+stst ; pass message + jr c,l601c ; move on if no error + cp $ff ; was error "no interface"? + call nz,l6588+stst ; if not, display error + ld hl,l6561+stst ; fail message +.l601c call l65a4+stst ; display pass/fail message + ld a,$0d + call l6391+stst ; display repeat/quit message + pop hl +.l6025 push hl + call l67f9+stst ; get key + pop hl + and $df ; capitalise + cp 'R' + jp z,l6000+stst ; re-start + cp 'Q' + jr nz,l6025 ; don't exit unless "Q" + push hl + pop af + ret ; finished! + +; Subroutine to perform the tests + +.l6038 ld a,$01 + call l6391+stst ; display signon message + call l6817+stst ; page ROM 2/bank 7 + call DOS_INITIALISE + call l683a+stst ; page ROM 1/bank 0 + ret nc ; exit if error + call l6817+stst ; page ROM 2/bank 7 + call DD_INTERFACE + call l683a+stst ; page ROM 1/bank 0 + jr c,l605b ; move on if interface found + ld a,$03 + call l6391+stst ; "no interface" message + xor a + ld a,$ff + ret ; exit +.l605b xor a + ld ($6aa4),a ; set unit 0 + ld a,$02 + call l6391+stst ; display "found drives" message + call l6817+stst ; page ROM 2/bank 7 + call DD_ASK_1 + call l683a+stst ; page ROM 1/bank 0 + ld a,'A' + jr nc,l6079 ; if only one drive, move on + ld a,'&' ; else output "&B" + rst $10 + ld a,'B' + rst $10 + ld a,'B' +.l6079 ld ($6aa3),a ; store max drive to test + ld a,$0d + rst $10 ; CR + ld ix,$6a61 + ld a,$00 ; initialise a standard XDPB + call l6817+stst ; page ROM 2/bank 7 + call DD_SEL_FORMAT + call l683a+stst ; page ROM 1/bank 0 + ret nc ; exit if error + call l60a1+stst ; test unit 0 + ret nc ; exit if error + ld a,$01 + ld ($6aa4),a ; set unit 1 + ld a,($6aa3) + cp 'B' + call z,l60a1+stst ; if B: present, test it + ret + +; Subroutine to test unit at $6aa4 + +.l60a1 ld a,$04 + call l6391+stst ; display "testing drive" message + ld a,($6aa4) + add a,'A' + rst $10 ; output drive letter + ld a,$0d + rst $10 ; CR + call l636a+stst ; get drive status + jr z,l60be ; move on if no disk + ld a,$06 + call l6391+stst ; display "remove disk" message +.l60b9 call l636a+stst ; get drive status + jr nz,l60b9 ; loop while disk present +.l60be ld a,$05 + call l6391+stst ; display "insert side 1" message +.l60c3 call l636a+stst ; get drive status + jr z,l60c3 ; loop until disk present + call l6817+stst ; page ROM 2/bank 7 + call DD_INIT + call l683a+stst ; page ROM 1/bank 0 + ld d,$00 + call l62a8+stst ; format track 0 + ret nc + call l61f5+stst ; spin speed test + ret nc + call l6817+stst ; page ROM 2/bank 7 + call DD_INIT + call l683a+stst ; page ROM 1/bank 0 + ld a,$0e + call l6391+stst ; display "formatting tracks" message + ld a,$e5 + ld ($6860),a ; use $e5 as filler + ld d,$13 + call l62a8+stst ; format track $13 + ret nc + ld d,$00 + call l62a8+stst ; format track 0 + ret nc + ld d,$27 + call l62a8+stst ; format track $27 + ret nc + ld a,$aa + ld ($6860),a ; use $aa as filler + ld d,$12 + call l62a8+stst ; format track $12 + ret nc + ld d,$01 + call l62a8+stst ; format track 1 + ret nc + ld d,$26 + call l62a8+stst ; format track $26 + ret nc + ld a,$e5 + ld ($6860),a ; test $e5 filled tracks + ld d,$00 + call l6269+stst ; test track 0 + ret nc + ld d,$13 + call l6269+stst ; test track $13 + ret nc + ld d,$27 + call l6269+stst ; test track $27 + ret nc + ld a,$aa + ld ($6860),a ; test $aa filled tracks + ld d,$01 + call l6269+stst ; test track 1 + ret nc + ld d,$12 + call l6269+stst ; test track $12 + ret nc + ld d,$26 + call l6269+stst ; test track $26 + ret nc + xor a + ld ($6860),a ; use 0 as filler + ld d,$00 + call l62a8+stst ; format track 0 + ret nc + ld d,$01 + call l62a8+stst ; format track 1 + ret nc + ld d,$12 + call l62a8+stst ; format track $12 + ret nc + ld d,$13 + call l62a8+stst ; format track $13 + ret nc + ld d,$26 + call l62a8+stst ; format track $26 + ret nc + ld d,$27 + call l62a8+stst ; format track $27 + ret nc + ld d,$00 + call l6269+stst ; test track 0 + ret nc + ld d,$01 + call l6269+stst ; test track 1 + ret nc + ld d,$12 + call l6269+stst ; test track $12 + ret nc + ld d,$13 + call l6269+stst ; test track $13 + ret nc + ld d,$26 + call l6269+stst ; test track $26 + ret nc + ld d,$27 + call l6269+stst ; test track $27 + ret nc + ld a,$06 + call l6391+stst ; display "remove disk" message +.l6196 call l636a+stst ; get drive status + jr nz,l6196 ; loop until no disk present + rst $28 + defw $0daf ; cls + ld a,$07 + call l6391+stst ; display "insert side 2" message +.l61a3 call l636a+stst ; get drive status + jr z,l61a3 ; loop until disk present + call l6817+stst ; page ROM 2/bank 7 + call DD_INIT + call l683a+stst ; page ROM 1/bank 0 + ld a,($6aa4) + ld c,a + call l6817+stst ; page ROM 2/bank 7 + call DD_DRIVE_STATUS + call l683a+stst ; page ROM 1/bank 0 + and $40 + jr nz,l61cb ; move on if write-protected + ld a,$08 + call l6391+stst ; display "disk not w/p" message + xor a ; fail + ld a,$ff + ret +.l61cb ld a,$0a + call l6391+stst ; display "checking data" message + ld b,$28 ; $28 tracks to check +.l61d2 push bc + ld a,b + add a,a + add a,a + add a,a + add a,a + add a,b + ld ($6860),a ; filler is $11*(track+1) + dec b + ld d,b ; track number to check + call l6269+stst ; check track + pop bc + ret nc ; exit if error + djnz l61d2 ; back for more + ld a,$06 + call l6391+stst ; display "remove disk" message +.l61ea call l636a+stst ; get drive status + jr nz,l61ea ; loop back until no disk + rst $28 + defw $0daf ; cls + xor a + scf ; success + ret + +; Subroutine to test spin speed + +.l61f5 ld a,$0b + call l6391+stst ; display "starting spin speed test" + call l62de+stst ; time a read data command + call l62de+stst ; and again + push hl + ld hl,$3030 + ld (l6522+stst),hl ; set spin speed difference 00% + pop hl + push de + ld ($685e),hl ; save low word of time taken + ld de,$4c2d + or a + sbc hl,de ; test time taken (low word) + ld a,'-' ; set spin speed +/- flag + jr nc,l6218 + ld a,'+' +.l6218 ld (l6521+stst),a ; set sign of spin speed difference + pop de + ld a,d + push af + or e ; test time taken (high word) + jr z,l6227 + pop af + ld a,$63 ; if not zero, spin speed out by 99% + jp l623f+stst +.l6227 pop af + ld hl,($685e) ; get low word of time taken + ld de,$4c2d + jr nc,l6231 + ex de,hl ; exchange if necessary +.l6231 xor a ; zero difference percentage + sbc hl,de ; get timing difference + ld de,$00c3 ; DE is 1% difference +.l6237 or a + sbc hl,de + jr c,l623f ; move on when within range + inc a ; increment percentage difference + jr l6237 ; loop back +.l623f push af + or a + jr z,l624c ; move on if spin speed exactly right + ld b,a +.l6244 ld hl,l6523+stst ; address of %age in "spin diff" message + call l625e+stst ; increment by B% + djnz l6244 +.l624c ld a,$0f + call l6391+stst ; display "spin speed diff" message + pop af + cp $03 + ld a,$10 + push af + call nc,l6391+stst ; if 3% out or more, "spin speed incorrect" + pop af + ld a,$ff + ret + +; Subroutine to increment %age figure (ASCII) at HL + +.l625e ld a,(hl) + inc a ; increment low digit + ld (hl),a + cp $3a + ret nz ; exit if in range + ld (hl),'0' ; else zero + dec hl ; and loop back for next higher digit + jr l625e + +; Subroutine to test track D contains sector filled with correct filler + +.l6269 ld b,$09 ; 9 sectors + push bc + dec b + ld a,($6aa4) + ld e,b ; E=sector (0 base) + ld c,a ; C=unit + ld b,$00 ; page 0 + ld e,$00 ; sector 0 + ld hl,$6861 ; buffer to read to + ld ix,$6a61 ; XDPB + call l6817+stst ; page ROM 2/bank 7 + call DD_READ_SECTOR + call l683a+stst ; page ROM 1/bank 0 + pop bc + ret nc ; exit if error + push bc + ld hl,$6861 + ld bc,$0200 ; $200 bytes to test +.l628f ld a,($6860) + cp (hl) ; test against filler + jr nz,l629e ; move on if different + inc hl + dec bc + ld a,b + or c + jr nz,l628f ; loop back + pop bc + scf ; success + ret +.l629e pop bc + ld a,$0c + call l6391+stst ; display "data not read correctly" message + xor a + ld a,$ff + ret + +; Subroutine to format track D with filler byte from $6860 + +.l62a8 ld a,($6860) + ld e,a ; E=filler byte + ld b,$00 ; B=page 0 + ld a,($6aa4) + ld c,a ; C=unit + ld hl,$6a7b + call l62c6+stst ; fill format buffer + ld ix,$6a61 ; IX=XDPB + call l6817+stst ; page ROM 2/bank 7 + call DD_FORMAT + call l683a+stst ; page ROM 1/bank 0 + ret + +; Subroutine to fill format buffer at HL for track D + +.l62c6 push af + push bc + push hl + ld b,$09 ; 9 sectors +.l62cb ld (hl),d ; insert track number + inc hl + ld (hl),$00 ; insert head number (0) + inc hl + ld a,$0a + sub b + ld (hl),a ; insert sector number (1-9) + inc hl + ld (hl),$02 ; insert sector size (512 bytes) + inc hl + djnz l62cb ; loop back + pop hl + pop bc + pop af + ret + +; Subroutine to turn on motor & time a read data command + +.l62de call l636a+stst ; get drive status + jr z,l62de ; loop until disk present + call l6817+stst ; page ROM 2/bank 7 + call DD_L_ON_MOTOR + call l683a+stst ; page ROM 1/bank 0 + di ; disable interrupts for timing + call l62f2+stst ; time a read data command + ei ; re-enable interrupts + ret + +; Subroutine to time an impossible "read data" command + +.l62f2 ld bc,$2ffd + in a,(c) ; get FDC status + bit 4,a + jr nz,l62f2 ; loop if busy + ld a,$66 + call l6353+stst ; send "read data" command + ld a,($6aa4) + call l6353+stst ; send unit + xor a + call l6353+stst ; send track 0 + xor a + call l6353+stst ; send head 0 + ld a,$fe + call l6353+stst ; send sector $fe + ld a,$03 + call l6353+stst ; send 1024 bytes/sector + ld a,$fe + call l6353+stst ; send endsector $fe + ld a,$2a + call l6353+stst ; send gaplength $2a + ld a,$ff + call l6353+stst ; send datalength $ff + ld hl,$0000 ; zero timer + ld de,$0000 +.l632d ld bc,$2ffd +.l6330 in a,(c) + bit 7,a + jr nz,l633e ; move on if FDC ready + inc hl ; increment timer DEHL + ld a,h + or l + jr nz,l6330 + inc de + jr l6330 ; loop back +.l633e bit 5,a + jr z,l6349 ; move on if not in execution phase + ld bc,$3ffd + in a,(c) ; discard a byte + jr l632d ; loop back +.l6349 ld bc,$2ffd + in a,(c) + bit 7,a + jr z,l6349 ; loop until FDC ready + ret + +; Subroutine to output a byte (A) to FDC data register + +.l6353 ld d,a ; save byte + ld bc,$2ffd +.l6357 in a,(c) + and $e0 + cp $80 + jr nz,l6357 ; loop back until FDC ready for data + ld bc,$3ffd + ld a,d + out (c),a ; output byte + ex (sp),hl ; short delay + ex (sp),hl + ex (sp),hl + ex (sp),hl + ret + +; Subroutine to get bit 5 of drive status + +.l636a ld a,($6aa4) ; get unit + ld c,a + call l6817+stst ; page ROM 2/bank 7 + call DD_DRIVE_STATUS + call l683a+stst ; page ROM 1/bank 0 + ld bc,$4000 +.l637a push bc + pop bc + dec bc + ld a,b + or c + jr nz,l637a ; delay loop + ld a,($6aa4) + ld c,a + call l6817+stst ; page ROM 2/bank 7 + call DD_DRIVE_STATUS + call l683a+stst ; page ROM 1/bank 0 + and $20 ; mask bit 5 + ret + +; Subroutine to set scroll count to max & display message A + +.l6391 push af + ld a,$ff + ld (SCR_CT),a + pop af + ld hl,l639e+stst ; first message + jp l6598+stst ; display message A + +; Message table + +.l639e defm $0d&0 + defm "Integral disk test V1.5"&$0d&0 + defm $0d&"Found Drive(s) :A"&0 + defm "NO DISK INTERFACE !"&$0d&$0d&"Test Aborted."&0 + defm $0d&"Testing Drive "&0 + defm $0d&"Insert side 1 of test disk."&$0d&0 + defm $0d&"Remove disk from drive"&$0d&0 + defm $0d&"Insert side 2 of test disk."&0 + defm $0d&"Disk is not write-protected."&$0d&0 + defm $0d&"Spin test not implemented."&$0d&0 + defm $0d&"Checking Data."&$0d&0 + defm $0d&"Starting spin-speed test."&$0d&0 + defm $0d&"Data not read correctly."&$0d&0 + defm $0d&"Press R to repeat, Q to quit"&$0d&0 + defm "Formatting tracks."&$0d&0 + defm "Spin speed difference " +.l6521 defm "*" +.l6522 defm "0" +.l6523 defm "0 %"&$0d&0 + defm $0d&"Spin speed is INCORRECT !"&$0d&0 +.l6544 defm $0d&"The disk drive test passed."&0 +.l6561 defm $0d&"The disk drive test failed."&0 + +; Subroutine to set print position to top left - appears unused + +.l657e push af + ld a,$16 +.l6581 rst $10 + xor a + rst $10 +.l6584 xor a + rst $10 + pop af + ret + +; Subroutine to display error message A + +.l6588 ld hl,l65b0+stst ; error message table start + cp $0a + jr c,l6591 + sub $0a ; adjust error number to fit table +.l6591 call l6598+stst ; display message + ld a,$0d + rst $10 ; CR + ret + +; Subroutine to display null-terminated message A, table start HL + +.l6598 ld b,a + inc b ; increment message count + xor a ; find null +.l659b push hl + pop de +.l659d cp (hl) +.l659e inc hl + jr nz,l659d ; loop back until null found + djnz l659b ; loop back until at start of correct message + ex de,hl ; DE=message start +.l65a4 ld a,(hl) ; get next char + inc hl + or a + ret z ; exit if null + cp $ff + ret z ; or $ff + rst $28 + defw $0010 ; output char + jr l65a4 ; loop back + +; Error message table + +.l65b0 defm "Drive not ready."&0 + defm "Disc is write protected."&0 + defm "Disc seek fail."&0 + defm "Disc CRC data error."&0 + defm "No data on disc."&0 + defm "Address mark missing."&0 + defm "Unrecognized disc format."&0 + defm "Unknown disc error. - Well done !"&0 + defm "Disc changed while in use."&0 + defm "Wrong type of disc in drive."&0 + defm "Bad filename."&0 + defm "Bad parameter."&0 + defm "Drive not found."&0 + defm "File not found."&0 + defm "File already exists."&0 + defm "End of file."&0 + defm "Disc full."&0 + defm "Directory full."&0 + defm "Read-only file."&0 + defm "File not open, or wrong access."&0 + defm "Access denied, file in use."&0 + defm "Cannot rename between drives"&0 + defm "Extent is missing."&0 + defm "Uncached. (software error)"&0 + defm "File too big."&0 + defm "Disc not bootable"&0 + defm "Drive was in use."&0 + defm "You should never see this."&0 + +; Subroutine to wait for a keypress, returning in A + +.l67f9 ld hl,FLAGS + res 5,(hl) ; set "no key" +.l67fe bit 5,(hl) + jr z,l67fe ; loop back until key available + ld a,(LAST_K) ; get it + res 5,(hl) ; set "no key" + ret + +; Subroutine to reset the computer - appears to be unused + +.l6808 di + ld bc,$1ffd + xor a + out (c),a + ld bc,$7ffd + out (c),a + jp $0000 + +; Subroutine to make sure ROM 2 & bank 7 are paged in + +.l6817 di +.l6818 push af + push bc + push hl + ld bc,$7ffd + ld hl,BANKM + ld a,(hl) + or $07 ; ensure ROM 0/2 & bank 7 paged + and $ef +.l6826 ld (hl),a + out (c),a + ld hl,BANK678 + ld a,(hl) + or $04 ; ensure ROM 2 paged + ld (hl),a + ld bc,$1ffd + out (c),a + pop hl + pop bc + pop af + ei + ret + +; Subroutine to make sure ROM 1 and bank 0 are paged in + +.l683a di + push af + push bc + push hl + ld bc,$7ffd + ld hl,BANKM + ld a,(hl) + or $10 ; ensure ROM 1/3 & bank 0 paged + and $f8 + ld (hl),a +.l684a out (c),a + ld a,(BANK678) +.l684f and $fb ; ensure ROM 1 paged + ld (BANK678),a + ld bc,$1ffd + out (c),a + pop hl + pop bc + pop af + ei + ret + nop + nop + push hl + nop + nop + +; *********************** END OF SELF-TEST PROGRAM ********************** + + +.l2cc4 defs 4417 + +; Paging routine, for calling a routine (address inline) in ROM 0 + +.l3e00 ld (OLDHL),hl ; save HL & AF + push af + pop hl + ld (OLDAF),hl + ex (sp),hl + ld c,(hl) + inc hl + ld b,(hl) ; BC=inline address to call + inc hl + ex (sp),hl ; restack updated return address + push bc + pop hl ; HL=address to call + ld a,(BANK678) + ld bc,$1ffd + res 2,a ; select ROM 0 + di + ld (BANK678),a + out (c),a + ei + ld bc,l3e2d + push bc ; stack address to return to repage ROM 2 + push hl ; stack address to call in ROM 0 + ld hl,(OLDAF) ; restore AF & HL + push hl + pop af + ld hl,(OLDHL) + ret ; "return" to call routine +.l3e2d push bc ; stack registers + push af + ld a,(BANK678) + ld bc,$1ffd + set 2,a ; select ROM 2 + di + ld (BANK678),a + out (c),a + ei + pop af ; restore registers + pop bc + ret ; done + + defs 191 + +; This routine is a duplicate of a routine to call a ROM 2 routine +; residing in ROM 1 (another copy is in ROM 0). As soon as ROM 2 is +; paged in, it takes control. + +.l3f00 ld (OLDHL),hl ; save HL, BC and AF + ld (OLDBC),bc + push af + pop hl + ld (OLDAF),hl + ex (sp),hl + ld c,(hl) + inc hl + ld b,(hl) ; BC=inline address + inc hl + ex (sp),hl ; restack updated return address + push bc + pop hl + ld a,(BANKM) + and $ef + di + ld (BANKM),a + ld bc,$7ffd + out (c),a ; page in ROM 0 + ld a,(BANK678) + or $04 + ld (BANK678),a +.l3f2a ld bc,$1ffd + out (c),a ; page in ROM 2 + ei + ld bc,l3f42 + push bc ; stack routine address to return to ROM 1 + push hl ; stack routine address to call + ld hl,(OLDAF) ; restore registers + push hl + pop af + ld bc,(OLDBC) + ld hl,(OLDHL) + ret ; exit to ROM 2 routine + +; This part then returns control to ROM 1 + +.l3f42 push bc ; save registers + push af + ld a,(BANK678) + and $fb + di + ld (BANK678),a + ld bc,$1ffd + out (c),a ; page in ROM 0 + ld a,(BANKM) + or $10 + ld (BANKM),a + ld bc,$7ffd + out (c),a ; page in ROM 1 + ei + pop af ; restore registers + pop bc + ret ; done! + +; Unused space + + defb $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + defb $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + defb $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + defb $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + defb $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + defb $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + defb $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + defb $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + defb $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + defb $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + defb $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + defb $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + defb $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + defb $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + defb $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + defb $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + defb $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + defb $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + defb $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + defb $ff,$ff,$ff,$ff + defb $66 + diff --git a/plus3dos_file_headers.txt b/plus3dos_file_headers.txt new file mode 100644 index 0000000..864ab3f --- /dev/null +++ b/plus3dos_file_headers.txt @@ -0,0 +1,53 @@ +File headers + +Tape files have headers which contain some system information. +3DOS +files may, or may not, have headers. All files created by BASIC's SAVE +command will have headers. + +The +3DOS header mechanism provides a dedicated 8 byte area in each +headed file reserved for BASIC's use. The remainder of the header is +reserved for +3DOS. This 8 byte header is utilised in files created by +BASIC commands (see DOS OPEN description). + ++3DOS files may have a single header in the first 128 bytes of the +file - the header record. These headers are detected by a 'signature' +and checksum. If the signature and checksum are as expected, then a +header is present; if not, these is no header. Thus, it is possible, +but unlikely, that a file without a header could be mistaken for one +with a header. + +The format of the header record is as follows: + + Bytes 0...7 - +3DOS signature - 'PLUS3DOS' + Byte 8 - 1Ah (26) Soft-EOF (end of file) + Byte 9 - Issue number + Byte 10 - Version number + Bytes 11...14 - Length of the file in bytes, 32 bit number, + least significant byte in lowest address + Bytes 15...22 - +3 BASIC header data + Bytes 23...126 - Reserved (set to 0) + Byte 127 - Checksum (sum of bytes 0...126 modulo 256) + +The issue and version numbers are provided for any future +expansion. The issue number must equal the software's issue number; +the version number must be less than or equal to the software's +version number. + ++3DOS performs all the necessary header 'house-keeping'. A pointer to ++3 BASIC's 8 byte header area may be returned using DOS REF HEAD. It +is never necessary to write directly to the 128 byte header. + + + ++3 BASIC header: +0x0f : Type +0x10 : FileSize High +0x11 : FileSize Low +0x12 : Parameter1 high : Start Address / RunLine +0x13 : Parameter1 Low +0x14 : Parameter2 high +0x15 : Parameter2 Low +0x16 : ?? + + + diff --git a/registry.h b/registry.h new file mode 100644 index 0000000..041733f --- /dev/null +++ b/registry.h @@ -0,0 +1,11 @@ +// Some defines for accessing the registry and wotnot. + +#ifndef _REGISTRY_H_INCLUDED +#define _REGISTRY_H_INCLUDED + +#define REG_ROOT _T("raww tools") +#define REG_SETTINGS _T("Settings") +#define REG_SETTINGS_HEADCACHE _T("HeadCache") +#define REG_SETTINGS_PREREADFILEINFO _T("PreReadFileInfo") + +#endif diff --git a/res/3eExplorer.ico b/res/3eExplorer.ico new file mode 100644 index 0000000000000000000000000000000000000000..7eef0bcbe6580a6f464d688906172c2d9de44262 GIT binary patch literal 1078 zcmc&zF>b>!3}jLb9s)T}@Kod(893@u8ajANzT`op9^o+)S?=nU(FD@%0s)Sg^oyC8{H z9myetc;MEP)59v(LMa~xK8Yu^jIR*H22uCFiq5%C{s7(PJi>o15i^bmX4(vPxWAio z9ryY#AU_jfnd047-@`)XzL?%iS$gQyFP{44kS9X)fN{{QoL~hO-&=q&20Zr*cxFAt PkaNE{wR~2C$NfnjhSXWT literal 0 HcmV?d00001 diff --git a/res/3eExplorer.rc2 b/res/3eExplorer.rc2 new file mode 100644 index 0000000..e5737d4 --- /dev/null +++ b/res/3eExplorer.rc2 @@ -0,0 +1,13 @@ +// +// 3EEXPLORER.RC2 - resources Microsoft Visual C++ does not edit directly +// + +#ifdef APSTUDIO_INVOKED + #error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// Add manually edited resources here... + +///////////////////////////////////////////////////////////////////////////// diff --git a/res/3eExplorerDoc.ico b/res/3eExplorerDoc.ico new file mode 100644 index 0000000000000000000000000000000000000000..b7b56b2b5880e7c990feb43dc660af91d5fe68f4 GIT binary patch literal 1078 zcmcJNJyOIl426|l2B#w1QjvD+ln;gYVZUKxxtHAdas+MNX6O|@GOCqaU3ZG z{YwaO1^C4ju5`|db7ZrTD08BsrwTqN;L&o&%;~WDf+G)-UXi&kPwOE)K>fq1u?r^W z{^03%E6!AgEOp(+8TaSho%Z{3nWsY|U)z81`Fvl_o5Ii!Q`$wG4asPS=EBE}GlpwN45fw0CJEI_q0Vom~E@1g5Ry4dOD!&FtosX=qn?=-A(6t6U zOVxPP`gb}vooQ*{G_7=m%5zpSdjE2JsNTD-Guwt)o$n@0`>@NK8bGHkdKdb4m#tglAgQo-)Js1!#;+~GSl z37z4Z<|(I?jV44Q_xOeQ!(IFmLY86pVjciLPfC9(?0)$t7hw7W^>>N<@a4LVKPMb~ ze5>=j|BL>!3lPen5hUo~ANKN`l7C&3 z%0PPq3XFRT-e1OO0*EKjOA>DIfd!JKE>l7s8ZOWoALnDl7bEaBMvn{Ll0`lMj_p*2 zeyj@cEB=w(f%+@Ig9SfK=ePQ9c)El>T>d0F2ei{lnHqPg}It<8 literal 0 HcmV?d00001 diff --git a/res/app.ico b/res/app.ico new file mode 100644 index 0000000000000000000000000000000000000000..305d4112dc757a8b3241800774747efa6ca9a779 GIT binary patch literal 17006 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 S`sWC?bdwp4yv_grH1Hozv2PXt literal 0 HcmV?d00001 diff --git a/res/app_ico_large.psd b/res/app_ico_large.psd new file mode 100644 index 0000000000000000000000000000000000000000..52f2594b01e20a41b823a1e3de8e00f0628089f0 GIT binary patch literal 62887 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 literal 0 HcmV?d00001 diff --git a/res/app_ico_medium copy.ico b/res/app_ico_medium copy.ico new file mode 100644 index 0000000000000000000000000000000000000000..11cbdc67c6966f641bd24869df7587a38b8219e9 GIT binary patch literal 3262 zcmc(hYe-y26vt1h#msCLEY=#O7%8Z2Hi`XYBv2w2lE8)wUt)dT3Oq!K-@3K6wY9XgG-qRn4t*r0 zE#9(4*cP@W?3c@Jao!deFBS%a?=lJ4Kp-$QG&DImIXpakdE0j3!k1N5Kg&Hzjwq?_ zNo9GJ=d_X!l$@P?`^uF*LmC(uuq+GX%*@P0mUiyk&HVhwbl9Oqhn&0QR2Mn)twXD; za&GR7fx!!60e^aWIxBoA6zcB2bmGKa%F*<(roT1m&q?RkX;PY2R$iN$`V7Eq`JBTKtxXFImfhw{S0!u5_LIP&}b;9tEu>G$8K7d3@6J%Nj1(%z$* z9%))$E*~4~-e&$A8X6dm6u}+f#-h<{m6gj>s%aYhzet~iw@;C!CokN%;kT@&?bMeu zgDBR)6R0wrV&&jLONb?^2+_Vb6Efm^$~46tT39G*Z$GjnzrVjfacZL>|AxPDdF?9x zA!*M$4$UixJ8nyUTF$2Y?@2f8bSR``G@7?1Ka+kW3WY$SIfR^?lT^P#zo8H}$tmBf z^!bbMgB+o3!d45saW+;L@zhxP_5D5%WMy+iM7`rVQ08xbi<$`4L$IIwmB7peCz zWTfuI%KrUdlULIa_=_SENa8m&HJMWg!5xLEru+HB{Oamz^Z-V4u({bTF8-4aIP$s@ zk#G{=A0A z8GCp?Pew+*tEqV?cdMQ6W#r>5qfljV05=(Mk0^0k7hPRHm6Y7eD~e{)?}nhh(B^i> zodehK%gT8&K0Xl${3wK-mY>u`=zm@T*@OnCNzYP-n@La47Znwuw0vwp)Z9x(q=^U* z1Qrj!Nlf=8-Y@m_r_=JG4b8xsLB^%wBTI12j~zVd+%1&EPkp~2nNaIaKIZ0a=*~Y@ zcpI|h#_~MV`;o&pUcf;hIE9g!EC4H6=?4elLy|$Elm>28Va`#QxZM9Wdw9&GpEqQh zKrqsnNj9H)YCaP-pFD3qzG=}v_`Z$D2;wuW!oSS;4s+S=LKiA>@yc;+@ZIEasb J?zI1D@-Mn8qc;Em literal 0 HcmV?d00001 diff --git a/res/app_ico_medium.psd b/res/app_ico_medium.psd new file mode 100644 index 0000000000000000000000000000000000000000..6a5a0cc13e635e7827dca2cafd87c8b005fe3d33 GIT binary patch literal 52068 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 Wy?Tdf=8Lbszf*X5C;Nu4#r_}T08N+x literal 0 HcmV?d00001 diff --git a/res/app_ico_small copy.ico b/res/app_ico_small copy.ico new file mode 100644 index 0000000000000000000000000000000000000000..fd24f4312004293aef5b276d09d264b919aba1b7 GIT binary patch literal 894 zcma))%}N4M6vxj+f)PJbqMQ=KTx1Z4+9nr*fw<5`7PJsu4W$E$kS^53Hs(TFj#Nw6 zwQdnD+W8P6^aQ;@R{!yglUYP(xOdJy^E;n&i3A>(i?GY2T8LJNXbTG_@?q)rsVv!u z^fi@oxt#w^B9T+;Uf0pdZ(ca-=dz+4g34yI&1UoVyJ(sw%d!x{YLAPDH!)V)V=)>f zK*6QaXxz<6zu%u`rch{qAvUJh7sp57>D@9MR)6xrV32t@=5je*m)vx;i#IYV%E4TG zhI#jTy|c%)9Z|0b{@@dd1S&OLDynN7D0CciyvO5V03=F+i@{57adW)USp=`Ds?Da4 zIFre=+ie3N3Wvj{4h8i)JF!Zog6SbFM@EmuVv#Yw-;efjL?X;etQPENO|EA!7;t0& z(4)~PTqH??yE*J+^4S*eG7meKglG)o$b#VPqG|W*zLv>z4ZU8E6~M@4!f?U=O{GrE zdeAILB;rOe8IF$#9G~w)tyY`#>cLm==%QM!atiD)nkmftgL&E~g6qMdz(9wfceZdn ou(Vow3;z+^WL$ot>qjE}x6iIpiRkLmEDMJ(eR)8XP8;X#PkfaU!~g&Q literal 0 HcmV?d00001 diff --git a/res/app_ico_small.psd b/res/app_ico_small.psd new file mode 100644 index 0000000000000000000000000000000000000000..b58e58c14c12d057d84b3db262c3e022489bd549 GIT binary patch literal 47389 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 literal 0 HcmV?d00001 diff --git a/res/descript.ion b/res/descript.ion new file mode 100644 index 0000000..e69de29 diff --git a/res/icobundl.exe b/res/icobundl.exe new file mode 100644 index 0000000000000000000000000000000000000000..39484ecffb04323fcfaf55959e98658036d442f0 GIT binary patch literal 15872 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 Y99K5l|6FCW|KW8$JvlOs{^!5`1NtVlvH$=8 literal 0 HcmV?d00001 diff --git a/res/icocheck.exe b/res/icocheck.exe new file mode 100644 index 0000000000000000000000000000000000000000..39605b6bfdf168c734a670e44cd2a1eab09b2ae7 GIT binary patch literal 13824 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 literal 0 HcmV?d00001 diff --git a/res/tree_ico_mask.bmp b/res/tree_ico_mask.bmp new file mode 100644 index 0000000000000000000000000000000000000000..144b4a5d6b7f9ce499ff68b6db7d14d73ad2cd2c GIT binary patch literal 12344 zcmeH{y-vhH41@zM4Wi-^D5(%NJ@5ZG;5#d$(f*B>aInedq`QpQ-}*1seSE&ZF4xbS z{k~k(-LgDhAI}$Sxi|mQgMY6F_Vaaq&Iz0oI497Y!1`~|dUAjkxB6!0x%UCJy`n}{ z>kAZ5L=fES25(|Lt3JD6MDbfNr9c)*8UWbD$>$T``HZYld9g@%BO~@#s7;a#*ey1t?#coyCe+& z9L#bgo_pD~_809fu>Rww?_J}Cn)t6?xQ0YqNl9S| z0JBy+fJD@I&zQi8ncPjhxp^=?Y3DRIP9R?xKFNc#*LBYD$#$M2aAeo)&Gqs4yrc8v XNE66+7$?Ox;myS%`C?PMI9!%*%FPox literal 0 HcmV?d00001 diff --git a/tapfile_format.txt b/tapfile_format.txt new file mode 100644 index 0000000..a8dcce5 --- /dev/null +++ b/tapfile_format.txt @@ -0,0 +1,118 @@ +TAP File Format + +The .TAP files contain blocks of tape-saved data. +All blocks start with two bytes specifying how many bytes will follow +(not counting the two length bytes). +Then raw tape data follows, including the flag and checksum bytes. +The checksum is the bitwise XOR of all bytes including the flag byte. +For example, when you execute the line SAVE "ROM" CODE 0,2 this will result: + + |------ Spectrum-generated data -------| |---------| + + 13 00 00 03 52 4f 4d 7x20 02 00 00 00 00 80 f1 04 00 ff f3 af a3 + + ^^^^^...... first block is 19 bytes (17 bytes+flag+checksum) + ^^... flag byte (A reg, 00 for headers, ff for data blocks) + ^^ first byte of header, indicating a code block + + file name ..^^^^^^^^^^^^^ + header info ..............^^^^^^^^^^^^^^^^^ + checksum of header .........................^^ + length of second block ........................^^^^^ + flag byte ............................................^^ + first two bytes of rom .................................^^^^^ + checksum (checkbittoggle would be a better name!).............^^ + +Note that it is possible to join .TAP files by simply stringing them together, +for example COPY /B FILE1.TAP + FILE2.TAP ALL.TAP + +For completeness, I'll include the structure of a tape header. +A header always consists of 17 bytes: + + Byte Length Description + --------------------------- + 0 1 Type (0,1,2 or 3) + 1 10 Filename (padded with blanks) + 11 2 Length of data block + 13 2 Parameter 1 + 15 2 Parameter 2 + +The type is 0,1,2 or 3 for a Program, Number array, Character array or Code file. +A SCREEN$ file is regarded as a Code file with start address 16384 and +length 6912 decimal. If the file is a Program file, parameter 1 holds the +autostart line number (or a number >=32768 if no LINE parameter was given) and +parameter 2 holds the start of the variable area relative to the start of the +program. If it's a Code file, parameter 1 holds the start of the code block +when saved, and parameter 2 holds 32768. For data files finally, the byte at +position 14 decimal holds the variable name. + + + +Some code... + + +// check filesize +ifs.seekg(0, std::ios::end); +int filesize = ifs.tellg(); +ifs.seekg(0, std::ios::beg); +if (filesize > 0xffff) filesize = 0xffff; +char* filebuf = new char[filesize + 3]; +ifs.read (&filebuf[3], filesize); +ifs.close(); + +// fix our filename length +zxfile.resize(10, ' '); + +// generate our header... +/* +Byte Length Description +--------------------------- +0 1 Type (0,1,2 or 3) +1 10 Filename (padded with blanks) +11 2 Length of data block +13 2 Parameter 1 +15 2 Parameter 2 +*/ + +char header[20]; +header[ 0] = 0x13; // length low +header[ 1] = 0x00; // length high +header[ 2] = 0x00; // flag "header" +header[ 3] = 0x03; // code block +strncpy(&header[ 4], zxfile.c_str(), 10); +header[15] = (filesize / 256); +header[14] = filesize - (256 * header[15]); +header[17] = (start_addr / 256); +header[16] = start_addr - (256 * header[17]); +header[18] = 0x00; +header[19] = (char)0x80; + +// work out the checksum +char chk = 0; +for (int ii = 2; ii < 20; ++ii) +{ + chk ^= header[ii]; +} + +ofs.write(header, 20); +ofs.write(&chk, 1); + + +int t_filesize = filesize + 2; +filebuf[2] = (char)0xff; +filebuf[1] = (t_filesize / 256); +filebuf[0] = t_filesize - (256 * filebuf[1]); + +chk = 0; +for (ii = 2; ii < t_filesize + 1; ++ii) +{ + chk ^= filebuf[ii]; +} + +ofs.write(filebuf, filesize + 3); +ofs.write(&chk, 1); + +ofs.close(); + + +