6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Tue May 14, 2024 6:21 am

All times are UTC




Post new topic Reply to topic  [ 14 posts ] 
Author Message
PostPosted: Mon Apr 17, 2023 1:34 am 
Offline

Joined: Mon Apr 17, 2023 1:12 am
Posts: 3
I built myself a 6502 based SBC with a 6522 and a 68B50 ACIA. At first it was doing polling to service the ACIA but I took a run at writing an interrupt service routine for the ACIA. I mainly followed the tutorial at http://6502.org/tutorials/interrupts.html by Garth Wilson with some slight modifications and its "working" mostly. I think that most, if not all of my other calls to read the serial port are of the style that it looks at the status and waits until a character is ready, but it fails to work with my xmodem routine that uses a different type of read that sets the carry flag if a character is ready, but returns with the carry flag clear if the character is not ready.

The relevant code is here:
Code:
WR_ptr = $ff            ; read and write buffer positons
RD_ptr = $fe            ; zero page
SER_cnt = $fd           ; count of bytes in the buffer
SER_buf = $0200         ; ring buffer for serial

WR_BUF:  LDX  WR_ptr     ; Start with A containing the byte to put in the buffer.
         STA  SER_buf,X   ; Get the pointer value and store the data where it says,
         INC  WR_ptr     ; then increment the pointer for the next write.
         INC  SER_cnt   ; increment the count
         RTS
 ;-------------
RD_BUF:  lda SER_cnt
         cmp #$e0       ; compare with 224
         bcs RD_BUF1    ; if its positive then leave the flow off
         lda #%10010101  ; Rx int, no Tx int + RTS low, 8n1, /16
         sta ACIActl     ; will result in 19200 bps
RD_BUF1: LDX  RD_ptr     ; Ends with A containing the byte just read from buffer.
         LDA  SER_buf,X   ; Get the pointer value and read the data it points to.
         INC  RD_ptr     ; Then increment the pointer for the next read.
         DEC  SER_cnt   ; decrement the count
         RTS
 ;-------------
ACIA1_Input:    ; wait for a character on rx
                lda SER_cnt
                beq ACIA1_Input ; wait for a character to be ready
                jsr RD_BUF      ; read the character
                rts

Get_Chr:
ACIA1_Scan:     ; check if a character is waiting and get it
                clc             ; clear carry flag
                lda SER_cnt
                beq ACIA_noscan ; no character is waiting so return
                jsr RD_BUF      ; read the character
                sec             ; set carry flag if we read a character
ACIA_noscan:    rts



and the ISR is here:
Code:
Interrupt      PHA                     ; a
               TXA                     ;
               PHA                     ; X
                lda     ACIActl
                bpl     ACIA_end        ; ACIA didn't call so go to end
                and     #$01            ; check if data is there
                beq     ACIA_error      ; no data - means an error conditon
                lda     ACIAdat         ; ACIA has data
                jsr     WR_buf          ;
                lda     SER_cnt         ; check how full the buffer is
                cmp     #$f0            ;
                bcc     IRQ_cleanup
                lda #%11010101  ; Rx int, no Tx int + RTS high, 8n1, /16
                sta ACIActl     ; will result in 19200 bps

ACIA_end:      TSX                     ; get stack pointer
               LDA   $0103,X           ; load INT-P Reg off stack
               AND   #$10              ; mask BRK
               BNE   BrkCmd            ; BRK CMD
ACIA_error:     bit     ACIAdat         ; read the data to clear interrupt
IRQ_cleanup:   PLA                     ; x
               tax                     ;
               pla                     ; a
NMIjump        RTI                     ; Null Interrupt return
BrkCmd         pla                     ; X
               tax                     ;
               pla                     ; A
               jmp   BRKroutine        ; patch in user BRK routine


I don't expect anyone to debug my code - but this is my first time ever writing an ISR, and I was wondering if I'm missing anything obvious. Would I need to have any of the other routines execute with SEI or anything. I think I talked myself out of it, since I thought that the ISR was restoring everything before returning.

Many thanks,
Tom.


Top
 Profile  
Reply with quote  
PostPosted: Mon Apr 17, 2023 7:37 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10800
Location: England
Welcome!

One thing which jumps out is that you're not saving Y on the stack, but you are calling into other routines - do those routines take care to preserve Y?


Top
 Profile  
Reply with quote  
PostPosted: Mon Apr 17, 2023 8:39 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8182
Location: Midwestern USA
First off, take a careful look at this code fragment from your ISR and see if you can see what I see. :D I gave you a small hint.

Code:
          lda     SER_cnt         ; check how full the buffer is
          cmp     #$f0            ;better if this is $E0
 ——————>  bcc     IRQ_cleanup <——————
          lda     #%11010101      ; Rx int, no Tx int + RTS high, 8n1, /16
          sta     ACIActl         ; will result in 19200 bps

ACIA_end: TSX                     ; get stack pointer

Incidentally, $F0 as a high-water mark is probably too high. I’d use $E0 to give the remote station more time to stop sending when RTS is de-asserted. Along with that, I’d reduce the low-water mark to $A0.

BTW, it isn’t necessary or desirable to maintain a queue counter (what you are calling a “buffer” is actually a circular queue or FIFO) as you are doing with SER_cnt. Relying on that counter creates the potential for queue corruption. The value you are maintaining in SER_cnt can be determined on the fly by subtracting the read index from the write index and ignoring the borrow. There is no need to use precious zero page space storing a number that only matters for managing flow control.

To determine if there are any bytes in the queue, simply compare the get and put indices. If they are equal, the queue is empty.

One other thing...subroutines are best avoided in ISRs. In your code, the call to WR_BUF consumes 12 cycles just executing JSR and RTS. Since the 6850 ACIA family has no receiver FIFO, an IRQ will occur on each incoming datum, requiring a call to WR_BUF to store it, assuming the queue has room. At the 6850’s official maximum speed of 38,400 bps, and assuming 8-N-1 data format, those JSR - RTS pairs will consume 46,080 MPU cycles per second of continuous serial input data flow.

Lastly, do you have the 6850’s DCD permanently tied low?

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


Top
 Profile  
Reply with quote  
PostPosted: Mon Apr 17, 2023 8:40 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8182
Location: Midwestern USA
BigEd wrote:
Welcome!

One thing which jumps out is that you're not saving Y on the stack, but you are calling into other routines - do those routines take care to preserve Y?

I don’t see .Y being touched by anything, so he should be safe.

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


Top
 Profile  
Reply with quote  
PostPosted: Mon Apr 17, 2023 9:00 am 
Offline
User avatar

Joined: Fri Aug 03, 2018 8:52 am
Posts: 746
Location: Germany
One way I would try to debug this is by having the get_chr function print out the contents of ser_cnt when called, or manually call your wr_buf function and afterwards check it everything got set correctly, then calling get_chr afterwards and again checking memory through prints.

Another alternative would be using a simulator and running your code on that to see what it's doing.

Besides that, I'm not a fan of the inconsistent capitalization on instructions... I recommend sticking to one style, randomly switching between fully uppercase or fully lowercase isn't great for readability.
On a site note, are you using an NMOS 6502? Because your code uses TXA PHA instead of just PHX.


Top
 Profile  
Reply with quote  
PostPosted: Mon Apr 17, 2023 2:16 pm 
Offline

Joined: Mon Apr 17, 2023 1:12 am
Posts: 3
Thanks for the help everyone!
Quote:
Code:
——————>  bcc     IRQ_cleanup <——————

So if I have the count - and subtract the high water mark - Carry set means that the number is smaller than the high water mark - so I can skip turning off the RTS line. So it would seem that I'm constantly turning off the flow and turning it back on in the read routine. I will give BCS a try.

Quote:
BTW, it isn’t necessary or desirable to maintain a queue counter

ok, that makes sense - I can factor that out. I'm just going to write it down though, to get my logic clear. Taking WR_ptr - RD_ptr
Case 1: WR_ptr = $80, RD_ptr = $60 = $20 used in the buffer, CMP #$E0 results in carry being set
Case 2: WR_ptr = $80, RD_ptr = $90 = $F0 used in the buffer, CMP #$E0 results in carry being clear
Case 3: WR_ptr = $10, RD_ptr = $F0 = $20 used in the buffer, CMP #$E0 results in carry being set
Case 4: WR_ptr = $10, RD_ptr = $20 = $F0 used in the buffer, CMP #$E0 results in carry being clear
It is alright to ignore the status of the carry flag before these calculations since it would at most affect the results by 1, but I don't need absolute accuracy?

Quote:
One other thing...subroutines are best avoided in ISRs.

Yes, I can inline that without any difficulty.

Quote:
Lastly, do you have the 6850’s DCD permanently tied low?

Yes.


Quote:
On a site note, are you using an NMOS 6502? Because your code uses TXA PHA instead of just PHX.

Yes, I'm using a NMOS 6502.

Quote:
Besides that, I'm not a fan of the inconsistent capitalization on instructions

Agree - the formatting on this code is poor. I mostly cobbled this together from various sources, and I've tried to differentiate between parts that I've touched through changing the case to lower, but its making it terribly unreadable. I'm hoping to clean it up once I get it working.

Many thanks again,
Tom.


Top
 Profile  
Reply with quote  
PostPosted: Tue Apr 18, 2023 12:10 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8182
Location: Midwestern USA
tlovie wrote:
Thanks for the help everyone!

Before you start going nuts changing code, here is some reading material for you. Please read pages 19 to 23, inclusive, for some fundamentals on circular queue management.

Attachment:
28l91_driving.pdf [573.32 KiB]
Downloaded 29 times

Also, here is an excerpt from my POC V1.3 unit’s firmware, which has four TIA-232 channels, interrupt-driven in both directions. This code is the receiver ISR for one channel:

Code:
;28L92 UART RECEIVER INTERRUPT PROCESSING EXCERPT
;
.0000020 lda (siosr,x)         ;get receiver status <-—-—-—-—-—-—-—-—-—-—-—<—-—-—-—-—-—-—-—-—-—-—+
         bit #nxprxdr          ;RHR empty?              <—— checking for incoming datum          |
         beq .0000030          ;yes, done with channel >-—-—-—-—-—-—-—-—-—-—-—>—-—-—-—-—-—-—-—+  |
;                                                                                             |  |     
         lda (siofif,x)        ;no, get datum from RHR                                        |  |
         xba                   ;protect it for now      <—— 65C816-specific instruction       |  |
         lda sioputrx,x        ;get queue ‘put’ pointer                                       |  |
         inc                   ;bump it                                                       |  |
         cmp siogetrx,x        ;any room in queue?                                            |  |
         beq .0000020          ;no, discard datum...                                          |  |
;                                                                                             V  ^     
;        —-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—                  |  |
;        Discarding the datum would only occur if CTS/RTS handshaking wasn’t                  |  |
;        working & the remote station continued sending after RTS had been                    |  |
;        de-asserted.                                                                         |  |
;        —-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—                  |  |
;                                                                                             |  |
         xba                   ;store datum...          <—— 65C816-specific instruction       |  |
         sta (sioputrx,x)      ;in queue                                                      |  |
         inc sioputrx,x        ;adjust ‘put’ pointer &...                                     |  |
         bra .0000020          ;process next datum  >-—-—-—-—-—-—-—-—-—-—-—>—-—-—-—-—-—-—-—-—-|-—+
;                                                                                             |
;        —-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-                   |
;        Our mext step is to determine if we can continue to accept datums                    |
;        from the remote station.  If we can’t, we de-assert RTS & hope the                   |
;        remote station stops transmitting.                                                   |
;        —-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-                   |
;                                                                                             |
.0000030 sec  <-—-—-—-—-—-—-—-—-—-—-—<—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—-—<—-—-—-—-—-—-+
         lda sioputrx,x        ;compute datums...
         sbc siogetrx,x        ;in queue
         cmp #s_siohwm         ;reach queue fill level? (high-water mark, ~80% of queue size)
         bcc .0000040          ;no
;
         lda siotstab,x        ;yes, tell ourselves we’re...
         tsb siorxst           ;deasserting RTS         <—— 65C02/65C816 instruction
         bne .0000040          ;RTS already deasserted
;
         lda #nxpcrrsd         ;tell DUART to de-assert RTS...
         sta (siocr,x)         ;on this channel
;
.0000040 iny                   ;next channel

Quote:
Quote:
On a site note, are you using an NMOS 6502? Because your code uses TXA PHA instead of just PHX.
Yes, I'm using a NMOS 6502.

You might want to consider using the 65C02. It fixes the errata in the NMOS part, plus adds some new instructions and addressing modes that are very useful.

Quote:
Proxy wrote:
Besides that, I'm not a fan of the inconsistent capitalization on instructions
Agree - the formatting on this code is poor...I'm hoping to clean it up once I get it working.

That’s two of us. Capitalization inconsistency can result in hard-to-find assembly-time errors in assemblers that are case-sensitive. That, and it’s just plain hard to read. 8)

As for your code’s general appearance, the time to “clean it up” is while you are writing/editing your source and while the logic behind what you’re developing is fresh in your mind. I’d estimate at least half of my time expended in the code editor is in documenting my thoughts and methods.

Developing a program of any size in assembly language requires a certain amount of discipline. That discipline starts with clearly-written comments explaining exactly what a function is doing, what it expects as input(s) and what it will deliver as output(s). In my libraries of code that I have developed over the years, I start each function’s code in that fashion, which means I can refer to it five or 10 years later and understand what the function does and how it is to be used. Here’s an example:

Code:
;blkread: READ FROM SCSI BLOCK DEVICE
;
;   ———————————————————————————————————————————————————————————————————————
;   Synopsis: This function reads one or more blocks from a SCSI block dev-
;             ice, e.g., a disk, into a buffer.  The logical unit number is
;             assumed to be zero.
;
;             § All parameters are pointers to data.
;
;             § The target device's bus ID is a 16-bit quantity, with the
;               MSB set to $00.
;
;             § The logical block address (LBA) is a 32-bit quantity, even
;               though the target device may not require or accept a 32-bit
;               LBA.
;
;             § The number of blocks to be accessed is a 16-bit quantity,
;               with the MSB set to $00.  The maximum number of blocks that
;               may be accessed is 127 ($007F).  If this field is $0000, no
;               operation will occur & this function will immediately ret-
;               urn without an error.
;
;             § The buffer address is expressed as 32-bits, with the MSB of
;               the MSW set to $00.  The buffer must be of sufficient size
;               to hold the requested number of blocks multiplied by the
;               device's block size.  The device's block size may be det-
;               erminded with a call to the SSPARMG BIOS API to load the
;               device's enumerated features.
;
;             § If the buffer pointer is null, the buffer pointer set by a
;               previous call to the SSBUFS BIOS API is assumed to have
;               been made.  It is recommended this sequence be used if rep-
;               itive accesses are to be made to the same buffer.  Doing so
;               will avoid the overhead of a BIOS API call on each access
;               to set the buffer pointer.  Use this feature with caution!
;
;             § The target SCSI device must have been enumerated during the
;               system POST.
;   ———————————————————————————————————————————————————————————————————————
;   Invocation example: pea #buf >> 16         ;buffer pointer MSW
;                       pea #buf & $ffff       ;buffer pointer LSW
;                       pea #nblk >> 16        ;block count pointer MSW
;                       pea #nblk & $ffff      ;block count pointer LSW
;                       pea #lba >> 16         ;LBA pointer MSW
;                       pea #lba & $ffff       ;LBA pointer LSW
;                       pea #scsi_id >> 16     ;device ID pointer MSW
;                       pea #scsi_id & $ffff   ;device ID pointer LSW
;                       .IF .DEF(_SCSI_)
;                       jsl blkread
;                       .ELSE
;                       jsr blkread
;                       .ENDIF
;                       BCS ERROR
;
;   Exit registers: .A: entry value ¹
;                   .B: entry value ²
;                   .X: entry value
;                   .Y: entry value
;                   DB: entry value
;                   DP: entry value
;                   PB: entry value
;                   SR: nvmxdizc
;                       ||||||||
;                       |||||||+———> 0: okay
;                       |||||||      1: error
;                       +++++++————> entry value
;
;   Notes: 1) One of the following if an error:
;
;               e_ptrnul: null pointer passed
;               e_ssabt : transaction aborted
;               e_ssblk : too many blocks
;               e_sschk : check condition
;               e_sscfe : controller FIFO error
;               e_sscge : controller general error
;               e_sscmd : illegal controller command
;               e_ssdne : device not enumerated
;               e_ssdnp : device not responding
;               e_sssnr : SCSI subsystem not ready
;               e_sstid : device bus ID out of range
;               e_ssubp : unsupported bus phase
;               e_ssubr : unexpected bus reset
;               e_ssucg : unsupported command group
;               e_ssudf : unsupported driver function
;               e_ssudt : unsupported device type
;
;          2) $00 if an error.
;   ———————————————————————————————————————————————————————————————————————

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


Top
 Profile  
Reply with quote  
PostPosted: Tue Apr 18, 2023 3:11 am 
Offline
User avatar

Joined: Fri Aug 03, 2018 8:52 am
Posts: 746
Location: Germany
tlovie wrote:
Quote:
On a site note, are you using an NMOS 6502? Because your code uses TXA PHA instead of just PHX.

Yes, I'm using a NMOS 6502.

can i ask why?
making a new SBC from scratch, there is basically no reason to use an NMOS 6502 over the modern 65C02 unless you want to replicate some retro system.
the W65C02S is still being produced so you don't have to buy old-stock, it runs cooler, much much faster, with lower power consumption, additional instructions like BDD said, and no hardware bugs (like RDY not working with writes, or Indirect Jumps not working correctly at page boundaries, etc).


Top
 Profile  
Reply with quote  
PostPosted: Tue Apr 18, 2023 4:01 am 
Online
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8433
Location: Southern California
The many differences are compiled at http://wilsonminesco.com/NMOS-CMOSdif/ .  I realize we have a few members in countries where availability is very limited; but if you can get CMOS ones, I, too, would strongly encourage it.

_________________
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: Tue Apr 18, 2023 7:22 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10800
Location: England
It would be good if we could stick to the Interrupt Help aspect of this thread, I think.


Top
 Profile  
Reply with quote  
PostPosted: Tue Apr 18, 2023 7:54 am 
Online
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8433
Location: Southern California
BigEd wrote:
It would be good if we could stick to the Interrupt Help aspect of this thread, I think.

Although a very minor point, the above was indeed relevant, because the OP was using TXA, PHA instead of just PHX to save X at the start of the interrupt-service routine, and more things related to the ISR and NMOS versus CMOS are bound to come up as the topic develops.

_________________
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: Tue Apr 18, 2023 2:25 pm 
Offline

Joined: Mon Apr 17, 2023 1:12 am
Posts: 3
Proxy wrote:
can i ask why?
making a new SBC from scratch, there is basically no reason to use an NMOS 6502 over the modern 65C02 unless you want to replicate some retro system.
the W65C02S is still being produced so you don't have to buy old-stock, it runs cooler, much much faster, with lower power consumption, additional instructions like BDD said, and no hardware bugs (like RDY not working with writes, or Indirect Jumps not working correctly at page boundaries, etc).


The simple answer is because that is the chip that I have. I'm not making a commercial product, only just trying to learn, expand my knowledge and have fun along the way. If I have to work around some bugs, and its not the most straightforward path, and I'm missing some nice to have instructions, so be it. I live in an area where it would be easy to obtain a shiny new set of chips in a fresh anti-static bag probably overnight, but then my NMOS 6502 would feel sad. It just begs the question: why do retro computing at all, if there are newer, more modern alternatives available. I want my retro computing experience to be like what the early personal computing pioneers had. Perhaps I will use a 65C02 in my next project, but for now I don't see the point of buying new hardware for the sake of convenience.

I'm grateful for everyone's help so far on this thread, in fact, I'm surprised that it attracted any attention at all. At this point I've been provided with some excellent resources, that will take some time to read, understand and experiment with.

Tom.


Top
 Profile  
Reply with quote  
PostPosted: Tue Apr 18, 2023 3:35 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1929
Location: Sacramento, CA, USA
I can't be sure, but I get a sneaky feeling that an intermittent glitch or race condition could be lurking in your ISR from checking the ACIA before checking for BRK. I don't have any solid evidence to support this feeling, but I can't easily dismiss it.

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Tue Apr 18, 2023 8:03 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8182
Location: Midwestern USA
barrym95838 wrote:
I can't be sure, but I get a sneaky feeling that an intermittent glitch or race condition could be lurking in your ISR from checking the ACIA before checking for BRK. I don't have any solid evidence to support this feeling, but I can't easily dismiss it.

An astute observation, sir. :D

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


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 14 posts ] 

All times are UTC


Who is online

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