Jump to content

It's Sparky

New Members
  • Posts

    5
  • Joined

  • Last visited

About It's Sparky

  • Birthday August 23

Profile Information

  • Gender
    Male
  • Location
    Nijmegen, the Netherlands
  • Interests
    AI, Compiler Construction, Natural Language Processing, Hardware.
  • Currently Playing
    Half life 2, episode 2 (again!)
  • Playing Next
    Portal

It's Sparky's Achievements

Combat Commando

Combat Commando (1/9)

2

Reputation

  1. While writing some code to demonstrate 9900 assembly to my students I ran into the situation where my code needs to be readable and understandable. One of the easiest ways to accomplish this is to use functions (routines/subroutines) to break up and re-use code. Of course, the use of functions involves a choice in calling conventions. On the 9900 we have several possibilities, for example those based on BLWP/RTWP or BL/B *R11. Apart from calling to and returning from a routine a decision should be made about passing of parameters. Inspired by other architectures (for example Sun/Sparc) I created a calling convention which is easy to use and has a lot of advantages: · Recursion is possible · Routine and subroutine pass parameters through registers · Each incarnation has its own free registers, no more implicit saving/restoring registers · Each incarnation has a number of scratch registers · Calling sequence is just 2 words · No implicit stack administration needed in routines The basic idea behind this convention is to use overlapping workspaces: the routine and the subroutine share half of their workspace. So, there is a ‘stack of workspaces’ (growing from hi addresses to lo addresses). Each incarnation will use 16 bytes. Although this seems to be a lot, the total amount of memory used for the system depends on the depth of nested subroutines, which is in most applications really limited. The following table illustrates the idea: When a function needs temporary register storage it can freely use R5, R6 and R7. However, their content will be destroyed as soon as the function calls another function. Parameters to the function are in R8 up to and including R12. Free (and persistent through calls) registers are R0 up to and including R4 which are also used to pass parameters to a subfunction. To call a function, I wrote some code that handles all the administration through the use of the XOP instruction. So my assembler will rewrite: call routine to xop routine,1 Which takes the same amount of instruction space as BL (2 words) The routine itself will return to the caller using a standard RTWP instruction. Note that you can manipulate R15 before returning to signify an error condition (for example by setting the parity flag). The XOP1 handler looks like this (a nice puzzle to see what is going on) xop1: mov r13,10(r13) mov r14,12(r13) mov r15,14(r13) mov r11,r14 ai r13,-16 rtwp Funny that the ending rtwp is actually calling the routine! Wonder what you think of this.
  2. Hope you guys don't mind digging up an old but interesting post by matthew. Just as matthew describes, thinking about the meaning of the classic B *R11 instruction is contra intuitive. Funny detail, B R11 is a completely valid instruction which will 'jump into your workspace, executing your registers (starting at R11)', which has obviously limited usability. Maybe TI developers realised the need for a 'real' indirect jump when they created the BIND (Branch INDirect) instruction on the 99000. This powerful instruction can be used to jump to a routine from a jump table (using indexed addressing) or even returning from a subroutine call where the return address is on a 'stack': BIND *R10+ The introduction of this BIND instruction was paired with the BLSK (Branch and Link StacK) instruction. BLSK R10 is comparable with the standard BL instruction, but instead of storing the return address in R11, it will be stored in the address pointed by R10 (the 'stack' pointer) that will be pre-decremented by 2 (so before the storing is done). A perfect pair of instructions when you want to implement stack-like behaviour. Maybe it is a cool idea to ramble about enhancing the 9900. Which instructions would you like to see? Maybe a super fast register set on chip? Of course it depends on your taste of your way of programming. Still love the original instruction set, don't get me wrong! Franc
  3. Yes I think you are right. The increase of the number registers (from 8 on the pdp11 to 16 on the 9900) costs a lot of instruction space. So the TI developers dropped the auto-decrement indirect addressing mode, and the nice orthogonal immediate addressing mode (and created seperate instructions for that). Maybe the tst(b) instruction was also deleted to make things fit. Still I feel uncomfortable to use an instruction that writes back to memory without a real reason. Maybe that is just me!
  4. yes, I think so. That is why I use my own assembler! I needed an assembler/linker/loader toolset that is able to do split I/D for a future 99105 project (this will increase your effective memory to 128K since instructions and data are separated). While creating it, I added some nice features, like local (reusable) labels to prevent name space cluttering: li r1,buffer 1: movb *r1+,r2 jne 1b Automatic byte and word literal generation (done by the loader in text or data segment): space equ ' ' ... cb *r1,=b(space) Literals with the same value will be mapped to the same address. Automatic long/short jump expansion: c r1,r2 bjne equal This will automatically expand to a (short) jne, or a skipping jeq and a branch if the destination label is out of reach. My loader generates mini-memory.ram files that are compatible with ti99sim, so I can directly run my programs in the simulator
  5. Hey Guys/Girls I really love this topic. Different authors, different angles on the same topic. I fell in love with the 9900 years ago (yeah I'm too old to specifically write the exact number) and I am happy I'm not the only one. Really like to contribute some stuff here, and got some questions too. Maybe start with a simple one: Let's assume I have r1 pointing to a byte in memory, and I want to test it for being zero (the byte that is!). What would be the shortest (fastest) way of doing that. A possible solution might be something like: MOVB *R1,R2 JEQ ITSZERO This will (obviously) store the byte in R2 and set the flags. Apart from wasting a register (which is not that bad) I'm not really interested in writing the byte (back) to memory and don't want to spoil the cycles involved with it. The pdp11 (a mini/micro processor similar to the 9900) had an instruction called tstb which actually tested a byte (compared it to zero): tstb (r1) beq itszero Wondering if someone can shed some light on this! PS: Some funny details: the pdp11 used the term 'branch' for short jumps (the 9900 calls them 'jumps') and jumps for long jumps (the 9900 calls them branches)! Furthermore (r1) means *R1, just a different notation. Way back when I first wrote my TI99/4 assembly program I loved the capitals, right now I write my 9900 programs in lowercase!
×
×
  • Create New...