mirror of
https://github.com/KarolS/millfork.git
synced 2024-09-27 12:57:41 +00:00
C64: File I/O support
This commit is contained in:
parent
7d596f3ed6
commit
492300d298
@ -49,6 +49,8 @@
|
|||||||
|
|
||||||
* [C64-only modules](stdlib/c64.md)
|
* [C64-only modules](stdlib/c64.md)
|
||||||
|
|
||||||
|
* [`cbm_file` module](stdlib/cbm_file.md)
|
||||||
|
|
||||||
* [NES-only modules](stdlib/nes.md)
|
* [NES-only modules](stdlib/nes.md)
|
||||||
|
|
||||||
## Implementation details
|
## Implementation details
|
||||||
|
70
docs/api/commodore-programming-guide.md
Normal file
70
docs/api/commodore-programming-guide.md
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
[< back to index](../index.md)
|
||||||
|
|
||||||
|
### A note about Commodore 64
|
||||||
|
|
||||||
|
#### Multifile programs
|
||||||
|
|
||||||
|
A multifile program is a program stored on a disk that consists of the main program file that is executed first
|
||||||
|
and several other files that can be loaded into memory on demand.
|
||||||
|
This allows for creating programs that are larger than 64 kilobytes.
|
||||||
|
|
||||||
|
Millfork allows building such programs, but leaves several things to the programmer:
|
||||||
|
|
||||||
|
* tracking of which parts of the program are currently loaded and which are not
|
||||||
|
|
||||||
|
* whether memory ranges of various parts overlap or not
|
||||||
|
|
||||||
|
* whether loading succeeded or not
|
||||||
|
|
||||||
|
##### Writing multifile programs
|
||||||
|
|
||||||
|
You will need to create a platform definition file with multiple segments.
|
||||||
|
The default segment should start at $80D.
|
||||||
|
Example:
|
||||||
|
|
||||||
|
segments=default,extra
|
||||||
|
; the first file will contain the initial code:
|
||||||
|
default_code_segment=default
|
||||||
|
segment_default_start=$80D
|
||||||
|
segment_default_codeend=$7fff
|
||||||
|
segment_default_datastart=after_code
|
||||||
|
segment_default_end=$7fff
|
||||||
|
; the second file will contain the extra code:
|
||||||
|
segment_extra_start=$8000
|
||||||
|
segment_extra_codeend=$9fff
|
||||||
|
segment_extra_datastart=after_code
|
||||||
|
segment_extra_end=$cfff
|
||||||
|
|
||||||
|
You also need to set `style=per_segment` in the `[output]` section.
|
||||||
|
|
||||||
|
Annotate things you want to place in that file with the `segment` keyword:
|
||||||
|
|
||||||
|
segment(extra)
|
||||||
|
void extra_function () {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
Then in your code you need to load the file:
|
||||||
|
|
||||||
|
load_file(last_used_device(), "eee"z)
|
||||||
|
if errno == err_ok {
|
||||||
|
extra_function()
|
||||||
|
} else {
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
|
||||||
|
(Prefer `last_used_device()` instead of hardcoded `8`, so your program will work when loaded from any disk drive.)
|
||||||
|
|
||||||
|
Compiling the program with `-o OUTPUT` will yield several PRG files.
|
||||||
|
The default segment will be in `OUTPUT.prg`, the segment called `extra` in `OUTPUT.extra.prg` and so on.
|
||||||
|
|
||||||
|
##### Packaging multifile programs
|
||||||
|
|
||||||
|
The Millfork compiler does not create Commodore disk images.
|
||||||
|
|
||||||
|
You can use a variety of tools to perform that task,
|
||||||
|
for example the `c1531` tool shipped with [the VICE emulator](http://vice-emu.sourceforge.net/).
|
||||||
|
|
||||||
|
To create a new disk image for the last example, use:
|
||||||
|
|
||||||
|
c1541 -format "example,01" d64 example.d64 -write OUTPUT.prg start -write OUTPUT.extra.prg eee
|
@ -15,6 +15,8 @@ The following platforms are currently supported:
|
|||||||
|
|
||||||
* `c64` – Commodore 64.
|
* `c64` – Commodore 64.
|
||||||
The compiler emits PRG files, not disk or tape images.
|
The compiler emits PRG files, not disk or tape images.
|
||||||
|
If you want to create a program consisting of multiple PRG files,
|
||||||
|
see [the Commodore programming guide](./commodore-programming-guide.md) for more info.
|
||||||
|
|
||||||
* `c64_crt8k` – Commodore 64, 8K ROM cartridge
|
* `c64_crt8k` – Commodore 64, 8K ROM cartridge
|
||||||
|
|
||||||
|
@ -49,6 +49,8 @@
|
|||||||
|
|
||||||
* [C64-only modules](stdlib/c64.md)
|
* [C64-only modules](stdlib/c64.md)
|
||||||
|
|
||||||
|
* [`cbm_file` module](stdlib/cbm_file.md)
|
||||||
|
|
||||||
* [NES-only modules](stdlib/nes.md)
|
* [NES-only modules](stdlib/nes.md)
|
||||||
|
|
||||||
## Implementation details
|
## Implementation details
|
||||||
|
@ -2,14 +2,18 @@
|
|||||||
|
|
||||||
# Commodore 64-oriented modules
|
# Commodore 64-oriented modules
|
||||||
|
|
||||||
## `c64_kernal` module
|
## c64_kernal module
|
||||||
|
|
||||||
The `c64_kernal` module is imported automatically on the C64 target.
|
The `c64_kernal` module is imported automatically on the C64 target.
|
||||||
|
It provides access to Kernal routines, so it requires the Kernal ROM to be enabled.
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
|
|
||||||
## c64_basic module
|
## c64_basic module
|
||||||
|
|
||||||
|
The `c64_basic` module provides access to Kernal routines, so it requires the Basic ROM to be enabled.
|
||||||
|
In particular, this means that it will not work on cartridge targets without extra preparations.
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
|
|
||||||
## c64_hardware
|
## c64_hardware
|
||||||
|
81
docs/stdlib/cbm_file.md
Normal file
81
docs/stdlib/cbm_file.md
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
[< back to index](../index.md)
|
||||||
|
|
||||||
|
## cbm_file
|
||||||
|
|
||||||
|
The `cbm_file` module provides support for loading and saving files to tape and disk.
|
||||||
|
Currently, it works only for Commodore 64 targets, although support for more targets is coming.
|
||||||
|
It uses Kernal routines, so it requires the Kernal ROM to be enabled.
|
||||||
|
|
||||||
|
#### byte last_used_device()
|
||||||
|
|
||||||
|
Returns the last device number, or 8 if none.
|
||||||
|
|
||||||
|
#### void load_file(byte device, pointer name)
|
||||||
|
|
||||||
|
Loads a PRG file with the given null-terminated name to the address specified in the file.
|
||||||
|
Sets `errno`.
|
||||||
|
|
||||||
|
#### void load_file_at(byte device, pointer name, pointer addr)
|
||||||
|
|
||||||
|
Loads a PRG file with the given null-terminated name to the given address.
|
||||||
|
Sets `errno`.
|
||||||
|
|
||||||
|
#### void save_file(byte device, pointer name, pointer start, word length)
|
||||||
|
|
||||||
|
Saves `length` bytes starting from `start` to a PRG file named `name` on device `device`.
|
||||||
|
Sets `errno`.
|
||||||
|
|
||||||
|
#### void exec_disk(byte device, pointer command)
|
||||||
|
|
||||||
|
Executes a CBM DOS command on the given drive.
|
||||||
|
|
||||||
|
#### void delete_file(byte device, pointer name)
|
||||||
|
|
||||||
|
Deletes given file in the given drive. (`S0:`)
|
||||||
|
|
||||||
|
#### void rename_file(byte device, pointer old_name, pointer new_name)
|
||||||
|
|
||||||
|
Renames given file in the given drive. (`R0:`)
|
||||||
|
|
||||||
|
#### void copy_file(byte device, pointer old_name, pointer new_name)
|
||||||
|
|
||||||
|
Copies given file in the given drive. (`C0:`)
|
||||||
|
|
||||||
|
#### void initialize_disk(byte device)
|
||||||
|
|
||||||
|
Reinitialized disk status in the given drive. (`I0`)
|
||||||
|
|
||||||
|
#### void validate_disk(byte device)
|
||||||
|
|
||||||
|
Validates disk status in the given drive. (`V0`)
|
||||||
|
|
||||||
|
#### void format_disk(byte device)
|
||||||
|
|
||||||
|
Formats disk status in the given drive. (`N0:`)
|
||||||
|
|
||||||
|
#### void open_file(byte device, pointer name, byte fd, byte mode)
|
||||||
|
|
||||||
|
Opens a file.
|
||||||
|
Sets `errno`.
|
||||||
|
TODO: buggy.
|
||||||
|
|
||||||
|
#### const byte MODE_READ = 0
|
||||||
|
#### const byte MODE_WRITE = 1
|
||||||
|
|
||||||
|
#### void close_file(byte fd)
|
||||||
|
|
||||||
|
Closes the given file.
|
||||||
|
Sets `errno`.
|
||||||
|
TODO: buggy.
|
||||||
|
|
||||||
|
#### byte getbyte_safe()
|
||||||
|
|
||||||
|
Reads a byte from file.
|
||||||
|
Sets `errno` to `err_eof` when reading the last byte, so don't abort reading when getting `errno != err_ok`.
|
||||||
|
TODO: buggy.
|
||||||
|
|
||||||
|
#### void putbyte_safe(byte b)
|
||||||
|
|
||||||
|
Wrires a byte from file.
|
||||||
|
Sets `errno`.
|
||||||
|
TODO: buggy.
|
@ -23,3 +23,8 @@ If the source string is longer than 255 bytes, then the behaviour is undefined (
|
|||||||
|
|
||||||
Converts a null-terminated string to a number.
|
Converts a null-terminated string to a number.
|
||||||
Sets `errno`.
|
Sets `errno`.
|
||||||
|
|
||||||
|
#### `void strzappend(pointer buffer, pointer str)`
|
||||||
|
#### `void strzappendchar(pointer buffer, byte char)`
|
||||||
|
|
||||||
|
Modifies the given null-terminated buffer by appending a null-terminated string or s single character respectively.
|
||||||
|
@ -28,6 +28,9 @@
|
|||||||
|
|
||||||
### Other examples
|
### Other examples
|
||||||
|
|
||||||
|
* Multifile ([source code](c64/multifile.mfk), [platform definition](c64/multifile.ini)) –
|
||||||
|
how to create a program made of multiple files loaded on demand
|
||||||
|
|
||||||
* [Panic](c64/panic_test.mfk) – how panic works on C64, showing the address of where it happened
|
* [Panic](c64/panic_test.mfk) – how panic works on C64, showing the address of where it happened
|
||||||
|
|
||||||
## Famicom/NES examples
|
## Famicom/NES examples
|
||||||
|
42
examples/c64/multifile.ini
Normal file
42
examples/c64/multifile.ini
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
; Commodore C64
|
||||||
|
; mostly based on c64.ini
|
||||||
|
; to use with multifile.mfk
|
||||||
|
|
||||||
|
[compilation]
|
||||||
|
arch=nmos
|
||||||
|
encoding=petscii
|
||||||
|
screen_encoding=petscr
|
||||||
|
modules=c64_hardware,loader_0801,c64_kernal,c64_panic,stdlib
|
||||||
|
|
||||||
|
[allocation]
|
||||||
|
zp_pointers=$FB,$FD,$43,$45,$47,$4B,$F7,$F9,$9E,$9B,$3D
|
||||||
|
; we want to have two output files:
|
||||||
|
segments=default,extra
|
||||||
|
; the first file will contain the initial code:
|
||||||
|
default_code_segment=default
|
||||||
|
segment_default_start=$80D
|
||||||
|
segment_default_codeend=$7fff
|
||||||
|
segment_default_datastart=after_code
|
||||||
|
segment_default_end=$7fff
|
||||||
|
; the second file will contain the extra code:
|
||||||
|
segment_extra_start=$8000
|
||||||
|
segment_extra_codeend=$9fff
|
||||||
|
segment_extra_datastart=after_code
|
||||||
|
segment_extra_end=$cfff
|
||||||
|
|
||||||
|
[define]
|
||||||
|
CBM=1
|
||||||
|
CBM_64=1
|
||||||
|
MOS_6510=1
|
||||||
|
WIDESCREEN=1
|
||||||
|
KEYBOARD=1
|
||||||
|
JOYSTICKS=2
|
||||||
|
HAS_BITMAP_MODE=1
|
||||||
|
|
||||||
|
[output]
|
||||||
|
; every segment should land in its own file:
|
||||||
|
style=per_segment
|
||||||
|
format=startaddr,allocated
|
||||||
|
extension=prg
|
||||||
|
|
||||||
|
|
23
examples/c64/multifile.mfk
Normal file
23
examples/c64/multifile.mfk
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// compile with
|
||||||
|
// millfork -t multifile -o multifile multifile.mfk
|
||||||
|
// build a disk image with
|
||||||
|
// c1541 -format "multifile,11" d64 multifile.d64 -write multifile.prg start -write multifile.extra.prg extra
|
||||||
|
|
||||||
|
import stdio
|
||||||
|
import cbm_file
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
load_file(last_used_device(), "extra"z)
|
||||||
|
if errno == err_ok {
|
||||||
|
extra()
|
||||||
|
} else {
|
||||||
|
putstrz("failed to load file"z)
|
||||||
|
new_line()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
segment(extra)
|
||||||
|
void extra() {
|
||||||
|
putstrz("hello from loaded file!"z)
|
||||||
|
new_line()
|
||||||
|
}
|
@ -31,7 +31,7 @@ JOYSTICKS=2
|
|||||||
HAS_BITMAP_MODE=1
|
HAS_BITMAP_MODE=1
|
||||||
|
|
||||||
[output]
|
[output]
|
||||||
; how the banks are laid out in the output files; so far, there is no bank support in the compiler yet
|
; how the banks are laid out in the output files
|
||||||
style=single
|
style=single
|
||||||
; output file format
|
; output file format
|
||||||
; startaddr - little-endian address of the first used byte in the bank
|
; startaddr - little-endian address of the first used byte in the bank
|
||||||
|
@ -4,6 +4,25 @@
|
|||||||
// Input: A = Byte to write.
|
// Input: A = Byte to write.
|
||||||
asm void putchar(byte a) @$FFD2 extern
|
asm void putchar(byte a) @$FFD2 extern
|
||||||
|
|
||||||
|
// CHRIN. Read byte from default input (for keyboard, read a line from the screen). (If not keyboard, must call OPEN and CHKIN beforehands.)
|
||||||
|
// Output: A = Byte read.
|
||||||
|
asm byte getchar() @$FFCF extern
|
||||||
|
|
||||||
|
// CHKIN. Define file as default input. (Must call OPEN beforehands.)
|
||||||
|
// Input: X = Logical number.
|
||||||
|
asm void chkin(byte x) @$FFC6 extern
|
||||||
|
|
||||||
|
// CHKOUT. Define file as default output. (Must call OPEN beforehands.)
|
||||||
|
// Input: X = Logical number.
|
||||||
|
asm void chkout(byte x) @$FFC9 extern
|
||||||
|
|
||||||
|
// CLRCHN. Close default input/output files (for serial bus, send UNTALK and/or UNLISTEN); restore default input/output to keyboard/screen.
|
||||||
|
asm void clrchn() @$FFCC extern
|
||||||
|
|
||||||
|
// READST. Fetch status of current input/output device, value of ST variable. (For RS232, status is cleared.)
|
||||||
|
// Output: A = Device status.
|
||||||
|
asm byte readst() @$FFB7 extern
|
||||||
|
|
||||||
inline void new_line() {
|
inline void new_line() {
|
||||||
putchar(13)
|
putchar(13)
|
||||||
}
|
}
|
||||||
|
219
include/cbm_file.mfk
Normal file
219
include/cbm_file.mfk
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
#if not(CBM)
|
||||||
|
#warn cbm_file module should be only used on Commodore targets
|
||||||
|
#endif
|
||||||
|
|
||||||
|
import string
|
||||||
|
import err
|
||||||
|
|
||||||
|
byte __last_used_device @$ba
|
||||||
|
inline byte last_used_device() {
|
||||||
|
byte device
|
||||||
|
device = __last_used_device
|
||||||
|
if device == 0 { device = 8 }
|
||||||
|
return device
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_file(byte device, pointer name) {
|
||||||
|
setnamz(name)
|
||||||
|
setlfs(1, device, 1)
|
||||||
|
asm {
|
||||||
|
lda #0
|
||||||
|
jsr load
|
||||||
|
? jmp __handle_disk_err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_file_at(byte device, pointer name, pointer at) {
|
||||||
|
setnamz(name)
|
||||||
|
setlfs(1, device, 0)
|
||||||
|
asm {
|
||||||
|
lda #0
|
||||||
|
? ldx at
|
||||||
|
? ldy at+1
|
||||||
|
jsr load
|
||||||
|
? jmp __handle_disk_err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
asm void __handle_disk_err() {
|
||||||
|
bcs __handle_disk_err_failed
|
||||||
|
lda #err_ok
|
||||||
|
? jmp __handle_disk_err_store
|
||||||
|
__handle_disk_err_failed:
|
||||||
|
ora #$40
|
||||||
|
jsr $FFD2
|
||||||
|
and #$BF
|
||||||
|
lsr
|
||||||
|
eor #2
|
||||||
|
bne __handle_disk_err_not_4_or_5
|
||||||
|
lda #err_nofile
|
||||||
|
bcc __handle_disk_err_store
|
||||||
|
lda #err_nodevice
|
||||||
|
[ $2c ]
|
||||||
|
__handle_disk_err_not_4_or_5:
|
||||||
|
lda #err_fail
|
||||||
|
__handle_disk_err_store:
|
||||||
|
? sta errno
|
||||||
|
? rts
|
||||||
|
}
|
||||||
|
|
||||||
|
void save_file(byte device, pointer name, pointer start, word length) {
|
||||||
|
setnamz(name)
|
||||||
|
setlfs(1, device, 0)
|
||||||
|
word end
|
||||||
|
end = start + length
|
||||||
|
asm {
|
||||||
|
lda #start
|
||||||
|
? ldx end
|
||||||
|
? ldy end+1
|
||||||
|
jsr save
|
||||||
|
? jmp __handle_disk_err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setnamz(pointer name) {
|
||||||
|
setnam(name, strzlen(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
array __cbm_cmd_buffer[64]
|
||||||
|
|
||||||
|
void exec_disk(byte device, pointer command) {
|
||||||
|
setnamz(command)
|
||||||
|
setlfs(1, device, 15)
|
||||||
|
open()
|
||||||
|
close(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
void __exec_disk(byte device) {
|
||||||
|
setnamz(__cbm_cmd_buffer)
|
||||||
|
setlfs(1, device, 15)
|
||||||
|
open()
|
||||||
|
close(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete_file(byte device, pointer name) {
|
||||||
|
byte i
|
||||||
|
byte length
|
||||||
|
__cbm_cmd_buffer[0] = 's'
|
||||||
|
__cbm_cmd_buffer[1] = '0'
|
||||||
|
__cbm_cmd_buffer[2] = ':'
|
||||||
|
length = strzlen(name)
|
||||||
|
for i,0,parallelto,length {
|
||||||
|
__cbm_cmd_buffer[i + 3] = name[i]
|
||||||
|
}
|
||||||
|
__exec_disk(device)
|
||||||
|
}
|
||||||
|
|
||||||
|
void initialize_disk(byte device) {
|
||||||
|
__cbm_cmd_buffer[0] = 'i'
|
||||||
|
__cbm_cmd_buffer[1] = '0'
|
||||||
|
__cbm_cmd_buffer[2] = 0
|
||||||
|
__exec_disk(device)
|
||||||
|
}
|
||||||
|
|
||||||
|
void validate_disk(byte device) {
|
||||||
|
__cbm_cmd_buffer[0] = 'v'
|
||||||
|
__cbm_cmd_buffer[1] = '0'
|
||||||
|
__cbm_cmd_buffer[2] = 0
|
||||||
|
__exec_disk(device)
|
||||||
|
}
|
||||||
|
|
||||||
|
void format_disk(byte device) {
|
||||||
|
setnam(__cmd_format_disk, __cmd_format_disk.length)
|
||||||
|
setlfs(1, device, 15)
|
||||||
|
open()
|
||||||
|
close(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const byte MODE_READ = 0
|
||||||
|
const byte MODE_WRITE = 1
|
||||||
|
// const byte MODE_OVERWRITE = 3 // TODO: SAVE@ bug?
|
||||||
|
|
||||||
|
void open_file(byte device, pointer name, byte fd, byte mode) {
|
||||||
|
byte length
|
||||||
|
byte i
|
||||||
|
__cbm_cmd_buffer[0] = '0'
|
||||||
|
__cbm_cmd_buffer[1] = ':'
|
||||||
|
length = strzlen(name)
|
||||||
|
for i,0,parallelto,length {
|
||||||
|
__cbm_cmd_buffer[i + 2] = name[i]
|
||||||
|
}
|
||||||
|
if length < 3 || __cbm_cmd_buffer[length] != ',' || (__cbm_cmd_buffer[length+1] != 'r' && __cbm_cmd_buffer[length+1] != 'w') {
|
||||||
|
strzappendchar(__cbm_cmd_buffer, ',')
|
||||||
|
if mode & MODE_WRITE != 0 {
|
||||||
|
strzappendchar(__cbm_cmd_buffer, 'w')
|
||||||
|
} else {
|
||||||
|
strzappendchar(__cbm_cmd_buffer, 'r')
|
||||||
|
}
|
||||||
|
length += 2
|
||||||
|
}
|
||||||
|
setnam(__cbm_cmd_buffer, length + 2)
|
||||||
|
setlfs(fd, device, fd)
|
||||||
|
asm {
|
||||||
|
jsr open
|
||||||
|
? jmp __handle_disk_err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_file(byte fd) {
|
||||||
|
asm {
|
||||||
|
? lda fd
|
||||||
|
? jsr close
|
||||||
|
? jmp __handle_disk_err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __translate_st_to_errno() {
|
||||||
|
byte st
|
||||||
|
st = readst()
|
||||||
|
if st == 0 {
|
||||||
|
errno = err_ok
|
||||||
|
} else if st & 0x80 != 0 {
|
||||||
|
errno = err_nodevice
|
||||||
|
} else if st & 0x40 != 0 {
|
||||||
|
errno = err_eof
|
||||||
|
} else {
|
||||||
|
errno = err_fail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte getbyte_safe() {
|
||||||
|
byte b
|
||||||
|
b = getchar()
|
||||||
|
garbage[garbage_index] = b
|
||||||
|
garbage_index += 1
|
||||||
|
__translate_st_to_errno()
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
void putbyte_safe(byte b) {
|
||||||
|
putchar(b)
|
||||||
|
garbage[garbage_index] = b
|
||||||
|
garbage_index += 1
|
||||||
|
__translate_st_to_errno()
|
||||||
|
}
|
||||||
|
|
||||||
|
array __cmd_format_disk = "n0:disk,01"z
|
||||||
|
|
||||||
|
|
||||||
|
void rename_file(byte device, pointer old_name, pointer new_name) {
|
||||||
|
__cbm_cmd_buffer[0]='r'
|
||||||
|
__cmd_rename_copy_common(device, old_name, new_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy_file(byte device, pointer old_name, pointer new_name) {
|
||||||
|
__cbm_cmd_buffer[0]='c'
|
||||||
|
__cmd_rename_copy_common(device, old_name, new_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
void __cmd_rename_copy_common(byte device, pointer old_name, pointer new_name) {
|
||||||
|
__cbm_cmd_buffer[1]='0'
|
||||||
|
__cbm_cmd_buffer[2]=':'
|
||||||
|
__cbm_cmd_buffer[3]=0
|
||||||
|
strzappend(__cbm_cmd_buffer, new_name)
|
||||||
|
strzappendchar(__cbm_cmd_buffer, '=')
|
||||||
|
strzappend(__cbm_cmd_buffer, old_name)
|
||||||
|
__exec_disk(device)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -5,6 +5,9 @@ enum error_number {
|
|||||||
err_outofmemory
|
err_outofmemory
|
||||||
err_domain
|
err_domain
|
||||||
err_range
|
err_range
|
||||||
|
err_nofile
|
||||||
|
err_nodevice
|
||||||
|
err_eof
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,6 +6,14 @@ import string_fastpointers
|
|||||||
import string_fastindices
|
import string_fastindices
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void strzappend(pointer buffer, pointer str) {
|
||||||
|
strzcopy(buffer + strzlen(buffer), str)
|
||||||
|
}
|
||||||
|
void strzappendchar(pointer buffer, byte char) {
|
||||||
|
buffer += strzlen(buffer)
|
||||||
|
buffer[0] = char
|
||||||
|
buffer[1] = 0
|
||||||
|
}
|
||||||
|
|
||||||
word strz2word(pointer str) {
|
word strz2word(pointer str) {
|
||||||
byte i
|
byte i
|
||||||
|
Loading…
Reference in New Issue
Block a user