1 /*****************************************************************************/
5 /* Code segment structure */
9 /* (C) 2001 Ullrich von Bassewitz */
11 /* D-70597 Stuttgart */
12 /* EMail: uz@musoftware.de */
15 /* This software is provided 'as-is', without any expressed or implied */
16 /* warranty. In no event will the authors be held liable for any damages */
17 /* arising from the use of this software. */
19 /* Permission is granted to anyone to use this software for any purpose, */
20 /* including commercial applications, and to alter it and redistribute it */
21 /* freely, subject to the following restrictions: */
23 /* 1. The origin of this software must not be misrepresented; you must not */
24 /* claim that you wrote the original software. If you use this software */
25 /* in a product, an acknowledgment in the product documentation would be */
26 /* appreciated but is not required. */
27 /* 2. Altered source versions must be plainly marked as such, and must not */
28 /* be misrepresented as being the original software. */
29 /* 3. This notice may not be removed or altered from any source */
32 /*****************************************************************************/
57 /*****************************************************************************/
59 /*****************************************************************************/
63 static CodeLabel* NewCodeSegLabel (CodeSeg* S, const char* Name, unsigned Hash)
64 /* Create a new label and insert it into the label hash table */
66 /* Not found - create a new one */
67 CodeLabel* L = NewCodeLabel (Name, Hash);
69 /* Enter the label into the hash table */
70 L->Next = S->LabelHash[L->Hash];
71 S->LabelHash[L->Hash] = L;
73 /* Return the new label */
79 static const char* SkipSpace (const char* S)
80 /* Skip white space and return an updated pointer */
82 while (IsSpace (*S)) {
90 static const char* ReadToken (const char* L, const char* Term,
91 char* Buf, unsigned BufSize)
92 /* Read the next token into Buf, return the updated line pointer. The
93 * token is terminated by one of the characters given in term.
96 /* Read/copy the token */
98 unsigned ParenCount = 0;
99 while (*L && (ParenCount > 0 || strchr (Term, *L) == 0)) {
105 } else if (*L == '(') {
111 /* Terminate the buffer contents */
114 /* Return the updated line pointer */
120 static CodeEntry* ParseInsn (CodeSeg* S, const char* L)
121 /* Parse an instruction nnd generate a code entry from it. If the line contains
122 * errors, output an error message and return NULL.
123 * For simplicity, we don't accept the broad range of input a "real" assembler
124 * does. The instruction and the argument are expected to be separated by
125 * white space, for example.
130 am_t AM = 0; /* Initialize to keep gcc silent */
137 L = ReadToken (L, " \t", Mnemo, sizeof (Mnemo));
139 /* Try to find the opcode description for the mnemonic */
140 OPC = FindOpcode (Mnemo);
142 /* If we didn't find the opcode, print an error and bail out */
144 Error ("ASM code error: %s is not a valid mnemonic", Mnemo);
148 /* Skip separator white space */
151 /* Get the addressing mode */
162 StrCopy (Expr, sizeof (Expr), L+1);
168 L = ReadToken (L+1, ",)", Expr, sizeof (Expr));
170 /* Check for errors */
172 Error ("ASM code error: syntax error");
176 /* Check the different indirect modes */
178 /* Expect zp x indirect */
180 if (toupper (*L) != 'X') {
181 Error ("ASM code error: `X' expected");
186 Error ("ASM code error: `)' expected");
191 Error ("ASM code error: syntax error");
195 } else if (*L == ')') {
196 /* zp indirect or zp indirect, y */
200 if (toupper (*L) != 'Y') {
201 Error ("ASM code error: `Y' expected");
206 Error ("ASM code error: syntax error");
210 } else if (*L == '\0') {
213 Error ("ASM code error: syntax error");
229 /* Absolute, maybe indexed */
230 L = ReadToken (L, ",", Expr, sizeof (Expr));
232 /* Assume absolute */
234 } else if (*L == ',') {
238 Error ("ASM code error: syntax error");
245 } else if (Reg == 'Y') {
248 Error ("ASM code error: syntax error");
252 Error ("ASM code error: syntax error");
261 /* If the instruction is a branch, check for the label and generate it
262 * if it does not exist.
265 if ((OPC->Info & CI_MASK_BRA) == CI_BRA) {
269 /* ### Check for local labels here */
270 CHECK (AM == AM_ABS);
272 Hash = HashStr (Expr) % CS_LABEL_HASH_SIZE;
273 Label = FindCodeLabel (S, Expr, Hash);
275 /* Generate a new label */
276 Label = NewCodeSegLabel (S, Expr, Hash);
280 /* We do now have the addressing mode in AM. Allocate a new CodeEntry
281 * structure and initialize it.
283 E = NewCodeEntry (OPC, AM, Label);
284 if (Expr[0] != '\0') {
285 /* We have an additional expression */
286 E->Arg.Expr = xstrdup (Expr);
289 /* Return the new code entry */
295 CodeSeg* NewCodeSeg (const char* Name)
296 /* Create a new code segment, initialize and return it */
300 /* Allocate memory */
301 CodeSeg* S = xmalloc (sizeof (CodeSeg));
303 /* Initialize the fields */
304 S->Name = xstrdup (Name);
305 InitCollection (&S->Entries);
306 InitCollection (&S->Labels);
307 for (I = 0; I < sizeof(S->LabelHash) / sizeof(S->LabelHash[0]); ++I) {
311 /* Return the new struct */
317 void FreeCodeSeg (CodeSeg* S)
318 /* Free a code segment including all code entries */
325 /* Free the entries */
326 Count = CollCount (&S->Entries);
327 for (I = 0; I < Count; ++I) {
328 FreeCodeEntry (CollAt (&S->Entries, I));
331 /* Free the collections */
332 DoneCollection (&S->Entries);
333 DoneCollection (&S->Labels);
335 /* Free all labels */
336 for (I = 0; I < sizeof(S->LabelHash) / sizeof(S->LabelHash[0]); ++I) {
337 CodeLabel* L = S->LabelHash[I];
345 /* Free the struct */
351 void AddCodeSegLine (CodeSeg* S, const char* Format, ...)
352 /* Add a line to the given code segment */
358 /* Format the line */
361 va_start (ap, Format);
362 xvsprintf (Buf, sizeof (Buf), Format, ap);
365 /* Skip whitespace */
368 /* Check which type of instruction we have */
369 E = 0; /* Assume no insn created */
373 /* Empty line, just ignore it */
377 /* Comment or hint, ignore it for now */
381 /* Control instruction */
382 ReadToken (L, " \t", Token, sizeof (Token));
383 Error ("ASM code error: Pseudo instruction `%s' not supported", Token);
387 E = ParseInsn (S, L);
391 /* If we have a code entry, transfer the labels and insert it */
394 /* Transfer the labels if we have any */
396 unsigned LabelCount = CollCount (&S->Labels);
397 for (I = 0; I < LabelCount; ++I) {
399 CodeLabel* L = CollAt (&S->Labels, I);
400 /* Mark it as defined */
402 /* Move it to the code entry */
403 CollAppend (&E->Labels, L);
406 /* Delete the transfered labels */
407 CollDeleteAll (&S->Labels);
409 /* Add the entry to the list of code entries in this segment */
410 CollAppend (&S->Entries, E);
417 CodeLabel* AddCodeLabel (CodeSeg* S, const char* Name)
418 /* Add a code label for the next instruction to follow */
420 /* Calculate the hash from the name */
421 unsigned Hash = HashStr (Name) % CS_LABEL_HASH_SIZE;
423 /* Try to find the code label if it does already exist */
424 CodeLabel* L = FindCodeLabel (S, Name, Hash);
426 /* Did we find it? */
428 /* We found it - be sure it does not already have an owner */
429 CHECK (L->Owner == 0);
431 /* Not found - create a new one */
432 L = NewCodeSegLabel (S, Name, Hash);
435 /* We do now have a valid label. Remember it for later */
436 CollAppend (&S->Labels, L);
438 /* Return the label */
444 void AddExtCodeLabel (CodeSeg* S, const char* Name)
445 /* Add an external code label for the next instruction to follow */
447 /* Add the code label */
448 CodeLabel* L = AddCodeLabel (S, Name);
450 /* Mark it as external label */
456 void AddLocCodeLabel (CodeSeg* S, const char* Name)
457 /* Add a local code label for the next instruction to follow */
459 /* Add the code label */
460 AddCodeLabel (S, Name);
465 void AddCodeSegHint (CodeSeg* S, unsigned Hint)
466 /* Add a hint for the preceeding instruction */
470 /* Get the number of entries in this segment */
471 unsigned EntryCount = CollCount (&S->Entries);
473 /* Must have at least one entry */
474 CHECK (EntryCount > 0);
476 /* Get the last entry */
477 E = CollAt (&S->Entries, EntryCount-1);
485 void DelCodeSegAfter (CodeSeg* S, unsigned Last)
486 /* Delete all entries including the given one */
488 /* Get the number of entries in this segment */
489 unsigned Count = CollCount (&S->Entries);
491 /* Remove all entries after the given one */
492 while (Last < Count) {
494 /* Get the next entry */
495 CodeEntry* E = CollAt (&S->Entries, Count-1);
497 /* We have to transfer all labels to the code segment label pool */
498 unsigned LabelCount = CollCount (&E->Labels);
499 while (LabelCount--) {
500 CodeLabel* L = CollAt (&E->Labels, LabelCount);
502 CollAppend (&S->Labels, L);
504 CollDeleteAll (&E->Labels);
506 /* Remove the code entry */
507 FreeCodeEntry (CollAt (&S->Entries, Count-1));
508 CollDelete (&S->Entries, Count-1);
515 void OutputCodeSeg (FILE* F, const CodeSeg* S)
516 /* Output the code segment data to a file */
520 /* Get the number of entries in this segment */
521 unsigned Count = CollCount (&S->Entries);
523 /* Output the segment directive */
524 fprintf (F, ".segment\t\"%s\"\n", S->Name);
526 /* Output all entries */
527 for (I = 0; I < Count; ++I) {
528 OutputCodeEntry (F, CollConstAt (&S->Entries, I));
534 CodeLabel* FindCodeLabel (CodeSeg* S, const char* Name, unsigned Hash)
535 /* Find the label with the given name. Return the label or NULL if not found */
537 /* Get the first hash chain entry */
538 CodeLabel* L = S->LabelHash[Hash];
540 /* Search the list */
542 if (strcmp (Name, L->Name) == 0) {
553 void MergeCodeLabels (CodeSeg* S)
554 /* Merge code labels. That means: For each instruction, remove all labels but
555 * one and adjust the code entries accordingly.
560 /* Walk over all code entries */
561 unsigned EntryCount = CollCount (&S->Entries);
562 for (I = 0; I < EntryCount; ++I) {
567 /* Get a pointer to the next entry */
568 CodeEntry* E = CollAt (&S->Entries, I);
570 /* If this entry has zero labels, continue with the next one */
571 unsigned LabelCount = CollCount (&E->Labels);
572 if (LabelCount == 0) {
576 /* We have at least one label. Use the first one as reference label.
577 * We don't have a notification for global labels for now, and using
578 * the first one will also keep the global function labels, since these
579 * are inserted at position 0.
581 RefLab = CollAt (&E->Labels, 0);
583 /* Walk through the remaining labels and change references to these
584 * labels to a reference to the one and only label. Delete the labels
585 * that are no longer used. To increase performance, walk backwards
588 for (J = LabelCount-1; J >= 1; --J) {
592 /* Get the next label */
593 CodeLabel* L = CollAt (&E->Labels, J);
595 /* Walk through all instructions referencing this label */
596 unsigned RefCount = CollCount (&L->JumpFrom);
597 for (K = 0; K < RefCount; ++K) {
599 /* Get the next instrcuction that references this label */
600 CodeEntry* E = CollAt (&L->JumpFrom, K);
602 /* Change the reference */
603 CHECK (E->JumpTo == L);
605 CollAppend (&RefLab->JumpFrom, E);
609 /* If the label is not an external label, we may remove the
613 if ((L->Flags & LF_EXT) == 0) {
615 CollDelete (&E->Labels, J);
620 /* The reference label is the only remaining label. If it is not an
621 * external label, check if there are any references to this label,
622 * and delete it if this is not the case.
625 if ((RefLab->Flags & LF_EXT) == 0 && CollCount (&RefLab->JumpFrom) == 0) {
626 /* Delete the label */
627 FreeCodeLabel (RefLab);
628 /* Remove it from the list */
629 CollDelete (&E->Labels, 0);
637 unsigned GetCodeSegEntries (const CodeSeg* S)
638 /* Return the number of entries for the given code segment */
640 return CollCount (&S->Entries);