+Vorticon Posted February 29 Share Posted February 29 Thanks! I made your suggested changes. Serial communication is tricky for sure. All my testing was done with a direct connection to a laptop running Teraterm, so obviously never ran the risk of overunning the pc on send 2 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted February 29 Author Share Posted February 29 (edited) I made Pascal/assembly programs once to control a lift (or elevator if you prefer). Three floors, buttons up/down, LED indicators for next floor and floor after that destinations. Control via the PIO port. There's a picture of it in this Programbiten newsletter, page 4. Edited February 29 by apersson850 3 Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted February 29 Share Posted February 29 4 hours ago, apersson850 said: I made Pascal/assembly programs once to control a lift (or elevator if you prefer). Three floors, buttons up/down, LED indicators for next floor and floor after that destinations. Control via the PIO port. There's a picture of it in this Programbiten newsletter, page 4. Now that is super nifty. At first I thought you were controlling a real elevator with the TI! I wish I could read that article. Looks like a much younger version of yourself I love the PIO port's simplicity for hardware interfacing, even for Sunday hobbyists like myself. My favorite remains the robotic arm control program I made over a decade ago, although this one was programmed in straight assembly. However I've also done projects using XB with assembly routines support for PIO port control but it's relatively slow. Here's an example with oscilloscope graphics: Using the pcode system is much faster and far more suited for interfacing, and I'm looking forward to see what I can come up with now that I have the RS232 library done. 4 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted February 29 Author Share Posted February 29 1 minute ago, Vorticon said: At first I thought you were controlling a real elevator with the TI! Well, it was "real", but built by Lego. 4 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted February 29 Author Share Posted February 29 (edited) Perhaps it's interesting to see how I used the PIO port when controlling the elevator. In my case the port was expanded to handle 8 bits out and 8 bits in at the same time. External latches were controlled by the EXTDIR output. Spoiler ;-------------------- ; ; Support for PIO in- and output for lift control. ; ; Requires ; ; var ; outdata, ; indata :integer; ; ; procedure pioreset; external; ; procedure piostore(bit:boolean; number:integer); external; ; function pioload(number:integer): boolean; external; ; ; A-DATA 860224 ; ;-------------------- PIOCRU .EQU 1300H PCODECRU .EQU 1F00H CARD .EQU 0 INTDIR .EQU 1 LATCH .EQU 2 EXTDIR .EQU 3 LED .EQU 7 PIOPORT .EQU 5000H SP .EQU 10 MINIMEM .EQU 7000H .PROC PIORESET .PUBLIC OUTDATA .DEF PIOON,PIOOFF,SETOUT,SETIN,STOREPIO,LOADPIO MOV R11,R7 BL @PIOON BL @SETOUT CLR @OUTDATA CLR R1 BL @STOREPIO BL @PIOOFF B *R7 SAVCRU .WORD 0 PIOON MOV R12,@SAVCRU LI R12,PCODECRU SBZ CARD LI R12,PIOCRU SBO LED SBO CARD B *R11 PIOOFF SBZ CARD SBZ LED LI R12,PCODECRU SBO CARD MOV @SAVCRU,R12 B *R11 SETOUT SBZ LATCH SBZ EXTDIR SBZ INTDIR B *R11 SETIN SBZ LATCH SBO INTDIR SBO EXTDIR B *R11 STOREPIO MOVB R1,@PIOPORT SBO LATCH SBZ LATCH B *R11 LOADPIO SBO LATCH SBZ LATCH MOVB @PIOPORT,R1 B *R11 .PROC PIOSTORE,2 .REF PIOON,SETOUT,STOREPIO,PIOOFF .PUBLIC OUTDATA MOV R11,R7 BL @PIOON BL @SETOUT MOVB @OUTDATA,R1 LI R0,7 ;Calculate bit count to shift S *SP+,R0 LI R3,8000H SRC R3,0 CLR R4 MOV *SP+,R2 JEQ ISZERO INCT R4 ISZERO X @SETTABLE(R4) MOVB R1,@OUTDATA BL @STOREPIO BL @PIOOFF B *R7 SETTABLE SZC R3,R1 SOC R3,R1 .FUNC PIOLOAD,1 .REF PIOON,PIOOFF,SETIN,LOADPIO .PUBLIC INDATA MOV R11,R7 BL @PIOON BL @SETIN LI R0,7 S *SP+,R0 LI R3,8000H SRC R3,0 BL @LOADPIO MOVB R1,@INDATA CLR *SP ;Set result false CZC R3,R1 JEQ ISZERO INC *SP ;Set result true ISZERO BL @PIOOFF B *R7 .FUNC FETCH,1 MOV @MINIMEM+30,R0 A *SP+,R0 MOV *R0,*SP CLR *R0 B *R11 .END Here's then the unit that used the assembly routines above. Spoiler unit liftcontrol; (* Allows control of the lift simulation system *) interface const (* Out bits *) motor = 0; direction = 1; yellow1 = 2; yellow2 = 4; yellow3 = 6; red1 = 3; red2 = 5; red3 = 7; (* In bits *) level1 = 2; level2 = 1; level3 = 0; request1 = 5; request2 = 4; request3 = 3; destA = 6; destB = 7; procedure setbit(value:boolean; number:integer); function readbit(number:integer):boolean; procedure levelwait(var level :integer); procedure liftkey(var keynum :integer); implementation const leveladdr = 28704; liftaddr = 28708; var outdata, indata :integer; levelsem, liftsem :^semaphore; procedure pioreset; external; procedure piostore(bit,number:integer); external; function pioload(number:integer):boolean; external; procedure loadpointer(var pointer; value :integer); external; function fetch(offset :integer) :integer; external; procedure setbit; (* Gives a bit a certain value *) begin if value then piostore(1,number) else piostore(0,number); end; (* setbit *) function readbit; (* Reads any bit *) begin readbit := pioload(number); end; (* readbit *) procedure levelwait; (* Waits for passed level indicator *) begin wait(levelsem^); level := fetch(0); end; (* levelwait *) procedure liftkey; (* Waits for pressed lift key *) begin wait(liftsem^); keynum := fetch(2); end; (* liftkey *) begin (* liftcontrol *) indata := 0; pioreset; loadpointer(levelsem,leveladdr); loadpointer(liftsem,liftaddr); seminit(levelsem^,0); seminit(liftsem^,0); end. (* liftcontrol *) Edited February 29 by apersson850 3 Quote Link to comment Share on other sites More sharing options...
+FarmerPotato Posted March 1 Share Posted March 1 @TheBF In any 9902 code, it helps me to have the symbol names for CRU bits like XBRE, RTSON etc. Ive been very confused about RTS/CTS. A lot of 4A software leaves RTS asserted during a session. But I saw that T I source code only asserts RTSON before sending a character, then turns it back off. (The 9902 waits for CTS before actually transmitting, so I guess you don't have to.) Another puzzle is: why does the 4A's RS232 card implement a CTS output? I suspect the answer has to do with the 4A being wired like DCE instead of DTE. (Which is weird.) If it were acting as a DCE to another DTE, then it needs to acknowledge a RTS. That's not symmetric. The document that clarified what I should do was on T I's guide to interfacing UART chips and the "modern" PC DB9S connector. 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted March 1 Share Posted March 1 (edited) 1 hour ago, FarmerPotato said: @TheBF In any 9902 code, it helps me to have the symbol names for CRU bits like XBRE, RTSON etc. Me too but in Forth they take up space so I used a couple of the important ones and put a list of them in comments in my file. 1 hour ago, FarmerPotato said: Ive been very confused about RTS/CTS. A lot of 4A software leaves RTS asserted during a session. But I saw that T I source code only asserts RTSON before sending a character, then turns it back off. (The 9902 waits for CTS before actually transmitting, so I guess you don't have to.) I have seen a lot of variations in interpretation of how all those signals are used in real world equipment. So you confusion is well founded. It's an ancient spec from a time when data transmission was having to deal with lots of new kinds of gear. With the carrier detect signal it appears that RS232 was designed for modems. ? Some equipment I have worked with ignores all of the handshakes and relies on really fast ISRs to catch every character in a healthy buffer and process the data faster than it can be delivered. Others handshake every character. 1 hour ago, FarmerPotato said: Another puzzle is: why does the 4A's RS232 card implement a CTS output? I suspect the answer has to do with the 4A being wired like DCE instead of DTE. (Which is weird.) If it were acting as a DCE to another DTE, then it needs to acknowledge a RTS. That's not symmetric. The document that clarified what I should do was on T I's guide to interfacing UART chips and the "modern" PC DB9S connector. Hit send too soon. From what I see the 9902 doesn't have a CTS line, so it looks like TI built one on the card. ?? Edit: Nope. That's not true. I just looked at the chip pinout. So I have no idea why they did that. That is a very nice document. Thanks for sharing. Edited March 1 by TheBF fixed comment 3 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted March 1 Share Posted March 1 In case someone else finds this useful, here is an MS Word doc that I found somewhere with more info on TI-99 and RS232. 9902.doc 4 1 Quote Link to comment Share on other sites More sharing options...
RickyDean Posted March 1 Share Posted March 1 (edited) 10 hours ago, TheBF said: In case someone else finds this useful, here is an MS Word doc that I found somewhere with more info on TI-99 and RS232. 9902.doc 93 kB · 0 downloads Back in the late 90's, I had a nice book about rS232 and all the ins and outs. Can't think of the name though. I may still have it, but it'd be in my semi-trailer if I do, and I haven't been in that for a couple of years, one of the back doors is rusted at the hinges, so when I do open it I have to be ready to make a new door with plywood. Edited March 1 by RickyDean spelling 2 Quote Link to comment Share on other sites More sharing options...
+FarmerPotato Posted March 1 Share Posted March 1 10 hours ago, TheBF said: With the carrier detect signal it appears that RS232 was designed for modems. ? Yeah. DTE to DCE should be a straight-through cable. Going back to the 80s, why our RS232 card didn't have a CD input. The workaround was to wire modem CD pin 8 to RS232/2 input DTR. (I remember a classified ad in Micropendium, offering this open secret for $20.) Going back to the era, computer manuals show what was in demand: Terminals (DTE) with DB25 connected to a minicomputer port by about 50 feet of thick cable Terminals (DTE) connected to modem (DCE) transmitting to modem (DCE) connected to minicomputer Terminals (DTE) connected by current loop to minicomputer--1000 feet. Terminals (DECNet) - RS-423 differential, on 6P6S phone modular jack, straight through 6-conductor to minicomputer (it's beautiful) The minicomputer end - Texas Instruments had a 2x9-pin male connector. At VCFSW I was given a CI403, TI's 4-channel serial card for the 990. There are four, 2x9 pin, male connectors, with pin 1 used a key. It allowed a LOT of terminal ports to be crammed in. Interesting that TI did imagine 4 ports to be a normal amount for the Home Computer! The CPU card of the 990/10A has one regular DB25 for the console terminal. I have seen TI products with one or two TMS9902 to DB25 ports, while the CI403 has four 8250s. One TMS7000 board has a 6850 UART. 2 Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted March 2 Share Posted March 2 On 2/29/2024 at 8:38 PM, FarmerPotato said: @TheBF In any 9902 code, it helps me to have the symbol names for CRU bits like XBRE, RTSON etc. Ive been very confused about RTS/CTS. A lot of 4A software leaves RTS asserted during a session. But I saw that T I source code only asserts RTSON before sending a character, then turns it back off. (The 9902 waits for CTS before actually transmitting, so I guess you don't have to.) I noticed that RTS is always ON on the remote PC (Teraterm) with a null modem connection from the TI, so checking for RTS prior to receiving on the TI side is unnecessary. Is this behavior standard with null modem connections? If it is then I'll get rid of the extra code since I don't plan on using a modem for connections with the pcode system. 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted March 2 Share Posted March 2 You are safe with that decision. I don't test RTS on receive side, I simply pull on the CTS line to keep Teraterm from overrunning TI-99. It's RPN but you can see what I use that works in non-interrupt driven receive. Not sure how much difference it makes but I also disabled interrupts on the chance that a character would be missed because an interrupt stole the CPU's attention away from my more important stuff. [CC] DECIMAL [TC] CODE KEY? ( -- n ) \ "com-key" 0 LIMI, R12 RPUSH, \ save R12 on return stack *Needed?* CARD @@ R12 MOV, \ set base address of CARD UART @@ R12 ADD, \ add UART, >1300+40 = CRU address TOS PUSH, \ give us a new TOS register (R4) TOS CLR, \ erase it \ use negative CRU address to reach back to CARD base address >1300 -27 SBZ, \ CARD CTS line LOW. You are clear to send 21 TB, \ test if char ready EQ IF, TOS 8 STCR, \ read the char 18 SBZ, \ reset 9902 rcv buffer TOS SWPB, \ shift char to other byte ENDIF, -27 SBO, \ CTS line HIGH. I am busy! R12 RPOP, \ restore old R12 *Needed?* 2 LIMI, NEXT, ENDCODE 2 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted March 3 Author Share Posted March 3 I have on occasions mentioned that I did an attempt to modify the p-system to handle pre-emptive multitasking, as a complement to the already existing volountary task switching mechanism. I was semi-successful. The principle worked, but the system couldn't handle certain heap operations when multitasking was active. Dynamic variables are valuable in all cases and especially in such contexts. Fixing the heapops unit without the source wouldn't be feasible, so I stopped there. I have later realized that a different approach would have been to actually implement the attach intrinsic. As it is, it's implemented in the system but it doesn't do anything. With volountary task switching, tasks of equal priority will not hand over the CPU to another such task unless the running one is stopped in some way. If it doesn't wait for some condition, it will run forever. No other task with the same priority will ever be let in. You can accomplish pre-emptive multitasking when a semaphore can be attached to a hardware timer, by attaching a process with high priority to a semaphore that's signalled every so often. That will allow that process to execute each time the interrupt shows up, since the process has a high priority. The only thing it does is to wait for the same semaphore again, so it immediately returns to the first task in the ready queue. But if the other tasks running have the same priority, the one that's interrupted will always be put at the last position in the ready queue, so the one that has waited the longest time will run next. In systems where it works, attach is used to connect a semaphore (they are already implemented) to some kind of (hardware) event. Thus the equivalent of signal could be applied to a semaphore with a timer expires or a key is pressed. The compiler can already compile attach to the correct p-code. Since the p-code interpretation table is in RAM, it's easy to implement a new interpretation of attach. You just have to store it somewhere. Back in the 1980's I used Minimemory RAM to do that, since it's a resource the p-system doesn't normally use. Since the interrupt execution is also in RAM when the p-system runs, it's easy enough to install a wedge there too, to allow branching to your own interrupt service. That's how I did the pre-emptive multitasking, by letting the VDP interrupt run an extra sequence to check if there were processes waiting in the ready queue and then let them start. Now with a working attach, it would be possible to create system events like a key is available in the keyboard buffer, a timer has expired or a character has been received by the RS232 card. There is even a solution to the problem that the RS232 card's interrupt service assumes a pointer to the reception buffer at the start of scratch pad RAM, where the p-system has the inner interpreter. At boot time, the p-system builds a table of CRU branch addresses for all interrupt service routines found. Hence it's possible to patch this branch table to our own service routine, which would run instead of the one on the card and store the received character in a queue we define. Perhaps in Mini Memory RAM, to avoid all interference with other things in the system. This means that interrupt handling could be pretty fast (the p-system only allows interrupts between p-codes) and the result (characters in the queue) could be published to the Pascal level by a signal to a semaphore. I have made no math to compute a realistic reception rate yet. 4 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted March 3 Share Posted March 3 Could this work with fast serial communication? At 9600 bps you have a byte coming in every millisecond or so. 16mS interrupt time would not allow very fast communication or am I missing the important part somewhere? Quote Link to comment Share on other sites More sharing options...
apersson850 Posted March 3 Author Share Posted March 3 (edited) The interrupt signal has to come from the card, of course. Not the VDP. But I've not done any math about how frequently that system can respond to interrupts. The TMS 9900 doesn't execute too many instructions in a millisecond. A rough estimate is 150, if there aren't wait states. Edited March 3 by apersson850 3 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted March 3 Share Posted March 3 7 minutes ago, apersson850 said: The interrupt signal has to come from the card, of course. Not the VDP. But I've not done any math about how frequently that system can respond to interrupts. The TMS 9900 doesn't execute too many instructions in a millisecond... Ok. That make sense. My RS232 ISR executes in 314 cycles on a zero wait-state machine (105uS) from my counts from the 9900 manual. Looking at Classic99 I see about 50% overhead compared to the textbook cycles. ?? But let's say it's 2X slower. If so that would ~210uS. If we are sending at 19,200bps full speed we have characters hitting about every 500uS, so that could consume over 40% of the CPU. 2 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted March 3 Author Share Posted March 3 Did you do account for the wait-state generation in the TI 99/4A when you did your cycle math? Anyway, for top speed nothing but assembly will work. Even that's tight, as your estimates show. I was more thinking along the line of implementing an ISR that would read incoming characters and stuff them in the reception buffer. Do that, increment the buffer pointer and control the CTS pin accordingly and that's about it. Checking if there are characters available and then signal the semaphore could be done on the VDP interrupt, if one would prefer. Then I'm not sure at what rate characters could be read out from the buffer. Some experimentation would probably be required. 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted March 3 Share Posted March 3 2 hours ago, apersson850 said: Did you do account for the wait-state generation in the TI 99/4A when you did your cycle math? Anyway, for top speed nothing but assembly will work. Even that's tight, as your estimates show. I was more thinking along the line of implementing an ISR that would read incoming characters and stuff them in the reception buffer. Do that, increment the buffer pointer and control the CTS pin accordingly and that's about it. Checking if there are characters available and then signal the semaphore could be done on the VDP interrupt, if one would prefer. Then I'm not sure at what rate characters could be read out from the buffer. Some experimentation would probably be required. No I counted from the book so it's the very best case which is why I doubled the number. I don't actually know how to calculate the slow down from the the TI-99 wait state generator. I took a look at the Classic99 dis-assembler and see between 0 difference from the text book for a register in the scratch-pad to what seems like double the cycles in some cases. Yes indeed my ISR does those things and hits the CTS pin when the buffer is 1/2 full. Again the code below is in RPN assembler and Forth but I think it could be worked into the Pascal environment from what you have said about working with interrupts. There are two big pieces. The "installer" that sets up the magic to trick the console ISR and the actual ISR to collect data into the queue. It's all derived work from other people but it's working well now so when the time comes it might be useful to @Vorticon to translate it. Spoiler \ RS232/1 Interrupt Handler for CAMEL99 Forth B Fox Feb 14 2019 \ Feb 2024- make this run on CAMELTTY Forth NEEDS DUMP FROM DSK1.TOOLS \ DEBUG ONLY NEEDS MOV, FROM DSK1.ASM9900 \ ************************************************************************* \ * Adaptation of Jeff Brown / Thierry Nouspikel (sp) idea to leverage \ * the ROM-based ISR to service external interrupts (RS232 in our case) \ * within the VDP interrupt framework. \ * Based on code by Insanemultitasker ATARIAGE \ Changes: HEX 83C0 CONSTANT ISRWKSP CARD @ UART @ + CONSTANT COM1 : (R4) R4 () ; \ syntax sugar for Forth Assembler \ ************************************************************ \ simple circular Q management 40 CONSTANT QSIZE QSIZE 1- CONSTANT QMASK \ circular mask value CREATE Q ( -- addr) QSIZE CELL+ ALLOT \ Queue pointers VARIABLE QHEAD VARIABLE QTAIL : QCLEAR Q QSIZE 0 FILL QHEAD OFF QTAIL OFF ; \ ************************************************************ \ * QKEY? - Read character from 'Q' at index 'QHEAD' HEX CODE QKEY? ( -- c | 0 ) \ 0 means queue empty TOS PUSH, \ make space in the TOS cache (R4) TOS CLR, \ FLAG to say no char ready QHEAD @@ QTAIL @@ CMP, NE IF, \ head<>tail means char waiting QHEAD @@ W MOV, \ get queue head index to W Q (W) TOS MOVB, \ get char from Q -> TOS TOS SWPB, \ move to other side of register W INC, \ inc the index W QMASK ANDI, \ wrap the index W QHEAD @@ MOV, \ save the new index ELSE, \ queue is empty... CARD @@ R12 MOV, \ can't assume R12 is correct 5 SBZ, \ set -CTS line LOW to get more data ENDIF, NEXT, ENDCODE \ ************************************************************** \ * ISR is in workspace 83C0. ONLY R3 & R4 are free to use!!! DECIMAL CREATE TTY1-ISR ( *isr with hardware handshake * ) ISRWKSP LWPI, \ 10 R12 CLR, \ select 9901 chip CRU address \ 10 2 SBZ, \ Disable VDP int prioritization \ 12 R11 SETO, \ 3.5.16 hinder screen timeout \ 10 R12 COM1 LI, \ select card1+uart1 \ 12 QTAIL @@ R4 MOV, \ Queue tail pointer ->R4 \ 22 16 TB, \ interrupt received? \ 12 EQ IF, \ Yes; enqueue char \ 10 Q (R4) 8 STCR, \ read byte into Q \ 52 \ *** manage Queue pointer *** R4 INC, \ bump the index 10 R4 QMASK ANDI, \ wrap the index 14 R4 QTAIL @@ MOV, \ save index in QTAIL 22 \ *** test buffer status *** QHEAD @@ R4 SUB, \ R4 has Qtail 22 R4 ABS, \ R4 has byte count in Q 12 R4 QSIZE 2/ CI, \ 1/2 full? 14 GTE IF, \ 10 \ we can change CTS line by using a negative bit value -27 SBO, \ CTS line HIGH. I am busy! 12 ENDIF, ENDIF, 18 SBO, \ clr rcv buffer, enable interrupts 12 R12 CLR, \ select 9901 chip CRU address 10 3 SBO, \ reset timer int 12 RTWP, \ Return 14 \ 104.6 uS 314 \ ******************************************************************* \ * Configure ROM ISR to pass through external interrupts as VDP interrupts \ * (Jeff Brown/Thierry) HEX \ get address Forth's tos register (R4) so we can transfer ISR handler \ to the ISR workspace 8300 4 CELLS + CONSTANT 'TOS CODE INSTALL ( ISR_address -- ) 0 LIMI, 83E0 LWPI, \ select GPL workspace R14 CLR, \ Disable cassette interrupt; protect 8379 R15 877B LI, \ disable VDPST reading; protect 837B ISRWKSP LWPI, \ switch to ISR workspace R1 SETO, \ [83C2] Disable all VDP interrupt processing 'TOS @@ R2 MOV, \ [83C4] set our interrupt vector from Forth R4 R11 SETO, \ Disable screen timeouts R12 CLR, \ Set to 9901 CRU base BEGIN, 2 TB, \ check for VDP interrupt NE UNTIL, 1 SBO, \ Enable external interrupt prioritization 2 SBZ, \ Disable VDP interrupt prioritization 3 SBZ, \ Disable Timer interrupt prioritization 8300 LWPI, \ return to the FORTH WS TOS POP, \ refill stack cache register 2 LIMI, \ 3.2 [rs232 ints now serviced!] NEXT, \ and return to Forth ENDCODE DECIMAL CODE ISRON ( uart -- ) \ * Turn on the 9902 interrupts 0 LIMI, TOS R12 MOV, 18 SBO, \ Enable rs232 RCV int TOS POP, 2 LIMI, NEXT, ENDCODE CODE ISROFF ( uart -- ) \ * Turn off the 9902 interrupts 0 LIMI, TOS R12 MOV, \ i.e., >1340 18 SBZ, \ Disable rs232 rcv int TOS POP, 2 LIMI, NEXT, ENDCODE : ISR-I/O QCLEAR \ reset Queue pointers, erase data KEY? DROP \ clear any char from 9902 COM1 ISROFF \ just to be safe TTY1-ISR INSTALL ['] QKEY? >BODY ['] KEY? ! \ patch KEY?' to read the queue COM1 ISRON ; CR .( Intalling ISR on port TTY1 ...) ISR-I/O CR .( ISR recieve enabled) CR 2 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted March 3 Author Share Posted March 3 (edited) Yes, my multitasking thing has something similar. The code to do it under the p-system and the installer that modifies the system to actually use it. Edited March 3 by apersson850 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted March 3 Author Share Posted March 3 On 3/1/2024 at 3:38 AM, FarmerPotato said: Ive been very confused about RTS/CTS. There is a reason that getting serial communication to work was considered among the most difficult things you could take on when I started to learn about computers in the late 1970's. I have a full book that's about nothing else but connecting V.24/RS 232C. 1 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted March 3 Author Share Posted March 3 45 minutes ago, TheBF said: No I counted from the book so it's the very best case which is why I doubled the number. I don't actually know how to calculate the slow down from the the TI-99 wait state generator. In the TMS 9900 documentation you have the basic number of clock cycles given. Reading the instruction itself is included, but on the TI 99/4A, reading the instruction has four wait states unless the instruction is in the console's ROM or in the RAM PAD (>8300). You have to add that. Normally you have the registers in RAM PAD so they don't add anything. In that same table, there are address modifications of type A and B. If any of those applies, then you add the clock cycles from A and/or B to the basic number of cycles. Next you look at how many memory accesses there are associated with A and B. If these acceses go elsewhere than to ROM or RAM in the console, you have to add four wait states for each cycle there too. This is why an instruction using more advanced addressing easily can grow from 14 to 30 cycles. It's also the reason for why internal memory expansion on 16-bit wide bus can increase speed with up to 110%, if you have both code and registers in expansion RAM. 4 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted March 3 Share Posted March 3 2 hours ago, apersson850 said: In the TMS 9900 documentation you have the basic number of clock cycles given. Reading the instruction itself is included, but on the TI 99/4A, reading the instruction has four wait states unless the instruction is in the console's ROM or in the RAM PAD (>8300). You have to add that. Normally you have the registers in RAM PAD so they don't add anything. In that same table, there are address modifications of type A and B. If any of those applies, then you add the clock cycles from A and/or B to the basic number of cycles. Next you look at how many memory accesses there are associated with A and B. If these acceses go elsewhere than to ROM or RAM in the console, you have to add four wait states for each cycle there too. This is why an instruction using more advanced addressing easily can grow from 14 to 30 cycles. It's also the reason for why internal memory expansion on 16-bit wide bus can increase speed with up to 110%, if you have both code and registers in expansion RAM. Yes, I knew about the A and B address modifications so my numbers in the code reflect that. The dirty details of how bad it gets with TI-99 expansion RAM is where I stopped. However if the speedup can be 110% then using a 2X more cycles as a "rule of thumb" is reasonable I guess. Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted March 5 Share Posted March 5 I've changed the GETBYTE routine from a function to a procedure so it can detect when a file transmission has ended by checking the receive buffer and set a flag. That way the host program can tell when the transfer is complete. Seems to work well. .proc getbyte,2 ;receive a byte from the serial port ;sets flag if sender times out .ref pcodeon,pcodoff,rs232on,rs232of,uartdis,procret,cruadr mov r11,@procret bl @pcodoff bl @rs232on mov *r10+,r1 ;get flag pointer clr r3 ;clear timeout flag mov *r10+,r2 ;get the pointer to the byte variable a @uartdis,r12 ;uart base cru address li r0,30000 ;countdown timer sbz -27 ;activate cts line chkbuf dec r0 ;activate timeout timer jne wait li r3,1 ;set timeout flag if counter is 0 jmp exitsub wait tb 21 ;check if receive buffer is empty jne chkbuf stcr r6,8 ;get byte into r6 sbo -27 ;inactivate cts line sbz 18 ;reset buffer cru bit 21 swpb r6 exitsub mov r6,*r2 ;store byte mov r3,*r1 ;save timeout flag bl @rs232of bl @pcodeon mov @procret,r11 b *r11 Here's a universal file receive program based on that. It can get either a text or binary file. (* receives any file from a remote computer *) (* by walid maalouli - march 4, 2024 *) program filetran; type byte = 0..255; byteword = record case boolean of true : (value : integer); false : (bytes : packed array[1..2] of byte); end; (* byteword *) var rval, sval : byteword; flag, key, i, nbyte : integer; outfile : file of integer; fname : string; procedure setrs232(base : integer); external; procedure getbyte(var n, flag : integer); external; procedure bitset(crubit, state : integer); external; function getkey : integer; external; begin page(output); setrs232(1); (* select first rs232 card *) bitset(5, 1); (* inactivate cts line *) writeln(chr(7),'destination file:'); readln(fname); rewrite(outfile, fname); writeln(chr(10),chr(7),'initiate transfer and press a key'); repeat key := getkey until key <> 255; getbyte(rval.value, flag); nbyte := 0; i := 0; gotoxy(1,14); writeln('bytes received:'); while flag = 0 do begin (* integers always written as words, so for a file the incoming integers have to be converted to bytes and concatenated with 1 word = 2 integers *) i := succ(i); if i < 3 then begin nbyte := succ(nbyte); gotoxy(1,15); writeln(nbyte); sval.bytes[i] := rval.bytes[2]; end else begin outfile^ := sval.value; put(outfile); i := 1; sval.bytes[i] := rval.bytes[2]; nbyte := succ(nbyte); gotoxy(1, 15); writeln(nbyte); end; getbyte(rval.value, flag); end; (* this section handles the case of an odd number of bytes in a binary file *) if i = 1 then sval.bytes[2] := 0; outfile^ := sval.value; put(outfile); writeln(chr(10),'transfer done!'); close(outfile, lock); end. (* filetran *) Now there is still an issue I have not been able to figure out. The second byte of any file is always skipped, so the received file is 1 byte short. There is no reason that I can see for this to happen, at least from a software standpoint. Could there be a hardware reason for this? This is no small problem, particularly for binary files... I know I know I really should just get that XMODEM program done already which will likely solve that, but I still would like to know why this is happening... 3 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted March 5 Share Posted March 5 That's a strange bug for sure. Something I notice is that you are shuffling the data across a few registers. You could be reading more directly to the final destination. Not sure it fix the bug but it will reduce the time spent in getbyte. STCR *Rx,8 INC Rx or maybe (have not tried this but I think it's valid) STCR *R10+,8 That way the data goes into the buffer directly with auto incrementing. Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted March 5 Share Posted March 5 9 hours ago, Vorticon said: Now there is still an issue I have not been able to figure out. The second byte of any file is always skipped, so the received file is 1 byte short. There is no reason that I can see for this to happen, at least from a software standpoint. Could there be a hardware reason for this? This is no small problem, particularly for binary files... In getbyte, if the MSB of R6 is ignored, this is not a problem, but, as far as I can tell, the MSB of R6 is indeterminate unless it has been cleared before invoking the procedure. ...lee 2 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.