artrag Posted March 28, 2018 Share Posted March 28, 2018 (edited) I support any hardware related functionality easy to implement and useful to get faster/smaller/more efficient programs. The noob programers can choose to use the commands they like and understand while more advanced coders can exploit the platform at its best without having to resort to assembly. But of course, compromises are needed, for Oscar's sanity ? Edited March 28, 2018 by artrag 1 Quote Link to comment Share on other sites More sharing options...
+nanochess Posted March 28, 2018 Author Share Posted March 28, 2018 I support any hardware related functionality easy to implement and useful to get faster/smaller/more efficient programs. The noob programers can choose to use the commands they like and understand while more advanced coders can exploit the platform at its best without having to resort to assembly. But of course, compromises are needed, for Oscar's sanity After all the requests I've implemented I'm not sane anymore 3 Quote Link to comment Share on other sites More sharing options...
intvnut Posted April 2, 2018 Share Posted April 2, 2018 Maybe a dull question. I'm computing a bit mask for testing COLX for a given events How do I do powers of 2 in intybasic ? m = 2^n It seems that ^ is not supported, I could solve with an array of bit masks but I wanted just to be sure that this is the sole way. PS Maybe an operator to shift bit positions could be more efficient and would allow things like this (COLX>>n) and 1 Bit shifting by multiple positions is somewhat of a drag on the Intellivision, especially if the shift-amount is variable. For testing bits, just declare a small table of bit values and use array indexing. It will be quite a bit faster. If you really want (x >> n) AND 1, for variable 'n', the execution time is going to be proportional to n unless you add a lookup table and use array indexing. Quote Link to comment Share on other sites More sharing options...
artrag Posted April 2, 2018 Share Posted April 2, 2018 I have a question about DEFINE card_num,total,XXXX and DEFINE ALTERNATE Why you do not allow the use of a general 16bit expression in place of XXXX? now in place of XXXX I use varptr #mydata(put the expression here) This means in ASM mydata+expression, so, why not allow directly the user to point in memory according to its expression In my case I have REM graphic data data0: data x,x,x,x.... data1: data x,x,x,x.... data2: data x,x,x,x.... datalist: data varptr data0(0),varptr data1(0),varptr data2(0),.... and I would like to do DEFINE card_num,total,datalist(n) <-- address of the nth graphic set 1 Quote Link to comment Share on other sites More sharing options...
+DZ-Jay Posted April 2, 2018 Share Posted April 2, 2018 (edited) Bit shifting by multiple positions is somewhat of a drag on the Intellivision, especially if the shift-amount is variable. For testing bits, just declare a small table of bit values and use array indexing. It will be quite a bit faster. If you really want (x >> n) AND 1, for variable 'n', the execution time is going to be proportional to n unless you add a lookup table and use array indexing. I just wanted to add to intvnut's comment to explain that the reason for the expense is that the CP-1610 shift instruction can only shift one or two bits at a time. You can somewhat optimize shift amounts higher than 8 by using the SWAP instruction, which byte-swaps a register (indeed, this is what IntyBASIC's constant multiplication routine does; see the "MULT" macro in the "intybasic_epilogue.asm" file), but you still have to mask the irrelevant bits or extend the sign yourself. If you really need to squeeze performance out of it, you are better off using a look-up table, like intvnut suggested. Array indexing in IntyBASIC is pretty fast. -dZ. Edited April 2, 2018 by DZ-Jay Quote Link to comment Share on other sites More sharing options...
artrag Posted April 2, 2018 Share Posted April 2, 2018 In many high level compiled languages shift instructions are resolved by an in line sequence of shift/rotation asm instructions when not solved with a call to a loop of shifts. In this, intellivision having shift by 2 positions and a swap instruction seems advantaged with respect to other architectures Quote Link to comment Share on other sites More sharing options...
+DZ-Jay Posted April 2, 2018 Share Posted April 2, 2018 In many high level compiled languages shift instructions are resolved by an in line sequence of shift/rotation asm instructions when not solved with a call to a loop of shifts. In this, intellivision having shift by 2 positions and a swap instruction seems advantaged with respect to other architectures True, but it is still slower than some alternatives. All shift instructions (including SWAP) cost 6 CPU cycles for a single position shift, and 8 for a two position shift. Doing it twice (for x >> 4) already puts you on level ground with a register immediate load and indirect move in from a look-up table. Generalizing it may be more expensive. Then again, I'm willing to bet that arbitrary shifts or multiplications are not the bottleneck of most IntyBASIC programs. I do take your point that an idiom for "x >> n" allows for more expressivity, so why not? dZ. Quote Link to comment Share on other sites More sharing options...
+nanochess Posted April 2, 2018 Author Share Posted April 2, 2018 DEFINE card_num,total,datalist(n) <-- address of the nth graphic set The most close thing to what you want to do is abusing VARPTR syntax: #c = your_varptr_array(index) DEFINE card_num,total,VARPTR xyz_array(#c - VARPTR xyz_array(0)) your_varptr_array: DATA VARPTR bitmap0(0), VARPTR bitmap1(0) I would prefer to use offsets instead of using VARPTR like this, more or less like this: #c = my_offsets(index) DEFINE card_num,total,VARPTR bitmaps(#c) my_offsets: DATA 0 * 4, 1 * 4 ' Multiplied by 4 because is the size of a 8x8 bitmap 1 Quote Link to comment Share on other sites More sharing options...
artrag Posted April 4, 2018 Share Posted April 4, 2018 Another request :-) Could SIGNED/UNSIGNED and DIM be combined in one line ? I was expecting something like DIM SIGNED X(10),Y(10) 1 Quote Link to comment Share on other sites More sharing options...
artrag Posted April 7, 2018 Share Posted April 7, 2018 (edited) Question, for confirmation: I see that constants and labels can have the same name without any apparent conflict or side effect. Is this a planned feature ? If yes, maybe it could be documented in the manual I have this in my code and works fine const isr_menu = 0 const isr_title = 1 const isr_game = 2 const isr_boss = 3 const isr_warp = 4 [...] isr_mode = isr_menu [...] on (isr_mode) goto isr_menu, isr_title, isr_game, isr_boss, isr_warp [...] isr_title: if play_log then gosub log_play end if return isr_game: gosub bullet_multiplex gosub voice_samples gosub sfx_player return isr_boss: [...] Edited April 7, 2018 by artrag Quote Link to comment Share on other sites More sharing options...
+DZ-Jay Posted April 7, 2018 Share Posted April 7, 2018 (edited) Question, for confirmation: I see that constants and labels can have the same name without any apparent conflict or side effect. Is this a planned feature ? If yes, maybe it could be documented in the manual I have this in my code and works fine const isr_menu = 0 const isr_title = 1 const isr_game = 2 const isr_boss = 3 const isr_warp = 4 [...] isr_mode = isr_menu [...] on (isr_mode) goto isr_menu, isr_title, isr_game, isr_boss, isr_warp [...] isr_title: if play_log then gosub log_play end if return isr_game: gosub bullet_multiplex gosub voice_samples gosub sfx_player return isr_boss: [...] Are you saying that a constant and a label with the same name do not collide? If so, this is probably by design, since a constant (an arbitrary value) is conceptually different from a label (a location in the program). I would imagine that it only needs to be documented if they collide, since that would be non-obvious behaviour. Now, a variable and a constant, on the other hand, should exist in the same namespace, so they cannot have the same name. -dZ. EDIT: I misunderstood artag's comment originally, so re-wrote my response. Edited April 7, 2018 by DZ-Jay Quote Link to comment Share on other sites More sharing options...
Darkhog Posted May 29, 2018 Share Posted May 29, 2018 I wish we could easily edit envelopes without editing the epilogue files. Proposed syntax: ENVELOPE (envid) (envdata) Where envid would be one of the numbers used for envelopes in the music data and the envelope data would be in the same format it is in the epilogue asm file. Perhaps even allow for more instruments? This would also allow for instrument remapping: Don't need a piano now and need it other instruments? Remap it with the command. 1 Quote Link to comment Share on other sites More sharing options...
Kiwi Posted July 4, 2018 Share Posted July 4, 2018 Oh I just got one. If you're only going to scroll in one direction(Not the band), such as scrolling horizontally. Can it use 12 16-bit words instead of 20? Quote Link to comment Share on other sites More sharing options...
Kiwi Posted July 10, 2018 Share Posted July 10, 2018 Oh I just got one. If you're only going to scroll in one direction(Not the band), such as scrolling horizontally. Can it use 12 16-bit words instead of 20? Sigh... Kiwi, It has to save the data by rows then draw the offset of the cards from memory, and not column(which is a snail method of printing tiles to screen). My straitjacket repair man can figure that one out. Gees... Quote Link to comment Share on other sites More sharing options...
+nanochess Posted July 10, 2018 Author Share Posted July 10, 2018 Cannot be by column because it would use just too much time in ADD. Rather it uses autoincrement features of processor. 1 Quote Link to comment Share on other sites More sharing options...
intvnut Posted July 20, 2018 Share Posted July 20, 2018 (edited) For a purely horizontal scroll, you don't need any additional RAM, and only 12 words of ROM for each new column you bring in. You still scroll each row one at a time, but you don't do it column-wise. That really would be too slow. I'm not able to test the following code right now, so beware of minor errors. I have used similar loops in the past, and so I know the approach is sound. . ;; Scroll entire display left 1 card ;; R3 points to new data to write at right SCROLL_LEFT PROC PSHR R5 MVII #$200, R5 MVII #$201, R4 MVII #12, R2 @@o_loop: REPEAT 19 MVI@ R4, R0 MVO@ R0, R5 ENDR MVI@ R3, R0 MVO@ R0, R5 INCR R3 INCR R4 DECR R2 BNEQ @@o_loop PULR PC ENDP ;; Scroll entire display right 1 card ;; R3 points to new data to write at left SCROLL_RIGHT PROC PSHR R5 MVII #$200, R4 MOVR R4, R5 MVII #12, R2 @@o_loop: MVI@ R3, R0 INCR R3 REPEAT 9 MVI@ R4, R1 ; Read from N MVO@ R0, R5 ; Write N-1 MVI@ R4, R0 ; Read from N+1 MVO@ R1, R5 ; Write N ENDR MVI@ R4, R1 ; Read from N MVO@ R0, R5 ; Write N-1 MVO@ R1, R5 ; Write N INCR R4 DECR R2 BNEQ @@o_loop PULR PC ENDP Edited July 20, 2018 by intvnut 1 Quote Link to comment Share on other sites More sharing options...
+DZ-Jay Posted July 21, 2018 Share Posted July 21, 2018 I'm with intvnut: for horizontal scrolling it's possible to block-copy the screen ahead of the BUSRQ fetch without a buffer. For instance, the code in Christmas Carol looks surprisingly like the one above: ;; ======================================================================== ;; ;; .DO_L_SHIFT ;; ;; Shifts the entire screen one column to the left. ;; ;; ;; ;; INPUT for .DO_L_SHIFT ;; ;; R5 Pointer to return address. ;; ;; ;; ;; OUTPUT ;; ;; R0 Trashed. ;; ;; R2 Trashed (zero). ;; ;; R3 Pointer to next column. ;; ;; R4 Trashed. ;; ;; R5 Trashed. ;; ;; ======================================================================== ;; .DO_L_SHIFT PROC .max_count QSET BACKTAB.Rows .start_addr QSET BACKTAB.Addrs CALL .GET_NEXT_COL.Left ; Get new column source address (R3) MVII #(.start_addr + 1), R4 ; Source MVII #(.start_addr + 0), R5 ; Destination MVII #.max_count, R2 ; Count @@__next_tile: REPEAT (BACKTAB.Cols - 1) MVI@ R4, R0 MVO@ R0, R5 ENDR MVI@ R3, R0 INCR R3 MVO@ R0, R5 INCR R4 LOOP R2, @@__next_tile WRET SCROLL_INFO.ShiftFunc ENDP As I mentioned in another thread, I believe that the critical point is to start early enough to stay ahead of the game. In my case, I had nothing else happening at the moment, so it was simple. You can't do it per column because it is too slow; and you can't do it from the end of BACKTAB backwards because it'll completely miss the STIC fetches (I know, I tried. The results were spectacularly weird, which is how I learned the hard way.) -dZ. Quote Link to comment Share on other sites More sharing options...
Kiwi Posted July 21, 2018 Share Posted July 21, 2018 I do understand that uses the 20 bytes of RAM to get and put on screen, it is quicker to grab data in a sequence and paste it in sequence. This is something I experimented using put_vram vs put_frame for Colecovision programming.I'm more likely going to lose a frame since I am doing quite a lot during the gameloop to tie the game together. Quote Link to comment Share on other sites More sharing options...
intvnut Posted July 23, 2018 Share Posted July 23, 2018 I'm with intvnut: for horizontal scrolling it's possible to block-copy the screen ahead of the BUSRQ fetch without a buffer. For instance, the code in Christmas Carol looks surprisingly like the one above: Wow. Except for the various macros/symbols that you've used to abstract out some details, it's identical, other than that I put INCR R3 / INCR R4 next to each other. I mean, we even used the same registers for the same purposes. Would you believe I typed that dang thing completely from memory? In any case, I just now took a quick look at what intybasic_epilogue.asm implements. It uses the same general technique for left/right scrolling, just with different register assignments. I'm not sure why nanochess used "MOVR R4, R5; INCR R5" to initialize R5 instead of simply "MVII #$201, R5", but what's 4 cycles among friends? For the vertical downward scroll, I vaguely remember trying to come up with a way to avoid the temporary buffer by using a portion of the BACKTAB. I think it's one of those situations where it's doable if you have tight integration between the scrolling code and the code that fills in the exposed line, but less-so otherwise. I don't remember how that particular experiment turned out off-hand. I wouldn't be surprised if it's buried in a thread somewhere here on AA or INTVPROG. 1 Quote Link to comment Share on other sites More sharing options...
+nanochess Posted July 23, 2018 Author Share Posted July 23, 2018 In any case, I just now took a quick look at what intybasic_epilogue.asm implements. It uses the same general technique for left/right scrolling, just with different register assignments. I'm not sure why nanochess used "MOVR R4, R5; INCR R5" to initialize R5 instead of simply "MVII #$201, R5", but what's 4 cycles among friends? 4 cycles is a lot! In fact I'll be optimizing the epilogue because each cycle used by the epilogue is one cycle less for a game. 2 Quote Link to comment Share on other sites More sharing options...
+DZ-Jay Posted July 23, 2018 Share Posted July 23, 2018 Wow. Except for the various macros/symbols that you've used to abstract out some details, it's identical, other than that I put INCR R3 / INCR R4 next to each other. I mean, we even used the same registers for the same purposes. Would you believe I typed that dang thing completely from memory? Well, it's some of our conventions: R4, R5, R0 for copy loop source, destination, and temporary, respectively; I use R3 for pointer arguments to functions and return values; and then R2 is the only one left for the loop iterator. Looking at it, I guess if I were to re-write it, I would do it the same way. In any case, I just now took a quick look at what intybasic_epilogue.asm implements. It uses the same general technique for left/right scrolling, just with different register assignments. I'm not sure why nanochess used "MOVR R4, R5; INCR R5" to initialize R5 instead of simply "MVII #$201, R5", but what's 4 cycles among friends? For the vertical downward scroll, I vaguely remember trying to come up with a way to avoid the temporary buffer by using a portion of the BACKTAB. I think it's one of those situations where it's doable if you have tight integration between the scrolling code and the code that fills in the exposed line, but less-so otherwise. I don't remember how that particular experiment turned out off-hand. I wouldn't be surprised if it's buried in a thread somewhere here on AA or INTVPROG. It might be buried in the INTVProg list. I sort of remember you referencing nanochess' IntyBASIC using a buffer in contrast... I'll go digging. -dZ. Quote Link to comment Share on other sites More sharing options...
intvnut Posted July 23, 2018 Share Posted July 23, 2018 Well, it's some of our conventions: R4, R5, R0 for copy loop source, destination, and temporary, respectively; I use R3 for pointer arguments to functions and return values; and then R2 is the only one left for the loop iterator. Looking at it, I guess if I were to re-write it, I would do it the same way. Well, there is R1 also. But for then, for SCROLL_RIGHT, we need both R0 and R1, and keeping similar allocations between the two makes both easier to understand. For the vertical downward scroll, I vaguely remember trying to come up with a way to avoid the temporary buffer by using a portion of the BACKTAB.It might be buried in the INTVProg list. I sort of remember you referencing nanochess' IntyBASIC using a buffer in contrast... I'll go digging. I seem to recall you can make it work if you're willing to copy a row twice, by making use of the fact that Row #11 goes away, and row #0 is allowed to be 'garbage': Copy row #5 to row #11. (~350 cycles) scroll #0 - #4 down to #1 - #5 with backward row-wise copy (~1900 cycles) Copy row #11 to row #0 (~350 cycles) scroll #6 - #10 down to #7 - #11 with backward row-wise copy (~1900 cycles) Copy row #0 down to #6 (~350 cycles) Cycle counts are hugely approximate. The double-copy would add ~350 cycles, perhaps in a critical spot right there in step 3. The STIC moves on to a new row of cards every 912 cycles, so if we assume we got row #1 on the display in time in step #2, we do have enough time to finish the job in steps 3 - 5. ~350 cycles for gaining back 20 words of RAM. Might be a fair trade. 1 Quote Link to comment Share on other sites More sharing options...
+DZ-Jay Posted July 24, 2018 Share Posted July 24, 2018 Well, there is R1 also. But for then, for SCROLL_RIGHT, we need both R0 and R1, and keeping similar allocations between the two makes both easier to understand. Yes, that is true. My SCROLL_RIGHT is slightly different from yours, though: ;; ======================================================================== ;; ;; .DO_R_SHIFT ;; ;; Shifts the entire screen one column to the right. ;; ;; ;; ;; INPUT for .DO_R_SHIFT ;; ;; R5 Pointer to return address. ;; ;; ;; ;; OUTPUT ;; ;; R0 Trashed. ;; ;; R1 Trashed. ;; ;; R2 Trashed. ;; ;; R3 Trashed. ;; ;; R4 Trashed. ;; ;; ======================================================================== ;; .DO_R_SHIFT PROC .max_count QSET BACKTAB.Rows .start_addr QSET BACKTAB.Addrs .end_addr QSET (.start_addr + (BACKTAB.Rows * BACKTAB.Cols)) CALL .GET_NEXT_COL.Right ; Get new column source address MVII #(.start_addr + 0), R4 ; Source MOVR R4, R5 ; Destination @@__next_tile: MVI@ R3, R0 ; Get next tile for new column INCR R3 REPEAT (BACKTAB.Cols / 2) MVI@ R4, R1 MVO@ R0, R5 MVI@ R4, R0 MVO@ R1, R5 ENDR CMPI #.end_addr, R5 BNC @@__next_tile @@__not_last: WRET SCROLL_INFO.ShiftFunc ENDP I guess I reserved R2 for the loop counter (which was not used), and then decided to use R1 as the only other register left. *shrug* It's been a while. Quote Link to comment Share on other sites More sharing options...
intvnut Posted July 24, 2018 Share Posted July 24, 2018 Yes, that is true. My SCROLL_RIGHT is slightly different from yours, though: ;; ======================================================================== ;; ;; .DO_R_SHIFT ;; ;; Shifts the entire screen one column to the right. ;; ;; ;; ;; INPUT for .DO_R_SHIFT ;; ;; R5 Pointer to return address. ;; ;; ;; ;; OUTPUT ;; ;; R0 Trashed. ;; ;; R1 Trashed. ;; ;; R2 Trashed. ;; ;; R3 Trashed. ;; ;; R4 Trashed. ;; ;; ======================================================================== ;; .DO_R_SHIFT PROC .max_count QSET BACKTAB.Rows .start_addr QSET BACKTAB.Addrs .end_addr QSET (.start_addr + (BACKTAB.Rows * BACKTAB.Cols)) CALL .GET_NEXT_COL.Right ; Get new column source address MVII #(.start_addr + 0), R4 ; Source MOVR R4, R5 ; Destination @@__next_tile: MVI@ R3, R0 ; Get next tile for new column INCR R3 REPEAT (BACKTAB.Cols / 2) MVI@ R4, R1 MVO@ R0, R5 MVI@ R4, R0 MVO@ R1, R5 ENDR CMPI #.end_addr, R5 BNC @@__next_tile @@__not_last: WRET SCROLL_INFO.ShiftFunc ENDP I guess I reserved R2 for the loop counter (which was not used), and then decided to use R1 as the only other register left. *shrug* It's been a while. Still very similar. You read one extra word from the BACKTAB that you don't need (so you can roll everything into the REPEAT), and use a CMPI rather than DECR. If I did my math right, it ends up costing you a whopping extra 40 cycles. (2 extra/iter for MVI@ instead of INCR, and 2 extra/iter for CMPI instead of DECR, and saving 8 cycles not setting up R2. 4*12 - 8 = 48 - 8 = 40.) Something tells me 40 cycles didn't really matter much in the context this was used. Quote Link to comment Share on other sites More sharing options...
+DZ-Jay Posted July 24, 2018 Share Posted July 24, 2018 Still very similar. You read one extra word from the BACKTAB that you don't need (so you can roll everything into the REPEAT), and use a CMPI rather than DECR. If I did my math right, it ends up costing you a whopping extra 40 cycles. (2 extra/iter for MVI@ instead of INCR, and 2 extra/iter for CMPI instead of DECR, and saving 8 cycles not setting up R2. 4*12 - 8 = 48 - 8 = 40.) Something tells me 40 cycles didn't really matter much in the context this was used. Ouch! But yeah, nothing else was happening then. This was to scroll the Practice Menu, so those extra cycles didn't hurt. I don't really know why I did it that way... hmmm. (I just saw in my repo that it goes all the way to the very first revision like that.) And... stop making up my code from memory!! :grin: P.S. Thanks for the corrections! 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.