Jump to content
  • 0

detecting I/O status in assembly?


desertfish
 Share

Question

Hi, when doing I/O via the kernel routines you can get a long way. However one thing that is missing (as far as I know) is a routine to check the I/O status. 
So I have some assembly code that works on the Commodore-64 where it checks  $90  directly which is the documented zero page location for the I/O status flag on the '64. 

I don't think that address is valid on the cx16.  How are you dealing with I/O errors on the Cx16?   

Link to comment
Share on other sites

Recommended Posts

  • 0

Hi.

I've written some routines that read and write sequential disk files in my project X16 Edit.

Basically, I've followed what's said about the relevant Kernal functions in the C64 PRG.

At least when you access a file, there are two types of errors that may occur.

  • I/O errors thrown by the Kernal, for instance error no 5 - Device not present
  • Errors thrown by the "disk drive", for instance error no 62 - File not found

The carry bit is set if the Kernal throws an I/O error at least when calling

  • SETLFS - $ffba
  • OPEN - $ffc0
  • CHKIN - $ffc6
  • CHKOUT - $ffc9

The error number returned in A register.

The meaning of each error is available in the C64 PRG. In my edition (1 st ed, 9th printing, 1987, it's on page 306). The possible errors are:

  1. Too many files open
  2. File already open
  3. File not open
  4. File not found
  5. Device not present
  6. File is not an input file
  7. File is not an output file
  8. File name is missing
  9. Illegal device number

You may get the other kind of error that is returned by the "disk drive" only by reading the status channel.

In basic you would OPEN 15,8,15 and INPUT#15,E,M$. The last error code returned in E and the error message returned in M$.

An assembly solution is a little more involved. You may look at my file handling functions here if it helps:

https://github.com/stefan-b-jakobsson/x16-edit/blob/master/file.inc

 

Link to comment
Share on other sites

  • 0

I forgot to mention the Kernal function READST in my post from yesterday.

To sum it up, this is what I think you should do to handle I/O errors when reading a file:

  • Call SETNAM to set the file name. This function does not throw I/O errors.
  • Call SETLFS to set file no, device no and command. As I understand, neither this function throws I/O errors
  • Call OPEN to open the file
    • Check carry bit, if set there was an I/O error, and the error number is in A register
    • Call READST to get I/O status returned in A register. I not zero, there was an error. This should not happen on opening a file for reading, but could occur in other I/O operations.
  • Call CHKIN to open a channel for input
    • Check carry bit, as after calling OPEN
    • Call READST as after calling OPEN
  • Call CHRIN to read one character from the device
    • Carry set does not indicate error after calling this function
    • Call READST after every invocation to get I/O status returned in A register. It not zero there was an error or you reached end of file
    • Call CHRIN in a loop until READST returns end of file or an error

My experience is that the above mentioned Kernal errors only occur if there is a problem communicating with the device. For instance Kernal I/O error no 5 - Device not present - will be thrown if a SD card is not mounted.

But the Kernal will say nothing if the error occurs on what used to be the disk drive side in the old days. For example, a file not found error must be checked by reading the disk status. So the last step is to do that, which requires opening another I/O channel:

  • Call SETNAM (name may be empty)
  • Call SETLFS (like OPEN 15,8,15)
  • Call OPEN. Check carry bit and READST for errors as above
  • Call CHKIN to open channel for input. Check carry bit and READST for errors as above
  • Call CHRIN to read status string
  • Call READST and loop back and call CHRIN until READST returns end of file or an error

There are small variances on how to handle errors depending on the type of I/O channel, for instance if it's a RS232 channel instead of a disk file.

One good source for the functioning of the Kernal is here:

https://www.pagetable.com/c64ref/kernal/

It puts the comments on each Kernal function from a handful different books, including the C64 PRG, side by side.

Or in more simple terms. Use the Kernal functions. Even if you find some memory addresses that return the I/O status they might be subject to change.

Edited by Stefan
  • Like 2
  • Thanks 1
Link to comment
Share on other sites

  • 0
3 hours ago, desertfish said:

@Stefan you are a legend. Thanks.

Thanks. I feel like a beginner, though. Almost every time a read through the documentation, I see something new. Truly managing all errors while communicating on the serial bus is not trivial.

Edited by Stefan
  • Like 1
Link to comment
Share on other sites

  • 0

Well look at that. With Stefan's help I managed to make a program that displays the directory of the disk in drive 8.  (without overwriting the program)

It works on both the C-64 and the CommanderX16.   It cleanly deals with I/O errors and you can abort it by pressing RUN/STOP.    Fantastic!!

image.png.fb4b32ad9418f13735412b8d0d4ab45d.png

Prog8 source code:

%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')
            repeat 2 {
                void c64.CHRIN()     ; skip 2 bytes
            }
            status = c64.READST()

            c64.STOP()
            if_nz
                break
        }

io_error:
        status = c64.READST()
        c64.CLOSE(1)
        c64.CLRCHN()        ; restore default i/o devices

        if status and status != 64 {            ; 64=end of file
            txt.print("\ni/o error, status: ")
            txt.print_ub(status)
            txt.chrout('\n')
        }
    }
}
Edited by desertfish
forgot the source code
Link to comment
Share on other sites

  • 0

Think of a stack -- Last In, First Out.  CLRCHN is the opposite of CHKIN/CHKOUT; CLOSE is the opposite of OPEN.  Therefore, we should use this sequence:

  1. OPEN
  2. CHKIN
  3.   ...
  4. CLRCHN (cancels CHKIN)
  5. CLOSE (cancels OPEN)

CLRCHN means: clear (cancel) the current input channel selection, and clear the current output channel selection.

  • Thanks 1
Link to comment
Share on other sites

  • 0

Hi,

@desertfish I think your program is fine.

CLRCHN resets all I/O channels to their default state. It's not connected to a specific file or channel. I don't think the stack analogy is relevant.

According to the C64 PRG, CLALL closes all files and invokes CLRCHN. You may look how the Kernal does this.

CLALL is at $d8a0. This is what it happens:

  • First it sets memory address $0289 to 0. This is done by the instructions at $d8a0 and $d8a2. $0289 might be an open file counter or a current file index on X16, but I'm not sure.
  • The next instruction is $d8a5, and that is the start of CLRCHN

Consequently, the Kernal seems to run CLRCHN after closing the files.

To simplify clean up after an I/O operation, use CLALL instead of CLOSE+CLRCHN.

Link to comment
Share on other sites

  • 0

NO!  DEFINITELY NOT!

$0289 holds the count of open files.
CLALL doesn't close files; it "drops" them -- it makes the computer forget that files still are open!  DOSes aren't told to close their files!

When CLRCHN resets the computer's I/O selections, it tells peripheral devices to reset their channel connections to the computer.  DOSes like to do that before they're told to close a file.

Link to comment
Share on other sites

  • 0

Hmm. As I said earlier in this thread, the Kernal I/O documentation is quite hard to fully grasp, and sometimes the information in, for instance, C64 PRG might even be misleading.

If you search for examples on the web, CLOSE is normally called before CLRCHN. In fact, I haven't seen those functions called in reverse order.

It's true that CLALL seems to only reset the open file counter, and thereafter run CLRCHN. CLOSE seems to send actual commands over the serial bus. Maybe it's safer to call CLOSE instead of CLALL, even though you could never guess from reading the C64 PRG.

All books I've seen only have partial code examples, never complete code showing how to safely read a file from disk, for instance. It would be nice to understand in more depth how the Kernal handles serial bus communication. If you have the time, the Kernal code is available out there 🙂

Link to comment
Share on other sites

  • 0

I found a book online that describes the use of CLALL, CLOSE, and CLRCHN in more depth: "Machine language routines for the Commodore 64 and 128" by Todd D. Heimarck and Patrick Parrish, 1987.

On page 63 they talk about CLALL.

As you may see by disassembling the Kernal in the X16 monitor, CLALL only resets the open file counter to 0 and then falls through to CLRCHN. The authors confirm that the routine, despite its name, doesn't close any files, and that the routine is not safe to use when working with files over the serial bus.

If you disassemble the CLOSE routine, you can see that it sends commands over the serial bus to notify the other device. To understand what those commands are, you need to dig deeper into the functioning of the C64 serial bus, which I haven’t done.

https://archive.org/details/Compute_s_Machine_Language_Routines_for_the_Commodore_64_and_128/page/n69/mode/2up

On page 172, there is a code example on how to close a file.

That routine calls CLOSE first and then CLRCHN.

https://archive.org/details/Compute_s_Machine_Language_Routines_for_the_Commodore_64_and_128/page/n177/mode/2up

To wrap it up:

  • Don't use CLALL to close files. Use CLOSE.
  • It’s normal to call CLOSE first and CLRCHN second.

It would be interesting to test how a device reacts to CLOSE if CLRCHN was called first, especially when writing to a file. My limited understanding of CLRCHN is that is sends UNTALK/UNLISTEN to all devices on the bus, and that could affect how a device handles a subsequent CLOSE.

I might do a small test and see if you get file corruption or other problems, or if it works fine either way.

Have a nice weekend.

Link to comment
Share on other sites

  • 0

A misunderstanding by some people doesn't make it "normal" or proper.

Here's what the BASIC interpreter does:

10 OPEN 1, 8, 2, "0:FILENAME,S,R"
20 INPUT#1, Y$
30 CLOSE 1
  1. calls SETLFS and SETNAM
  2. calls OPEN
  3. calls CHKIN
  4. calls CHRIN
  5. repeats step 4 until a carriage-return is seen
  6. calls CLRCHN
  7. calls CLOSE

BASIC doesn't call CLOSE before it calls CLRCHN!

Edited by Greg King
Link to comment
Share on other sites

  • 0

I made a small test program that writes to file. One file was closed with CLOSE+CLRCHN and the other with CLRCHN+CLOSE.

Both alternatives seem to work without issues.

It might just be a matter of taste in what order you call the functions. To me it's more logical to call CLRCHN last, as it's not paired to one CHKIN or CHKOUT invocation - it resets all I/O channels. To clarify what I mean: say that you at the same time open one file for reading and another file for writing to copy the content of the first file to the second file. It's more logical to first close those files before you invoke CLRCHN.

There's no reason to say that Heimarck and Parrish misunderstood the Kernal. Reading their book, it's obvious that they knew the Kernal in great detail.

Link to comment
Share on other sites

  • 0

To me, it's more logical to tell a drive, "stop talking/listenning about the files" before it's told, "close the files".  (You wouldn't tell someone to put a book back on a shelf before that one stopped reading it.)

A better two-file example is this:
A program reads two files together.  It interleaves data from those files.  It reads some data from the first file.  Then, it reads some data from the second file.  Then, it reads some more data from the first file.  Then, it reads some more data from the second file.  Then, it reads some more data from the first file ... and, so on.  The program must use CHKIN calls to switch between the two files.  The files are on separate devices.

If the program tells the first drive to talk about its file, then tells the second drive to talk about its file, then both drives will talk at the same time!  The computer will hear garbage.  Each drive must be told to stop talking, so that the other one can have its turn to talk without interference.  The program really must pair a CLRCHN with each CHKIN.

OPEN and CLOSE are a pair.  The two CHK... functions and CLRCHN actually are a pair!  I recommend that people get into the habit of thinking about them that way.  Then, you won't have trouble when you start to write programs that handle several files together.

Link to comment
Share on other sites

  • 0

Hmm, now that I think more about your example, I see that it, also, cannot work if the files are on separate devices, and the program wants to change the data before writing it.  If the program told the first drive to talk, and told the second drive to listen, then the first one would send its data directly to the second one.  The program wouldn't get a chance to alter the data.

Another hmm.  I guess that people who are used to PCs will be confused by this discussion.  Well, ...
a Commodore 8-bit system is a network.  The computer, the disk drives, and the printers are independent microcomputer nodes on that network.  The computer is the boss.  But, drives can be told to talk directly to each other.  Drives can talk directly to printers.  They don't need to send their data through the computer.  They usually do go through the computer, but they are able to bypass it.

And, if file I/O switching isn't managed properly, then bypasses or collisions could happen by accident.

Link to comment
Share on other sites

  • 0

@Greg KingOh man, the flashback

Quote

In later versions of Fast Hack'em, disk copying can be performed without the computer if two Commodore 1541 disk drives are available. The software is loaded with a Commodore 64, the two drive option is be selected which transfers software to the drives' controller memory, and the serial cable can be disconnected from the computer. Any number of copies can be performed as long as neither drive is powered down.

I used that exact feature.  Could play games on the C64 while the two drives were copying a stack of floppies. It was amazing

Link to comment
Share on other sites

  • 0
On 10/7/2020 at 7:52 PM, Greg King said:

CLRCHN means: clear (cancel) the current input channel selection, and clear the current output channel selection.

One should use this function with care, only when finishing all I/O operations.
Imagine when you read simultaneously from 2 files or devices, calling this function for one device would also close the channel of the other!

This is the description of CLRCHN:

  Description: This routine is called to clear all open channels and re-
store the I/O channels to their original default values. It is usually
called after opening other I/O channels (like a tape or disk drive) and
using them for input/output operations. The default input device is 0
(keyboard). The default output device is 3 (the Commodore 64 screen).
  If one of the channels to be closed is to the serial port, an UNTALK
signal is sent first to clear the input channel or an UNLISTEN is sent to
clear the output channel. By not calling this routine (and leaving lis-
tener(s) active on the serial bus) several devices can receive the same
data from the Commodore 64 at the same time. One way to take advantage
of this would be to command the printer to TALK and the disk to LISTEN.
This would allow direct printing of a disk file.
  This routine is automatically called when the KERNAL CLALL routine is
executed.

 

Link to comment
Share on other sites

  • 0

That description of clrchn shows why you should call the function, not why you shouldn't call it.  Those actions are stunts!  Normal programs don't want to do a circus act.  They want to have a single connection between the computer and a single file -- at a time.

On 2/9/2021 at 3:51 PM, svenvandevelde said:

Imagine when you read simultaneously from 2 files or devices.

You can't do that.  It's like having two people talking to you simultaneously.  You can't understand them.  You must tell one of them, "be quiet.  Let the other one speak.  Take turns."

Taking turns (reading concurrently) -- first one, then the other, then the one again, then the other again -- is the purpose of chkin/chkout and clrchn.  The first file/device must be told to stop talking, so that the second file/device can start talking -- without interference.

Edited by Greg King
Fixed an adverb.
Link to comment
Share on other sites

  • 0
4 hours ago, Greg King said:

That description of clrchn shows why you should call the function, not why you shouldn't call it.  Those actions are stunts!  Normal programs don't want to do a circus act.  They want to have a single connection between the computer and a single file -- at a time.

You can't do that.  It's like having two people talking to you simultaneously.  You can't understand them.  You must tell one of them, "be quiet.  Let the other one speak.  Take turns."

Taking turns (reading consecutively) -- first one, then the other, then the one again, then the other again -- is the purpose of chkin/chkout and clrchn.  The first file/device must be told to stop talking, so that the second file/device can start talking -- without interference.

i meant this:

10 open5,8,5,"file1.txt,s,r"
20 open6,8,6,"file2.txt,s,r"
30 for i=1 to 15
40 get#5,a$:print a$;
50 get#6,b$:print b$;
60 next i
70 close5:close6
Link to comment
Share on other sites

  • 0

I've been reading more about CLOSE and CLRCHN in the Kernal sources and in @Michael Steil 's article published here: https://www.pagetable.com/?p=1031

CLOSE sends the following commands on the serial bus:

  • LISTEN <device number>
  • CLOSE <file number>
  • UNLISTEN

CLRCHN does the following:

  • Sends UNTALK <input device>
  • Sends UNLISTEN <output device>
  • Sets defltn (address $028A) and deflto (address $028B) to their default values, i.e. output=3 (screen), and input=0 (keyboard)

I would think that after calling CLOSE it will not matter to the device on the serial bus if also you call CLRCHN. As CLOSE has already sent LISTEN-CLOSE-UNLISTEN, the device will neither talk nor listen whether or not you call CLRCHN. So if you have called CLOSE on all files, calling CLRCHN is only meaningful in order to reset the input and output device numbers used by the Kernal (defltn and deflto).

As @svenvandevelde was writing above, it makes perfect sense to call CLRCHN after you are completely done with the I/O operation, as you could be dealing with more than one file at the same time.

But the above also, I would say, show that it doesn't really matter in what order you are calling CLOSE and CLRCHN.

Edited by Stefan
  • Like 1
Link to comment
Share on other sites

  • 0

Can somebody help me? Please install kickc and complile the attached program load.c. I've also attached a TEXT file.

TEXT

load.c

This C routine opens the TEXT file and reads it. But for some reason AFTER this process has been successfully executed, it seems that the character buffer ALSO contains the characters that were read from the file!

And before I forget to mention, I read this from an sdcard file; I've also attached my X16.VHD image that you can load using -sdcard X16.vhd option of x16emu.

//#pragma link("load.ld")
 
#include <printf.h>
#include <mos6522.h>
 
// Address to load to banked memory.
dword const loadtext = 0x00000000;
charconst text = 0xA000;
 
const word CBM_OPEN   = 0xFFC0;
const word CBM_CHKIN  = 0xFFC6;
const word CBM_READST = 0xFFB7;
const word CBM_CHRIN  = 0xFFCF;
const word CBM_CLOSE  = 0xFFC3;
const word CBM_CLRCHN = 0xFFCC;
 
// Kernal SETNAM function
// SETNAM. Set file name parameters.
void cbm_k_setnam(charfilename) {
    char filename_len = (char)strlen(filename);
    asm {
        // Kernal SETNAM function
        // SETNAM. Set file name parameters.
        // Input: A = File name length; X/Y = Pointer to file name.
        lda filename_len
        ldx filename
        ldy filename+1
        jsr $ffbd
    }
}
 
// SETLFS. Set file parameters.
void cbm_k_setlfs(char channelchar devicechar secondary) {
    asm {
        // SETLFS. Set file parameters.
        // Input: A = Logical number; X = Device number; Y = Secondary address.
        ldx device
        lda channel
        ldy secondary
        jsr $ffba
    }
}
 
/*
B-18. Function Name: OPEN
 
  Purpose: Open a logical file
  Call address: $FFC0 (hex) 65472 (decimal)
  Communication registers: None
  Preparatory routines: SETLFS, SETNAM
  Error returns: 1,2,4,5,6,240, READST
  Stack requirements: None
  Registers affected: A, X, Y
 
  Description: This routine is used to OPEN a logical file. Once the
logical file is set up, it can be used for input/output operations. Most
of the I/O KERNAL routines call on this routine to create the logical
files to operate on. No arguments need to be set up to use this routine,
but both the SETLFS and SETNAM KERNAL routines must be called before
using this routine.
 
How to Use:
 
  0) Use the SETLFS routine.
  1) Use the SETNAM routine.
  2) Call this routine.
*/
char cbm_k_open() {
    char status;
    asm {
        //.byte $db
        jsr CBM_OPEN
        bcs error
        lda #$ff
        error:
        sta status
    }
    return status;
}
 
/*
B-9. Function Name: CLOSE
 
  Purpose: Close a logical file
  Call address: $FFC3 (hex) 65475 (decimal)
  Communication registers: A
  Preparatory routines: None
  Error returns: 0,240 (See READST)
  Stack requirements: 2+
  Registers affected: A, X, Y
 
  Description: This routine is used to close a logical file after all I/O
operations have been completed on that file. This routine is called after
the accumulator is loaded with the logical file number to be closed (the
same number used when the file was opened using the OPEN routine).
 
How to Use:
 
  1) Load the accumulator with the number of the logical file to be
     closed.
  2) Call this routine.
*/
char cbm_k_close(char channel) {
    char status;
    asm {
        //.byte $db
        lda channel
        jsr CBM_CLOSE
        bcs error
        lda #$ff
        error:
        sta status
    }
    return status;
}
 
/*
B-2. Function Name: CHKIN
 
  Purpose: Open a channel for input
  Call address: $FFC6 (hex) 65478 (decimal)
  Communication registers: X
  Preparatory routines: (OPEN)
  Error returns:
  Stack requirements: None
  Registers affected: A, X
 
  Description: Any logical file that has already been opened by the
KERNAL OPEN routine can be defined as an input channel by this routine.
Naturally, the device on the channel must be an input device. Otherwise
an error will occur, and the routine will abort.
  If you are getting data from anywhere other than the keyboard, this
routine must be called before using either the CHRIN or the GETIN KERNAL
routines for data input. If you want to use the input from the keyboard,
and no other input channels are opened, then the calls to this routine,
and to the OPEN routine are not needed.
  When this routine is used with a device on the serial bus, it auto-
matically sends the talk address (and the secondary address if one was
specified by the OPEN routine) over the bus.
 
How to Use:
 
  0) OPEN the logical file (if necessary; see description above).
  1) Load the X register with number of the logical file to be used.
  2) Call this routine (using a JSR command).
 
Possible errors are:
 
  #3: File not open
  #5: Device not present
  #6: File not an input file
*/
char cbm_k_chkin(char channel) {
    char status;
    asm {
        ldx channel
        jsr CBM_CHKIN
        bcs error
        lda #$ff
        error:
        sta status
    }
    return status;
}
 
/*
B-10. Function Name: CLRCHN
 
  Purpose: Clear I/O channels
  Call address: $FFCC (hex) 65484 (decimal)
  Communication registers: None
  Preparatory routines: None
  Error returns:
  Stack requirements: 9
  Registers affected: A, X
 
  Description: This routine is called to clear all open channels and re-
store the I/O channels to their original default values. It is usually
called after opening other I/O channels (like a tape or disk drive) and
using them for input/output operations. The default input device is 0
(keyboard). The default output device is 3 (the Commodore 64 screen).
  If one of the channels to be closed is to the serial port, an UNTALK
signal is sent first to clear the input channel or an UNLISTEN is sent to
clear the output channel. By not calling this routine (and leaving lis-
tener(s) active on the serial bus) several devices can receive the same
data from the Commodore 64 at the same time. One way to take advantage
of this would be to command the printer to TALK and the disk to LISTEN.
This would allow direct printing of a disk file.
  This routine is automatically called when the KERNAL CLALL routine is
executed.
 
How to Use:
  1) Call this routine using the JSR instruction.
*/
void cbm_k_clrchn() {
    asm {
        jsr CBM_CLRCHN
    }
}
 
/*
B-4. Function Name: CHRIN
 
  Purpose: Get a character from the input channel
  Call address: $FFCF (hex) 65487 (decimal)
  Communication registers: A
  Preparatory routines: (OPEN, CHKIN)
  Error returns: 0 (See READST)
  Stack requirements: 7+
  Registers affected: A, X
 
  Description: This routine gets a byte of data from a channel already
set up as the input channel by the KERNAL routine CHKIN. If the CHKIN has
NOT been used to define another input channel, then all your data is
expected from the keyboard. The data byte is returned in the accumulator.
The channel remains open after the call.
  Input from the keyboard is handled in a special way. First, the cursor
is turned on, and blinks until a carriage return is typed on the
keyboard. All characters on the line (up to 88 characters) are stored in
the BASIC input buffer. These characters can be retrieved one at a time
by calling this routine once for each character. When the carriage return
is retrieved, the entire line has been processed. The next time this
routine is called, the whole process begins again, i.e., by flashing the
cursor.
 
How to Use:
 
FROM THE KEYBOARD
 
  1) Retrieve a byte of data by calling this routine.
  2) Store the data byte.
  3) Check if it is the last data byte (is it a CR?)
  4) If not, go to step 1.
 
FROM OTHER DEVICES
 
  0) Use the KERNAL OPEN and CHKIN routines.
  1) Call this routine (using a JSR instruction).
  2) Store the data.
*/
char cbm_k_chrin() {
    char value;
    asm {
        //.byte $db
        jsr CBM_CHRIN
        sta value
    }
    return value;
}
 
/*
B-22. Function Name: READST
 
  Purpose: Read status word
  Call address: $FFB7 (hex) 65463 (decimal)
  Communication registers: A
  Preparatory routines: None
  Error returns: None
  Stack requirements: 2
  Registers affected: A
 
  Description: This routine returns the current status of the I/O devices
in the accumulator. The routine is usually called after new communication
to an I/O device. The routine gives you information about device status,
or errors that have occurred during the I/O operation.
  The bits returned in the accumulator contain the following information:
(see table below)
 
+---------+------------+---------------+------------+-------------------+
|  ST Bit | ST Numeric |    Cassette   |   Serial   |    Tape Verify    |
| Position|    Value   |      Read     |  Bus R/W   |      + Load       |
+---------+------------+---------------+------------+-------------------+
|    0    |      1     |               |  time out  |                   |
|         |            |               |  write     |                   |
+---------+------------+---------------+------------+-------------------+
|    1    |      2     |               |  time out  |                   |
|         |            |               |    read    |                   |
+---------+------------+---------------+------------+-------------------+
|    2    |      4     |  short block  |            |    short block    |
+---------+------------+---------------+------------+-------------------+
|    3    |      8     |   long block  |            |    long block     |
+---------+------------+---------------+------------+-------------------+
|    4    |     16     | unrecoverable |            |   any mismatch    |
|         |            |   read error  |            |                   |
+---------+------------+---------------+------------+-------------------+
|    5    |     32     |    checksum   |            |     checksum      |
|         |            |     error     |            |       error       |
+---------+------------+---------------+------------+-------------------+
|    6    |     64     |  end of file  |  EOI line  |                   |
+---------+------------+---------------+------------+-------------------+
|    7    |   -128     |  end of tape  | device not |    end of tape    |
|         |            |               |   present  |                   |
+---------+------------+---------------+------------+-------------------+
 
How to Use:
 
  1) Call this routine.
  2) Decode the information in the A register as it refers to your pro-
     gram.
*/
char cbm_k_readst() {
    char status;
    asm {
        //.byte $db
        jsr CBM_READST
        sta status
    }
    return status;
}
 
// LOAD. Load or verify file. (Must call SETLFS and SETNAM beforehands.)
// - verify: 0 = Load, 1-255 = Verify
//
// Returns a status, 0xff: Success other: Kernal Error Code
char cbm_k_load(charaddresschar verify) {
    char status;
    asm {
        //LOAD. Load or verify file. (Must call SETLFS and SETNAM beforehands.)
        // Input: A: 0 = Load, 1-255 = Verify; X/Y = Load address (if secondary address = 0).
        // Output: Carry: 0 = No errors, 1 = Error; A = KERNAL error code (if Carry = 1); X/Y = Address of last byte loaded/verified (if Carry = 0).
        .byte $db
        ldx address
        ldy address+1
        lda verify
        jsr $ffd5
        bcs error
        lda #$ff
        error:
        sta status
    }
    return status;
}
 
// Configure the bank of the CX16 banked ram between A000 and BFFF.
void cx16_Bank(char bank) {
    VIA1->PORT_A = bank;
}
 
// Load a file to memory
// Returns a status:
// - 0xff: Success
// - other: Kernal Error Code (https://commodore.ca/manuals/pdfs/commodore_error_messages.pdf)
char cx16_LoadFileBanked(char channelchar devicechar secondarycharfilenamedword address) {
    cbm_k_setnam(filename);
    cbm_k_setlfs(channeldevicesecondary);
    byte status = 0;
    char ch = 0;
    byte bank = (byte)(((((word)<(>address)<<8)|>(<address))>>5)+((word)<(>address)<<3));
    byteaddr = ((<address)&0x1FFF); // stip off the top 3 bits, which are representing the bank of the word!
    addr += 0xA000;
    printf("%s/%lx/%hx/%x\n",filename,addressbankaddr);
    cx16_Bank(bank);
    status = cbm_k_open();
    if(status!=$FF) return status;
    status = cbm_k_chkin(channel);
    if(status!=$FF) return status;
    ch = cbm_k_chrin();
    status = cbm_k_readst();
    while (!status) {
        //printf("%c",ch);
        *addr = ch;
        addr++;
        ch = cbm_k_chrin();
        status = cbm_k_readst();
    }
    status = cbm_k_close(channel);
    cbm_k_clrchn();
 
    cx16_Bank(bank);
    return status;
}
 
void main() {
    // Load sprite file into memory
    char status = cx16_LoadFileBanked(180"TEXT"loadtext);
    if(status!=0xffprintf("status = %x\n",status);
    printf("text = %s\n"text);
}

And those characters are then displayed. The X16 terminal locks up completely after that!

It looks like the file buffer is filled when calling the above procedures!

image.png.fddb0ba1e6d1b7bfd2da9ff1e3d79df1.png

Does anybody have an idea why this happens?

X16.vhd

Edited by svenvandevelde
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...

Important Information

Please review our Terms of Use