GDMike Posted July 10, 2019 Share Posted July 10, 2019 (edited) Ahhh.i was trying to do everything on the @GADR data..lol .. meaning without putting the address in a reg. Thank you. Edited July 10, 2019 by GDMike Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted July 10, 2019 Share Posted July 10, 2019 2 hours ago, GDMike said: Ahhh.i was trying to do everything on the @GADR data..lol .. meaning without putting the address in a reg. Thank you. EQU does not create data you can change in your program. It is an assembler directive that creates a constant known only at assembly time. It just makes it easier to read your program. ...lee 1 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted July 10, 2019 Share Posted July 10, 2019 5 hours ago, GDMike said: Ahhh.i was trying to do everything on the @GADR data..lol .. meaning without putting the address in a reg. Thank you. This would also work: MOV @GADR,R1 MOV @GADR+2,R2 MOV @GADR+4,R3 ... But the resulting machine code is longer. 1 1 Quote Link to comment Share on other sites More sharing options...
intvnut Posted July 10, 2019 Share Posted July 10, 2019 1 hour ago, Asmusr said: This would also work: MOV @GADR,R1 MOV @GADR+2,R2 MOV @GADR+4,R3 ... But the resulting machine code is longer. Interestingly, despite being larger, I think it would end up being slightly faster since it avoids a memory access to update the indirect pointer, if I understood the cycle count chart correctly. So, if you just have a few items at a fixed address, the larger codesize may justify the unrolled approach. Indirect access with post-increment works better in loops though, especially loops whose trip count isn't known until run-time. 1 Quote Link to comment Share on other sites More sharing options...
+FarmerPotato Posted July 10, 2019 Share Posted July 10, 2019 7 hours ago, intvnut said: Interestingly, despite being larger, I think it would end up being slightly faster since it avoids a memory access to update the indirect pointer, if I understood the cycle count chart correctly. So, if you just have a few items at a fixed address, the larger codesize may justify the unrolled approach. Indirect access with post-increment works better in loops though, especially loops whose trip count isn't known until run-time. Also, if your registers are in PAD (e.g. >8300) then arguments in registers save a lot of cycles. Fetching the address from 8-bit memory costs 6 cycles after wait states, but fetching a register is 1 memory access. * Really fast code assuming registers in PAD LI R9,GADR STWP R0 INCT R0 * set pointer to R1.. could be LI R0,MYWS+2 or LI R0,>8302 MOV *R9+,*R0+ * copies to R1 MOV *R9+,*R0+ * copies to R2 MOV *R9+,*R0+ * copies to R3 1 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted July 12, 2019 Share Posted July 12, 2019 When I was doing a lot of programming on my TI 99/4A, I usually wrote everything that was doable in Pascal in - you get it - Pascal. There are a few things you can't access, like CRU bit etc., but most things can be done in Pascal. Then, when the program was debugged and the idea found feasible, I evaluated if there were segments of code that delayed the result too much, so that they were worth the effort of converting them to assembly language. If so, I did that, thus keeping most (hopefully) of the program in the easier-to-use Pascal and only converting the parts that really makes a difference to assembler. I've never seen the need to write programs that necessarily are only one or another language, when you can use more than one in the same application, each to its benefit. There is, for example, rarely any improvement in reading data the user is typing in by an assembly program. The user is so slow anyway, that you can do that on a higher level. But processing the data, there you may gain something from going the assembly way. 2 Quote Link to comment Share on other sites More sharing options...
Samuel Pedigo Posted July 16, 2019 Share Posted July 16, 2019 hello, new to this sub forum. I am trying to assemble the first program on this page. it is asking for the source file, object code and device name. Source code is Dsk1.save no object code, no idea what device name means. any help would be appreciated. Using classic99 1 Quote Link to comment Share on other sites More sharing options...
hhos Posted July 16, 2019 Share Posted July 16, 2019 32 minutes ago, Samuel Pedigo said: hello, new to this sub forum. I am trying to assemble the first program on this page. it is asking for the source file, object code and device name. Source code is Dsk1.save no object code, no idea what device name means. any help would be appreciated. Using classic99 The "DSK1." part of the file name is the device name. The object code is the device name and file name to which the output of the assembler will be stored. You probably have the option to send that output to an alternate device with Classic99, like "DSK2", "DSK3" or another device supported by the emulator. 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted July 16, 2019 Share Posted July 16, 2019 49 minutes ago, Samuel Pedigo said: hello, new to this sub forum. I am trying to assemble the first program on this page. it is asking for the source file, object code and device name. Source code is Dsk1.save no object code, no idea what device name means. any help would be appreciated. Using classic99 I presume you are using the Editor/Assembler cartridge. The source file is the file with the source code. If this is the first instance, it will be the name for saving the source code you type into the editor. The object code is the name of the file where you want the Assembler’s output saved. You should use all capital letters for file and device names. In Classic99, you can save Windows names such as file.obj or file.txt without regard to case or name length, but it is still a good idea to use all uppercase and less than or equal to 10 characters for the filename portion if you intend to use it on real iron, e.g., DSK1.XYZ4567890. And, of course, what @hhos said. ...lee 1 Quote Link to comment Share on other sites More sharing options...
Samuel Pedigo Posted July 19, 2019 Share Posted July 19, 2019 much appreciated. Quote Link to comment Share on other sites More sharing options...
GDMike Posted July 20, 2019 Share Posted July 20, 2019 On 7/10/2019 at 10:34 AM, FarmerPotato said: Also, if your registers are in PAD (e.g. >8300) then arguments in registers save a lot of cycles. Fetching the address from 8-bit memory costs 6 cycles after wait states, but fetching a register is 1 memory access. * Really fast code assuming registers in PAD LI R9,GADR STWP R0 INCT R0 * set pointer to R1.. could be LI R0,MYWS+2 or LI R0,>8302 MOV *R9+,*R0+ * copies to R1 MOV *R9+,*R0+ * copies to R2 MOV *R9+,*R0+ * copies to R3 Just want to say, Thank you for straightening me out on this routine! I've almost got a complete package operational. I was practically giving up on assy until you guys pointed out how to handle my BL and how to save R11. Because all the while I thought a simple RT would handle R11 on its own. Sometimes my BL would work but as my program became larger I would lose control. Big difference in how my program is running these days ..my KSCAN probs just boiled down to BL probs as mentioned above. But I'm a happy camper these days!! Quote Link to comment Share on other sites More sharing options...
+TheBF Posted July 21, 2019 Share Posted July 21, 2019 It's cool when the light comes on isn't it? ? FYI: On other CPUs the equivalent of a branch and link instruction (ex: CALL in x86 code) automatically saves the return address for you in chunk of memory call the "stack" There is a special register that holds the address of the "stack" and it automatically saves "R11" (so to speak) and moves the stack register for you. And when you do a return it automatically gets the old "R11" and moves the stack register back for you. The 9900 was created before a lot of people realized the power of a stack "built-into" the CPU however you can do it yourself by dedicating a register for the job. It's actually described in a book I have called "Software Development Handbook" by Vincent and Gill, Texas Instruments 1981. It just requires a bit of discipline on the part of the coder. In the native code compiler I am working on I decided to begin every sub-routine with SP DECT, MOV R11,*SP where SP is the register I use for the stack pointer (pick your favourite) And RT is replaced by a two instruction "pseudo-instruction" (you can do these things when you have the source code for the Assembler program) ? I call the new pseudo-instruction RET to avoid confusion and it does: MOV *SP+,R11 B *R11 By using this format I can nest sub-routines deeply and the overhead is not too bad. The other part to remember is to initialize your "SP" register to a place in memory that you reserve for that purpose. Typically 32 bytes would be enough which provides 16 levels of nesting. 1 Quote Link to comment Share on other sites More sharing options...
HOME AUTOMATION Posted July 21, 2019 Share Posted July 21, 2019 (edited) recalculating... Edited July 21, 2019 by HOME AUTOMATION needs rethinking... find notes. 1 Quote Link to comment Share on other sites More sharing options...
GDMike Posted July 21, 2019 Share Posted July 21, 2019 (edited) Oh..yes I see now. On another note, I know how to address a memory location, and increment it by the stack pointer. But how do I increase the label of memory to point to like 80 words in advance. Like if I have a label: BNK EQU >A000 And LI R15,80 And I want to add a register that contains 80 as in BNK+80. I tried Ai @BNK,R15 I need BNK to look like BNK+80 But my register will vary in number so I can't just say BNK+80. Thanks. Edit: I tried Ai *BNK,R15 typo Edited July 21, 2019 by GDMike Quote Link to comment Share on other sites More sharing options...
intvnut Posted July 21, 2019 Share Posted July 21, 2019 (edited) 3 hours ago, GDMike said: Oh..yes I see now. On another note, I know how to address a memory location, and increment it by the stack pointer. But how do I increase the label of memory to point to like 80 words in advance. Like if I have a label: BNK EQU >A000 And LI R15,80 And I want to add a register that contains 80 as in BNK+80. I tried Ai @BNK,R15 I need BNK to look like BNK+80 But my register will vary in number so I can't just say BNK+80. I believe the following would do: LI R15, 80 ; or some other value, perhaps computed into R15 AI R15, BNK ; add the address of your label to the value ; Now R15 = BNK + 80 (or BNK + whatever was in R15). Use *R15 to access whatever. Edited July 21, 2019 by intvnut clarification, fix syntax error Quote Link to comment Share on other sites More sharing options...
+FarmerPotato Posted July 21, 2019 Share Posted July 21, 2019 Here is a (tested) example of using a stack to store return addresses. R10 will be my stack pointer. The stack area is reserved by a STACK BES 2*8 directive so it is big enough for 8 words. BES is like BSS but it sets the label equal to the *end of* the reserved area (the address after). Because we are going to be counting down from STACK. BSS and BES reserve data areas, mixed with your assembled code. In a modern computer, these areas would be somewhere in memory far away from the code. In the program, START calls FOO. FOO calls BAR (3 times). BAR calls VMBW. VMBW calls SETVA. START -> FOO -> BAR -> VMBW -> SETVA Each of these "pushes" R11 on the stack, then "pops" it back to R11 for its return. (remember RT just means B *R11) Even START is pushing the R11 given to it. DEF START VDPWD EQU >8C00 VDPWA EQU >8C02 VDPRD EQU >8800 VDPSTA EQU >8802 * BES equates STACK to the address after 8*2 bytes. Opposite of BSS. STACK BES 8*2 up to 8 levels deep * initialize stack pointer START LI R10,STACK DECT R10 save given R11 on stack. it returns back to E/A MOV R11,*R10 * call something BL @FOO BL @WAIT * exit program MOV *R10+,R11 RT FOO DECT R10 save R11 on stack MOV R11,*R10 * call BAR LI R0,34 BL @BAR LI R0,144 BL @BAR LI R0,254 BL @BAR * return from FOO MOV *R10+,R11 RT BAR DECT R10 save R11 on stack MOV R11,*R10 LI R1,HELLO LI R2,HELLO# BL @VMBW * return from BAR MOV *R10+,R11 RT * the usual multiple byte write * R0 vdp address * R1 data address * R2 length VMBW DECT R10 MOV R11,*R10 ORI R0,>4000 BL @SETVA VMBW1 MOVB *R1+,@VDPWD DEC R2 JNE VMBW1 * return from VMBW MOV *R10+,R11 RT * single byte R1, multiple times * R0 vdp address * R1 byte to fill with * R2 length VSBMW DECT R10 MOV R11,*R10 ORI R0,>4000 BL @SETVA VSBMW1 MOVB R1,@VDPWD DEC R2 JNE VSBMW1 * return MOV *R10+,R11 RT * SETVA won't be calling any subroutines so it doesn't save R11 on stack SETVA SWPB R0 MOVB R0,@VDPWA SWPB R0 MOVB R0,@VDPWA RT * wait pushes and pops R11 just to make a point WAIT DECT R10 MOV R11,*R10 SETO R0 big number DEC R0 JNE $-2 i like no labels MOV *R10+,R11 RT HELLO TEXT 'HELLO' HELLO# EQU $-HELLO EVEN END 2 Quote Link to comment Share on other sites More sharing options...
HOME AUTOMATION Posted July 21, 2019 Share Posted July 21, 2019 (edited) 1 hour ago, GDMike said: Oh..yes I see now. On another note, I know how to address a memory location, and increment it by the stack pointer. But how do I increase the label of memory to point to like 80 words in advance. Like if I have a label: BNK EQU >A000 And LI R15,80 And I want to add a register that contains 80 as in BNK+80. I tried Ai @BNK,R15 I need BNK to look like BNK+80 But my register will vary in number so I can't just say BNK+80. Thanks. I think immediate operands(AI) can only be loaded into a workspace register. ADD WORDS(A) can handle two general addresses... meaning a workspace register or a symbolic address(@)... A R15,@BNK * if >A000=>00 and >A001=>00 * this would make >A000=>00 and >A001=>50 ...not sure how this equates to "80 words in advance" though. Label values are resolved at assembly time/can't be changed by the running program. Edited July 21, 2019 by HOME AUTOMATION clarity 1 Quote Link to comment Share on other sites More sharing options...
GDMike Posted July 21, 2019 Share Posted July 21, 2019 Oh, so I could do that possibly. I'll give it a try. Quote Link to comment Share on other sites More sharing options...
Asmusr Posted July 22, 2019 Share Posted July 22, 2019 Try @BNK(R15) Quote Link to comment Share on other sites More sharing options...
+TheBF Posted July 22, 2019 Share Posted July 22, 2019 18 hours ago, FarmerPotato said: Here is a (tested) example of using a stack to store return addresses. R10 will be my stack pointer. The stack area is reserved by a STACK BES 2*8 directive so it is big enough for 8 words. BES is like BSS but it sets the label equal to the *end of* the reserved area (the address after). Because we are going to be counting down from STACK. BSS and BES reserve data areas, mixed with your assembled code. In a modern computer, these areas would be somewhere in memory far away from the code. In the program, START calls FOO. FOO calls BAR (3 times). BAR calls VMBW. VMBW calls SETVA. START -> FOO -> BAR -> VMBW -> SETVA Each of these "pushes" R11 on the stack, then "pops" it back to R11 for its return. (remember RT just means B *R11) Even START is pushing the R11 given to it. DEF START VDPWD EQU >8C00 VDPWA EQU >8C02 VDPRD EQU >8800 VDPSTA EQU >8802 * BES equates STACK to the address after 8*2 bytes. Opposite of BSS. STACK BES 8*2 up to 8 levels deep * initialize stack pointer START LI R10,STACK DECT R10 save given R11 on stack. it returns back to E/A MOV R11,*R10 * call something BL @FOO BL @WAIT * exit program MOV *R10+,R11 RT FOO DECT R10 save R11 on stack MOV R11,*R10 * call BAR LI R0,34 BL @BAR LI R0,144 BL @BAR LI R0,254 BL @BAR * return from FOO MOV *R10+,R11 RT BAR DECT R10 save R11 on stack MOV R11,*R10 LI R1,HELLO LI R2,HELLO# BL @VMBW * return from BAR MOV *R10+,R11 RT * the usual multiple byte write * R0 vdp address * R1 data address * R2 length VMBW DECT R10 MOV R11,*R10 ORI R0,>4000 BL @SETVA VMBW1 MOVB *R1+,@VDPWD DEC R2 JNE VMBW1 * return from VMBW MOV *R10+,R11 RT * single byte R1, multiple times * R0 vdp address * R1 byte to fill with * R2 length VSBMW DECT R10 MOV R11,*R10 ORI R0,>4000 BL @SETVA VSBMW1 MOVB R1,@VDPWD DEC R2 JNE VSBMW1 * return MOV *R10+,R11 RT * SETVA won't be calling any subroutines so it doesn't save R11 on stack SETVA SWPB R0 MOVB R0,@VDPWA SWPB R0 MOVB R0,@VDPWA RT * wait pushes and pops R11 just to make a point WAIT DECT R10 MOV R11,*R10 SETO R0 big number DEC R0 JNE $-2 i like no labels MOV *R10+,R11 RT HELLO TEXT 'HELLO' HELLO# EQU $-HELLO EVEN END Do the new assemblers allow you to rename registers and do they support macros? With those two features you can make the stack code look very explicit. However I realize that some people want to see "behind the curtain" at all times so it's not everyone's preference. 1 Quote Link to comment Share on other sites More sharing options...
+FarmerPotato Posted July 22, 2019 Share Posted July 22, 2019 TheBF said Quote Do the new assemblers allow you to rename registers and do they support macros? With those two features you can make the stack code look very explicit. However I realize that some people want to see "behind the curtain" at all times so it's not everyone's preference. SP EQU 10 Does the job of renaming. R10 is just an equate anyway, enabled by the R option in the original 99/4 assembler. MOV *10+,11 is the eventual syntax. RAG assembler and GenAsm had macros. I see ralph xas99 (from xdt99) has macros. Here I go with xas99 macros. Untested code: * initstack .defm spinit sp equ 10 li sp,stack * push R11 onto stack .defm savert dect sp mov r11,*sp .endm * pop from stack into R11 and return .defm return mov *sp+,r11 rt .endm * generic push <arg> .defm push dect sp mov #1,*sp .endm * generic pop <arg> .defm pop mov *sp+,#1 .endm * rewritten savert, return .defm savert push r11 .endm .defm return pop r11 rt .endm * Example. * Call chain: start -> foo -> bar. * Bar is polite: it saves/restore the register it uses, R12. * That is another use for stack push/pop, given that the stack is deep enough. start: spinit pushrt bl @foo return foo: pushrt bl @bar return bar: push r12 li r12,>1300 sbo 0 pop r12 rt 1 Quote Link to comment Share on other sites More sharing options...
+mizapf Posted July 22, 2019 Share Posted July 22, 2019 The problem is that the TMS99xx processors do not have a fixed stack pointer register; that is, changing workspaces also means losing the current value of the stack pointer. I thought stack pointers were more common in earlier days, i.e. before the TMS99xx. On the other hand, the instruction set architecture of the TMS99xx goes back to the TTL-based TI990 from 1973, and the Intel 8008 was introduced just a year before. 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted July 22, 2019 Share Posted July 22, 2019 1 hour ago, mizapf said: The problem is that the TMS99xx processors do not have a fixed stack pointer register; that is, changing workspaces also means losing the current value of the stack pointer. I thought stack pointers were more common in earlier days, i.e. before the TMS99xx. On the other hand, the instruction set architecture of the TMS99xx goes back to the TTL-based TI990 from 1973, and the Intel 8008 was introduced just a year before. Yes that can be minor inconvenience, but if your BLWPing out to new workspace then you probably want that freedom no? In Forth we manage 2 stacks and when I BLWP out of Forth workspace its because I don't need a stack but I want to use registers for parameters that stay with the routine when I leave and come back to it later. Seems to work well. I haven't done much research on the topic of early CPU hardware stacks, but I know IBM 360 and earlier did not have a hardware stack. I believe it was the uP guys who saw the value early on and built stack instructions into their machines. (reference needed) I know that the Burroughs made the B5000 in the 1960s. It was a pure stack machine ie, no registers and that Chuck Moore the inventor of Forth did some early work with that machine which influenced his later work. But it was the only one in its day I think and was very different than a register machine with push/pop instructions. 1 Quote Link to comment Share on other sites More sharing options...
+mizapf Posted July 23, 2019 Share Posted July 23, 2019 What I wanted to point out is that if we want to set up something like a stack with a register as stack pointer, this does not mix well with the BLWP concept. TI should have kept such a stack pointer outside of the R0-R15 set, but it is easy in hindsight to say what one should have done. ? 1 1 Quote Link to comment Share on other sites More sharing options...
GDMike Posted July 23, 2019 Share Posted July 23, 2019 I'm also not finding a DEV folder on WHTECH. I was looking for R.A.G. source. 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.