mirror of
https://github.com/irmen/prog8.git
synced 2024-12-22 18:30:01 +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 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,
|
||||
; if you're going to read from it yourself instead of using f_read()!
|
||||
|
||||
txt.print("@TODO: f_open\n")
|
||||
return false
|
||||
%ir {{
|
||||
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 {
|
||||
; -- 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)
|
||||
txt.print("@TODO: f_read\n")
|
||||
return 0
|
||||
uword actual
|
||||
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 {
|
||||
; -- read the full contents of the file, returns number of bytes read.
|
||||
; It is assumed the file size is less than 64 K.
|
||||
txt.print("@TODO: f_read_all\n")
|
||||
return 0
|
||||
uword actual
|
||||
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 {
|
||||
; 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).
|
||||
; 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!
|
||||
; This routine is not able here to return the status as well in a secondary return value, so you have to do that yourself.
|
||||
txt.print("@TODO: f_readline\n")
|
||||
return 0
|
||||
; The length of the line is returned. Note that an empty line is okay and is length 0!
|
||||
; The success status is returned in the Carry flag instead: C set = success, C clear = failure/endoffile
|
||||
ubyte size
|
||||
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() {
|
||||
; -- 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()!
|
||||
; To be 100% sure if this call was successful, you have to use status()
|
||||
; and check the drive's status message!
|
||||
txt.print("@TODO: f_open_w\n")
|
||||
return false
|
||||
%ir {{
|
||||
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 {
|
||||
; -- write the given number of bytes to the currently open file
|
||||
; you can call this multiple times to append more data
|
||||
txt.print("@TODO: f_write\n")
|
||||
return false
|
||||
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
|
||||
bufferpointer++
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
sub f_close_w() {
|
||||
; -- 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) {
|
||||
; -- change current directory.
|
||||
txt.print("@TODO: chdir\n")
|
||||
%ir {{
|
||||
loadm.w r65535,diskio.chdir.path
|
||||
syscall 50 (r65535.w)
|
||||
}}
|
||||
}
|
||||
|
||||
sub mkdir(str name) {
|
||||
; -- make a new subdirectory.
|
||||
txt.print("@TODO: mkdir\n")
|
||||
%ir {{
|
||||
loadm.w r65535,diskio.mkdir.name
|
||||
syscall 49 (r65535.w)
|
||||
}}
|
||||
}
|
||||
|
||||
sub rmdir(str name) {
|
||||
; -- remove a subdirectory.
|
||||
txt.print("@TODO: rmdir\n")
|
||||
%ir {{
|
||||
loadm.w r65535,diskio.rmdir.name
|
||||
syscall 51 (r65535.w)
|
||||
}}
|
||||
}
|
||||
|
||||
sub curdir() -> uword {
|
||||
; return current directory name or 0 if error
|
||||
txt.print("@TODO: curdir\n")
|
||||
return 0
|
||||
%ir {{
|
||||
syscall 48 (): r0.w
|
||||
returnr.w r0
|
||||
}}
|
||||
}
|
||||
|
||||
sub status() -> str {
|
||||
|
@ -59,7 +59,7 @@ Libraries:
|
||||
- fix the problems in atari target, and flesh out its libraries.
|
||||
- c128 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:
|
||||
|
@ -1,11 +1,51 @@
|
||||
%import textio
|
||||
%import diskio
|
||||
%option no_sysinit
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
alias print = txt.print
|
||||
alias zz=print
|
||||
zz("chained")
|
||||
txt.lowercase()
|
||||
|
||||
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.IRDataType
|
||||
import java.io.File
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.listDirectoryEntries
|
||||
import kotlin.io.path.*
|
||||
import kotlin.math.*
|
||||
|
||||
/*
|
||||
SYSCALLS:
|
||||
SYSCALLS: DO NOT RENUMBER THESE OR YOU WILL BREAK EXISTING CODE
|
||||
|
||||
0 = reset ; resets system
|
||||
1 = exit ; stops program and returns statuscode from r0.w
|
||||
@ -58,6 +57,16 @@ SYSCALLS:
|
||||
45 = directory
|
||||
46 = getconsolesize
|
||||
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 {
|
||||
@ -108,7 +117,17 @@ enum class Syscall {
|
||||
RENAME,
|
||||
DIRECTORY,
|
||||
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 {
|
||||
@ -517,7 +536,7 @@ object SysCalls {
|
||||
}
|
||||
Syscall.DIRECTORY -> {
|
||||
// no arguments
|
||||
val directory = Path(".")
|
||||
val directory = Path("")
|
||||
println("Directory listing for ${directory.toAbsolutePath().normalize()}")
|
||||
directory.listDirectoryEntries().sorted().forEach {
|
||||
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
|
||||
}
|
||||
|
||||
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 java.awt.Color
|
||||
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.random.Random
|
||||
|
||||
@ -34,6 +39,9 @@ class BreakpointException(val pcChunk: IRCodeChunk, val pcIndex: Int): Exception
|
||||
@Suppress("FunctionName")
|
||||
class VirtualMachine(irProgram: IRProgram) {
|
||||
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 machinedef = VirtualMachineDefinition()
|
||||
val program: List<IRCodeChunk>
|
||||
@ -2489,6 +2497,62 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
fun randomSeedFloat(seed: Double) {
|
||||
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) {
|
||||
|
Loading…
Reference in New Issue
Block a user