Asmusr Posted July 1, 2017 Share Posted July 1, 2017 Re JH and JHE after subtraction, the jump path for JHE is the only one always taken, i.e., one, but not both, of the L and EQ flags will be set in comparisons to 0. Conversely, the jump path for JL will never be taken and that for JLE, only when the result is 0. ...lee OK, the jump path for JH will not be taken if the result is zero, but that's already covered by JNE. So I guess the answer to my question is no, these conditions are only useful with compare instructions. Quote Link to comment Share on other sites More sharing options...
matthew180 Posted July 2, 2017 Author Share Posted July 2, 2017 (edited) ... I was able to easily understand most of this post up to the point where your code started plotting random characters on the screen. The DIV OpCode is where I started to stumble as I've never really used it before. I went to p.88 of the E/A manual as a reference. To help in my understanding of the logic I added comments as notes. A way to further clarify what exactly is going on between registers during the Divide, etc. ... Like Lee said, don't read into the instruction that much. DIV is just a 32 x 16 divide, and to hold the 32-bit dividend you need to use two 16-bit registers. Since the CPU can only produce a 16-bit quotient for the answer, the divisor has to be big enough when compared to the dividend such that the answer will fit in a 16-bit register. For example, you can't divide something like 200,000 by 3, because the quotient would be 66,666 which will not fit in a 16-bit register. That is all they are trying to say in the E/A manual (which got its information from the 9900 datasheet) about the divisor having to be greater-than the MS-word of the dividend. If you look at the bits for 200,000 it looks like this: MSW LSW 0000 0000 0000 0011 : 0000 1101 0100 0000 / 0000 0000 0000 0011 32-bit dividend 200,000 divisor 3 The MS-word of the 32-bit dividend is 3, which is less-than or equal-to the divisor (which is 3 in this contrived example), which means the quotient will not fit in a 16-bit register. The CPU performs this check before even trying to start the DIV operation. The manual mentions this because as a programmer you need to be aware of this limitation, i.e. there are ranges of numbers that you cannot divide in lopsided divisions (lopsided meaning any division where the divisor and dividend are not the same bit-size). I posted about this earlier when I was implementing the DIV instruction for the F18A GPU, see post #230: http://atariage.com/forums/topic/162941-assembly-on-the-994a/page-10?do=findComment&comment=2732413 As for the code that caused you the confusion, it makes more sense if you consider what it is doing. To plot a random point on the screen, you need a random X and Y value. However, since we know the screen is really just a continuous array of 768 (32 * 24) bytes in VRAM, we can achieve the same result by picking a random number between 0 and 767: x = rnd(32) - 1 // our pretend rnd() function returns a value of 1 to the specified number. y = rnd(24) - 1 pos = y * 32 + x Or, easier and faster: pos = rnd(768) - 1 Now, in our case in assembly language, the random number function we are using will only return a 16-bit value. So when we want a random number we get a value between 1..65535 (the random number will never be 0). So, we need to convert that to a usable screen position value between 0..767: num = rnd(65535) // Range we get from RANDNO of 1..65535 pos = num % 768 // The % is the modulo operator In higher level languages you have to use a modulo operator (or do integer division, then subtract from the original number) to get the remainder, which will be a number between 0 and 767. So that is all we are doing with the DIV in assembly. We know the dividend is always a 16-bit number (because that's what our random number function gives us), and the divisor is always 768. This will produce a remainder between 0 and 767. It is nice that DIV gives us the quotient and the remainder in a single operation, and in this case we don't care about the quotient part, just the remainder. So, in setting up our 32-bit dividend the MS-word is always zero, which is why we just CLR R4. The LS-word of the dividend is the 16-bit random number. We are still doing a 32-bit x 16-bit divide, but the 32-bit dividend will never be more than 65535, so we know the DIV will never overflow. For trivia, with a divisor of 768, our 32-bit dividend could be any number less-than or equal-to 50,331,647, which is actually a pretty small range considering a 32-bit number can hold values up to 4,294,967,295. In this case (a divisor of 768) numbers up to 50,331,647 are only 1.16% of the possible range of a 32-bit number. ... I'm finding this thread incredibly helpful. Forgive my snails pace as I pick my way through the lessons. To me it seems to be moving along quite well yet based on the dates of your original posts I'm moving along a bit slower than Owen and the guys from 2010. Thanks again! ... I'm glad you are getting something out of it. And for what it is worth, I don't think Owen actually went through the exercises. He wanted to learn, but I'm not sure if he followed through with it. No matter, we each do what interests us to make the hobby fun. Learning assembly is always a slow pace activity (but very rewarding). Edited July 2, 2017 by matthew180 2 Quote Link to comment Share on other sites More sharing options...
matthew180 Posted July 2, 2017 Author Share Posted July 2, 2017 Note, the longer post about division was #230 in this thread: http://atariage.com/forums/topic/162941-assembly-on-the-994a/page-10?do=findComment&comment=2732413 1 Quote Link to comment Share on other sites More sharing options...
Airshack Posted July 7, 2017 Share Posted July 7, 2017 (edited) Thanks for the feedback guys! I just finished page three of this wonderfully informative 19 page thread. Again, I'm humbled by all the help I've received. Matt's approach to learning Assembly Language is something that has grabbed my attention but more importantly, it's really helping me learn while staying engaged. The Gameloop code from page three of this thread took me a while to wrap my mind around. In the process I read multiple user's comments and did some research into the TMS9900 Guide and TI's E/A manual. I've attached a version of Matthew's GameLoop code with "For Dummies" style comments and additional information in an attempt to encapsulate everything I have learned while pouring through the code. A beginner with some of the same questions as myself, and very few of the expert-like assumptions of a proficient Assembly programmer, may find the additional comments useful. Hopefully I'm not stepping on any toes doing so. This week I was able to demo the BASIC gameloop v Assembly Gameloop programs to Sparkdrummer at the July VAST meeting in PHX. We had a lot of fun looking at this code and comparing its performance to BASIC. Thanks again! - James GameLoop.a99 Edited July 8, 2017 by Airshack 1 Quote Link to comment Share on other sites More sharing options...
Airshack Posted July 8, 2017 Share Posted July 8, 2017 Beware self-modifying code... I often do things like this versus using a stack... SUB1 MOV R11,@SUB1RT+2 ... SUB1RT B @0 Matthew - this is an awesome thread you've got running This is giving me some great ideas for a few programs I'd have trouble writing without getting out of my dsr/utility/input-driven serial-event processing mindset This sparked my interest! I'm guessing here: SUB1 MOV R11, @SUB1RT+2 * this stores the PC/return address of the calling code into a hard memory location immediately following this routine ??? <code> SUB1RT B @0 *I don't get what the '@0' does or how it somehow gets ignored and replaced with the stored return address at SUB1RT+2 ??? Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted July 8, 2017 Share Posted July 8, 2017 (edited) This sparked my interest! I'm guessing here: SUB1 MOV R11, @SUB1RT+2 * this stores the PC/return address of the calling code into a hard memory location immediately following this routine ??? <code> SUB1RT B @0 *I don't get what the '@0' does or how it somehow gets ignored and replaced with the stored return address at SUB1RT+2 ??? The “B @0” takes 4 bytes (2 words). The first word is the machine code for the “B” instruction and the second is the branch address, which, here, is a placeholder. Actually branching to >0000 would reset send the TI-99/4A into the weeds! The second word, though >0000 at startup, gets changed to the contents of R11 at SUB1—presumably after a “BL @SUB1”, which puts the return address in R11. Obviously, SUB1RT+2 must be in RAM space. When the branch is taken, it has the proper branch (return) address. ...lee Edited July 9, 2017 by Lee Stewart 1 Quote Link to comment Share on other sites More sharing options...
Airshack Posted July 8, 2017 Share Posted July 8, 2017 (edited) After Matthew's GameLoop breakdown the thread seems to have moved along (page4), to Owen's viewport code (Beryl) with KSCAN used. I'm going to download that code and try to figure everything out next. Matthew mentioned his disappointment with TI's KSCAN code due to wasted speed via two delay loops. This is probably a good time to look into how KSCAN works. Hopefully Owen's code will be helpful in unravelling yet another TI coding mystery. Willsy posted (6 Jun 2010) KSCAN replacement code? Probably something I need to understand before moving on. I'll start with p.250 of the E/A manual, 16.2.1 KSCAN. Edited July 8, 2017 by Airshack Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted July 8, 2017 Share Posted July 8, 2017 ... TI's KSCAN code ... You might have a look at this thread: The TI-99/4A Operating System. TI’s KSCAN is in the first of the 3 files in the ZIP file of the first post. It is the original TI source code as far as we can tell. ...lee 1 Quote Link to comment Share on other sites More sharing options...
Airshack Posted July 8, 2017 Share Posted July 8, 2017 Thanks Lee! Earlier posts here outline the problems with using BLWP: speed, scratchpad RAM overwriting, etc. Looks to me like TI's KSCAN requires this... along with the REF... thus limitations to be avoided. I'll investigate your link. Thanks! Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted July 9, 2017 Share Posted July 9, 2017 Actually branching to >0000 would reset the TI-99/4A! I think a software reset should be done with a BLWP @>0000 Remember, that location has vectors stored there (>83E0, >0024). Also Tursi concluded it would be much more safe/stable to clear Scratch-Pad prior to resetting. 1 Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted July 9, 2017 Share Posted July 9, 2017 Thanks Lee! Earlier posts here outline the problems with using BLWP: speed, scratchpad RAM overwriting, etc. Looks to me like TI's KSCAN requires this... along with the REF... thus limitations to be avoided. There is nothing wrong with using REF/DEF, BLWP or any of the console routines. It all depends on what you as the programmer are trying to achieve and write. Not everyone rolls their own code for every aspect of their programs, and much learning can be accomplished using those simple, pre-written routines. Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted July 9, 2017 Share Posted July 9, 2017 I think a software reset should be done with a BLWP @>0000 Remember, that location has vectors stored there (>83E0, >0024). Also Tursi concluded it would be much more safe/stable to clear Scratch-Pad prior to resetting. Oops! I was not suggesting that a software reset “should” be done, just that it “would” be done—but, “B @0” will not do that, of course—it will just send the TI-99/4A to never-never land. Thanks for spotting my gaffe. And—I believe that clearing the interrupt vector (>83C4) in Scratch-Pad RAM was all @Tursi suggested should be done prior to doing the reset. Thanks again. ...lee Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted July 9, 2017 Share Posted July 9, 2017 AndI believe that clearing the interrupt vector (>83C4) in Scratch-Pad RAM was all @Tursi suggested should be done prior to doing the reset. Okay. Yea, maybe. Can't find all the stuff, but me thought me heard someone grunt to clear it all. http://atariage.com/forums/topic/199239-pause-or-clearing-vdp-memory/?do=findComment&comment=2542105 http://atariage.com/forums/topic/245161-blwp-0/?do=findComment&comment=3358799 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted July 9, 2017 Share Posted July 9, 2017 There's no interrupt vector at >834C. It's just a branch address. An interrupt vector contains both the address to run code at and the new workspace address. The location of all interrupt vectors are fixed, and almost all of them are at the beginning of the memory. BLWP is a good instruction, if you really need it. If not, it's a waste. But it's impossible to get the same effect as you get with BLWP by a number of other instructions, without that taking much longer time. 1 Quote Link to comment Share on other sites More sharing options...
Tursi Posted July 9, 2017 Share Posted July 9, 2017 Okay. Yea, maybe. Can't find all the stuff, but me thought me heard someone grunt to clear it all. http://atariage.com/forums/topic/199239-pause-or-clearing-vdp-memory/?do=findComment&comment=2542105 http://atariage.com/forums/topic/245161-blwp-0/?do=findComment&comment=3358799 I clear it all just to be safe. It's more than just the interrupt vector - although having something there will always run during the reset, so it's a good idea to clear it. Certain corruption in the GPLWS will also crash the system on reboot - although I can't remember what it was again... it's got to do with the bitflags that the interrupt routine checks. 1 Quote Link to comment Share on other sites More sharing options...
Tursi Posted July 9, 2017 Share Posted July 9, 2017 There's no interrupt vector at >834C. It's just a branch address. An interrupt vector contains both the address to run code at and the new workspace address. The location of all interrupt vectors are fixed, and almost all of them are at the beginning of the memory. That's the strict definition, yes, but any address used to contain an address to jump to code can legitimately be called a vector. This vector is used by the interrupt routine, so, "interrupt vector" - but yes, it's pure software. I'd usually clarify myself with "hardware interrupt vector" or "CPU interrupt vector". Quote Link to comment Share on other sites More sharing options...
matthew180 Posted July 9, 2017 Author Share Posted July 9, 2017 There is nothing wrong with using REF/DEF, BLWP or any of the console routines. It all depends on what you as the programmer are trying to achieve and write. Not everyone rolls their own code for every aspect of their programs, and much learning can be accomplished using those simple, pre-written routines. Agreed. @Airshack (or anyone else following along to learn), always keep in mind that I like to roll my own *everything*. I have used all the console video, keyboard, etc. routines back in the day, before I knew how it all worked. Those routines served me well. However, now that I know all the gory details and such, and being someone obsessed with code efficiency (especially when coding in assembly), I personally do not like to use most of the console routines. Like Tim says, know your situation, and if the console routines will work for you then use them. IMO the console routines should be avoided for games though. Quote Link to comment Share on other sites More sharing options...
matthew180 Posted July 9, 2017 Author Share Posted July 9, 2017 This sparked my interest! I'm guessing here: SUB1 MOV R11, @SUB1RT+2 * this stores the PC/return address of the calling code into a hard memory location immediately following this routine ??? <code> SUB1RT B @0 *I don't get what the '@0' does or how it somehow gets ignored and replaced with the stored return address at SUB1RT+2 ??? This is basically a very clever way to call a subroutine. It accomplishes a few things: 1. Does not require a stack or stack management. 2. Does not require using a register to save the return address if the routine branches to another routine. It has some limitations that you must be aware of though: 1. It is not reentrant. Meaning, once the function is called it cannot be called again until it returns. On the 99/4A this might happen if the function is called by your main code and also called by the ISR, or you are using recursion, or some other type of situation. 2. The function itself must reside in RAM, so you cannot use this method if you are writing code that will ultimately run from something like a cartridge ROM. To understand how it works and where it came from, you have to understand the normal way BL works. There are my ways to implement functions in assembly, one of the being BL (Branch and Link), and is typically used like this: Line Addr Data -------------- 0001 A000 06A0 BL @FUN1 0002 A002 A100 0003 A004 0602 DEC R2 . . . 1000 A100 04C0 FUN1 CLR R0 1001 A102 045B B *R11 Note: The assembler provides an "RT" directive that assembles to the "B *R11" instruction, so in some code (depending on the programmer's preferences) you will see "RT" and sometimes "B *R11". Not a real exciting function I know (clears R0). I have included lines numbers for reference, as well as fake addresses and the actual memory data. Since the 9900 is a Von Neumann architecture (see WikiPedia), the bytes that make up the instruction opcodes, as well as data, are stored in memory together (this is getting to be a slippery slope of explanation, so I'll stop now). The "branch" part of the instruction means the program counter (PC) is going to be changed, and the "link" part means that the current address in the PC is going to be stored ("linked") in a known place so the program can return to the location just after the BL instruction. This is really a form of a linked-list, which is probably where the "link" part of the instruction comes from. When the BL at line #1 above is executed, the 9900 reads the 06A0 opcode and increments the PC. The instruction is decoded and the 9900 knows it needs the address of FUN1, which is stored in memory immediately following the BL opcode. So the 9900 reads the address A100 and increments the PC, which now contains A004, i.e. points to the DEC R2 instruction. To execute the BL instruction the 9900 stores the value of the PC (A004) into R11, then loads the PC with the value (A100) it read immediately following the BL opcode. In this example, that value is A100, or the address of FUN1. So the next opcode loaded by the 9900 will be from address A100, i.e. the first address of the function FUN1. At this point the function will be executed, and when the 9900 reaches the B *R11, it will move the value stored in R11 (A004) into the PC. Remember, the value in R11 was stored by the BL instruction and is the address of the instruction immediately following the BL instruction, i.e. the DEC R2 in this example. Notice a few points about this: 1. The function cannot modify R11 or the location to return to will be lost. 2. If the function calls another function with BL, the value in R11 will be lost. Therefore, if you need your function to call other functions, you must manage the return value in R11. There are a few ways to do that: 1. Create and manage a "stack". Since the 9900 does not have any stack-support instructions, stack management must be done manually. 2. Save R11 in another register. 3. Do self-modifying code (what this post is all about). There are probably other methods, but those are the most common. Note. be careful with #2, you have to know that any functions you call do not also store R11 off into the same alternate register. This method is fine for a depth of 1 or 2 functions, but starts to become tedious and error prone very quickly. The key to Tim's method of self-modifying code is that the value in R11 is used to directly modify the address that the function uses when it returns. To see this, you need to look at the object code: Line Addr Data -------------- 0001 A000 06A0 BL @FUN1 0002 A002 A100 0003 A004 0602 DEC R2 . . . 1000 A100 04C0 FUN1 MOV R11,@FUN1RT+2 1001 A102 A108 1002 A104 04C0 CLR R0 1003 A106 045B FUN1RT B @0 1004 A108 0000 The function is called in the regular way with BL, and the return address is stored in R11 as before. However, the first instruction of FUN1 at line #1000 will move the value of R11 (which contains the return address) into memory at FUN1RT+2. Remember, FUN1RT is just a label that we put at the last line of the function, line #1003. As written above the code would do an unconditional branch to address 0000 (and pretty much crash the computer). However, the 0000 is just a placeholder value so the code will assemble, and the value 0000 is modified when the code runs (which is what makes it "self modifying" code). So FUN1RT is address A106 in our example, and FUN1RT+2 is the same as A106+2, or address A108. So the first instruction of the function becomes: MOV R11,@A108 You can see this in line #1001 where the assembler calculated the memory address and stored it following the MOV instruction, for use by the MOV instruction. The value at address A108 is what the 9900 will use when it executes the "B" instruction at the end of the function. Thus, the return value in R11 at the start of the function is written to the location in memory where it will be used by the "B" (unconditional branch) instruction at the end of the function. Labels are used to let the assembler work out all this addressing for us. I hope this helps clear this up. I tried to keep it brief, but there are a lot of opportunities to slip down slopes when trying to explain things like this. 1 Quote Link to comment Share on other sites More sharing options...
matthew180 Posted July 9, 2017 Author Share Posted July 9, 2017 ... I've attached a version of Matthew's GameLoop code with "For Dummies" style comments and additional information in an attempt to encapsulate everything I have learned while pouring through the code. A beginner with some of the same questions as myself, and very few of the expert-like assumptions of a proficient Assembly programmer, may find the additional comments useful. Hopefully I'm not stepping on any toes doing so. ... In your code you have this comment in the PLOT01 function: CLR R4 * Dividend will be 32-bit R4&R5 (two words used by DIV) which is really just * a 16-bit dividend (R5) with leading zeros in R4 to satisfy the TI DIV OpCode I would not say "to satisfy the TI DIV OpCode". You are not satisfying the 9900, rather as a programmer you are choosing to make sure the divide is a 16-bit / 16-bit operation, since any random data in R4 would effect the result (and may cause the divide to overflow). The 9900 is going to use the 32-bit value formed by the two registers, regardless of what values are in those registers. 1 Quote Link to comment Share on other sites More sharing options...
Airshack Posted July 10, 2017 Share Posted July 10, 2017 (edited) GameLp4D.a99 <==== Corrected GameLoop for Dummies commented code Playing around with that viewport code of Owen's (and the Matthew Methodology version) has me really excited right now. At first I thought Owen's Viewport code represented some skipping ahead, or at very least some confusing thread creep. My expectations were for Matthew to transition towards the next logical conversation --KSCAN.. Loading and running the Viewport code has opened up some very interesting possibilities. It appears Owen is using the traditional KSCAN routine with his viewport code while Matthew is taking the "Roll Yer Own" approach. What better way to learn than to pour through both code segments and test the differences? InsaneMultitasker (Tim) correctly noted that there's nothing wrong with using the console routines. Here lies an opportunity to get familiar with the pre-written routines as well as some efficient custom routines. Win-Win! Edited July 10, 2017 by Airshack Quote Link to comment Share on other sites More sharing options...
Airshack Posted July 10, 2017 Share Posted July 10, 2017 (edited) Remember, FUN1RT is just a label that we put at the last line of the function, line #1003. As written above the code would do an unconditional branch to address 0000 (and pretty much crash the computer). However, the 0000 is just a placeholder value so the code will assemble, and the value 0000 is modified when the code runs (which is what makes it "self modifying" code). It is now clear what is meant by "a placeholder," and the self-modifying code example is understood. The key for me understanding this example was realizing the placeholder is only there to satisfy the assembler. Coming from an interpreted BASIC paradigm, I was assuming the code "FUN1RT B @0" was always going to Branch to >0000. I forgot that this looks crash-worthy post-assembly, on a listing of the assembled code, yet it's modified at run-time to Branch to the intended return address. Clever! Edited July 10, 2017 by Airshack Quote Link to comment Share on other sites More sharing options...
Airshack Posted July 10, 2017 Share Posted July 10, 2017 (edited) My .mag file? Okay, head over to the Berly thread, about page 15 or so and download the map *you* posted. It is your map. I used Magellan to check a few things like tile pattern assignments and actual size of the map, and the data export to assembly code, but that's it. The map data is unmodified since the idea was to use Magellan generated data and load it from disk. Right? lava.zip <==== Early Beryl Magellan File Magellan Graphics Editor Tool for TI-99/4A (runs on Windows): http://atariage.com/forums/topic/267322-programming-resources-for-ti-xb-graphicssprites/?p=3798140 Some graphics inspiration: http://bifi.msxnet.org/msxnet/konami/ Useful links while looking at the Viewport code from 2 June 2010 Edited July 10, 2017 by Airshack Quote Link to comment Share on other sites More sharing options...
Airshack Posted July 10, 2017 Share Posted July 10, 2017 (edited) You might have a look at this thread: The TI-99/4A Operating System. TI’s KSCAN is in the first of the 3 files in the ZIP file of the first post. It is the original TI source code as far as we can tell. ...lee Hey Lee, Did I get it all? B @>0070 To GPL interpreter** GPL SCAN (Scan keyboard with return to GPL interpreter):* LI 11,>0070 Trick with return** Keybord scanning*KEYSCN MOV 11,@>83D8 Save R11 BL @PUSHGA Save GROM address to substack CLR 12 CRU 0 SBO >0015 Alpha lock MOVB @>8374,5 Keyboard mode SRL 5,8 MOV 5,6 JEQ >02EC 0 LI 0,>0FFF DEC 6 1 R0=>0FFF JEQ >02F4 LI 0,>F0FF DEC 6 2 R0=>F0FF JEQ >02F4 DEC 6 3 C 6,@>0072 JH >0382 >5? MOVB 6,@>8374 Keyboard mode 0 SWPB 6 MOVB 6,@>83C6 Keybord debounce (3=0, 4=1, 5=2) CLR 5 CLR 0 CLR 6 JMP >032E Mode 0,3,4,5 DATA >2925 LI 12,>0024 Mode 1,2 scan Joystick LDCR @>0405(5),3 CRU 06 and 07 LI 12,>0006 CLR 3 SETO 4 STCR 4,5 Fetch CRU SRL 4,9 JOC >0310 No fire key? MOVB @>02F1(5),@>83E7 Lbyte R3 SLA 4,1 AI 4,>16E0 GROM address Joystick table MOVB 4,@>0402(13) Write address MOVB @>83E9,@>0402(13) JMP >0322 MOVB *13,@>8376 Fetch values Y**~~Page 19: TI99/4A INTERN (Heiner Martin)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* MOVB *13,@>8377 and X MOV 3,3 Fire key ? JNE >03AA LI 1,>0005 Scan keys CLR 2 CLR 7 LI 12,>0024 SWPB 1 LDCR 1,3 Load CRU SWPB 1 LI 12,>0006 SETO 4 STCR 4,8 Fetch CRU INV 4 Invert bits MOV 1,1 0? JNE >0354 MOVB 4,7 ANDI 4,>0F00 SZC 0,4 JEQ >037A No key? MOV 1,1 Last loop? JNE >0360 MOV 5,5 Mode 1 or 2? JNE >037A MOV 2,2 Already a key? JNE >037A SETO 2 MOV 1,3 SLA 3,3 DEC 3 INC 3 SLA 4,1 Built key value in R3 JNC >036C MOV 1,1 JEQ >037A LI 1,>0001 Shorten loop DEC 1 JOC >0336 Smaller than 0? MOV 2,2 Key pressed? JNE >03AA CLR 6 Set pointer for no key MOVB 6,@>83C7 SETO 0 CB 0,@>83C8(5) JEQ >0394 BL @>0498 Time delay MOVB 0,@>83C8 MOVB 0,@>83C8(5) MOV 5,5 Mode 1 or 2? JNE >0478 End MOVB 0,@>83C9 MOVB 0,@>83CA JMP >0478 End**~~Page 20: TI99/4A INTERN (Heiner Martin)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* CB @>83E7,@>83C8(5) Same key as last time? JEQ >03E6 LI 6,>2000 Set GPL status BL @>0498 Time delay MOVB @>83E7,@>83C8 Set pointer (Lbyte R3) MOVB @>83E7,@>83C8(5) MOV 5,5 Mode 1 or 2 JNE >03E2 MOV 3,12 AI 12,>FFF8 JLT >03E2 LI 1,>0002 SRL 12,3 JOC >03DC DEC 1 MOVB @>83E7,@>83C8(1) MOVB 7,@>83C7 Last loop scanning on >83C7 MOVB @>83C7,7 LI 1,>17C0 GROM table mode 1 and 2 MOV 5,5 JNE >040E LI 1,>1790 GROM table CNTRL SLA 7,2 JOC >040E LI 1,>1760 GROM table FCTN SRL 7,15 JOC >040E LI 1,>1730 GROM table SHIFT DEC 7 JEQ >040E LI 1,>1700 GROM address table small letters A 3,1 +Key value MOVB 1,@>0402(13) Write address MOVB @>83E3,@>0402(13) JMP >041C MOVB *13,0 Values from GROM MOV 5,5 Mode 1 or 2? JNE >0478 MOVB @>83C6,@>83E7 Keyboard mode in R3 Lbyte BL @>04A2 Compare DATA >617A**~~Page 21: TI99/4A INTERN (Heiner Martin)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* JNE >0444 Small letter? CLR 12 MOV 3,3 Mode <4 JEQ >043E SBZ >0015 Activate ALPHA LOCK SRC 12,14 TB >0007 Scan ALPHA LOCK JEQ >0442 SB @>03B4,0 ->20 (Capital letter) SBO >0015 MOV 3,3 Mode 4 or 5 JNE >0456 BL @>04A2 Compare DATA >101F JEQ >0382 R0 between >10 and >1F CB 0,@>0587 5F? JH >0382 DEC 3 Mode 5 JNE >0478 CB 0,@>0025 0D (CR)? JEQ >0478 CB 0,@>02CA 0F? JH >046C SOCB @>0470,0 Set 1st bit JMP >0478 BL @>04A2 Compare DATA >809F JNE >0478 R0 smaller than >80 or bigger than >9F SZCB @>0470,0 Reset 1st bit MOVB 0,@>8375 ASCII value key on >8375 BL @POPGRA Restore GROM address MOVB 6,@>837C Set GPL status JEQ >0492 MOVB @>83D4,*15 Load VDP register 01 CLR @>83D6 Clear screen timeout counter MOVB @>0B61,*15 MOV @>83D8,11 Fetch return B *11** Time delay* LI 12,>04E2 Loop counter DEC 12 JNE >049C B *11** Compare RO with status EQU if R0 is in area of Hbyte and Lbyte* of the data value* MOV *11+,12 CB 0,12 JL >04B0**~~Page 22: TI99/4A INTERN (Heiner Martin)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* CB 0,@>83F9 JH >04B0 CB 0,0 B *11** CLEAR keyboard scanning(status EQU, if pressed):*CLRSCN LI 12,>0024 Load CRU keyboard select LDCR @>0012,3 >00 SRC 12,7 LI 12,>0006 STCR 12,8 Fetch CRU CZC @>0036,12 Right key? JNE >04DC LI 12,>0024 CRU 2nd key LDCR @>0074,3 >03 SRC 12,7 LI 12,>0006 STCR 12,8 Fetch CRU CZC @>0036,12 Right key B *11 BackI'm guessing the time delay segment will raise issues with at least one member of this group? Edited July 11, 2017 by Airshack Quote Link to comment Share on other sites More sharing options...
matthew180 Posted July 10, 2017 Author Share Posted July 10, 2017 Yes, I am aware of the time delay built into the console routines. That is one of the main reasons to not use them for games. I'm sure this has been discussed somewhere in this thread, or certainly in the forum. Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted July 11, 2017 Share Posted July 11, 2017 Hey Lee, Did I get it all? It looks like it, but I think you will find ROM-4A-a.a99 from ROM-4A_20160317.zip (further down in that same first post) to be easier to follow because it has comments from the original TI programmers for the same code. ...lee 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.