+mizapf Posted April 21, 2015 Share Posted April 21, 2015 Three additions: So now for the TMS9901. This circuit is mapped into port addresses 0x0000 - 0x003e for a total of 32 ports (0x40 / 2). The first 16 ports are named CONTROL / INT1 / INT2 ... / INT15. These ports can be used as input lines. When you call TB 1, for example, you get the current value of INT1. 1. The CONTROL line is used to switch between the two modes of the 9901, interrupt vs. timer mode. The others can be used as input lines. 2. When you transfer multiple bits via CRU (by LDCR or STCR), the bits are transferred right-to-left. If you write the value 0x000B, the bits will be: 1, 1, 0, 1, 0 ... 0 (each time increasing the CRU address). Also, if you transfer 8 bits or less, the bits are taken from the left byte of the word, while from 9 to 16, the first bit is the rightmost bit of the word. Example (sorry, still in lecture mode ): R12 = 0x1100 R2 = 0x010B LDCR R2,10 Result is: 1100 = 1 1102 = 1 1104 = 0 1106 = 1 1108 = 0 110A = 0 110C = 0 110E = 0 1110 = 1 1112 = 0 while LDCR R2,4 yields 1100 = 1 1102 = 0 1104 = 0 1106 = 0 3. Not all lines have their own pin on the 9901. The lines INT1 ... INT6 have own pins and are input-only. The lines INT7 ... INT15 share their pins with P15 ... P7 (in that order). They can be configured as inputs or outputs. The lines P1 ... P6 have their own pins again. The confusing detail is that on these shared lines you will read the same value by two different ports, while writing to those two ports has different effects. For instance, /INT7 is the same pin as P15. That is, TB 7 will always deliver the same value as TB 31. However, when you do a SBO 7, you arm the INT7 input to raise interrupts, while a SBO 31 will output a 1 via that pin. 4. The /INT lines are active low, but only in terms of triggering interrupts. If you don't let them trigger an interrupt, and you read the pin by TB or STCR, you will get the level that is present at that pin. There is no need to invert. Quote Link to comment Share on other sites More sharing options...
+OLD CS1 Posted April 21, 2015 Share Posted April 21, 2015 Don't worry - as I said, I'll put the text in ninerpedia so that it won't get lost. (However, people still need to remember that it will be found there...) A mention in the development resources sticky, too. The CRU is a mystical beast. Quote Link to comment Share on other sites More sharing options...
Dexter Posted April 21, 2015 Author Share Posted April 21, 2015 (edited) I’ve typed part of a joystick routine to see if I understand things so far. Judging from existing code, that’s the case. * SUPPOSE WE WANT TO CHECK JOYSTICK 1 * SET THE COLUMN DECODER P5 P4 P3 P2 TO 6, JOYSTICK 1 LI R12,36 * SET THE BASE ADDRESS TO P2 (SEE TABLE MIZAPF) LI R3,>0600 * SET THE COLUMN TO 6, THAT'S JOYST1 (SEE TABLE MIZAPF) LDCR R3,3 * LOAD THE 3 LSb's OF THE VALUE IN R3, I.E. 6. IS BINARY 110 * NOW THE BITS P4 P3 P2 ARE RESP. 1 1 0, I.E. COLUMN 6 IS SELECTED * SET THE ROW TO THE BASE OF THE JOYSTICK BITS INT7 INT6 INT5 INT4 INT3, * I.E. UP DOWN RIGHT LEFT BUTTON * THAT'S BIT 3, MUTIPLIED BY 2, IS 6 LI R12,6 * BASE ADDRESS FOR THE ROW, FIRST BIT IS INT3 (SEE TABLE MIZAPF) TB 0 * TEST JOYSTICK BUTTON (INT3, BASE) * RESULT IS IN EQ BIT OF THE STATUS REG JEQ SKIP1 * IF ZERO, THEN NO BUTTON PUSSHED, SKIP TO NEXT TEST ACT_B * ACT ON BUTTON PUSHED SKIP1 TB 1 * TEST JOYSTICK LEFT (INT4, BASE+1) TB 2 * TEST JOYSTICK RIGHT (INT5, BASE+2) TB 3 * TEST JOYSTICK DOWN (INT6, BASE+3) TB 4 * TEST JOYSTICK UP (INT7, BASE+4) However, to make it more interesting, I tried to decode the key “J”, and two more. * SUPPOSE WE WANT TO CHECK THE KEY "J" * SET THE COLUMN DECODER P5 P4 P3 P2 TO 3, U 7 F J M 4 R V LI R12,36 * SET THE BASE ADDRESS TO P2 (SEE TABLE MIZAPF) LI R3,>0300 * SET THE COLUMN TO 6, THAT'S U 7 F J M 4 R V (SEE TABLE MIZAPF) LDCR R3,2 * LOAD THE 2 LSb's OF THE VALUE IN R3, I.E. 3. IS BINARY 11 * NOW THE BITS P3 P2 ARE RESP. 1 1, I.E. COLUMN 3 IS SELECTED * SET THE ROW TO THE BASE OF THE JOYSTICK BITS INT10 INT9 INT8 INT7 INT6 INT5 INT4 INT3, * I.E. V R F 4 7 U J M * THAT'S BIT 4, MUTIPLIED BY 2, IS 8 LI R12,8 * BASE ADDRESS FOR THE ROW, FIRST BIT IS INT4 (SEE TABLE MIZAPF) TB 0 * TEST FOR KEY "J" (INT4, BASE) * RESULT IS IN EQ BIT OF THE STATUS REG JEQ SKIP1 * IF ZERO, THEN NOT PRESSED, SKIP TO NEXT TEST ACT_B * ACT ON PRESSED SKIP1 TB >FF * TEST FOR KEY "M" (INT3, BASE-1) TB >01 * TEST FOR KEY "U" (INT5, BASE+1) . . . I hope I got it right? Indeed, your table is very handy. I made a little table to see which bit represents which line. Edited April 21, 2015 by Dexter 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted April 21, 2015 Share Posted April 21, 2015 ... ... TB 0 * TEST JOYSTICK BUTTON (INT3, BASE) * RESULT IS IN EQ BIT OF THE STATUS REG JEQ SKIP1 * IF ZERO, THEN NO BUTTON PUSSHED, SKIP TO NEXT TEST ACT_B * ACT ON BUTTON PUSHED SKIP1 ... ... Remember that JEQ tests for whether the EQ bit is set (1), not whether it is reset (0). It seems you intend to jump to SKIP1 when EQ = 0, which would require JNE. ...lee Quote Link to comment Share on other sites More sharing options...
+mizapf Posted April 21, 2015 Share Posted April 21, 2015 JEQ SKIP1 * IF ZERO, THEN NO BUTTON PUSSHED, SKIP TO NEXT TEST ACT_B * ACT ON BUTTON PUSHED SKIP1 TB 1 * TEST JOYSTICK LEFT (INT4, BASE+1) TB 2 * TEST JOYSTICK RIGHT (INT5, BASE+2) TB 3 * TEST JOYSTICK DOWN (INT6, BASE+3) TB 4 * TEST JOYSTICK UP (INT7, BASE+4) Each TB sets the status register EQ bit, i.e. you must use a JEQ / JNE shortly after each TB, at least before the next one. Also keep in mind from the illustration that a keypress in a selected column means that the /INT line is pulled down, so TB will put a 0 into the status register. Accordingly, a JEQ will react when the key is not pressed, because in that case the /INT line is 1. Your comment should read "if one, no button is pressed". Take care not to confuse this test with the test against 0, e.g. MOV R1,R1. If R1 is zero, the EQ bit is set. Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted April 21, 2015 Share Posted April 21, 2015 As happens now and then, I was right for the wrong reason! I should have corrected the comment: TB 0 * TEST JOYSTICK BUTTON (INT3, BASE) * RESULT IS IN EQ BIT OF THE STATUS REG JEQ SKIP1 * IF ONE, THEN NO BUTTON PUSSHED, SKIP TO NEXT TESTACT_B * ACT ON BUTTON PUSHED SKIP1 ...lee Quote Link to comment Share on other sites More sharing options...
Dexter Posted April 21, 2015 Author Share Posted April 21, 2015 ... ... TB 0 * TEST JOYSTICK BUTTON (INT3, BASE) * RESULT IS IN EQ BIT OF THE STATUS REG JEQ SKIP1 * IF ZERO, THEN NO BUTTON PUSSHED, SKIP TO NEXT TEST ACT_B * ACT ON BUTTON PUSHED SKIP1 ... ... Remember that JEQ tests for whether the EQ bit is set (1), not whether it is reset (0). It seems you intend to jump to SKIP1 when EQ = 0, which would require JNE. ...lee I often mix those kind of things up, and wonder why it doesn't work as expeced. I'm just getting old Same thing with the 6502, there it's BNE and BEQ. However, it seems the code was right, but the comment schould state "IF ONE, THEN NO BUTTON PUSHED..." Each TB sets the status register EQ bit, i.e. you must use a JEQ / JNE shortly after each TB, at least before the next one. Also keep in mind from the illustration that a keypress in a selected column means that the /INT line is pulled down, so TB will put a 0 into the status register. Accordingly, a JEQ will react when the key is not pressed, because in that case the /INT line is 1. Your comment should read "if one, no button is pressed". Take care not to confuse this test with the test against 0, e.g. MOV R1,R1. If R1 is zero, the EQ bit is set. Either it's a common mistake, or you read my mind *shivers*. Indeed, the comment was not right. This is very enjoyable! I'll have to make decent notes of this all. 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted April 21, 2015 Share Posted April 21, 2015 ... I'm just getting old ... Join the club! ...lee Quote Link to comment Share on other sites More sharing options...
pnr Posted April 21, 2015 Share Posted April 21, 2015 Thank you! This is an EXCELLENT explanation. I’m going to use some colors to clarify. BLACK, my own comments / questions RED, I don’t understand GREEN, perfectly clear BLUE, don’t agree Although, I’m not sure what you could explain more or better about the subject. How about a bit of history? The TI990 was designed as a successor to the TI960 and TI980 computers. The TI960 was a process/production control computer, quite similar to what we would now call a PLC. Its purpose was to interface to all the sensors and actuators of an industrial plant, and that is where the bit addressability comes in handy. A brochure for the TI960 is here: http://bitsavers.informatik.uni-stuttgart.de/pdf/ti/960/960Flyer.pdf The CPU would interface to multiple cages with I/O cards through a simple 15 wire bus (12 address, cruin, cruout, cruclk). The TI990 operated two busses that were physically separate, the CRU bus (to communicate with industrial I/O cards) and the TILINE bus (for memory, disk controllers, etc.). As mentioned in an earlier post, the TMS9900 multiplexes the two buses onto shared pins. Up to 7 I/O cages could be hooked up, with up to 24 cards in each cage. A card typically provided 32 I/O's. A description of how TI envisioned the TI990 series can be found here: http://bitsavers.informatik.uni-stuttgart.de/pdf/ti/990/945250-9701_990_Computer_Family_Systems_Handbook_3ed_May76.pdf Section 2.1.10 has an extensive explanation of the above. The TI960 heritage was also one of the considerations in choosing the workspace concept for registers, or so it seems. 2 Quote Link to comment Share on other sites More sharing options...
matthew180 Posted April 22, 2015 Share Posted April 22, 2015 @pnr: Thanks for the info. I always hated the 9900 CRU, why couldn't TI just do I/O like everyone else? However, I assumed there was a reason that would make some sense of the madness, and indeed there is. I think that the CRU was also an experiment, as well as the memory-based registers. Quote Link to comment Share on other sites More sharing options...
Tursi Posted April 22, 2015 Share Posted April 22, 2015 You can read the Joystick using the same set up and a STCR instead of multiple TBs. Reading 5 bits will give you the button and all four axes in one instruction, which is faster and usually more convenient since you can more quickly free up R12. (STCR R0,5 -- will store the bits in the 5 most significant bits of R0). Then you can simply use COC (Compare Ones Corresponding - sort of an AND/Compare combined instruction) to see if the bits you care about are set. 2 Quote Link to comment Share on other sites More sharing options...
Willsy Posted April 22, 2015 Share Posted April 22, 2015 You can read the Joystick using the same set up and a STCR instead of multiple TBs... That's how TurboForth does it: ; JOYST ( joystick# -- value ) ; Scans the joystick returning the direction value _joyst mov *stack,r1 ; get unit number ai r1,6 ; use keyboard select 6 for #0, 7 for #1 swpb r1 li r12,36 ldcr r1,3 li r12,6 stcr r1,5 swpb r1 inv r1 andi r1,>001f mov r1,*stack li r12,_next mov r12,@>83d6 ; defeat auto screen blanking mov @bank1_,@retbnk ; return to bank 1 if interuupts should fire limi 2 ; briefly enable interrupts limi 0 ; and turn 'em off again b @retb0 ; return to caller in bank 0 For joystick #1 use a unit# of 0. For joystick 2 use a unit# of 1. The returned value value is a bit code which can be decoded as follows: 1=Fire 2=Left 4=Right 8=Down 16=Up Since each direction has its own bit, combinations are possible: for example, UP+LEFT+FIRE returns a value of 19. Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted April 22, 2015 Share Posted April 22, 2015 That's how TurboForth does it: ... ...as does fbForth if the user so chooses. Actually, fbForth allows you to use either the CRU method or the console's KSCAN (default due to TI Forth compatibility constraints). ...lee Quote Link to comment Share on other sites More sharing options...
Dexter Posted April 23, 2015 Author Share Posted April 23, 2015 How about a bit of history? The TI990 was designed as a successor to the TI960 and TI980 computers. The TI960 was a process/production control computer, quite similar to what we would now call a PLC. Its purpose was to interface to all the sensors and actuators of an industrial plant, and that is where the bit addressability comes in handy. A brochure for the TI960 is here: http://bitsavers.informatik.uni-stuttgart.de/pdf/ti/960/960Flyer.pdf The CPU would interface to multiple cages with I/O cards through a simple 15 wire bus (12 address, cruin, cruout, cruclk). The TI990 operated two busses that were physically separate, the CRU bus (to communicate with industrial I/O cards) and the TILINE bus (for memory, disk controllers, etc.). As mentioned in an earlier post, the TMS9900 multiplexes the two buses onto shared pins. Up to 7 I/O cages could be hooked up, with up to 24 cards in each cage. A card typically provided 32 I/O's. A description of how TI envisioned the TI990 series can be found here: http://bitsavers.informatik.uni-stuttgart.de/pdf/ti/990/945250-9701_990_Computer_Family_Systems_Handbook_3ed_May76.pdf Section 2.1.10 has an extensive explanation of the above. The TI960 heritage was also one of the considerations in choosing the workspace concept for registers, or so it seems. Wow, farout! Industry technology of the 70’s. Well it’s not the simplest design one can think of, but it surely is expandable. You can read the Joystick using the same set up and a STCR instead of multiple TBs. Reading 5 bits will give you the button and all four axes in one instruction, which is faster and usually more convenient since you can more quickly free up R12. (STCR R0,5 -- will store the bits in the 5 most significant bits of R0). Then you can simply use COC (Compare Ones Corresponding - sort of an AND/Compare combined instruction) to see if the bits you care about are set. Thanks for pointing out. @Willsy, @Lee Hmmm, Forth, a complete mystery to me. Maybe one day... Quote Link to comment Share on other sites More sharing options...
+mizapf Posted April 23, 2015 Share Posted April 23, 2015 Hmmm, Forth, a complete mystery to me. Maybe one day... :imho I forthprogram isauthor if canread else scratchhead then; 4 Quote Link to comment Share on other sites More sharing options...
Willsy Posted April 23, 2015 Share Posted April 23, 2015 :imho I forthprogram isauthor if canread else scratchhead then; Hilarious! Also true. It's very easy to write "write-only" code in Forth. Chuck Moore (inventor of Forth) claims that Forth is an amplifier. If you're skilled in the art, then you write exquisite Forth with care. If not, it's an un-readable mess. I'm somewhere inbetween, I think. Of course, what Mr. Moore doesn't say is that, beauty is often in the eye of the beholder! Quote Link to comment Share on other sites More sharing options...
Willsy Posted April 23, 2015 Share Posted April 23, 2015 Actually Michael, I'm interested to hear what you think overall of this Forth code. Semi-readable? Mark \ screen drawing/management routines : DefineGraphics ( --) \ define the user defined graphics and set colours of character sets 1 GMODE FALSE SSCROLL ! 32 0 DO I 1 14 COLOR LOOP 16 1 15 COLOR \ brick colour 17 4 15 COLOR \ robot colour 18 15 14 COLOR \ empty tile colour 19 1 15 COLOR \ orb colour 21 3 14 COLOR \ time bar BrickUDG BallUDG1 RobotUDG1 OrbUDG EmptyUDG BarUDG2 BarUDG4 BarUDG6 BarUDG8 ; : NextColumn ( --) \ advance to the next column 2 +TO Column ; : DrawIt ( a b c d --) \ emits the four ascii characters on the stack to the screen as follows: \ ac \ bd Column 1+ Row 1+ GOTOXY EMIT \ d Column 1+ Row GOTOXY EMIT \ c Column Row 1+ GOTOXY EMIT \ b Column Row GOTOXY EMIT \ a NextColumn ; : DrawBrick ( --) \ draws a brick tile at Column & Row 128 129 130 131 DrawIt ; : DrawBall ( --) \ draws a ball tile at Column & Row 132 133 134 135 DrawIt ; : DrawRobot ( --) \ draws a robot at Column & Row 136 137 138 139 DrawIt ; : DrawOrb ( --) \ draws an Orb at Column & Row 152 153 154 155 DrawIt ; : DrawEmpty ( --) \ draws an empty tile at Column & Row 144 144 144 144 DrawIt ; : C@++ ( addr -- val addr+1) \ fetches a byte from addr and leaves the next address on top of stack DUP C@ SWAP 1+ ; : InfoPanel ( --) \ set up the bottom of the display (score, # of lives, etc) 0 18 GOTOXY ." Time" TRUE ZEROS ! 0 19 GOTOXY ." Score:" Score U. FALSE ZEROS ! 25 19 GOTOXY ." Lives:" Lives 1- . 0 20 GOTOXY ." Level:" Level 1+ . 18 4 $AB 28 HCHAR ; : DecodeLevel ( level --) \ walk thru 38 bytes of level data, decoding, and drawing the screen \ level data is encoded in groups of 2 bits, as follows: \ 00 - empty space \ 01 - brick \ 10 - orb \ 11 - unused 38 * 'LevelData + \ address of level data in memory C@++ C@++ -ROT \ addr robotaddr balladdr 16 /MOD 2* TO RobotRow 2* TO RobotCol 16 /MOD 2* TO BallRow 2* TO BallCol 0 TO Row 0 TO OrbCount 9 0 DO \ 9 rows 0 TO Column 4 0 DO \ 4 bytes to a row C@++ SWAP \ get a byte of data DUP $03 AND \ get 000000xx from the byte OVER 2 >> $03 AND \ get 0000xx00 from the byte 2 PICK 4 >> $03 AND \ get 00xx0000 from the byte 3 PICK 6 >> $03 AND \ get xx000000 from the byte \ now decode the extracted bit patterns... 4 0 DO CASE 0 OF DrawEmpty ENDOF 1 OF DrawBrick ENDOF 2 OF DrawOrb 1 +TO OrbCount ENDOF ENDCASE LOOP DROP LOOP 2 +TO Row LOOP DROP InfoPanel \ draw the score, # lives BallRow TO Row BallCol TO Column DrawBall \ set up ball RobotRow TO Row RobotCol TO Column DrawRobot ( set up robot) ; : Tick ( --) \ update time bar at bottom of playfield \ the last character of the time bar is sucessively replaced. \ it starts with an 8 pixel wide character, then a 6 pixel, \ then a 4 pixel, then a 2 pixel wide character. \ Finally, it is replaced with an empty character, and the length \ of the bar is reduced. 18 TimeCol GCHAR \ get the ascii code from the end of the time bar DUP $A8 = IF \ replace with empty character... DROP $AB 18 TimeCol 32 1 HCHAR -1 +TO TimeCol ELSE 1- \ otherwise reduce the ascii value by 1 THEN 18 TimeCol ROT 1 HCHAR \ draw new ascii character Time 100 = IF \ time getting low? Speech? IF Hurry THEN \ say "hurry hurry hurry" 21 8 14 COLOR \ change bar to red TRUE TO FlashOrbs? THEN ; : CheckTime ( --) \ update Time and check if time bar should be updated -1 +TO Time Time 4 MOD 0= IF Tick THEN ; : Bonus ( --) \ convert un-used time to bonus points, re-draw time bar as bonus \ is computed Time 0> IF TRUE ZEROS ! 1000 \ sound pitch parameter Time 4 / 1 DO Tick 5 +TO Score 6 19 GOTOXY Score U. DUP 7 0 SOUND 8 - DUP 8 - 7 1 SOUND LOOP DROP 0 15 0 SOUND 0 15 1 SOUND \ turn off sound 4 18 GOTOXY SPACE ZEROS 0! THEN ; : Animate ( --) \ animates either the ball or the robot Ball IsPlayer? = IF BallFrames Frame# CELLS + @ EXECUTE ELSE RobotFrames Frame# CELLS + @ EXECUTE THEN Frame# 1+ 4 MOD TO Frame# ; Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted April 23, 2015 Share Posted April 23, 2015 :imho I forthprogram isauthor if canread else scratchhead then; OK—I must point it out! : That code would not have a chance of working without a space after the ‘:’ and one before the ‘;’ . ...lee Quote Link to comment Share on other sites More sharing options...
Dexter Posted April 23, 2015 Author Share Posted April 23, 2015 1 Quote Link to comment Share on other sites More sharing options...
Opry99er Posted April 23, 2015 Share Posted April 23, 2015 ":imho? then;?" 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.