Re: M-R bug crossing page boundary on 154x, 157x and 1581

From: Spiro Trikaliotis (ml-cbmhackers_at_trikaliotis.net)
Date: 2009-01-25 14:36:30

Hello,

* On Sat, Jan 24, 2009 at 07:22:23PM +0100 I wrote:
 
> I just found out something that was not known to me beforehand. Is this
> a known bug?
> 
> If issuing a M-R command on the floppy (1540, 1541, 1570, 1571, 1581),
> if the M-R crosses a page boundary, the M-R is stopped early.
> 
> That is: If I read from $0500, I can read $100 byte:
> 
>   "M-R"CHR$(0)CHR$(5)CHR$(0)
> 
> However, if I read from $0510, I can read only $F0 byte. If I specify
> more bytes, the floppy aborts the read, after exactly $F0 byte.
> 
> Is this known and documented somewhere?

I started some investigation on this. Here is what I found out (for the
154x/157x):

M-R is implemented starting at $CB20. Essentially, it writes the start
address of the bytes to be read into $A5/$A6 - this is the pointer to
the error buffer, which normally points to $02D5. (To be more precise:
The first byte is stored in $85 and later (at $D449) in $0243, and the
pointer at $A5/$A6 points to the next byte, but that's just a minor
detail.)

The end address of where to read is stored at $0249. Note that only the
low byte can be stored, there is no memory available for the high byte!

Later, when sending the data bytes to the computer, the routine at $E909
is used. The pointer is advanced to the next byte at $E992, which
executes a JSR $D3AA.

Here, we have:

.8:d3aa   A6 82      LDX $82
.8:d3ac   20 25 D1   JSR $D125
.8:d3af   D0 03      BNE $D3B4          Taken for the command channel

.8:d3b4   A5 83      LDA $83            Secondary address (here: $0F)
.8:d3b6   C9 0F      CMP #$0F
.8:d3b8   F0 5A      BEQ $D414          Yes, it is the command channel, branch taken

.8:d414   20 E8 D4   JSR $D4E8          this routines gets the buffer
                                        pointr of the current character;
                                        low into $94, high into $95. It
                                        returns the low byte in A, too.
                                        For the error channel, the
                                        pointer at $99+2*6/$9A+2*6 is
                                        taken, that is $A5/$A6 (see
                                        above)
.8:d417   C9 D4      CMP #$D4           Compare the low byte with $D4
                                        (the error channel starts at
                                        $02D5; thus, if the pointer is
                                        at $02D4, then a new error
                                        message has to be generated)
.8:d419   D0 18      BNE $D433          No new error message, skip
                                        (Afterwards, the high byte would
                                        have been compared to $02. If it
                                        is different, then the BNE would
                                        be taken to $D433; otherwise, a
                                        CR would be generated.)


.8:d433   20 37 D1   JSR $D137          get the next character from the buffer
.8:d436   85 85      STA $85            remember for output
.8:d438   D0 09      BNE $D443          z=1 (from JSR above) means: End of buffer!


                                        If we reach here, the buffer endedd
.8:d43a   A9 D4      LDA #$D4           store $02D4 as new pointer for error channel
.8:d43c   20 C8 D4   JSR $D4C8          first command: STA $99,X. The rest does not interest us here.
.8:d43f   A9 02      LDA #$02
.8:d441   95 9A      STA $9A,X
.8:d443   A9 88      LDA #$88   
.8:d445   85 F7      STA $F7
.8:d447   A5 85      LDA $85            store remembered output byte
.8:d449   8D 43 02   STA $0243          as new byte to send over the IEC bus
.8:d44c   60         RTS


So, the "magic" of finding the EOI condition is in $D137:

.8:d137   20 2F D1   JSR $D12F         
.8:d13a   B9 44 02   LDA $0244,Y        for error channel: LDA $0249 -
                                        that's the calculated end of
                                        transmission, as remarked above
                                        in the description of the M-R
                                        command
.8:d13d   F0 12      BEQ $D151          if the transfer ends is at $xx00, branch

.8:d13f   A1 99      LDA ($99,X)        X is $0C here for the command
                                        channel: Thus, LDA ($A5), gets
                                        the next byte
.8:d141   48         PHA

.8:d142   B5 99      LDA $99,X          check if we reached the end of the block
.8:d144   D9 44 02   CMP $0244,Y
.8:d147   D0 04      BNE $D14D          no --> then branch

.8:d149   A9 FF      LDA #$FF           "delete" the low byte
.8:d14b   95 99      STA $99,X
.8:d14d   68         PLA

.8:d14e   F6 99      INC $99,X         advance to the next character
.8:d150   60         RTS

.8:d151   A1 99      LDA ($99,X)       Get the next character
.8:d153   F6 99      INC $99,X         advance to the next character
.8:d155   60         RTS

You see something? Yes, the complete routine does not care at all about
the high byte! This makes sense for the error channel and the other
buffers, as the buffers always start at a page boundary. For M-R,
however, this is a restriction. But it gets obvious: M-R cannot cross a
page boundary!

Now, one question remains: Where does the CR at the end of reading the
command channel (error channel) come from? That's why $02D4 is stored
into $A5/$A6 at $D43A-$D441. As already stated, the error buffer starts
at $02D5, one byte later.

At $D417-$D41F, it is tested if the pointer points to $02D4. If this is
the case, $D421-$D423 stores a CR as the output character:

.8:d421   A9 0D      LDA #$0D
.8:d423   85 85      STA $85

Thus, in some sense, the CR at the end is not the last read character,
but more the first read character of the next output! Interesting, isn't
it?


There is another interesting fact here: If doing a M-R in the $0200
page, the you cannot read beyond $02D4! That's because of the special
handling of the $02D4 address. Thus, if you want to read the complete
$0200 page, you have to read first $0200-$02D3, then $02D4-$02FF.

For the 1581, the special address is $02CF; thus, here, you have to read
$0200-$02CE, then $02CF-$02FF.

Regards,
Spiro.

-- 
Spiro R. Trikaliotis                              http://opencbm.sf.net/
http://www.trikaliotis.net/                     http://www.viceteam.org/


       Message was sent through the cbm-hackers mailing list

Archive generated by hypermail pre-2.1.8.