Jump to content
IGNORED

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


Recommended Posts

Speaking of multiplication bugs.. I thought I had this logic worked out months ago, but clearly I've never really tested it. I'd love it if someone else could try this code, and see if I'm crazy (I'm kinda hoping maybe I have a corrupt prologue or epilogue or something):

	GOSUB print_test

        loop:
	goto loop

print_test:	PROCEDURE

	num_1 = 35
	num_2 = 85

	num_1_tens=num_1/10
	num_1_ones=num_1-num_1_tens*10
	
	'print at 0,<3>num_1_tens
	'print at 20,<3>num_1_ones

	num_2_tens=num_2/10
	num_2_ones=num_2-num_2_tens*10
	print at 120,<3>num_2_tens
	print at 140,<3>num_2_ones

	END

What we SHOULD see is 8, followed by 5. What *I* see is 030, followed by 041. The math should work, and I can verify it two ways:

 

1. Uncomment one or both of the other PRINT statements

2. Change the first multiplication to num_1_ones=num_1-num_1_tens*8

 

Do either of those things, and the calculation that matters magically works as expected. I can understand maybe how the *8 fixes things if we have some kind of deep-seated bug with multiplication by 10, but how the PRINT does it is a little beyond me. Interestingly the division by 10 is not the problem. But it's like there's some kind of 'memory' of the previous *10 - maybe a register that goes wonky?

 

(And before anyone points it out, I'm aware that IntyBASIC has built in routines to print numbers without breaking them out like this. I have a reason for my madness)

 

Edit: well, I have a workaround, Every time I call anything with *10, I jump to this and it fixes whatever the problem is:

cleanup_print_hack:	PROCEDURE
	PRINT AT 220,<1>num_1_tens
	PRINT AT 220,$603
	END

As I have a blank card at that position, I can get away with it. And this is only hitting me on static screens, so it's acceptable.

 

Still... seems like a nasty bug. Hopefully my "fix" gives a clue as to what's going on under the covers.

 

 

I just cut and pasted your example into a file, compiled it and ran it with a stock IntyBASIC 1.2.5. I see this:

 

post-14113-0-87362200-1469935681_thumb.gif

 

That looks right to me.

 

Interestingly, it looks broken if I turn on --jlp, though. (Reporting how you invoke the tools is important!)

 

post-14113-0-29161200-1469936101_thumb.gif

 

 

The issue appears to be the divide:

.

    ;[17]   num_2_tens=num_2/10
    SRCFILE "/tmp/fw.bas",17
    MVI V2,R0
    MVII #10,R4
    MVO R0,40842
    MVO R4,40843
    MOVR R1,R0
    NOP
    MVO R0,V5

.

I don't see where it's reading in the result of the division. There's that weird NOP there though. At this point in the generated code, R1 holds the previous result of num_1_tens*10.

 

I suspect IntyBASIC is doing some value tracking of some sort, saying "Hey, I know R1 holds the last value read from location 40846, so I don't need to re-load it!" The JLP accelerators, though, change the values in memory out from under it. If that's what's going on, there's your bug.

 

The reason it doesn't happen more places likely has to do with the order IntyBASIC picks registers. Most of the time, stuff read from the accelerators will end up in R0. But, since you're subtracting the accelerator's result from something else, the tree walker ends up putting it in R1. And since R1 doesn't get clobbered by anything else between these two expressions, it looks like the most likely cause to me.

 

Here's a bigger piece of the assembly, showing the connection:

.

    ;[12]   num_1_ones=num_1-num_1_tens*10
    SRCFILE "/tmp/fw.bas",12
    MVI V1,R0
    MVI V3,R1
    MVII #10,R4
    MVO R1,40838
    MVO R4,40839
    MVI 40846,R1    ; <<<< loads the accelerator result into R1
    SUBR R1,R0
    MVO R0,V4
    ;[13]   
    SRCFILE "/tmp/fw.bas",13
    ;[14]   'print at 0,<3>num_1_tens
    SRCFILE "/tmp/fw.bas",14
    ;[15]   'print at 20,<3>num_1_ones
    SRCFILE "/tmp/fw.bas",15
    ;[16] 
    SRCFILE "/tmp/fw.bas",16
    ;[17]   num_2_tens=num_2/10
    SRCFILE "/tmp/fw.bas",17
    MVI V2,R0
    MVII #10,R4
    MVO R0,40842
    MVO R4,40843
    MOVR R1,R0       ; <<<< incorrectly reuses the accelerator result from above
    NOP              ; <<<< unnecessary NOP
    MVO R0,V5

.

 

 

On the topic of multiply bugs, there were some bugs at one time in my multiply macros, and those bugs survived through dZ's tweak of them and on into IntyBASIC, where nanochess then fixed (some of? all of?) them manually as I understand it. I'm pretty certain *10 was heavily tested, given the number of games that print scores in decimal. ;-)

 

FWIW, I believe I fixed the error in my "mult_by_constant" generator. If folks are inclined to play with it, have a look.

 

All the tests above, though, were run with stock IntyBASIC 1.2.5, irrespective of my tweaked multiply generator.

updated_multiplies.zip

Edited by intvnut
Link to comment
Share on other sites

Oh wow, thanks man. I suspected it had to be something unusual - and now I understand. I KNEW I tested this code at some point. JLP would be a relatively recent addition (months ago, but time flies). I had snooped a bit on the registers but I won't lie, I get lost quickly enough doing that.

 

The one thing I now realize that I *didn't* try, was to put the mult into a temporary variable and then do the subtraction. That magically fixes things, for obvious reasons. Interestingly I only have to do this on the first value and it fixes both of them. Probably a better workaround than my PRINT hack.

 

Excellent sleuthing!

Link to comment
Share on other sites

Bigtime cheers, nanochess. I don't mind putting in the odd workaround (in fact sometimes I'm a bit proud when I come up with a clever one) but I'd much rather just have the production code done right. This will take another one off my issue list :D

 

Only 50 or so to go... sigh. (Problems for me, not bugs!)

  • Like 1
Link to comment
Share on other sites

Bloody hell, guys, I swear this was JUST discussed recently but I can't for the life of me find it...

 

How can we detect if an ECS is plugged in, in IntyBASIC? I seem to recall POKEing or PEEKing something, but I just can't find it.

 

Also, consider this an IntyBASIC feature request, like the NTSC check :D

Link to comment
Share on other sites

Bloody hell, guys, I swear this was JUST discussed recently but I can't for the life of me find it...

 

How can we detect if an ECS is plugged in, in IntyBASIC? I seem to recall POKEing or PEEKing something, but I just can't find it.

 

Also, consider this an IntyBASIC feature request, like the NTSC check :D

I recall it was discussed but couldn't find it here either.

I do have this saved in my notes

 

https://groups.yahoo.com/neo/groups/intvprog/conversations/topics/7731

Link to comment
Share on other sites

Bloody hell, guys, I swear this was JUST discussed recently but I can't for the life of me find it...

 

How can we detect if an ECS is plugged in, in IntyBASIC? I seem to recall POKEing or PEEKing something, but I just can't find it.

 

Also, consider this an IntyBASIC feature request, like the NTSC check :D

 

Well, you could check for the ECS EXEC to be present ($2000 - $2FFF, $7000 - $7FFF, $E000 - $EFFF), or the second PSG ($F0 - $FF), or the UART ($E0 - $E2), or the extra 8-it RAM ($4000 - $47FF).

 

If you're only using the ECS for the PSG, I'd recommend just checking for that. The ROMs can be page-flipped out (and will need to be page-flipped out for large games), and—blush—jzIntv doesn't implement the UART.

 

To test for the second PSG, you'll need to write and read one of its registers. e.g. tmp = PEEK($F0) : POKE $F0, tmp + 1 : IF PEEK($F0) = tmp THEN ....

 

The reason for needing both a write and a read is that the Intellivoice also responds on the same range of addresses for its expansion bus. (Yes, there's a buffer fight here, and the Intellivoice is actually designed to tolerate it. It fights with the other PSG too, to allow the planned wireless hand controllers to work.)

 

Alternately, POKE/PEEK some locations in the ECS 8-bit RAM at $4000 - $47FF. Hopefully, IntyBASIC doesn't use the addresses that also alias the STIC ($4000 - $403F), so those won't have any variables allocated to them.

 

Options, options, options. :-)

Link to comment
Share on other sites

I started with the 8-bit RAM check from the Yahoo discussion, had no luck. So I figured I'd give the PSG idea a go. The idea being that if we can write to $F0, and the result returns the same, that we have an ECS?

 

So, I think you made a typo - correct me if I'm wrong. The comparison should be IF PEEK($F0) = tmp+1, no? That seems to work for me. Also, I notice that the first read returns different values depending on if I have the ECS attached or not, but I assume that's not sufficient.

 

I can confirm that IntyBASIC entirely avoids $3000-$47FF, so we're good on that front. I have it marked as a no-fly zone in my own notes so I don't accidentally map ROM anywhere in there.

Link to comment
Share on other sites

I started with the 8-bit RAM check from the Yahoo discussion, had no luck. So I figured I'd give the PSG idea a go. The idea being that if we can write to $F0, and the result returns the same, that we have an ECS?

 

Hmmm, I wonder why it didn't work. You should be able to change a value and read it back, just as with the PSG.

 

 

So, I think you made a typo - correct me if I'm wrong. The comparison should be IF PEEK($F0) = tmp+1, no? That seems to work for me. Also, I notice that the first read returns different values depending on if I have the ECS attached or not, but I assume that's not sufficient.

 

It's not a typo actually. You just need to be able to discriminate between "did I change the value, or not?" Either test would suffice, and one of them is likely to be cheaper than the other, either in size or speed. (Size probably matters more if it's a "once at the start of time" type of test.)

 

 

I can confirm that IntyBASIC entirely avoids $3000-$47FF, so we're good on that front. I have it marked as a no-fly zone in my own notes so I don't accidentally map ROM anywhere in there.

 

OK, I didn't know if IntyBASIC had a flag that would allocate 8-bit variables in the ECS's 2K RAM.

Link to comment
Share on other sites

This is the code I use in P-Machinery to detect the ECS. It is based on code provided by Joe Z. and Arnauld Chevallier. I believe I have tested it on the real hardware in the past and it works.

;; ======================================================================== ;;
;;  .BOOT.REQ_ECS                                                           ;;
;;  Check if the ECS is present and fail boot sequence if not.              ;;
;;                                                                          ;;
;;  Detection is performed by writing a word to ECS 8-bit RAM and reading a ;;
;;  byte back, twice.  If the process succeeds, we can assume the ECS unit  ;;
;;  is present.                                                             ::
;;                                                                          ;;
;;  INPUT for .BOOT.REQ_ECS                                                 ;;
;;      R5      Pointer to return address.                                  ;;
;;                                                                          ;;
;;  OUTPUT                                                                  ;;
;;      None.                                                               ;;
;; ======================================================================== ;;
.BOOT.ECS_CHECK PROC
_b.ecs_byte     QEQU    MEMMAP.EcsRam8bit               ; Chosen arbitrarily (any address in ECS RAM will do)

                ; --------------------------------------
                ; Attempt to detect ECS
                ; --------------------------------------
                MVII    #_b.ecs_byte,           R1      ; \_ Pick an ECS RAM address and read what's there
                MVI@    R1,     R0                      ; /
                COMR    R0                              ; \_ Complement it, including upper byte,
                MVO@    R0,     R1                      ; /     and store it back.
                XOR@    R1,     R0                      ; XOR the stored value with original inverted one
                CMPI    #PM.MSB_MASK,           R0      ; Make sure only the lower-byte is cancelled
                BNEQ    .BOOT.ECS_FAIL                  ;   No?  halt boot sequence with an error message

                JR      R5                              ;   Yes? continue with main program
                ENDP

I guess in IntyBASIC you could do that with PEEK and POKE:

  TMP = PEEK($4040) XOR $FFFF

  POKE $4040, TMP

  IF ((PEEK($4040) XOR TMP) = $FF) THEN
    ' Succes
  ELSE
    ' Failure
  END IF

Or something like that...?

Edited by DZ-Jay
  • Like 1
Link to comment
Share on other sites

It's not a typo actually. You just need to be able to discriminate between "did I change the value, or not?" Either test would suffice, and one of them is likely to be cheaper than the other, either in size or speed. (Size probably matters more if it's a "once at the start of time" type of test.)

 

Oh. Just to be clear, your IF..THEN is implied to be NOT ECS in your example (ie: you couldn't change the value).

 

Ya know, I swear I tried this last night and could never get it to work. I flipped it to comparing to tmp+1 and things worked, so I assumed my peabrain wasn't following something. Of course, I try it again this morning, and yeah - I can make either way work.

 

No idea why I couldn't get the 8-bit RAM check working, but probably for a similar reason.

 

Thanks! You got me moved on last night at any rate.

Link to comment
Share on other sites

  • 1 month later...

I've been using IntyBASIC to develop Sydney Hunter & the Sacred Tribe, corrected some bugs in compiler and just updated it to v1.2.7 :) (check first post)

  • Allows to concatenate lines using the character \ at the end of a line. (suggested by GroovyBee)
  • Now optimizes access to arrays when using same index even if different array or offset. (double speed in common cases!!! intvnut suggested it a long of time ago)
  • Solved bug where DEFINE VARPTR swallowed one extra lexical component.
  • Solved bug where 1024-x*x generated wrong code.
  • Solved bug where PRINT "string" as first statement would write in non-screen memory.
  • Minor edit in manual (definition of BORDER)
  • Like 7
Link to comment
Share on other sites

That looks great!

 

Can you show an example of an array access pattern hat shows the speed improvement?

 

Thanks sir!

 

 

 

 

I've been using IntyBASIC to develop Sydney Hunter & the Sacred Tribe, corrected some bugs in compiler and just updated it to v1.2.7 :) (check first post)

 

  • Allows to concatenate lines using the character \ at the end of a line. (suggested by GroovyBee)
  • Now optimizes access to arrays when using same index even if different array or offset. (double speed in common cases!!! intvnut suggested it a long of time ago)
  • Solved bug where DEFINE VARPTR swallowed one extra lexical component.
  • Solved bug where 1024-x*x generated wrong code.
  • Solved bug where PRINT "string" as first statement would write in non-screen memory.
  • Minor edit in manual (definition of BORDER)
Link to comment
Share on other sites

For this test code:

	DIM array(10)
	DIM array2(20)

	array(4) = array(3)
	array(4+a) = array(3+a)
	array(a+4) = array(a+3)
	array(a+5) = array(a)

	array2(a) = array(a)
	array2(4+a) = array(4+a)
	array2(2+a) = array(4+a)
	array2(4+a) = array(2+a)
The newest version of the compiler generates this code: (note the pattern saving array pointer in r3)

	;FILE test39.bas
	;[1] 	DIM array(10)
	;[2] 	DIM array2(20)
	;[3] 
	;[4] 	array(4) = array(3)
	MVI Q3+3,R0
	MVO R0,Q3+4
	;[5] 	array(4+a) = array(3+a)
	MVII #Q3+3,R3
	ADD V1,R3
	MVI@ R3,R0
	INCR R3
	MVO@ R0,R3
	;[6] 	array(a+4) = array(a+3)
	DECR R3
	MVI@ R3,R0
	INCR R3
	MVO@ R0,R3
	;[7] 	array(a+5) = array(a)
	SUBI #4,R3
	MVI@ R3,R0
	ADDI #5,R3
	MVO@ R0,R3
	;[8] 
	;[9] 	array2(a) = array(a)
	SUBI #5,R3
	MVI@ R3,R0
	ADDI #Q4-Q3,R3
	MVO@ R0,R3
	;[10] 	array2(4+a) = array(4+a)
	ADDI #Q3-Q4+4,R3
	MVI@ R3,R0
	ADDI #Q4-Q3,R3
	MVO@ R0,R3
	;[11] 	array2(2+a) = array(4+a)
	ADDI #Q3-Q4,R3
	MVI@ R3,R0
	ADDI #Q4-Q3-2,R3
	MVO@ R0,R3
	;[12] 	array2(4+a) = array(2+a)
	ADDI #Q3-Q4,R3
	MVI@ R3,R0
	ADDI #Q4-Q3+2,R3
	MVO@ R0,R3
While the oldest version generates this code:

	;[1] 	DIM array(10)
	;[2] 	DIM array2(20)
	;[3] 
	;[4] 	array(4) = array(3)
	MVI Q2+3,R0
	MVO R0,Q2+4
	;[5] 	array(4+a) = array(3+a)
	MVII #Q2,R1
	MVII #3,R2
	ADD V1,R2
	ADDR R2,R1
	MVI@ R1,R0
	MVII #Q2,R1
	MVII #4,R2
	ADD V1,R2
	ADDR R2,R1
	MVO@ R0,R1
	;[6] 	array(a+4) = array(a+3)
	MVII #Q2+3,R3
	ADD V1,R3
	MVI@ R3,R0
	MVII #Q2+4,R3
	ADD V1,R3
	MVO@ R0,R3
	;[7] 	array(a+5) = array(a)
	MVII #Q2,R3
	ADD V1,R3
	MVI@ R3,R0
	MVII #Q2+5,R3
	ADD V1,R3
	MVO@ R0,R3
	;[8] 
	;[9] 	array2(a) = array(a)
	MVII #Q2,R3
	ADD V1,R3
	MVI@ R3,R0
	MVII #Q3,R3
	ADD V1,R3
	MVO@ R0,R3
	;[10] 	array2(4+a) = array(4+a)
	MVII #Q2,R1
	MVII #4,R2
	ADD V1,R2
	ADDR R2,R1
	MVI@ R1,R0
	MVII #Q3,R1
	MVII #4,R2
	ADD V1,R2
	ADDR R2,R1
	MVO@ R0,R1
	;[11] 	array2(2+a) = array(4+a)
	MVII #Q2,R1
	MVII #4,R2
	ADD V1,R2
	ADDR R2,R1
	MVI@ R1,R0
	MVII #Q3,R1
	MVII #2,R2
	ADD V1,R2
	ADDR R2,R1
	MVO@ R0,R1
	;[12] 	array2(4+a) = array(2+a)
	MVII #Q2,R1
	MVII #2,R2
	ADD V1,R2
	ADDR R2,R1
	MVI@ R1,R0
	MVII #Q3,R1
	MVII #4,R2
	ADD V1,R2
	ADDR R2,R1
	MVO@ R0,R1
This improves code in any point where arrays are used as most of time the same index is used again and again, this happens to be the most common case.
  • Like 1
Link to comment
Share on other sites

The newest version of the compiler generates this code: (note the pattern saving array pointer in r3)

...snip...
	;[5] 	array(4+a) = array(3+a)
	MVII #Q3+3,R3
	ADD V1,R3
	MVI@ R3,R0
	INCR R3
	MVO@ R0,R3
	;[6] 	array(a+4) = array(a+3)
	DECR R3
	MVI@ R3,R0
	INCR R3
	MVO@ R0,R3
...snip...
While the oldest version generates this code:

...snip...
	;[5] 	array(4+a) = array(3+a)
	MVII #Q2,R1
	MVII #3,R2
	ADD V1,R2
	ADDR R2,R1
	MVI@ R1,R0
	MVII #Q2,R1
	MVII #4,R2
	ADD V1,R2
	ADDR R2,R1
	MVO@ R0,R1
	;[6] 	array(a+4) = array(a+3)
	MVII #Q2+3,R3
	ADD V1,R3
	MVI@ R3,R0
	MVII #Q2+4,R3
	ADD V1,R3
	MVO@ R0,R3
...snip...
This improves code in any point where arrays are used as most of time the same index is used again and again, this happens to be the most common case.

 

 

 

Trimmed down the example in the quote... That is a marked improvement! Impressive!

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

Just a very quick question. How do you use the RANDOM function to force IntyBASIC to invoke a new random number on a frame?

 

Do you use it by doing,

 

enemydir=RANDOM/32

 

or do it as

 

for i=0 to 3

RANDOM

enemydir(i)=RAND/32

next i

 

Both of these don't work.

 

Got it, RANDOM(range)

 

Means:

 

RANDOM 8

 

to get number 0-7

 

Question aborted. *pulls lever*

Edited by Kiwi
Link to comment
Share on other sites

  • 2 weeks later...

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