fixed C64 ZP addresses to allow disk I/O, introduced diskio library module

This commit is contained in:
Irmen de Jong 2020-10-14 01:17:18 +02:00
parent bee6c65293
commit 439761cb67
6 changed files with 212 additions and 94 deletions

View File

@ -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)
}
}

View File

@ -148,16 +148,16 @@ internal object C64MachineDefinition: IMachineDefinition {
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 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) { if(options.zeropage!=ZeropageType.DONTUSE) {
// add the other free Zp addresses, // add the free Zp addresses
// these are valid for the C-64 (when no RS232 I/O is performed) but to keep BASIC running fully: // 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, free.addAll(listOf(0x04, 0x05, 0x06, 0x0a, 0x0e,
0x94, 0x95, 0xa7, 0xa8, 0xa9, 0xaa, 0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa5, 0xa6,
0xb5, 0xb6, 0xf7, 0xf8, 0xf9)) 0xb0, 0xb1, 0xbe, 0xbf, 0xf9))
} else { } else {
// don't use the zeropage at all // don't use the zeropage at all
free.clear() free.clear()

View File

@ -186,11 +186,11 @@ class TestC64Zeropage {
@Test @Test
fun testFreeSpaces() { fun testFreeSpaces() {
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false)) 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)) 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)) 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)) val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
assertEquals(238, zp4.available()) assertEquals(238, zp4.available())
} }
@ -220,7 +220,7 @@ class TestC64Zeropage {
@Test @Test
fun testBasicsafeAllocation() { fun testBasicsafeAllocation() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
assertEquals(16, zp.available()) assertEquals(18, zp.available())
assertFailsWith<ZeropageDepletedError> { assertFailsWith<ZeropageDepletedError> {
// in regular zp there aren't 5 sequential bytes free // in regular zp there aren't 5 sequential bytes free
@ -273,16 +273,18 @@ class TestC64Zeropage {
@Test @Test
fun testEfficientAllocation() { fun testEfficientAllocation() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false)) 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(0x04, zp.allocate("", DataType.WORD, null, errors))
assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors)) assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors))
assertEquals(0x0a, zp.allocate("", DataType.UBYTE, null, errors)) assertEquals(0x0a, zp.allocate("", DataType.UBYTE, null, errors))
assertEquals(0x94, zp.allocate("", DataType.UWORD, null, errors)) assertEquals(0x9b, zp.allocate("", DataType.UWORD, null, errors))
assertEquals(0xa7, zp.allocate("", DataType.UWORD, null, errors)) assertEquals(0x9e, zp.allocate("", DataType.UWORD, null, errors))
assertEquals(0xa9, zp.allocate("", DataType.UWORD, null, errors)) assertEquals(0xa5, zp.allocate("", DataType.UWORD, null, errors))
assertEquals(0xb5, zp.allocate("", DataType.UWORD, null, errors)) assertEquals(0xb0, zp.allocate("", DataType.UWORD, null, errors))
assertEquals(0xf7, zp.allocate("", DataType.UWORD, null, errors)) assertEquals(0xbe, zp.allocate("", DataType.UWORD, null, errors))
assertEquals(0x0e, zp.allocate("", DataType.UBYTE, 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(0xf9, zp.allocate("", DataType.UBYTE, null, errors))
assertEquals(0, zp.available()) assertEquals(0, zp.available())
} }

View File

@ -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

View File

@ -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')
}
}
}

View File

@ -1,14 +1,42 @@
%import textio %import textio
%import syslib %import diskio
%import floats
%zeropage basicsafe %zeropage basicsafe
main { main {
sub start() { 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() { asmsub testX() {
%asm {{ %asm {{
stx _saveX stx _saveX
@ -24,6 +52,3 @@ _saveX .byte 0
}} }}
} }
} }