Airshack Posted January 23, 2020 Share Posted January 23, 2020 1 hour ago, PeteE said: To use the scratchpad for data, I use a group of EQUates to define address of variables, leaving room for the register workspace, and calculate the offsets manually by adding the size of the previous item to the current offset: WRKSP EQU >8300 ; workspace at top of scratchpad PLAYER EQU WRKSP+32 ; 32 is the size of WRKSP, player is 2 bytes ENEMY EQU WRKSP+34 ; 2 bytes after PLAYER, enemy is 2 bytes KEYLOC EQU WRKSP+36 ; 2 bytes after ENEMY, keyloc is 2 bytes ETC Keep in mind the scratchpad is only 256 bytes, so the offset cannot exceed 255... and you either need to avoid using scratchpad data used by the ISR, or keep interrupts off (LIMI 0) for the duration of your program. Thanks @PeteE. I’m familiar with the scratchpad and EQUates. I do like the way you’re using offsets vs actual addresses, as I’m using. Your method makes it easy to maintain available scratchpad awareness without doing any Hex math. As for the ISR, I’ve been convinced to avoid that for game programming with LIMI 0 as per previous posts on this same thread. Rolling my own routines to preserve scratchpad predictability and speed. 1 Quote Link to comment Share on other sites More sharing options...
PeteE Posted January 23, 2020 Share Posted January 23, 2020 50 minutes ago, Airshack said: Thanks @PeteE. I’m familiar with the scratchpad and EQUates. I do like the way you’re using offsets vs actual addresses, as I’m using. Your method makes it easy to maintain available scratchpad awareness without doing any Hex math. As for the ISR, I’ve been convinced to avoid that for game programming with LIMI 0 as per previous posts on this same thread. Rolling my own routines to preserve scratchpad predictability and speed. Another method I've been meaning to try out is the xdt99 feature of XORG, so you could lay out your data in memory using DATA statements without having to calculate offsets manually, like this: VARDAT XORG >8320 ; start after workspace PLAYER DATA >0000 ENEMY DATA >0000 KEYLOC DATA >0000 ETC The difference between this and AORG, is that the DATA bytes are copied into your program at VARDAT, so you can initialize your variables by copying words from VARDAT to >8320 when your program starts. 1 Quote Link to comment Share on other sites More sharing options...
Airshack Posted January 24, 2020 Share Posted January 24, 2020 11 hours ago, PeteE said: In order to run on an unexpanded console, you need to put your program in cartridge ROM like this: AORG >6000 ; start at cartridge ROM * cartridge header * your program code MAPDAT BYTE .... ; your map data from Magellan BYTE .... ; note this is now read-only This is interesting. Let’s say I’d like to test what I have so far using FinalGROM. After using AORG >6000, I’ll need to know more about cartridge headers. Any good sources? I have but a vague notion of what a cartridge header involves. Quote Link to comment Share on other sites More sharing options...
PeteE Posted January 24, 2020 Share Posted January 24, 2020 41 minutes ago, Airshack said: This is interesting. Let’s say I’d like to test what I have so far using FinalGROM. After using AORG >6000, I’ll need to know more about cartridge headers. Any good sources? I have but a vague notion of what a cartridge header involves. Here's an example cartridge header, with a single program in the program list: 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 ... You would change the string at CRTNM to reflect what you want to appear in the TI menu "PRESS 2 FOR" ... and it must appear in all upper-case. 2 1 Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted January 24, 2020 Share Posted January 24, 2020 (edited) 22 hours ago, Airshack said: One thought: If I keep track of where my key objects should be located on the scrollable game map, I can simply plot them over grass tiles, after each viewport redraw. I’m imagining this will create a blinking appearance as the map is continually redrawn, and the key is then replotted. Perhaps it will appear as a nice feature highlighting the key’s location? If you, say, "require" the Memory Expansion (32K) for your game, you can copy the map from ROM (cartridge) to CPU RAM (and go from there as you wanted). Lots of bigger games of later years require the Memory Exp., so that's all okay, except if you're a wannabe purist. It's probably my kinda niche, though I'll keep doors open. Back in the day, millions or most TI-99/4A owners did not have the Memory Exp. These days, anyone with emulation has easy (or de-facto) access to the Memory Exp. Also (with or without Memory Exp.), you can have more than one screen in VDP RAM. You can draw on one, while displaying another. Often refered to as double buffering. When you've done drawing, you switch the display to that new location. You can theoretically (and practical) have as much as 16 different screens stored in the VDP at once. If you switch right after a VDP interrupt, you'll see no screen tearing on real hardware (have to assume unexpanded console because of interrupts) (also Classic99 generally tears a lot these days, but that's another issue). Edited January 24, 2020 by sometimes99er 3 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 25, 2020 Share Posted January 25, 2020 21 hours ago, PeteE said: Here's an example cartridge header, with a single program in the program list: 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 ... You would change the string at CRTNM to reflect what you want to appear in the TI menu "PRESS 2 FOR" ... and it must appear in all upper-case. Hmmm... with this tidy information I can see a Cartridge version of CAMEL99 Forth coming out of the cross-compiler. This makes it look very straightforward. Thanks very much for this. 1 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted January 25, 2020 Share Posted January 25, 2020 4 minutes ago, TheBF said: Hmmm... with this tidy information I can see a Cartridge version of CAMEL99 Forth coming out of the cross-compiler. This makes it look very straightforward. Thanks very much for this. Same kind of info at the head of each of the four banks of fbForth 2.0 source code available on my website below. ...lee 1 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted January 25, 2020 Share Posted January 25, 2020 1 minute ago, Lee Stewart said: Same kind of info at the head of each of the four banks of fbForth 2.0 source code available on my website below. ...lee Awesome. Thanks Lee. I have never gone down that path but it was on my todo list. The post by PeteE made me see that it's not as tricky as I imagined. Away on some business for the weekend but will get back to the coding next week early. Freeing up 8K more Expansion RAM would really make a difference for the size of project I can support. I have been envious of you and Willsy on that front. 1 Quote Link to comment Share on other sites More sharing options...
PeteE Posted January 25, 2020 Share Posted January 25, 2020 (edited) 43 minutes ago, TheBF said: Hmmm... with this tidy information I can see a Cartridge version of CAMEL99 Forth coming out of the cross-compiler. This makes it look very straightforward. Thanks very much for this. You're welcome. I can't claim credit though, I'm sure I took it from somewhere else, but I can't remember where. Probably here Edited January 25, 2020 by PeteE 1 1 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted January 25, 2020 Share Posted January 25, 2020 (edited) On 1/23/2020 at 2:44 PM, Airshack said: Anyway, it’s working now. I have a bridge key on my game map! MOV @MAPDAT, R6 * pointer to beginning of map LI R7,>D900 * Character D9 = bridge key MOVB R7,*R6 * Place key at map origin as a test My problem last night was I was using the following first line: LI R6, @MAPDAT * then basically corrupting MAPDAT Caused all sorts of havoc onto my game screen. Well, none of the above is 100% correct. MOV @MAPDAT,R6 will not store a pointer to MAPDAT in R6, but the content of the memory word at MAPDAT. Thus your byte >D9 will end up at the address pointed to by the data at the first word of MAPDAT, not in the first word of MAPDAT. This will defnitely not be what you expect, and will perhaps lock up your computer. That's the least myserious thing that could occur. On the other hand, LI R6,@MAPDAT isn't correct either. It's not even correct syntax, since such a construct doesn't exits for the TMS 9900. What you want to do is LI R6,MAPDAT, since it's the value of MAPDAT you want to use as a pointer to the first MAPDAT word. To map an area in memory, that's not in line with your code, you can use a construct even TI's original assembler provides. You can use dummy segments, where the dummy origin, DORG, allows for generating entries in the symbol list, without generating any code. DORG >8300 FIRST DATA 1234 SECOND DATA >35F2 THIRD BYTE 22 FOURTH TEXT 'X' Will generate symbol table entries for the labels, like if they were assembled starting at address >8300. But no data is created in a dummy segment, nor loaded into memory at load time. Edited January 25, 2020 by apersson850 2 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted April 18, 2020 Share Posted April 18, 2020 I think this question has been answered somewhere in Atariage but I can't find the right search terms to find it Is it possible to run a program with E/A 5 and return to the E/A cartridge menu? (versus exiting to the main screen) Quote Link to comment Share on other sites More sharing options...
HOME AUTOMATION Posted April 18, 2020 Share Posted April 18, 2020 (maybe)... E/A manual, 24.11.3 p.442 Other returns Clear the GPL STATUS byte, load GPL Workspace Registers, branch to @>0070. P.S. In another example(speech) they show >006A as the return addr(p.361). P.P.S. I used to be more familiar with these. 3 Quote Link to comment Share on other sites More sharing options...
+mizapf Posted April 18, 2020 Share Posted April 18, 2020 006A clears the GPL status (see TI Intern). You should also make sure that the GROM address counter did not change (no accesses to GROM while running the machine language program). 1 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted April 18, 2020 Share Posted April 18, 2020 11 minutes ago, HOME AUTOMATION said: (maybe)... E/A manual, 24.11.3 p.442 Other returns Clear the GPL STATUS byte, load GPL Workspace Registers, branch to @>0070. P.S. In another example(speech) they show >006A as the return addr(p.361). P.P.S. I used to be more familiar with these. Thanks!. I saw that but had no idea if I was supposed to go back to the GPL interpreter. I will give it a try. \ SIMPLE super cart binary program HEX CROSS-ASSEMBLING START. NEW. ABSOLUTE 6000 ORIGIN. \ super cart RAM >6000 TI-99.EA5 \ builds the file header in RAM \ =============================================== PROGRAM: GPLRTN 8300 LWPI, \ set workspace pointer located at >8300, fast ram \ wasting time loop R1 FFFF LI, BEGIN, E002 @@ INCT, E004 @@ DEC, R1 DEC, EQ UNTIL, \ * return to GPL with ROM routine R0 CLR, R0 837C @@ MOVB, 83E0 LWPI, 0070 @@ B, END. \ ============================================== \ save the binary image file FILENAME$ $SAVE-EA5. BYE And hey it works. This code is pretty weird cuz it's written for my Forth cross-assembler but you get the idea. GPLReturn.mp4 3 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted April 18, 2020 Share Posted April 18, 2020 10 minutes ago, mizapf said: 006A clears the GPL status (see TI Intern). You should also make sure that the GROM address counter did not change (no accesses to GROM while running the machine language program). This works as well without clearing >837C first. Thanks everyone. 2 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted May 16, 2020 Share Posted May 16, 2020 Is there a clever way in assembly - without tests and jumps - to calculate the sign of a register (if r > 0 then 1 else if r < 0 then -1 else 0)? Quote Link to comment Share on other sites More sharing options...
+mizapf Posted May 16, 2020 Share Posted May 16, 2020 You don't mean MOV Rx,Rx, I suppose. Maybe SRL Rx,15? You cannot test for 0 and for the signs at the same time, I think. Quote Link to comment Share on other sites More sharing options...
Asmusr Posted May 16, 2020 Share Posted May 16, 2020 1 hour ago, mizapf said: You don't mean MOV Rx,Rx, I suppose. Maybe SRL Rx,15? You cannot test for 0 and for the signs at the same time, I think. I want the result 1, 0 or -1 in the register. If you SRA Rx,15 that gives you 0 or -1, but how do you get 1 if Rx > 0? 1 Quote Link to comment Share on other sites More sharing options...
PeteE Posted May 16, 2020 Share Posted May 16, 2020 (edited) 4 hours ago, Asmusr said: I want the result 1, 0 or -1 in the register. If you SRA Rx,15 that gives you 0 or -1, but how do you get 1 if Rx > 0? You can get 1 by doing ((abs(r)^-r)>>15) which is basically "if the sign bit changes between negating and absolute value, then you know it's > 0" * Returns 1 if R0>0, -1 if R0<0, otherwise 0 MOV R0,R1 MOV R0,R2 NEG R1 ABS R2 XOR R2,R1 SRL R1,15 SRA R0,15 SOC R1,R0 This even works for -32768 which would usually be an edge case. EDIT: What is your goal for the exercise? No branches, fewest instructions, shortest execution time? Edited May 16, 2020 by PeteE 1 Quote Link to comment Share on other sites More sharing options...
+mizapf Posted May 16, 2020 Share Posted May 16, 2020 (edited) 3 hours ago, Asmusr said: I want the result 1, 0 or -1 in the register. If you SRA Rx,15 that gives you 0 or -1, but how do you get 1 if Rx > 0? Sample value in R0, result in R1. LI R1,1 MOV R0,R0 JGT DONE SRA R0,15 DONE ... Edited May 16, 2020 by mizapf Space lines in code 2 Quote Link to comment Share on other sites More sharing options...
GDMike Posted May 16, 2020 Share Posted May 16, 2020 Sweeeet Quote Link to comment Share on other sites More sharing options...
intvnut Posted May 16, 2020 Share Posted May 16, 2020 (edited) 1 hour ago, mizapf said: Sample value in R0, result in R1. LI R1,1 MOV R0,R0 JGT DONE SRA R0,15 DONE ... Don't you need to move R0 to R1 after the SRA? The SRA will take 2 cycles for every position shifted, plus 12 cycles, plus 3 mem cycles. That makes it pretty expensive. (42 + 3*mem). If I add up the worst case for your routine (adding the missing MOV): LI R1, 1 ; 12 + 3*mem MOV R0, R0 ; 14 + 4*mem JGT DONE ; 8 + 1*mem (not taken) SRA R0, 15 ; 42 + 3*mem MOV R0, R1 ; 14 + 4*mem DONE ... ;------------ ; 90 + 15*mem How about this one, which leaves the result in the same register it started with? NEG R0 ; 12 + 3*mem Sets EQ if zero, GT if negative SETO R0 ; 10 + 3*mem R0 = -1, no flag modifications JGT DONE ; 8 + 1*mem (n/t) If R0 was negative, we're done. JNE POS ; 8 + 1*mem (n/t) If R0 was non-zero, turn -1 to +1 DEC R0 ; 10 + 3*mem R0 was 0, so turn -1 to -2, to become 0 POS: INCT R0 ; 10 + 3*mem This either gives is 0 or +1 now. DONE: ;----------- ; 58 + 14*mem I think this ends up being the same size, too, once you add the missing MOV to yours. (FWIW, I'm using the cycle counts here, and the instruction references here and here.) I know the original request was without tests or jumps: 7 hours ago, Asmusr said: Is there a clever way in assembly - without tests and jumps - to calculate the sign of a register (if r > 0 then 1 else if r < 0 then -1 else 0)? But on this architecture, jumps are surprisingly cheap compared to the alternatives. They're cheaper than MOVs, which is surprising until you remember this is a memory-memory architecture. Edited May 16, 2020 by intvnut Further clarification on cost structure of ISA. Add comments to my code. 1 Quote Link to comment Share on other sites More sharing options...
+mizapf Posted May 16, 2020 Share Posted May 16, 2020 Yes, you need to add a MOV R0,R1. Also true, shifts use a lot of cycles. I'd favor your approach with the jumps, but I was not quite sure about the constraints. Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted May 16, 2020 Share Posted May 16, 2020 Considering the necessity of jumps, I see this as shortest: MOV R0,R0 JEQ DONE SETO R0 JLT DONE INCT R0 DONE ... ...lee 3 2 Quote Link to comment Share on other sites More sharing options...
intvnut Posted May 16, 2020 Share Posted May 16, 2020 (edited) 7 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 *doh* Of course! You can use the fact R0 is 0 to save an instruction over my approach, and be slightly faster both on average and in the worst case. 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 50+12*mem is noticeably faster than my 58+14*mem. 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. Edited May 16, 2020 by intvnut 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.