+InsaneMultitasker Posted March 8, 2016 Share Posted March 8, 2016 For quite some time I have been mulling over ways to improve character reception in a TI terminal emulator. Most terminal emulators combine the TI interrupt service routine and the RS232's circular interrupt buffer to receive characters.During character reception, the TI ROM ISR first determines if an external interrupt has occurred. If so, it must scan the peripheral cards one at a time to find the interrupt. Once the proper RS232 port is located, it must execute the card's interrupt service routine. The RS232's interrupt routine populates the 254 byte circular buffer that resides in VDP memory.Unfortunately, this whole process requires a lot of overhead and starts to fail miserably at speeds above 4800bps. Not only is the buffer size too small, all of the processing required to scan the card, handle the interrupt, and stuff it into VDP, require too much time. TIMXT could not exceed 4800bps successfully - at 9600, the TI spent 100% of its time servicing the interrupts, dropping characters along the way. Implementing hardware flow control was an option but it meant building a special cable that many people wouldn't care to make. I went through the same scenario with my Geneve terminal emulator: PORT can sustain 38.4kKbps without a special cable; it only requires handshaking when displaying color text mode (using the V9938 graphics mode and its slow character plotting) or when transferring files with Ymodem-G. But I digress. Months later I was looking at some information on Thierry's site when I came across an interesting article. It described an ingenious method to manage external interrupts using (abusing) the ROM interrupt service routine:http://www.unige.ch/medecine/nouspikel/ti99/jeff.txtThis idea resonated with me. In PORT, I hijacked the system's interrupt vectors so that I could process external interrupts, video, and keyboard with no system overhead. This was "easy" to do with an OS in RAM. But the TI ROM is..well..ROM!I was skeptical at first; will there be much of an improvement?It took me a day or so to modify the emulator. I reserved a 4K buffer to stash incoming data. Two routines are required: an interrupt-driven RS232 capture routine and a buffer emptying routine. Think of the buffer like a bucket: one routine fills the bucket with characters; the other routine draws them out for display and other purposes. Only the active RS232 is checked and it is given immediate priority in the user ISR.Once complete I did some base testing with my Geneve. I then asked Omega to test the program at 9600bps. He reported success. He then told me he used 19.2 with success! So, I added an option for 38.4K and surprisingly, it worked! With no hardware handshaking or cable magic!There are a few considerations:1. Some peripherals rely upon GPLWS R15. Jeff's method changes this value, so disk and other peripherals may fail. My solution was to modify DSRLNK to turn off RS232 interrupts and restore R15 prior to calling the ROM routine. The environment is restored afterwards.2. All VDP-based automatic processing is inhibited. No sound, no quit key, no 50/60hz timers. You can enable the VDP interrupts and service them if you restore things to 'normal', and then re-enable the interrupt handler when complete. You cannot have both operating at the same time. I suppose a 9901-based 60hz timer may work, I'll have to try that some day.3. Keyboard scanning takes a long time. For time-sensitive RS232 input, I use a modified direct keyboard scanning routine that can be interrupted in between steps.My next challenges are to improve the keyscan routine and optimize the display interpreter. Later a uni-directional flow control may be needed especially when there is so much data being displayed so quickly. 11 Quote Link to comment Share on other sites More sharing options...
matthew180 Posted March 8, 2016 Share Posted March 8, 2016 The F18A GPU might be a good candidate for the display interpreter. Push the raw data to a specific VRAM buffer and trigger the GPU to process and display the data. Just a thought. 2 Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted March 11, 2016 Author Share Posted March 11, 2016 The F18A GPU might be a good candidate for the display interpreter. Push the raw data to a specific VRAM buffer and trigger the GPU to process and display the data. Just a thought. I might give that a shot down the road. First I want to optimize the interpreter and get it working more like a state machine. There is just something about pushing the limits of the raw hardware (where possible) that is a fun challenge. I also want to post the basic interrupt/rs232 code here so that others can see the nuts and bolts. It's a little "harder" to do that since I moved my development to the real hardware. (i.e., using the Geneve to edit/assemble and the F18A Ti for testing) Quote Link to comment Share on other sites More sharing options...
apersson850 Posted March 22, 2016 Share Posted March 22, 2016 Since my own 99/4A has RAM shadowing console ROM, I would have better abilities to do this on a real 99/4A, I presume. I can copy console ROM, change interrupt vectors and run from there, if I want to. Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted March 22, 2016 Author Share Posted March 22, 2016 Since my own 99/4A has RAM shadowing console ROM, I would have better abilities to do this on a real 99/4A, I presume. I can copy console ROM, change interrupt vectors and run from there, if I want to. Yes. That would be optimal for your case where you have RAM shadowing ROM. You could change the interrupt vector and bypass the on-board ISR entirely with one of your own design. Quote Link to comment Share on other sites More sharing options...
apersson850 Posted March 22, 2016 Share Posted March 22, 2016 I made a demonstration once, at a Programbiten user's group meeting, of two assembly processes that were running concurrently. The were running under the control of a task switching program. First it modified the console ROM, after copying it to shadow RAM, to make the TMS 9901 PSI interrupt trigger the pre-emptive task switching algorithm. Then it used the timer inside the 9901 to set the run time quota, and finally launched the two processes. Both provided output to the screen, one character at a time, so the spectators could see it alternating between A and B. Perhaps two or three of those attending the meeting actually understood what was going on. 2 Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted August 23, 2016 Author Share Posted August 23, 2016 (edited) Yesterday I finally had a chance to copy my Geneve development platter (backup!) and export the RS232 routines related to this post. There are some things I still plan to do to improve performance, such as code optimization and use of SAMS or 8K Superspace for incoming buffers. Since those future plans don't change the concept, I updated a few of the comments and am posting here for those interested. * 3.3.2016 TT * * 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. * * This approach allows the standard, unmodified TI/99-4A to capture * RS232 data at a maximum speed of 38.4Kbps. Buffer over-runs and * data loss may occur depending on calling routine and how the data * is processed. * * BL @RSINIT Set up RS232, clear buffer pointers, configure ISR * BL @RCV Extract a character from buffer * BL @RCVON turn rcv ints on * BL @RCVOFF turn rcv ints off * BL @KEYOUT transmit a character * * The DSR-based RS232 circular interrupt buffer is slow, buffers to VDP, * holds at most 254 characters, and relies upon the ROM ISR finding the * proper card to service the interrupt. Characters are dropped at * 4800bps and above. * * Using Jeff's idea, the following routines capture data to CPU RAM * on an interrupt basis. The RS232 interrupt is polled directly * during servicing based on the current active port. Buffer capture * continues while the main program is processing received data. *--------------------- * Per Jeff/Thierry: * Assumes that VDP reg 1 has bit >20 set so that the VDP interrupts * Counter-intuitive because we won't actually process these interrupts. * We must wait for the first one to occur, then never clear it. * Necessary for hack to work correctly! *-------------------- * 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!) * * Keyscan routine has been modified to allow RS232 servicing by * adding LIMI 2/0 within the routine itself. * * Many TI programs call the KSCAN and other processing routines * within a tight loop. It is beneficial in timing-sensitive * programs to limit KSCAN and other similar routines, to maximize * the time servicing interrupts. This can be done with a state * machine approach, where CPU-intensive (or interrupt disabling) * routines are only called a fraction of the time. * 3.3.16 config routine copied from Thierry's site. * 3.3.16 Initial RS232 routines implemented * 3.7.16 buffer pointers moved into scratchpad where CIB used to live * Cleaned up interrupt handler (removed extra CRU operations) * 3.18.16 Consider using rcvoff/on to shut down all 4 RS232 port interrupts * during a port configuration. * Review the need to turn off interrupts on the current card when * changing to a new RS232 port. * * Future use INTENABLE RT INTDISABLE RT *-------------------------------------------------------------------- * Init RS232,buffers,CIB. Destroys R5; R12 * Only configure one RS232 as defined by BASE and PORT * If we switch BASE or PORT, the previous RS232 will be left with * interrupts enabled. May need to track this and disable * the previous card. * RSINIT LIMI 0 inhibit ints until setup is complete MOV @BASE,R12 A @PORT,R12 MOV R12,@MANIP3+2 self-modify interrupt handler R12 to save * clock cycles within the int handler SBO >1F Reset 9902 LI R5,>0200 INIT1 DEC R5 delay JNE INIT1 SBZ >0D Bit 13, disable interval register LDCR @CREG,8 set 8n1 LDCR @BREG,>0C set baud SBO >12 Enable RCV Interrupt * LIMI 2 *may separate rsinit at later date ** RT * Reset circular buffer pointers PATISR LI R0,BSTART MOV R0,@RFIRST MOV R0,@RLAST CLR @RBYTES ** RT * Configure ROM ISR to pass through external interrupts as VDP interrupts * (Jeff Brown/Thierry) **CONFIG LIMI 0 LWPI >83E0 CLR R14 Disable cassette interrupt; protect 8379 LI R15,>877B disable VDPST reading; protect 837B * Munge INTWS LWPI >83C0 SETO R1 [83C2] Disable all VDP interrupt processing LI R2,INTRPT [83C4] set our interrupt vector SETO R11 Disable screen timeouts CLR R12 Set to 9901 CRU base SYNC TB 2 check for VDP int JEQ SYNC wait until ready SBO 1 Enable external interrupt prioritization SBZ 2 Disable VDP interrupt prioritization SBZ 3 Disable Timer interrupt prioritization LWPI WS#1 switch to the main WS LIMI 2 3.2 [rs232 ints now serviced!] RT and return ********************************************************** * Interrupt Handler * Entered from the ROM ISR via the user defined interrupt * We immediately test the configured RS232 for a received character. * MANIP3 is used during setup to eliminate the need to move the base * and port into R12, saving some precious time. Move to PAD for further * speed enhancements. * (30 bytes) INTRPT LWPI >83C0 R12 should still be clear upon entry CLR R12 but we will clear it anyway to be safe SBZ 2 Disable VDP int prioritization SETO R11 3.5.16 hinder screen timeout (should never happen) MANIP3 LI R12,>1340 3.7.2016; base/port set inline for maximum speed TB 16 rcvint? JEQ GET2 Yes; go service the rcv buffer CLR R12 No; test other possible sources: * TB 1 external int? [assume above captures them] * JNE EXTINT **dangerous; lockup w/spurious ext. int. * **be sure to turn off rs232 ints for all cards during setup TB 3 timer int? JNE TIMINT RTWP Nothing to do here. Return. TIMINT SBO 3 reset timer latch int (essentially ignore it) RTWP return *-------------------------------------------------------- * RS232 Circular Buffer character reception * Only test interrupts on active port as defined during setup * Spurious ints from another RS232 will result in virtual lockup * because they will never be serviced. * Future: optimize routine. Consider PAD RAM for this handler. * (46 bytes) MANIP2 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 *************************************************** * RCV - Extract character from circular buffer. This routine * is called from the main program to check for and process * a character from the buffer. The interrupt handler is expected * to add characters to this buffer even during 'extraction' so long * as the buffer has space. * Out: R7MSByte=character; 0=no char * * * 3.17 To allow NULL in R7, should test RBYTES and pass EQ bit to caller * * Future: Configure for (1) 8K superspace buffer (2) SAMS buffer * Verify null characters pass through * * (34 bytes) RCV LIMI 2 Force interrupts on (just in case they were off) CLR R7 FLAG to say no char to process ABS @RBYTES 3.17, can we rely upon EQ bit here? JEQ EMPTY *** LIMI 0 3.3- rcv int doesn't touch RFIRST. Allow ints. MOV @RFIRST,R0 get current pointer MOVB *R0+,R7 get character from buffer and advance pointer CI R0,BEND now at end of buffer? JNE NOWRAP no LI R0,BSTART yes, wraparound to start NOWRAP MOV R0,@RFIRST save updated buffer pointer DEC @RBYTES one fewer character in the buffer ***EMPTY LIMI 2 enable ints (3.3-not needed now) EMPTY RT BSTART EQU >3000 BEND EQU >3FF0 BMAX DATA >0FEF one less than BEND-BSTART * Buffer pointers in scratchpad RFIRST EQU >8300 Initialized during setup RLAST EQU >8302 " " RBYTES EQU >8304 " " INTFLG DATA 0 current rcv interrupt status 1=on; 0=off * Turn on the 9902 interrupt. RCVON LIMI 0 MOV @BASE,R12 A @PORT,R12 SBO >12 Enable rs232 RCV int SETO @INTFLG LIMI 2 RT * Turn off the 9902 interrupt. RCVOFF LIMI 0 MOV @BASE,R12 A @PORT,R12 i.e., >1340 SBZ >12 Disable rs232 rcv int CLR @INTFLG LIMI 2 RT **************************************** * Xmit a character from R6-MSByte * Future: timeout loop and handshaking options * KEYOUT MOV @BASE,R12 SBO 7 turn on pretty light A @PORT,R12 XMIT2 LIMI 0 SBO >10 XMIT1 TB >16 timeout loop in future... JNE XMIT1 LDCR R6,8 SBZ >10 LIMI 2 MOV @BASE,R12 SBZ 7 RT * eof Edited August 23, 2016 by InsaneMultitasker 3 Quote Link to comment Share on other sites More sharing options...
Willsy Posted August 23, 2016 Share Posted August 23, 2016 Wow! Really nice! Here's a fast keyboard scanning routine. The interesting stuff starts at the label key_scan ; ; full, stand-alone keyboard scan - not ROM/GPL dependant ; Mark Wills January 2014 ; FBFORTH EQU 0 ; set to 1 to assemble for fbForth TURBOF EQU 1 ; set to 1 to assemble for TurboForth IF FBFORTH stack equ r9 ; assembling for TI Forth next equ r15 ENDIF IF TURBOF stack equ r4 ; assembling for TurboForth next equ r12 ENDIF AORG >2000 ; note: can run from any address DEF KSCAN ; turboforth word DEF VBLANK ; set to an odd value to defeat screen blanking DEF KSTAT ; returns 0=no key. 1=new key. -1=same key DEF KEYUP ; set to non-zero to force key-up detection DEF ARDEL1 ; long delay before auto-repeat starts DEF ARDEL2 ; short delay between auto-repeats ; Note: To disable auto-repeat set ARDEL1 to -1 ; Note: For auto-repeat to work, ARDEL1 should be set to a positive ; value, ARDEL2 should be set to a positive value, and key-up ; detection should be enabled (i.e. set to a non-zero value). VBLANK bss 2 ; flag to enable/disable blanking KSTAT bss 2 ; holds KSCAN status prev_key bss 2 ; holds previous key keyup bss 2 ; flag to determine if keyup events are wanted ardel1 bss 2 ; long delay before auto-repeat starts. ardel2 bss 2 ; short delay between auto-repeats delay bss 2 ; delay 1 counter (reloaded from ardelay) KSCAN data again ; Forth CFA again bl @key_scan ; do the keyboard scan ci r0,-1 ; no key? jne cont ; if not, then continue clr @kstat ; no key. zero kstat jmp do_kstat ; update previous key and push to stack cont c r0,@prev_key ; compare keypress to previous keypress jne not_same ; jump if not the same mov @keyup,r1 ; same key. check if keyup needed? jeq no_keyup ; if not then skip mov @delay,r1 ; check if auto-repeat enabled jle again ; just scan again if not enabled dec @delay ; decrement long delay counter jne again mov @ardel2,@delay no_keyup seto @kstat ; same key. otherwise set kstat to -1 jmp do_kstat not_same mov @ardel1,@delay ; reset auto-repeat counter li r1,1 ; kstat (1=new key) mov r1,@kstat ; new keypress do_kstat mov r0,@prev_key ; update prev_key with this keypress dect r4 ; make space on stack mov r0,*stack ; push to stack mov @vblank,@>83d6 ; enable/disable video blanking IF TURBOF li next,>8326 ; restore pointer to NEXT ENDIF b *next ; exit key_scan ; get column 0 and save for later... clr r0 ; column 0 clr r6 ; byte ops li r12,>24 ; keyboard decoder address ldcr r0,3 ; select the column li r12,6 ; address of first row stcr r6,8 ; read 8 rows into r6 ; check columns 1 to 5... clr r1 ; byte operations li r0,>0100 ; starting column (column 1) li r2,8 ; index into ascii lookup table next_column li r12,>24 ; keyboard decoder address ldcr r0,3 ; select the column li r12,6 ; address of first row stcr r1,8 ; read 8 rows into r1 ci r1,>ff00 ; keys pressed? jne get_key ; jump if something was pressed ai r0,>0100 ; otherwise select next keyboard column ai r2,8 ; next column in ascii table ci r0,>0600 ; done all columns? jne next_column ; if not then do next column ; no key detected - check = space and enter (todo) li r1,>0100 ; check = czc r1,r6 ; compare with column 0 status jne check_space li r0,'=' ; load ascii code jmp check_fctn check_space sla r1,1 ; check space czc r1,r6 jne check_enter li r0,' ' ; load ascii code rt check_enter sla r1,1 czc r1,r6 ; check enter jne no_keys li r0,13 ; ascii for ENTER rt check_fctn li r1,>1000 ; key mask for fctn czc r1,r6 ; pressed? jne check_shift li r0,5 ; code for fctn/= rt check_shift sla r1,1 ; point to shift czc r1,r6 ; check jne exit_rtn li r0,'+' ; otherwise load + exit_rtn rt ; no key detected at all. push -1 for ascii code no_keys seto r0 ; load -1 rt ; a key was detected on the current row. look it up... get_key li r0,>0100 ; key mask next_key czc r0,r1 ; check key jeq found_key sla r0,1 ; otherwise adjust mask inc r2 ; adjust ascii table index jmp next_key ; repeat ; found the key. table offset is in r2 found_key li r1,tables ; point to data table pointers li r0,>1000 ; start the check with fctn found_key1 czc r0,r6 ; pressed? jeq get_table inct r1 ; point to next table sla r0,1 ; check next key ci r0,>8000 ; checked them all? jne found_key1 get_table mov *r1,r1 ; get table address a r1,r2 ; apply offset movb *r2,r0 ; get the key code srl r0,8 ; move to low byte ci r1,key_shift ; are we already pointing to the shifted table? jeq exit_rtn ; if yes then don't waste time testing a-lock ci r0,'a' ; check if in lower case range jlt exit_rtn ci r0,'z' jgt exit_rtn ; the character is in the lower case range. we need to check alpha lock clr r12 ; cru root address sbz >15 ; power up the alpha lock circuit tb 7 ; read output from circuit sbo >15 ; power down the circuit jeq exit_rtn ; alpha lock not engaged ai r0,-32 ; engaged. convert to upper case rt tables data key_fctn, key_shift, key_ctrl, key_normal key_normal byte '=',' ',13 , 0 , 0 , 0 , 0 , 0 byte '.','l','o','9','2','s','w','x' byte ',','k','i','8','3','d','e','c' byte 'm','j','u','7','4','f','r','v' byte 'n','h','y','6','5','g','t','b' byte '/',';','p','0','1','a','q','z' key_shift byte '+',' ',013,>FF,>FF,>FF,>FF,>FF byte '>','L','O','(','@','S','W','X' byte '<','K','I','*','#','D','E','C' byte 'M','J','U','&','$','F','R','V' byte 'N','H','Y','^','%','G','T','B' byte '-',':','P',')','!','A','Q','Z' key_fctn byte '=',' ',13 , 0 , 0 , 0 , 0 , 0 byte 185,194,39,15,4,8,126,10 byte 184,193,63,6,7,9,11,96 byte 195,192,95,1,2,123,91,127 byte 196,191,198,12,14,125,93,190 byte 186,189,34,188,3,124,197,92 key_ctrl byte 29,' ',13,>FF,>FF,>FF,>FF,>FF byte 27,12,15,31,24,19,23,24 byte 0,11,9,30,25,4,5,3 byte 13,10,21,29,26,6,18,22 byte 14,8,25,28,27,7,20,2 byte 27,28,16,22,23,1,17,26 end 3 Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted August 23, 2016 Author Share Posted August 23, 2016 Wow! Really nice! Here's a fast keyboard scanning routine. The interesting stuff starts at the label key_scan Thanks Willsy If you're interested I'll post the Mass Transfer/TIMXT version of the standalone keyboard scan for comparison. (I didn't write it) I've always wondered if the standalone utilities like DSRLNK, KSCAN, etc. were ripped out of the EA support, as most of them look nearly identical, or if back then the assembly programmers shared early "libraries" and source that was used commonly. Come to think of it, I don't recall where my first iterations came from. 2 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted September 16, 2016 Share Posted September 16, 2016 Even if internet wasn't available to share all human knowledge, there were still information sent around. Samples from the E/A manual were of course used first and foremost. Then somebody came up with better code, and you started using that, as soon as you knew about it. Myself, for example, started with the normal DSRLNK, as suggested in the E/A manual, then went with a better one, which could also handle cassette access within the same routine. Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted November 1, 2016 Author Share Posted November 1, 2016 Recently I got my F18A system back in working order. I was playing around with TIMXT and noticed that during the display of a 38.4K stream, an occasional character was dropped. At first I thought the keyboard routine was delaying reception, so I disabled keyboard input during the display stream. No change. Later I added a few NOPs to the RS232 interrupt handler. I wanted to understand whether or not the interrupt itself was taking too long. I discovered that just five NOPs was enough to disrupt character reception 30-40%. It seems 38.4K may be right at the threshold for what is possible. I have mitigated the problem by tightening up the interrupt code. I don't have sufficient PAD RAM space for the routine, though I might be able to shove a few VDP read/write routines there to minimize non-interrupt time within the interpreter. 3 Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted January 1, 2017 Author Share Posted January 1, 2017 TIMXT requires quite a bit of processing time to capture and interpret the incoming data stream. The system is often so busy with the data that keyboard scanning is inhibited, lest one or more characters get dropped. Up until now, I had been allowing interrupts to steal control from the keyboard routine after each column is scanned (LIMI 2/LIMI 0). This caused some debounce and response problems depending on the current input stream, mostly due to the residual ROM ISR code overhead. grrr. I tried a few different things then settled on a polled interrupt approach within the key scan routine: 1. All interrupt processing is disabled (LIMI 0). 2. RS232 interrupts are enabled. 3. The RS232 interrupt bit is polled in-line, just prior to scanning each keyboard column and during some keyboard post-processing. 4. If an RS232 character is received, it is copied into a receive buffer, and control is quickly returned to continue the key scan. Overall keyboard responsiveness has improved and at 19.2K, everything responds as it should. Now I can return to playing with the RS232 interrupt handler and the nanoPEB-induced problem, maybe even re-stabilizing 38.4K in the process. 5 Quote Link to comment Share on other sites More sharing options...
+Ksarul Posted January 1, 2017 Share Posted January 1, 2017 Very nice progress there, o Insane One! 1 Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted January 2, 2017 Author Share Posted January 2, 2017 Sometimes we forget to consider simple, obvious solutions to problems. The old key scan routine used a workspace located in the slow 32K memory space. I only just noticed this tonight; I changed the workspace to operate out of scratchpad and was "rewarded" with a faster key scan that returned throughput to 38.4Kbps. My next steps are (1) to re-enable limited keyboard scanning during display and (2) to test some SAMS buffering routines. A 4K buffer is filled VERY quickly at 38.4K. 3 Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted April 8, 2017 Author Share Posted April 8, 2017 Something old, something 'new'. 1 Quote Link to comment Share on other sites More sharing options...
Omega-TI Posted April 8, 2017 Share Posted April 8, 2017 Nice. Quote Link to comment Share on other sites More sharing options...
+TheBF Posted April 10, 2017 Share Posted April 10, 2017 (edited) Yesterday I finally had a chance to copy my Geneve development platter (backup!) and export the RS232 routines related to this post. There are some things I still plan to do to improve performance, such as code optimization and use of SAMS or 8K Superspace for incoming buffers. Since those future plans don't change the concept, I updated a few of the comments and am posting here for those interested. *-------------------------------------------------------- * RS232 Circular Buffer using binary wrap method * no jumps required BUFFER BSS >100 256 byte buffer for example MOV @RLAST,R4 get current ^loc pointer into R4 MOVB R3,*R4+ stuff char into buffer, auto inc. puts you at next location ready for next interrupt ANDI R4 >00FF wrap R4 by ANDing with a mask. MOV R4,@RLAST save next buffer loc INC @RBYTES inc total bytes in buffer etc... I did a lot of work with RS 232 interrupt handlers on the PC and I found it really sped things up to set the buffer size to a power of 2 and let the pointer wrap by ANDing it with a mask. It worked well in both Assembler and was very effective in Turbo C. I don't understand you code well, but I edited it without testing to show what I mean. BF Edited April 10, 2017 by TheBF 2 Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted April 10, 2017 Author Share Posted April 10, 2017 I did a lot of work with RS 232 interrupt handlers on the PC and I found it really sped things up to set the buffer size to a power of 2 and let the pointer wrap by ANDing it with a mask. It worked well in both Assembler and was very effective in Turbo C. I don't understand you code well, but I edited it without testing to show what I mean. BF Thank you, great idea! In the current code, the buffer pointer starts at 0x3000 and increases to 0x3ff0, then wraps back around to 0x3000. If I shift the buffer space from 0x2000 to 0x2fff, I can mask the pointer with 0x2fff without spilling into another 4k segment. (masking 0x3fff would allow the buffer to wrap back to 0x1000). Yes, I will need to give this a try. Quote Link to comment Share on other sites More sharing options...
+TheBF Posted April 11, 2017 Share Posted April 11, 2017 (edited) Also, if your Interrupt routine uses it's own workspace, you don't need to keep variables since you can use the local registers to hold the queue pointers, no? (which are just memory that the machine can get at easier anyway ie: faster variables) It would be even faster again. *-------------------------------------------------------- * RS232 Circular Buffer using binary wrap method * no jumps required * example register usage * R3 tail pointer next byte to read * R4 head pointer next available input location * R5 byte counter INTWSP BSS >20 BUFFER BSS >100 256 byte buffer for example * interrupt handler for the queueing the characters MOVB R3,*R4+ stuff char into buffer, auto inc. puts you at next location ready for next interrupt ANDI R4 >00FF wrap R4 by ANDing with a mask. INC R5 inc total bytes in buffer RTWP In some applications with a big enough buffer, you can "live on the edge" and not even deal with overflow in the ISR. It just wraps around and it's up to your reader program to keep up! :-) However that only works if the data comes in bursts. (Or the reader program hits the handshake line to stop the sending until it catches up) BF And of course reading characters out becomes faster as well with the wrap mask method. Edited April 11, 2017 by TheBF 2 Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted April 11, 2017 Author Share Posted April 11, 2017 It's a bit trickier than that, I'm afraid, but I like what you suggested! To service the RS232 interrupt more quickly, the console ROM video/scanline interrupt routine is "hijacked" to pass the RS232 external interrupts through to the user interrupt vector. The active RS232 is then polled (since we know which port we are using) for an interrupt and if found, the byte is read from the 9902 receive buffer. There is still excess overhead from the ROM routine, so to keep up with a 38.4K stream a fast workspace is required. The only memory we have that fits the bill is in scratchpad RAM from 8300-83ff. In TIMXT I am re-using/leveraging the fast interrupt workspace at 0x83C0. I haven't investigated whether or not there are any registers that go untouched that could be dedicated to head/tail/counter. Your comments lead me to consider additional options, which include sharing a register or two with the main workspace. This would result in what some might consider 'sloppy' code but it would have the benefit of ensuring the fastest interrupt handling and extraction. The wrap method would then be usable for the character extraction. Hmm. the keyboard handler would need to be modified, since I am pre-empting the interrupt routine through direct polling of the RS232; otherwise we lose characters during the time it takes to scan for a key. My current focus has been on improving the interpreter's processing speed. It is not able to process and display the incoming data quickly enough to "live on the edge". But we're getting there. And now I have some more good ideas to think about 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted April 12, 2017 Share Posted April 12, 2017 I feel your pain. LOL. You have way more patience than I do. Something to consider is that there might be enough reduction in instruction count with the wrap method and using workspace registers, that the speed-up matches the benefit of being in 8300 memory space. PAD memory is faster but running 2x as many instructions, with variables in slow ram, could end up being the same. Maybe someone with more experience could weigh in on this. It is possible to test some code fragments in Forth pretty quickly test for speed. BF Quote Link to comment Share on other sites More sharing options...
Willsy Posted April 12, 2017 Share Posted April 12, 2017 Would overlapping register workspaces help? It's a neat way to "pass" parameters to the ISR - they're already there - just in different registers! Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted April 12, 2017 Author Share Posted April 12, 2017 Hard to say, Willsy. I think changing the workspace pointer may be the best option - it is quick and simple. I'm already doing this in the DSRLNK code and keyboard routine Thing is, I didn't write the majority of the program so with retrofitting comes not breaking things. Something to consider is that there might be enough reduction in instruction count with the wrap methodand using workspace registers, that the speed-up matches the benefit of being in 8300 memory space. PAD memory is faster but running 2x as many instructions, with variables in slow ram, could end up being the same. Quite possible. For what its worth, before slimming down what little code I am using in the interrupt handler, the process was already operating in the danger zone. Adding two simple instructions for nanoPEB support caused enough of a delay to miss periodic characters. I had to make a few assumptions to eliminate instructions to get back those lost clock cycles. Your wrap idea probably would have saved me that trouble. Still, even if we eliminate the entire RS232 handler routine, there are still many instructions in ROM that we cannot eliminate. Had TI placed the interrupt vectors in RAM, this discussion would be a moot point. We could eliminate all the extraneous code and focus solely on the RS232 interrupt. Ah well, there are many "if only" laments we must overcome with the TI. 1 Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted April 16, 2017 Author Share Posted April 16, 2017 Something old, something 'new'. IMG_0487.JPG Even though I haven't done much to improve the interpreter, TIMXT now seems to be stable at 57.6K with the wired connections shown in the screenshot. Quote Link to comment Share on other sites More sharing options...
apersson850 Posted April 18, 2017 Share Posted April 18, 2017 This is of course one reason for that I, when I made my 32 K RAM internal 16-bit memory expansion also allowed it to overlap all other memory in the machine. Thus I can copy console ROM to RAM, switch to RAM and change the vectors as I like. Had TI placed the interrupt vectors in RAM, this discussion would be a moot point. We could eliminate all the extraneous code and focus solely on the RS232 interrupt. Ah well, there are many "if only" laments we must overcome with the TI. 1 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.