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 0000000..7eef0bc Binary files /dev/null and b/res/3eExplorer.ico differ 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 0000000..b7b56b2 Binary files /dev/null and b/res/3eExplorerDoc.ico differ diff --git a/res/Toolbar.bmp b/res/Toolbar.bmp new file mode 100644 index 0000000..d501723 Binary files /dev/null and b/res/Toolbar.bmp differ diff --git a/res/app.ico b/res/app.ico new file mode 100644 index 0000000..305d411 Binary files /dev/null and b/res/app.ico differ diff --git a/res/app_ico_large copy.ico b/res/app_ico_large copy.ico new file mode 100644 index 0000000..008e69d Binary files /dev/null and b/res/app_ico_large copy.ico differ diff --git a/res/app_ico_large.psd b/res/app_ico_large.psd new file mode 100644 index 0000000..52f2594 Binary files /dev/null and b/res/app_ico_large.psd differ diff --git a/res/app_ico_medium copy.ico b/res/app_ico_medium copy.ico new file mode 100644 index 0000000..11cbdc6 Binary files /dev/null and b/res/app_ico_medium copy.ico differ diff --git a/res/app_ico_medium.psd b/res/app_ico_medium.psd new file mode 100644 index 0000000..6a5a0cc Binary files /dev/null and b/res/app_ico_medium.psd differ diff --git a/res/app_ico_small copy.ico b/res/app_ico_small copy.ico new file mode 100644 index 0000000..fd24f43 Binary files /dev/null and b/res/app_ico_small copy.ico differ diff --git a/res/app_ico_small.psd b/res/app_ico_small.psd new file mode 100644 index 0000000..b58e58c Binary files /dev/null and b/res/app_ico_small.psd differ 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 0000000..39484ec Binary files /dev/null and b/res/icobundl.exe differ diff --git a/res/icocheck.exe b/res/icocheck.exe new file mode 100644 index 0000000..39605b6 Binary files /dev/null and b/res/icocheck.exe differ diff --git a/res/tree_ico.bmp b/res/tree_ico.bmp new file mode 100644 index 0000000..44c68fc Binary files /dev/null and b/res/tree_ico.bmp differ diff --git a/res/tree_ico.psd b/res/tree_ico.psd new file mode 100644 index 0000000..a472a50 Binary files /dev/null and b/res/tree_ico.psd differ diff --git a/res/tree_ico_mask.bmp b/res/tree_ico_mask.bmp new file mode 100644 index 0000000..144b4a5 Binary files /dev/null and b/res/tree_ico_mask.bmp differ 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(); + + +