/*
casm - Simple, portable assembler
Copyright (C) 2003-2015 Ian Cowburn (ianc@noddybox.co.uk)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
-------------------------------------------------------------------------
Amstrad CPC tape output handler.
*/
#include
#include
#include
#include "global.h"
#include "codepage.h"
#include "cpcout.h"
#include "expr.h"
/* ---------------------------------------- MACROS & TYPES
*/
#define BLOCK_SIZE 2048
enum option_t
{
OPT_START_ADDR,
};
static const ValueTable option_set[] =
{
{"cpc-start", OPT_START_ADDR},
{NULL}
};
typedef struct
{
int start_addr;
} Options;
static Options options = {-1};
/* ---------------------------------------- PRIVATE FUNCTIONS
*/
static Byte WriteByte(FILE *fp, Byte b, Byte chk)
{
chk ^= b;
putc(b, fp);
return chk;
}
static Byte WriteWord(FILE *fp, int w, Byte chk)
{
chk = WriteByte(fp, w & 0xff, chk);
chk = WriteByte(fp, (w & 0xff00) >> 8, chk);
return chk;
}
static Byte Write3Word(FILE *fp, int w, Byte chk)
{
chk = WriteByte(fp, w & 0xff, chk);
chk = WriteByte(fp, (w & 0xff00) >> 8, chk);
chk = WriteByte(fp, (w & 0xff0000) >> 16, chk);
return chk;
}
static Byte WriteString(FILE *fp, const char *p, int len,
Byte fill, Codepage cp, Byte chk)
{
while(len--)
{
chk = WriteByte(fp, *p ? CodeFromNative(cp, *p++) :
CodeFromNative(cp, fill), chk);
}
return chk;
}
/* ---------------------------------------- INTERFACES
*/
const ValueTable *CPCOutputOptions(void)
{
return option_set;
}
CommandStatus CPCOutputSetOption(int opt, int argc, char *argv[], int quoted[],
char *err, size_t errsize)
{
CommandStatus stat = CMD_OK;
CMD_ARGC_CHECK(1);
switch(opt)
{
case OPT_START_ADDR:
CMD_EXPR(argv[0], options.start_addr);
break;
default:
break;
}
return stat;
}
int CPCOutput(const char *filename, const char *filename_bank,
MemoryBank **bank, int count, char *error, size_t error_size)
{
FILE *fp = fopen(filename, "wb");
int f;
if (!fp)
{
snprintf(error, error_size, "Failed to create %s", filename);
return FALSE;
}
/* First get the initial address is none defined
*/
if (options.start_addr == -1)
{
options.start_addr = bank[0]->min_address_used;
}
/* Output the binary files
*/
WriteString(fp, "ZXTape!", 7, 0, CP_ASCII, 0);
WriteByte(fp, 0x1a, 0);
WriteByte(fp, 1, 0);
WriteByte(fp, 13, 0);
for(f = 0; f < count; f++)
{
const Byte *mem;
int min, max, len, blocks, addr;
int block, blocklen;
mem = bank[f]->memory;
min = bank[f]->min_address_used;
addr = min;
max = bank[f]->max_address_used;
len = max - min + 1;
blocks = len / BLOCK_SIZE;
if ((len % BLOCK_SIZE) == 0)
{
blocks--;
}
for(block = 0; block <= blocks; block++)
{
Byte chk;
int first, last;
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;
if (f == 0)
{
chk = WriteString(fp, filename, 16, 0, CP_ASCII, chk);
}
else
{
char fn[16];
snprintf(fn, sizeof fn, filename_bank, bank[f]->number);
chk = WriteString(fp, fn, 16, 0, CP_ASCII, chk);
}
chk = WriteByte(fp, block+1, chk);
if (block == 0)
{
first = 255;
if (len > BLOCK_SIZE)
{
blocklen = BLOCK_SIZE;
}
else
{
blocklen = len;
}
}
if (block == blocks)
{
last = 255;
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);
WriteByte(fp, chk, 0);
addr += blocklen;
/* Output file data
*/
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, blocklen + 1, 0); /* LEN */
chk = 0;
while(min < addr)
{
chk = WriteByte(fp, mem[min], chk);
min++;
}
WriteByte(fp, chk, 0);
}
}
fclose(fp);
return TRUE;
}
/*
vim: ai sw=4 ts=8 expandtab
*/