6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Jun 23, 2024 7:54 pm

All times are UTC




Post new topic Reply to topic  [ 24 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: IDE Data format puzzle
PostPosted: Tue Jan 19, 2021 3:43 am 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1378
I'm finding an odd issue with accessing IDE drives, or more correctly here, a Compact Flash Card in True IDE mode. Note that data access is in 16-bit mode, so the hardware has an upper 8-bit latch that handles data lines D8 - D15. Here's the puzzling part:

1- Using the Identify Drive command. Note that while this does not actually read a block of data from the media itself, it does use the same 16-bit transfer as reading a block of data and the hardware operates the same, i.e., the command sequence and handling of data from the IDE device is identical to reading a block. As the data coming in is 16-bit, the data is swapped between upper and lower bytes when storing into a buffer in memory. Examining the data, everything looks okay and matches what is shown in the technical manual. The data fields and text fields that describe the drive characteristics are properly shown by viewing memory.

2- Reading a block of data from the device is odd, as looking at a DOS formatted CF Card, the data bytes in each block are swapped, so it's reverse of how the drive identity data is. The tell tale sign is the partition block (1st on the device) has the upper and lower bytes swapped, hence the signature is "AA55" instead of "55AA". The same is found for any data, ASCII text has characters swapped. So it would appear that either the drive identity information is swapped (high byte low byte) or the data when formatted under DOS, WIndows, etc., is accessing the block opposite.

3- A fly in the ointment. I have one of Bill's (Plasmo here) Tiny68K system, which is running CP/M-68K from a CF Card. Examining data from that drive on my CF Card interface and BIOS shows everything in the correct order, so the drive identity data and the actual data from the media are in the same high byte / low byte arrangement.

So, can someone help clarify what order the bytes "should" be in? It seems odd that the identity data appears in one format and actual media data appears swapped for the filesystem access, or at least from the filesystem being DOS FAT formatted from an Intel machine.

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 19, 2021 12:32 pm 
Offline
User avatar

Joined: Sun Nov 27, 2011 12:03 pm
Posts: 229
Location: Amsterdam, Netherlands
A confusing factor is probably that text within the Identify Drive result is stored in an odd way, so it looks 'garbled' in a memory dump.

The 'working draft' ATAPI-6 document says (between double quotes) :

"
Some parameters are defined as a string of ASCII characters. ASCII data fields shall contain only code values
20h through 7Eh. For the string “Copyright”, the character “C” is the first byte, the character “o” is the second
byte, etc. When such fields are transferred, the order of transmission is:
the 1st character (“C”) is on DD(15:8) of the first word,
the 2nd character (“o”) is on DD(7:0) of the first word,
the 3rd character (“p”) is on DD(15:8) of the second word,
the 4th character (“y”) is on DD(7:0) of the second word,
the 5th character (“r”) is on DD(15:8) of the third word,
the 6th character (“i”) is on DD(7:0) of the third word,
the 7th character (“g”) is on DD(15:8) of the fourth word,
the 8th character (“h”) is on DD(7:0) of the fourth word,
the 9th character (“t”) is on DD(15:8) of the fifth word,
the 10th character (“space”) is on DD(7:0) of the fifth word,
etc.
"

Why this weirdness crept into the standard is anyone's guess.


Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 19, 2021 1:27 pm 
Offline

Joined: Fri Dec 21, 2018 1:05 am
Posts: 1084
Location: Albuquerque NM USA
floobydust wrote:
3- A fly in the ointment. I have one of Bill's (Plasmo here) Tiny68K system, which is running CP/M-68K from a CF Card. Examining data from that drive on my CF Card interface and BIOS shows everything in the correct order, so the drive identity data and the actual data from the media are in the same high byte / low byte arrangement.

The short answer is Tiny68K CF disk format is the wrong byte order.

I can give you my history regarding byte ordering of CF data bus. I designed three 16-bit SBC computers in rapid succession several years ago. Part of the design motivation was to interface to CF disk which was new experience for me. A superficial read of the ATA documentation and look at the data bus names, it seems reasonable to put most significant byte in D15-D8 and least significant byte in D7-D0 of CF data bus, and that's what I did. It was in fact the wrong order, but because CP/M file system is more or less self contained, not well supported in desktop applications (not strictly true as I've found out later), the byte order error was not discovered until much later. By then there were so many units in the field, it was too late to change horse(s) in mid stream. To this date, MPU302, Tiny68K (and its derivatives), and Z280RC still all have the original (wrong) byte ordering. This is a problem only when you want to read/edit the CF disk on workstation using "dd" or "Windisk imager" in conjunction with CPMtools software application. In "dd" there is a switch to change byte order, so the wrong byte order is not even an issue in Linux machine.

Bill


Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 19, 2021 7:30 pm 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1378
First, thanks for the quick replies to both... and to Bill for clarifying your findings when you built your systems up. It sorta makes more sense... but the identity data format is backwards by comparison. I'm going to make the change in my current BIOS and then update the loadable utility program I've been working on.

I am wondering how the data is transferred when you put the CF Card into 8-bit mode... just a thought :shock:

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
PostPosted: Tue Jan 19, 2021 8:18 pm 
Offline

Joined: Fri Dec 21, 2018 1:05 am
Posts: 1084
Location: Albuquerque NM USA
When CF is set to 8-bit wide data bus (with the Set Feature command), the byte order of data is correct; no byte swapping is required whether the CF is read/write as 8-bit wide port by 6502 or the same CF is read/write as 16-bit wide port by Windows PC. However, the CF ID data still need to be swapped in 8-bit wide port!
Bill


Top
 Profile  
Reply with quote  
PostPosted: Wed Jan 20, 2021 3:18 am 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1378
Thanks Bill,

I was thinking that would probably be the case. I made changes the BIOS for the CF Card and have it working in a normal byte mode now. Reading in blocks from a CF Card (FAT Format) now looks fine. I've made a few other minor tweaks on the BIOS as well, so managed to get the performance up a bit too.

I'm finding the older SanDisk 128MB CF Cards to be the better performers... at 321KB/Second for a contiguous 16MB read access in LBA mode. Doing the same 16MB write, with issuing a Verify after each block write still runs at bit over 274KB/Second. This is with a 8MHz W65C02S based system.

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
PostPosted: Wed Jan 20, 2021 1:38 pm 
Offline

Joined: Fri Dec 21, 2018 1:05 am
Posts: 1084
Location: Albuquerque NM USA
floobydust wrote:
I'm finding the older SanDisk 128MB CF Cards to be the better performers... at 321KB/Second for a contiguous 16MB read access in LBA mode. Doing the same 16MB write, with issuing a Verify after each block write still runs at bit over 274KB/Second. This is with a 8MHz W65C02S based system.

You are running the 16MB read with the 100Hz interrupt running, right? Have you measure how much time the interrupt processing uses? I also have a clock interrupt, but it is only 1Hz. The transfer rate does varies quite a bit by CF brands; some take almost 30 seconds, most are about 25 seconds, the fastest one is 22 seconds. This is with 14.7MHz clock.
Bill


Top
 Profile  
Reply with quote  
PostPosted: Wed Jan 20, 2021 2:14 pm 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1378
plasmo wrote:
floobydust wrote:
I'm finding the older SanDisk 128MB CF Cards to be the better performers... at 321KB/Second for a contiguous 16MB read access in LBA mode. Doing the same 16MB write, with issuing a Verify after each block write still runs at bit over 274KB/Second. This is with a 8MHz W65C02S based system.

You are running the 16MB read with the 100Hz interrupt running, right? Have you measure how much time the interrupt processing uses? I also have a clock interrupt, but it is only 1Hz. The transfer rate does varies quite a bit by CF brands; some take almost 30 seconds, most are about 25 seconds, the fastest one is 22 seconds. This is with 14.7MHz clock.
Bill


Yes, I have two interrupt service routines. These are soft vectored via page $03 ($0300 range). One routine handles the SCC2691 UART, which has services for transmit, receive, received break and the 16-bit timer. The other routine handles the Compact Flash IDE controller for reading a block in LBA mode. The BIOS is setup to run through the CF Card ISR first, then to the UART ISR.

The ISR for the UART timer does several things. First, there is a Page Zero variable for timer ticks. This is set to 100 decimal and gets decremented via each ISR, hence the 10ms Jiffy Clock. Beyond this, there are additional flag bits used to specify software delays, the benchmark timer itself and a software RTC function. I haven't tallied up the latest BIOS release for the interrupt service routine that handles the UART, but it will vary depending on what's going on with the RTC variables. The Benchmark timer itself uses 3- Page Zero locations. Two are for a 16-bit second counter (0-65535) and the third is for a 10ms counter (0.00 - 0.99).

Needless to say, it does take a fair amount of clock cycles to service the timer ISR. I should go through the UART ISR again and count up the clock cycles for an average run through it with the benchmark timer running. I haven't done this since I updated the Software RTC code, which now mimics the DS1511 hardware RTC, as the software RTC gets it's variables loaded at boot time from the DS1511.

You've got a very fast transfer rate... but I'm thinking your ISR is fairly short and the block transfer from the CF Card is not using any interrupt. From what I'm sensing for the Identity data, it appears that CF Cards are only configured for a single block access per command... are you able to request a multiple block transfer with your SBC?

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
PostPosted: Wed Jan 20, 2021 3:30 pm 
Offline

Joined: Fri Dec 21, 2018 1:05 am
Posts: 1084
Location: Albuquerque NM USA
I hooked up a scope and measured the ISR. It is about 80mS. The reason for the long ISR is because it updates the entire I2C display every second to show the clock movement. Calculate a new clock face takes 7mS, bit-banging the I2C bus takes most of the 80mS because I update the entire 128x64 display.

The CF interface is not interrupt driven at all. It is straight polling, one sector at a time. I only move 512 byte data from CF to corresponding 512 byte memory locations. For diagnostic purpose (at the cost of 3 more seconds) I put out a dot for each sector read and a 't' for the beginning of a new track. The display looks right. Attached it the 16meg read test routine.
Bill


Attachments:
testMon.txt [2.64 KiB]
Downloaded 38 times
Top
 Profile  
Reply with quote  
PostPosted: Wed Jan 20, 2021 7:17 pm 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1378
Thanks for the explanation and the code... that's pretty short and kinda hard-coded, so that does give you quite a short loop for reading sectors in. By comparison, my code path is quite a bit more involved, but there's a reason for it... as these are all BIOS routines which are flexible and perform error checking and save the final status in a Page Zero location.

I've used several Page Zero locations to hold parameters for LBA number, Transfer Address and there are two locations used to hold the working address via indirect addressing, i.e., LDA (XFER_ADDR). Here's a few code snippets that are being used for setting up and doing the transfer of data via interrupt:

Code:
IDE_READ_LBA                            ;Read a Block of data from IDE device
;
; This routine requires loading the requested LBA into the appropriate registers and
; issuing the READ command 20h. The LBA count supported for the BIOS are bits 0-23,
; so bits 24-27 are always set to 0. This provides access to IDE devices up to 8GB.
;
; Once the registers/parameters are setup, the Read Block command is issued.
; This results in an interrupt being generated. The ISR handles the transfer of LBA
; data from the CF Card to memory.
;
; The registers used are the same for read/write/verify. These are:
;
;       IDE_COMMAND = function requested (20h = READ LBA command)
;       IDE_DRV_HEAD = (Upper 4 bits) used as:
;               bit 7 = 1 per Sandisk documentation
;               bit 6 = 1 for LBA mode
;               bit 5 = 1 per Sandisk documentation
;               bit 4 = 0 for Drive 0
;       IDE_DRV_HEAD = LBA Address bits 27-24 (lower 4 bits) - not used, always 0000
;       IDE_CYL_HIGH = LBA Address bits 23-16
;       IDE_CYL_LOW = LBA Address bits 15-8
;       IDE_SCT_NUM = LBA Address bits 7-0
;       IDE_SCT_CNT = number of blocks to read (most CF-Cards are limited to 1)
;
                JSR     IDE_SET_PARMS   ;Setup required parameters (6)
;
                SMB3    MATCH           ;Set Read LBA bit (5)
                STZ     IDE_STATUS_RAM  ;Clear RAM Status Register, ISR updates it (3)
                LDA     #$20            ;Get Read LBA command (2)
IDENT_READ      STA     IDE_COMMAND     ;Send command to IDE Controller (4)
LBA_RD_CMD
                LDA     IDE_ALT_STATUS  ;Get IDE Alternate Status register (4)
                BPL     LBA_RD_CMD      ;Loop until IDE controller Busy (2/3)
;
LBA_RD_WAIT
                LDA     IDE_ALT_STATUS  ;Get IDE Alternate Status register (4)
                CMP     #$50            ;Compare for ready (2)
                BEQ     LBA_RD_NORM     ;If normal status, finish up and exit (2/3)
                CMP     #$51            ;Compare for error bit set (2)
                BEQ     LBA_RD_ERR      ;Branch to handle error (2/3)
                BRA     LBA_RD_WAIT     ;Else branch back (waiting on IDE controller) (3)
;
LBA_RD_ERR
                RMB3    MATCH           ;Reset Read LBA bit (no ISR invoked) (5)
                LDA     IDE_ALT_STATUS  ;Get IDE Alternate Status register (4)
                STA     IDE_STATUS_RAM  ;Update RAM Status Register (3)
                RTS                     ;Return to caller (7)
;
LBA_RD_NORM
                BBS2    MATCH,LBA_RD_NORM       ;Wait for Read completed via ISR (5)
                RTS                     ;Return to caller (status in A Reg) (7)
;


Code:
; This routine sets the LBA number used for all transfers.
; - The IDE Controller is checked first to ensure it's ready to receive parameters
; - then the requested LBA (stored in Page Zero variables) are loaded into the
; - IDE Controller registers, followed by the required Mode parameters.
; - Last, the transfer address is setup which points to the location in memory that
; - will be used to transfer Data to or from.
;
IDE_SET_PARMS                           ;Set All parameters for LBA transfers
;
                LDA     IDE_ALT_STATUS  ;Get IDE Alternate Status register (4)
                BMI     IDE_SET_PARMS   ;Loop until it's clear (2/3)
;
                LDA     LBA_EXT_BYTE    ;Set LBA bits 23-16 (3)
                STA     IDE_CYL_HIGH    ;Send to IDE (4)
                LDA     LBA_HIGH_BYTE   ;Set LBA bits 15-8 (3)
                STA     IDE_CYL_LOW     ;Send to IDE (4)
                LDA     LBA_LOW_BYTE    ;Get LBA bits 7-0 (3)
                STA     IDE_SCT_NUM     ;Send to IDE (4)
                LDA     LBA_XFER_CNT    ;Get Block count to read (CF always 1) (3)
                STA     IDE_SCT_CNT     ;Send to IDE (4)
;
IDE_SET_PARMS2                          ;Set partial parameters (non LBA xfer commands)
;
                LDA     #%11100000      ;Set Drive 0, LBA mode, LBA bits 27-24 as 0 (2)
                STA     IDE_DRV_HEAD    ;Send to IDE (4)
;
                LDA     LBA_ADDR_LOW    ;Setup buffer address (3)
                STA     BIOS_XFERL      ;Store low byte (3)
                LDA     LBA_ADDR_HIGH   ;Block Buffer Address (3)
                STA     BIOS_XFERH      ;Store high byte (3)
                RTS                     ;Return to caller (7)


Code:
;
;       NOTE: 25 clock cycles to here if UART ISR is second!
;       NOTE: 40 clock cycles to here if NO UART interrupt occurs!
;
INTERUPT1                               ;Interrupt 1
                LDA     IDE_ALT_STATUS  ;Get Alternate Status Register (4)
                BMI     REGEXT01        ;If Busy bit active, just exit (2/3)
;
; - Check for Data Request (DRQ), as it's the only function that will require servicing
; against the IDE controller.
;
                LDA     IDE_STATUS      ;Get Status (resets IRQ) (4)
                AND     #%00001000      ;Check for DRQ (2)
                BNE     IDE_READ_BLK    ;Branch if active (2/3)
;
; - If no DRQ, possible Write or Verify Block command, check for these next.
;
                BBS2    MATCH,IDE_WRIT_BLK      ;If Bit 2 set, Write operation (5)
                BBS1    MATCH,IDE_VRFY_BLK      ;If Bit 1 set, Verify operation (5)
                BRA     REGEXT01        ;No IDE interrupt, exit the ISR handler (3)
;
IDE_READ_BLK                            ;IDE Read a Block of data
                BBR3    MATCH,REGEXT01  ;If Bit 3 not set, exit ISR (rogue IRQ?) (5)
;
LBA_XFER        LDA     IDE_ALT_STATUS  ;Get Status (4)
                AND     #%00001000      ;Check for DRQ (2)
                BEQ     IDE_RD_DONE     ;If not active, done, exit (2/3)
;
IDE_RD_RBLK     
                LDA     IDE_DATA        ;Read low byte (high byte in latch) (4)
                STA     (BIOS_XFERL)    ;Store low byte (5)
                INC     BIOS_XFERL      ;Increment pointers (5)
                BNE     IDE_RD_BLK1     ; (2/3)
                INC     BIOS_XFERH      ; (5)
IDE_RD_BLK1
                LDA     IDE_16_READ     ;Read high byte from latch (4)
                STA     (BIOS_XFERL)    ;Store high byte (5)
                INC     BIOS_XFERL      ;Increment pointers (5)
                BNE     LBA_XFER        ;Loop back to Xfer, saves 3 clock cycles (2/3)
                INC     BIOS_XFERH      ; (5)
IDE_RD_BLK2
                BRA     LBA_XFER        ;Loop back till no more DRQs (3)
;
IDE_RD_DONE     RMB3    MATCH           ;Clear Read Block flag (5)
;
IDE_ALL_DONE    LDA     IDE_ALT_STATUS  ;Get IDE status (4)
                STA     IDE_STATUS_RAM  ;Save it to RAM location (3)
REGEXT01        JMP     (VECINSRT0)     ;Exit ISR handler (6)
;
IDE_WRIT_BLK                            ;IDE Write a Block of data
                RMB2    MATCH           ;Clear Write Block flag (5)
                BRA     IDE_ALL_DONE    ;Branch and finish ISR (3)
;
IDE_VRFY_BLK                            ;IDE Verify a Block of data
                RMB1    MATCH           ;Clear Verify Block flag (5)
                BRA     IDE_ALL_DONE    ;Branch and finish ISR (3)


Code:
;
IRQ_VECTOR                              ;This is the ROM start for the BRK/IRQ handler
                PHA                     ;Save A Reg (3)
                PHX                     ;Save X Reg (3)
                PHY                     ;Save Y Reg (3)
                TSX                     ;Get Stack pointer (2)
                LDA     $0100+4,X       ;Get Status Register (4)
                AND     #$10            ;Mask for BRK bit set (2)
                BNE     DO_BRK          ;If set, handle BRK (2/3)
                JMP     (IRQVEC0)       ;Jump to Soft vectored IRQ Handler (6)
DO_BRK          JMP     (BRKVEC0)       ;Jump to Soft vectored BRK Handler (6)
NMI_ROM         JMP     (NMIVEC0)       ;Jump to Soft vectored NMI handler (6)
;
;This is the standard return for the IRQ/BRK handler routines
;
IRQ_EXIT0       PLY                     ;Restore Y Reg (4)
                PLX                     ;Restore X Reg (4)
                PLA                     ;Restore A Reg (4)
                RTI                     ;Return from IRQ/BRK routine (6)
;


As you can see, a fair amount of code... but additional interrupt handlers can be added to pre or post mode, and the BRK instruction is also checked for when the IRQ vector is called.... along with registers saved and restored. You'll also notice many instructions have a number in parentheses at the end of the comment, which is the number of clock cycles to execute each one. There's also a JUMP table in ROM that points to many routines, so you have a common address to call them from, regardless of future code changes. Finally, here's the shorter routine that is used to benchmark the 16MB sequential read:

Code:
; Simple test program to transfer multiple sectors - READ
;
        LDA     #$00            ;Get transfer address
        LDY     #$10            ; of $1000
        LDX     #$01            ;Sector count of 1
        JSR     $FF15           ;Call BIOS routine to set it
        LDA     #$00            ;Get LBA starting block
        LDY     #$00            ; of $000000
        LDX     #$00            ;
        JSR     $FF12           ;Call BIOS to set it
;
        LDX     #$00            ;Set for 256 blocks (128KB)
        LDY     #$80            ;Set multiplier of 128 (* 256)
        JSR     B_CNT_STRT      ;Reset and start benchmark counter
;
LBA_RBLK
        JSR     $FF09           ;Call BIOS Read Block
        LDA     IDE_STATUS_RAM  ;Get IDE Status (RAM)
        LSR     A               ;Shift error bit into carry
        BCS     RD_ERR          ;Branch if error
        INC     $FA             ;Else, increment LBA low number
        BNE     SKP_HI_RD       ;Skip if no rollover
        INC     $FB             ;Else, increment LBA high byte
;
SKP_HI_RD
        DEX                     ;Decrement low index
        BNE     LBA_RBLK        ;Loop back until zero
        DEY                     ;Decrement multiplier index
        BNE     LBA_RBLK        ;Loop back until done
        JSR     M_CROUT         ;Send CR/LF
        JSR     M_QUITB         ;Quit Benchmark counter, print results
        RTS                     ;Return to caller
;
RD_ERR
        JSR     B_IDE_GET_STAT  ;Get Extended error code (X reg)
        BRK                     ;Enter Monitor, display registers
;

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
PostPosted: Wed Jan 20, 2021 7:29 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8460
Location: Southern California
plasmo wrote:
You are running the 16MB read with the 100Hz interrupt running, right? Have you measure how much time the interrupt processing uses?

The "jiffy timer" (as BDD calls it) need not take much of the processing time at all. The 6502 interrupts primer has code about 40% of the way down the page to set it up, and then code about 60% of the way down the page for running the 32-bit binary timer and seven bytes of time-of-day plus date variables. For my own use, I also add a 32-bit variable holding the time of next alarm due and code in the ISR to check for a match, and the whole NMI ISR takes about 1% of the processor's time at 5MHz. (Most occurrences take very few instructions because the increment of the lowest byte does not carry, so the next ones can be skipped; also, the checking for a match on the alarm-due time usually fails on the first byte.) If there's no alarm pending but the time matches, what happens is basically like the routine called from there returns with no action.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Wed Jan 20, 2021 9:13 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8230
Location: Midwestern USA
floobydust wrote:
Code:
LBA_RD_WAIT
                LDA     IDE_ALT_STATUS  ;Get IDE Alternate Status register (4)
                CMP     #$50            ;Compare for ready (2)
                BEQ     LBA_RD_NORM     ;If normal status, finish up and exit (2/3)
                CMP     #$51            ;Compare for error bit set (2)
                BEQ     LBA_RD_ERR      ;Branch to handle error (2/3)
                BRA     LBA_RD_WAIT     ;Else branch back (waiting on IDE controller) (3)
;
LBA_RD_ERR ...

Rewriting the above as the following would subtract a few cycles from the LBA_RD_WAIT section:

Code:
LBA_RD_WAIT     LDA     IDE_ALT_STATUS  ;Get IDE Alternate Status register (4)
                CMP     #$50            ;Compare for ready (2)
                BEQ     LBA_RD_NORM     ;If normal status, finish up and exit (2/3)
                CMP     #$51            ;Compare for error bit set (2)
                BNE     LBA_RD_WAIT     ;branch back (waiting on IDE controller) (3)...
;
;   ——————————————————————————————————————————————————————————————————
;   When possible, arrange your code so the most common or likely case
;   does not cause a branch to be taken.
;
;   Also, test values such as $50 & $51 should be symbolically defined
;   instead of hard-coded.  What, exactly, is it that is being tested?
;   One would never know from reading the above code.  It may be that
;   the tests could be more efficiently carried out, but I can't dec-
;   ide if that's the case because I can't tell whether the CMP #$50 &
;   CMP #$51 test values are bit-wise or absolute.
;   ——————————————————————————————————————————————————————————————————
;
LBA_RD_ERR ...

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Thu Jan 21, 2021 1:53 am 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1378
GARTHWILSON wrote:
plasmo wrote:
You are running the 16MB read with the 100Hz interrupt running, right? Have you measure how much time the interrupt processing uses?

The "jiffy timer" (as BDD calls it) need not take much of the processing time at all. The 6502 interrupts primer has code about 40% of the way down the page to set it up, and then code about 60% of the way down the page for running the 32-bit binary timer and seven bytes of time-of-day plus date variables. For my own use, I also add a 32-bit variable holding the time of next alarm due and code in the ISR to check for a match, and the whole NMI ISR takes about 1% of the processor's time at 5MHz. (Most occurrences take very few instructions because the increment of the lowest byte does not carry, so the next ones can be skipped; also, the checking for a match on the alarm-due time usually fails on the first byte.) If there's no alarm pending but the time matches, what happens is basically like the routine called from there returns with no action.


Hi Garth,

Yes, as usual, I've also looked at your interrupts section over the years. I also have the old 6502 Assembly Subroutines (Leventhal/Seville) and my RTC routine was somewhat patterned after that. As noted, it's not a huge chunk of clock cycles, but if everything lines up just right, it can be a long once through the routine to update everything on a rollover. Timer handler is here:

Code:
;NOTE: Stop timer cmd resets the interrupt flag, counter continues to generate interrupts.
; NOTE: 25 clock cycles to here from INTERUPT0 - 50 in total
;
UART_RTC        LDA     #%10010000      ;Get Command mask for stop timer (2)
                STA     UART_COMMAND    ;Send command to 2691 (4)
;
; Check the MATCH flag bit7 to see if a Delay is active. If yes, decrement the MSDELAY
; variable once each pass until it is zero, then clear the MATCH flag bit7
;
                BBR7    MATCH,SKIP_DLY  ;Skip Delay if bit7 is clear (5)
                DEC     MSDELAY         ;Decrement Millisecond delay variable (5)
                BNE     SKIP_DLY        ;If not zero, skip (2/3)
                RMB7    MATCH           ;Else clear MATCH flag (5)
;
; Check the MATCH flag bit6 to see if Benchmarking is active. If yes, increment the
; variables once each pass until the MATCH flag bit6 is inactive.
;
SKIP_DLY        BBR6    MATCH,SKIP_CNT  ;Skip Count if bit6 bit is clear (5)
                INC     MS10_CNT        ;Else, increment 10ms count (5)
                LDA     MS10_CNT        ;Load current value (3)
                CMP     #100            ;Compare for 1 second elapsed time (2)
                BCC     SKIP_CNT        ;If not, skip to RTC update (2/3)
                STZ     MS10_CNT        ;Else, zero 10ms count (3)
                INC     SECL_CNT        ;Increment low byte elapsed seconds (5)
                BNE     SKIP_CNT        ;If no overflow, skip to RTC update (2/3)
                INC     SECH_CNT        ;Else, increment high byte elapsed seconds (5)
;
SKIP_CNT        DEC     TICKS           ;Decrement RTC tick count (5)
                BNE     REGEXT0         ;Exit if not zero (2/3)
                LDA     #DF_TICKS       ;Get default tick count (2)
                STA     TICKS           ;Reset tick count (3)
;
                INC     SECS            ;Increment seconds (5)
                LDA     SECS            ;Load it to A reg (3)
                CMP     #60             ;Check for 60 seconds (2)
                BCC     REGEXT0         ;If not, exit (2/3)
                STZ     SECS            ;Else, reset seconds, inc Minutes (3)
;
                INC     MINS            ;Increment Minutes (5)
                LDA     MINS            ;Load it to A reg (3)
                CMP     #60             ;Check for 60 minutes (2)
                BCC     REGEXT0         ;If not, exit (2/3)
                STZ     MINS            ;Else, reset Minutes, inc Hours (3)
;
                INC     HOURS           ;Increment Hours (5)
                LDA     HOURS           ;Load it to A reg (3)
                CMP     #24             ;Check for 24 hours (2)
                BCC     REGEXT0         ;If not, exit (2/3)
                STZ     HOURS           ;Else, reset hours, inc Days (3)
;
;This is the tricky part ;-)
; One variable holds the Day of the week and the Date of the Month!
; First, update the Day of the Week, which is the upper 3 bits of the variable
; Valid days are 1 to 7. Mask off the upper 3 bits, and add #%00100000 to it,
; if the result is zero, it was #%11100000, so start over by making the Day
; #%00100000, then save it to RTC_TEMP variable.
;
;Once that's done, load the variable again, mask off the Date and increase by
; one, then check against days of the month, update as required and check for
; Leap year and February 29th stuff. When all updated, OR in the Day from the
; RTC_TEMP variable and finish updating the DAY_DATE variable.
;
                LDA     DAY_DATE        ;Get Day and Date variable (3)
                AND     #%11100000      ;Mask off for Day of Week (1-7) (2)
                CLC                     ;Clear carry for add (2)
                ADC     #%00100000      ;Add effective "1" to Day of week (2)
                CLC                     ;Clear carry to avoid extra day add (2)
                BNE     NO_DAY_ADD      ;If non-zero, don't reset to "1" (2/3)
                LDA     #%00100000      ;Else, reset Day to "1" (2)
NO_DAY_ADD
                STA     RTC_TEMP        ;Store the updated Day into temp (3)
;
;Get the Month and Year variable, move the upper 4-bits down to get the
; current Month. Xfer it the X reg, then get the Date, increment by one
; and check against the number of days in that month.
;
                LDA     MONTH_CENTURY   ;Get Month and Year variable (3)
                LSR     A               ;Shift Month to lower 4 bits (2)
                LSR     A               ; (2)
                LSR     A               ; (2)
                LSR     A               ; (2)
                TAX                     ;Move to X reg (2)
                LDA     DAY_DATE        ;Get Day and Date variable (3)
                AND     #%00011111      ;Mask off for Date of Month (1-31) (2)
                INC     A               ;Increment by one (2)
                CMP     MAX_DATE-1,X    ;Check for Max Day per Month+1 (4)
                BCS     MONTH_ADD       ;Branch if we need to update the Month (2/3)
DO_29           ORA     RTC_TEMP        ;Else OR in updated Date to updated Day (3)
                STA     DAY_DATE        ;Update Day and Date variable (3)
                BRA     REGEXT00        ;Then exit IRQ Handler (3)
;
MONTH_ADD       CPX     #$02            ;Check for Month = February (2)
                BNE     MONTH_INC       ;If not, increment Month (2/3)
                LDA     YEAR            ;Else, Get current year low byte (3)
                AND     #%00000011      ;Mask off lower 2 bits (2)
                BNE     MONTH_INC       ;If not a Leap Year, continue on (2/3)
                LDA     DAY_DATE        ;Get Day and Date variable (3)
                AND     #%00011111      :Mask off Date (2)
                INC     A               ;Increment by one (2)
                CMP     #30             ;Check for 29th+1 Day for Leap Year (2)
                BCC     DO_29           ;Save Date as 29th and exit IRQ handler (2/3)
;
MONTH_INC       LDA     RTC_TEMP        ;Get updated Day (Date is effective zero)
                ORA     #%00000001      ;OR in the 1st Day of the Month
                STA     DAY_DATE        ;Save updated Day and Date of the Month
;
                LDA     MONTH_CENTURY   ;Get Month and Year variable
                CLC                     ;Clear Carry for add
                ADC     #$10            ;Add "1" to Month (upper 4 bits)
                STA     RTC_TEMP        ;Save it to work temp
                AND     #%11110000      ;Mask off Century (lower 4 bits)
                CMP     #$D0            ;Check for "13" (December + 1)
                BCS     YEAR_ADD        ;If 13 go add to YEAR
                LDA     RTC_TEMP        ;Else, Get updated Month and Century
                STA     MONTH_CENTURY   ;Save it
                BRA     REGEXT00        ;Exit IRQ Handler
;
YEAR_ADD        LDA     MONTH_CENTURY   ;Get Month and Century
                AND     #%00001111      ;Mask off old month
                ORA     #%00010000      ;OR in $10 for January
                STA     MONTH_CENTURY   ;Save updated Month and existing upper 4 bits
                INC     YEAR            ;Increment Year low byte (0-255)
                BNE     REGEXT00        ;If no rollver, exit ISR
                LDA     MONTH_CENTURY   ;Get Month and Year variable
                TAX                     ;Save to X reg
                AND     #%11110000      ;Mask off upper 4-bits for year
                STA     RTC_TEMP        ;Save it in the temp area
                TXA                     ;Get the Month and Year back
                AND     #%00001111      ;Mask off the month
                CLC                     ;Clear carry for add
                ADC     #$01            ;Add 1 to upper 4 bits
                ORA     RTC_TEMP        ;OR in the Month
                STA     MONTH_CENTURY   ;Update Month and Century variable
REGEXT00        JMP     (IRQRTVEC0)     ;If no rollover, then exit IRQ handler (6)

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
PostPosted: Thu Jan 21, 2021 2:01 am 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1378
BigDumbDinosaur wrote:
floobydust wrote:
Code:
LBA_RD_WAIT
                LDA     IDE_ALT_STATUS  ;Get IDE Alternate Status register (4)
                CMP     #$50            ;Compare for ready (2)
                BEQ     LBA_RD_NORM     ;If normal status, finish up and exit (2/3)
                CMP     #$51            ;Compare for error bit set (2)
                BEQ     LBA_RD_ERR      ;Branch to handle error (2/3)
                BRA     LBA_RD_WAIT     ;Else branch back (waiting on IDE controller) (3)
;
LBA_RD_ERR ...

Rewriting the above as the following would subtract a few cycles from the LBA_RD_WAIT section:

Code:
LBA_RD_WAIT     LDA     IDE_ALT_STATUS  ;Get IDE Alternate Status register (4)
                CMP     #$50            ;Compare for ready (2)
                BEQ     LBA_RD_NORM     ;If normal status, finish up and exit (2/3)
                CMP     #$51            ;Compare for error bit set (2)
                BNE     LBA_RD_WAIT     ;branch back (waiting on IDE controller) (3)...
;
;   ——————————————————————————————————————————————————————————————————
;   When possible, arrange your code so the most common or likely case
;   does not cause a branch to be taken.
;
;   Also, test values such as $50 & $51 should be symbolically defined
;   instead of hard-coded.  What, exactly, is it that is being tested?
;   One would never know from reading the above code.  It may be that
;   the tests could be more efficiently carried out, but I can't dec-
;   ide if that's the case because I can't tell whether the CMP #$50 &
;   CMP #$51 test values are bit-wise or absolute.
;   ——————————————————————————————————————————————————————————————————
;
LBA_RD_ERR ...


Hi BDD,

Yes, my bad... I haven't gone through all of the code on a clean up / optimize mission as of yet. As the lowest order bit is the Error bit from the Status register, I might do a logical shift to the carry flag and branch on that.

As these are code snippets, the bit meanings for the Status register are covered elsewhere in the code. Granted, I could declare them with a more obvious definition, but I usually use a binary representation for those. In any case, I did define the bit meanings elsewhere in the BIOS source:

Code:
; Status Register bits as defined as follows:
;       - Bit 7 = Busy (a Command has been accepted)
;       - Bit 6 = Ready (IDE controller is ready to accept a command)
;       - Bit 5 = Write Fault (A write command failed against the media)
;       - Bit 4 = DSC (is set when the CF Card is ready)
;       - Bit 3 = Data Request (set when there is data to transfer, read or write)
;       - Bit 2 = Correction (set when a recoverable data error was corrected)
;       - Bit 1 = 0 (not used)
;       - Bit 0 = Error (set when the previous command had an unrecoverable error)
;


At the rate of finding and fixing odd issues that come up, I'm thinking I'll likely call the first truly final BIOS release 3.10 :wink:

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
PostPosted: Thu Jan 21, 2021 4:58 am 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1378
plasmo wrote:
I hooked up a scope and measured the ISR. It is about 80mS. The reason for the long ISR is because it updates the entire I2C display every second to show the clock movement. Calculate a new clock face takes 7mS, bit-banging the I2C bus takes most of the 80mS because I update the entire 128x64 display.

The CF interface is not interrupt driven at all. It is straight polling, one sector at a time. I only move 512 byte data from CF to corresponding 512 byte memory locations. For diagnostic purpose (at the cost of 3 more seconds) I put out a dot for each sector read and a 't' for the beginning of a new track. The display looks right. Attached it the 16meg read test routine.
Bill


Hope you don't mind, but I took your code and changed it a bit.... it's shorter and will execute a bit quicker. I didn't see the hexout routine used, but I shortened that a bit too.

Attachment:
testMon2.txt [2.55 KiB]
Downloaded 39 times

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 24 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 43 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: