Jump to content
IGNORED

IntyBASIC compiler v1.2.9: The good things are now better!


Recommended Posts

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 by artrag
  • Like 1
Link to comment
Share on other sites

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 :dunce:

  • Like 3
Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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

  • Like 1
Link to comment
Share on other sites

 

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 by DZ-Jay
Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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
  • Like 1
Link to comment
Share on other sites

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 by artrag
Link to comment
Share on other sites

 

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 by DZ-Jay
Link to comment
Share on other sites

  • 1 month later...

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.

  • Like 1
Link to comment
Share on other sites

  • 1 month later...

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...

Link to comment
Share on other sites

  • 2 weeks later...

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 by intvnut
  • Like 1
Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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. :-o

 

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.

  • Like 1
Link to comment
Share on other sites

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.

  • Like 2
Link to comment
Share on other sites

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. :-o

 

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... :ponder: I'll go digging.

 

-dZ.

Link to comment
Share on other sites

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... :ponder: 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':

  1. Copy row #5 to row #11. (~350 cycles)
  2. scroll #0 - #4 down to #1 - #5 with backward row-wise copy (~1900 cycles)
  3. Copy row #11 to row #0 (~350 cycles)
  4. scroll #6 - #10 down to #7 - #11 with backward row-wise copy (~1900 cycles)
  5. 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.

  • Like 1
Link to comment
Share on other sites

 

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. :)

Link to comment
Share on other sites

 

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. :)

Link to comment
Share on other sites

 

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: :grin: :grin:

 

P.S. Thanks for the corrections!

Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...