Jump to content
IGNORED

Assembly on the 99/4A


matthew180

Recommended Posts

 

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.

Link to comment
Share on other sites

...

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 by matthew180
  • Like 2
Link to comment
Share on other sites

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 by Airshack
  • Like 1
Link to comment
Share on other sites

Beware self-modifying code... I often do things like this versus using a stack... icon_wink.gif

 

SUB1   MOV  R11,@SUB1RT+2
       ...
SUB1RT B  @0

Matthew - this is an awesome thread you've got running icon_smile.gif 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 icon_smile.gif

 

 

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 ???

Link to comment
Share on other sites

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 by Lee Stewart
  • Like 1
Link to comment
Share on other sites

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 by Airshack
Link to comment
Share on other sites

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!

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

 

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. :dunce:

 

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.

  • Like 1
Link to comment
Share on other sites

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.

  • Like 1
Link to comment
Share on other sites

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".

Link to comment
Share on other sites

 

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.

Link to comment
Share on other sites

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.

  • Like 1
Link to comment
Share on other sites

...

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.

  • Like 1
Link to comment
Share on other sites

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 by Airshack
Link to comment
Share on other sites

 

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 by Airshack
Link to comment
Share on other sites

My .mag file? Okay, head over to the Berly thread, about page 15 or so and download the map *you* posted. icon_winking.gif 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 by Airshack
Link to comment
Share on other sites

 

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               Back
I'm guessing the time delay segment will raise issues with at least one member of this group? Edited by Airshack
Link to comment
Share on other sites

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

  • Like 1
Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...