mirror of
https://github.com/irmen/prog8.git
synced 2024-12-23 09:32:43 +00:00
vm: implemented reading/writing files in diskio
This commit is contained in:
parent
76b05cb5fd
commit
483d193ced
@ -1,4 +1,8 @@
|
|||||||
; file I/O routines. (partially implemented)
|
; File I/O routines for the VM target
|
||||||
|
;
|
||||||
|
; NOTE: not all is implemented.
|
||||||
|
; NOTE: some calls are slightly different from the "official" diskio library because for example,
|
||||||
|
; here we cannot deal with multiple return values.
|
||||||
|
|
||||||
%import textio
|
%import textio
|
||||||
%import syslib
|
%import syslib
|
||||||
@ -59,37 +63,86 @@ diskio {
|
|||||||
; NOTE: the default input isn't yet set to this logical file, you must use reset_read_channel() to do this,
|
; NOTE: the default input isn't yet set to this logical file, you must use reset_read_channel() to do this,
|
||||||
; if you're going to read from it yourself instead of using f_read()!
|
; if you're going to read from it yourself instead of using f_read()!
|
||||||
|
|
||||||
txt.print("@TODO: f_open\n")
|
%ir {{
|
||||||
return false
|
loadm.w r65535,diskio.f_open.filenameptr
|
||||||
|
syscall 52 (r65535.w): r0.b
|
||||||
|
returnr.b r0
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
|
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
|
||||||
; -- read from the currently open file, up to the given number of bytes.
|
; -- read from the currently open file, up to the given number of bytes.
|
||||||
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
|
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
|
||||||
txt.print("@TODO: f_read\n")
|
uword actual
|
||||||
return 0
|
repeat num_bytes {
|
||||||
|
%ir {{
|
||||||
|
syscall 54 (): r0.w
|
||||||
|
storem.w r0,$ff02
|
||||||
|
}}
|
||||||
|
if cx16.r0H==0
|
||||||
|
return actual
|
||||||
|
@(bufferpointer) = cx16.r0L
|
||||||
|
bufferpointer++
|
||||||
|
actual++
|
||||||
|
}
|
||||||
|
return actual
|
||||||
}
|
}
|
||||||
|
|
||||||
sub f_read_all(uword bufferpointer) -> uword {
|
sub f_read_all(uword bufferpointer) -> uword {
|
||||||
; -- read the full contents of the file, returns number of bytes read.
|
; -- read the full contents of the file, returns number of bytes read.
|
||||||
; It is assumed the file size is less than 64 K.
|
; It is assumed the file size is less than 64 K.
|
||||||
txt.print("@TODO: f_read_all\n")
|
uword actual
|
||||||
return 0
|
repeat {
|
||||||
|
%ir {{
|
||||||
|
syscall 54 (): r0.w
|
||||||
|
storem.w r0,$ff02
|
||||||
|
}}
|
||||||
|
if cx16.r0H==0
|
||||||
|
return actual
|
||||||
|
@(bufferpointer) = cx16.r0L
|
||||||
|
bufferpointer++
|
||||||
|
actual++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub f_readline(uword bufptr) -> ubyte {
|
sub f_readline(uword bufptr) -> ubyte {
|
||||||
; Routine to read text lines from a text file. Lines must be less than 255 characters.
|
; Routine to read text lines from a text file. Lines must be less than 255 characters.
|
||||||
; Reads characters from the input file UNTIL a newline or return character (or EOF).
|
; Reads characters from the input file UNTIL a newline or return character (or EOF).
|
||||||
; The line read will be 0-terminated in the buffer (and not contain the end of line character).
|
; The line read will be 0-terminated in the buffer (and not contain the end of line character).
|
||||||
; The length of the line is returned in Y. Note that an empty line is okay and is length 0!
|
; The length of the line is returned. Note that an empty line is okay and is length 0!
|
||||||
; This routine is not able here to return the status as well in a secondary return value, so you have to do that yourself.
|
; The success status is returned in the Carry flag instead: C set = success, C clear = failure/endoffile
|
||||||
txt.print("@TODO: f_readline\n")
|
ubyte size
|
||||||
return 0
|
repeat {
|
||||||
|
%ir {{
|
||||||
|
syscall 54 (): r0.w
|
||||||
|
storem.w r0,$ff02
|
||||||
|
}}
|
||||||
|
|
||||||
|
if cx16.r0H==0 {
|
||||||
|
sys.clear_carry()
|
||||||
|
return size
|
||||||
|
} else {
|
||||||
|
if cx16.r0L == '\n' or cx16.r0L=='\r' {
|
||||||
|
@(bufptr) = 0
|
||||||
|
sys.set_carry()
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
@(bufptr) = cx16.r0L
|
||||||
|
bufptr++
|
||||||
|
size++
|
||||||
|
if_z {
|
||||||
|
@(bufptr) = 0
|
||||||
|
return 255
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub f_close() {
|
sub f_close() {
|
||||||
; -- end an iterative file loading session (close channels).
|
; -- end an iterative file loading session (close channels).
|
||||||
txt.print("@TODO: f_close\n")
|
%ir {{
|
||||||
|
syscall 56 ()
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -102,20 +155,35 @@ diskio {
|
|||||||
; (for example, if it already exists). This is different than f_open()!
|
; (for example, if it already exists). This is different than f_open()!
|
||||||
; To be 100% sure if this call was successful, you have to use status()
|
; To be 100% sure if this call was successful, you have to use status()
|
||||||
; and check the drive's status message!
|
; and check the drive's status message!
|
||||||
txt.print("@TODO: f_open_w\n")
|
%ir {{
|
||||||
return false
|
loadm.w r65535,diskio.f_open_w.filenameptr
|
||||||
|
syscall 53 (r65535.w): r0.b
|
||||||
|
returnr.b r0
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub f_write(uword bufferpointer, uword num_bytes) -> bool {
|
sub f_write(uword bufferpointer, uword num_bytes) -> bool {
|
||||||
; -- write the given number of bytes to the currently open file
|
; -- write the given number of bytes to the currently open file
|
||||||
; you can call this multiple times to append more data
|
; you can call this multiple times to append more data
|
||||||
txt.print("@TODO: f_write\n")
|
repeat num_bytes {
|
||||||
|
%ir {{
|
||||||
|
loadm.w r0,diskio.f_write.bufferpointer
|
||||||
|
loadi.b r1,r0
|
||||||
|
syscall 55 (r1.b): r0.b
|
||||||
|
storem.b r0,$ff02
|
||||||
|
}}
|
||||||
|
if cx16.r0L==0
|
||||||
return false
|
return false
|
||||||
|
bufferpointer++
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
sub f_close_w() {
|
sub f_close_w() {
|
||||||
; -- end an iterative file writing session (close channels).
|
; -- end an iterative file writing session (close channels).
|
||||||
txt.print("@TODO: f_close_w\n")
|
%ir {{
|
||||||
|
syscall 57 ()
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -123,23 +191,34 @@ diskio {
|
|||||||
|
|
||||||
sub chdir(str path) {
|
sub chdir(str path) {
|
||||||
; -- change current directory.
|
; -- change current directory.
|
||||||
txt.print("@TODO: chdir\n")
|
%ir {{
|
||||||
|
loadm.w r65535,diskio.chdir.path
|
||||||
|
syscall 50 (r65535.w)
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub mkdir(str name) {
|
sub mkdir(str name) {
|
||||||
; -- make a new subdirectory.
|
; -- make a new subdirectory.
|
||||||
txt.print("@TODO: mkdir\n")
|
%ir {{
|
||||||
|
loadm.w r65535,diskio.mkdir.name
|
||||||
|
syscall 49 (r65535.w)
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub rmdir(str name) {
|
sub rmdir(str name) {
|
||||||
; -- remove a subdirectory.
|
; -- remove a subdirectory.
|
||||||
txt.print("@TODO: rmdir\n")
|
%ir {{
|
||||||
|
loadm.w r65535,diskio.rmdir.name
|
||||||
|
syscall 51 (r65535.w)
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub curdir() -> uword {
|
sub curdir() -> uword {
|
||||||
; return current directory name or 0 if error
|
; return current directory name or 0 if error
|
||||||
txt.print("@TODO: curdir\n")
|
%ir {{
|
||||||
return 0
|
syscall 48 (): r0.w
|
||||||
|
returnr.w r0
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub status() -> str {
|
sub status() -> str {
|
||||||
|
@ -59,7 +59,7 @@ Libraries:
|
|||||||
- fix the problems in atari target, and flesh out its libraries.
|
- fix the problems in atari target, and flesh out its libraries.
|
||||||
- c128 target: make syslib more complete (missing kernal routines)?
|
- c128 target: make syslib more complete (missing kernal routines)?
|
||||||
- pet32 target: make syslib more complete (missing kernal routines)?
|
- pet32 target: make syslib more complete (missing kernal routines)?
|
||||||
- VM: implement more diskio support
|
- VM: implement the last diskio support (file listings)
|
||||||
|
|
||||||
|
|
||||||
Optimizations:
|
Optimizations:
|
||||||
|
@ -1,11 +1,51 @@
|
|||||||
%import textio
|
%import textio
|
||||||
|
%import diskio
|
||||||
%option no_sysinit
|
%option no_sysinit
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
alias print = txt.print
|
txt.lowercase()
|
||||||
alias zz=print
|
|
||||||
zz("chained")
|
uword buffer = memory("buffer", 16000, 0)
|
||||||
|
uword line = 0
|
||||||
|
|
||||||
|
if diskio.f_open("gradlew.bat") {
|
||||||
|
uword size = diskio.f_read(buffer, 1000)
|
||||||
|
txt.print_uw(size)
|
||||||
|
txt.nl()
|
||||||
|
diskio.f_close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if diskio.f_open("gradlew.bat") {
|
||||||
|
size = diskio.f_read_all(buffer)
|
||||||
|
txt.print_uw(size)
|
||||||
|
txt.nl()
|
||||||
|
|
||||||
|
diskio.f_close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if diskio.f_open("gradlew.bat") {
|
||||||
|
ubyte linesize, status
|
||||||
|
repeat {
|
||||||
|
linesize = diskio.f_readline(buffer)
|
||||||
|
if_cs {
|
||||||
|
line++
|
||||||
|
} else break
|
||||||
|
}
|
||||||
|
diskio.f_close()
|
||||||
|
|
||||||
|
txt.print_uw(line)
|
||||||
|
txt.nl()
|
||||||
|
}
|
||||||
|
|
||||||
|
if diskio.f_open_w("result.txt") {
|
||||||
|
void diskio.f_write("line 1\n", 7)
|
||||||
|
void diskio.f_write("line 2\n", 7)
|
||||||
|
void diskio.f_write("line 3\n", 7)
|
||||||
|
diskio.f_close_w()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,11 @@ package prog8.vm
|
|||||||
import prog8.intermediate.FunctionCallArgs
|
import prog8.intermediate.FunctionCallArgs
|
||||||
import prog8.intermediate.IRDataType
|
import prog8.intermediate.IRDataType
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.*
|
||||||
import kotlin.io.path.listDirectoryEntries
|
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
|
|
||||||
/*
|
/*
|
||||||
SYSCALLS:
|
SYSCALLS: DO NOT RENUMBER THESE OR YOU WILL BREAK EXISTING CODE
|
||||||
|
|
||||||
0 = reset ; resets system
|
0 = reset ; resets system
|
||||||
1 = exit ; stops program and returns statuscode from r0.w
|
1 = exit ; stops program and returns statuscode from r0.w
|
||||||
@ -58,6 +57,16 @@ SYSCALLS:
|
|||||||
45 = directory
|
45 = directory
|
||||||
46 = getconsolesize
|
46 = getconsolesize
|
||||||
47 = memcmp
|
47 = memcmp
|
||||||
|
48 = CURDIR
|
||||||
|
49 = MKDIR
|
||||||
|
50 = CHDIR
|
||||||
|
51 = RMDIR
|
||||||
|
52 = OPEN_FILE
|
||||||
|
53 = OPEN_FILE_WRITE
|
||||||
|
54 = READ_FILE_BYTE
|
||||||
|
55 = WRITE_FILE_BYTE
|
||||||
|
56 = CLOSE_FILE
|
||||||
|
57 = CLOSE_FILE_WRITE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
enum class Syscall {
|
enum class Syscall {
|
||||||
@ -108,7 +117,17 @@ enum class Syscall {
|
|||||||
RENAME,
|
RENAME,
|
||||||
DIRECTORY,
|
DIRECTORY,
|
||||||
GETGONSOLESIZE,
|
GETGONSOLESIZE,
|
||||||
MEMCMP
|
MEMCMP,
|
||||||
|
CURDIR,
|
||||||
|
MKDIR,
|
||||||
|
CHDIR,
|
||||||
|
RMDIR,
|
||||||
|
OPEN_FILE,
|
||||||
|
OPEN_FILE_WRITE,
|
||||||
|
READ_FILE_BYTE,
|
||||||
|
WRITE_FILE_BYTE,
|
||||||
|
CLOSE_FILE,
|
||||||
|
CLOSE_FILE_WRITE,
|
||||||
;
|
;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -517,7 +536,7 @@ object SysCalls {
|
|||||||
}
|
}
|
||||||
Syscall.DIRECTORY -> {
|
Syscall.DIRECTORY -> {
|
||||||
// no arguments
|
// no arguments
|
||||||
val directory = Path(".")
|
val directory = Path("")
|
||||||
println("Directory listing for ${directory.toAbsolutePath().normalize()}")
|
println("Directory listing for ${directory.toAbsolutePath().normalize()}")
|
||||||
directory.listDirectoryEntries().sorted().forEach {
|
directory.listDirectoryEntries().sorted().forEach {
|
||||||
println("${it.toFile().length()}\t${it.normalize()}")
|
println("${it.toFile().length()}\t${it.normalize()}")
|
||||||
@ -552,6 +571,51 @@ object SysCalls {
|
|||||||
}
|
}
|
||||||
return returnValue(callspec.returns.single(), 30*256 + 80, vm) // just return some defaults in this case 80*30
|
return returnValue(callspec.returns.single(), 30*256 + 80, vm) // just return some defaults in this case 80*30
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Syscall.CURDIR -> {
|
||||||
|
val curdir = Path("").toAbsolutePath().toString()
|
||||||
|
vm.memory.setString(0xfe00, curdir, true)
|
||||||
|
return returnValue(callspec.returns.single(), 0xfe00, vm)
|
||||||
|
}
|
||||||
|
Syscall.MKDIR -> {
|
||||||
|
val namePtr = getArgValues(callspec.arguments, vm).single() as UShort
|
||||||
|
val name = vm.memory.getString(namePtr.toInt())
|
||||||
|
Path(name).createDirectory()
|
||||||
|
}
|
||||||
|
Syscall.RMDIR -> {
|
||||||
|
val namePtr = getArgValues(callspec.arguments, vm).single() as UShort
|
||||||
|
val name = vm.memory.getString(namePtr.toInt())
|
||||||
|
Path(name).deleteIfExists()
|
||||||
|
}
|
||||||
|
Syscall.CHDIR -> throw NotImplementedError("chdir is not possible in java/kotlin")
|
||||||
|
Syscall.OPEN_FILE -> {
|
||||||
|
val namePtr = getArgValues(callspec.arguments, vm).single() as UShort
|
||||||
|
val name = vm.memory.getString(namePtr.toInt())
|
||||||
|
val success = vm.open_file_read(name)
|
||||||
|
return returnValue(callspec.returns.single(), success, vm)
|
||||||
|
}
|
||||||
|
Syscall.OPEN_FILE_WRITE -> {
|
||||||
|
val namePtr = getArgValues(callspec.arguments, vm).single() as UShort
|
||||||
|
val name = vm.memory.getString(namePtr.toInt())
|
||||||
|
val success = vm.open_file_write(name)
|
||||||
|
return returnValue(callspec.returns.single(), success, vm)
|
||||||
|
}
|
||||||
|
Syscall.READ_FILE_BYTE -> {
|
||||||
|
val (success, byte) = vm.read_file_byte()
|
||||||
|
if(success)
|
||||||
|
return returnValue(callspec.returns.single(), 0x0100 or byte.toInt(), vm)
|
||||||
|
else
|
||||||
|
return returnValue(callspec.returns.single(), 0x0000, vm)
|
||||||
|
}
|
||||||
|
Syscall.WRITE_FILE_BYTE -> {
|
||||||
|
val byte = getArgValues(callspec.arguments, vm).single() as UByte
|
||||||
|
if(vm.write_file_byte(byte))
|
||||||
|
return returnValue(callspec.returns.single(), 1, vm)
|
||||||
|
else
|
||||||
|
return returnValue(callspec.returns.single(), 0, vm)
|
||||||
|
}
|
||||||
|
Syscall.CLOSE_FILE -> vm.close_file_read()
|
||||||
|
Syscall.CLOSE_FILE_WRITE -> vm.close_file_write()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,12 @@ import prog8.code.target.virtual.VirtualMachineDefinition
|
|||||||
import prog8.intermediate.*
|
import prog8.intermediate.*
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
import java.awt.Toolkit
|
import java.awt.Toolkit
|
||||||
import kotlin.collections.ArrayDeque
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.inputStream
|
||||||
|
import kotlin.io.path.outputStream
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@ -34,6 +39,9 @@ class BreakpointException(val pcChunk: IRCodeChunk, val pcIndex: Int): Exception
|
|||||||
@Suppress("FunctionName")
|
@Suppress("FunctionName")
|
||||||
class VirtualMachine(irProgram: IRProgram) {
|
class VirtualMachine(irProgram: IRProgram) {
|
||||||
class CallSiteContext(val returnChunk: IRCodeChunk, val returnIndex: Int, val fcallSpec: FunctionCallArgs)
|
class CallSiteContext(val returnChunk: IRCodeChunk, val returnIndex: Int, val fcallSpec: FunctionCallArgs)
|
||||||
|
|
||||||
|
private var fileOutputStream: OutputStream? = null
|
||||||
|
private var fileInputStream: InputStream? = null
|
||||||
val memory = Memory()
|
val memory = Memory()
|
||||||
val machinedef = VirtualMachineDefinition()
|
val machinedef = VirtualMachineDefinition()
|
||||||
val program: List<IRCodeChunk>
|
val program: List<IRCodeChunk>
|
||||||
@ -2489,6 +2497,62 @@ class VirtualMachine(irProgram: IRProgram) {
|
|||||||
fun randomSeedFloat(seed: Double) {
|
fun randomSeedFloat(seed: Double) {
|
||||||
randomGeneratorFloats = Random(seed.toBits())
|
randomGeneratorFloats = Random(seed.toBits())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun open_file_read(name: String): Int {
|
||||||
|
try {
|
||||||
|
fileInputStream = Path(name).inputStream()
|
||||||
|
} catch (_: IOException) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun open_file_write(name: String): Int {
|
||||||
|
try {
|
||||||
|
fileOutputStream = Path(name).outputStream()
|
||||||
|
} catch (_: IOException) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun close_file_read() {
|
||||||
|
fileInputStream?.close()
|
||||||
|
fileInputStream=null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun close_file_write() {
|
||||||
|
fileOutputStream?.flush()
|
||||||
|
fileOutputStream?.close()
|
||||||
|
fileOutputStream=null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun read_file_byte(): Pair<Boolean, UByte> {
|
||||||
|
return if(fileInputStream==null)
|
||||||
|
false to 0u
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
val byte = fileInputStream!!.read()
|
||||||
|
if(byte>=0)
|
||||||
|
true to byte.toUByte()
|
||||||
|
else
|
||||||
|
false to 0u
|
||||||
|
} catch(_: IOException) {
|
||||||
|
false to 0u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun write_file_byte(byte: UByte): Boolean {
|
||||||
|
if(fileOutputStream==null)
|
||||||
|
return false
|
||||||
|
try {
|
||||||
|
fileOutputStream!!.write(byte.toInt())
|
||||||
|
return true
|
||||||
|
} catch(_: IOException) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun ArrayDeque<UByte>.pushw(value: UShort) {
|
internal fun ArrayDeque<UByte>.pushw(value: UShort) {
|
||||||
|
Loading…
Reference in New Issue
Block a user