From 394ac6380582d5e6d23bdf76fd5946eff790c873 Mon Sep 17 00:00:00 2001 From: Joel Heikkila Date: Mon, 25 Jan 2021 16:13:59 -0500 Subject: [PATCH 1/4] 0x80 + 0x0D is a2 newline --- include/encoding/apple2.tbl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/encoding/apple2.tbl b/include/encoding/apple2.tbl index 2e446324..b4c5173a 100644 --- a/include/encoding/apple2.tbl +++ b/include/encoding/apple2.tbl @@ -10,4 +10,4 @@ a-z=C1 {q}=02 {apos}=07 {nbsp}=40 - +{n}=8D From d6deb81166534ae98581504c49e8e9a74b2b6e3d Mon Sep 17 00:00:00 2001 From: Joel Heikkila Date: Mon, 25 Jan 2021 20:15:18 -0500 Subject: [PATCH 2/4] change monitor address to not print prompt --- include/apple2_kernel.mfk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/apple2_kernel.mfk b/include/apple2_kernel.mfk index a875f140..ff39a9b2 100644 --- a/include/apple2_kernel.mfk +++ b/include/apple2_kernel.mfk @@ -11,7 +11,7 @@ asm void bell() @$FBE4 extern asm void putchar(byte register(a) char) @$FDED extern asm void new_line() @$FC62 extern asm pointer readline() { - jsr $FD6A + jsr $FD6F ldx #$ff __readline_loop: inx From 3a2e29888d6defba67b56f3ac082b8bcb6c80ab3 Mon Sep 17 00:00:00 2001 From: Joel Heikkila Date: Mon, 25 Jan 2021 20:29:54 -0500 Subject: [PATCH 3/4] some documentation of what I've tested --- docs/api/apple2-programming-guide.md | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/api/apple2-programming-guide.md b/docs/api/apple2-programming-guide.md index c37f4033..26d399ae 100644 --- a/docs/api/apple2-programming-guide.md +++ b/docs/api/apple2-programming-guide.md @@ -1,10 +1,14 @@ [< back to index](../doc_index.md) -### A note about Apple II +## Apple II -Apple II variants other than II+/IIe/Enhanced IIe are untested; -this includes the original II, IIc and IIc+, but also later compatible computers (Apple III and IIgs). -They may or may not work. +### Model support + +The current platform configuration for the Apple II targets the original Apple II with an NMOS processor. +Simple programs have been tested on the Apple II, II+, IIe and enhanced IIe. The IIc, IIc+ are untested. +The IIgs may work in compatibility mode, but this is untested. The Apple III is untested. + +### Running your program The compiler output is a raw machine code file, which then has to be put on a disk. You can do it using [CiderPress](http://a2ciderpress.com/), @@ -13,6 +17,12 @@ or some other tool. The file has to be loaded from $0C00. An example how to put such a file onto a disk using AppleCommander: - java -jar AppleCommander-1.3.5.jar -p disk_image.dsk FILENAME B 0xc00 < compiler_output.a2 - + java -jar AppleCommander.jar -p disk_image.dsk FILENAME B 0xc00 < compiler_output.a2 + +When you have placed your file on disk, boot the disk and enter this at the BASIC prompt: + + ] BRUN FILENAME + +This has been successfully tested under DOS 3.3 and [ProDOS 2.4](https://prodos8.com/), on an Apple II+ and Apple IIe. + Creating a bootable disk is beyond the scope of this document. From ef34f534f967ae4b6625e7f4435f167955da1f66 Mon Sep 17 00:00:00 2001 From: Joel Heikkila Date: Tue, 2 Feb 2021 23:33:45 -0500 Subject: [PATCH 4/4] first attempt at a ProDOS library for the Apple II --- examples/apple2/diskrom_dump.mfk | 37 +++++ include/apple2_prodos.mfk | 273 +++++++++++++++++++++++++++++++ 2 files changed, 310 insertions(+) create mode 100644 examples/apple2/diskrom_dump.mfk create mode 100644 include/apple2_prodos.mfk diff --git a/examples/apple2/diskrom_dump.mfk b/examples/apple2/diskrom_dump.mfk new file mode 100644 index 00000000..03fa1f20 --- /dev/null +++ b/examples/apple2/diskrom_dump.mfk @@ -0,0 +1,37 @@ +// simple demonstration of ProDOS library routines that will write +// the Disk II ROM (assuming it is in slot 6) to the file DISKII.ROM + +import stdio +import apple2_prodos + +void main() { + // ProDOS requires a 1K aligned page for an open file as an IO buffer + array iobuf [1024] align(256) + + // you have to explicitly create a file if it doesn't exist yet + // this can fail (so you should check prodos_error + // 06 is general binary type + prodos_create("DISKII.ROM"p, $06) + if prodos_error != 0 { + // prodos error call will be returned in prodos_error so you know + // what went wrong. 0 = no error + putstrz("{n}couldn't create file"z) + panic() + } + + // ProDOS file handle + byte fp + + fp = prodos_open("DISKII.ROM"p, iobuf) + // should check here again for error, and after all calls + + // write the disk controller ROM to the file + prodos_write(fp, $c600, 256) + + // closing frees the handle and io buffer + prodos_close(fp) + + putstrz("{n}DONE"z) + + while true { } +} diff --git a/include/apple2_prodos.mfk b/include/apple2_prodos.mfk new file mode 100644 index 00000000..a2460bf0 --- /dev/null +++ b/include/apple2_prodos.mfk @@ -0,0 +1,273 @@ + +const byte PRODOS_ALLOC_INTERRUPT = $40 +const byte PRODOS_DEALLOC_INTERRUPT = $41 +const byte PRODOS_READ_BLOCK = $80 +const byte PRODOS_WRITE_BLOCK = $81 +const byte PRODOS_GET_TIME = $82 +const byte PRODOS_CREATE = $c0 +const byte PRODOS_DESTROY = $c1 +const byte PRODOS_RENAME = $c2 +const byte PRODOS_SET_FILE_INFO = $c3 +const byte PRODOS_GET_FILE_INFO = $c4 +const byte PRODOS_ON_LINE = $c5 +const byte PRODOS_SET_PREFIX = $c6 +const byte PRODOS_GET_PREFIX = $c7 +const byte PRODOS_OPEN = $c8 +const byte PRODOS_NEWLINE = $c9 +const byte PRODOS_READ = $ca +const byte PRODOS_WRITE = $cb +const byte PRODOS_CLOSE = $cc +const byte PRODOS_FLUSH = $cd +const byte PRODOS_SET_MARK = $ce +const byte PRODOS_GET_MARK = $cf +const byte PRODOS_SET_EOF = $d0 +const byte PRODOS_GET_EOF = $d1 +const byte PRODOS_SET_BUF = $d2 +const byte PRODOS_GET_BUF = $d3 + + +// ProDOS MLI parameter lists + +struct read_block_plist { + byte param_count + byte unit_num + pointer data_buffer + word block_num +} + +struct write_block_plist { + byte param_count + byte unit_num + pointer data_buffer + word block_num +} + +struct close_plist { + byte param_count + byte ref_num +} + +struct flush_plist { + byte param_count + byte ref_num +} + +struct create_plist { + byte param_count + pointer pathname + byte access + byte file_type + word aux_type + byte storage_type + word create_time + word create_date +} + +struct destroy_plist { + byte param_count + pointer pathname +} + +struct open_plist { + byte param_count + pointer pathname + pointer io_buffer + byte ref_num +} + +struct newline_plist { + byte param_count + byte ref_num + byte enable_mask + byte newline_char +} + +struct read_plist { + byte param_count + byte ref_num + pointer data_buffer + word request_count + word trans_count +} + +struct rename_plist { + byte param_count + pointer pathname + pointer new_pathname +} + +struct write_plist { + byte param_count + byte ref_num + pointer data_buffer + word request_count + word trans_count +} + +struct get_prefix_plist { + byte param_count + pointer data_buffer +} + +struct set_prefix_plist { + byte param_count + pointer data_buffer +} + +// we'll just reuse the same area for all plists + +union prodos_plist { + read_block_plist read_block + write_block_plist write_block + create_plist create + destroy_plist destroy + rename_plist rename + open_plist open + newline_plist newline + read_plist read + + write_plist write + close_plist close + flush_plist flush + + get_prefix_plist get_prefix + set_prefix_plist set_prefix + +} + +prodos_plist plist + +byte prodos_error + +// Millfork doesn't support self-modification in the assembler yet, so this +// code is placed in the following array: +// +// jsr $bf00 +// $00 +// plist.addr +// rts +// +// We modify mli_trampoline[3] ($00 on the second line) to set the specific +// ProDOS call before we jsr to $bf00 +// +// TODO: can we just jmp to bf00 and save the extra rts ? + +array mli_trampoline = [ $20, 0, $bf, 0, plist.addr.lo, plist.addr.hi, $60 ] + +asm void prodos_mli_call(byte register(a) pdcall) { + sta mli_trampoline+3 + jsr mli_trampoline + sta prodos_error + rts +} + +void prodos_read_block(byte unum, pointer dbuf, word bnum) { + plist.read_block.param_count = 3 + plist.read_block.unit_num = unum + plist.read_block.data_buffer = dbuf + plist.read_block.block_num = bnum + + prodos_mli_call(PRODOS_READ_BLOCK) +} + +void prodos_write_block(byte unum, pointer dbuf, word bnum) { + plist.write_block.param_count = 3 + plist.write_block.unit_num = unum + plist.write_block.data_buffer = dbuf + plist.write_block.block_num = bnum + + prodos_mli_call(PRODOS_WRITE_BLOCK) +} + +void prodos_close(byte rnum) { + plist.close.param_count = 1 + plist.close.ref_num = rnum + + prodos_mli_call(PRODOS_CLOSE) +} + +void prodos_flush(byte fp) { + plist.flush.param_count = 1 + plist.flush.ref_num = fp + + prodos_mli_call(PRODOS_FLUSH) +} + +void prodos_get_prefix(pointer fnbuf) { + plist.get_prefix.param_count = 1 + plist.get_prefix.data_buffer = fnbuf + + prodos_mli_call(PRODOS_GET_PREFIX) +} + +void prodos_set_prefix(pointer fnbuf) { + plist.set_prefix.param_count = 1 + plist.set_prefix.data_buffer = fnbuf + + prodos_mli_call(PRODOS_SET_PREFIX) +} + +void prodos_create(pointer fn, byte ftype) { + plist.create.param_count = 7 + plist.create.pathname = fn + plist.create.access = $c3 + plist.create.file_type = ftype + plist.create.aux_type = $0 + plist.create.storage_type = 1 + plist.create.create_time = 0 + plist.create.create_date = 0 + + prodos_mli_call(PRODOS_CREATE) +} + +void prodos_destroy (pointer fn) { + plist.destroy.param_count = 0 + plist.destroy.pathname = fn + prodos_mli_call(PRODOS_DESTROY) +} + +void prodos_rename(pointer fn, pointer newfn) { + plist.rename.param_count = 2 + plist.rename.pathname = fn + plist.rename.new_pathname = newfn + + prodos_mli_call(PRODOS_RENAME) +} + +// returns file handle if no error +byte prodos_open (pointer fn, pointer b) { + plist.open.param_count = 3 + plist.open.pathname = fn + plist.open.io_buffer = b + + prodos_mli_call(PRODOS_OPEN) + + return plist.open.ref_num +} + +void prodos_newline(byte fp, byte mask, byte nlchar) { + plist.newline.param_count = 3 + plist.newline.ref_num = fp + plist.newline.enable_mask = mask + plist.newline.newline_char = nlchar + + prodos_mli_call(PRODOS_NEWLINE) +} + +void prodos_read(byte rnum, pointer dbuf, word rcnt) { + plist.read.param_count = 4 + plist.read.ref_num = rnum + plist.read.data_buffer = dbuf + plist.read.request_count = rcnt + + prodos_mli_call(PRODOS_READ) +} + +void prodos_write(byte rnum, pointer dbuf, word rcnt) { + plist.write.param_count = 4 + plist.write.ref_num = rnum + plist.write.data_buffer = dbuf + plist.write.request_count = rcnt + + prodos_mli_call(PRODOS_WRITE) +} \ No newline at end of file