aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Cowburn <ianc@noddybox.co.uk>2021-09-21 21:07:39 +0100
committerIan Cowburn <ianc@noddybox.co.uk>2021-09-21 21:07:39 +0100
commit16e3e35846578e2df90a0c0d75bc2ff41e403946 (patch)
treea4276f55e3063f508ffe0fd26c3649aba8c5ebbd
parent7c234482b55feffb7e7f98f0b96d402760624552 (diff)
Imported V1.7V1.7
-rw-r--r--README.md10
-rw-r--r--doc/casm.html43
-rw-r--r--src/casm.c2
-rw-r--r--src/cpcout.c288
-rw-r--r--src/example/cpc.asm11
5 files changed, 278 insertions, 76 deletions
diff --git a/README.md b/README.md
index 519aa51..7983730 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,6 @@
# casm
-Portable cross assembler. Source control for this is now located in Subversion
-at <a href="https://noddybox.co.uk/svn/casm/trunk">
-https://noddybox.co.uk/svn/casm/trunk</a>. The homepage is at
-<a href="https://noddybox.co.uk/casm.php">
-https://noddybox.co.uk/casm.php</a>.
-Sorry, I tried to like GIT but
-found it's workflow not as comfortable as SVN for me.
+Portable cross assembler.
## Usage
@@ -15,7 +9,7 @@ Simply pass it the file to assemble, i.e.
`casm source.txt`
Full documentation can be found in
-<a href="https://rawgit.com/noddybox/casm/master/doc/casm.html">HTML</a> format.
+<a href="/svnbrowse/cat.php?u=https://noddybox.co.uk/svn/casm/trunk/doc/casm.html">HTML</a> format.
## Processors
diff --git a/doc/casm.html b/doc/casm.html
index 31e1d92..c7e17ab 100644
--- a/doc/casm.html
+++ b/doc/casm.html
@@ -1005,6 +1005,13 @@ labels.
A NES ROM file.
</td></tr>
+<tr><td class="cmd">
+<a href="#cpcout">cpc</a>
+</td>
+<td class="def">
+An Amstrad CPC CDT file.
+</td></tr>
+
</table>
</td></tr>
@@ -1459,6 +1466,42 @@ Defines whether there is battery backed RAM in the cartridge. Defaults to
</table>
+<h3 id="cpcout">Amstrad CPC CDT Output Format</h3>
+<p>
+Generates an Amstrad CPC TAP file for an emulator.
+The file can also be run into a real CPC using TZX to wav file converters.
+</p>
+
+<p>The CDT file will be given the same name as the output filename,
+unless memory banks have been used, in which case each code file in the CDT
+file will use the <b>output-bank</b> setting to generate the filename for
+each block.
+</p>
+
+<h4>Amstrad CPC CDT Output Format options</h4>
+
+<p>The Amstrad CPC CDT output driver supports the following settings that can be
+set via an <b>option</b> command.
+</p>
+
+<table>
+
+<thead><tr><td class="head">Option</td>
+<td class="head">Description</td></tr></thead>
+
+<tr><td class="cmd">
+option cpc-start, <i>address</i>
+</td>
+<td class="def">
+The start address for the tape file. If left undefined defaults to the start
+of written memory.
+</td></tr>
+
+
+</table>
+
+
+
<h2>Listing</h2>
<p>
diff --git a/src/casm.c b/src/casm.c
index 852fa24..c747ad9 100644
--- a/src/casm.c
+++ b/src/casm.c
@@ -59,7 +59,7 @@
*/
static const char *casm_usage =
-"Version 1.7 development\n"
+"Version 1.7\n"
"\n"
"This program is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
diff --git a/src/cpcout.c b/src/cpcout.c
index e3e871d..6e37a1c 100644
--- a/src/cpcout.c
+++ b/src/cpcout.c
@@ -36,6 +36,8 @@
*/
#define BLOCK_SIZE 2048
+#define LO_BYTE(w) ((w) & 0xff)
+#define HI_BYTE(w) (((w) & 0xff00)>>8)
enum option_t
{
@@ -55,43 +57,162 @@ typedef struct
static Options options = {-1};
+typedef struct
+{
+ Byte *stream;
+ size_t length;
+} Stream;
+
/* ---------------------------------------- PRIVATE FUNCTIONS
*/
-static Byte WriteByte(FILE *fp, Byte b, Byte chk)
+static void InitStream(Stream *s)
+{
+ s->stream = NULL;
+ s->length = 0;
+}
+
+static void AddStreamByte(Stream *s, Byte b)
+{
+ s->length++;
+ s->stream = Realloc(s->stream, s->length);
+ s->stream[s->length - 1] = b;
+}
+
+static void AddStreamMem(Stream *s, Byte *mem, size_t len)
+{
+ while(len--)
+ {
+ AddStreamByte(s, *mem++);
+ }
+}
+
+
+static void FreeStream(Stream *s)
+{
+ if (s->stream)
+ {
+ free(s->stream);
+ }
+
+ InitStream(s);
+}
+
+
+static Word CRC(const Byte *b, int size)
+{
+ Word crc = 0xffff;
+ int f;
+
+ for(f = 0; f < 256; f++)
+ {
+ Word w;
+ int n;
+
+ if (f < size)
+ {
+ w = b[f];
+ }
+ else
+ {
+ w = 0;
+ }
+
+ crc ^= w << 8;
+
+ for(n = 0; n < 8; n++)
+ {
+ if (crc & 0x8000)
+ {
+ crc = ((crc << 1) ^ 0x1021) & 0xffff;
+ }
+ else
+ {
+ crc = (crc << 1) & 0xffff;
+ }
+ }
+ }
+
+ crc ^= 0xffff;
+
+ return crc;
+}
+
+
+static void WriteByte(FILE *fp, Byte b)
{
- chk ^= b;
putc(b, fp);
- return chk;
}
-static Byte WriteWord(FILE *fp, int w, Byte chk)
+static void WriteWord(FILE *fp, int w)
+{
+ WriteByte(fp, LO_BYTE(w));
+ WriteByte(fp, HI_BYTE(w));
+}
+
+
+static void WriteDWord(FILE *fp, unsigned long w)
+{
+ WriteWord(fp, w & 0xffff);
+ WriteWord(fp, (w & 0xffff0000) >> 16);
+}
+
+
+static void WriteMem(FILE *fp, const Byte *mem, int len)
+{
+ fwrite(mem, 1, len, fp);
+}
+
+
+static void WriteWordMem(Byte *mem, int offset, int w)
+{
+ mem[offset] = LO_BYTE(w);
+ mem[offset+1] = HI_BYTE(w);
+}
+
+
+static void Write3Word(FILE *fp, int w)
{
- chk = WriteByte(fp, w & 0xff, chk);
- chk = WriteByte(fp, (w & 0xff00) >> 8, chk);
- return chk;
+ WriteByte(fp, LO_BYTE(w));
+ WriteByte(fp, HI_BYTE(w));
+ WriteByte(fp, (w & 0xff0000) >> 16);
}
-static Byte Write3Word(FILE *fp, int w, Byte chk)
+static void WriteString(FILE *fp, const char *p, int len,
+ Byte fill, Codepage cp)
{
- chk = WriteByte(fp, w & 0xff, chk);
- chk = WriteByte(fp, (w & 0xff00) >> 8, chk);
- chk = WriteByte(fp, (w & 0xff0000) >> 16, chk);
- return chk;
+ while(len--)
+ {
+ WriteByte(fp, *p ? CodeFromNative(cp, *p++) : CodeFromNative(cp, fill));
+ }
}
-static Byte WriteString(FILE *fp, const char *p, int len,
- Byte fill, Codepage cp, Byte chk)
+static void WriteStringMem(Byte *mem, int offset, const char *p, int len,
+ Byte fill, Codepage cp)
{
while(len--)
{
- chk = WriteByte(fp, *p ? CodeFromNative(cp, *p++) :
- CodeFromNative(cp, fill), chk);
+ mem[offset++] =
+ *p ? CodeFromNative(cp, *p++) : CodeFromNative(cp, fill);
}
+}
+
- return chk;
+static void OutputTZXTurboBlock(FILE *fp, Stream *s)
+{
+ WriteByte(fp, 0x11); /* Block type - Turbo block */
+ WriteWord(fp, 0x0b21); /* PILOT pulse len */
+ WriteWord(fp, 0x05ad); /* SYNC 1 len */
+ WriteWord(fp, 0x05ad); /* SYNC 2 len */
+ WriteWord(fp, 0x05ac); /* Zero len */
+ WriteWord(fp, 0x0af4); /* One len */
+ WriteWord(fp, 0x1002); /* PILOT tone */
+ WriteByte(fp, 8); /* Last byte used bits */
+ WriteWord(fp, 0x0011); /* Pause after block */
+ Write3Word(fp, s->length);
+ WriteMem(fp, s->stream, s->length);
}
@@ -143,16 +264,17 @@ int CPCOutput(const char *filename, const char *filename_bank,
/* Output the binary files
*/
- WriteString(fp, "ZXTape!", 7, 0, CP_ASCII, 0);
- WriteByte(fp, 0x1a, 0);
- WriteByte(fp, 1, 0);
- WriteByte(fp, 13, 0);
+ WriteString(fp, "ZXTape!", 7, 0, CP_ASCII);
+ WriteByte(fp, 0x1a);
+ WriteByte(fp, 1);
+ WriteByte(fp, 0x14);
for(f = 0; f < count; f++)
{
const Byte *mem;
int min, max, len, blocks, addr;
int block, blocklen;
+ Stream stream;
mem = bank[f]->memory;
min = bank[f]->min_address_used;
@@ -168,39 +290,31 @@ int CPCOutput(const char *filename, const char *filename_bank,
for(block = 0; block <= blocks; block++)
{
- Byte chk;
+ Byte header[256] = {0};
+ Word crc;
int first, last;
+ int seg, segs;
+
+ InitStream(&stream);
first = 0;
last = 0;
- WriteByte(fp, 0x11, 0); /* Block type */
-
- WriteWord(fp, 0x626, 0); /* PILOT */
- WriteWord(fp, 0x34f, 0); /* SYNC1 */
- WriteWord(fp, 0x302, 0); /* SYNC2 */
- WriteWord(fp, 0x33a, 0); /* ZERO */
- WriteWord(fp, 0x673, 0); /* ONE */
- WriteWord(fp, 0xffe, 0); /* PILOT LEN */
- WriteByte(fp, 8, 0); /* USED BITS */
- WriteWord(fp, 0x10, 0); /* PAUSE */
- Write3Word(fp, 0x0041, 0); /* LEN */
-
- chk = 0;
-
+ /* Create the header
+ */
if (f == 0)
{
- chk = WriteString(fp, filename, 16, 0, CP_ASCII, chk);
+ WriteStringMem(header, 0, filename, 16, 0, CP_ASCII);
}
else
{
char fn[16];
snprintf(fn, sizeof fn, filename_bank, bank[f]->number);
- chk = WriteString(fp, fn, 16, 0, CP_ASCII, chk);
+ WriteStringMem(header, 0, fn, 16, 0, CP_ASCII);
}
- chk = WriteByte(fp, block+1, chk);
+ header[16] = block + 1;
if (block == 0)
{
@@ -222,42 +336,86 @@ int CPCOutput(const char *filename, const char *filename_bank,
blocklen = len % BLOCK_SIZE;
}
- chk = WriteByte(fp, last, chk);
- chk = WriteByte(fp, 2, chk);
- chk = WriteWord(fp, blocklen, chk);
- chk = WriteWord(fp, addr, chk);
- chk = WriteByte(fp, first, chk);
- chk = WriteWord(fp, len, chk);
- chk = WriteWord(fp, options.start_addr, chk);
- chk = WriteString(fp, "", 64 - 28, 0, CP_ASCII, chk);
+ if (blocklen == BLOCK_SIZE)
+ {
+ segs = 7;
+ }
+ else
+ {
+ segs = blocklen / 256;
+ }
- WriteByte(fp, chk, 0);
+ header[17] = last;
+ header[18] = 2;
+ WriteWordMem(header, 19, blocklen);
+ WriteWordMem(header, 21, addr);
+ header[23] = first;
+ WriteWordMem(header, 24, len);
+ WriteWordMem(header, 26, options.start_addr);
- addr += blocklen;
+ crc = CRC(header, 256);
- /* Output file data
+ /* Write CSW data
*/
- WriteByte(fp, 0x11, 0); /* Block type */
+ AddStreamByte(&stream, 0x2c);
+ AddStreamMem(&stream, header, 256);
+ AddStreamByte(&stream, HIBYTE(crc));
+ AddStreamByte(&stream, LOBYTE(crc));
+ AddStreamByte(&stream, 0xff);
+ AddStreamByte(&stream, 0xff);
+ AddStreamByte(&stream, 0xff);
+ AddStreamByte(&stream, 0xff);
+
+ OutputTZXTurboBlock(fp, &stream);
- WriteWord(fp, 0x626, 0); /* PILOT */
- WriteWord(fp, 0x34f, 0); /* SYNC1 */
- WriteWord(fp, 0x302, 0); /* SYNC2 */
- WriteWord(fp, 0x33a, 0); /* ZERO */
- WriteWord(fp, 0x673, 0); /* ONE */
- WriteWord(fp, 0xffe, 0); /* PILOT LEN */
- WriteByte(fp, 8, 0); /* USED BITS */
- WriteWord(fp, 0x10, 0); /* PAUSE */
- Write3Word(fp, blocklen + 1, 0); /* LEN */
+ FreeStream(&stream);
- chk = 0;
+ /* Loop round for the segments (up to 8)
+ */
+ InitStream(&stream);
+ AddStreamByte(&stream, 0x16);
- while(min < addr)
+ for(seg = 0; seg <= segs; seg++)
{
- chk = WriteByte(fp, mem[min], chk);
- min++;
+ Byte segment[256] = {0};
+ int segi = 0;
+ int last_seg = 0;
+
+ last_seg = (seg == segs);
+
+ if (!last_seg || blocklen == BLOCK_SIZE)
+ {
+ addr += 256;
+ }
+ else
+ {
+ addr += blocklen % 256;
+ }
+
+ while(min < addr)
+ {
+ segment[segi++] = mem[min++];
+ }
+
+ /* Add segment data to stream
+ */
+ AddStreamMem(&stream, segment, 256);
+ crc = CRC(segment, 256);
+ AddStreamByte(&stream, HIBYTE(crc));
+ AddStreamByte(&stream, LOBYTE(crc));
+
+ if (last_seg)
+ {
+ AddStreamByte(&stream, 0xff);
+ AddStreamByte(&stream, 0xff);
+ AddStreamByte(&stream, 0xff);
+ AddStreamByte(&stream, 0xff);
+ }
}
- WriteByte(fp, chk, 0);
+ OutputTZXTurboBlock(fp, &stream);
+
+ FreeStream(&stream);
}
}
diff --git a/src/example/cpc.asm b/src/example/cpc.asm
index 0c9ff5e..5a37702 100644
--- a/src/example/cpc.asm
+++ b/src/example/cpc.asm
@@ -11,12 +11,19 @@
start: org $8000
ld hl,msg
-loop:
+ call print
+ jp $
+
+ org $8400
+print:
ld a,(hl)
+ cp 0
ret z
+ push hl
call $bb5a
+ pop hl
inc hl
- jr loop
+ jr print
org $8800
msg: defb "Hello World",0