From 439761cb6713a36afa6fb8a88ee331b4dd99c2eb Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 14 Oct 2020 01:17:18 +0200 Subject: [PATCH] fixed C64 ZP addresses to allow disk I/O, introduced diskio library module --- compiler/res/prog8lib/diskio.p8 | 165 ++++++++++++++++++ .../target/c64/C64MachineDefinition.kt | 10 +- compiler/test/UnitTests.kt | 22 +-- examples/diskdir-sys50000.p8 | 15 -- examples/diskdir.p8 | 59 ------- examples/test.p8 | 35 +++- 6 files changed, 212 insertions(+), 94 deletions(-) create mode 100644 compiler/res/prog8lib/diskio.p8 delete mode 100644 examples/diskdir-sys50000.p8 delete mode 100644 examples/diskdir.p8 diff --git a/compiler/res/prog8lib/diskio.p8 b/compiler/res/prog8lib/diskio.p8 new file mode 100644 index 000000000..5a11904eb --- /dev/null +++ b/compiler/res/prog8lib/diskio.p8 @@ -0,0 +1,165 @@ +%import textio +%import syslib + +; Note: this code is compatible with C64 and CX16. + +diskio { + + + sub directory(ubyte drivenumber) -> byte { + ; -- Shows the directory contents of disk drive 8-11 (provide as argument). + + c64.SETNAM(1, "$") + c64.SETLFS(1, drivenumber, 0) + void c64.OPEN() ; open 1,8,0,"$" + if_cs + goto io_error + void c64.CHKIN(1) ; use #1 as input channel + if_cs + goto io_error + + repeat 4 { + void c64.CHRIN() ; skip the 4 prologue bytes + } + + ; while not key pressed / EOF encountered, read data. + ubyte status = c64.READST() + while not status { + txt.print_uw(mkword(c64.CHRIN(), c64.CHRIN())) + txt.chrout(' ') + ubyte @zp char + do { + char = c64.CHRIN() + txt.chrout(char) + } until char==0 + txt.chrout('\n') + void c64.CHRIN() ; skip 2 bytes + void c64.CHRIN() + status = c64.READST() + void c64.STOP() + if_nz + break + } + +io_error: + status = c64.READST() + c64.CLRCHN() ; restore default i/o devices + c64.CLOSE(1) + + if status and status != 64 { ; 64=end of file + txt.print("\ni/o error, status: ") + txt.print_ub(status) + txt.chrout('\n') + return false + } + + return true + } + + + sub status(ubyte drivenumber) { + ; -- display the disk drive's current status message + c64.SETNAM(0, $0000) + c64.SETLFS(15, drivenumber, 15) + void c64.OPEN() ; open 15,8,15 + if_cs + goto io_error + void c64.CHKIN(15) ; use #15 as input channel + if_cs + goto io_error + + while not c64.READST() + txt.chrout(c64.CHRIN()) + +io_error: + c64.CLRCHN() ; restore default i/o devices + c64.CLOSE(15) + } + + + str filename = "0:??????????????????????????????????????" + + sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> byte { + ubyte flen = strlen(filenameptr) + filename[0] = '0' + filename[1] = ':' + memcopy(filenameptr, &filename+2, flen) + memcopy(",s,w", &filename+2+flen, 5) + c64.SETNAM(flen+6, filename) + c64.SETLFS(1, drivenumber, 1) + void c64.OPEN() + if_cs + goto io_error + + c64.CHKOUT(1) + if_cs + goto io_error + + repeat size { + c64.CHROUT(@(address)) + address++ + } + +io_error: + c64.CLRCHN() + c64.CLOSE(1) + return c64.READST()==0 + } + + sub load(ubyte drivenumber, uword filenameptr, uword address) -> byte { + ubyte flen = strlen(filenameptr) + filename[0] = '0' + filename[1] = ':' + memcopy(filenameptr, &filename+2, flen) + memcopy(",s,r", &filename+2+flen, 5) + c64.SETNAM(flen+6, filename) + c64.SETLFS(1, drivenumber, 2) + void c64.OPEN() + if_cs + goto io_error + void c64.CHKIN(1) + if_cs + goto io_error + + do { + @(address) = c64.CHRIN() + address++ + } until c64.READST() + +io_error: + c64.CLRCHN() + c64.CLOSE(1) + + ubyte status = c64.READST() + return status==0 or status==64 + } + + sub delete(ubyte drivenumber, uword filenameptr) { + ; -- delete a file on the drive + ubyte flen = strlen(filenameptr) + filename[0] = 's' + filename[1] = ':' + memcopy(filenameptr, &filename+2, flen+1) + c64.SETNAM(flen+2, filename) + c64.SETLFS(1, drivenumber, 15) + void c64.OPEN() + c64.CLRCHN() + c64.CLOSE(1) + } + + sub rename(ubyte drivenumber, uword oldfileptr, uword newfileptr) { + ; -- rename a file on the drive + ubyte flen_old = strlen(oldfileptr) + ubyte flen_new = strlen(newfileptr) + filename[0] = 'r' + filename[1] = ':' + memcopy(newfileptr, &filename+2, flen_new) + filename[flen_new+2] = '=' + memcopy(oldfileptr, &filename+3+flen_new, flen_old+1) + c64.SETNAM(3+flen_new+flen_old, filename) + c64.SETLFS(1, drivenumber, 15) + void c64.OPEN() + c64.CLRCHN() + c64.CLOSE(1) + } +} diff --git a/compiler/src/prog8/compiler/target/c64/C64MachineDefinition.kt b/compiler/src/prog8/compiler/target/c64/C64MachineDefinition.kt index cbabc1b45..e7cc51702 100644 --- a/compiler/src/prog8/compiler/target/c64/C64MachineDefinition.kt +++ b/compiler/src/prog8/compiler/target/c64/C64MachineDefinition.kt @@ -148,16 +148,16 @@ internal object C64MachineDefinition: IMachineDefinition { 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, - 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xf + 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff )) } if(options.zeropage!=ZeropageType.DONTUSE) { - // add the other free Zp addresses, - // these are valid for the C-64 (when no RS232 I/O is performed) but to keep BASIC running fully: + // add the free Zp addresses + // these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O* free.addAll(listOf(0x04, 0x05, 0x06, 0x0a, 0x0e, - 0x94, 0x95, 0xa7, 0xa8, 0xa9, 0xaa, - 0xb5, 0xb6, 0xf7, 0xf8, 0xf9)) + 0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa5, 0xa6, + 0xb0, 0xb1, 0xbe, 0xbf, 0xf9)) } else { // don't use the zeropage at all free.clear() diff --git a/compiler/test/UnitTests.kt b/compiler/test/UnitTests.kt index 72c44f692..1ee422070 100644 --- a/compiler/test/UnitTests.kt +++ b/compiler/test/UnitTests.kt @@ -186,11 +186,11 @@ class TestC64Zeropage { @Test fun testFreeSpaces() { val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false)) - assertEquals(16, zp1.available()) + assertEquals(18, zp1.available()) val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false)) - assertEquals(89, zp2.available()) + assertEquals(91, zp2.available()) val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false)) - assertEquals(125, zp3.available()) + assertEquals(127, zp3.available()) val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false)) assertEquals(238, zp4.available()) } @@ -220,7 +220,7 @@ class TestC64Zeropage { @Test fun testBasicsafeAllocation() { val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false)) - assertEquals(16, zp.available()) + assertEquals(18, zp.available()) assertFailsWith { // in regular zp there aren't 5 sequential bytes free @@ -273,16 +273,18 @@ class TestC64Zeropage { @Test fun testEfficientAllocation() { val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false)) - assertEquals(16, zp.available()) + assertEquals(18, zp.available()) assertEquals(0x04, zp.allocate("", DataType.WORD, null, errors)) assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors)) assertEquals(0x0a, zp.allocate("", DataType.UBYTE, null, errors)) - assertEquals(0x94, zp.allocate("", DataType.UWORD, null, errors)) - assertEquals(0xa7, zp.allocate("", DataType.UWORD, null, errors)) - assertEquals(0xa9, zp.allocate("", DataType.UWORD, null, errors)) - assertEquals(0xb5, zp.allocate("", DataType.UWORD, null, errors)) - assertEquals(0xf7, zp.allocate("", DataType.UWORD, null, errors)) + assertEquals(0x9b, zp.allocate("", DataType.UWORD, null, errors)) + assertEquals(0x9e, zp.allocate("", DataType.UWORD, null, errors)) + assertEquals(0xa5, zp.allocate("", DataType.UWORD, null, errors)) + assertEquals(0xb0, zp.allocate("", DataType.UWORD, null, errors)) + assertEquals(0xbe, zp.allocate("", DataType.UWORD, null, errors)) assertEquals(0x0e, zp.allocate("", DataType.UBYTE, null, errors)) + assertEquals(0x92, zp.allocate("", DataType.UBYTE, null, errors)) + assertEquals(0x96, zp.allocate("", DataType.UBYTE, null, errors)) assertEquals(0xf9, zp.allocate("", DataType.UBYTE, null, errors)) assertEquals(0, zp.available()) } diff --git a/examples/diskdir-sys50000.p8 b/examples/diskdir-sys50000.p8 deleted file mode 100644 index 1746fc84e..000000000 --- a/examples/diskdir-sys50000.p8 +++ /dev/null @@ -1,15 +0,0 @@ -%target c64 -%import textio -%import syslib -%zeropage basicsafe -%option no_sysinit -%launcher none -%address 50000 - -; This example shows the directory contents of disk drive 8. -; You load it with LOAD "diskdir-sys50000",8,1 -; and then call it with SYS 50000. - -; The only difference with diskdir.p8 is the directives that make this load at 50000. - -%import diskdir diff --git a/examples/diskdir.p8 b/examples/diskdir.p8 deleted file mode 100644 index ab1b9d34e..000000000 --- a/examples/diskdir.p8 +++ /dev/null @@ -1,59 +0,0 @@ -%import textio -%import syslib -%option no_sysinit -%zeropage basicsafe - -; This example shows the directory contents of disk drive 8. -; Note: this program is compatible with C64 and CX16. - -main { - sub start() { - txt.print("directory of disk drive #8:\n\n") - diskdir(8) - } - - sub diskdir(ubyte drivenumber) { - c64.SETNAM(1, "$") - c64.SETLFS(1, drivenumber, 0) - void c64.OPEN() ; open 1,8,0,"$" - if_cs - goto io_error - void c64.CHKIN(1) ; use #1 as input channel - if_cs - goto io_error - - repeat 4 { - void c64.CHRIN() ; skip the 4 prologue bytes - } - - ; while not key pressed / EOF encountered, read data. - ubyte status = c64.READST() - while not status { - txt.print_uw(mkword(c64.CHRIN(), c64.CHRIN())) - txt.chrout(' ') - ubyte @zp char - do { - char = c64.CHRIN() - txt.chrout(char) - } until char==0 - txt.chrout('\n') - void c64.CHRIN() ; skip 2 bytes - void c64.CHRIN() - status = c64.READST() - void c64.STOP() - if_nz - break - } - -io_error: - status = c64.READST() - c64.CLRCHN() ; restore default i/o devices - c64.CLOSE(1) - - if status and status != 64 { ; 64=end of file - txt.print("\ni/o error, status: ") - txt.print_ub(status) - txt.chrout('\n') - } - } -} diff --git a/examples/test.p8 b/examples/test.p8 index 6d125576c..aa7b90494 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,14 +1,42 @@ %import textio -%import syslib -%import floats +%import diskio %zeropage basicsafe main { sub start() { + if diskio.directory(8) + txt.print("all good\n") + else + txt.print("there was an error\n") + + testX() + + if diskio.save(8, "irmen", $0400, 40*25) + txt.print("saved ok\n") + else + txt.print("save error\n") + + diskio.status(8) + + txt.clear_screenchars('?') + + if diskio.load(8, "irmen", $0400) + txt.print("load ok\n") + else + txt.print("load error\n") + + diskio.status(8) + + diskio.rename(8, "irmen", "newirmen") + diskio.status(8) + diskio.delete(8, "test") + diskio.status(8) + } + asmsub testX() { %asm {{ stx _saveX @@ -24,6 +52,3 @@ _saveX .byte 0 }} } } - - -