Asmusr Posted May 16, 2020 Share Posted May 16, 2020 (edited) 18 minutes ago, Lee Stewart said: Considering the necessity of jumps, I see this as shortest: MOV R0,R0 JEQ DONE SETO R0 JLT DONE INCT R0 DONE ... ...lee I like this solution. Edited May 16, 2020 by Asmusr Quote Link to comment Share on other sites More sharing options...
intvnut Posted May 16, 2020 Share Posted May 16, 2020 (edited) 12 minutes ago, intvnut said: It's annoying MOV is 14 + 4*mem. Does ABS set Carry in a useful way for positive vs. negative numbers? It's faster than MOV for positive numbers, and uses fewer memory cycles in all cases. Answering my own question by looking at the code in MAME. void tms99xx_device::alu_abs() { // LAECO (from original word!) // O if >8000 // C is alwas reset set_status_bit(ST_OV, m_current_value == 0x8000); set_status_bit(ST_C, false); compare_and_set_lae(m_current_value, 0); if ((m_current_value & 0x8000)!=0) { m_current_value = (((~m_current_value) & 0x0000ffff) + 1) & 0xffff; pulse_clock(2); // If ABS is performed it takes one machine cycle more } else { MPC++; // skips over the next micro operation (MEMORY_WRITE) } pulse_clock(2); } It appears Carry isn't set usefully. But, it appears the other flags are set based on the original value, not the value after negation. So, you could shave a couple cycles with: ABS R0 ; 12 + 2*mem (if positive) JEQ DONE ; 8 + 1*mem (n/t) SETO R0 ; 10 + 3*mem JLT DONE ; 8 + 1*mem (n/t) INCT R0 ; 10 + 3*mem DONE ... ;------------ ; 48 + 10*mem I guess I had missed the fine print "If MSB(SA) = 0 and (SA) ≠ 0" here: Edited May 16, 2020 by intvnut Correct # of mems for ABS. 1 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted May 16, 2020 Share Posted May 16, 2020 (edited) 51 minutes ago, intvnut said: It appears Carry isn't set usefully. But, it appears the other flags are set based on the original value, not the value after negation. So, you could shave a couple cycles with: ... * R0 negative... * Lee Stewart...... MOV R0,R0 ; 14 + 4*mem JEQ DONE ; 8 + 1*mem (n/t) SETO R0 ; 10 + 3*mem JLT DONE ; 8 + 1*mem INCT R0 ; 10 + 3*mem (n/t) DONE ... ;-------------+ ; 40 + 9*mem | ;-------------+ * intvnut......... ABS R0 ; 14 + 3*mem (IF NEGATIVE) JEQ DONE ; 8 + 1*mem (n/t) SETO R0 ; 10 + 3*mem JLT DONE ; 8 + 1*mem INCT R0 ; 10 + 3*mem (n/t) DONE ... ;-------------+ ; 40 + 8*mem | ;-------------+ We are closest when R0 < 0, with yours winning by only 1*mem. But, when R0 ≥ 0, yours is better by 2 + 2*mem, so you win. ...lee Edited May 16, 2020 by Lee Stewart general corrections of timing 1 Quote Link to comment Share on other sites More sharing options...
+mizapf Posted May 16, 2020 Share Posted May 16, 2020 Ah yes, SETO does not affect the status bits. Good idea. As for ABS, the Ed/Ass manual says the instruction does not affect the Carry bit, yet the TMS9900/9995 specs say it does. But it will always be reset. Here is a test I ran on my Geneve. * * LAECOPX * FFFE 10000*** * FFFF 10000*** * 0000 00100*** * 0001 11000*** * 0002 11000*** * 7FFE 11000*** * 7FFF 11000*** * 8000 10001*** * 8001 10000*** 1 Quote Link to comment Share on other sites More sharing options...
intvnut Posted May 16, 2020 Share Posted May 16, 2020 (edited) 26 minutes ago, Lee Stewart said: * Lee Stewart...... MOV R0,R0 ; 14 + 4*mem JEQ DONE ; 8 + 1*mem (n/t) SETO R0 ; 10 + 3*mem JLT DONE ; 8 + 1*mem (n/t) INCT R0 ; 10 + 3*mem DONE ... ;-------------+ ; 50 + 12*mem | ;-------------+ * intvnut......... ABS R0 ; 14 + 3*mem (IF NEGATIVE) JEQ DONE ; 8 + 1*mem (n/t) SETO R0 ; 10 + 3*mem JLT DONE ; 8 + 1*mem (n/t) INCT R0 ; 10 + 3*mem DONE ... ;-------------+ ; 50 + 11*mem | ;-------------+ Your worst case scenario with R0 < 0 is 1 memory access less than mine, so you win. ...lee I only got there with your help, naturally. You shaved more cycles off mine than I did off yours. For the R0 < 0 case, the JLT gets taken, so we never actually end up with 50 + 11*mem. That case ends up being 42 + 8*mem, since we skip the INCT, but the JLT becomes 10 cycles. So, the final cycle counts end up being: R0 > 0: 48 + 10*mem R0 = 0: 22 + 3*mem (ABS is 12+2*mem, JEQ is 10+1*mem) R0 < 0: 42 + 8*mem (ABS is 14+3*mem, JLT becomes 10+1*mem) To put it in perspective, a single SRA R0, 15 is 42 + 3*mem. In zero wait-state memory, this is only slightly slower than that shift in the worst case. Nice! Edited May 16, 2020 by intvnut Clarify we never actually have 11*mem, because JLT taken when R0<0. 1 Quote Link to comment Share on other sites More sharing options...
+mizapf Posted May 16, 2020 Share Posted May 16, 2020 1 hour ago, intvnut said: Answering my own question by looking at the code in MAME. Yes, that code manifests my findings as shown above in the test value list. The list was output from a program that ran on my real Geneve. Quote Link to comment Share on other sites More sharing options...
hhos Posted May 16, 2020 Share Posted May 16, 2020 36 minutes ago, mizapf said: Ah yes, SETO does not affect the status bits. Good idea. As for ABS, the Ed/Ass manual says the instruction does not affect the Carry bit, yet the TMS9900/9995 specs say it does. But it will always be reset. Here is a test I ran on my Geneve. * * LAECOPX * FFFE 10000*** * FFFF 10000*** * 0000 00100*** * 0001 11000*** * 0002 11000*** * 7FFE 11000*** * 7FFF 11000*** * 8000 10001*** * 8001 10000*** I'm mildly confused here. Neither the "JLT" or "JEQ" depend on the carry flag to operate, so it does not effect the assembly code presented here. Only the "JOC" and "JNC" test the carry flag to make a decision. The examples you've presented here seem to indicate that the TMS900/9995 docs are correct. Assuming that the carry flag was set before executing the ABS, in each case, that seems to be the case. Is that what this means? Quote Link to comment Share on other sites More sharing options...
+mizapf Posted May 16, 2020 Share Posted May 16, 2020 Yes, my test was done in two passes; first setting the status register to 0000, then again setting the status register to FFFF. The 9995 has a helpful command: LST. The asterisks show the bits that are not affected by the operation. It is easy to see that C=0 for every value presented to ABS: - If the value is nonnegative, it remains unchanged. - If the value is negative, it is replaced by its two's complement. NEG on 0 implies C=1, because the ALU obviously calculates the one's complement first, then increments it by 1. This increment may cause a carry. As you see, it only occurs for NEG 0. But for 0, ABS does nothing. 1 Quote Link to comment Share on other sites More sharing options...
HOME AUTOMATION Posted May 16, 2020 Share Posted May 16, 2020 There can only be one "ZERO". Quote Link to comment Share on other sites More sharing options...
matthew180 Posted May 16, 2020 Author Share Posted May 16, 2020 1 hour ago, mizapf said: As for ABS, the Ed/Ass manual says the instruction does not affect the Carry bit, yet the TMS9900/9995 specs say it does. In the datasheet, I see the ABS instruction listed in the ST3 (carry) row of the status-flags table, where the condition is "if CARRY OUT = 1". However, absolute value operation is performed on negative numbers *only* (there is a pre-test of the value, which is why values that are already positive are not written), and is calculated by inverting the the bits and adding 1 (this is the same operation as NEG). As soon as you invert the negative number, the MSbit becomes zero, so it is not possible to have a carry out. ABS is probably listed as affecting the carry bit because the instruction will cause it to change, i.e. be set to 0 if it was previously a 1. Quote Link to comment Share on other sites More sharing options...
intvnut Posted May 16, 2020 Share Posted May 16, 2020 25 minutes ago, mizapf said: Yes, my test was done in two passes; first setting the status register to 0000, then again setting the status register to FFFF. The 9995 has a helpful command: LST. The asterisks show the bits that are not affected by the operation. It is easy to see that C=0 for every value presented to ABS: - If the value is nonnegative, it remains unchanged. - If the value is negative, it is replaced by its two's complement. NEG on 0 implies C=1, because the ALU obviously calculates the one's complement first, then increments it by 1. This increment may cause a carry. As you see, it only occurs for NEG 0. But for 0, ABS does nothing. The unsigned vs. signed greater than flags (L> and A>) on TMS9900 family always throw me off when I return for a visit. On most other CPUs I've encountered, there isn't a separate unsigned greater-than flag. Rather, the carry bit serves that purpose. If you treat a 2's complement subtract as merely "invert and inject a carry," then the carry will be clear after A - B when A < B (unsigned), and set otherwise. That gives you "unsigned less than" and "unsigned greater than/equal" just looking at the carry bit. Add in the EQ bit and you'd have "unsigned less than/equal" and "unsigned greater than." You can get by with four flag bits rather than five. In general, the TMS9900 family is weird about their carry bit compared to other architectures I've worked with. You can set it or clear it, but it's difficult to consume it without a branch. You can't use it directly for extended precision adds, subtracts, or shifts. You gotta branch, or do a funky dance with STST. It makes sense that ABS always zeros it out given how it's implemented. Without the L> bit, I could see using the carry flag to indicate whether ABS had modified the number. 31 minutes ago, HOME AUTOMATION said: There can only be one "ZERO". Not in floating point or 1's complement! I'm pretty sure that includes the 99/4A's weird Radix-100 floating point. 1 Quote Link to comment Share on other sites More sharing options...
+mizapf Posted May 16, 2020 Share Posted May 16, 2020 (edited) My Linear Algebra professor in my first semester once said, "the zeros cannot hide". (From the math point of view, it meant that a zero vector makes every family of vectors linearly dependent. Of course, there was another subtle meaning concerning the audience...) Edited May 16, 2020 by mizapf Oh man, could you possibly agree on null or zero in English? 1 1 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted May 18, 2020 Share Posted May 18, 2020 nil? Quote Link to comment Share on other sites More sharing options...
Airshack Posted June 25, 2020 Share Posted June 25, 2020 Hello All! I'm having trouble understanding the E/A Manual's description of the SAVE Utility in section 24.5 "SAVE UTILITY." Of course there's no example even though page 421 ends with 80% of the page blank. Why? The goal here is to convert my EA3 loadable program to EA5, so it can then be converted to a cartridge binary .bin via Fred Kaal's Module Creator. EA page 420: "Your program must contain DEF SFIRST,SLAST,SLOAD...." SFIRST -- How exactly do I make this a pointer to the start of my program? Right now the start of my program has a label 'ENK" which is the program name. SLOAD -- How do I make this a pointer for "where the saved program is to be located" on a cartridge binary file? Assuming this is going to be a GROM address? SLAST -- I again assume I'll just make this the label for the END directive in my program? This part seems clear from the EA manual, I think. It is my understanding an EA5 option runnable file is needed for input into Fred's Module Creator? Perhaps it is best to use Matthew's simplified code outline to illustrate the solution. How may we use SFIRST,SLAST,SLOAD in the example below? DEF START * VDP Memory Map * VDPRD EQU >8800 * VDP read data VDPSTA EQU >8802 * VDP status VDPWD EQU >8C00 * VDP write data VDPWA EQU >8C02 * VDP set read/write address * Workspace WRKSP EQU >8300 * Workspace R0LB EQU WRKSP+1 * R0 low byte reqd for VDP routines * Program execution starts here START LIMI 0 LWPI WRKSP LIMI 2 LP9999 JMP LP9999 END As always, I appreciate your inputs and assistance. - James Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted June 25, 2020 Share Posted June 25, 2020 *DON'T NEED DEF *SFIRST IS THE FIRST LINE OF CODE IN THE PROGRAM AND IT MUST BE ACTUAL CODE, NOT DATA OR TEXT SFIRST SLOAD LIMI 0 LWPI WRKSP LIMI 2 LP9999 JMP LP9999 SLAST END Quote Link to comment Share on other sites More sharing options...
+mizapf Posted June 25, 2020 Share Posted June 25, 2020 But the SAVE utility uses REFs for SFIRST, SLOAD, SLAST, so I'd expect you have to DEF them. (Ah, this is possibly ambiguous ... you say you don't need another DEF for your program.) 1 Quote Link to comment Share on other sites More sharing options...
Airshack Posted June 25, 2020 Share Posted June 25, 2020 59 minutes ago, mizapf said: But the SAVE utility uses REFs for SFIRST, SLOAD, SLAST, so I'd expect you have to DEF them. (Ah, this is possibly ambiguous ... you say you don't need another DEF for your program.) DEF-ing them worked out. Quote Link to comment Share on other sites More sharing options...
Airshack Posted June 25, 2020 Share Posted June 25, 2020 3 hours ago, senior_falcon said: *DON'T NEED DEF *SFIRST IS THE FIRST LINE OF CODE IN THE PROGRAM AND IT MUST BE ACTUAL CODE, NOT DATA OR TEXT SFIRST SLOAD LIMI 0 LWPI WRKSP LIMI 2 LP9999 JMP LP9999 SLAST END This worked...kind of. The SAVE program launched and it did create three files: DSK4.ENKA, DSK4.ENKB, and DSK4.ENKC. I got all excited when I used EA Option-5 to launch DSK4.ENKA. Everything ran as expected via the EA Option-5 Prompt. Upon restarting Classic99 and re-selecting EA Option-5, the program launched but seemed to be graphically distorted and didn't seem to run properly. Here's how it should look upon launch: Here's how it looks after I reset Classic99 and selected EA Option-5, entered DSK4.ENKA, then pressed enter. Is it possible EA5 is only launching the first 8K segment named "DSK4.ENKA?" The program is non-responsive and just hangs. Strange because it runs fine via the EA5 option immediately after using the SAVE program. Only after resetting Classic99 will EA5 with Option-5 result in the black screen of death above? I'm doing something wrong and hope it is obvious to the experts. Quote Link to comment Share on other sites More sharing options...
apersson850 Posted June 25, 2020 Share Posted June 25, 2020 When doing Load and run from EA, you have a particular definition of VDP RAM and some other configurations. They are still there when you execute the save utility. But the next time, you can't make any assumptions about anything in the computer. You have to define everything yourself. Like VDP and character definition setup, for example. That's probably what you haven't done. Quote Link to comment Share on other sites More sharing options...
Airshack Posted June 26, 2020 Share Posted June 26, 2020 4 hours ago, apersson850 said: You have to define everything yourself. Like VDP and character definition setup, for example. That's probably what you haven't done. Ugh! So close yet so far. Where do I begin to “define everything“ myself? Sounds daunting. 1 Quote Link to comment Share on other sites More sharing options...
Airshack Posted June 26, 2020 Share Posted June 26, 2020 On 1/23/2020 at 7:06 PM, PeteE said: Here's an example cartridge header... AORG >6000 ; Cartridge header in all banks HEADER BYTE >AA ; Standard header BYTE >01 ; Version number 1 BYTE >01 ; Number of programs (optional) BYTE >00 ; Reserved (for FG99 this can be G,R,or X) DATA >0000 ; Pointer to power-up list DATA PRGLST ; Pointer to program list DATA >0000 ; Pointer to DSR list DATA >0000 ; Pointer to subprogram list PRGLST DATA >0000 ; Next program list entry DATA START ; Program address BYTE CRTNME-CRTNM ; Length of name CRTNM TEXT 'CARTRIDGE NAME' CRTNME EVEN START ; Your program starts here LWPI WRKSP ; Load the workspace pointer to fast RAM LIMI 0 ; Interrupts off I believe the answers I need are in the code example from PeteE above. Post #1029, this thread: Others have replied that this is very clear and to the point. I'll continue to try to understand it. Questions: 1. Number of programs optional? 2. G,R,X stand for? 3. DATA >0000 = pointer to power-up list, DSR list, and subprogram list? I'm lost. Maybe I don't have to know what's going on here to get it to work? I'll just experiment a little with cutting and pasting this into my code? Hopefully heading in the right direction. Will this serve @apersson850's suggestion to "define everything myself?" - James Quote Link to comment Share on other sites More sharing options...
PeteE Posted June 26, 2020 Share Posted June 26, 2020 9 minutes ago, Airshack said: Questions: 1. Number of programs optional? 2. G,R,X stand for? 3. DATA >0000 = pointer to power-up list, DSR list, and subprogram list? I'm lost. Maybe I don't have to know what's going on here to get it to work? I'll just experiment a little with cutting and pasting this into my code? James, you don't need the cartridge header for EA/5 programs. I believe the suggestion to "define everything yourself" means to set the VDP registers yourself, you can check by comparing the VDP registers for your two versions in Classic99 debugger window. The VDP registers are labeled "VDP0" to "VDP7". If you need help setting the registers, I can share the code I use. But to answer your questions: Some cartridges may not have programs, only GROM data, or DSR or subroutine lists. From the FinalGrom99 page: [R] RAM Mode: provides up to 512 KB of ROM and 512 KB of RAM [G] GRAM Mode: turns occupied GROM into writable GRAM [X] RAM/GRAM Mode: provides both RAM and GRAM Those are linked lists, and >0000 is the NULL pointer which indicates the list is empty, or the end of the list if used in the final next list entry. If there were more than one PRGLST entry, each next list entry pointer is followed by the BIOS to enumerate the list of programs, and stops when it finds >0000. Quote Link to comment Share on other sites More sharing options...
Tursi Posted June 26, 2020 Share Posted June 26, 2020 I didn't see anyone else mention it, so maybe they already know you aren't using them, but if you are, the most common reason that coverting from EA#3 to EA#5 fails is that you don't have the Editor/Assembler routines in memory when you start from EA#5. So when you BLWP @VSBW, for instance, there IS no VSBW, and you jump off to empty code and crash. The easy fix is just to load the EA routines as an extra source file, so you have (for instance) your three program files plus one to low RAM for the utilities. There are a few copies of a pre-made file for that floating around the forums. If it's not that, then yeah, as noted above you have made some assumption about the state of the hardware. Make sure your program sets every register in the VDP and initializes any memory it uses to appropriate values (ie: don't assume they are zero). You should probably also load the character set yourself - but in fairness THAT doesn't usually become an issue till you move to cartridge. Usually it's the VDP setup. Check the registers in the Classic99 debugger and you should quickly see if they are different. 1 Quote Link to comment Share on other sites More sharing options...
GDMike Posted June 26, 2020 Share Posted June 26, 2020 In my program, I copy a couple k of code at >2000 at boot, then temporarily place it in my supercart, then I put it back in >2000 and those ea commands work. Quote Link to comment Share on other sites More sharing options...
apersson850 Posted June 27, 2020 Share Posted June 27, 2020 Yes, I was mainly referring to the VDP setup. But if you rely on the EA support routines, you have to manage them too. 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.