Jump to content
IGNORED

Interrupt Service Routine and the RS232


Recommended Posts

Something else to consider With the WiModem232 and some other WiFi modems, they have configurable buffers for the Telnet connections that can be as little as 64 bytes to over 1K.

 

With MyTerm, I have at present a 9K buffer to capture characters. There is no data overrun at 38.4K when communicating with HeatWave or Fusion BBS. However, I do have one spot within 9640News when someone does a new file scan with the over 200 areas, there is an excess of a 16K data stream transferred faster than the present screen coding can keep up with. So, this results in a buffer overrun. The characters are initially captured, but then overwritten. The user actually is never aware of it, however if I were trying to capture a 16K text document into the log buffer, then I would l miss some things.

 

My point here is that depending upon your timing and where you are in your code before looking at serial port, you could have a significant amount of data buffered ready to send from a WiFi modem to the RS232 with a significant sustained burst at whatever your WiFi modem to RS232 baud rate is configured. If you have the room for significant buffers, you might consider using it.

 

Beery

Edited by BeeryMiller
Link to comment
Share on other sites

This of course improves if use a slower baud rate because you have more time between characters.

 

Morning coffee talking

Yes, at a slower baud rate you always have more time between each character being completely received. But you don't necessarily have more time between the characters, i.e. from the last bit in character n to the first bit in character n+1. You can receive 20 characters per second at 38400 bits/s. You just have comparatively long time between the last bit of character n and the first in character n+1.

 

Of course, normally you want to be as efficient as possible, so you try to throw out the characters as soon as possible. Thus the delay is usually held short.

I just wanted to point out that there could be another delay here. If you design both ends of the communicating system, you do have the ability to use a high baud rate (provided the cable is short enough and the UART can handle it), and then make sure the transmitting end doesn't send the new character immediately after the previous one. This can render a higher speed than going down in baudrate and send the characters packed as much as possible.

 

I hope the coffee was good!

  • Like 3
Link to comment
Share on other sites

 

282uS / 286uS -> 98% of CPU used by RS232 interrupt with 47 instructions in fast RAM.

 

This of course improves if use a slower baud rate because you have more time between characters.

 

Morning coffee talking

This seems pretty close to what I've observed; adding a few extra instructions to deal with the nanopeb resulted in dropped characters in some of my earlier routines. I had to really tighten up the interrupt handler by setting some immediate values via self-modifying code among other things. I never tried computing the instruction load inclusive of the ISR - might be a fun exercise for a rainy day :)

  • Like 2
Link to comment
Share on other sites

This seems pretty close to what I've observed; adding a few extra instructions to deal with the nanopeb resulted in dropped characters in some of my earlier routines. I had to really tighten up the interrupt handler by setting some immediate values via self-modifying code among other things. I never tried computing the instruction load inclusive of the ISR - might be a fun exercise for a rainy day :)

 

Cool. This is why I am curious about what would happen if you changed your GET2 routine from 11 instructions to 7 below. That should reduce overhead a bit.

And I think you could remove the sub-routine call (?) and lose 2 more instructions.

 

I have not got the interrupt working yet under Forth or I would measure it. Not sure what's wrong yet :?

Q      BSS  100               buffer address. size is power of 2
  
GET2   MOV  @RLAST,R4         get current queue index
       STCR Q@(R4),8          get character from RS232 (R12 set @manip3)
       SBO  >12
       INC  R4
       ANDI R4,00FF           mask is 1 less than buffer size
       MOV  R4,@RLAST         save next buffer INDEX
       CLR  R12
       RTWP
Link to comment
Share on other sites

 

 

Cool. This is why I am curious about what would happen if you changed your GET2 routine from 11 instructions to 7 below. That should reduce overhead a bit.

And I think you could remove the sub-routine call (?) and lose 2 more instructions.

 

I have not got the interrupt working yet under Forth or I would measure it. Not sure what's wrong yet :?

You are setting up the interrupt handler first, right? That is an important step and one I can't predict will/won't work in the Forth environment. Be sure Forth isn't resetting any of the initial setup. Maybe post all the current flow after you've tried some things.

 

When I was testing my code initially, I changed the screen color when the interrupt fired. I was able to visually tell whether I was in the interrupt handler, the setup, or the buffering routine. Later I started using counters in the routines so that I could figure out if the routine was called as expected.

 

If you allow the buffer to overwrite in a terminal situation, you can lose more than the character that caused the overflow when the pointer wraps. So I opt to just let the tail end suffer. I haven't touched the code in a while for various reasons but hope to get back into it by summertime.

 

Looks like you are having fun ;)

  • Like 1
Link to comment
Share on other sites

You are setting up the interrupt handler first, right? That is an important step and one I can't predict will/won't work in the Forth environment. Be sure Forth isn't resetting any of the initial setup. Maybe post all the current flow after you've tried some things.

 

When I was testing my code initially, I changed the screen color when the interrupt fired. I was able to visually tell whether I was in the interrupt handler, the setup, or the buffering routine. Later I started using counters in the routines so that I could figure out if the routine was called as expected.

 

If you allow the buffer to overwrite in a terminal situation, you can lose more than the character that caused the overflow when the pointer wraps. So I opt to just let the tail end suffer. I haven't touched the code in a while for various reasons but hope to get back into it by summertime.

 

Looks like you are having fun ;)

 

Thanks.

 

Forth doesn't do anything while sitting in the console loop. It's pretty harmless while waiting.

 

Yes I have broken out your routines so I can execute them from the console. ;)

So RCVON, RCVOFF and INSTALL (what you called CONFIG) are commands in Forth.

 

I already had a 9902 setup routine so I just modified it to reset the circular pointers and setup a variable with the queue buffer address.

 

I like the idea of the screen color changes! Simple to code, little overhead and you can't miss it.

 

For the curious here was I took from your code, ( for the actual ISR handler) re-written in structured Forth assembler.

(the 'IF', ELSE and ENDIF just translate to jump instructions)

 

Edit: Code below now works. Had to grab the char and clear 9902 rcv buffer ASAP, just like Insane multi-tasker version.

DECIMAL
CREATE TTY1-ISR ( * this is a label, not a runnable Forth word * )
       ISRWKSP LWPI,
       R12 CLR,          \ select 9901 chip CRU address
       2 SBZ,            \ Disable VDP int prioritization
       R11 SETO,         \ 3.5.16 hinder screen timeout (should never happen)
       PORT @@ R12 MOV,  \ set CRU PORT
       16 TB,            \ interrupt received?
       EQ IF,            \ Yes; enqueue char
            R3 8 STCR,   \ read the char. into R3
            18 SBO,            \ clr rcv buffer, enable interrupts
            QTAIL @@ R4 MOV,   \ index->R4
            R3 Q R4 () MOVB,   \ "MOV R3,Q@(R4)"
            R4 INC,            \ bump the index
            R4 >00FF ANDI,     \ wrap the index
            R4 QTAIL @@ MOV,   \ save the index
            R12 CLR,
            RTWP,
       ENDIF,
       R12 CLR,        \ select 9901 chip CRU address
       3 TB,           \ check timer interrupt
       EQ IF,
            3 SBO,     \ reset timer latch int (essentially ignore it)
       ENDIF,
       RTWP,           \ Nothing to do here. Return.

Edited by TheBF
Link to comment
Share on other sites

I _really_ need some help getting some RS232 routines that I can use from C. *beg*

 

-Thom

 

I feel your pain. I am trying to get receive interrupts working under Forth.

 

I am taking the approach that interrupts are only really needed for receive.

Does that work for your needs?

 

I don't have GCC installed, but from what I see on GITHUB, Tursi wrote the same kind of thing that I did for NON-interrupt driven routines.

Link to comment
Share on other sites

I finally got this working. I was trying to be a little too creative. :-)

 

But! I did manage to reduce the size of the "GET2" part of the routine that reads a character and stuffs it into the queue.

 

Changes:

  1. I used indexed addressing and binary wrapping for the byte-queue pointers.
  2. I removed the over-run jump to RSINX by just letting the tail over-run the head. It's up to me to get the data out fast enough or use a bigger queue.
  3. I don't count the bytes in the ISR. I can get that in the program by subtracting the queue pointers if I need it . ABS(TAIL-HEAD)

The queue memory ("Q") itself is an address that is set when you INIT eveyrthing.

 

These changes should work in Insanemultitasker's code as well.

 

Original "GET2" routine: (12 Instructions)

GET2   STCR R3,8              get character from RS232 (R12 set @manip3)
       SBO  >12
       C    @RBYTES,@BMAX     buffer at max?
       JEQ  RSINX             yes, trash the rcvd character. 
       MOV  @RLAST,R4         get current ^loc pointer
       CI   R4,BEND           at end?
       JL   ADDROK            no
       LI   R4,BSTART         yes, wrap
ADDROK MOVB R3,*R4+           stuff char into buffer
       MOV  R4,@RLAST         save next buffer loc
       INC  @RBYTES           inc total bytes in buffer
       CLR  R12
*      NOP               debug
       RTWP
RSINX  SBO  >12          Just to be safe
       CLR  R12          Reset for re-entry (probably not needed any more)
       RTWP

"New and Improved" GET2 (8 instructions and no jumps so it should be a bit faster)

 

Forth assembler format with a translation comment for indexed addressing

        R3 8 STCR,         \ read the char. into R3
        18 SBO,            \ clr rcv buffer, enable interrupts
        QTAIL @@ R4 MOV,   \ index->R4
        R3 Q R4 () MOVB,   \ "MOVB R3,Q@(R4)"
        R4 INC,            \ bump the index
        R4 >00FF ANDI,     \ wrap the index
        R4 QTAIL @@ MOV,   \ save the index
        R12 CLR,
        RTWP,
  • Like 2
Link to comment
Share on other sites

Here is the complete ISR Handler and I made it a little smaller by setting R4 to the tail pointer before testing the CRU bit.

That way I can read the byte from CRU directly to the Q@(R4). Saves 1 move to R3.

DECIMAL
CREATE TTY1-ISR ( * this is a label, not a runnable Forth word * )
       ISRWKSP LWPI,
       R12 CLR,          \ select 9901 chip CRU address
       2 SBZ,            \ Disable VDP int prioritization
       R11 SETO,         \ 3.5.16 hinder screen timeout (should never happen)
       PORT @@ R12 MOV,  \ set CRU PORT
       QTAIL @@ R4 MOV,   \ index->R4
       16 TB,            \ interrupt received?
       EQ IF,            \ Yes; enqueue char
            Q R4 () 8 STCR,
            18 SBO,            \ clr rcv buffer, enable interrupts
            R4 INC,            \ bump the index
            R4 >00FF ANDI,     \ wrap the index
            R4 QTAIL @@ MOV,   \ save the index
            R12 CLR,
            RTWP,
       ENDIF,
       R12 CLR,        \ select 9901 chip CRU address
       3 TB,           \ check timer interrupt
       EQ IF,
            3 SBO,     \ reset timer latch int (essentially ignore it)
       ENDIF,
       RTWP,           \ Nothing to do here. Return.

Link to comment
Share on other sites

Nice work. May I ask you to "translate" the enhanced routine for the Forth-deficient? ;)

 

Remember that if you want this code to work with the nano devices, you'll either need to turn the nano serial port on/off in the interrupt handler or you can leave the card enabled so long as you track every possible instance where a DSRLNK could be called or some other device in the system could be turned on in the same address space @ 0x4000.

 

Also, if you want to use the TI disk controller (or nano/cf7/CorComp variants) you must remember to restore GPLWS R15 to the VDPWA address. In TI MXT I handle this by saving R15 within the DSRLNK routine, setting R15 to the expected VDPWA address, then restoring the mungled R15 before exiting the DSRLNK routine. The save step happens at the start of the CRU scan. While an interrupt is extremely unlikely to happen at this point in the DSRLNK, I play it safe with a LIMI 0. See code.

 

 

 

 

* Mass Transfer DSRLNK
*
* 3.6.2015  Added R15 save to ensure DSRs have the proper value for VDPWA (8c02)
*
SCLEN  EQU  >8354
SCNAME EQU  >8356
CRULST EQU  >83D0
SADDR  EQU  >83D2
R15SAV DATA 0            3.6.2016 preserve GPLWS R15 for interrupt purposes
FLGPTR DATA 0
SVGPRT DATA 0
SAVCRU DATA 0
SAVENT DATA 0
SAVLEN DATA 0
SAVPAB DATA 0
SAVVER DATA 0
NAMBUF DATA 0,0,0,0,0
DLNKWS DATA 0,0,0,0,0
TYPES  DATA 0,0,0,0,0,0,0,0,0,0,0
C100   DATA 100
H20    EQU  $
H2000  DATA >2000
DECMAL TEXT '.'
HAA    BYTE >AA
CYC1   DATA 0           *MY STORAGE AREA
H1300  DATA >1300
DSRLNK DATA DLNKWS,DLENTR
DLENTR MOV  *R14+,R5
       SZCB @H20,R15
       MOV  @SCNAME,R0
       MOV  R0,R9
       AI   R9,-8
       BLWP @VSBR
       MOVB R1,R3
       SRL  R3,8
       SETO R4
       LI   R2,NAMBUF
LNK$LP INC  R0
       INC  R4
       CI   R4,7
       JGT  LNKERR
       C    R4,R3
       JEQ  LNK$LN
       BLWP @VSBR
       MOVB R1,*R2+
       CB   R1,@DECMAL
       JNE  LNK$LP
LNK$LN MOV  R4,R4
       JEQ  LNKERR
*LNK$LP INC  R0
*      INC  R4
*      C    R4,R3
*      JEQ  LNK$LN
*      BLWP @VSBR
*      MOVB R1,*R2+
*      CB   R1,@DECMAL
*      JNE  LNK$LP
*LNK$LN MOV  R4,R4
*      JEQ  LNKERR
*      CI   R4,7
*      JGT  LNKERR
       CLR  @CRULST
       MOV  R4,@SCLEN
       MOV  R4,@SAVLEN
       INC  R4
       A    R4,@SCNAME
       MOV  @SCNAME,@SAVPAB

SROM   LWPI GPLWS
       LIMI 0
       MOV  R15,@R15SAV       3.6.2016; Must restore R15 for any DSRs
       LI   R15,>8C02         dependent upon it for VDP operations
       CLR  R1
       MOV  @H2000,@CYC1  *SET UPPER CRU BOUNDARY TO >2000
       LI   R12,>1100   *CHANGED FROM >0F00
       JMP  NOROM
SROM1  LI   R12,>0F00
       MOV  @H1300,@CYC1
NOROM  MOV  R12,R12
       JEQ  NOOFF
       SBZ  0
NOOFF  AI   R12,>0100
       CLR  @CRULST
       CI   R12,>2000
       JEQ  SROM1
       C    R12,@CYC1
       JEQ  NODSR
       MOV  R12,@CRULST
       SBO  0
       LI   R2,>4000
       CB   *R2,@HAA
       JNE  NOROM
       A    @TYPES,R2
       JMP  SGO2
SGO    MOV  @SADDR,R2
       SBO  0
SGO2   MOV  *R2,R2
       JEQ  NOROM
       MOV  R2,@SADDR
       INCT R2
       MOV  *R2+,R9
       MOVB @SCLEN+1,R5
       JEQ  NAME2
       CB   R5,*R2+
       JNE  SGO
       SRL  R5,8
       LI   R6,NAMBUF
NAME1  CB   *R6+,*R2+
       JNE  SGO
       DEC  R5
       JNE  NAME1
NAME2  INC  R1
       MOV  R1,@SAVVER
       MOV  R9,@SAVENT
       MOV  R12,@SAVCRU
       BL   *R9               R15 *must* contain VDP address
       JMP  SGO
       SBZ  0
       MOV  @R15SAV,R15       3.6.2016
       LWPI DLNKWS
       MOV  R9,R0
       BLWP @VSBR
       SRL  R1,13
       JNE  IOERR
       RTWP
NODSR  MOV  @R15SAV,R15       3.6.2016
       LWPI DLNKWS
* NODSR  LWPI DLNKWS
LNKERR CLR  R1
IOERR  SWPB R1
       MOVB R1,*R13
       SOCB @H20,R15
       RTWP
* eof

 

 

Link to comment
Share on other sites

I feel your pain. I am trying to get receive interrupts working under Forth.

 

I am taking the approach that interrupts are only really needed for receive.

Does that work for your needs?

 

I don't have GCC installed, but from what I see on GITHUB, Tursi wrote the same kind of thing that I did for NON-interrupt driven routines.

Yeah, I wrote that for Thom, but you know, he wants things that actually WORK for Plato... ;)

 

I was always under the impression that the overhead for RS232 interrupts wasn't worth the effort - how are you finding it works in practice? What are the caveats?

  • Like 1
Link to comment
Share on other sites

No surprises IMHO. I don't require sending interrupts and I think they are overrated, since that is under the sender's control.

 

My application is sending to the Forth compiler over serial link.

Without interrupts at 9600 BPS the protocol is: delay each char 1 mS and delay each line 275 mS (waiting to be sure the line compiled.)

Effectively <1K BPS :-(

 

With interrupts I can send a line at full speed and then wait for compile still remains at 275mS.

So this is an big improvement in this narrow application.

 

I have only been testing the ISR to date, but will re-build the Forth Kernel with ISR driven receive. It will then be a pretty functional way to code. I suspect compile times to be about the same as floppy disk with serial rcv interrupts. It's painfully slow without them.

 

I have not fully explored the caveats. I don't fully grok the ramifications of all the VDP interrupt shenanigans in the code, but that would be the thing I would be wary of.

 

I suppose the one thing I notice is that people want to push the old 9900 to 38.4K BPS with huge buffers.

This will have the effect of consuming the entire CPU which kind of negates the reason to have interrupts. (but its still amazing to see it work :) )

Link to comment
Share on other sites

I just ran the code below, which removes the TB 3 and the JNE at the end and it seems to work fine.

 

Is there some reason we should only set the timer latch if it is = 0?

QSIZE EQU >100        * make this size a power of 2 (100,200,400 etc.)
QMASK EQU QSIZE-1

Q     BSS  QSIZE
QHEAD DATA 0
QTAIL DATA 0
PORT  DATA >1340      * this can be changed by a Forth program

TTY1-ISR                  
      LWPI  >83C0 
      CLR   R12           \ select 9901 chip CRU address
      SBZ   2             \ Disable VDP int prioritization
      SETO  R11           \ 3.5.16 hinder screen timeout (should never happen)
      MOV   @PORT,R12     \ set CRU PORT
      MOV   @QTAIL,R4     \ index->R4
      TB    16            \ interrupt received?
      JNE   ENDIF         \ Yes; enqueue char
      STCR  Q@(R4),8 
      SBO   18            \ clr rcv buffer, enable interrupts
      INC   R4            \ bump the index
      ANDI  R4,QMASK      \ wrap the index
      MOV   R4,@QTAIL     \ save the index
      CLR   R12
      RTWP
ENDIF CLR   R12           \ select 9901 chip CRU address
      SBO   3             \ reset timer latch int (essentially ignore it)
      RTWP                \ Nothing to do here. Return.
Edited by TheBF
Link to comment
Share on other sites

 

I just ran the code below, which removes the TB 3 and the JNE at the end and it seems to work fine.

 

Is there some reason we should only set the timer latch if it is = 0?

If you are not using/enabling the 9901 timer you can ignore it. Just like you can technically ignore any other interrupts if you know you won't be generating any. I think Jeff may have been done this for completeness and to show how to do it. At least one of his programs used the timer for other purposes. I intended to use the timer for file transfer and keyboard input testing, coupled with an in-interrupt routine to capture characters.

 

Hmm... if you don't use any other interrupts you could assume with high confidence that the only time the interrupt routine is called is when the RS232 generates an interrupt. No more interrupt testing. I may have to try that for giggles. Of course, if you encounter an interrupt the handler can't manage, your program will spin in an endless interrupt loop.

 

TIMXT's keyboard routine currently tests for a serial interrupt between reading the columns to avoid losing characters during all of that keyboard CRU work. This affords some level of responsiveness while the interrupt routine and interpreter are duking it out, yet sacrificing as few clock cycles as possible waiting for input. There are probably some better methods I can employ when I get back into it.

 

19.2k is probably a better, more comfortable speed but for terminal emulation, I've always wanted to see how far things can be pushed. ;)

  • Like 2
Link to comment
Share on other sites

Although I am having trouble getting this code working inside the Forth kernel, I have continued testing and I can confirm that this minimized code works.

 

I use the 9901 timer in Forth, but it runs continuously and has the interrupt disabled. I use it for testing only to measure the actual speed of small routines.

It's great for that because it can resolve to 21.3 uS. Coupled with the Forth console you can compile a one line program that let's me truly compare which one of my latest brainstorms is faster. ;-)

QSIZE EQU >100        * make this size a power of 2 (100,200,400 etc.)
QMASK EQU QSIZE-1

Q     BSS  QSIZE
QHEAD DATA 0
QTAIL DATA 0
PORT  DATA >1340      * this can be changed by a Forth program

TTY1-ISR                  
      LWPI  >83C0 
      CLR   R12           \ select 9901 chip CRU address
      SBZ   2             \ Disable VDP int prioritization
*     SETO  R11           ** This should be done outside the interrupt **
      MOV   @PORT,R12     \ set CRU PORT
      MOV   @QTAIL,R4     \ index->R4
      TB    16            \ interrupt received?
      JNE   ENDIF         \ Yes; enqueue char
      STCR  Q@(R4),8 
      SBO   18            \ clr rcv buffer, enable interrupts
      INC   R4            \ bump the index
      ANDI  R4,QMASK      \ wrap the index
      MOV   R4,@QTAIL     \ save the index
      CLR   R12
ENDIF RTWP                \ Nothing to do here. Return.
Link to comment
Share on other sites

Well done. Certainly, 9600 seems plenty fast for the text!

 

Your movie demo reminded me of an important context point to consider when we discussing terminal speeds.

 

One of my primary reasons for extracting maximum speed within the terminal emulator environment is to balance the ANSI overhead needed for position and format coding. An ANSI terminal may interpret 5-10 bytes of data to display just on character. This loosely translates to an effective speed range of "4000-8000 baud" for the more color-intensive displays.

Link to comment
Share on other sites

Well done. Certainly, 9600 seems plenty fast for the text!

 

Your movie demo reminded me of an important context point to consider when we discussing terminal speeds.

 

One of my primary reasons for extracting maximum speed within the terminal emulator environment is to balance the ANSI overhead needed for position and format coding. An ANSI terminal may interpret 5-10 bytes of data to display just on character. This loosely translates to an effective speed range of "4000-8000 baud" for the more color-intensive displays.

 

Yes there is quite a bit of overhead for those terminals. My actual plan is to have the VDP display for graphics and games and such and replace KSCAN with the TTY connection.

It's all for my own amusement. I do want to play around with some terminal based color and such. In some ways its more versatile than the VDP display having an attribute for every character on the screen.

 

Here is my "meta language" for Forth to do basic VT100 control. It is postfix of course but it shouldn't take too much more to write the more advanced stuff using this kind of structure.

CR .( VT100 terminal control)

DECIMAL
\ type a number in base 10, with no space
: <ARG>   ( n -- )
           BASE @ >R  
           0 <#  DECIMAL #S  #> TYPE 
           R> BASE ! ;

\ markup language for terminal control codes
: <ESC>[  ( -- )   27 EMIT  91 EMIT  ;
: <UP>    ( n -- ) <ESC>[ <ARG> ." A" ;
: <DOWN>  ( n -- ) <ESC>[ <ARG> ." B" ;
: <RIGHT> ( n -- ) <ESC>[ <ARG> ." C" ;
: <BACK>  ( n -- ) <ESC>[ <ARG> ." D" ;
: <HOME>  ( -- )   <ESC>[ ." 0;0H" ;
: <CLS>   ( n -- ) <ESC>[ ." 2J" ;
: <CLRLN> ( -- )   <ESC>[ ." K" ;

\ redefine Forth words using markup words
: AT-XY   ( col row --) 
          2DUP VROW 2!
          SWAP <ESC>[ <ARG> ." ;" <ARG> ." f" ;

: TTYPAGE ( -- ) <CLS>  <HOME> ;

AND... Thanks again for your code.

It was so much help. I am not sure I would every have figured out all the pieces that needed to be circumvented to figure out RS232 interrupts on this convoluted old machine.

  • Like 2
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

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

×   Your previous content has been restored.   Clear editor

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

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...