/*****************************************************************************/ /* */ /* asm.c */ /* */ /* Assembler output for the sp65 sprite and bitmap utility */ /* */ /* */ /* */ /* (C) 2012, Ullrich von Bassewitz */ /* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ /* warranty. In no event will the authors be held liable for any damages */ /* arising from the use of this software. */ /* */ /* Permission is granted to anyone to use this software for any purpose, */ /* including commercial applications, and to alter it and redistribute it */ /* freely, subject to the following restrictions: */ /* */ /* 1. The origin of this software must not be misrepresented; you must not */ /* claim that you wrote the original software. If you use this software */ /* in a product, an acknowledgment in the product documentation would be */ /* appreciated but is not required. */ /* 2. Altered source versions must be plainly marked as such, and must not */ /* be misrepresented as being the original software. */ /* 3. This notice may not be removed or altered from any source */ /* distribution. */ /* */ /*****************************************************************************/ #include #include #include /* common */ #include "chartype.h" #include "check.h" #include "cmdline.h" #include "version.h" /* sp65 */ #include "attr.h" #include "bin.h" #include "error.h" /*****************************************************************************/ /* Code */ /*****************************************************************************/ static int ValidAsmLabel (const char* L) /* Check an assembler label for validity */ { /* Must begin with underscore or alphabetic character */ if (*L != '_' && !IsAlpha (*L)) { return 0; } ++L; /* Remainder must be as above plus digits */ while (*L) { if (*L != '_' && !IsAlNum (*L)) { return 0; } ++L; } /* Ok */ return 1; } static unsigned GetBytesPerLine (const Collection* A) /* Return the number of bytes per line from the attribute collection A */ { char C; unsigned BytesPerLine = 16; /* Check for a bytesperline attribute */ const char* V = GetAttrVal (A, "bytesperline"); if ((V && sscanf (V, "%u%c", &BytesPerLine, &C) != 1) || (BytesPerLine < 1 || BytesPerLine > 64)) { Error ("Invalid value for attribute `bytesperline'"); } return BytesPerLine; } static unsigned GetBase (const Collection* A) /* Return the number base from the attribute collection A */ { char C; unsigned Base = 16; /* Check for a base attribute */ const char* V = GetAttrVal (A, "base"); if ((V && sscanf (V, "%u%c", &Base, &C) != 1) || (Base != 2 && Base != 10 && Base != 16)) { Error ("Invalid value for attribute `base'"); } return Base; } static const char* GetLabel (const Collection* A) /* Return the assembler label from the attribute collection A */ { /* Check for a label attribute */ const char* Label = GetAttrVal (A, "label"); if (Label && !ValidAsmLabel (Label)) { Error ("Invalid value for attribute `label'"); } return Label; } static const char* GetSegment (const Collection* A) /* Return the segment name from the attribute collection A */ { /* Check for a label attribute */ const char* Seg = GetAttrVal (A, "segment"); if (Seg && !ValidAsmLabel (Seg)) { Error ("Invalid value for attribute `segment'"); } return Seg; } void WriteAsmFile (const StrBuf* Data, const Collection* A) /* Write the contents of Data to the given file in assembler (ca65) format */ { FILE* F; const char* D; unsigned Size; /* Get the file name */ const char* Name = NeedAttrVal (A, "name", "write"); /* Check the number of bytes per line */ unsigned BytesPerLine = GetBytesPerLine (A); /* Get the number base */ unsigned Base = GetBase (A); /* Get the assembler label */ const char* Label = GetLabel (A); /* Get the segment */ const char* Segment = GetSegment (A); /* Open the output file */ F = fopen (Name, "w"); if (F == 0) { Error ("Cannot open output file `%s': %s", Name, strerror (errno)); } /* Write a readable header */ fprintf (F, ";\n" "; This file was generated by %s %s\n" ";\n" "\n", ProgName, GetVersionAsString ()); /* If we have a segment defined, output a segment directive */ if (Segment) { fprintf (F, ".segment \"%s\"\n\n", Segment); } /* If we have an assembler label, output that */ if (Label) { fprintf (F, "%s:\n", Label); } /* Write the data */ D = SB_GetConstBuf (Data); Size = SB_GetLen (Data); while (Size) { unsigned I; /* Output one line */ unsigned Chunk = Size; if (Chunk > BytesPerLine) { Chunk = BytesPerLine; } fputs (" .byte ", F); for (I = 0; I < Chunk; ++I) { unsigned char V = *D++; if (I > 0) { fputc (',', F); } switch (Base) { case 2: fprintf (F, "%%%u%u%u%u%u%u%u%u", (V >> 7) & 0x01, (V >> 6) & 0x01, (V >> 5) & 0x01, (V >> 4) & 0x01, (V >> 3) & 0x01, (V >> 2) & 0x01, (V >> 1) & 0x01, (V >> 0) & 0x01); break; case 10: fprintf (F, "%u", V); break; case 16: fprintf (F, "$%02X", V); break; } } fputc ('\n', F); /* Bump the counters */ Size -= Chunk; } /* Add an empty line at the end */ fputc ('\n', F); /* Close the file */ if (fclose (F) != 0) { Error ("Error closing output file `%s': %s", Name, strerror (errno)); } }