aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan C <ianc@noddybox.co.uk>2016-04-06 23:37:07 +0100
committerIan C <ianc@noddybox.co.uk>2016-04-06 23:37:07 +0100
commitd99a9070450157dd8fc3e5d6ad5172af4cc0be59 (patch)
tree55d0b494d0ea1d1df8df08fe9f95495a5b7acc98
parentfdef5d70c1890c7970f31061184a73e699ccd9fb (diff)
Added ZX81 output driver
-rw-r--r--doc/casm.html168
-rw-r--r--src/Makefile12
-rw-r--r--src/casm.c11
-rw-r--r--src/codepage.c10
-rw-r--r--src/example/Makefile14
-rw-r--r--src/example/zx81.asm34
-rw-r--r--src/label.c8
-rw-r--r--src/output.c14
-rw-r--r--src/output.h7
-rw-r--r--src/rawout.c2
-rw-r--r--src/specout.c2
-rw-r--r--src/t64out.c2
-rw-r--r--src/t64out.h2
-rw-r--r--src/zx81out.c268
-rw-r--r--src/zx81out.h56
15 files changed, 558 insertions, 52 deletions
diff --git a/doc/casm.html b/doc/casm.html
index 9553a15..100abdc 100644
--- a/doc/casm.html
+++ b/doc/casm.html
@@ -163,9 +163,7 @@ banks of RAM can be added by using the <b>bank</b> or <b>org</b> directives.
Banks are numbered from zero upwards.</p>
-<h2>Source Code</h2>
-
-<h3>Layout</h3>
+<h2>Source Code Layout</h2>
The source files follow this basic format:
@@ -229,7 +227,7 @@ to put the other quote type inside the string.</li>
</ul>
-<h3>Recognised directives</h3>
+<h2>Recognised directives</h2>
<p>The following directives are also recognised with an optional period (.) in
front of them, and are case insensitive. Directives can also be used to
@@ -412,7 +410,7 @@ word<br>
</table>
-<h3>Expressions</h3>
+<h2>Expressions</h2>
<p>In any of the directives above, where a value is defined, an expression can
be entered.</p>
@@ -604,7 +602,7 @@ Bitwise XOR.
</table>
-<h3>Character Sets</h3>
+<h2>Character Sets</h2>
<p>The assembler has built-in support for a few different character sets.
These can be set by using the options `charset` or `codepage`, i.e.</p>
@@ -636,22 +634,84 @@ The character codes as used on the Sinclair ZX Spectrum.
</td></tr>
<tr><td class="cmd">
+cbm
+</td>
+<td class="def">
+PETSCII as used on the Commodore Business Machine's range from the
+PET to the C128. See <a href="https://en.wikipedia.org/wiki/PETSCII">
+https://en.wikipedia.org/wiki/PETSCII</a> more details.
+</td></tr>
+
+<tr><td class="cmd">
zx81
</td>
<td class="def">
-The character codes as used on the Sinclair ZX-81. Lower case
+The character codes as used on the Sinclair ZX81. Lower case
letters are encoded as normal upper case letters and upper case
-letter will be encoded as inverse upper case letters.
+letter will be encoded as inverse upper case letters. In addition the following
+characters that have no corresponding ZX81 character are mapped as:
+<table>
+
+<tr><td class="cmd">
+#
+</td>
+<td class="def">
+The British Pound sign.
</td></tr>
<tr><td class="cmd">
-cbm
+&apos;
</td>
<td class="def">
-PETSCII as used on the Commodore Business Machine's range from the
-PET to the C128. See <a href="https://en.wikipedia.org/wiki/PETSCII">
-https://en.wikipedia.org/wiki/PETSCII</a> more details.
+Inverse double quotes.
+</td></tr>
+
+<tr><td class="cmd">
+\
+</td>
+<td class="def">
+Inverse slash.
+</td></tr>
+
+<tr><td class="cmd">
+!
+</td>
+<td class="def">
+Inverse question mark.
</td></tr>
+
+<tr><td class="cmd">
+|
+</td>
+<td class="def">
+Inverse space.
+</td></tr>
+
+<tr><td class="cmd">
+~
+</td>
+<td class="def">
+Inverse minus sign.
+</td></tr>
+
+<tr><td class="cmd">
+{ }
+</td>
+<td class="def">
+Inverse round brackets.
+</td></tr>
+
+<tr><td class="cmd">
+`
+</td>
+<td class="def">
+The newline character. Note that the newline is actually a HALT opcode used to
+terminate the line.
+</td></tr>
+
+</table>
+</td></tr>
+
</table>
<p>e.g.</p>
@@ -678,7 +738,7 @@ https://en.wikipedia.org/wiki/PETSCII</a> more details.
</pre>
-<h3>Macros</h3>
+<h2>Macros</h2>
<p>
Macros can be defined in one of two ways; either parameterless or with named
@@ -743,13 +803,14 @@ be replaced by using the following option, e.g.
</pre>
<p>
-Note that this is enforced when the macro is *used*, not when it is *defined*.
+Note that this is enforced when the macro is <u>used</u> not when it is
+defined.
Also the character must not be quoted, as that will be parsed as a string
holding the character code of the character.
</p>
-<h3>Output Format</h3>
+<h2>Output Format</h2>
By default the assembled code is written to a file called <b>output</b> as raw
binary. The generated output can be controlled with the following options.
@@ -801,10 +862,10 @@ A Spectrum emulator TAP file.
</td></tr>
<tr><td class="cmd">
-<a href="#zx81out">p-file</a>
+<a href="#zx81out">zx81</a>
</td>
<td class="def">
-A ZX-81 emulator P file.
+A ZX81 emulator P file.
</td></tr>
<tr><td class="cmd">
@@ -850,6 +911,71 @@ another TAP file containing a BASIC loader, for example.
<h3 id="zx81out">ZX81 .P Output Format</h3>
+<p>
+Generates a P-file for an emulator. A ZX81 .P file is simply a dump of memory
+from the system variables onwards.
+</p>
+
+<p>This format does not support memory blocks (the .P file is not a container
+format) and so will only output anything in the first bank used, and using the
+<b>output-file</b> for the filename.
+</p>
+
+<p>The output file will be created as a BASIC program, containing a REM
+statement holding the machine code, and a command to execute the code. As
+such the output will fail if the code in Bank 0 does not start at address 16514.
+Your code must also support being executed from this address.
+</p>
+
+<p>Another important thing to note is about the display file. In the ZX81
+memory map the DFILE can move around depending on the size of the program.
+The output driver will create a display file for you. The easiest way to
+reference this is to read the DFILE system variable when your program starts.
+</p>
+
+<p>Alternatively you can just as easily set up your own display file once
+your program starts if you have special requirements, e.g. a display file for
+pseudo hi-res.
+</p>
+
+
+<h4>ZX81 .P Output Format options</h4>
+
+<p>The ZX81 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 zx81-margin, &lt;pal|ntsc&gt;
+</td>
+<td class="def">
+Sets the MARGIN system variable appropriately either for the PAL or NTSC
+TV system. Defaults to PAL.
+</td></tr>
+
+<tr><td class="cmd">
+option zx81-autorun, &lt;on|off&gt;
+</td>
+<td class="def">
+Whether the ZX81 should auto run the machine code. Defaults to on.
+</td></tr>
+
+<tr><td class="cmd">
+option zx81-collapse-dfile, &lt;on|off&gt;
+</td>
+<td class="def">
+Whether the display file should be generated collapsed (e.g. for 1K mode).
+Defaults to off.
+</td></tr>
+
+
+</table>
+
<h3 id="t64out">C64 T64 Tape Output Format</h3>
<p>
@@ -876,7 +1002,7 @@ to start the machine code, e.g.
<p>Any remaining blocks will be stored as-is without any basic loader.</p>
-<h3>Listing</h3>
+<h2>Listing</h2>
<p>
By default no output listing is generated. This can be controlled by the
@@ -1088,14 +1214,14 @@ equivalent:
<p>
For the hidden OUT instruction using the flag register, $00 or $ff depending
-on where you're reading, the following are all equivalent, where _value_ can
-be any value at all:
+on where you're reading, the following are all equivalent, where <i>value</i>
+can be any value at all:
</p>
<pre class="codeblock">
out (c)
out (c),f
- out (c),<value>
+ out (c),<i>value</i>
</pre>
diff --git a/src/Makefile b/src/Makefile
index 0812105..29ef34c 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -41,7 +41,8 @@ SOURCE = casm.c \
z80.c \
rawout.c \
specout.c \
- t64out.c
+ t64out.c \
+ zx81out.c
OBJECTS = casm.o \
expr.o \
@@ -60,7 +61,8 @@ OBJECTS = casm.o \
z80.o \
rawout.o \
specout.o \
- t64out.o
+ t64out.o \
+ zx81out.o
$(TARGET): $(OBJECTS)
$(CC) $(CLAGS) -o $(TARGET) $(OBJECTS)
@@ -73,7 +75,7 @@ clean:
alias.o: alias.c global.h basetype.h util.h state.h alias.h
casm.o: casm.c global.h basetype.h util.h state.h expr.h label.h macro.h \
cmd.h parse.h codepage.h stack.h listing.h alias.h output.h rawout.h \
- specout.h z80.h 6502.h
+ specout.h t64out.h zx81out.h z80.h 6502.h
codepage.o: codepage.c global.h basetype.h util.h state.h codepage.h \
parse.h cmd.h
expr.o: expr.c global.h basetype.h util.h state.h expr.h label.h
@@ -84,7 +86,7 @@ listing.o: listing.c global.h basetype.h util.h state.h label.h macro.h \
macro.o: macro.c global.h basetype.h util.h state.h codepage.h parse.h \
cmd.h varchar.h macro.h
output.o: output.c global.h basetype.h util.h state.h output.h parse.h \
- cmd.h rawout.h specout.h t64out.h
+ cmd.h rawout.h specout.h t64out.h zx81out.h
parse.o: parse.c global.h basetype.h util.h state.h codepage.h parse.h \
cmd.h
rawout.o: rawout.c global.h basetype.h util.h state.h rawout.h parse.h \
@@ -100,3 +102,5 @@ varchar.o: varchar.c global.h basetype.h util.h state.h codepage.h \
parse.h cmd.h varchar.h
z80.o: z80.c global.h basetype.h util.h state.h expr.h label.h parse.h \
cmd.h codepage.h varchar.h z80.h
+zx81out.o: zx81out.c global.h basetype.h util.h state.h codepage.h \
+ parse.h cmd.h zx81out.h
diff --git a/src/casm.c b/src/casm.c
index 7624978..bad07f1 100644
--- a/src/casm.c
+++ b/src/casm.c
@@ -40,10 +40,7 @@
#include "stack.h"
#include "listing.h"
#include "alias.h"
-
#include "output.h"
-#include "rawout.h"
-#include "specout.h"
/* ---------------------------------------- PROCESSORS
*/
@@ -404,6 +401,14 @@ static CommandStatus OPTION(const char *label, int argc, char *argv[],
{
return SpecTAPOutputSetOption(entry->value, ac, args, q, err, errsize);
}
+ else if ((entry = ParseTable(opt, T64OutputOptions())))
+ {
+ return T64OutputSetOption(entry->value, ac, args, q, err, errsize);
+ }
+ else if ((entry = ParseTable(opt, ZX81OutputOptions())))
+ {
+ return ZX81OutputSetOption(entry->value, ac, args, q, err, errsize);
+ }
else if ((entry = ParseTable(opt, cpu->options())))
{
return cpu->set_option(entry->value, ac, args, q, err, errsize);
diff --git a/src/codepage.c b/src/codepage.c
index 4cbc1bc..cecdf66 100644
--- a/src/codepage.c
+++ b/src/codepage.c
@@ -101,8 +101,8 @@ static CodepageDef cp_ascii[] =
static CodepageDef cp_zx81[] =
{
- {' ', 0x00}, {'!', 0x00}, {'"', 0x0b}, {'#', 0x0c},
- {'$', 0x0d}, {'%', 0x00}, {'&', 0x00}, {'\'', 0x0b},
+ {' ', 0x00}, {'!', 0x8f}, {'"', 0x0b}, {'#', 0x0c},
+ {'$', 0x0d}, {'%', 0x00}, {'&', 0x00}, {'\'', 0x8b},
{'(', 0x10}, {')', 0x11}, {'*', 0x17}, {'+', 0x15},
{',', 0x1a}, {'-', 0x16}, {'.', 0x1b}, {'/', 0x24},
{'0', 0x1c}, {'1', 0x1d}, {'2', 0x1e}, {'3', 0x1f},
@@ -116,15 +116,15 @@ static CodepageDef cp_zx81[] =
{'P', 0xb5}, {'Q', 0xb6}, {'R', 0xb7}, {'S', 0xb8},
{'T', 0xb9}, {'U', 0xba}, {'V', 0xbb}, {'W', 0xbc},
{'X', 0xbd}, {'Y', 0xbe}, {'Z', 0xbf}, {'[', 0x10},
- {'\\', 0x24}, {']', 0x11}, {'^', 0xde}, {'_', 0x80},
- {'`', 0x60}, {'a', 0x26}, {'b', 0x27}, {'c', 0x28},
+ {'\\', 0x98}, {']', 0x11}, {'^', 0xde}, {'_', 0x80},
+ {'`', 0x76}, {'a', 0x26}, {'b', 0x27}, {'c', 0x28},
{'d', 0x29}, {'e', 0x2a}, {'f', 0x2b}, {'g', 0x2c},
{'h', 0x2d}, {'i', 0x2e}, {'j', 0x2f}, {'k', 0x30},
{'l', 0x31}, {'m', 0x32}, {'n', 0x33}, {'o', 0x34},
{'p', 0x35}, {'q', 0x36}, {'r', 0x37}, {'s', 0x38},
{'t', 0x39}, {'u', 0x3a}, {'v', 0x3b}, {'w', 0x3c},
{'x', 0x3d}, {'y', 0x3e}, {'z', 0x3f}, {'{', 0x90},
- {'|', 0x00}, {'}', 0x91}, {'~', 0x96},
+ {'|', 0x80}, {'}', 0x91}, {'~', 0x96},
{0, 0}
};
diff --git a/src/example/Makefile b/src/example/Makefile
index e9ddfce..7c53209 100644
--- a/src/example/Makefile
+++ b/src/example/Makefile
@@ -20,18 +20,24 @@
# Makefile for examples
#
-ALL = spectrum.tap c64.t64 # zx81.p
+ALL = spectrum.tap c64.t64 zx81.p
CASM = ../casm
-all: $(ALL)
+all: $(ALL) $(CASM)
+
+$(CASM): ../*.[ch]
+ cd .. ; make
remake: clean all
-spectrum.tap: spectrum.asm
+spectrum.tap: spectrum.asm $(CASM)
$(CASM) spectrum.asm
-c64.t64: c64.asm
+c64.t64: c64.asm $(CASM)
$(CASM) c64.asm
+zx81.p: zx81.asm $(CASM)
+ $(CASM) zx81.asm
+
clean:
rm -f $(ALL)
diff --git a/src/example/zx81.asm b/src/example/zx81.asm
new file mode 100644
index 0000000..3a0021c
--- /dev/null
+++ b/src/example/zx81.asm
@@ -0,0 +1,34 @@
+ ; Simple example ZX81 code
+ ;
+
+ option output-file,zx81.p
+ option output-format,zx81
+
+ option zx81-margin,pal
+ option zx81-autorun,on
+ option zx81-collapse-dfile,off
+
+ option codepage,zx81
+
+DFILE: equ 16396
+
+ org 16514
+
+ ld hl,(DFILE)
+ inc hl
+ ld de,hello
+loop:
+ ld a,(de)
+ cp 255
+ ret z
+ ld (hl),a
+ inc hl
+ inc de
+
+ ld a,(hl)
+ cp $76
+ jr nz,loop
+ inc hl
+ jr loop
+
+hello: db "HELLO|WORLD hello world (#69.99 well spent)",255
diff --git a/src/label.c b/src/label.c
index c15627f..75270ef 100644
--- a/src/label.c
+++ b/src/label.c
@@ -277,17 +277,17 @@ int LabelExpand(const char *expr, int *result)
{
found = ParseBase(expr + 1, 16, result, '\0');
}
- else if (last == 'h')
+ else if (last == 'h' || last == 'H')
{
- found = ParseBase(expr, 16, result, 'h');
+ found = ParseBase(expr, 16, result, last);
}
else if (first == '%')
{
found = ParseBase(expr, 2, result, '\0');
}
- else if (last == 'b')
+ else if (last == 'b' || last == 'B')
{
- found = ParseBase(expr, 2, result, 'b');
+ found = ParseBase(expr, 2, result, last);
}
else if (first == '\'' && last == '\'' && len == 3)
{
diff --git a/src/output.c b/src/output.c
index 4c03e4e..8476f9d 100644
--- a/src/output.c
+++ b/src/output.c
@@ -28,12 +28,6 @@
#include "global.h"
#include "output.h"
-#include "rawout.h"
-#include "specout.h"
-#include "t64out.h"
-/* TODO #include "zx81out.h" */
-
-
/* ---------------------------------------- GLOBALS
*/
@@ -56,7 +50,8 @@ typedef enum
{
RAW,
TAP,
- T64
+ T64,
+ ZX81
} Format;
static char output[4096] = "output";
@@ -69,6 +64,7 @@ static ValueTable format_table[] =
{"raw", RAW},
{"spectrum", TAP},
{"t64", T64},
+ {"zx81", ZX81},
{NULL}
};
@@ -142,6 +138,10 @@ int OutputCode(void)
return T64Output(output, output_bank, bank, count,
error, sizeof error);
+ case ZX81:
+ return ZX81Output(output, output_bank, bank, count,
+ error, sizeof error);
+
default:
break;
}
diff --git a/src/output.h b/src/output.h
index cb0e5ca..8ea1c1d 100644
--- a/src/output.h
+++ b/src/output.h
@@ -29,6 +29,13 @@
#include "parse.h"
#include "cmd.h"
+/* Pull in the output drivers
+*/
+#include "rawout.h"
+#include "specout.h"
+#include "t64out.h"
+#include "zx81out.h"
+
/* ---------------------------------------- INTERFACES
*/
diff --git a/src/rawout.c b/src/rawout.c
index 35ff650..3f75b50 100644
--- a/src/rawout.c
+++ b/src/rawout.c
@@ -67,7 +67,7 @@ int RawOutput(const char *filename, const char *filename_bank,
if (!(fp = fopen(name, "wb")))
{
- snprintf(error, error_size, "Failed to open %s\n", name);
+ snprintf(error, error_size, "Failed to open %s", name);
return FALSE;
}
diff --git a/src/specout.c b/src/specout.c
index e5ed897..b29b6ba 100644
--- a/src/specout.c
+++ b/src/specout.c
@@ -81,7 +81,7 @@ int SpecTAPOutput(const char *filename, const char *filename_bank,
if (!fp)
{
- snprintf(error, error_size, "Failed to create %s\n", filename);
+ snprintf(error, error_size, "Failed to create %s", filename);
return FALSE;
}
diff --git a/src/t64out.c b/src/t64out.c
index 0fd308f..6aa82f2 100644
--- a/src/t64out.c
+++ b/src/t64out.c
@@ -122,7 +122,7 @@ int T64Output(const char *filename, const char *filename_bank,
if (!fp)
{
- snprintf(error, error_size, "Failed to create %s\n", filename);
+ snprintf(error, error_size, "Failed to create %s", filename);
return FALSE;
}
diff --git a/src/t64out.h b/src/t64out.h
index 2743d0e..b4c5103 100644
--- a/src/t64out.h
+++ b/src/t64out.h
@@ -43,7 +43,7 @@ CommandStatus T64OutputSetOption(int opt, int argc, char *argv[],
size_t error_size);
-/* Spectrum TAP output of assembly. Returns TRUE if OK, FALSE for failure.
+/* C64 T64 tape output of assembly. Returns TRUE if OK, FALSE for failure.
*/
int T64Output(const char *filename, const char *filename_bank,
MemoryBank **bank, int count,
diff --git a/src/zx81out.c b/src/zx81out.c
new file mode 100644
index 0000000..fe4d83c
--- /dev/null
+++ b/src/zx81out.c
@@ -0,0 +1,268 @@
+/*
+
+ casm - Simple, portable assembler
+
+ Copyright (C) 2003-2015 Ian Cowburn (ianc@noddybox.demon.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 <http://www.gnu.org/licenses/>.
+
+ -------------------------------------------------------------------------
+
+ ZX81 tape output handler.
+
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "global.h"
+#include "codepage.h"
+#include "zx81out.h"
+
+
+/* ---------------------------------------- MACROS & TYPES
+*/
+
+enum option_t
+{
+ OPT_MARGIN,
+ OPT_AUTORUN,
+ OPT_DFILE_COLLAPSED
+};
+
+static const ValueTable option_set[] =
+{
+ {"zx81-margin", OPT_MARGIN},
+ {"zx81-autorun", OPT_AUTORUN},
+ {"zx81-collapse-dfile", OPT_DFILE_COLLAPSED},
+ {NULL}
+};
+
+typedef enum
+{
+ PAL = 55,
+ NTSC = 31
+} TVFormat;
+
+static ValueTable format_table[] =
+{
+ {"pal", PAL},
+ {"NTSC", NTSC},
+ {NULL}
+};
+
+static TVFormat tv_format = PAL;
+static int run = TRUE;
+static int collapse_dfile = FALSE;
+
+/* ---------------------------------------- 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);
+}
+
+
+/* ---------------------------------------- INTERFACES
+*/
+const ValueTable *ZX81OutputOptions(void)
+{
+ return option_set;
+}
+
+CommandStatus ZX81OutputSetOption(int opt, int argc, char *argv[],
+ int quoted[],
+ char *err, size_t errsize)
+{
+ const ValueTable *val;
+
+ CMD_ARGC_CHECK(1);
+
+ switch(opt)
+ {
+ case OPT_MARGIN:
+ CMD_TABLE(argv[0], format_table, val);
+ tv_format = val->value;
+ break;
+
+ case OPT_AUTORUN:
+ run = ParseTrueFalse(argv[0], TRUE);
+ break;
+
+ case OPT_DFILE_COLLAPSED:
+ collapse_dfile = ParseTrueFalse(argv[0], TRUE);
+ break;
+
+ default:
+ break;
+ }
+
+ return CMD_OK;
+}
+
+int ZX81Output(const char *filename, const char *filename_bank,
+ MemoryBank **bank, int count, char *error, size_t error_size)
+{
+ static const int line[] =
+ {
+ 0, 10, 14, 0, 0xf9, 0xd4, 0x1d, 0x22, 0x21, 0x1d,
+ 0x20, 0x7e, 0x8f, 0x01, 0x04, 0x00, 0x00, 0x76, -1
+ };
+
+ FILE *fp = fopen(filename, "wb");
+ Byte *mem;
+ int min;
+ int max;
+ int len;
+ int next;
+ int dfile;
+ int vars;
+ int addr;
+ int f;
+
+ mem = bank[0]->memory;
+ min = bank[0]->min_address_used;
+ max = bank[0]->max_address_used;
+
+ len = max - min + 1;
+
+ if (min != 16514)
+ {
+ snprintf(error, error_size, "Code must start at 16514 to work with the "
+ "ZX81 output driver.");
+ return FALSE;
+ }
+
+ if (!fp)
+ {
+ snprintf(error, error_size, "Failed to create %s", filename);
+ return FALSE;
+ }
+
+ /* Create the system variables and BASIC program around the supplied code.
+ BASIC is done first to calculate various system variables.
+ */
+ addr = 16509;
+
+ /* Program line with REM statement
+ */
+ addr = PokeW(mem, addr, 0x0000); /* Line number */
+ addr = PokeW(mem, addr, len + 2); /* Line length */
+ addr = PokeB(mem, addr, 0xea); /* REM token */
+
+ addr += len; /* Skip code */
+
+ addr = PokeB(mem, addr, 0x76); /* NL token */
+
+ /* Program line to launch the code
+ */
+ next = addr;
+
+ f = 0;
+ while (line[f] != -1)
+ {
+ addr = PokeB(mem, addr, line[f++]);
+ }
+
+ /* Display file
+ */
+ dfile = addr;
+
+ if (collapse_dfile)
+ {
+ for(f = 0; f < 25; f++)
+ {
+ addr = PokeB(mem, addr, 0x76);
+ }
+ }
+ else
+ {
+ addr = PokeB(mem, addr, 0x76);
+
+ for(f = 0; f < 24; f++)
+ {
+ int n;
+
+ for(n = 0; n < 32; n++)
+ {
+ addr = PokeB(mem, addr, 0);
+ }
+
+ addr = PokeB(mem, addr, 0x76);
+ }
+ }
+
+ /* Vars
+ */
+ vars = addr;
+ addr = PokeB(mem, addr, 0x80);
+
+ /* System variables
+ */
+ addr = 0x4009;
+
+ addr = PokeB(mem, addr, 0); /* VERSN */
+ addr = PokeW(mem, addr, 0); /* E_PPC */
+ addr = PokeW(mem, addr, dfile); /* D_FILE */
+ addr = PokeW(mem, addr, dfile + 1); /* DF_CC */
+ addr = PokeW(mem, addr, vars); /* VARS */
+ addr = PokeW(mem, addr, 0); /* DEST */
+ addr = PokeW(mem, addr, vars + 1); /* E_LINE */
+ addr = PokeW(mem, addr, vars - 1); /* CH_ADD */
+ addr = PokeW(mem, addr, 0); /* X_PTR */
+ addr = PokeW(mem, addr, vars + 5); /* STKBOT */
+ addr = PokeW(mem, addr, vars + 5); /* STKEND */
+ addr = PokeB(mem, addr, 0); /* BREG */
+ addr = PokeW(mem, addr, 16477); /* MEM */
+ addr = PokeB(mem, addr, 0); /* unused */
+ addr = PokeB(mem, addr, 2); /* DF_SZ */
+ addr = PokeW(mem, addr, 2); /* S_TOP */
+ addr = PokeW(mem, addr, 0xffff); /* LAST_K */
+ addr = PokeB(mem, addr, 0xff); /* LAST_K */
+ addr = PokeB(mem, addr, tv_format); /* MARGIN */
+ addr = PokeW(mem, addr, run ? next : dfile);/* NXTLIN */
+ addr = PokeW(mem, addr, 0); /* OLDPPC */
+ addr = PokeB(mem, addr, 0); /* FLAGX */
+ addr = PokeW(mem, addr, 0); /* STRLEN */
+ addr = PokeW(mem, addr, 0xc8d); /* T_ADDR */
+ addr = PokeW(mem, addr, 0); /* SEED */
+ addr = PokeW(mem, addr, 0x0ffff); /* FRAMES */
+ addr = PokeW(mem, addr, 0); /* COORDS */
+ addr = PokeB(mem, addr, 0xbc); /* PR_CC */
+ addr = PokeB(mem, addr, 33); /* S_POSN */
+ addr = PokeB(mem, addr, 24); /* S_POSN */
+ addr = PokeW(mem, addr, 0x40); /* CDFLAG */
+
+ /* Write the constructed P file
+ */
+ fwrite(mem + 0x4009, vars - 0x4009 + 1, 1, fp);
+
+ fclose(fp);
+
+ return TRUE;
+}
+
+
+/*
+vim: ai sw=4 ts=8 expandtab
+*/
diff --git a/src/zx81out.h b/src/zx81out.h
new file mode 100644
index 0000000..e6f3190
--- /dev/null
+++ b/src/zx81out.h
@@ -0,0 +1,56 @@
+/*
+
+ casm - Simple, portable assembler
+
+ Copyright (C) 2003-2015 Ian Cowburn (ianc@noddybox.demon.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 <http://www.gnu.org/licenses/>.
+
+ -------------------------------------------------------------------------
+
+ ZX81 .P file output
+
+*/
+
+#ifndef CASM_ZX81OUT_H
+#define CASM_ZX81OUT_H
+
+#include "parse.h"
+#include "state.h"
+#include "cmd.h"
+
+/* ---------------------------------------- INTERFACES
+*/
+
+
+/* RAW Output options
+*/
+const ValueTable *ZX81OutputOptions(void);
+
+CommandStatus ZX81OutputSetOption(int opt, int argc, char *argv[],
+ int quoted[], char *error,
+ size_t error_size);
+
+
+/* ZX81 P file output of assembly. Returns TRUE if OK, FALSE for failure.
+*/
+int ZX81Output(const char *filename, const char *filename_bank,
+ MemoryBank **bank, int count,
+ char *error, size_t error_size);
+
+#endif
+
+/*
+vim: ai sw=4 ts=8 expandtab
+*/