/* * 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; /* JSR $BF00 DFB command_code DW parm_block parm_block dfb parm_count parameters... */ namespace RuntimeData.Apple { public class ProDOS8 : MarshalByRefObject, IPlugin, IPlugin_SymbolList, IPlugin_InlineJsr { private const string P8_MLI_TAG = "ProDOS8-MLI-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, 1); private static Param PATHNAME = new Param(DataType.NumericLE, DataSubType.Address, 2); private static Param BUFFER = new Param(DataType.NumericLE, DataSubType.Address, 2); private static Param CODEPTR = new Param(DataType.NumericLE, DataSubType.Address, 2); private static Param ACCESS = new Param(DataType.NumericLE, DataSubType.Hex, 1); private static Param FILE_TYPE = new Param(DataType.NumericLE, DataSubType.Hex, 1); private static Param AUX_TYPE = new Param(DataType.NumericLE, DataSubType.Hex, 2); private static Param STORAGE_TYPE = new Param(DataType.NumericLE, DataSubType.Hex, 1); private static Param DATE = new Param(DataType.NumericLE, DataSubType.Hex, 2); private static Param TIME = new Param(DataType.NumericLE, DataSubType.Hex, 2); private static Param BLOCKS_USED = new Param(DataType.NumericLE, DataSubType.Hex, 2); private static Param UNIT_NUM = new Param(DataType.NumericLE, DataSubType.Hex, 1); private static Param REF_NUM = new Param(DataType.NumericLE, DataSubType.Decimal, 1); private static Param COUNT = new Param(DataType.NumericLE, DataSubType.Hex, 2); private static Param BLOCK_NUM = new Param(DataType.NumericLE, DataSubType.Hex, 2); private static Param POSITION = new Param(DataType.NumericLE, DataSubType.Hex, 3); private static Param MISC1 = new Param(DataType.NumericLE, DataSubType.Hex, 1); private static Param MISC2 = new Param(DataType.NumericLE, DataSubType.Hex, 2); private static Param NULL3 = new Param(DataType.NumericLE, DataSubType.Hex, 3); private Dictionary ParamDescrs = new Dictionary() { { 0x40, // ALLOC_INTERRUPT new Param[] { PARAM_COUNT, MISC1, CODEPTR } }, { 0x41, // DEALLOC_INTERRUPT new Param[] { PARAM_COUNT, MISC1 } }, { 0x65, // QUIT new Param[] { PARAM_COUNT, MISC1, MISC2, MISC1, MISC2 } }, { 0x80, // READ_BLOCK new Param[] { PARAM_COUNT, UNIT_NUM, BUFFER, BLOCK_NUM } }, { 0x81, // WRITE_BLOCK new Param[] { PARAM_COUNT, UNIT_NUM, BUFFER, BLOCK_NUM } }, // 0x82 GET_TIME has no parameter list { 0xc0, // CREATE new Param[] { PARAM_COUNT, PATHNAME, ACCESS, FILE_TYPE, AUX_TYPE, STORAGE_TYPE, DATE, TIME } }, { 0xc1, // DESTROY new Param[] { PARAM_COUNT, PATHNAME } }, { 0xc2, // RENAME new Param[] { PARAM_COUNT, PATHNAME, PATHNAME } }, { 0xc3, // SET_FILE_INFO new Param[] { PARAM_COUNT, PATHNAME, ACCESS, FILE_TYPE, AUX_TYPE, NULL3, DATE, TIME } }, { 0xc4, // GET_FILE_INFO new Param[] { PARAM_COUNT, PATHNAME, ACCESS, FILE_TYPE, AUX_TYPE, STORAGE_TYPE, BLOCKS_USED, DATE, TIME, DATE, TIME } }, { 0xc5, // ON_LINE new Param[] { PARAM_COUNT, UNIT_NUM, BUFFER } }, { 0xc6, // SET_PREFIX new Param[] { PARAM_COUNT, PATHNAME } }, { 0xc7, // GET_PREFIX new Param[] { PARAM_COUNT, PATHNAME } }, { 0xc8, // OPEN new Param[] { PARAM_COUNT, PATHNAME, BUFFER, REF_NUM } }, { 0xc9, // NEWLINE new Param[] { PARAM_COUNT, REF_NUM, MISC1, MISC1 } }, { 0xca, // READ new Param[] { PARAM_COUNT, REF_NUM, BUFFER, COUNT, COUNT } }, { 0xcb, // WRITE new Param[] { PARAM_COUNT, REF_NUM, BUFFER, COUNT, COUNT } }, { 0xcc, // CLOSE new Param[] { PARAM_COUNT, REF_NUM } }, { 0xcd, // FLUSH new Param[] { PARAM_COUNT, REF_NUM } }, { 0xce, // SET_MARK new Param[] { PARAM_COUNT, REF_NUM, POSITION } }, { 0xcf, // GET_MARK new Param[] { PARAM_COUNT, REF_NUM, POSITION } }, { 0xd0, // SET_EOF new Param[] { PARAM_COUNT, REF_NUM, POSITION } }, { 0xd1, // GET_EOF new Param[] { PARAM_COUNT, REF_NUM, POSITION } }, { 0xd2, // SET_BUF new Param[] { PARAM_COUNT, REF_NUM, BUFFER } }, { 0xd3, // GET_BUF new Param[] { PARAM_COUNT, REF_NUM, BUFFER } }, }; #endregion Parameter block defs private IApplication mAppRef; private byte[] mFileData; private Dictionary mFunctionList; private AddressTranslate mAddrTrans; public string Identifier { get { return "Apple II ProDOS 8 MLI call handler"; } } public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) { mAppRef = appRef; mFileData = fileData; mAddrTrans = addrTrans; mAppRef.DebugLog("ProDOS(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, P8_MLI_TAG, mAppRef); } public bool IsLabelSignificant(string beforeLabel, string afterLabel) { return false; } public void CheckJsr(int offset, int operand, out bool noContinue) { const int MLI_ENTRY = 0xbf00; noContinue = false; if (offset + 6 < mFileData.Length && operand == MLI_ENTRY) { // match! byte req = mFileData[offset + 3]; int blockAddr = Util.GetWord(mFileData, offset + 4, 2, false); if (VERBOSE) { mAppRef.DebugLog("P8 MLI call detected at +" + offset.ToString("x6") + ", cmd=$" + req.ToString("x2") + " addr=$" + blockAddr.ToString("x4")); } PlSymbol sym; if (mFunctionList.TryGetValue(req, out sym)) { mAppRef.SetInlineDataFormat(offset + 3, 1, DataType.NumericLE, DataSubType.Symbol, sym.Label); } else { mAppRef.SetInlineDataFormat(offset + 3, 1, DataType.NumericLE, DataSubType.None, null); } mAppRef.SetInlineDataFormat(offset + 4, 2, DataType.NumericLE, DataSubType.Address, null); Param[] parms; if (ParamDescrs.TryGetValue(req, out parms)) { // Try to format the parameter block. Start by figuring out how long it is. int blockLen = 0; foreach (Param parm in parms) { blockLen += parm.Length; } // Locate it and verify that the entire thing fits in the file. int blockOff = mAddrTrans.AddressToOffset(offset, blockAddr); if (Util.IsInBounds(mFileData, blockOff, blockLen)) { if (VERBOSE) { mAppRef.DebugLog("Formatting P8 block at +" + blockOff.ToString("x6")); } foreach (Param parm in parms) { // We could try to dereference pathname buffers to see if it's a // fixed value and not an empty buffer, but it's hard for us to // reliably tell the difference between a length-limited pathname // and junk. If the length byte is bad, we run the risk of lumping // a bunch of stuff into the pathname buffer. mAppRef.SetInlineDataFormat(blockOff, parm.Length, parm.Type, parm.SubType, null); blockOff += parm.Length; } } } if (req == 0x65) { // QUIT call noContinue = true; } } } } }