From ef34f534f967ae4b6625e7f4435f167955da1f66 Mon Sep 17 00:00:00 2001 From: Joel Heikkila Date: Tue, 2 Feb 2021 23:33:45 -0500 Subject: [PATCH] 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