-
Posts
4,469 -
Joined
-
Last visited
Content Type
Profiles
Forums
Blogs
Gallery
Events
Store
Posts posted by TheBF
-
-
And then remember that an HEX integer can hold up to 4 digits, a HEX byte can be up to 2 digits.
So if you read the file as integers you need a 4 digit buffer, if as bytes a 2 byte buffer
You put the results of the characters into the buffer backwards. ie: right to left
So you need to read a byte, process 2 chars into the buffer. The leftmost chars could be a zero.
That is why I recommended the other procedure to "make a number" which will be a little buffer of characters that you can print to the screen.
-
18 minutes ago, Vorticon said:
Oops... I'm dividing by 256 instead of 16.
write(dec2hex(buffer[i] div 16)); write(dec2hex(buffer[i] mod 16));
Also I forgot to subtract 10 from the A-F conversion
if n >= 10 then dec2hex := chr(n + 65 - 10);
Still not working though... What else am I missing???
Ok one bug down.
Only the results of the mod operation , passed through the digit convertor goes into the buffer.
This name dec2hex is a bit misleading since a binary number you are processing is neither BASE 10 nor BASE 16.
This process of converting it to text, is what will determine if it decimal or hexadecimal or binary or whatever.
This is should work and it's smaller!
function todigit(n : integer) : char; begin if n > 9 then n := n + 7 todigit := chr(n + 48); end; (*todigit *)
-
52 minutes ago, jrhodes said:
Well this is just lovely... My windows 10 pc installed a update, and now I get a black screen and a mouse pointer upon logging in. I can Ctrl alt del and get a task manager.
A preliminary search online seems to suggest that I run" SFC /SCANNOW " from administrative powershell.
<sigh> 😔
My update wasn't that bad but there seems to be a bug in the file windows where they "stop responding". Not like the file system is important or anything...
- 1
-
I'm biased by you know what, but you might try factoring this into some smaller pieces.
Here are the pieces used in a Forth system to chop an integer into a set of ascii digits.
TODIGIT converts a binary digit to ascii but handles the gap between 0 .. 9 and A .. F.
Then you need a way to divide and modulo-divide an integer by the radix, in this case 16.
The quotient is retained, the modulus is passed to TODIGIT and the ascii character is placed in a buffer starting from right to left.
Lather rinse and repeat until there is nothing left in the quotient.
In very bad pseudo code: (I don't think like this any more)
function TODIGIT ( X : integer)
var out : char
IF X > 9 THEN X=X+7 { handles ABCDEF }
out := x + 48 { ascii 0 }
return out
end;
procedure MAKE_NUMBER ( input: integer )
var hp, quotient , modulus , length : integer
var hbuff() 6 bytes { should be global and big enough to hold a 16 bit number }
hp := 6 { called a hold pointer in Forth }
length:=0
repeat
quotient := input / 16 { if you made 16 a global variable you code convert to different bases }
modulus := input mod 16
hbuff(hp):=todigit(modulus) { put the ascii digit in the buffer }
hp:=hp-1 { move the pointer to the next digit location
inc(length)
until quotient=0
return hbuff() , length { length needs to global to make this easy to do in Pascal ? )
end;
Maybe that will give some helpful hints. ???
-
1 hour ago, Lee Stewart said:
Surely you intended
SRL R6,8
...lee 😄
LOL. Yup.
-
When I reviewed the thread on this program I found the results of this program compiled with the BASIC compiler.
Compiled it kicks Forth's butt!
"Maybe versus Weiand's Forth:
Compiled-Basic: 1m40s
Weiand-Forth: 3m"
But the Weiand Forth version was written by Lucien who admitted that he was new to Forth and the algorithm is completely different.
Now, with a line by line translation, we can see that indirect-threaded Forth and compiled BASIC are running about the same speed.
If we compiled my BASIC version with simpler printing it would probably take the extra 20 seconds off or maybe more.
@Lee Stewart wrote a version using Forth idiomatically at it runs on my system in 1:02 with the default scroll code.
If increase the scroll buffer to the entire screen it runs in 55 seconds, but that's wasteful.
LOL. I am moving over to the old Seven's problem post. I wonder if my JIT can swallow the new version... ?
-
I just followed the early thread and the program I posted was written by @unhuman
I will add that to the post.
Thanks for making me do my homework professor.
-
Outsider curiosity question.
Do you need any special incantations to declare a leaf sub-routine?
Perhaps the compiler simply looks for any calls within the sub-routine and modifies the entry and exit code?
-
2 minutes ago, Lee Stewart said:
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
You got those eagle eyes.
So this is similar to the case of C@ in our Forth systems where we would use:
SRA R6,8 rather than SWPB R6
The GCC team was fighting with this for a while too.
- 2
-
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.
-
Still working on cleaning up my repository and I came across the old sevens problem.
I always wanted a version that was a literal translation of the BASIC so I did one.
It's fun to look at. I did not use any tricks unless you call one DUP a trick.
Here is the TI BASIC version that runs in 27 minutes.
I am afraid I have lost the accreditation for it so speak up if it's yours. (Lucien?)Edit: Original program was written by @unhuman.
I modified lines 340 to 360 to speed up printing. The original used HCHAR.
Spoiler1 rem for comparison to Forth 2 rem TI BASIC version runs in 27mins and 20 seconds 10 OPEN #1:"CLOCK" 20 INPUT #1:A$,DATE$,START$ 100 DIM A(256) 110 PRINT "7's Problem" 120 A(1)=7 130 WIN=0 140 POWER=1 150 NUMLEN=1 160 POWER=POWER+1 170 PRINT "7 ^";POWER;"IS:" 180 CARRY=0 190 INAROW=0 200 FOR I=1 TO NUMLEN 210 A(I)=A(I)*7+CARRY 220 CARRY=INT(A(I)/10) 230 A(I)=A(I)-CARRY*10 240 IF A(I)<>7 THEN 290 250 INAROW=INAROW+1 260 IF INAROW<>6 THEN 300 270 WIN=1 280 GOTO 300 290 INAROW=0 300 NEXT I 310 A(I)=CARRY 320 IF CARRY=0 THEN 340 330 NUMLEN=NUMLEN+1 340 FOR I=NUMLEN TO 1 STEP -1 350 PRINT CHR$(A(I)+48); 360 NEXT I 363 REM we're done get the time 365 INPUT #1:A$,DATE$,END$ 370 PRINT :: 380 IF WIN<>1 THEN 160 390 PRINT "WINNER IS 7 ^";POWER 394 Print "Sevens started:";START$ 400 PRINT "Ended at ";END$ 410 CLOSE #1 420 END
And here is a Forth literal translation that runs in edit: 1 minute 10 seconds on my indirect threaded system.
( Re-timed using a stop-watch because the there was too much VDP I/O playing with the ISR timer)
I have included the BASIC code as comments.
It would be faster if I replace the VARIABLEs with VALUEs but this is vanilla Forth.
Most systems have character array creator CARRAY or it can be made easily enough.
Spoiler\ literal translation of BASIC program to Forth INCLUDE DSK1.TOOLS INCLUDE DSK1.ELAPSE INCLUDE DSK1.ARRAYS DECIMAL : ?BREAK ?TERMINAL ABORT" *BREAK*" ; \ must define all data before use VARIABLE WIN VARIABLE POWER VARIABLE NUMLEN VARIABLE CARRY VARIABLE INAROW VARIABLE NDX ( transfers loop index out of DO LOOP ) 256 CARRAY ]A \ 100 DIM A(256) : RUN CR ." 7's Problem " \ 110 PRINT "7's Problem" 0 ]A 256 0 FILL \ init ]A to zero 7 0 ]A C! \ 120 A(1)=7 WIN OFF \ 130 WIN=0 1 POWER ! \ 140 POWER=1 0 NUMLEN ! \ 150 NUMLEN=1 BEGIN POWER 1+! \ 160 POWER=POWER+1 ." 7 ^" POWER @ . ." IS:" \ 170 PRINT "7 ^";POWER;"IS:" ?BREAK CARRY OFF \ 180 CARRY=0 INAROW OFF \ 190 INAROW=0 NUMLEN @ 1+ 0 \ 200 FOR I=1 TO NUMLEN DO I NDX ! \ copy I for later I ]A C@ 7 * CARRY @ + \ 210 A(I)=A(I)*7+CARRY \ We avoid some math with divide & mod function 0 10 UM/MOD CARRY ! \ 220 CARRY=INT(A(I)/10) I ]A C! \ 230 A(I)=A(I)-CARRY*10 I ]A C@ 7 = \ 240 IF A(I)<>7 THEN 290 IF INAROW 1+! \ 250 INAROW=INAROW+1 INAROW @ 6 = \ 260 IF INAROW<>6 THEN 300 IF WIN ON \ 270 WIN=1 LEAVE THEN ELSE \ 280 GOTO 300 INAROW OFF \ 290 INAROW=0 THEN ?BREAK LOOP \ 300 NEXT I CARRY @ DUP NDX @ 1+ ]A C! \ 310 A(I)=CARRY IF \ 320 IF CARRY=0 THEN 340 NUMLEN 1+! \ 330 NUMLEN=NUMLEN+1 THEN CR \ replaces PRINT 0 NUMLEN @ \ 340 FOR I=NUMLEN TO 1 DO I ]A C@ >DIGIT (EMIT) \ 350 PRINT CHR$(A(I)+48); -1 +LOOP \ 360 NEXT I ( STEP -1) CR CR \ 370 PRINT :: WIN @ \ 380 IF WIN<>1 UNTIL \ THEN 160 ." Winner is 7 ^" POWER @ . \ 390 PRINT "WINNER IS 7 ^";POWER ; \ 420 END
- 3
- 1
-
When I first got the system running it compiled very slowly. I did a little test typing 1 to 9 with a space between each digit and hitting enter.
This was a worst case search and it was something like 3 or 4 seconds.
I wrote (FIND) in Assembler and the same test takes just under 1 second. Brad made Camel Forth for small size so somethings suffer.
Interesting stuff on the hashing. PolyForth used to limit the dictionary to the length, three characters and a hash value for the rest.
I can't remember the details but that was for compact size.
F83 created 4 search threads and put each word in a different thread by hashing the 1st character. This speeds up the average search time by 4.
Forth Inc. uses 8 search threads. I have considered doing a 4 way hashed dictionary.
I did make a hash table of the entire dictionary to compare search times using only Forth code and it was 3 times faster than what I have now.
Hashing is amazing when you find the right one.
- 3
-
Part of the innards of a Forth system is some way to "parse" through source, extracting one space delimited word at a time.
Traditionally this was done with BL WORD. Looks simple but if you peek behind on the curtain on any Forth system there is fair bit of code behind WORD.
Here is a modern way to code PARSE-NAME that I found on the Forth Standard site.
PARSE-NAME is what they use these days instead of BL WORD.
This code is note-worthy to me because of the word XT-SKIP which is like SKIP but instead of testing for a character match, XT-SKIP runs a piece of code so it can compare any range of characters.
In this case any character less than a space is white space and anything not white space is a valid character but we could put any code in there.
This appears to be a lot less code than I used in CAMEL99 Forth but on the other hand I have WORD PARSE and PARSE-WORD and I make PARSE-NAME
with:
: PARSE-NAME BL PARSE-WORD ;
Anyway I thought some folks might want to see how modern languages are influencing Forth thought leaders
and how Forth can replicate those "mapping" features without much trouble. (this code compiles on Camel99 FORTH)
\ PARSE-NAME from https://forth-standard.org/standard/core/PARSE-NAME : white? ( c -- f ) BL 1+ U< ; \ space and below are white chars : -white? ( c -- f ) white? 0= ; \ everything above are not : xt-skip ( addr1 n1 xt -- addr2 n2 ) \ skip all characters satisfying xt ( c -- f ) >R BEGIN DUP WHILE OVER C@ R@ EXECUTE WHILE 1 /STRING REPEAT THEN R> DROP ; : PARSE-NAME ( "name" -- c-addr u ) SOURCE >IN @ /STRING ['] white? xt-skip OVER >R ['] -white? xt-skip ( -- end-word restlen) ( r: start-word ) 2DUP 1 MIN + SOURCE DROP - >IN ! DROP R> TUCK - ;
- 1
-
I promise I didn't pay anybody but Camel99 Forth is listed among the systems at the Forth Standard site.
I believe it may be because I joined a Forth group on Github and Lars Brinkhof probably stuck the link in the list. ??
The things that can happen on the interweb with these kids nowadays.
- 1
-
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.
-
That makes for nice light work for the Forth system. Very nice.
-
You know it's a wonder I don't have a much flatter forehead.
I was thinking about how to transfer binary data over RS232 now that I have this reliable com port receiver.
I realized that the way I implement KEY? would be a problem because my version tests for a key and returns the ASCII value pressed OR returns a zero if no key was pressed.
But binary data can contain zeros. So I started wondering how does Forth handle that?
I went to the Standard and see this:
10.6.1.1755
KEY? key-question
( -- flag )
If a character is available, return true. Otherwise, return false.Oops! I implemented this incorrectly. KEY? is just the test. KEY is what reads the character.
I thought I would be "efficient" and while I was checking, just read the character as well.
By using KEY? and KEY together you can collect binary data no problem.
If you do it my way... not so much.
Time for some kernel fixing.
- 2
-
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
-
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
-
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?
-
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
-
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.
- 4
- 1
-
1 hour ago, FarmerPotato said:
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.
- 3
-
I did some experiments to see how small I could make the queue buffer. I reduced in steps down to 64 bytes,
which means it fills up to 32 bytes with 32 more for over runs in case the sender doesn't recognize the handshake fast enough.
The TI did not drop one character. The video shows it compiling a pretty large file over the terminal with the small buffer.
Here is what I will call a final version. It has greatly simplified Q creation and a few instructions less.
BTW I tried writing QKEY? in Forth and it is a few bytes bigger.
I have to make a good machine Forth using the power of the 9900 inline code.
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 register 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, \ make sure to select the card 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 int NE UNTIL, \ loop until <> 0 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
- 3
Pascal on the 99/4A
in TI-99/4A Development
Posted
Oh I see now that I actually read the rest of your code. You use the quotient as the 1st digit.
OK. Very specific number conversion.
I will leave it with you as an exercise to to make an integer version.
I am happy that I am able to help in some small way.