aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--DOSes.txt49
-rw-r--r--Makefile82
-rw-r--r--bank3.s9
-rw-r--r--bank7.s85
-rw-r--r--cart.txt141
-rw-r--r--gzip2deflate.c78
-rw-r--r--mkcart.1142
-rw-r--r--mkcart.c678
-rw-r--r--mkcart.rst111
-rw-r--r--newtitle.s8
-rw-r--r--portstat.s1
-rw-r--r--soundasm.s2
-rw-r--r--taipan.c19
13 files changed, 1393 insertions, 12 deletions
diff --git a/DOSes.txt b/DOSes.txt
new file mode 100644
index 0000000..5bb1308
--- /dev/null
+++ b/DOSes.txt
@@ -0,0 +1,49 @@
+
+Taipan's intended target is really an 800 with an 810 drive and DOS
+2.0S. However, most people use something else these days.
+
+There are only two XL/XE specific things in Taipan:
+
+- The checkmem code attempts to disable built-in BASIC if
+ it's enabled, which won't work on an 800 (you get a "remove
+ cartridge" message instead)
+
+- Keyclicks are disabled on XL/XE, with POKE 731,1. This doesn't
+ work on an 800, but doesn't hurt anything either (you just get
+ the game with keyclicks enabled)
+
+Unfortunately, the wide variety of Atari DOSes, both ancient and modern,
+plus the various new bits of hardware (MyIDE, Atarimax Flash cart, etc)
+leads to a compatibility nightmare. The rest of this file is a list of
+DOSes (and other loaders), and their compatibility with Taipan.
+
+Tested on actual hardware:
+
+- Atari DOS 2.0S: success
+- Atari DOS 2.5: success
+- MyDOS 4.50 (floppy): success
+- MyDOS (with MyIDE): success (but what version?)
+- SpartaDOS X: success, with "X" command
+- SpartaDOS 3.2dx: fails to load. I think "3.2dx" might be a typo, it
+ might be "3.2d"?
+- SIDE2: success (older build though, need to get results for latest)
+- APE loader: success (older build, need newer results)
+- DOS XL 2.30: success
+
+Tested on emulators:
+
+- atari800 3.1.0 direct xex loader: success
+- atari++ direct xex loader: success
+- altirra direct xex loader: success
+- Fenders 3-sector loader on atari800: success
+- SpartaDOS 3.2g on atari800: success (unexpectedly so)
+- Atari DOS 3.0: success
+
+Untested (waiting on results from testers):
+
+- Atarimax flash cart EXE loader
+
+Untested, but worth looking at:
+
+- MyPicoDOS
+- RealDOS, BeweDOS? probably same results as Sparta 3...
diff --git a/Makefile b/Makefile
index c72c325..36f950f 100644
--- a/Makefile
+++ b/Makefile
@@ -61,7 +61,7 @@ STACK_SIZE=0x200
# and the later github cc65, so it's been removed here.
#CFLAGS=-t $(SYS) -T -I. -L. -Wl -D__SYSTEM_CHECK__=1 -Wl -D__RESERVED_MEMORY__=1056 $(COPT)
-CFLAGS=-t $(SYS) -T -I. -L. -Wl -D__SYSTEM_CHECK__=1 -DFONT_ADDR=$(FONT_ADDR) --start-addr $(TAIMAIN_ADDR) -Wl -D__STACKSIZE__=$(STACK_SIZE) $(COPT)
+CFLAGS=-t $(SYS) -T -I. -L. -Wl -D__SYSTEM_CHECK__=1 -DFONT_ADDR=$(FONT_ADDR) --start-addr $(TAIMAIN_ADDR) -Wl -D__STACKSIZE__=$(STACK_SIZE) $(COPT) $(EXTRACFLAGS)
AS=ca65
ASFLAGS=
@@ -240,6 +240,66 @@ PORTSTAT.DAT: mkportstats.xex
convfont: convfont.c
$(HOSTCC) $(HOSTCFLAGS) -DFONT_ADDR=$(FONT_ADDR) -o convfont convfont.c
+# Cartridge-related targets. Build as a cart isn't really supported yet.
+
+# mkcart turns a raw binary into an atar800 .cart image with 16-byte header.
+mkcart: mkcart.c
+ $(HOSTCC) $(HOSTCFLAGS) -o mkcart mkcart.c
+
+# cc65 doc atari.html explains how to produce a raw binary file.
+# we build it for $03ff because cc65-produced atari binaries always
+# have an RTS as the first byte (for compatibility with SpartaDOS).
+# the "tail -c+2" stuff removes the first byte, so we end up with
+# a romable_taimain.raw that's ready to be copied to $0400 and run
+# via "JSR $0400".
+romable_taimain.raw: $(TAIMAIN_C_SRC) $(TAIMAIN_ASM_SRC) $(TAIMAIN_HDRS)
+ rm -f taimain.xex
+ $(MAKE) TAIMAIN_ADDR=0x3ff EXTRACFLAGS="-Wl -D__AUTOSTART__=1 -Wl -D__EXEHDR__=1 -DCART_TARGET=1 --asm-define CART_TARGET=1" taimain.xex
+ tail -c+2 taimain.xex > romable_taimain.raw
+ rm -f taimain.xex
+
+# 512 bytes of $ff filler, for the last page of each code bank. wasting
+# this little bit of space simplifies the copying code in bank7.s, and
+# guarantees I don't accidentally end up with a 0 in the "cart present"
+# byte of the cart trailer.
+fill512:
+ perl -Mbytes -e 'print chr(0xff) x 512' > fill512
+
+# 8192 bytes of $ff filler, for unused banks. Possibly these will be
+# used for something like an interactive game manual/tutorial.
+blankbank:
+ perl -Mbytes -e 'print chr(0xff) x 8192' > blankbank
+
+splitrom.raw.0:
+splitrom.raw.1:
+splitrom.raw.2:
+splitrom.raw.3:
+ split -b 7680 -a 1 -d romable_taimain.raw splitrom.raw.
+
+bank0: splitrom.raw.0 fill512
+ cat splitrom.raw.0 fill512 > bank0
+
+bank1: splitrom.raw.1 fill512
+ cat splitrom.raw.1 fill512 > bank1
+
+bank2: splitrom.raw.2 fill512
+ cat splitrom.raw.2 fill512 > bank2
+
+bank3: splitrom.raw.3 bank3.s taifont
+ cl65 -l bank3.lst -m bank3.map -t none -o bank3 bank3.s
+
+bank7: bank7.s titledata.dat
+ cl65 -l bank7.lst -m bank7.map -t none -o bank7 bank7.s
+
+taipan.rom: bank0 bank1 bank2 bank3 bank7 blankbank
+ cat bank0 bank1 bank2 bank3 blankbank blankbank blankbank bank7 > taipan.rom
+
+cart: taipan.cart
+
+taipan.cart: taipan.rom mkcart
+ ./mkcart -otaipan.cart -t13 taipan.rom
+ ./mkcart -ctaipan.cart
+
# Rules for building various file types with the cc65 toolchain.
.s.o:
$(AS) $(ASFLAGS) -o $@ $<
@@ -269,6 +329,26 @@ lorchatest: lorchatest.c draw_lorcha.s taifont.xex
cat taifont.xex lorchatest1.xex > lorchatest.xex
atari800 -nobasic lorchatest.xex
+#### cruft, from when I was planning to use a 32K cart:
+# this was a blind alley: zlib is too slow to decompress, plus there's
+# no need to compress taimain.xex since I'm able to use a 64K cart.
+
+# gzip2deflate downloaded from https://github.com/pfusik/zlib6502
+# I could have used deflator.c that ships with cc65's source, but
+# it's deprecated by its own upstream (same author as gzip2deflate).
+gzip2deflate: gzip2deflate.c
+ $(HOSTCC) $(HOSTCFLAGS) -o gzip2deflate gzip2deflate.c
+
+zlibtest.xex: gzip2deflate zlibtest.c zlibtestdata.s romable_taimain.raw
+ gzip -9c < romable_taimain.raw | ./gzip2deflate > rom.dfl
+ $(CC) -t atari -m zlibtest.map -l zlibtest.lst -Wl -D__SYSTEM_CHECK__=1 --start-addr 0x7000 -o zlibtest.xex zlibtest.c zlibtestdata.s
+romable_taimain.xex: $(TAIMAIN_C_SRC) $(TAIMAIN_ASM_SRC) $(TAIMAIN_HDRS)
+ rm -f taimain.xex
+ $(MAKE) TAIMAIN_ADDR=0x3ff EXTRACFLAGS="-DCART_TARGET=1 --asm-define CART_TARGET=1"
+ mv taimain.xex romable_taimain.xex
+
+###
+
soundtest: sounds.c
cl65 -DTESTXEX -t atari -o sounds.xex sounds.c
atari800 -nobasic sounds.xex
diff --git a/bank3.s b/bank3.s
new file mode 100644
index 0000000..950b6a0
--- /dev/null
+++ b/bank3.s
@@ -0,0 +1,9 @@
+
+ .include "atari.inc"
+
+font = $9c00
+
+ .org $8000
+ .incbin "splitrom.raw.3"
+ .res font - *, $ff
+ .incbin "taifont"
diff --git a/bank7.s b/bank7.s
new file mode 100644
index 0000000..c86f889
--- /dev/null
+++ b/bank7.s
@@ -0,0 +1,85 @@
+
+ .include "atari.inc"
+
+font = $9c00
+CCNTL = $d500
+destptr = FR0
+srcptr = FR1
+codedest = $0400
+
+cart_trailer = $bffa
+ .org $a000 ; left cartridge
+
+titledata:
+ .incbin "titledata.dat"
+
+dlist:
+
+; copy 7680 bytes from $8000-$9dff to (destptr).
+; on exit, destptr points to the next 7680 byte chunk.
+copy_30_pages:
+ lda #$0
+ tay
+ sta srcptr
+ lda #$80
+ sta srcptr+1
+ ldx #$1e
+@copypage:
+ lda (srcptr),y
+ sta (destptr),y
+ dey
+ bne @copypage
+ inc srcptr+1
+ inc destptr+1
+ dex
+ bne @copypage
+init:
+ rts
+
+start:
+; turn off ANTIC DMA to speed up copying to RAM
+ lda #0
+ sta SDMCTL
+ sta DMACTL
+
+; copy code to RAM
+ lda #<codedest
+ sta destptr
+ lda #>codedest
+ sta destptr+1
+ lda #0 ; bank 0...
+ sta CCNTL ; ...select it
+ jsr copy_30_pages
+ lda #1 ; bank 1...
+ sta CCNTL ; ...select it
+ jsr copy_30_pages
+ lda #2 ; bank 2...
+ sta CCNTL ; ...select it
+ jsr copy_30_pages
+ lda #3 ; bank 3...
+ sta CCNTL ; ...select it
+ jsr copy_30_pages
+
+ ; leave bank 3 enabled, as it has our custom font in it
+
+; set up display list
+ lda #34
+ sta SDMCTL
+ lda #>font
+ sta CHBAS
+ ;lda $ff
+ ;sta CH
+;@wait4key:
+ ;cmp CH
+ ;beq @wait4key
+ jsr $0400
+
+ .if * > cart_trailer
+ .fatal "bank7 code too large"
+ .endif
+
+ .res cart_trailer - *, $ff
+ .word start ; entry point
+ .byte 0 ; 0 = cartridge present
+ .byte 4 ; init and run the cart, don't boot the disk, non-diagnostic
+ .word init ; init address (just an RTS)
diff --git a/cart.txt b/cart.txt
new file mode 100644
index 0000000..a7783da
--- /dev/null
+++ b/cart.txt
@@ -0,0 +1,141 @@
+
+What's needed to get taipan onto a cart:
+
+joey_z is willing to manufacture carts like this:
+
++---------------------------------------------------------------------------+
+| Type 13: XEGS 64 KB cartridge (banks 0-7) |
++---------------------------------------------------------------------------+
+
+ One of the two variants of the 64 KB XEGS cartridge, that's built on either
+ a C100649 board with the W1 solder point not connected, or a C026449 board
+ with pin 9 of the 74LS374 chip unconnected.
+ This bank-switched cartridge occupies 16 KB of address space between $8000
+ and $BFFF. The cartridge memory is divided into 8 banks, 8 KB each.
+ Bank 7 (the last one) is always mapped to $A000-$BFFF. Three lowest bits of
+ a byte written to $D500-$D5FF select the bank mapped to $8000-$9FFF.
+ The initially selected bank is random, although it seems that 0 gets chosen
+ the most often. Atari800 always selects bank 0 initially.
+
+ Reference:
+ http://www.atarimax.com/jindroush.atari.org/acarts.html#xegs
+
+"For bank-switched cartridges banks are numbered in the order they appear
+in the image file, starting with 0."
+
+...so:
+
+Bank 7 will have the startup code, uncompressed title screen, title
+code, and code to copy from the other banks to RAM.
+
+That gives me banks 0-6 to store code in. The code is 27174 bytes, so I
+have plenty of space: it'll occupy 4 banks, leaving 2 empty ones. The
+last code bank will only be 1/3 full of code... but 1K of it will be
+used for the font, so 4.5K free there.
+
+Code in bank 7 will copy all the chunks to correct place in RAM... and
+I don't need to leave room for DOS or anything else, so the code can be
+ORGed at $0400 (romable_taimain.raw target in the Makefile does this).
+
+$0400 + 27174 means the code ends at $6e26, and the BSS is less than a
+page. The OS will place the GR.0 display list at $7c20, and the stack
+will grow down from there to $7a40 (except it never grows that much).
+
+Copying the code to RAM will take some time, but not too much. Should be
+around 1/4 second, don't need a progress bar or anything. Not sure when to do
+the copying:
+
+1. At boot, before displaying title screen?
+2. After the title screen is displayed, before the menu is active?
+3. After the player presses space/enter to start the game?
+
+Options 1 and 3 will allow turning off ANTIC DMA during the copy, making
+it a bit faster. Option 2 gives the player something to look at while
+the copy is happening, but I think it'll be so quick as to not matter.
+
+Amusingly, the Taipan cart will work on a 32K 800. Not a 16K Atari though.
+
+bank 7: fixed bank
+$a000-$b9xx - title screen data, dl, menu code
+$ba00-$bff9 - memory checker plus code to copy romable_taimain to RAM
+$bffa-$bfff - cart trailer
+
+banks 0, 1, 2: full banks of romable_taimain code
+$8000-$9dff - 30 pages (7680 bytes) of code
+$9f00-$9fff - unused
+
+bank 3: last (partial) bank of romable_taimain, plus the font
+$8000-$9bff - up to 7K of code (28 pages)
+$9c00-$9fff - font (1K)
+
+Unused areas are filled with $ff. For the banks that map at $8000, this
+includes the cart trailer area. A non-zero byte (our $ff) at $9ffc tells
+the OS that a cart isn't inserted in the right slot, so it won't try to
+initialize/run our cart as a right cart. Only bank 7 (that maps as a
+left cart) needs a valid cart trailer... according to cart.txt, every
+once in a while, bank 7 might come up selected at power on. This shouldn't
+matter: it'll be in both bank areas, and if the OS tries to init it as
+a right cart, the init/run addresses will point to the left cart area.
+
+banks 4, 5, 6 are unused (24K total). Possibly the manual goes here,
+if I write one.
+
+--
+
+Changes the game will need for a cart version: Not many.
+
+checkmem.s won't be needed any longer... though there will need to be
+a new memory checker (in bank 7) that says "32K required" if someone
+tries it on a 16K machine.
+
+"Play again?" should probably be "Press any key to play again" since
+there's no place to exit() to. Unless I do a manual! Then it'll exit
+to the title screen, from whence the user can choose the manual or else
+start the game again.
+
+The title decompression will be gone: it'll just display the title screen
+DL and data straight from ROM. The menu help text might be in RAM though,
+as at least 2 bytes need to be modified (sound on/off).
+
+The font will be located in ROM, on a 1K boundary, so CHBAS can point
+to it (no need to copy to RAM).
+
+num_buf and firm will be located in the BSS rather than page 6 as they
+are in the .xex version.
+
+Since I have 3 empty banks... Why not include a manual on the cart,
+with pseudo-hypertext UI?
+
+--
+
+Cart header (trailer, actually). Can I get away with using the same one
+for all banks? IIRC, the low one ("right cartridge") gets looked at first,
+does that mean the high bank doesn't even need a header?
+
+atari800 can load a .xex that's built with --start-addr 0x400. This will
+make testing the cart stuff slightly easier.
+
+--
+
+What would be *really* slick: figure out a way to split the code up
+across banks and include bankswitching in the logic, so it runs from
+the cart and switches banks as needed. This would allow Taipan to run
+on a 16K or even an 8K 400/800!
+
+Can it be done? Surely. Can it be done without rewriting everything
+in asm? Probably. Do I want to? Not really.
+
+--
+
+The cartridge label should look like a classic brown 1st-gen 400/800 cart.
+Yellow text:
+
+TAIPAN!
+Computer Game
+
+and a bogus serial number.. CXU000001 or such (U for Urchlay).
+
+The top should say LEFT CARTRIDGE.
+
+If there's going to be a printed manual, it should be based on the Apple II
+version's manual. If there was one. If I can find a copy.
diff --git a/gzip2deflate.c b/gzip2deflate.c
new file mode 100644
index 0000000..7edf6ac
--- /dev/null
+++ b/gzip2deflate.c
@@ -0,0 +1,78 @@
+// gzip2deflate by Piotr Fusik <fox@scene.pl>
+// http://xasm.atari.org
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef __WIN32
+#include <fcntl.h>
+#endif
+
+static int get_byte(void)
+{
+ int b = getchar();
+ if (b == EOF) {
+ fputs("gzip2deflate: unexpected end of stream\n", stderr);
+ exit(1);
+ }
+ return b;
+}
+
+static void skip_bytes(int n)
+{
+ while (--n >= 0)
+ get_byte();
+}
+
+static void skip_string(void)
+{
+ while (get_byte() != 0);
+}
+
+int main (int argc, char *argv[])
+{
+ int flg;
+ char buf[8 + 4096];
+ size_t len;
+#ifdef __WIN32
+ _setmode(_fileno(stdin), _O_BINARY);
+ _setmode(_fileno(stdout), _O_BINARY);
+#endif
+ if (get_byte() != 0x1f || get_byte() != 0x8b || get_byte() != 8) {
+ fputs("gzip2deflate: not a gzip file on stdin\n", stderr);
+ return 1;
+ }
+ flg = get_byte();
+ skip_bytes(6);
+ if ((flg & 4) != 0) {
+ int xlen = get_byte();
+ xlen += get_byte() << 8;
+ skip_bytes(xlen);
+ }
+ if ((flg & 8) != 0)
+ skip_string();
+ if ((flg & 16) != 0)
+ skip_string();
+ if ((flg & 2) != 0)
+ skip_bytes(2);
+ /* copy everything except the last 8 bytes */
+ len = 0;
+ for (;;) {
+ len += fread(buf + len, 1, sizeof(buf) - len, stdin);
+ if (len != sizeof(buf))
+ break;
+ fwrite(buf, 1, sizeof(buf) - 8, stdout);
+ memcpy(buf, buf + sizeof(buf) - 8, 8);
+ len = 8;
+ }
+ if (ferror(stdin)) {
+ fputs("gzip2deflate: read error\n", stderr);
+ return 1;
+ }
+ if (len < 8) {
+ fputs("gzip2deflate: unexpected end of stream\n", stderr);
+ return 1;
+ }
+ fwrite(buf, 1, len - 8, stdout);
+ return 0;
+}
diff --git a/mkcart.1 b/mkcart.1
new file mode 100644
index 0000000..f957b33
--- /dev/null
+++ b/mkcart.1
@@ -0,0 +1,142 @@
+.\" Man page generated from reStructuredText.
+.
+.TH MKCART 1 "2015-04-22" "2.10.12" "DASM-Dillon"
+.SH NAME
+mkcart \- Convert between raw ROM images and atari800 CART format
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.SH SYNOPSIS
+.sp
+mkcart \-oCARTFILE \-tTYPE RAWFILE [RAWFILE ...]
+.sp
+mkcart \-cCARTFILE
+.sp
+mkcart \-xRAWFILE CARTFILE
+.sp
+mkcart \-l
+.SH DESCRIPTION
+.sp
+A companion tool to \fIdasm(1)\fP, mkcart can:
+.INDENT 0.0
+.IP \(bu 2
+convert one or more \fIdasm(1)\fP raw (\-f3) object files to a CART image
+format for use with emulators such as \fIatari800(1)\fP\&.
+.IP \(bu 2
+convert a CART image back to a raw image.
+.IP \(bu 2
+check the integrity and report information about a CART image.
+.UNINDENT
+.SH OPTIONS
+.INDENT 0.0
+.TP
+.BI \-t\fB TYPE
+Cartridge type (1\-67, see \fI\-l\fP for list), default = guess (poorly!).
+Only used in \-o mode.
+.TP
+.BI \-o\fB CARTFILE
+Create CARTFILE from RAWFILE(s). \fI\-t\fP optional but highly
+recommended.
+.TP
+.BI \-c\fB CARTFILE
+Check integrity of file (header, checksum, and size).
+.TP
+.BI \-x\fB RAWFILE
+Create raw binary from CARTFILE (remove header).
+.TP
+.B \-l
+List all supported \-t types with their sizes in
+bytes and their human\-readable names, and exit.
+.UNINDENT
+.sp
+\-?, \-h Show built\-in help message.
+.SH EXAMPLES
+.nf
+# a standard 8KB cartridge:
+dasm example.asm \-f3 \-oexample.bin
+mkcart \-oexample.cart \-t1 example.bin
+.fi
+.sp
+.nf
+# a bankswitched OSS 16KB cartridge:
+dasm bank1.asm \-f3 \-obank1.bin
+dasm bank2.asm \-f3 \-obank2.bin
+mkcart \-oexample.cart \-t15 bank1.bin bank2.bin
+.fi
+.sp
+.SH EXIT CODES
+.sp
+With \-o and \-x, mkcart will exit with status \fI0\fP if it was able
+to complete the conversion, or \fI1\fP if something went wrong.
+.sp
+With \-c, mkcart will exit with status \fI0\fP if the image is OK (has a
+valid header, known type, good checksum, etc), or \fI1\fP if not.
+.SH BUGS
+.sp
+With \-o and \-x, the input files are opened, read, and closed twice:
+once to calculate the checksum and verify that there\(aqs enough data, then
+the header is written and the input files are re\-opened and reread. Bad
+Things will probably happen if any of the input files change in between
+the two passes.
+.sp
+The \-x option should split bankswitched cartridges into multiple raw
+images instead of one combined image. Workaround: use \fIsplit(1)\fP
+or \fIdd(1)\fP to split the raw image into bank\-sized chunks:
+.nf
+mkcart \-xmac65.bin mac65.cart
+.fi
+.sp
+.nf
+# split into mac65.bank00 and mac65.bank01
+split \-b8192 mac65.bin mac65.bank
+.fi
+.sp
+.nf
+# same thing, with dd
+dd if=mac65.bin of=mac65.bank00 bs=1024 count=8
+dd if=mac65.bin of=mac65.bank01 bs=1024 count=8 skip=8
+.fi
+.sp
+.sp
+Either way, you have to know the bank size (usually 8 or 16 KB), which
+is less than ideal.
+.SH SEE ALSO
+.INDENT 0.0
+.IP \(bu 2
+\fIdasm(1)\fP
+.IP \(bu 2
+\fIatari800(1)\fP
+.IP \(bu 2
+\fIcart.txt\fP
+.UNINDENT
+.SH AUTHOR
+B. Watson <yalhcru@gmail.com>
+.SH COPYRIGHT
+This document is licensed under the same terms as DASM
+itself, see the file COPYING for details.
+.\" Generated by docutils manpage writer.
+.
diff --git a/mkcart.c b/mkcart.c
new file mode 100644
index 0000000..28ba5d5
--- /dev/null
+++ b/mkcart.c
@@ -0,0 +1,678 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ mkcart.c, by B. Watson, part of DASM Atari 8-bit support.
+
+ DASM and atari800 are both GPLv2 so I've lifted code straight
+ from the emulator.
+
+ This is not great C code. It's what happens when I try to write
+ C after spending a week hacking assembly code.
+ */
+
+/* nobody needs more input files than this, right? I should
+ define it to 640K :) */
+#define MAX_INPUT_FILES 1024
+
+/* this is the smallest supported cart size */
+#define CARTBUFLEN 2048
+
+typedef struct {
+ char *name;
+ int size;
+} cart_t;
+
+/* from atari800-3.1.0/src/cartridge.h */
+enum {
+ CARTRIDGE_UNKNOWN = -1,
+ CARTRIDGE_NONE = 0,
+ CARTRIDGE_STD_8 = 1,
+ CARTRIDGE_STD_16 = 2,
+ CARTRIDGE_OSS_034M_16 = 3,
+ CARTRIDGE_5200_32 = 4,
+ CARTRIDGE_DB_32 = 5,
+ CARTRIDGE_5200_EE_16 = 6,
+ CARTRIDGE_5200_40 = 7,
+ CARTRIDGE_WILL_64 = 8,
+ CARTRIDGE_EXP_64 = 9,
+ CARTRIDGE_DIAMOND_64 = 10,
+ CARTRIDGE_SDX_64 = 11,
+ CARTRIDGE_XEGS_32 = 12,
+ CARTRIDGE_XEGS_07_64 = 13,
+ CARTRIDGE_XEGS_128 = 14,
+ CARTRIDGE_OSS_M091_16 = 15,
+ CARTRIDGE_5200_NS_16 = 16,
+ CARTRIDGE_ATRAX_128 = 17,
+ CARTRIDGE_BBSB_40 = 18,
+ CARTRIDGE_5200_8 = 19,
+ CARTRIDGE_5200_4 = 20,
+ CARTRIDGE_RIGHT_8 = 21,
+ CARTRIDGE_WILL_32 = 22,
+ CARTRIDGE_XEGS_256 = 23,
+ CARTRIDGE_XEGS_512 = 24,
+ CARTRIDGE_XEGS_1024 = 25,
+ CARTRIDGE_MEGA_16 = 26,
+ CARTRIDGE_MEGA_32 = 27,
+ CARTRIDGE_MEGA_64 = 28,
+ CARTRIDGE_MEGA_128 = 29,
+ CARTRIDGE_MEGA_256 = 30,
+ CARTRIDGE_MEGA_512 = 31,
+ CARTRIDGE_MEGA_1024 = 32,
+ CARTRIDGE_SWXEGS_32 = 33,
+ CARTRIDGE_SWXEGS_64 = 34,
+ CARTRIDGE_SWXEGS_128 = 35,
+ CARTRIDGE_SWXEGS_256 = 36,
+ CARTRIDGE_SWXEGS_512 = 37,
+ CARTRIDGE_SWXEGS_1024 = 38,
+ CARTRIDGE_PHOENIX_8 = 39,
+ CARTRIDGE_BLIZZARD_16 = 40,
+ CARTRIDGE_ATMAX_128 = 41,
+ CARTRIDGE_ATMAX_1024 = 42,
+ CARTRIDGE_SDX_128 = 43,
+ CARTRIDGE_OSS_8 = 44,
+ CARTRIDGE_OSS_043M_16 = 45,
+ CARTRIDGE_BLIZZARD_4 = 46,
+ CARTRIDGE_AST_32 = 47,
+ CARTRIDGE_ATRAX_SDX_64 = 48,
+ CARTRIDGE_ATRAX_SDX_128 = 49,
+ CARTRIDGE_TURBOSOFT_64 = 50,
+ CARTRIDGE_TURBOSOFT_128 = 51,
+ CARTRIDGE_ULTRACART_32 = 52,
+ CARTRIDGE_LOW_BANK_8 = 53,
+ CARTRIDGE_SIC_128 = 54,
+ CARTRIDGE_SIC_256 = 55,
+ CARTRIDGE_SIC_512 = 56,
+ CARTRIDGE_STD_2 = 57,
+ CARTRIDGE_STD_4 = 58,
+ CARTRIDGE_RIGHT_4 = 59,
+ CARTRIDGE_BLIZZARD_32 = 60,
+ CARTRIDGE_MEGAMAX_2048 = 61,
+ CARTRIDGE_THECART_128M = 62,
+ CARTRIDGE_MEGA_4096 = 63,
+ CARTRIDGE_MEGA_2048 = 64,
+ CARTRIDGE_THECART_32M = 65,
+ CARTRIDGE_THECART_64M = 66,
+ CARTRIDGE_XEGS_8F_64 = 67,
+ CARTRIDGE_LAST_SUPPORTED = 67
+};
+
+#define CARTRIDGE_MAX_SIZE (128 * 1024 * 1024)
+
+#define CARTRIDGE_STD_8_DESC "Standard 8 KB cartridge"
+#define CARTRIDGE_STD_16_DESC "Standard 16 KB cartridge"
+#define CARTRIDGE_OSS_034M_16_DESC "OSS two chip 16 KB cartridge (034M)"
+#define CARTRIDGE_5200_32_DESC "Standard 32 KB 5200 cartridge"
+#define CARTRIDGE_DB_32_DESC "DB 32 KB cartridge"
+#define CARTRIDGE_5200_EE_16_DESC "Two chip 16 KB 5200 cartridge"
+#define CARTRIDGE_5200_40_DESC "Bounty Bob 40 KB 5200 cartridge"
+#define CARTRIDGE_WILL_64_DESC "64 KB Williams cartridge"
+#define CARTRIDGE_EXP_64_DESC "Express 64 KB cartridge"
+#define CARTRIDGE_DIAMOND_64_DESC "Diamond 64 KB cartridge"
+#define CARTRIDGE_SDX_64_DESC "SpartaDOS X 64 KB cartridge"
+#define CARTRIDGE_XEGS_32_DESC "XEGS 32 KB cartridge"
+#define CARTRIDGE_XEGS_07_64_DESC "XEGS 64 KB cartridge (banks 0-7)"
+#define CARTRIDGE_XEGS_128_DESC "XEGS 128 KB cartridge"
+#define CARTRIDGE_OSS_M091_16_DESC "OSS one chip 16 KB cartridge"
+#define CARTRIDGE_5200_NS_16_DESC "One chip 16 KB 5200 cartridge"
+#define CARTRIDGE_ATRAX_128_DESC "Atrax 128 KB cartridge"
+#define CARTRIDGE_BBSB_40_DESC "Bounty Bob 40 KB cartridge"
+#define CARTRIDGE_5200_8_DESC "Standard 8 KB 5200 cartridge"
+#define CARTRIDGE_5200_4_DESC "Standard 4 KB 5200 cartridge"
+#define CARTRIDGE_RIGHT_8_DESC "Right slot 8 KB cartridge"
+#define CARTRIDGE_WILL_32_DESC "32 KB Williams cartridge"
+#define CARTRIDGE_XEGS_256_DESC "XEGS 256 KB cartridge"
+#define CARTRIDGE_XEGS_512_DESC "XEGS 512 KB cartridge"
+#define CARTRIDGE_XEGS_1024_DESC "XEGS 1 MB cartridge"
+#define CARTRIDGE_MEGA_16_DESC "MegaCart 16 KB cartridge"
+#define CARTRIDGE_MEGA_32_DESC "MegaCart 32 KB cartridge"
+#define CARTRIDGE_MEGA_64_DESC "MegaCart 64 KB cartridge"
+#define CARTRIDGE_MEGA_128_DESC "MegaCart 128 KB cartridge"
+#define CARTRIDGE_MEGA_256_DESC "MegaCart 256 KB cartridge"
+#define CARTRIDGE_MEGA_512_DESC "MegaCart 512 KB cartridge"
+#define CARTRIDGE_MEGA_1024_DESC "MegaCart 1 MB cartridge"
+#define CARTRIDGE_SWXEGS_32_DESC "Switchable XEGS 32 KB cartridge"
+#define CARTRIDGE_SWXEGS_64_DESC "Switchable XEGS 64 KB cartridge"
+#define CARTRIDGE_SWXEGS_128_DESC "Switchable XEGS 128 KB cartridge"
+#define CARTRIDGE_SWXEGS_256_DESC "Switchable XEGS 256 KB cartridge"
+#define CARTRIDGE_SWXEGS_512_DESC "Switchable XEGS 512 KB cartridge"
+#define CARTRIDGE_SWXEGS_1024_DESC "Switchable XEGS 1 MB cartridge"
+#define CARTRIDGE_PHOENIX_8_DESC "Phoenix 8 KB cartridge"
+#define CARTRIDGE_BLIZZARD_16_DESC "Blizzard 16 KB cartridge"
+#define CARTRIDGE_ATMAX_128_DESC "Atarimax 128 KB Flash cartridge"
+#define CARTRIDGE_ATMAX_1024_DESC "Atarimax 1 MB Flash cartridge"
+#define CARTRIDGE_SDX_128_DESC "SpartaDOS X 128 KB cartridge"
+#define CARTRIDGE_OSS_8_DESC "OSS 8 KB cartridge"
+#define CARTRIDGE_OSS_043M_16_DESC "OSS two chip 16 KB cartridge (043M)"
+#define CARTRIDGE_BLIZZARD_4_DESC "Blizzard 4 KB cartridge"
+#define CARTRIDGE_AST_32_DESC "AST 32 KB cartridge"
+#define CARTRIDGE_ATRAX_SDX_64_DESC "Atrax SDX 64 KB cartridge"
+#define CARTRIDGE_ATRAX_SDX_128_DESC "Atrax SDX 128 KB cartridge"
+#define CARTRIDGE_TURBOSOFT_64_DESC "Turbosoft 64 KB cartridge"
+#define CARTRIDGE_TURBOSOFT_128_DESC "Turbosoft 128 KB cartridge"
+#define CARTRIDGE_ULTRACART_32_DESC "Ultracart 32 KB cartridge"
+#define CARTRIDGE_LOW_BANK_8_DESC "Low bank 8 KB cartridge"
+#define CARTRIDGE_SIC_128_DESC "SIC! 128 KB cartridge"
+#define CARTRIDGE_SIC_256_DESC "SIC! 256 KB cartridge"
+#define CARTRIDGE_SIC_512_DESC "SIC! 512 KB cartridge"
+#define CARTRIDGE_STD_2_DESC "Standard 2 KB cartridge"
+#define CARTRIDGE_STD_4_DESC "Standard 4 KB cartridge"
+#define CARTRIDGE_RIGHT_4_DESC "Right slot 4 KB cartridge"
+#define CARTRIDGE_BLIZZARD_32_DESC "Blizzard 32 KB cartridge"
+#define CARTRIDGE_MEGAMAX_2048_DESC "MegaMax 2 MB cartridge"
+#define CARTRIDGE_THECART_128M_DESC "The!Cart 128 MB cartridge"
+#define CARTRIDGE_MEGA_4096_DESC "Flash MegaCart 4 MB cartridge"
+#define CARTRIDGE_MEGA_2048_DESC "MegaCart 2 MB cartridge"
+#define CARTRIDGE_THECART_32M_DESC "The!Cart 32 MB cartridge"
+#define CARTRIDGE_THECART_64M_DESC "The!Cart 64 MB cartridge"
+#define CARTRIDGE_XEGS_8F_64_DESC "XEGS 64 KB cartridge (banks 8-15)"
+
+/* this bit didn't come from atari800 */
+static cart_t cart_types[CARTRIDGE_LAST_SUPPORTED + 1];
+#define UI_MENU_ACTION(index, desc) \
+ cart_types[index].size = CARTRIDGE_kb[index]*1024; \
+ cart_types[index].name = desc;
+
+/* from atari800-3.1.0/src/cartridge.c */
+int const CARTRIDGE_kb[CARTRIDGE_LAST_SUPPORTED + 1] = {
+ 0,
+ 8, /* CARTRIDGE_STD_8 */
+ 16, /* CARTRIDGE_STD_16 */
+ 16, /* CARTRIDGE_OSS_034M_16 */
+ 32, /* CARTRIDGE_5200_32 */
+ 32, /* CARTRIDGE_DB_32 */
+ 16, /* CARTRIDGE_5200_EE_16 */
+ 40, /* CARTRIDGE_5200_40 */
+ 64, /* CARTRIDGE_WILL_64 */
+ 64, /* CARTRIDGE_EXP_64 */
+ 64, /* CARTRIDGE_DIAMOND_64 */
+ 64, /* CARTRIDGE_SDX_64 */
+ 32, /* CARTRIDGE_XEGS_32 */
+ 64, /* CARTRIDGE_XEGS_64_07 */
+ 128, /* CARTRIDGE_XEGS_128 */
+ 16, /* CARTRIDGE_OSS_M091_16 */
+ 16, /* CARTRIDGE_5200_NS_16 */
+ 128, /* CARTRIDGE_ATRAX_128 */
+ 40, /* CARTRIDGE_BBSB_40 */
+ 8, /* CARTRIDGE_5200_8 */
+ 4, /* CARTRIDGE_5200_4 */
+ 8, /* CARTRIDGE_RIGHT_8 */
+ 32, /* CARTRIDGE_WILL_32 */
+ 256, /* CARTRIDGE_XEGS_256 */
+ 512, /* CARTRIDGE_XEGS_512 */
+ 1024, /* CARTRIDGE_XEGS_1024 */
+ 16, /* CARTRIDGE_MEGA_16 */
+ 32, /* CARTRIDGE_MEGA_32 */
+ 64, /* CARTRIDGE_MEGA_64 */
+ 128, /* CARTRIDGE_MEGA_128 */
+ 256, /* CARTRIDGE_MEGA_256 */
+ 512, /* CARTRIDGE_MEGA_512 */
+ 1024, /* CARTRIDGE_MEGA_1024 */
+ 32, /* CARTRIDGE_SWXEGS_32 */
+ 64, /* CARTRIDGE_SWXEGS_64 */
+ 128, /* CARTRIDGE_SWXEGS_128 */
+ 256, /* CARTRIDGE_SWXEGS_256 */
+ 512, /* CARTRIDGE_SWXEGS_512 */
+ 1024, /* CARTRIDGE_SWXEGS_1024 */
+ 8, /* CARTRIDGE_PHOENIX_8 */
+ 16, /* CARTRIDGE_BLIZZARD_16 */
+ 128, /* CARTRIDGE_ATMAX_128 */
+ 1024, /* CARTRIDGE_ATMAX_1024 */
+ 128, /* CARTRIDGE_SDX_128 */
+ 8, /* CARTRIDGE_OSS_8 */
+ 16, /* CARTRIDGE_OSS_043M_16 */
+ 4, /* CARTRIDGE_BLIZZARD_4 */
+ 32, /* CARTRIDGE_AST_32 */
+ 64, /* CARTRIDGE_ATRAX_SDX_64 */
+ 128, /* CARTRIDGE_ATRAX_SDX_128 */
+ 64, /* CARTRIDGE_TURBOSOFT_64 */
+ 128, /* CARTRIDGE_TURBOSOFT_128 */
+ 32, /* CARTRIDGE_ULTRACART_32 */
+ 8, /* CARTRIDGE_LOW_BANK_8 */
+ 128, /* CARTRIDGE_SIC_128 */
+ 256, /* CARTRIDGE_SIC_256 */
+ 512, /* CARTRIDGE_SIC_512 */
+ 2, /* CARTRIDGE_STD_2 */
+ 4, /* CARTRIDGE_STD_4 */
+ 4, /* CARTRIDGE_RIGHT_4 */
+ 32, /* CARTRIDGE_TURBO_HIT_32 */
+ 2048, /* CARTRIDGE_MEGA_2048 */
+ 128*1024, /* CARTRIDGE_THECART_128M */
+ 4096, /* CARTRIDGE_MEGA_4096 */
+ 2048, /* CARTRIDGE_MEGA_2048 */
+ 32*1024, /* CARTRIDGE_THECART_32M */
+ 64*1024, /* CARTRIDGE_THECART_64M */
+ 64 /* CARTRIDGE_XEGS_64_8F */
+};
+
+/* Adapted from from atari800-3.1.0/src/ui.c, by s/,$/;/ */
+void init() {
+ UI_MENU_ACTION(CARTRIDGE_STD_8, CARTRIDGE_STD_8_DESC);
+ UI_MENU_ACTION(CARTRIDGE_STD_16, CARTRIDGE_STD_16_DESC);
+ UI_MENU_ACTION(CARTRIDGE_OSS_034M_16, CARTRIDGE_OSS_034M_16_DESC);
+ UI_MENU_ACTION(CARTRIDGE_5200_32, CARTRIDGE_5200_32_DESC);
+ UI_MENU_ACTION(CARTRIDGE_DB_32, CARTRIDGE_DB_32_DESC);
+ UI_MENU_ACTION(CARTRIDGE_5200_EE_16, CARTRIDGE_5200_EE_16_DESC);
+ UI_MENU_ACTION(CARTRIDGE_5200_40, CARTRIDGE_5200_40_DESC);
+ UI_MENU_ACTION(CARTRIDGE_WILL_64, CARTRIDGE_WILL_64_DESC);
+ UI_MENU_ACTION(CARTRIDGE_EXP_64, CARTRIDGE_EXP_64_DESC);
+ UI_MENU_ACTION(CARTRIDGE_DIAMOND_64, CARTRIDGE_DIAMOND_64_DESC);
+ UI_MENU_ACTION(CARTRIDGE_SDX_64, CARTRIDGE_SDX_64_DESC);
+ UI_MENU_ACTION(CARTRIDGE_XEGS_32, CARTRIDGE_XEGS_32_DESC);
+ UI_MENU_ACTION(CARTRIDGE_XEGS_07_64, CARTRIDGE_XEGS_07_64_DESC);
+ UI_MENU_ACTION(CARTRIDGE_XEGS_128, CARTRIDGE_XEGS_128_DESC);
+ UI_MENU_ACTION(CARTRIDGE_OSS_M091_16, CARTRIDGE_OSS_M091_16_DESC);
+ UI_MENU_ACTION(CARTRIDGE_5200_NS_16, CARTRIDGE_5200_NS_16_DESC);
+ UI_MENU_ACTION(CARTRIDGE_ATRAX_128, CARTRIDGE_ATRAX_128_DESC);
+ UI_MENU_ACTION(CARTRIDGE_BBSB_40, CARTRIDGE_BBSB_40_DESC);
+ UI_MENU_ACTION(CARTRIDGE_5200_8, CARTRIDGE_5200_8_DESC);
+ UI_MENU_ACTION(CARTRIDGE_5200_4, CARTRIDGE_5200_4_DESC);
+ UI_MENU_ACTION(CARTRIDGE_RIGHT_8, CARTRIDGE_RIGHT_8_DESC);
+ UI_MENU_ACTION(CARTRIDGE_WILL_32, CARTRIDGE_WILL_32_DESC);
+ UI_MENU_ACTION(CARTRIDGE_XEGS_256, CARTRIDGE_XEGS_256_DESC);
+ UI_MENU_ACTION(CARTRIDGE_XEGS_512, CARTRIDGE_XEGS_512_DESC);
+ UI_MENU_ACTION(CARTRIDGE_XEGS_1024, CARTRIDGE_XEGS_1024_DESC);
+ UI_MENU_ACTION(CARTRIDGE_MEGA_16, CARTRIDGE_MEGA_16_DESC);
+ UI_MENU_ACTION(CARTRIDGE_MEGA_32, CARTRIDGE_MEGA_32_DESC);
+ UI_MENU_ACTION(CARTRIDGE_MEGA_64, CARTRIDGE_MEGA_64_DESC);
+ UI_MENU_ACTION(CARTRIDGE_MEGA_128, CARTRIDGE_MEGA_128_DESC);
+ UI_MENU_ACTION(CARTRIDGE_MEGA_256, CARTRIDGE_MEGA_256_DESC);
+ UI_MENU_ACTION(CARTRIDGE_MEGA_512, CARTRIDGE_MEGA_512_DESC);
+ UI_MENU_ACTION(CARTRIDGE_MEGA_1024, CARTRIDGE_MEGA_1024_DESC);
+ UI_MENU_ACTION(CARTRIDGE_SWXEGS_32, CARTRIDGE_SWXEGS_32_DESC);
+ UI_MENU_ACTION(CARTRIDGE_SWXEGS_64, CARTRIDGE_SWXEGS_64_DESC);
+ UI_MENU_ACTION(CARTRIDGE_SWXEGS_128, CARTRIDGE_SWXEGS_128_DESC);
+ UI_MENU_ACTION(CARTRIDGE_SWXEGS_256, CARTRIDGE_SWXEGS_256_DESC);
+ UI_MENU_ACTION(CARTRIDGE_SWXEGS_512, CARTRIDGE_SWXEGS_512_DESC);
+ UI_MENU_ACTION(CARTRIDGE_SWXEGS_1024, CARTRIDGE_SWXEGS_1024_DESC);
+ UI_MENU_ACTION(CARTRIDGE_PHOENIX_8, CARTRIDGE_PHOENIX_8_DESC);
+ UI_MENU_ACTION(CARTRIDGE_BLIZZARD_16, CARTRIDGE_BLIZZARD_16_DESC);
+ UI_MENU_ACTION(CARTRIDGE_ATMAX_128, CARTRIDGE_ATMAX_128_DESC);
+ UI_MENU_ACTION(CARTRIDGE_ATMAX_1024, CARTRIDGE_ATMAX_1024_DESC);
+ UI_MENU_ACTION(CARTRIDGE_SDX_128, CARTRIDGE_SDX_128_DESC);
+ UI_MENU_ACTION(CARTRIDGE_OSS_8, CARTRIDGE_OSS_8_DESC);
+ UI_MENU_ACTION(CARTRIDGE_OSS_043M_16, CARTRIDGE_OSS_043M_16_DESC);
+ UI_MENU_ACTION(CARTRIDGE_BLIZZARD_4, CARTRIDGE_BLIZZARD_4_DESC);
+ UI_MENU_ACTION(CARTRIDGE_AST_32, CARTRIDGE_AST_32_DESC);
+ UI_MENU_ACTION(CARTRIDGE_ATRAX_SDX_64, CARTRIDGE_ATRAX_SDX_64_DESC);
+ UI_MENU_ACTION(CARTRIDGE_ATRAX_SDX_128, CARTRIDGE_ATRAX_SDX_128_DESC);
+ UI_MENU_ACTION(CARTRIDGE_TURBOSOFT_64, CARTRIDGE_TURBOSOFT_64_DESC);
+ UI_MENU_ACTION(CARTRIDGE_TURBOSOFT_128, CARTRIDGE_TURBOSOFT_128_DESC);
+ UI_MENU_ACTION(CARTRIDGE_ULTRACART_32, CARTRIDGE_ULTRACART_32_DESC);
+ UI_MENU_ACTION(CARTRIDGE_LOW_BANK_8, CARTRIDGE_LOW_BANK_8_DESC);
+ UI_MENU_ACTION(CARTRIDGE_SIC_128, CARTRIDGE_SIC_128_DESC);
+ UI_MENU_ACTION(CARTRIDGE_SIC_256, CARTRIDGE_SIC_256_DESC);
+ UI_MENU_ACTION(CARTRIDGE_SIC_512, CARTRIDGE_SIC_512_DESC);
+ UI_MENU_ACTION(CARTRIDGE_STD_2, CARTRIDGE_STD_2_DESC);
+ UI_MENU_ACTION(CARTRIDGE_STD_4, CARTRIDGE_STD_4_DESC);
+ UI_MENU_ACTION(CARTRIDGE_RIGHT_4, CARTRIDGE_RIGHT_4_DESC);
+ UI_MENU_ACTION(CARTRIDGE_BLIZZARD_32, CARTRIDGE_BLIZZARD_32_DESC);
+ UI_MENU_ACTION(CARTRIDGE_MEGAMAX_2048, CARTRIDGE_MEGAMAX_2048_DESC);
+ UI_MENU_ACTION(CARTRIDGE_THECART_128M, CARTRIDGE_THECART_128M_DESC);
+ UI_MENU_ACTION(CARTRIDGE_MEGA_4096, CARTRIDGE_MEGA_4096_DESC);
+ UI_MENU_ACTION(CARTRIDGE_MEGA_2048, CARTRIDGE_MEGA_2048_DESC);
+ UI_MENU_ACTION(CARTRIDGE_THECART_32M, CARTRIDGE_THECART_32M_DESC);
+ UI_MENU_ACTION(CARTRIDGE_THECART_64M, CARTRIDGE_THECART_64M_DESC);
+ UI_MENU_ACTION(CARTRIDGE_XEGS_8F_64, CARTRIDGE_XEGS_8F_64_DESC);
+}
+
+static int type = -1; /* -1 = guess */
+static int extracting = 0;
+static const char *outfile = NULL;
+static FILE *output;
+static const char *inputfiles[MAX_INPUT_FILES+1];
+static int inputcount = 0;
+static int checksum = 0;
+static int keep_outfile = 0;
+static unsigned char buf[CARTBUFLEN];
+
+void list_types() {
+ int i;
+ for(i = 1; i <= CARTRIDGE_LAST_SUPPORTED; i++) {
+ printf("%d %d %s\n", i, cart_types[i].size, cart_types[i].name);
+ }
+}
+
+void usage() {
+ puts("mkcart v20150421 - create atari800 CART image from raw binaries");
+ puts("\nUsage: mkcart -oCARTFILE -tTYPE RAWFILE [RAWFILE ...]");
+ puts( " mkcart -cCARTFILE");
+ puts( " mkcart -xRAWFILE CARTFILE");
+ puts( " mkcart -l");
+ printf("\n -tTYPE Cartridge type (1-%d), default = guess (poorly!)\n",
+ CARTRIDGE_LAST_SUPPORTED);
+ puts( " -oCARTFILE Create CARTFILE from RAWFILE(s)");
+ puts( " -cCARTFILE Check integrity of file (checksum and size)");
+ puts( " -xRAWFILE Create raw binary from CARTFILE (remove header)");
+ puts( " -l List all supported -t types and exit");
+ puts( " -h, -? This help message");
+}
+
+void open_output() {
+ if(!outfile) {
+ fprintf(stderr, "No output file given, use -o option\n");
+ exit(1);
+ }
+ if( !(output = fopen(outfile, "wb")) ) {
+ perror(outfile);
+ exit(1);
+ }
+}
+
+FILE *open_input(const char *fname) {
+ FILE *f;
+
+ f = fopen(fname, "rb");
+ if(!f) {
+ perror(fname);
+ exit(1);
+ }
+ return f;
+}
+
+int has_cart_header(const unsigned char *b) {
+ return ( (buf[0] == 'C') &&
+ (buf[1] == 'A') &&
+ (buf[2] == 'R') &&
+ (buf[3] == 'T') );
+}
+
+void write_header() {
+ int i, j, size = 0, checkhdr;
+ FILE *f;
+ size_t got;
+
+ for(i = 0; i < inputcount; i++) {
+ f = open_input(inputfiles[i]);
+
+ if(extracting) {
+ /* read and check header insead of writing one */
+ if(fread(buf, 1, 16, f) < 16) {
+ perror(inputfiles[i]);
+ exit(-1);
+ }
+ if(!has_cart_header(buf)) {
+ fprintf(stderr, "%s doesn't have a CART header\n", inputfiles[i]);
+ exit(-1);
+ }
+ return;
+ }
+
+ checkhdr = 1;
+
+ while( (got = fread(buf, 1, CARTBUFLEN, f)) > 0) {
+ if(checkhdr) { /* only do this on first chunk read */
+ if(has_cart_header(buf)) {
+ fprintf(stderr,
+ "warning: raw file %s appears to have a CART header\n",
+ inputfiles[i]);
+ }
+ checkhdr = 0;
+ }
+ if(got < CARTBUFLEN) {
+ fprintf(stderr, "warning: %s size not a multiple of %d bytes\n",
+ inputfiles[i], CARTBUFLEN);
+ }
+ for(j = 0; j < got; j++) checksum += buf[j];
+ size += got;
+ }
+ if(ferror(f)) {
+ perror(inputfiles[i]);
+ exit(1);
+ }
+ fclose(f);
+ }
+
+ if(type > 0 && size != cart_types[type].size) {
+ fprintf(stderr,
+ "warning: cart type %d (%s) must be %d bytes, "
+ "but we read %d from our input files\n",
+ type, cart_types[type].name, cart_types[type].size, size);
+ }
+
+ if(type < 1) {
+ for(i = 1; i <= CARTRIDGE_LAST_SUPPORTED; i++) {
+ if(size == (cart_types[i].size)) {
+ type = i;
+ fprintf(stderr, "warning: no -t option, guessing type %d (%s)\n",
+ i, cart_types[i].name);
+ break;
+ }
+ }
+ if(type < 1) {
+ fprintf(stderr,
+ "fatal: no -t option, no type matches file size %d bytes\n",
+ size);
+ exit(-1);
+ }
+ }
+
+ /* more like assembly than C, but it avoids endian issues */
+ buf[0] = 'C';
+ buf[1] = 'A';
+ buf[2] = 'R';
+ buf[3] = 'T';
+ buf[4] = buf[5] = buf[6] = 0;
+ buf[7] = type;
+ buf[8] = (checksum >> 24) & 0xff;
+ buf[9] = (checksum >> 16) & 0xff;
+ buf[10] = (checksum >> 8) & 0xff;
+ buf[11] = checksum & 0xff;
+ buf[12] = buf[13] = buf[14] = buf[15] = 0;
+
+ i = fwrite(buf, 1, 16, output);
+ if(i < 0) {
+ perror(outfile);
+ exit(-1);
+ } else if(i < 16) {
+ fprintf(stderr, "short write on %s\n", outfile);
+ exit(-1);
+ }
+ /* leave output open here */
+}
+
+void write_data() {
+ int i;
+ FILE *f;
+ size_t got;
+
+ for(i = 0; i < inputcount; i++) {
+ f = open_input(inputfiles[i]);
+ if(extracting) fread(buf, 1, 16, f); /* skip header */
+ while( (got = fread(buf, 1, CARTBUFLEN, f)) > 0) {
+ if( (fwrite(buf, 1, got, output)) < got ) {
+ perror(outfile);
+ exit(-1);
+ }
+ }
+ if(ferror(f)) {
+ perror(inputfiles[i]);
+ exit(1);
+ }
+ fclose(f);
+ }
+
+ /* if we made it here with no errors, the output file is good */
+ keep_outfile = 1;
+}
+
+void add_file(const char *filename) {
+ if(inputcount > MAX_INPUT_FILES) {
+ fprintf(stderr, "Too many input files (limit is %d, sorry)\n",
+ MAX_INPUT_FILES);
+ exit(1);
+ }
+ inputfiles[inputcount++] = filename;
+}
+
+int extract4(const unsigned char *b) {
+ return ( (b[0] << 24) |
+ (b[1] << 16) |
+ (b[2] << 8) |
+ (b[3] ) );
+}
+
+void check_file(const char *filename) {
+ int j, hdr_checksum, hdr_type, hdr_unused, ok = 1;
+ FILE *f;
+ int got, size, hdr_size;
+
+ f = open_input(filename);
+ got = fread(buf, 1, 16, f);
+ if(got < 0) {
+ perror(filename);
+ exit(1);
+ } else if(got < 16) {
+ fprintf(stderr, "%s is only %d bytes long, not a valid CART\n",
+ filename, (int)got);
+ exit(1);
+ }
+
+ if(!has_cart_header(buf)) {
+ fprintf(stderr, "%s missing CART header\n", filename);
+ exit(1);
+ }
+
+ printf("%s has CART header\n", filename);
+
+ hdr_type = extract4(buf + 4);
+ hdr_checksum = extract4(buf + 8);
+ hdr_unused = extract4(buf + 12);
+
+ if(hdr_type < 1 || hdr_type > CARTRIDGE_LAST_SUPPORTED) {
+ fprintf(stderr, "%s has invalid cart type %d (should be 1-%d)\n",
+ filename, hdr_type, CARTRIDGE_LAST_SUPPORTED);
+ exit(1);
+ }
+
+ printf("%s is type %d: %s (%d bytes)\n",
+ filename, hdr_type, cart_types[hdr_type].name, cart_types[hdr_type].size);
+
+ if(hdr_unused) {
+ fprintf(stderr, "warning: %s unused area in CART header is non-zero\n", filename);
+ }
+
+ hdr_size = CARTRIDGE_kb[hdr_type] * 1024;
+
+ while( (got = fread(buf, 1, CARTBUFLEN, f)) > 0) {
+ if(got < CARTBUFLEN) {
+ fprintf(stderr, "warning: %s data size not a multiple of %d bytes\n",
+ filename, CARTBUFLEN);
+ }
+ for(j = 0; j < got; j++) checksum += buf[j];
+ size += got;
+ }
+ if(ferror(f)) {
+ perror(filename);
+ exit(1);
+ }
+ fclose(f);
+
+ if(size != hdr_size) {
+ ok = 0;
+ fprintf(stderr,
+ "%s header says the data size should be %d bytes, but we read %d, ",
+ filename, hdr_size, size);
+ }
+
+ if(size > hdr_size) {
+ fprintf(stderr, "junk at the end? downloaded in ASCII mode?\n");
+ } else if(size < hdr_size) {
+ fprintf(stderr, "truncated?\n");
+ } else {
+ printf("%s has correct data size, %d bytes\n", filename, size);
+ }
+
+ if(hdr_checksum == checksum) {
+ printf("%s has valid checksum\n", filename);
+ } else {
+ ok = 0;
+ fprintf(stderr, "%s has BAD checksum\n", filename);
+ }
+
+ printf("%s results: %s\n", filename, (ok ? "OK" : "FAILED"));
+ exit(!ok);
+}
+
+void cleanup() {
+ if(outfile && !keep_outfile) unlink(outfile); /* ignore error here */
+}
+
+int main(int argc, char **argv) {
+ init();
+ atexit(cleanup);
+
+ if(argc < 2) {
+ usage();
+ exit(0);
+ }
+
+ while(++argv, --argc > 0) {
+ if(argv[0][0] == '-') {
+ switch(argv[0][1]) {
+ case 'l':
+ list_types();
+ exit(0);
+ break;
+
+ case 't':
+ type = atoi(&argv[0][2]);
+ if(type < 1 || type > CARTRIDGE_LAST_SUPPORTED) {
+ fprintf(stderr, "Invalid -t, use -t1 thru -t%d (not -t 1)\n\n",
+ CARTRIDGE_LAST_SUPPORTED);
+ usage();
+ exit(1);
+ }
+ break;
+
+ case 'o':
+ if(argv[0][2]) {
+ outfile = &argv[0][2];
+ } else {
+ fprintf(stderr, "Invalid -o, use -ofilename (not -o filename)\n\n");
+ exit(1);
+ }
+ break;
+
+ case 'x':
+ if(argv[0][2]) {
+ outfile = &argv[0][2];
+ extracting = 1;
+ } else {
+ fprintf(stderr, "Invalid -x, use -xfilename (not -x filename)\n\n");
+ exit(1);
+ }
+ break;
+
+ case 'c':
+ if(argv[0][2]) {
+ check_file(&argv[0][2]); /* exits */
+ } else {
+ fprintf(stderr, "Invalid -c, use -cfilename (not -c filename)\n\n");
+ exit(1);
+ }
+ break;
+
+ case 'h':
+ case '?':
+ usage();
+ exit(0);
+ break;
+
+ default:
+ fprintf(stderr, "Invalid option %s\n\n", *argv);
+ usage();
+ exit(1);
+ break;
+ }
+ } else { /* argv[0][0] != '-' */
+ add_file(*argv);
+ }
+ }
+
+ open_output();
+ write_header();
+ write_data();
+ exit(0);
+}
diff --git a/mkcart.rst b/mkcart.rst
new file mode 100644
index 0000000..dbf5480
--- /dev/null
+++ b/mkcart.rst
@@ -0,0 +1,111 @@
+======
+mkcart
+======
+
+-------------------------------------------------------
+Convert between raw ROM images and atari800 CART format
+-------------------------------------------------------
+
+.. |date| date::
+
+:Manual section: 1
+:Manual group: DASM-Dillon
+:Authors: `B. Watson <yalhcru@gmail.com>`
+:Date: |date|
+:Version: 2.10.12
+:Copyright: This document is licensed under the same terms as DASM
+ itself, see the file COPYING for details.
+
+SYNOPSIS
+========
+
+mkcart -oCARTFILE -tTYPE RAWFILE [RAWFILE ...]
+
+mkcart -cCARTFILE
+
+mkcart -xRAWFILE CARTFILE
+
+mkcart -l
+
+DESCRIPTION
+===========
+
+A companion tool to `dasm(1)`, mkcart can:
+
+- convert one or more `dasm(1)` raw (-f3) object files to a CART image
+ format for use with emulators such as `atari800(1)`.
+
+- convert a CART image back to a raw image.
+
+- check the integrity and report information about a CART image.
+
+OPTIONS
+=======
+
+-tTYPE Cartridge type (1-67, see `-l` for list), default = guess (poorly!).
+ Only used in -o mode.
+
+-oCARTFILE Create CARTFILE from RAWFILE(s). `-t` optional but highly
+ recommended.
+
+-cCARTFILE Check integrity of file (header, checksum, and size).
+
+-xRAWFILE Create raw binary from CARTFILE (remove header).
+
+-l List all supported -t types with their sizes in
+ bytes and their human-readable names, and exit.
+
+-?, -h Show built-in help message.
+
+EXAMPLES
+========
+
+| # a standard 8KB cartridge:
+| dasm example.asm -f3 -oexample.bin
+| mkcart -oexample.cart -t1 example.bin
+
+| # a bankswitched OSS 16KB cartridge:
+| dasm bank1.asm -f3 -obank1.bin
+| dasm bank2.asm -f3 -obank2.bin
+| mkcart -oexample.cart -t15 bank1.bin bank2.bin
+
+EXIT CODES
+==========
+
+With -o and -x, mkcart will exit with status `0` if it was able
+to complete the conversion, or `1` if something went wrong.
+
+With -c, mkcart will exit with status `0` if the image is OK (has a
+valid header, known type, good checksum, etc), or `1` if not.
+
+BUGS
+====
+
+With -o and -x, the input files are opened, read, and closed twice:
+once to calculate the checksum and verify that there's enough data, then
+the header is written and the input files are re-opened and reread. Bad
+Things will probably happen if any of the input files change in between
+the two passes.
+
+The -x option should split bankswitched cartridges into multiple raw
+images instead of one combined image. Workaround: use `split(1)`
+or `dd(1)` to split the raw image into bank-sized chunks:
+
+| mkcart -xmac65.bin mac65.cart
+
+| # split into mac65.bank00 and mac65.bank01
+| split -b8192 mac65.bin mac65.bank
+
+| # same thing, with dd
+| dd if=mac65.bin of=mac65.bank00 bs=1024 count=8
+| dd if=mac65.bin of=mac65.bank01 bs=1024 count=8 skip=8
+
+Either way, you have to know the bank size (usually 8 or 16 KB), which
+is less than ideal.
+
+SEE ALSO
+========
+
+* `dasm(1)`
+* `atari800(1)`
+* `cart.txt`
diff --git a/newtitle.s b/newtitle.s
index 2919059..798320d 100644
--- a/newtitle.s
+++ b/newtitle.s
@@ -7,14 +7,14 @@
; location sound code will look at to see whether sound
; is disabled (0 = enabled, !0 = disabled). If you
; change this here, change it in sounds.h also!
-sound_disabled = $06ff
+sound_disabled = $03c0
; since we're changing the font and colors, we'll save the old
; ones here. If you change these, change them in taipan.c
; also.
-fontsave = $06fc
-color1save = $06fd
-color2save = $06fe
+fontsave = $03c1
+color1save = $03c2
+color2save = $003c3
; where our screen was loaded (see newtitle.pl)
;screendata = $2400
diff --git a/portstat.s b/portstat.s
index 2f0d075..72ef06c 100644
--- a/portstat.s
+++ b/portstat.s
@@ -5,5 +5,6 @@
; in atari800. H: needs to be set writable and pointed to the current
; directory.
+ .rodata
_port_stat_screen:
.incbin "PORTSTAT.DAT"
diff --git a/soundasm.s b/soundasm.s
index 512ec81..d606b9a 100644
--- a/soundasm.s
+++ b/soundasm.s
@@ -34,7 +34,7 @@ repeats = tmp4
; this must agree with newtitle.s
; 0 = enabled, 1 = disabled
-sound_disabled = $06ff
+sound_disabled = $03c0
_under_attack_sound:
; C version was:
diff --git a/taipan.c b/taipan.c
index 9731162..4e042ee 100644
--- a/taipan.c
+++ b/taipan.c
@@ -122,6 +122,7 @@ extern void __fastcall__ clear_lorcha(int which);
/* used to set the background/text colors here, but now the
title screen does it (newtitle.s) */
void atari_text_setup() {
+#ifndef CART_TARGET
jsleep(1);
POKE(560, PEEK(212)); // restore the
POKE(561, PEEK(213)); // display list
@@ -129,6 +130,7 @@ void atari_text_setup() {
POKE(559, 34); // turn on the screen (normal playfield)
jsleep(1);
POKE(756, FONT_ADDR / 256); // use our custom font
+#endif
POKE(731, 1); // disable keyclick on XL/XE (does nothing on 400/800)
}
@@ -252,9 +254,14 @@ void cprint_li_yuen(void);
unsigned char firmpos;
-/* use page 6 for these buffers */
+/* use page 6 for these buffers, for .xex build. Otherwise they'e BSS. */
+#ifdef CART_TARGET
+char firm[23];
+char num_buf[20];
+#else
char *firm = (char *) 0x680;
char *num_buf = (char *) 0x600;
+#endif
char *item[] = { "Opium", "Silk", "Arms", "General Cargo" };
@@ -1939,12 +1946,12 @@ void final_stats(void)
/* restore CHBAS to its original value, generally the ROM font.
This is called fontsave in newtitle.s. */
- POKE(756, PEEK(0x6fc));
+ POKE(756, PEEK(0x3c1));
/* restore COLOR1 and COLOR2. These locations are called
color1save and color2save in newtitle.s. */
- POKE(709, PEEK(0x6fd));
- POKE(710, PEEK(0x6fe));
+ POKE(709, PEEK(0x3c2));
+ POKE(710, PEEK(0x3c3));
exit(0);
}
@@ -2392,7 +2399,7 @@ void li_yuen_extortion(void) {
compradores_report();
cprint_li_yuen();
- cputs("asks ");
+ cputs(" asks ");
cprintfancy(amount);
cputs(" in donation\r\nto the temple of Tin Hau, the Sea\r\nGoddess. Will you pay? ");
choice = yngetc(0);
@@ -3207,7 +3214,7 @@ int main(void) {
if((port != 1) && (li == 0) && (!one_chance_in(4))) {
compradores_report();
cprint_li_yuen();
- cputs("has sent a Lieutenant,\r\n"
+ cputs(" has sent a Lieutenant,\r\n"
"Taipan. He says his admiral wishes\r\n"
"to see you in Hong Kong, posthaste!\r\n");
bad_joss_sound();