/* common */
+#include "hashfunc.h"
+#include "hashtab.h"
#include "xmalloc.h"
/* ca65 */
+#include "global.h"
#include "objfile.h"
#include "segment.h"
#include "span.h"
+#include "spool.h"
+
+
+
+/*****************************************************************************/
+/* Forwards */
+/*****************************************************************************/
+
+
+
+static unsigned HT_GenHash (const void* Key);
+/* Generate the hash over a key. */
+
+static const void* HT_GetKey (const void* Entry);
+/* Given a pointer to the user entry data, return a pointer to the key */
+
+static int HT_Compare (const void* Key1, const void* Key2);
+/* Compare two keys. The function must return a value less than zero if
+ * Key1 is smaller than Key2, zero if both are equal, and a value greater
+ * than zero if Key1 is greater then Key2.
+ */
+/* Hash table functions */
+static const HashFunctions HashFunc = {
+ HT_GenHash,
+ HT_GetKey,
+ HT_Compare
+};
+
+/* Span hash table */
+static HashTable SpanTab = STATIC_HASHTABLE_INITIALIZER (1051, &HashFunc);
+
+
+
+/*****************************************************************************/
+/* Hash table functions */
+/*****************************************************************************/
+
+
+
+static unsigned HT_GenHash (const void* Key)
+/* Generate the hash over a key. */
+{
+ /* Key is a Span pointer */
+ const Span* S = Key;
+
+ /* Hash over a combination of segment number, start and end */
+ return HashInt ((S->Seg->Num << 28) ^ (S->Start << 14) ^ S->End);
+}
+
+
+
+static const void* HT_GetKey (const void* Entry)
+/* Given a pointer to the user entry data, return a pointer to the key */
+{
+ return Entry;
+}
+
+
+
+static int HT_Compare (const void* Key1, const void* Key2)
+/* Compare two keys. The function must return a value less than zero if
+ * Key1 is smaller than Key2, zero if both are equal, and a value greater
+ * than zero if Key1 is greater then Key2.
+ */
+{
+ /* Convert both parameters to Span pointers */
+ const Span* S1 = Key1;
+ const Span* S2 = Key2;
+
+ /* Compare segment number, then start and end */
+ int Res = (int)S2->Seg->Num - (int)S1->Seg->Num;
+ if (Res == 0) {
+ Res = (int)S2->Start - (int)S1->Start;
+ if (Res == 0) {
+ Res = (int)S2->End - (int)S1->End;
+ }
+ }
+
+ /* Done */
+ return Res;
+}
+
+
+
/*****************************************************************************/
/* Code */
/*****************************************************************************/
Span* S = xmalloc (sizeof (Span));
/* Initialize the struct */
+ InitHashNode (&S->Node);
+ S->Id = ~0U;
S->Seg = Seg;
S->Start = Start;
S->End = End;
+ S->Type = EMPTY_STRING_ID;
/* Return the new struct */
return S;
-void OpenSpans (Collection* Spans)
+static Span* MergeSpan (Span* S)
+/* Check if we have a span with the same data as S already. If so, free S and
+ * return the already existing one. If not, remember S and return it.
+ */
+{
+ /* Check if we have such a span already. If so use the existing
+ * one and free the one from the collection. If not, add the one to
+ * the hash table and return it.
+ */
+ Span* E = HT_Find (&SpanTab, S);
+ if (E) {
+ /* If S has a type and E not, move the type */
+ if (S->Type != EMPTY_STRING_ID) {
+ CHECK (E->Type == EMPTY_STRING_ID);
+ E->Type = S->Type;
+ }
+
+ /* Free S and return E */
+ FreeSpan (S);
+ return E;
+ } else {
+ /* Assign the id, insert S, then return it */
+ S->Id = HT_GetCount (&SpanTab);
+ HT_Insert (&SpanTab, S);
+ return S;
+ }
+}
+
+
+
+void SetSpanType (Span* S, const StrBuf* Type)
+/* Set the generic type of the span to Type */
+{
+ /* Ignore the call if we won't generate debug infos */
+ if (DbgSyms) {
+ S->Type = GetStrBufId (Type);
+ }
+}
+
+
+
+Span* OpenSpan (void)
+/* Open a span for the active segment and return it. */
+{
+ return NewSpan (ActiveSeg, ActiveSeg->PC, ActiveSeg->PC);
+}
+
+
+
+Span* CloseSpan (Span* S)
+/* Close the given span. Be sure to replace the passed span by the one
+ * returned, since the span will get deleted if it is empty or may be
+ * replaced if a duplicate exists.
+ */
+{
+ /* Set the end offset */
+ if (S->Start == S->Seg->PC) {
+ /* Span is empty */
+ FreeSpan (S);
+ return 0;
+ } else {
+ /* Span is not empty */
+ S->End = S->Seg->PC;
+
+ /* Check if we have such a span already. If so use the existing
+ * one and free the one from the collection. If not, add the one to
+ * the hash table and return it.
+ */
+ return MergeSpan (S);
+ }
+}
+
+
+
+void OpenSpanList (Collection* Spans)
/* Open a list of spans for all existing segments to the given collection of
* spans. The currently active segment will be inserted first with all others
* following.
-void CloseSpans (Collection* Spans)
+void CloseSpanList (Collection* Spans)
/* Close a list of spans. This will add new segments to the list, mark the end
* of existing ones, and remove empty spans from the list.
*/
} else {
/* Span is not empty */
S->End = S->Seg->PC;
- CollReplace (Spans, S, J++);
+
+ /* Merge duplicate spans, then insert it at the new position */
+ CollReplace (Spans, MergeSpan (S), J++);
}
}
-void WriteSpans (const Collection* Spans)
+void WriteSpanList (const Collection* Spans)
/* Write a list of spans to the output file */
{
unsigned I;
- /* Write the number of spans */
- ObjWriteVar (CollCount (Spans));
+ /* We only write spans if debug info is enabled */
+ if (DbgSyms == 0) {
+ /* Number of spans is zero */
+ ObjWriteVar (0);
+ } else {
+ /* Write the number of spans */
+ ObjWriteVar (CollCount (Spans));
+
+ /* Write the spans */
+ for (I = 0; I < CollCount (Spans); ++I) {
+ /* Write the id of the next span */
+ ObjWriteVar (((const Span*)CollConstAt (Spans, I))->Id);
+ }
+ }
+}
- /* Write the spans */
- for (I = 0; I < CollCount (Spans); ++I) {
- /* Get next range */
- const Span* S = CollConstAt (Spans, I);
- /* Write data for th span We will write the size instead of the end
- * offset to save some bytes, since most spans are expected to be
- * rather small.
- */
- ObjWriteVar (S->Seg->Num);
- ObjWriteVar (S->Start);
- ObjWriteVar (S->End - S->Start);
- }
+static int CollectSpans (void* Entry, void* Data)
+/* Collect all spans in a collection sorted by id */
+{
+ /* Cast the pointers to real objects */
+ Span* S = Entry;
+ Collection* C = Data;
+
+ /* Place the entry into the collection */
+ CollReplaceExpand (C, S, S->Id);
+
+ /* Keep the span */
+ return 0;
}
+
+
+void WriteSpans (void)
+/* Write all spans to the object file */
+{
+ /* Tell the object file module that we're about to start the spans */
+ ObjStartSpans ();
+
+ /* We will write scopes only if debug symbols are requested */
+ if (DbgSyms) {
+
+ unsigned I;
+ /* We must first collect all items in a collection sorted by id */
+ Collection SpanList = STATIC_COLLECTION_INITIALIZER;
+ CollGrow (&SpanList, HT_GetCount (&SpanTab));
+ /* Walk over the hash table and fill the span list */
+ HT_Walk (&SpanTab, CollectSpans, &SpanList);
+ /* Write the span count to the file */
+ ObjWriteVar (CollCount (&SpanList));
+
+ /* Write all spans */
+ for (I = 0; I < CollCount (&SpanList); ++I) {
+
+ /* Get the span and check it */
+ const Span* S = CollAtUnchecked (&SpanList, I);
+ CHECK (S->End > S->Start);
+
+ /* Write data for the span We will write the size instead of the
+ * end offset to save some bytes, since most spans are expected
+ * to be rather small.
+ */
+ ObjWriteVar (S->Seg->Num);
+ ObjWriteVar (S->Start);
+ ObjWriteVar (S->End - S->Start);
+ ObjWriteVar (S->Type);
+ }
+
+ /* Free the collection with the spans */
+ DoneCollection (&SpanList);
+
+ } else {
+
+ /* No debug info requested */
+ ObjWriteVar (0);
+
+ }
+
+ /* Done writing the spans */
+ ObjEndSpans ();
+}