/*
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 .
-------------------------------------------------------------------------
Commodore T64 tape output handler.
*/
#include
#include
#include
#include "global.h"
#include "codepage.h"
#include "prgout.h"
#include "expr.h"
/* ---------------------------------------- MACROS & TYPES
*/
/* ---------------------------------------- PRIVATE TYPES AND VARS
*/
enum option_t
{
OPT_START_ADDR,
OPT_SYSTEM_TYPE
};
static const ValueTable option_set[]=
{
{"prg-start", OPT_START_ADDR},
{"prg-system", OPT_SYSTEM_TYPE},
{NULL}
};
typedef enum
{
SYS_C64,
SYS_VIC20,
SYS_VIC20_8K
} system_t;
static ValueTable system_table[]=
{
{"c64", SYS_C64},
{"vic20", SYS_VIC20},
{"vic20+8k",SYS_VIC20_8K},
{NULL}
};
typedef struct
{
int start_addr;
system_t system;
} Options;
static Options options =
{
-1,
SYS_C64
};
/* ---------------------------------------- PRIVATE FUNCTIONS
*/
static int PokeB(Byte *mem, int addr, Byte b)
{
mem[addr++] = b;
return (addr % 0x10000);
}
static int PokeW(Byte *mem, int addr, int w)
{
addr = PokeB(mem, addr, w & 0xff);
return PokeB(mem, addr, (w & 0xff00) >> 8);
}
static int PokeS(Byte *mem, int addr, const char *str)
{
while(*str)
{
addr = PokeB(mem, addr, CodeFromNative(CP_CBM, *str++));
}
return addr;
}
static void WriteByte(FILE *fp, Byte b)
{
putc(b, fp);
}
static void WriteWord(FILE *fp, int w)
{
WriteByte(fp, w & 0xff);
WriteByte(fp, (w & 0xff00) >> 8);
}
/* ---------------------------------------- INTERFACES
*/
const ValueTable *PRGOutputOptions(void)
{
return option_set;
}
CommandStatus PRGOutputSetOption(int opt, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
const ValueTable *val;
CommandStatus stat = CMD_OK;
CMD_ARGC_CHECK(1);
switch(opt)
{
case OPT_START_ADDR:
CMD_EXPR(argv[0], options.start_addr);
break;
case OPT_SYSTEM_TYPE:
CMD_TABLE(argv[0], system_table, val);
options.system = val->value;
break;
default:
break;
}
return stat;
}
int PRGOutput(const char *filename, const char *filename_bank,
MemoryBank **bank, int count, char *error, size_t error_size)
{
int f;
for(f = 0; f < count; f++)
{
FILE *fp;
char buff[4096];
const char *name;
Byte *mem;
int min, max, len;
char sys[16];
int addr;
int start_addr;
int next;
if (count == 1)
{
name = filename;
}
else
{
snprintf(buff, sizeof buff, filename_bank, bank[f]->number);
name = buff;
}
if (!(fp = fopen(name, "wb")))
{
snprintf(error, error_size, "Failed to open %s", name);
return FALSE;
}
switch(options.system)
{
case SYS_C64:
addr = 0x803;
start_addr = 0x801;
break;
case SYS_VIC20:
addr = 0x1003;
start_addr = 0x1001;
break;
case SYS_VIC20_8K:
addr = 0x1203;
start_addr = 0x1201;
break;
}
mem = bank[f]->memory;
min = bank[f]->min_address_used;
max = bank[f]->max_address_used;
/* We're going to prepend some BASIC
*/
if (min < (addr + 0x10))
{
snprintf(error, error_size, "Bank starts below a safe "
"area to add BASIC loader");
return FALSE;
}
if (options.start_addr == -1)
{
snprintf(sys, sizeof sys, "%d", bank[f]->min_address_used);
}
else
{
snprintf(sys, sizeof sys, "%d", options.start_addr);
}
addr = PokeW(mem, addr, 10);
addr = PokeB(mem, addr, 0x9e);
addr = PokeS(mem, addr, sys);
addr = PokeB(mem, addr, 0x00);
next = addr;
addr = PokeW(mem, addr, 0x00);
PokeW(mem, start_addr, next);
min = start_addr; /* Start of BASIC */
len = max - min + 1;
/* Output PRG file
*/
WriteWord(fp, min);
fwrite(mem + min, len, 1, fp);
fclose(fp);
}
return TRUE;
}
/*
vim: ai sw=4 ts=8 expandtab
*/