/* * Copyright 2018 faddenSoft * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using PluginCommon; /* # inline_entry equ $e100a8 # jsl inline_entry # dc i2'callnum' # dc i4'parmblock' # bcs error # stack_entry equ $e100b0 # pea parmblock|-16 # pea parmblock # pea callnum # jsl stack_entry # bcs error */ namespace RuntimeData.Apple { /// /// Identify and format ProDOS-16 and GS/OS system calls. /// public class GSOS : MarshalByRefObject, IPlugin, IPlugin_SymbolList, IPlugin_InlineJsl { private const string GSOS_FUNC_TAG = "AppleIIgs-GSOS-Functions"; // tag used in .sym65 file private bool VERBOSE = false; #region Parameter block defs private class Param { public DataType Type { get; private set; } public DataSubType SubType { get; private set; } public int Length { get; private set; } public Param(DataType type, DataSubType subType, int length) { Type = type; SubType = subType; Length = length; } } private static Param PARAM_COUNT = new Param(DataType.NumericLE, DataSubType.Decimal, 2); private static Param REF_NUM = new Param(DataType.NumericLE, DataSubType.Decimal, 2); private static Param DEV_NUM = new Param(DataType.NumericLE, DataSubType.Decimal, 2); private static Param STRING_PTR = new Param(DataType.NumericLE, DataSubType.Address, 4); private static Param BUF_PTR = new Param(DataType.NumericLE, DataSubType.Address, 4); private static Param PROC_PTR = new Param(DataType.NumericLE, DataSubType.Address, 4); private static Param ACCESS = new Param(DataType.NumericLE, DataSubType.Hex, 2); private static Param FILE_TYPE = new Param(DataType.NumericLE, DataSubType.Hex, 2); private static Param AUX_TYPE = new Param(DataType.NumericLE, DataSubType.Hex, 4); private static Param STORAGE_TYPE = new Param(DataType.NumericLE, DataSubType.Hex, 2); private static Param FILE_OFFSET = new Param(DataType.NumericLE, DataSubType.Hex, 4); private static Param DATE_TIME = new Param(DataType.Dense, DataSubType.None, 8); private static Param OLD_DATE = new Param(DataType.NumericLE, DataSubType.Hex, 2); private static Param OLD_TIME = new Param(DataType.NumericLE, DataSubType.Hex, 2); private static Param MISC2 = new Param(DataType.NumericLE, DataSubType.Hex, 2); private static Param MISC4 = new Param(DataType.NumericLE, DataSubType.Hex, 4); private static Param RESULT2 = new Param(DataType.NumericLE, DataSubType.Hex, 2); private static Param RESULT4 = new Param(DataType.NumericLE, DataSubType.Hex, 4); private Dictionary ParamDescrs = new Dictionary() { // // "Class 0" ProDOS-16 calls. // { 0x0031, // ALLOC_INTERRUPT new Param[] { MISC2, BUF_PTR } }, { 0x0004, // CHANGE_PATH new Param[] { STRING_PTR, STRING_PTR } }, { 0x000b, // CLEAR_BACKUP_BIT new Param[] { STRING_PTR } }, { 0x0014, // CLOSE new Param[] { REF_NUM } }, { 0x0001, // CREATE new Param[] { STRING_PTR, ACCESS, FILE_TYPE, AUX_TYPE, STORAGE_TYPE, OLD_DATE, OLD_TIME } }, { 0x0032, // DEALLOC_INTERRUPT new Param[] { MISC2 } }, { 0x0002, // DESTROY new Param[] { STRING_PTR } }, { 0x002c, // D_INFO new Param[] { DEV_NUM, BUF_PTR } }, { 0x0025, // ERASE_DISK new Param[] { STRING_PTR, STRING_PTR, MISC2 } }, { 0x000e, // EXPAND_PATH new Param[] { STRING_PTR, BUF_PTR, MISC2 } }, { 0x0015, // FLUSH new Param[] { REF_NUM } }, { 0x0024, // FORMAT new Param[] { STRING_PTR, STRING_PTR, MISC2 } }, { 0x0028, // GET_BOOT_VOL new Param[] { BUF_PTR } }, { 0x0020, // GET_DEV_NUM new Param[] { STRING_PTR, DEV_NUM } }, { 0x001c, // GET_DIR_ENTRY new Param[] { REF_NUM, MISC2, MISC2, MISC2, BUF_PTR, MISC2, FILE_TYPE, FILE_OFFSET, MISC4, DATE_TIME, DATE_TIME, ACCESS, AUX_TYPE, MISC2 } }, { 0x0019, // GET_EOF new Param[] { REF_NUM, FILE_OFFSET } }, { 0x0006, // GET_FILE_INFO new Param[] { STRING_PTR, ACCESS, FILE_TYPE, AUX_TYPE, STORAGE_TYPE, OLD_DATE, OLD_TIME, OLD_DATE, OLD_TIME, MISC4 } }, { 0x0021, // GET_LAST_DEV new Param[] { DEV_NUM } }, { 0x001b, // GET_LEVEL new Param[] { MISC2 } }, { 0x0027, // GET_NAME new Param[] { BUF_PTR } }, { 0x000a, // GET_PREFIX new Param[] { MISC2, BUF_PTR } }, { 0x002a, // GET_VERSION new Param[] { MISC2 } }, { 0x0011, // NEWLINE new Param[] { REF_NUM, MISC2, MISC2 } }, { 0x0010, // OPEN new Param[] { REF_NUM, STRING_PTR, MISC4 } }, { 0x0029, // QUIT new Param[] { STRING_PTR, MISC2 } }, { 0x0012, // READ new Param[] { REF_NUM, BUF_PTR, MISC4, MISC4 } }, { 0x0022, // READ_BLOCK new Param[] { DEV_NUM, BUF_PTR, MISC4 } }, { 0x0018, // SET_EOF new Param[] { REF_NUM, FILE_OFFSET } }, { 0x0005, // SET_FILE_INFO new Param[] { STRING_PTR, ACCESS, FILE_TYPE, AUX_TYPE, MISC2, OLD_DATE, OLD_TIME, OLD_DATE, OLD_TIME } }, { 0x001a, // SET_LEVEL new Param[] { MISC2 } }, { 0x0016, // SET_MARK new Param[] { REF_NUM, FILE_OFFSET } }, { 0x0009, // SET_PREFIX new Param[] { MISC2, STRING_PTR } }, { 0x0008, // VOLUME new Param[] { STRING_PTR, BUF_PTR, MISC4, MISC4, MISC2 } }, { 0x0013, // WRITE new Param[] { REF_NUM, BUF_PTR, MISC4, MISC4 } }, { 0x0023, // WRITE_BLOCK new Param[] { DEV_NUM, BUF_PTR, MISC4 } }, // // "Class 1" GS/OS calls. // // Some changes were made since the GS/OS Reference was published for System 5.0. // See "Programmer's Reference for System 6.0", by Mike Westerfield / Byteworks, // and GS/OS tech note #13. // { 0x2034, // AddNotifyProcGS new Param[] { PARAM_COUNT, PROC_PTR } }, { 0x201d, // BeginSessionGS new Param[] { PARAM_COUNT } }, { 0x2031, // BindIntGS new Param[] { PARAM_COUNT, RESULT2, MISC2, MISC4 } }, { 0x2004, // ChangePathGS new Param[] { PARAM_COUNT, STRING_PTR, STRING_PTR, MISC2 } }, { 0x200b, // ClearBackupBitGS new Param[] { PARAM_COUNT, STRING_PTR } }, { 0x2014, // CloseGS new Param[] { PARAM_COUNT, REF_NUM } }, { 0x2001, // CreateGS new Param[] { PARAM_COUNT, STRING_PTR, ACCESS, FILE_TYPE, AUX_TYPE, STORAGE_TYPE, FILE_OFFSET, FILE_OFFSET } }, { 0x202e, // DControlGS (has a bunch of sub-calls) new Param[] { PARAM_COUNT, DEV_NUM, MISC2, BUF_PTR, MISC2, RESULT4 } }, { 0x2035, // DelNotifyProcGS new Param[] { PARAM_COUNT, PROC_PTR } }, { 0x2002, // DestroyGS new Param[] { PARAM_COUNT, STRING_PTR } }, { 0x202c, // DInfoGS new Param[] { PARAM_COUNT, DEV_NUM, STRING_PTR, MISC2, MISC4, MISC2, MISC2, MISC2, MISC2, MISC2, MISC2, BUF_PTR } }, { 0x202f, // DReadGS new Param[] { PARAM_COUNT, DEV_NUM, BUF_PTR, MISC4, MISC4, MISC2, MISC4 } }, { 0x2036, // DRenameGS new Param[] { PARAM_COUNT, DEV_NUM, STRING_PTR } }, { 0x202d, // DStatus (has a bunch of sub-calls) new Param[] { PARAM_COUNT, DEV_NUM, MISC2, BUF_PTR, MISC4, MISC4 } }, { 0x2030, // DWriteGS new Param[] { PARAM_COUNT, DEV_NUM, BUF_PTR, MISC4, MISC4, MISC2, MISC4 } }, { 0x201e, // EndSessionGS new Param[] { PARAM_COUNT } }, { 0x2025, // EraseDiskGS new Param[] { PARAM_COUNT, STRING_PTR, STRING_PTR, MISC2, MISC2, MISC2, BUF_PTR } }, { 0x200e, // ExpandPathGS new Param[] { PARAM_COUNT, STRING_PTR, STRING_PTR, MISC2 } }, { 0x2015, // FlushGS (see GS/OS tech note #13) new Param[] { PARAM_COUNT, REF_NUM, MISC2 } }, { 0x2024, // FormatGS new Param[] { PARAM_COUNT, STRING_PTR, STRING_PTR, MISC2, MISC2, MISC2, BUF_PTR } }, { 0x2033, // FSTSpecific new Param[] { PARAM_COUNT, MISC2, MISC2 /*...*/ } }, { 0x2028, // GetBootVolGS new Param[] { PARAM_COUNT, BUF_PTR } }, { 0x2020, // GetDevNumberGS new Param[] { PARAM_COUNT, STRING_PTR, DEV_NUM } }, { 0x201c, // GetDirEntryGS new Param[] { PARAM_COUNT, REF_NUM, MISC2, MISC2, MISC2, STRING_PTR, MISC2, FILE_TYPE, FILE_OFFSET, MISC4, DATE_TIME, DATE_TIME, ACCESS, AUX_TYPE, MISC2, BUF_PTR, FILE_OFFSET, MISC4 } }, { 0x2019, // GetEOFGS new Param[] { PARAM_COUNT, REF_NUM, FILE_OFFSET } }, { 0x2006, // GetFileInfoGS new Param[] { PARAM_COUNT, STRING_PTR, ACCESS, FILE_TYPE, AUX_TYPE, STORAGE_TYPE, DATE_TIME, DATE_TIME, BUF_PTR, FILE_OFFSET, MISC4, FILE_OFFSET, MISC4 } }, { 0x202b, // GetFSTInfoGS new Param[] { PARAM_COUNT, MISC2, MISC2, STRING_PTR, MISC2, MISC2, MISC2, MISC4, MISC4} }, { 0x201b, // GetLevelGS new Param[] { PARAM_COUNT, MISC2, MISC2 } }, { 0x2017, // GetMarkGS new Param[] { PARAM_COUNT, REF_NUM, FILE_OFFSET } }, { 0x2027, // GetNameGS new Param[] { PARAM_COUNT, BUF_PTR, MISC2 } }, { 0x200a, // GetPrefixGS new Param[] { PARAM_COUNT, MISC2, STRING_PTR } }, { 0x2039, // GetRefInfoGS new Param[] { PARAM_COUNT, REF_NUM, ACCESS, STRING_PTR, MISC2, MISC2 } }, { 0x2038, // GetRefNumGS new Param[] { PARAM_COUNT, STRING_PTR, REF_NUM, ACCESS, MISC2, MISC2, MISC2 } }, { 0x2037, // GetStdRefNumGS new Param[] { PARAM_COUNT, MISC2, REF_NUM } }, { 0x200f, // GetSysPrefsGS new Param[] { PARAM_COUNT, MISC2 } }, { 0x202a, // GetVersionGS new Param[] { PARAM_COUNT, MISC2 } }, { 0x2007, // JudgeNameGS new Param[] { PARAM_COUNT, MISC2, MISC2, BUF_PTR, MISC2, STRING_PTR, MISC2 } }, { 0x2011, // NewLine new Param[] { PARAM_COUNT, REF_NUM, MISC2, MISC2, BUF_PTR } }, { 0x200d, // NullGS new Param[] { PARAM_COUNT } }, { 0x2010, // OpenGS new Param[] { PARAM_COUNT, REF_NUM, STRING_PTR, ACCESS, MISC2, ACCESS, FILE_TYPE, AUX_TYPE, STORAGE_TYPE, DATE_TIME, DATE_TIME, BUF_PTR, FILE_OFFSET, MISC4, FILE_OFFSET, MISC4 } }, { 0x2003, // OSShutdownGS new Param[] { PARAM_COUNT, MISC2 } }, { 0x2029, // QuitGS new Param[] { PARAM_COUNT, STRING_PTR, MISC2 } }, { 0x2012, // ReadGS new Param[] { PARAM_COUNT, REF_NUM, BUF_PTR, MISC4, MISC4, MISC2 } }, { 0x2026, // ResetCacheGS new Param[] { PARAM_COUNT } }, { 0x201f, // SessionStatusGS new Param[] { PARAM_COUNT, MISC2 } }, { 0x2018, // SetEOFGS new Param[] { PARAM_COUNT, REF_NUM, MISC2, FILE_OFFSET } }, { 0x2005, // SetFileInfoGS new Param[] { PARAM_COUNT, STRING_PTR, ACCESS, FILE_TYPE, AUX_TYPE, MISC2, DATE_TIME, DATE_TIME, BUF_PTR, MISC4, MISC4, MISC4, MISC4 } }, { 0x201a, // SetLevelGS new Param[] { PARAM_COUNT, MISC2, MISC2 } }, { 0x2016, // SetMarkGS new Param[] { PARAM_COUNT, REF_NUM, MISC2, FILE_OFFSET } }, { 0x2009, // SetPrefixGS new Param[] { PARAM_COUNT, MISC2, STRING_PTR } }, { 0x203a, // SetStdRefNumGS new Param[] { PARAM_COUNT, MISC2, REF_NUM } }, { 0x200c, // SetSysPrefsGS new Param[] { PARAM_COUNT, MISC2 } }, { 0x2032, // UnbindIntGS new Param[] { PARAM_COUNT, MISC2 } }, { 0x2008, // VolumeGS new Param[] { PARAM_COUNT, STRING_PTR, STRING_PTR, MISC4, MISC4, MISC2, MISC2, MISC2, MISC2} }, { 0x2013, // WriteGS new Param[] { PARAM_COUNT, REF_NUM, BUF_PTR, MISC4, MISC4, MISC2 } }, // TODO: ProDOS 16 calls }; #endregion Parameter block defs private IApplication mAppRef; private byte[] mFileData; private Dictionary mFunctionList; private AddressTranslate mAddrTrans; public string Identifier { get { return "Apple IIgs GS/OS call handler"; } } public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) { mAppRef = appRef; mFileData = fileData; mAddrTrans = addrTrans; mAppRef.DebugLog("GSOS(id=" + AppDomain.CurrentDomain.Id + "): prepare()"); //System.Diagnostics.Debugger.Break(); } public void Unprepare() { mAppRef = null; mFileData = null; mAddrTrans = null; } public void UpdateSymbolList(List plSyms) { // Extract the list of function name constants from the platform symbol file. mFunctionList = PlSymbol.GeneratePlatformValueList(plSyms, GSOS_FUNC_TAG, mAppRef); } public bool IsLabelSignificant(string beforeLabel, string afterLabel) { return false; } public void CheckJsl(int offset, int operand, out bool noContinue) { const int INLINE_ENTRY = 0xe100a8; noContinue = false; if (offset + 7 < mFileData.Length && operand == INLINE_ENTRY) { // match! int req = Util.GetWord(mFileData, offset + 4, 2, false); int blockAddr = Util.GetWord(mFileData, offset + 6, 4, false); if (VERBOSE) { mAppRef.DebugLog("GSOS call detected at +" + offset.ToString("x6") + ", cmd=$" + req.ToString("x4") + " addr=$" + blockAddr.ToString("x6")); } PlSymbol sym; if (mFunctionList.TryGetValue(req, out sym)) { mAppRef.SetInlineDataFormat(offset + 4, 2, DataType.NumericLE, DataSubType.Symbol, sym.Label); } else { mAppRef.SetInlineDataFormat(offset + 4, 2, DataType.NumericLE, DataSubType.None, null); } mAppRef.SetInlineDataFormat(offset + 6, 4, DataType.NumericLE, DataSubType.Address, null); // Try to format parameter block. FormatParameterBlock(offset, req, blockAddr); // Check for P16_QUIT or QuitGS call. if (req == 0x0029 || req == 0x2029) { noContinue = true; } } } /// /// Attempts to format the parameter block that is passed into the GS/OS call. /// /// /// All "class 1" GS/OS calls have a parameter block that begins with a parameter /// count. The count indicates how many parameters are provided after the pCount /// field. This is a field count, not a byte count, and may be zero. /// /// Sometimes different calls will overlap, e.g GET_FILE_INFO and SET_FILE_INFO. This /// is generally only done when the parameters line up, so I'm not expecting things /// to come out weird, but it's possible. /// private void FormatParameterBlock(int offset, int req, int blockAddr) { Param[] parms; if (!ParamDescrs.TryGetValue(req, out parms)) { // We don't have a parameter list for this call. return; } int blockOff = mAddrTrans.AddressToOffset(offset, blockAddr); int paramCount; if (req < 0x0100) { // ProDOS-16 parameter blocks are fixed-length. paramCount = parms.Length; } else { // Confirm we can at least get the parameter count. if (!Util.IsInBounds(mFileData, blockOff, 2)) { return; } int pCount = Util.GetWord(mFileData, blockOff, 2, false); if (pCount >= parms.Length) { // Might be an uninitialized value. Just format the pCount. pCount = 1; } paramCount = pCount + 1; // add 1 for pCount itself } // Compute parameter block length in bytes. int blockLen = 0; for (int i = 0; i < paramCount; i++) { blockLen += parms[i].Length; } // Confirm that the entire thing fits in the file. if (!Util.IsInBounds(mFileData, blockOff, blockLen)) { return; } if (VERBOSE) { mAppRef.DebugLog("Formatting GS/OS call block at +" + blockOff.ToString("x6") + ", count=" + paramCount); } // Format each entry. for (int i = 0; i < paramCount; i++) { Param parm = parms[i]; mAppRef.SetInlineDataFormat(blockOff, parm.Length, parm.Type, parm.SubType, null); blockOff += parm.Length; } } } }