6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Nov 24, 2024 1:18 pm

All times are UTC




Post new topic Reply to topic  [ 321 posts ]  Go to page Previous  1 ... 18, 19, 20, 21, 22  Next
Author Message
PostPosted: Sun Feb 11, 2018 4:57 am 
Offline

Joined: Sat Mar 11, 2017 1:56 am
Posts: 276
Location: Lynden, WA
Chip is 65C02. No NMOS here.

I'm not sure what interrupt question you want answered. If I'm understanding though, even if I RTS to a garbage location, my ISR should still work. Yet it never goes back high.

I'm not knowingly calling any WAI or BRKs. Don't know what a JAM instruction is.

I don't understand how my calling routine could be effected by my saving the regs on the stack. The subroutine is not reliant on previous register values, and why would that matter anyway? Doesn't push leave the register unchanged?


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 11, 2018 6:26 am 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
Dan Moos wrote:
Chip is 65C02. No NMOS here.
Okay, cool -- thanks.

Quote:
If I'm understanding though, even if I RTS to a garbage location, my ISR should still work. Yet it never goes back high.
Rather than RTS, don't you mean RTI? Not confusing interrupts and subroutine calls, I hope. Just checking!

Quote:
I'm not sure what interrupt question you want answered.
I just want an overview of all the relevant pieces of this problem so I understand what is supposed to happen. I haven't read the entire thread; sorry it's too long. So, I'm relying on the summary you gave earlier tonight:

    "There is a terminal read routine that takes data from the ACIA, and puts it in the COM1_RXBUF string. Every time there is a keypress, that routine checks to see if it was the "enter" key. If it was, it puts a null at the end of the COM1_RXBUF string, and calls the naughty subroutine that's having the problem. That routine's job is to take the string that looks like, for random example, "read 8000 80FF" and turn it into "read<null>8000<null>80FF <null>" It also stores indices in the ARG_INDEX array so that the parser can extract them. The parser would be the next thing called if nothing crashes."

So, does that include the part which involves interrupts? It almost sounds as if the whole thing (except the parser) might be included in the ISR, but you don't say. The summary is helpful, but it seems to omit some key details.

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 11, 2018 3:41 pm 
Offline

Joined: Sat Mar 11, 2017 1:56 am
Posts: 276
Location: Lynden, WA
I see the confusion. This is not my ISR. Not is it running during my ISR. My ISR just sets a flag that tells my main loop a key has been pressed. Thus, I do indeed mean RTS, not RTI.


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 11, 2018 4:58 pm 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
Quote:
even if I RTS to a garbage location, my ISR should still work. [...] I do indeed mean RTS, not RTI.
Looking at this with fresh eyes this morning I see I somehow mis-read -- sorry. Indeed you could RTS to a garbage location. And "garbage" to me means PC is eating something it shouldn't, either data or irrelevant code. Or I/O :shock: or uninitialized/unpopulated memory. Often the errant PC will end up repeating a loop of some kind. But it is still fetching instructions and at least trying. :)

A JAM instruction is an undefined opcode which, on an NMOS chip, causes it to cease fetching instructions. It's considered a bug. Only a reset will get you out of the lockup. Modern WDC chips have an instruction whose effect is similar or identical, but they gave it a new opcode and an official name: STP ( SToP ). The only trigger to exit the lockup is if you do a reset (give the CPU's RESB input a low pulse). Another new opcode is WAI (WAIt), which is similar but -- more conveniently -- it accepts the exit trigger on the CPU's IRQB input instead. This works even after an SEI, so you can forgo the actual interrupt if you want to. The datasheet has the details.

OK, so: the ISR sets a flag that tells the main loop a key has been pressed. Is it also the ISR's job to read the the peripheral's status register/whatever (and thus allow the IRQ line to return high)? Y' might wanna show us the ISR too, please -- also any other code that may be pertinent.

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Last edited by Dr Jefyll on Sun Feb 11, 2018 5:36 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 11, 2018 5:36 pm 
Offline

Joined: Sat Mar 11, 2017 1:56 am
Posts: 276
Location: Lynden, WA
Here I post more code. I tried to fix the horrible formatting a simple cut and paste winds up with. Hopefully I didn't create typos in that process.

EDIT: my formatting turned out no better. sorry :P

Here is the ISR. As you can see, it does as little as possible. I don't think it is related to the problem, as it will long have finished, and multiple JSR's called by the time the problem happens.

Code:
;***************************
;Interrupt service routine *
;***************************   
 
isr:         PHA
      
      LDA      COM1_STA      ;get ACIA status register
      
            BPL      end_isr                ;if no IRQ, return from ISR
            LDA          #$FF
            STA          READ_FLAG    ;main loop pols this to see if a key has been presses
        
end_isr:       PLA
      RTI


Also, here is the snippet in my main loop that deals with the flag the ISR sets.

Code:
inf_loop:   LDA      READ_FLAG   ;see if key was pressed. ISR sets this flag
      BNE      read_irq           ;not 0? Flag was set
      JMP      inf_loop            ;no flag. Loop as usual
read_irq:   STZ      READ_FLAG   ;flag was set. Clear it
      JSR      terminal_r   ;process the keypress
      JMP      inf_loop           ;loop as usual


Finally, here is the terminal_r subroutine. This routine eventually calls process_cmdln, which is the troublesome routine. I edited my comments to reflect our conversation in a few spots, so look for that.

Code:
;**************************
;Terminal read subroutine *
;**************************   
 
terminal_r:   
               
      PHA
      PHX
      PHY
   
      LDX      STRING_INDEX      ;get string index. This is index is the current position in       the input buffer
      LDY      COM1_DAT              ;get data  from ACIA Rx   
            CPY      #$0D                         ;check if enter was pressed
            BEQ      process_cmd              ;if enter pressed, process the input string
            JSR           com1_TX_chk           ;wait til TX buffer clears
            TYA      ;I can't remember why I initially use Y, and then transfer it. Clue maybe?
            STA      COM1_DAT
            STA      COM1_RXBUF,X      ;place recieved data into input buffer
            INX                                       ;increment it
            STX      STRING_INDEX      ;put incremented version back in memory
         
            PLY
            PLX
            PLA
            RTS
process_cmd: 
                STZ      COM1_RXBUF,X      ;put NULL on end of string
            JSR      process_cmdln           ;index arguments, if any. This is the subroutine that crashes   
            STZ      STRING_INDEX      ;reset index. When it crashes, this line is never reached
            JSR      parse_cmd              ;parse the command
            JMP      (SELECTED_CMD)      ;run routine for selected command
rtn_to_tr:    LDA      #<prompt              ;get lower byte of prompt message
            STA      STRING_PTR              ;store it in first byte of the string pointer
            LDA      #>prompt              ;get upper byte of prompt message
            STA      STRING_PTR+1      ;store it in second byte of string pointer     
            JSR      terminal_w              ;display prompt
            PLY
            PLX
            PLA
            RTS


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 11, 2018 5:53 pm 
Offline

Joined: Sat Mar 11, 2017 1:56 am
Posts: 276
Location: Lynden, WA
I fixed another error in my post. I had incorrectly commented that the stack calls in terminal_r cause the problem. That is incorrect. Those calls work. It is the calls in process_cmdln, the routine I posted earlier. Sorry. I edited that comment out just now


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 11, 2018 6:08 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1950
Location: Sacramento, CA, USA
Code:
     ...
     JMP  (SELECTED_CMD)      ;run routine for selected command
     ...


I'm not sure (because AFAIK you haven't posted a source for any SELECTED_CMDs here), but it seems to me that you could be unbalancing your stack and setting yourself up for failure here. Since this is a JMP and not a JSR, it is the duty of each of your SELECTED_CMDs to JMP rtn_to_tr: when they're done doing their thing, instead of RTS, which if I'm not mistaken would cause a sudden swerve into the weeds like the one you describe. There is no JSR (abs) on the 65c02, but it can be easily simulated with a little "springboard", if you decide to pursue that path.

Mike B.


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 11, 2018 6:51 pm 
Offline

Joined: Sat Mar 11, 2017 1:56 am
Posts: 276
Location: Lynden, WA
They do indeed jump back. I do it this way because I don't know what routine I'm going to run until the last minute. There is a jump, not a RTS at returning end. There are no JSRs unmatched with RTSs. Each
Jumped o to routine returns to this spot via a another jump
Never the less, this all works.


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 11, 2018 7:00 pm 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
Quote:
They do indeed jump back.
OK, understood.
Quote:
I do it this way because I don't know what routine I'm going to run until the last minute.
Alright. (Ideally what you want is a JSR using indirect mode. And that can be faked with a springboard as Mike said -- by manually pushing the return address then doing an indirect JMP, but never mind that now.)

Code:
            JSR      process_cmdln           ;index arguments, if any. This is the subroutine that crashes   
            STZ      STRING_INDEX      ;reset index. When it crashes, this line is never reached
And "when it crashes" is also when the interrupt line gets stuck low? I think it's more accurate to say it goes and stays low at the time of the following keypress -- right?

This is quite a head-scratcher! Just in case, can you also show us the labels that define all the pertinent memory addresses please.

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 11, 2018 8:46 pm 
Offline

Joined: Sat Mar 11, 2017 1:56 am
Posts: 276
Location: Lynden, WA
Yes, it is the key press immediately following the crash that sticks the IRQ line low.

Here is my complete label space.

Code:
     
;*************     
;UART Labels *
;*************

;COM1. Main UART           
COM1_DAT = $5000
COM1_STA = COM1_DAT+1
COM1_CON = COM1_DAT+3
COM1_COM = COM1_DAT+2

;************************
;Display port VIA Labels*
;************************

VIA_1PORTB = $6000
VIA_1PORTA = VIA_1PORTB+1
VIA_1DDRB  = VIA_1PORTB+2
VIA_1DDRA  = VIA_1PORTB+3



;******************
;0 page variables *
;******************

STRING_PTR       .DS       $02     ;pointer to current output message string
STRING_INDEX    .DS       $01     ;index into input buffer
LINE_CNT       .DS       $01     ;clear screen line count
ARG_INDEX   .DS   $03   ;indices into command line string to arguments
ARG_CNT      .DS       $01   ;number of command line arguments
CMD_INDEX      .DS       $02     ;index into command string list   
SELECTED_CMD    .DS       $02     ;pointer to selected command routine
CMDLN_ERROR   .DS   $01   ;command line error condition
ARG_TEMP   .DS    $04   ;holds the 4 bit decoded character values of input arguments
ARG_VALUE   .DS    $02   ;the final 16 bit numeric value of the arguments
ADR_START   .DS   $02   ;holds lower limit of commandline memory ops.
ADR_END      .DS   $02   ;holds upper limit of commandline memory ops.
HEX_INPUT   .DS   $02
HEX_HIGH   .DS   $01   ;holds ascii representation of high nibble of displayed hex byte
HEX_LOW      .DS   $01   ;holds ascii representation of low nibble of displayed hex byte
HEX_DSPLY   .DS   $28   ;holds strings of hex/ascii data to be displayed
SCRN_BF_TOP   .DS   $02   ;top of ring buffer that contains screen contents. Physical top of screen.
SCRN_BF_CUR   .DS   $02   ;pointer to current cursor position in display buffer
ROW      .DS   $01   ;current cursor row position
COLUMN      .DS   $01   ;current cursor column position
CHARACTER   .DS   $01   ;current character to be displayed
BUG_LITE   .DS   $01   ;debug status light on VIA_1 PA0
READ_FLAG   .DS   $01

;***************************
;memory starting at page 2 *
;***************************

           .ORG      $0200
           
           
SCRN_BUF   .DS   $C80   ;Screen buffer. 3,200 bytes for an 80*40 character display.   
                              ;Placed at start of page for ease of inderect indexing


SCRN_BUF_ENDL = $80      ;C80 is 3,200 for a 3,200 byte 80*40 character screen buffer
SCRN_BUF_ENDH = $0E
                             
                              
                              
COM1_RXBUF      .DS      $100    ;UART input buffer
         


;**********************
;ROM data starts here *
;**********************

            .ORG    $8000
     
     
;*****************
;command strings *
;*****************
     
commands:   
cmd_empty:    .BYTE   $00         ;empty:user pressed enter with no characters
cmd_write:     .BYTE   "write", $00      ;command to write to memory
cmd_read:      .BYTE   "read", $00     ;command to read from memory
cmd_test:      .BYTE   "test", $00      ;command for system self-test
cmd_clr:   .BYTE   "clr", $00   ;command to clear screen
cmd_list_end:   .BYTE   $FF         ;marker for end of list

;******************************
; pointers to command strings *
;******************************

cmd_indices:
empty_index:    .WORD   cmd_empty
write_index:    .WORD   cmd_write
read_index:     .WORD   cmd_read
test_index:     .WORD   cmd_test
clr_index   .WORD    cmd_clr

;******************************
;pointers to command routines *
;******************************
   
cmd_routines:
empty_rtn_ptr:  .WORD   empty_rtn
write_rtn_ptr:  .WORD   write_rtn
read_rtn_ptr:   .WORD   read_rtn
test_rtn_ptr:   .WORD   test_rtn
clr_rtn_ptr   .WORD   clr_rtn
no_cmd_rtn_ptr: .WORD   no_cmd_rtn

;************
;UI strings *
;************ 
     
strings:
prompt:         .BYTE   $0D, $0A, $0A,  "WOPRjr:" ,$00       ;commandline prompt
empty_msg      .BYTE   $0D, $0A, $00                  ;empty string
message:      .BYTE   $0D, $0A, "You entered: ", $00        ;input response message
write_msg:      .BYTE   $0D, $0A, "writing", $00         ;temp write message
read_msg:      .BYTE   $0D, $0A, $0A, "No arguments entered."    ;line 1 of default read message
read_msg2:   .BYTE   $0D, $0A,"Enter a single HEX address, or 2 addresses as a range", $00 ;line 2
test_msg:      .BYTE   $0D, $0A, "testing", $00
clr_msg:   .BYTE   $0D, $0A, "clearing", $00      ;temp clear screen message
nl_cr:      .BYTE   $0D, $0A, $00            ;newline, carriage return
no_cmd_msg:      .BYTE   $0D, $0A, $0A, "Unknown command", $00    ;default message for unknown string
bad_args_msg:    .BYTE   $0D, $0A, $0A, "Bad, or too many arguments", $00   ;wrong or too many arguments
     


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 11, 2018 8:51 pm 
Offline

Joined: Sat Mar 11, 2017 1:56 am
Posts: 276
Location: Lynden, WA
BTW. , About my indirect jumps: Is the reason to push the return address and JMP() so that I can have an RTS in the routine, making it a little more universal, rather than only being able to return to a hardcoded location? Makes sense if so, although right now I can't imagine needing that flexibility in these particular routines. Maybe a good practice for my future indirect subroutine jumps?


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 11, 2018 9:01 pm 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
Dan Moos wrote:
BTW. , About my indirect jumps: Is the reason to push the return address and JMP() so that I can have an RTS in the routine, making it a little more universal, rather than only being able to return to a hardcoded location? Makes sense if so, although right now I can't imagine needing that flexibility in these particular routines. Maybe a good practice for my future indirect subroutine jumps?
Right. There's still a hardcoded return address, but it appears only once in the caller routine, not n times (once in each of the callee routines). Just remember we're in 6500-land, so it's Return_Address minus one the caller needs to manually push.

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Last edited by Dr Jefyll on Sun Feb 11, 2018 9:04 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 11, 2018 9:03 pm 
Offline

Joined: Sat Mar 11, 2017 1:56 am
Posts: 276
Location: Lynden, WA
BTW. I love these kind of bugs. Forces me to gain a much deeper understanding of how the system works.

When reading my code, I offer this disclaimer: this is the first and only thing I've ever written in 6502 assembly, and its been pecked away slowly over many months. You might notice that my skills were at different places in different sections! Also, I'm sure there is old code that never gets used form earlier iterations that I haven't cleaned up yet. A thorough re-write is in the near future. Other than this issue, it all works perfectly though.


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 11, 2018 9:12 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1950
Location: Sacramento, CA, USA
The "springboard" method is easier than manually pushing any hard-coded addresses. All you need to do is find a little out-of-the-way spot to stick
Code:
springboard:
     JMP  (SELECTED_CMD)

then
Code:
     GET  some_stuff_done
     JSR  springboard
     GET  more_stuff_done

and let the assembler figure the addresses and the 65c02 do the pushing for you.

Mike B.


Top
 Profile  
Reply with quote  
PostPosted: Sun Feb 11, 2018 9:16 pm 
Offline

Joined: Sat Mar 11, 2017 1:56 am
Posts: 276
Location: Lynden, WA
Damn, that's clever


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 321 posts ]  Go to page Previous  1 ... 18, 19, 20, 21, 22  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 9 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: