/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
#include "stdinc.h"
#include "nlmessages.h"
#include "stringlists.h"
#include "codechunks.h"
#include "entryaddress.h"
#include "invaddress.h"
#include "strutil.h"
#include "cmdarg.h"
#include "msg_level.h"
#include "dasmdef.h"
#include "cpulist.h"
#include "console.h"
#include "nls.h"
#include "version.h"
#include "das.rsc"
#include "deco68.h"
#include "deco87c800.h"
#include "deco4004.h"
#define TABSIZE 8
char *pEnvName = "DASCMD";
void WrCopyRight(const char *Msg)
{
printf("%s\n%s\n", Msg
, InfoMessCopyright
);
}
typedef void (*tChunkCallback)(const OneChunk *pChunk, Boolean IsData, void *pUser);
static void IterateChunks(tChunkCallback Callback, void *pUser)
{
Word NextCodeChunk, NextDataChunk;
const OneChunk *pChunk;
Boolean IsData;
NextCodeChunk = NextDataChunk = 0;
while ((NextCodeChunk < UsedCodeChunks.RealLen) || (NextDataChunk < UsedDataChunks.RealLen))
{
if (NextCodeChunk >= UsedCodeChunks.RealLen)
{
pChunk = UsedDataChunks.Chunks + (NextDataChunk++);
IsData = True;
}
else if (NextDataChunk >= UsedDataChunks.RealLen)
{
pChunk = UsedCodeChunks.Chunks + (NextCodeChunk++);
IsData = False;
}
else if (UsedDataChunks.Chunks[NextDataChunk].Start < UsedCodeChunks.Chunks[NextCodeChunk].Start)
{
pChunk = UsedDataChunks.Chunks + (NextDataChunk++);
IsData = True;
}
else
{
pChunk = UsedCodeChunks.Chunks + (NextCodeChunk++);
IsData = False;
}
Callback(pChunk, IsData, pUser);
}
}
typedef struct
{
FILE *pDestFile;
LargeWord Sum;
} tDumpIteratorData;
static void DumpIterator(const OneChunk *pChunk, Boolean IsData, void *pUser)
{
String Str;
tDumpIteratorData *pData = (tDumpIteratorData*)pUser;
HexString(Str, sizeof(Str), pChunk->Start, 0);
fprintf(pData
->pDestFile
, "\t\t; %s...", Str
);
HexString(Str, sizeof(Str), pChunk->Start + pChunk->Length - 1, 0);
fprintf(pData
->pDestFile
, "%s (%s)\n", Str
, IsData
? "data" :"code");
pData->Sum += pChunk->Length;
}
static void DumpChunks(const ChunkList *NChunk, FILE *pDestFile)
{
tDumpIteratorData Data;
String Str;
UNUSED(NChunk);
Data.pDestFile = pDestFile;
Data.Sum = 0;
fprintf(pDestFile
, "\t\t; disassembled area:\n");
IterateChunks(DumpIterator, &Data);
as_snprintf(Str, sizeof(Str), "\t\t; %lllu/%lllu bytes disassembled", Data.Sum, GetCodeChunksStored(&CodeChunks));
}
static int tabbedstrlen(const char *s)
{
int Result = 0;
for (; *s; s++)
{
if (*s == '\t')
Result += TABSIZE - (Result % TABSIZE);
else
Result++;
}
return Result;
}
static void PrTabs(FILE *pDestFile, int TargetLen, int ThisLen)
{
while (ThisLen < TargetLen)
{
ThisLen += TABSIZE - (ThisLen % TABSIZE);
}
}
static as_cmd_result_t ArgError(int MsgNum, const char *pArg)
{
if (pArg)
fprintf(stderr
, "%s\n", getmessage
(MsgNum
));
return e_cmd_err;
}
static as_cmd_result_t CMD_BinFile(Boolean Negate, const char *pArg)
{
LargeWord Start = 0, Len = 0, Gran = 1;
char *pStart = NULL, *pLen = NULL, *pGran = NULL;
String Arg;
Boolean OK;
tCodeChunk Chunk;
if (Negate || !*pArg)
return ArgError(Num_ErrMsgFileArgumentMissing, NULL);
strmaxcpy(Arg, pArg, sizeof(Arg));
if ((pStart
= strchr(Arg
, '@')))
{
*pStart++ = '\0';
if ((pLen
= strchr(pStart
, ',')))
{
*pLen++ = '\0';
if ((pGran
= strchr(pLen
, ',')))
*pGran++ = '\0';
}
}
if (pStart && *pStart)
{
Start = ConstLongInt(pStart, &OK, 10);
if (!OK)
return ArgError(Num_ErrMsgInvalidNumericValue, pStart);
}
else
Start = 0;
if (pLen && *pLen)
{
Len = ConstLongInt(pLen, &OK, 10);
if (!OK)
return ArgError(Num_ErrMsgInvalidNumericValue, pLen);
}
else
Len = 0;
if (pGran && *pGran)
{
Gran = ConstLongInt(pGran, &OK, 10);
if (!OK)
return ArgError(Num_ErrMsgInvalidNumericValue, pGran);
}
else
Gran = 1;
InitCodeChunk(&Chunk);
if (ReadCodeChunk(&Chunk, Arg, Start, Len, Gran))
return ArgError(Num_ErrMsgCannotReadBinaryFile, Arg);
MoveCodeChunkToList(&CodeChunks, &Chunk, TRUE);
return e_cmd_arg;
}
static void ResizeBuffer(Byte* *ppBuffer, LargeWord *pAllocLen, LargeWord ReqLen)
{
if (ReqLen > *pAllocLen)
{
Byte
*pNew
= *ppBuffer
? realloc(*ppBuffer
, ReqLen
) : malloc(ReqLen
);
if (pNew)
{
*ppBuffer = pNew;
*pAllocLen = ReqLen;
}
}
}
static Boolean GetByte(char* *ppLine, Byte *pResult)
{
if (!as_isxdigit(**ppLine))
return False;
*pResult
= isdigit(**ppLine
) ? (**ppLine
- '0') : (as_toupper
(**ppLine
) - 'A' + 10);
(*ppLine)++;
if (!as_isxdigit(**ppLine))
return False;
*pResult
= (*pResult
<< 4) | (isdigit(**ppLine
) ? (**ppLine
- '0') : (as_toupper
(**ppLine
) - 'A' + 10));
(*ppLine)++;
return True;
}
static void FlushChunk(tCodeChunk *pChunk)
{
pChunk->Granularity = 1;
pChunk->pLongCode = (LongWord*)pChunk->pCode;
pChunk->pWordCode = (Word*)pChunk->pCode;
MoveCodeChunkToList(&CodeChunks, pChunk, TRUE);
InitCodeChunk(pChunk);
}
/* ------------------------------------------------------- */
static Boolean write_version_exit, write_help_exit, write_cpu_list_exit;
static int screen_height = 0;
static void write_console_next(const char *p_line)
{
static int LineZ;
WrConsoleLine(p_line, True);
if (screen_height && (++LineZ >= screen_height))
{
LineZ = 0;
WrConsoleLine(getmessage(Num_KeyWaitMsg), False);
}
}
static as_cmd_result_t CMD_HexFile(Boolean Negate, const char *pArg)
{
FILE *pFile;
char Line[300], *pLine;
size_t Len;
Byte *pLineBuffer = NULL, *pDataBuffer = 0, RecordType, Tmp;
LargeWord LineBufferStart = 0, LineBufferAllocLen = 0, LineBufferLen = 0,
Sum;
tCodeChunk Chunk;
unsigned z;
if (Negate || !*pArg)
return ArgError(Num_ErrMsgFileArgumentMissing, NULL);
pFile
= fopen(pArg
, "r");
if (!pFile)
{
return ArgError(Num_ErrMsgCannotReadHexFile, pArg);
}
InitCodeChunk(&Chunk);
{
fgets(Line
, sizeof(Line
), pFile
);
if ((Len > 0) && (Line[Len -1] == '\n'))
Line[--Len] = '\0';
if ((Len > 0) && (Line[Len -1] == '\r'))
Line[--Len] = '\0';
if (*Line != ':')
continue;
Sum = 0;
pLine = Line + 1;
if (!GetByte(&pLine, &Tmp))
return ArgError(Num_ErrMsgInvalidHexData, pArg);
ResizeBuffer(&pLineBuffer, &LineBufferAllocLen, Tmp);
LineBufferLen = Tmp;
Sum += Tmp;
LineBufferStart = 0;
for (z = 0; z < 2; z++)
{
if (!GetByte(&pLine, &Tmp))
return ArgError(Num_ErrMsgInvalidHexData, pArg);
LineBufferStart = (LineBufferStart << 8) | Tmp;
Sum += Tmp;
}
if (!GetByte(&pLine, &RecordType))
return ArgError(Num_ErrMsgInvalidHexData, pArg);
Sum += RecordType;
if (RecordType != 0)
continue;
for (z = 0; z < LineBufferLen; z++)
{
if (!GetByte(&pLine, &pLineBuffer[z]))
return ArgError(Num_ErrMsgInvalidHexData, pArg);
Sum += pLineBuffer[z];
}
if (!GetByte(&pLine, &Tmp))
return ArgError(Num_ErrMsgInvalidHexData, pArg);
Sum += Tmp;
if (Sum & 0xff)
return ArgError(Num_ErrMsgHexDataChecksumError, pArg);
if (Chunk.Start + Chunk.Length == LineBufferStart)
{
ResizeBuffer(&Chunk.pCode, &Chunk.Length, Chunk.Length + LineBufferLen);
memcpy(&Chunk.
pCode[Chunk.
Length - LineBufferLen
], pLineBuffer
, LineBufferLen
);
}
else
{
if (Chunk.Length)
FlushChunk(&Chunk);
ResizeBuffer(&Chunk.pCode, &Chunk.Length, LineBufferLen);
memcpy(Chunk.
pCode, pLineBuffer
, LineBufferLen
);
Chunk.Start = LineBufferStart;
}
}
if (Chunk.Length)
FlushChunk(&Chunk);
if (pLineBuffer)
if (pDataBuffer)
return e_cmd_ok;
}
static as_cmd_result_t CMD_EntryAddress(Boolean Negate, const char *pArg)
{
LargeWord Address;
char *pName = NULL;
String Arg, Str;
Boolean OK;
if (Negate || !*pArg)
return ArgError(Num_ErrMsgAddressArgumentMissing, NULL);
strmaxcpy(Arg, pArg, sizeof(Arg));
if ((pName = ParenthPos(Arg, ',')))
*pName++ = '\0';
if (*Arg)
{
if (*Arg == '(')
{
Byte Vector[8];
char *pVectorAddress = NULL, *pAddrLen = NULL, *pEndianess = NULL;
LargeWord AddrLen, VectorAddress = 0, z;
Boolean VectorMSB;
int l;
pVectorAddress = Arg + 1;
if (pVectorAddress[l - 1] != ')')
return ArgError(Num_ErrMsgClosingPatentheseMissing, pVectorAddress);
pVectorAddress[l - 1] = '\0';
if ((pAddrLen
= strchr(pVectorAddress
, ',')))
{
*pAddrLen++ = '\0';
if ((pEndianess
= strchr(pAddrLen
, ',')))
*pEndianess++ = '\0';
}
if (pVectorAddress && *pVectorAddress)
{
VectorAddress = ConstLongInt(pVectorAddress, &OK, 10);
if (!OK)
return ArgError(Num_ErrMsgInvalidNumericValue, pVectorAddress);
}
else
pVectorAddress = 0;
if (pAddrLen && *pAddrLen)
{
AddrLen = ConstLongInt(pAddrLen, &OK, 10);
if (!OK || (AddrLen > sizeof(Vector)))
return ArgError(Num_ErrMsgInvalidNumericValue, pAddrLen);
}
else
AddrLen = 1;
if (pEndianess && *pEndianess)
{
if (!as_strcasecmp(pEndianess, "MSB"))
VectorMSB = True;
else if (!as_strcasecmp(pEndianess, "LSB"))
VectorMSB = False;
else
return ArgError(Num_ErrMsgInvalidEndinaness, pEndianess);
}
else
VectorMSB = True; /* TODO: depend on CPU */
if (!RetrieveCodeFromChunkList(&CodeChunks, VectorAddress, Vector, AddrLen))
return ArgError(Num_ErrMsgCannotRetrieveEntryAddressData, NULL);
Address = 0;
for (z = 0; z < AddrLen; z++)
{
Address <<= 8;
Address |= VectorMSB ? Vector[z] : Vector[AddrLen - 1 - z];
}
as_snprintf(Str, sizeof Str, "indirect address @ %lllx -> 0x%lllx", VectorAddress, Address);
AddChunk(&UsedDataChunks, VectorAddress, AddrLen, True);
if (pName && *pName)
{
String Str;
as_snprintf(Str, sizeof(Str), "Vector_2_%s", pName);
AddInvSymbol(Str, VectorAddress);
}
}
else
{
Address = ConstLongInt(pArg, &OK, 10);
if (!OK)
return ArgError(Num_ErrMsgInvalidNumericValue, pArg);
}
}
else
Address = 0;
if (pName && *pName)
AddInvSymbol(pName, Address);
AddEntryAddress(Address);
return e_cmd_arg;
}
static as_cmd_result_t CMD_Symbol(Boolean Negate, const char *pArg)
{
LargeWord Address;
char *pName = NULL;
String Arg;
Boolean OK;
if (Negate || !*pArg)
return ArgError(Num_ErrMsgSymbolArgumentMissing, NULL);
strmaxcpy(Arg, pArg, sizeof(Arg));
if ((pName
= strchr(Arg
, '=')))
*pName++ = '\0';
if (*Arg)
{
Address = ConstLongInt(Arg, &OK, 10);
if (!OK)
return ArgError(Num_ErrMsgInvalidNumericValue, Arg);
}
else
Address = 0;
if (pName && *pName)
AddInvSymbol(pName, Address);
return e_cmd_arg;
}
static as_cmd_result_t CMD_CPU(Boolean Negate, const char *pArg)
{
const tCPUDef *pCPUDef;
if (Negate || !*pArg)
return ArgError(Num_ErrMsgCPUArgumentMissing, NULL);
if (!as_strcasecmp(pArg, "?") || !as_strcasecmp(pArg, "LIST"))
{
write_cpu_list_exit = True;
return e_cmd_ok;
}
pCPUDef = LookupCPUDefByName(pArg);
if (!pCPUDef)
return ArgError(Num_ErrMsgUnknownCPU, pArg);
pCPUDef->SwitchProc(pCPUDef->pUserData);
return e_cmd_arg;
}
static as_cmd_result_t CMD_HexLowerCase(Boolean Negate, const char *Arg)
{
UNUSED(Arg);
HexStartCharacter = Negate ? 'A' : 'a';
return e_cmd_ok;
}
static as_cmd_result_t CMD_PrintVersion(Boolean Negate, const char *Arg)
{
UNUSED(Arg);
if (Negate)
return e_cmd_err;
write_version_exit = True;
return e_cmd_ok;
}
static as_cmd_result_t CMD_PrintHelp(Boolean Negate, const char *Arg)
{
UNUSED(Arg);
if (Negate)
return e_cmd_err;
write_help_exit = True;
return e_cmd_ok;
}
static as_cmd_result_t CMD_screen_height(Boolean negate, const char *p_arg)
{
Boolean ok;
int new_screen_height;
if (negate)
{
screen_height = 0;
return e_cmd_ok;
}
new_screen_height = ConstLongInt(p_arg, &ok, 10);
if (!ok)
return e_cmd_err;
screen_height = new_screen_height;
return e_cmd_arg;
}
static as_cmd_rec_t DASParams[] =
{
{ "CPU" , CMD_CPU },
{ "BINFILE" , CMD_BinFile },
{ "HEXFILE" , CMD_HexFile },
{ "ENTRYADDRESS" , CMD_EntryAddress },
{ "SCREENHEIGHT" , CMD_screen_height },
{ "SYMBOL" , CMD_Symbol },
{ "h" , CMD_HexLowerCase },
{ "HELP" , CMD_PrintHelp },
{ "VERSION" , CMD_PrintVersion }
};
typedef struct
{
FILE *pDestFile;
int MaxSrcLineLen, MaxLabelLen;
} tDisasmData;
static void DisasmIterator(const OneChunk *pChunk, Boolean IsData, void *pUser)
{
LargeWord Address;
char NumString[50];
tDisassInfo Info;
tDisasmData *pData = (tDisasmData*)pUser;
const char *pLabel;
Byte Code[100];
unsigned z;
int DataSize = -1;
Address = pChunk->Start;
HexString(NumString, sizeof(NumString), Address, 0);
PrTabs(pData->pDestFile, pData->MaxLabelLen, 0);
fprintf(pData
->pDestFile
, "org\t$%s\n", NumString
);
while (Address < pChunk->Start + pChunk->Length)
{
pLabel = LookupInvSymbol(Address);
if (pLabel
&& !strncmp(pLabel
, "Vector_", 7) && IsData
)
{
String Num;
char *pEnd
= strchr(pLabel
+ 7, '_');
if (pEnd)
{
int l = pEnd - (pLabel + 7);
Num[l] = '\0';
DataSize
= strtol(Num
, &pEnd
, 10);
if (*pEnd)
DataSize = -1;
}
}
Disassemble(Address, &Info, IsData, DataSize);
if (Info.pRemark)
{
PrTabs(pData->pDestFile, pData->MaxLabelLen, 0);
fprintf(pData
->pDestFile
, "; %s\n", Info.
pRemark);
}
if (pLabel)
{
fprintf(pData
->pDestFile
, "%s:", pLabel
);
PrTabs(pData->pDestFile, pData->MaxLabelLen, tabbedstrlen(pLabel) + 1);
}
else
PrTabs(pData->pDestFile, pData->MaxLabelLen, 0);
fprintf(pData
->pDestFile
, "%s", Info.
SrcLine);
PrTabs(pData->pDestFile, pData->MaxSrcLineLen, tabbedstrlen(Info.SrcLine));
RetrieveCodeFromChunkList(&CodeChunks, Address, Code, Info.CodeLen);
for (z = 0; z < Info.CodeLen; z++)
{
HexString(NumString, sizeof(NumString), Code[z], 2);
fprintf(pData
->pDestFile
, " %s", NumString
);
}
fputc('\n', pData
->pDestFile
);
Address += Info.CodeLen;
}
}
int main(int argc, char **argv)
{
LargeWord Address, NextAddress;
Boolean NextAddressValid;
tDisassInfo Info;
unsigned z;
tDisasmData Data;
int ThisSrcLineLen;
as_cmd_results_t cmd_results;
strutil_init();
nls_init();
NLS_Initialize(&argc, argv);
dasmdef_init();
cpulist_init();
msg_level_init();
nlmessages_init("das.msg", *argv, MsgId1, MsgId2);
deco68_init();
deco87c800_init();
deco4004_init();
write_version_exit = write_help_exit = write_cpu_list_exit = False;
as_cmd_register(DASParams, as_array_size(DASParams));
if (e_cmd_err == as_cmd_process(argc, argv, pEnvName, &cmd_results))
{
fprintf(stderr
, "%s%s\n", getmessage
(cmd_results.
error_arg_in_env ? Num_ErrMsgInvEnvParam
: Num_ErrMsgInvParam
), cmd_results.
error_arg);
}
if ((msg_level >= e_msg_level_verbose) || write_version_exit)
{
String Ver;
as_snprintf(Ver, sizeof(Ver), "DAS V%s", Version);
WrCopyRight(Ver);
}
if (write_help_exit)
{
char *ph1, *ph2;
String Tmp;
as_snprintf(Tmp, sizeof(Tmp), "%s%s%s", getmessage(Num_InfoMessHead1), as_cmdarg_get_executable_name(), getmessage(Num_InfoMessHead2));
write_console_next(Tmp);
for (ph1
= getmessage
(Num_InfoMessHelp
), ph2
= strchr(ph1
, '\n'); ph2
; ph1
= ph2
+ 1, ph2
= strchr(ph1
, '\n'))
{
*ph2 = '\0';
write_console_next(ph1);
*ph2 = '\n';
}
}
if (write_cpu_list_exit)
{
printf("%s\n", getmessage
(Num_InfoMessCPUList
));
PrintCPUList(write_console_next);
}
if (write_version_exit || write_help_exit || write_cpu_list_exit)
if (!Disassemble)
{
fprintf(stderr
, "no CPU set, aborting\n");
}
/* walk through code */
NextAddress = 0;
NextAddressValid = False;
Data.MaxSrcLineLen = 0;
while (EntryAddressAvail())
{
Address = GetEntryAddress(NextAddressValid, NextAddress);
Disassemble(Address, &Info, False, -1);
AddChunk(&UsedCodeChunks, Address, Info.CodeLen, True);
if ((ThisSrcLineLen = tabbedstrlen(Info.SrcLine)) > Data.MaxSrcLineLen)
Data.MaxSrcLineLen = ThisSrcLineLen;
for (z = 0; z < Info.NextAddressCount; z++)
if (!AddressInChunk(&UsedCodeChunks, Info.NextAddresses[z]))
AddEntryAddress(Info.NextAddresses[z]);
NextAddress = Address + Info.CodeLen;
NextAddressValid = True;
}
/* round up src line & symbol length to next multiple of tabs */
Data.MaxSrcLineLen += TABSIZE - (Data.MaxSrcLineLen % TABSIZE);
Data.MaxLabelLen = GetMaxInvSymbolNameLen() + 1;
Data.MaxLabelLen += TABSIZE - (Data.MaxLabelLen % TABSIZE);
Data.pDestFile = stdout;
/* bring areas into order */
SortChunks(&UsedCodeChunks);
SortChunks(&UsedDataChunks);
/* dump them out */
IterateChunks(DisasmIterator, &Data);
/* summary */
DumpChunks(&UsedCodeChunks, Data.pDestFile);
return 0;
}