Tursi Posted February 20 Share Posted February 20 It is a tricky one. I found three cases in Classic99 over the years that led to understanding the sequence (which matches what you have there, @Asmusr // For operations like MOV R3,*R3+ (from Corcomp's memory test), the address // value is written to the same address before the increment occurs. // There are even trickier cases in the console like MOV *R3+,@>0008(R3), // where the post-increment happens before the destination address calculation. // Then in 2021 we found MOV *R0+,R1, where R0 points to itself, and found // that the increment happens before *R0 is fetched for the move. 5 1 Quote Link to comment Share on other sites More sharing options...
+mizapf Posted February 20 Share Posted February 20 The "9900 Family Systems Design Handbook" says: Workspace Register Indirect Auto-Increment (Word operand): CYCLE TYPE ADDRESS BUS DATA BUS 1 Memory read Register address Register contents 2 ALU Unchanged Source data register (internal) 3 ALU Unchanged Source data register (internal) 4 Memory write Register address Register contents + 2 5 Memory read Register contents Operand This is independent of the position of the *Rx+, whether in source or destination position. For that reason, this procedure is called "data derivation sequence" - it always fetches the operand. The actual instruction then needs to do a memory write for the destination operand: For A, AB, MOV etc: CYCLE TYPE ADDRESS BUS DATA BUS 1 Memory read Program counter Instruction 2 ALU No change Source data register (internal) Ns Data derivation sequence (source) 3+Ns ALU No change Source data register (internal) Nd Data derivation sequence (destination) 4+Ns+Nd ALU No change Source data register (internal) 5+Ns+Nd Memory write Destination address Result So the DDS (source) puts 8300 (as the address of R0) on the address bus, gets the value (8300) from there; in cycle 4 it writes the value+2 (8302) to that memory location. From the initially loaded address (8300) it then gets the value 8302 (incremented during cycle 4). [@8300=8302, SD=8302] The DDS (destination) puts 8300 on the address bus, gets 8302 from there, writes 8304 to 8300. Then it fetches some unknown value from 8302 to be discarded. The MOV operation finally writes to 8302 the value gained in the DDS (source), which is 8302. So R0(8300) = 8304, R1(8302) = 8302. By the way, the TMS9995 replaces the DDS with an ADS = "address derivation sequence", and it does not fetch the data at that location anymore. 5 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 21 Share Posted February 21 I was running out when I saw the post and replied in haste. I missed the other register part of the question. Homer Simpson is alive and well here in the great white north. So is this considered to be a bug, a feature or a side-effect? Quote Link to comment Share on other sites More sharing options...
+dhe Posted February 21 Share Posted February 21 More EA, I like the flexibility this approach provides for passing variables. But, I think there is a bug. But I think there is another bug in the EA manual. I think the second MOV (Y Value) also needs to do an increment of R11. 2 Quote Link to comment Share on other sites More sharing options...
+mizapf Posted February 21 Share Posted February 21 11 hours ago, TheBF said: I was running out when I saw the post and replied in haste. I missed the other register part of the question. Homer Simpson is alive and well here in the great white north. So is this considered to be a bug, a feature or a side-effect? You mean the *R0+,*R0+? This is normal behaviour as specified, but somewhat difficult to grasp intuitively, because you really need to know the sequence of actions inside the CPU. 2 1 Quote Link to comment Share on other sites More sharing options...
HOME AUTOMATION Posted February 21 Share Posted February 21 I'm guessing that Dan's, considering that with only one auto-inc, the RETURN address will point to the second DATA word from the calling program. Looks like it needs in INCT. But the chances that the data will be a valid instruction seem small, so might work ...mostly. 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted February 21 Share Posted February 21 3 hours ago, dhe said: More EA, I like the flexibility this approach provides for passing variables. But, I think there is a bug. But I think there is another bug in the EA manual. I think the second MOV (Y Value) also needs to do an increment of R11. You are correct. Otherwise, “DATA 1804” will almost always bypass the next 10 bytes because it translates to a JOC instruction that jumps to 4 words (8 bytes) beyond the MOV. The reason it will almost always take the jump is that the carry flag from the “S 2,0” instruction is always set except when R0 goes from 0 to a negative result. There is another bug: “MOV 0,RESULT” should be “MOV 0,@RESULT”. ...lee 4 Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted February 21 Share Posted February 21 3 hours ago, Lee Stewart said: You are correct. Otherwise, “DATA 1804” will almost always bypass the next 10 bytes because it translates to a JOC instruction that jumps to 4 words (8 bytes) beyond the MOV. The reason it will almost always take the jump is that the carry flag from the “S 2,0” instruction is always set except when R0 goes from 0 to a negative result. There is another bug: “MOV 0,RESULT” should be “MOV 0,@RESULT”. ...lee I never thought of passing data to subroutines that way. The learning never stops... 1 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted February 21 Share Posted February 21 (edited) Isn't there some principle saying that it's bad practice to mix code and data? The closest I could find was the Harward architecture, but that's not exactly it. Anyway, that's what I always try to do, so I wouldn't dump my parameter values in between my subroutine calls. Edited February 21 by Asmusr Quote Link to comment Share on other sites More sharing options...
apersson850 Posted February 21 Share Posted February 21 As a general thing I'd concur. But in this case it's more or less a code thing. You could imagine that the difference between BL @DSRLNK DATA 8 and BL @DSRLNK DATA 10 could equally well be described by BL @DSRLNK vs. BL @SUBLNK or something similar. Now instead of calling two different labels to do (almost) the same thing, you call the same label with different handling instructions stored as data behind the call. You can do the same trick when you use BLWP, but in that case you access data with MOV *R14+. If you want to reach parameters in the caller's workspace, you index through R13. MOV @6(R13) will fetch the content of R3 in the caller's workspace. 1 Quote Link to comment Share on other sites More sharing options...
Stuart Posted February 21 Share Posted February 21 Another favourite I've seen is to manipulate R11 to use different return addresses from a routine, for example: BL @CALC DATA >AAAA *Return address if result is zero. DATA >BBBB *Return address if result is positive. DATA >CCCC *Return address if result is negative. 1 Quote Link to comment Share on other sites More sharing options...
hhos Posted February 21 Share Posted February 21 3 hours ago, Vorticon said: I never thought of passing data to subroutines that way. The learning never stops... BLWP @>005A(9) past eof: expand file DATA >D800 save R0, R1, R3, R4 MOV 0,3 desired sector offset BL @A4658 call subroutine DATA A4964 append enough sectors to reach offset in R3 BLWP @>005A(9) DATA >D801 restore R0, R1, R3, R4 This is from the disk controller DSR ROM. Every instance of BLWP @>005A(9) that I checked is followed by a DATA directive. If I remember right there are 5? services that this routine handles? One of them is store(save) registers, another is restore registers. There are 203 instances of BLWP @>005A(9) in the dc.ROM.asm source file. I'd say it's a fairly common practice.😃 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted February 21 Share Posted February 21 16 minutes ago, hhos said: BLWP @>005A(9) past eof: expand file DATA >D800 save R0, R1, R3, R4 MOV 0,3 desired sector offset BL @A4658 call subroutine DATA A4964 append enough sectors to reach offset in R3 BLWP @>005A(9) DATA >D801 restore R0, R1, R3, R4 This is from the disk controller DSR ROM. Every instance of BLWP @>005A(9) that I checked is followed by a DATA directive. If I remember right there are 5? services that this routine handles? One of them is store(save) registers, another is restore registers. There are 203 instances of BLWP @>005A(9) in the dc.ROM.asm source file. I'd say it's a fairly common practice.😃 So that is effectively co-opting the return function to become caller. It's clever, but it makes my old Pascal text books quiver on the shelf. It also makes me wonder if it is really the best way to handle these situations or was it an "in house" coding practice that was started by someone in the early days at TI and then just carried on by tradition. What do the experts here think? 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted February 21 Share Posted February 21 2 hours ago, TheBF said: So that is effectively co-opting the return function to become caller. It's clever, but it makes my old Pascal text books quiver on the shelf. It also makes me wonder if it is really the best way to handle these situations or was it an "in house" coding practice that was started by someone in the early days at TI and then just carried on by tradition. What do the experts here think? Not presuming to be an expert, but, considering that there is only free space of 158 bytes at the end of the DSR ROM, I would guess conserving space was the prime objective. ...lee 1 Quote Link to comment Share on other sites More sharing options...
+InsaneMultitasker Posted February 22 Share Posted February 22 The disassembled code is usually more difficult to read and follow than the source code. For the latter, you would typically expect "descriptive" labels, not addresses or data, to help you understand the code. And the DATA statements after a BL or BLWP make for more challenging, iterative disassembly work. 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted February 22 Share Posted February 22 3 hours ago, InsaneMultitasker said: And the DATA statements after a BL or BLWP make for more challenging, iterative disassembly work. Especially when a variable number of DATA statements is possible for the same routine, as is the case in XB ROMs. ...lee Quote Link to comment Share on other sites More sharing options...
JasonACT Posted February 22 Share Posted February 22 15 hours ago, Asmusr said: Isn't there some principle saying that it's bad practice to mix code and data? The closest I could find was the Harward architecture, but that's not exactly it. Anyway, that's what I always try to do, so I wouldn't dump my parameter values in between my subroutine calls. Well, maybe. IBM mainframes have many assembler macros (and I mean heaps, for their macro assembler, making it a much higher level language than you'd expect) that embed data next to / following the code for "non rent" code generation (re-entrant code is typically more complex, where old macros may have, at runtime, written to nearby RAM for conditional data - and as time progressed, you could specify additional options in the standard macros to avoid it)... I don't want to be a party pooper, but TI didn't invent any of this stuff - it's been there since the 1960's, and a lot of what I've seen with the TI was copied from them. The reason? IBM mainframes have a 4KB offset system in their machine code instructions, so you optimise data access around the code you're executing using a single base register. You really can't avoid it on that hardware, compared to much newer architectures that have still not killed off that platform... Despite over 30 years of attempts to do so. ARM, that's smarter still, instructions that execute (or not) depending on the status register set by previously executed instructions - avoiding conditional jumps. 1 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted February 22 Share Posted February 22 For the TI, I've used macros in the assembler for small stuff. For more complex tasks, I ususally returned to Pascal and did them there, unless they still were speed-critical or needed to do something not doable from Pascal. Quote Link to comment Share on other sites More sharing options...
JasonACT Posted February 22 Share Posted February 22 15 minutes ago, apersson850 said: something not doable from Pascal. No such thing, at least, that's what I told myself in 1989 having mastered Turbo Pascal 3. 1 1 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted February 22 Share Posted February 22 Turbo Pascal 3 doesn't run on the TI 99/4A. A more detailed expression would be "something not doable from UCSD Pascal on the TI 99/4A". You can do a lot with it as it is, like creating peek and poke on Pascal level, even access the VDP RAM directly from Pascal. But if you need to involve CRU IO, then there is no way it can be done from Pascal level, as there are no p-codes that handle that kind of IO. Quote Link to comment Share on other sites More sharing options...
Tursi Posted February 22 Share Posted February 22 On 2/21/2024 at 12:10 PM, Asmusr said: Isn't there some principle saying that it's bad practice to mix code and data? The closest I could find was the Harward architecture, but that's not exactly it. Anyway, that's what I always try to do, so I wouldn't dump my parameter values in between my subroutine calls. If you're writing in assembly language you're already breaking most of the so-called 'good practices' Don't think of data after a BL as data, though, think of it more like arguments to a function call. 3 1 Quote Link to comment Share on other sites More sharing options...
Tursi Posted February 22 Share Posted February 22 On 2/21/2024 at 12:35 PM, Stuart said: Another favourite I've seen is to manipulate R11 to use different return addresses from a routine, for example: BL @CALC DATA >AAAA *Return address if result is zero. DATA >BBBB *Return address if result is positive. DATA >CCCC *Return address if result is negative. That one I've never seen. That would take a moment to figure out if you'd never seen it before. Quote Link to comment Share on other sites More sharing options...
retrodroid Posted February 22 Share Posted February 22 (edited) 16 hours ago, Tursi said: If you're writing in assembly language you're already breaking most of the so-called 'good practices' Don't think of data after a BL as data, though, think of it more like arguments to a function call. That's one of the things I love about it! I have to say, after coding "properly" for some many years it took some real disciple to stop myself from trying to abstract functions in assembly, it was never worth the effort and usually became impractical due to running out of registers or stack space. Edited February 23 by retrodroid Quote Link to comment Share on other sites More sharing options...
+FarmerPotato Posted February 23 Share Posted February 23 3 hours ago, Tursi said: That one I've never seen. That would take a moment to figure out if you'd never seen it before. I haven't come across something like that. It's a case statement. Much simpler, but in TI books, I have seen a lot of: BL @MOVPAC DATA ERROR if the routine is successful it does INCT R11 but if not MOV *R11+,R11 RT Quote Link to comment Share on other sites More sharing options...
Asmusr Posted February 23 Share Posted February 23 7 hours ago, Tursi said: If you're writing in assembly language you're already breaking most of the so-called 'good practices' That's a pretty bold claim without any evidence to back it up. 🙂 2 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.