Jump to content
IGNORED

Assembly on the 99/4A


matthew180

Recommended Posts

49 minutes ago, TheBF said:

Order of operations:

- select the base CARD CRU address first 

-RESET bit 5 for clear to send 

-then add the displacement to get access to the UART 

- test UART bit 21 

- if character is ready then:

      - read a byte from the UART 

       - IMPORTANT!  reset bit 18 on the uart after each character is received to reset the rcv buffer in the UART

        (bit 18 is the Receive interrupt enable bit, but needs to be touched even when not using interrrupt driven receive)

- endif

- re-select the CARD CRU address

- set bit 5 to block further inputs

Not shown is the fact that R12 is already set up with the base RS232 CRU address of >1300. I am mostly following that outline, except that I did not know that CTS was active LOW. Here's what it looks like now and it still does not work...

 

        sbz     5               ;activate cts line. ready to receive
        a       @uartdis,r12    ;add uart displacement to cru base
chkdsr  tb      26              ;test rts pin. signal is inverted!
        jne     chkdsr          ;if line high then not ready
chkbuf  tb      21              ;test receive buffer
        jne     chkbuf
        stcr    r6,8            ;get byte into r6
	    sbz     18              ;reset buffer cru bit 21
        mov     @cruadr,r12     ;restore base rs232 card cru address
        sbo     5               ;inactivate cts line. not ready to receive
        swpb    r6

 

The following works on the other hand although without any flow control:

 


        a       @uartdis,r12    ;add uart displacement to cru base
chkdsr  tb      26              ;test rts pin. signal is inverted!
        jne     chkdsr          ;if line high then not ready
chkbuf  tb      21              ;test receive buffer
        jne     chkbuf
        stcr    r6,8            ;get byte into r6
	    sbz     18              ;reset buffer cru bit 21
        swpb    r6

 

Still stumped...

Link to comment
Share on other sites

52 minutes ago, Stuart said:

Also worth pointing out that SBO, SBZ and TB can take negative values. So you can leave R12 pointing at the UART and use negative values to control the CTS line. The offset is (from memory) *twice* the difference in CRU bit numbers. So if R12 = >1340, you need a SBO -25 to switch on the card LED at address >130E.

Good to remember, although it does make the code a little less clear. 

  • Like 1
Link to comment
Share on other sites

2 hours ago, Vorticon said:

I'm trying to implement a simple low level data transfer routine for the RS232 card using RTS/CTS flow control and I'm not able to receive anything. The problem appears to be with the CTS control. According to Nouspikel, CRU bit 5 of the >1300 CRU base of the first RS232 card controls the CTS1 line. In the following excerpt, I first activate CTS, get the byte, then inactivate CTS and return to the calling routine (not shown). Removing the CTS parts allows me to receive data but I can't obviously control the flow. Anyone here familiar with the RS232 serial ops at the low level?

 

sbo     5               ;activate cts line. ready to receive
        a       @uartdis,r12    ;add uart displacement to cru base
chkdsr  tb      26              ;test rts pin. signal is inverted!
        jne     chkdsr          ;if line high then not ready
chkbuf  tb      21              ;test receive buffer
        jne     chkbuf
        mov     @cruadr,r12     ;restore base rs232 card cru address
        sbz     5               ;inactivate cts line. not ready to receive
        a       @uartdis,r12    ;add uart displacement to cru base
        stcr    r6,8            ;get byte into r6
        swpb    r6
        sbz     18              ;reset buffer cru bit 21

 

Back to your original problem ... your TB 26 to test the RTS pin. That is actually testing the state or the 9902's RTS *output*. Are you trying to test the RTS pin from the other device? Don't think that is supported - you need to rely on the other device's DTR, which you typically monitor on the 9902's DSR input.

 

If you remove the TB 26 line and jump after it, does it work?

Link to comment
Share on other sites

24 minutes ago, Stuart said:

Back to your original problem ... your TB 26 to test the RTS pin. That is actually testing the state or the 9902's RTS *output*. Are you trying to test the RTS pin from the other device? Don't think that is supported - you need to rely on the other device's DTR, which you typically monitor on the 9902's DSR input.

 

If you remove the TB 26 line and jump after it, does it work?

YES! I replaced RTS with DSR (CRU 27) which is what Nouspikel does and it worked! I was indeed trying to test the RTS pin from the remote device... This is the kind of institutional knowledge that makes all the hair pulling worthwhile 😄 Thanks!

 

So question: since I am controlling flow here, does it really matter what the baud setting is?

EDIT: I'll answer my own question, and it's no. I tried it with 300 baud and 2400 baud, and it's looking like the actual transfer rate is between 110 and 300 baud. The reason it's that slow is that all the processing of the received byte is being done at high level in pcode, so for each byte a call is made to the receive routine in assembly, the byte is received and returned to the calling program, processed, and so on and so forth. This is further complicated by the fact that the pcode card needs to be turned off with each access to the RS232 card, which adds delays as well. This is the full routine for the curious.

 

        .ref   pcodeon,pcodoff,rs232on,rs232of,uartdis,procret,cruadr
        mov     r11,@procret
        bl      @pcodoff
        bl      @rs232on
        sbz     5               ;activate cts line. ready to receive
        a       @uartdis,r12    ;add uart displacement to cru base
chkdsr  tb      27              ;test dsr pin. signal is inverted!
        jne     chkdsr          ;if line high then not ready
chkbuf  tb      21              ;test receive buffer
        jne     chkbuf
        stcr    r6,8            ;get byte into r6
        sbz     18              ;reset buffer cru bit 21
	    swpb    r6
	    mov     @cruadr,r12     ;restore base rs232 card cru address
        sbo     5               ;inactivate cts line. not ready to receive
        mov     r6,*r10         ;place byte on return stack
        bl      @rs232of
        bl      @pcodeon
        mov     @procret,r11
        b       *r11

 

  • Like 3
Link to comment
Share on other sites

You aren't doing anything a "normal" RS232 card needs to be turned on to do. CRU access works anyway with most cards. So you can leave p-code on. The enable bit handles memory access only, except for with some odd cards.

 

Also don't forget that baud rate is about the bits in a character. You can send one character every second at 9600 baud.

Edited by apersson850
Link to comment
Share on other sites

1 hour ago, apersson850 said:

You aren't doing anything a "normal" RS232 card needs to be turned on to do. CRU access works anyway with most cards. So you can leave p-code on. The enable bit handles memory access only, except for with some odd cards.

Keeping the pcode card on and not turning the RS232 card does not work. Maybe the reception buffer needs an active RS232 card for access?

Link to comment
Share on other sites

3 hours ago, Vorticon said:

YES! I replaced RTS with DSR (CRU 27) which is what Nouspikel does and it worked! I was indeed trying to test the RTS pin from the remote device... This is the kind of institutional knowledge that makes all the hair pulling worthwhile 😄 Thanks!

 

So question: since I am controlling flow here, does it really matter what the baud setting is?

EDIT: I'll answer my own question, and it's no. I tried it with 300 baud and 2400 baud, and it's looking like the actual transfer rate is between 110 and 300 baud. The reason it's that slow is that all the processing of the received byte is being done at high level in pcode, so for each byte a call is made to the receive routine in assembly, the byte is received and returned to the calling program, processed, and so on and so forth. This is further complicated by the fact that the pcode card needs to be turned off with each access to the RS232 card, which adds delays as well. This is the full routine for the curious.

 

        .ref   pcodeon,pcodoff,rs232on,rs232of,uartdis,procret,cruadr
        mov     r11,@procret
        bl      @pcodoff
        bl      @rs232on
        sbz     5               ;activate cts line. ready to receive
        a       @uartdis,r12    ;add uart displacement to cru base
chkdsr  tb      27              ;test dsr pin. signal is inverted!
        jne     chkdsr          ;if line high then not ready
chkbuf  tb      21              ;test receive buffer
        jne     chkbuf
        stcr    r6,8            ;get byte into r6
        sbz     18              ;reset buffer cru bit 21
	    swpb    r6
	    mov     @cruadr,r12     ;restore base rs232 card cru address
        sbo     5               ;inactivate cts line. not ready to receive
        mov     r6,*r10         ;place byte on return stack
        bl      @rs232of
        bl      @pcodeon
        mov     @procret,r11
        b       *r11

 

Very happy you are making progress. 

From what I understand in your code you are using the DSR handshake line before receiving. 

As far as I understand it you connected you app to PC or some other device  DTR/DSR is typically used to establish that the devices are connected. 

This sentence from this web site is my understanding

https://www.commfront.com/pages/3-easy-steps-to-understand-and-control-your-rs232-devices

DTR/DSR are normally ON or OFF for the whole connection session (e.g. Off-hook), while RTS/CTS are ON or OFF for each data transmission."

 

So the  CTS/RTS handshake is used to prevent data overruns. 

If you are using RS232/1 (primary UART) the  CTS line manipulation as I showed will prevent a PC with hardware handshaking enabled, from over-running the TI-99.

Your code is telling the sender that they are "clear to send" all the time. 

Oops missed the SBZ 5 LINE in your code.  :) 

 

Since you have a slow polling program you will probably need to toggle the CTS to block the sender until you have put a character into its final home and your loop gets back to read the next character.

 

If you are using RS232/2 I could not see a separate CTS line for that UART exposed on the TI-99 card.

Maybe somebody else knows a secret I could not find. 

 

Once you have this working the better way to go will be enable RS232 on an interrupt and put the data into a queue.

  • Like 1
Link to comment
Share on other sites

25 minutes ago, TheBF said:

Very happy you are making progress. 

From what I understand in your code you are using the DSR handshake line before receiving. 

As far as I understand it you connected you app to PC or some other device  DTR/DSR is typically used to establish that the devices are connected. 

This sentence from this web site is my understanding

https://www.commfront.com/pages/3-easy-steps-to-understand-and-control-your-rs232-devices

DTR/DSR are normally ON or OFF for the whole connection session (e.g. Off-hook), while RTS/CTS are ON or OFF for each data transmission."

 

So the  CTS/RTS handshake is used to prevent data overruns. 

If you are using RS232/1 (primary UART) the  CTS line manipulation as I showed will prevent a PC with hardware handshaking enabled, from over-running the TI-99.

Your code is telling the sender that they are "clear to send" all the time.  Since you have a slow polling program you will probably need to toggle the CTS to block the sender until you have put a character into its final home and your loop gets back to read the next character.

 

If you are using RS232/2 I could not see a separate CTS line for that UART exposed on the TI-99 card.

Maybe somebody else knows a secret I could not find. 

 

Once you have this working the better way to go will be enable RS232 on an interrupt and put the data into a queue.

I do toggle the CTS line in my code: activate on entry, deactivate on exit, which is how I'm preventing the TI from being overrun as you said. Otherwise I get a ton of dropped characters.

Unfortunately the pcode system does not allow interrupts as far as I know. 

As things stand, I am now able to reliably transfer text source code from my PC to the TI under the pcode system and save it to disk. With a little bit manipulation, I should be able to do the same with raw binary files as well. That's the next step. But what I am eventually aiming at is a proper bidirectional file transfer protocol, albeit a simple one, namely XMODEM. I've studied the specs and I should be able to do it. Famous last words... 😁

This has been quite the rabbit hole!

  • Like 1
Link to comment
Share on other sites

16 minutes ago, Vorticon said:

I do toggle the CTS line in my code: activate on entry, deactivate on exit, which is how I'm preventing the TI from being overrun as you said. Otherwise I get a ton of dropped characters.

Unfortunately the pcode system does not allow interrupts as far as I know. 

As things stand, I am now able to reliably transfer text source code from my PC to the TI under the pcode system and save it to disk. With a little bit manipulation, I should be able to do the same with raw binary files as well. That's the next step. But what I am eventually aiming at is a proper bidirectional file transfer protocol, albeit a simple one, namely XMODEM. I've studied the specs and I should be able to do it. Famous last words... 😁

This has been quite the rabbit hole!

Yes I just noticed that line in your code and struck out my comment. 

Super.  This is real progress.  Congrats.

Shame about the interrupts. That is the real secret to fast serial communication. 

 

Funny thing, I have been looking at the Xmodem protocol to put on my RS232 Forth to transfer files as well.

I think I even found a version in a dialect of Forth but have not ported it.  So much code so little time... 

  • Like 2
Link to comment
Share on other sites

9 hours ago, Vorticon said:

Keeping the pcode card on and not turning the RS232 card does not work. Maybe the reception buffer needs an active RS232 card for access?

With an original TI RS232 card I tried talking to the TMS 9902 without turning on the card, and it responded. Now I didn't do a full scale communication program, but still a bit weird you can't make that work. I don't remember exactly what I read, but I think it was some kind of status word. It did change if I first sent a reset to the TMS 9902 and read it, then read it again a little later without the reset. So I was definitely getting some data from the UART. Seems pretty weird if they would gate it differently depending on a TMS 9902 status.

Anyway, I've never done any special serial communication with the p-system, as there has never been any reason to. Nothing to communicate with. So I have no knowledge from experience here. I've done out-of-the-ordinary parallel port communication under the p-system, but that's different. And then you do have to enable the card and disable the p-code card, since the parallel data buffers are in the memory map. 

Quote

Unfortunately the pcode system does not allow interrupts as far as I know. 

But it does. Even with things that weren't available when it was designed. I know that for a fact, since I made my own real time clock. When I did, I linked the RTC's interrupt pin to the system interrupt request in the box. I tried writing a DSR which will display the current time in the upper right corner of the p-system's screen, and then let the RTC interrupt every 1/10 s to do that. It works.

It can most certainly allow the RS232 card to interrupt too, but how well it works after that, I don't know. Timingwise, I mean. I haven't checked exactly what the system does with the incoming data either, if interrupt driven. One could hope you can just read them from the REMIN:, but I don't know exactly how that works.

  • Like 1
Link to comment
Share on other sites

4 hours ago, apersson850 said:

With an original TI RS232 card I tried talking to the TMS 9902 without turning on the card, and it responded. Now I didn't do a full scale communication program, but still a bit weird you can't make that work. I don't remember exactly what I read, but I think it was some kind of status word. It did change if I first sent a reset to the TMS 9902 and read it, then read it again a little later without the reset. So I was definitely getting some data from the UART. Seems pretty weird if they would gate it differently depending on a TMS 9902 status.

Anyway, I've never done any special serial communication with the p-system, as there has never been any reason to. Nothing to communicate with. So I have no knowledge from experience here. I've done out-of-the-ordinary parallel port communication under the p-system, but that's different. And then you do have to enable the card and disable the p-code card, since the parallel data buffers are in the memory map. 

But it does. Even with things that weren't available when it was designed. I know that for a fact, since I made my own real time clock. When I did, I linked the RTC's interrupt pin to the system interrupt request in the box. I tried writing a DSR which will display the current time in the upper right corner of the p-system's screen, and then let the RTC interrupt every 1/10 s to do that. It works.

It can most certainly allow the RS232 card to interrupt too, but how well it works after that, I don't know. Timingwise, I mean. I haven't checked exactly what the system does with the incoming data either, if interrupt driven. One could hope you can just read them from the REMIN:, but I don't know exactly how that works.

I am able to access the individual CRU bits without the card being turned on (I had tested that before with the card LED), but in the setting of actual serial communication it does not seem to work.

As for interrupts, I thought I had read in the manual that interrupts were not supported. Apparently I read wrong. In any case, I've never worked with interrupts before, and while I do understand the concept, there is little in the EA manual to guide the uninitiated and it's not a topic that is usually taken up by the assembly language books. There is no question about its usefulness however, so I'd love to learn. Maybe @matthew180 would like to come up with one of his wonderful tutorials on the subject? :)

  • Like 1
Link to comment
Share on other sites

I noticed before that you tested with the LED. But the LED is implemented with the same 74LS259 as is used to enable the card, so there's no way they can prevent that from responding with the card off. The first eight bits are always open.

There is a technical possibility to not enable the TMS 9902 chips unless the card has been enabled first. With that I mean CRU access to these UART's could also be gated via card enable. But it is could, nothing more. It doesn't have to be like that.

I did like you did with the LED (which didn't really prove anything more than the obvious), but I tested with the two TMS 9902 instead. I read out some status I think it was (via CRU) and got a certain value. Then I sent a reset command (via CRU) and read status again (via CRU). It had now changed. I read status once again (via CRU) and it was back to the first value. All this without turning on the card itself.

Thus I'd say it was obvious that I could read a value that was different before and right after reset. From that condition I drew the conclusion that I was actually talking to the TMS 9902, not just reading some random electric signal from space.

 

But again, I've not tried doing anything serious with this method.

  • Like 1
Link to comment
Share on other sites

1 hour ago, Vorticon said:

As for interrupts, I thought I had read in the manual that interrupts were not supported. Apparently I read wrong. In any case, I've never worked with interrupts before, and while I do understand the concept, there is little in the EA manual to guide the uninitiated and it's not a topic that is usually taken up by the assembly language books. There is no question about its usefulness however, so I'd love to learn.

 

FYI, here is the TI RS232 DSR ROM disassembled and commented by Thierry Nouspikel and register numbers converted to R values by yours truly. Note the ISR at >A4092 – >A415C:

RS232.a99

 

Also, see Thierry’s RS232 ISR discussion.

 

...lee

Edited by Lee Stewart
addendum
  • Like 2
  • Thanks 1
Link to comment
Share on other sites

34 minutes ago, Lee Stewart said:

 

FYI, here is the TI RS232 DSR ROM disassembled and commented by Thierry Nouspikel and register numbers converted to R values by yours truly. Note the ISR at >A4092 – >A415C:

RS232.a99 49.95 kB · 2 downloads

 

Also, see Thierry’s RS232 ISR discussion.

 

...lee

Thanks! Nice reference to have.

  • Like 1
Link to comment
Share on other sites

If you ever want to go down the interrupt controlled serial receive road, here is code I got from @InsaneMultitasker.

 

I adapted it to my system and there are some improvements that can be made to manage the circular queue with less instructions, which speeds up the time spent in the interrupt code.

That could be done in a version 2.

This is pretty daunting if you have never worked with interrupts before and I don't think the TI-99 designers ever imagined this way of doing things.

This is pretty clever magic IMHO. 

 

RS232-ISR.A99

  • Like 2
  • Thanks 1
Link to comment
Share on other sites

1 hour ago, TheBF said:

If you ever want to go down the interrupt controlled serial receive road, here is code I got from @InsaneMultitasker.

 

I adapted it to my system and there are some improvements that can be made to manage the circular queue with less instructions, which speeds up the time spent in the interrupt code.

That could be done in a version 2.

This is pretty daunting if you have never worked with interrupts before and I don't think the TI-99 designers ever imagined this way of doing things.

This is pretty clever magic IMHO. 

 

RS232-ISR.A99 10.65 kB · 3 downloads

Wow, 2016. Time sure files.  One thing worth mentioning - that has been discussed in various topics- is if this code is used with a nano PEB serial port, the "card" must be enabled and disabled at appropriate times. Not relevant here but sometimes people adapt code to other devices.  It is usually ok to leave the nanopeb rs232 CRU 'enabled' except when performing file IO via DSRLNK.  IIRC, your improvements have to do with managing the circular buffer BEND/BSTART pointers within a memory range that allows for the use of ANDI to "reset" the buffer position without a comparison.  Neat stuff  Edit:  @TheBF if you poke at a rev 2 some day, PM me and I'll dig up the last changes I made in TIMXT for comparison.

  • Like 2
Link to comment
Share on other sites

58 minutes ago, InsaneMultitasker said:

 @TheBF if you poke at a rev 2 some day, PM me and I'll dig up the last changes I made in TIMXT for comparison.

Thanks. I re-wrote the whole thing in Forth Assembler so it looks quite different but I will reciprocate and you can see what I did. 

The one thing that stopped me using it all the time was I got bad reactions when I tried to read the floppy drives.

My system keeps interrupts running except during VDP access so maybe that's the problem. ??

Never figured it out. 

 

  • Like 2
Link to comment
Share on other sites

4 minutes ago, TheBF said:

The one thing that stopped me using it all the time was I got bad reactions when I tried to read the floppy drives.

My system keeps interrupts running except during VDP access so maybe that's the problem. ??

It -sounds- like R15 wasn't saved/restored within the DSR routine. 

* Implementation Notes:
*      Some peripheral cards, including TI and Myarc controllers rely
*      upon GPLWS R15 for VDP access.  The DSRLNK routine has been
*      modified to save/restore R15 and disable/enable interrupts (LIMI 0/2)
*      directly before calling the DSR ROM routine. It may be necessary
*      to turn off the RS232 interrupt (or use handshaking) if a DSR
*      tries to do a LIMI 2 while servicing.  (should not happen!)

 

Here is the TIMXT DSRLNK. R15SAV is used to preserve GPLWS R15 (which is 'corrupted' by the rs232 init routine) and then it is restored in a few places.  Interrupts are disabled though I think that's just standard fare, which means somewhere, ints must be enabled after the DSR call. I'm guessing the terminal emulator's main loop does this automatically.

 

geb_dsr.txt

  • Like 1
  • Thanks 2
Link to comment
Share on other sites

This might explain it since I re-wrote DSRLiNK using more code I found by you on Atariage. :)

I don't save R15 so I will give that a try.

I disable interrupts in my wrapper that calls DSRLINK but the DSR ROM code itself if probably screwing that up. 

 

I will have to see what the real issue is. I might be able to just use CTS during the ISR and gate the sender from getting in my way. 

Thanks for all your assistance.

 

 

  • Like 1
Link to comment
Share on other sites

Although the p-system will execute the interrupt service routine on the RS232 card, I've not investigated if it really has the proper pointers at >8300 for that queue system to work. The addresses from >8300 and a bit down are occupied by the PME inner interpreter when the p-system is running.

 

Regarding the CRU enable for the TMS 9902 - I did notice that the PAL equations are published, so that can be found out. I just need to see what they imply.

Edited by apersson850
  • Like 2
Link to comment
Share on other sites

6 hours ago, TheBF said:

This might explain it since I re-wrote DSRLiNK using more code I found by you on Atariage. :)

I don't save R15 so I will give that a try.

I disable interrupts in my wrapper that calls DSRLINK but the DSR ROM code itself if probably screwing that up. 

 

I will have to see what the real issue is. I might be able to just use CTS during the ISR and gate the sender from getting in my way. 

Thanks for all your assistance.

 

 

FYI: Saving R15 did not fix the problem but I think it changed how things crash which is progress. 

There is something else I am missing.  

I will try pushing R12 before calling DSRLINK and popping R12 when it's done.

I can't understand how that could be the problem though because I in different workspaces with DSRLINK. ??

Link to comment
Share on other sites

2 hours ago, TheBF said:

I will try pushing R12 before calling DSRLINK and popping R12 when it's done.

I can't understand how that could be the problem though because I in different workspaces with DSRLINK. ??

Standard DSRLNK flips to GPLWS before the call. Whether that matters or not, I am not sure. It is forth, so maybe you need to pop before you push?  :rolling:

 

  • Haha 1
Link to comment
Share on other sites

3 hours ago, InsaneMultitasker said:

Standard DSRLNK flips to GPLWS before the call. Whether that matters or not, I am not sure. It is forth, so maybe you need to pop before you push?  :rolling:

 

It matters. There are paths in the DSR that won't work correctly if the workspace isn't GPLWS. That's why Classic99 warns about it. ;)

 

  • Like 3
Link to comment
Share on other sites

Yes, I noticed for example in the RS232 DSR for the interrupt service that they do a STWP R4, then address the queue pointer and other with the address @-offset(R4). Thus they hit the first words/bytes in scratch PAD RAM with @-224(R4) or @>FF20(R4). If the workspace isn't GPLWS at that time, then poofff...

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...