Jump to content
Sign in to follow this  
Revontuli

Trying to port Batari Basic rand16 to Java/C/etc...

Recommended Posts

I'm trying to port the Rand/Rand16 process used in batari Basic to Java, mainly so I can try some tests and visualization in some different systems like Processing (a Java scripting system at processing.org), and if I can get it to Java, I can probably get it to C/C++/C# etc. etc.  The thing is that assembly is still a very large blind spot for me, so I would appreciate some help.  I think I have the basic gist of what's going on, but I do have a few questions.

 

I'm pretty sure this is the rand16 code used in batari Basic currently (found in std_routines.asm):

randomize
    lda rand
    lsr
 
ifconst rand16
    rol rand16
endif
    
    bcc noeor
    eor #$B4
noeor
    sta rand

ifconst rand16
    eor rand16
endif
    
RETURN

 

Between adapting 8bit operations to a 32/64bit system and my own ignorance of assembly, there's enough places where I could be doing something wrong (to say nothing of being off by 1, or missing a digit somewhere) that I would appreciate some other eyes looking at what I'm coding :)  Debugging random output is, by definition, tricky!


I have a few questions about what's going on with the accumulator/bcc branching, etc. but I hope I can show my thought process with my current code here, attempting to do what "randomize" does.

 

Some more context/things I'm dealing with in current Java:
-A complicating factor is that integers are 32bit, so I lot of what I'm doing is "converting" rand from a 32bit integer to an 8 bit one.
-In Processing, there's a function called binary(), which changes an interger to a string of 0's and 1's.  I'm using this to get the "carry bits."  I'm sure there are other ways, but I want a "paper trail" while I'm figuring this out.  It's sloppy, but I want to get the code working before I get it elegant.

//rand and rand16 are 32bit integers, and global

int Rand()
{
  char carryBit;
  char carryBit16;
  String carryString;
  String carryString16;
  
  int tempRand = rand;
  int tempRand16 = rand16;
  
  //shorten to 8 bits from 32, effectively convert to "byte"
  tempRand = tempRand & 255;
  tempRand16 = tempRand16 & 255;

  //grab the carry bits ahead of time, there's probably more graceful ways to do this but
  //I'm trying to make sure I have the bits before operating on them.  32bit value means
  //I need to grab the binary values at slots 31 and 24.  I think this is right, they might 
  //need to be swapped or offset by 1...
  
  carryString= binary(tempRand);
  carryBit = carryString.charAt(31);

  carryString16 = binary(tempRand16);
  carryBit16 = carryString16.charAt(24);

  //lda rand
  //lsr 
  tempRand = tempRand >> 1;
  tempRand = tempRand & 255;
  
  //rol rand16
  tempRand16 = tempRand16 << 1;
  tempRand16 = tempRand16 & 255;
  if (carryBit16 == '1') tempRand16 += 1;

  //bcc noeor
  //I -think- this what bcc is doing?  Checking a carry bit and branching accoringly?

  if (carryBit16 == '0') 
  {
    //eor $B4
    tempRand = tempRand ^ 180;
  }

  //eor rand16
  tempRand = tempRand ^ tempRand16;


  tempRand16 = tempRand16 & 255;
  rand16 = tempRand16;

  tempRand = tempRand & 255;
  rand = tempRand;

  return rand;
}

One thing I'm a particularly hazy about is the final "eor rand16" operates on, if we've already stored rand back in memory?  If it's in the accumulator, is it still "saved"? 


Many thanks in advance from myself, a total novice at assembly code!

Share this post


Link to post
Share on other sites

I'm not that good with Java, but your implementation leaves off the seeding of rand from the timer.  Here's how bB implements that internally, vs how I would do so myself in C as an example:

 

 

/*
 * lda #1       ; Ensure random seed is nonzero
 * ora INTIM    ; Use random value in timer, and OR with 1
 * sta rand     ; Save in rand
 */
    srand(time(0));     /* Random seed based on system clock */
    rand8 = (rand()&255);   /* Set 8-bit seed for our random function */   

 

Another point is that the random value that is used is what is left in the accumulator at the end of the function, not what is saved in rand.

 

As for rand16, I don't see where that is ever seeded, or that the value of it ever changes in a bB program, so I'm not sure what use it has.

Share this post


Link to post
Share on other sites

Err nevermind ... rand16 starts at zero, but "rol rand16" does actually write to the variable, so it changes with each invocation. 

Share this post


Link to post
Share on other sites

Java isn't my thing either, but I don't think your carry code is quite right, as the rand16 ROL-shift doesn't affect carry like it should. Here's a C implementation that spits out 512 numbers, which should be easier to translate. I confirmed the output compared to bB for the first few digits...

 

#include <stdio.h>
#include <stdlib.h>

int main(int argc,char **argv)
{
	int t;
	int rand, rand16, A, carry, bitmode;

	bitmode=8;
	rand=42; rand16=42; // a seed picked at random

	// with the above seed...
	// expected rand sequence:   (42) 21 190 95 155 ...
	// expected rand16 sequence: (42) 65 163 227 253 ...

	for(t=0;t<512;t++)
	{
		A=rand;
		carry=A&1; // save the bit about to be right-shifted away
		A=A>>1;
		if(bitmode==16)
		{
			rand16=(rand16<<1)|carry;
			carry=rand16&0x100; // save the carry via the 9th bit
			rand16=rand16&0xff; // and then mask off the 9th bit
		}
		if(carry!=0) 
			A=A^0xB4; 
		rand=A;
		if(bitmode==16)
			A=A^rand16;
	
		printf("%d ",A);
		if(t==254)
			printf("\n\n");
	}
}

Note that the final A value is the result I printf, not rand - it's the same thing in 8-bit mode, but different in 16-bit mode. (bB uses A to update whatever variable you assigned the random number to, after the subroutine return)

 

If the ultimate goal here is to port a bB game, one needs to keep in mind that bB only runs the random number routine if "rand" is referenced in a variable assignment. If you use rand as a loop end condition or if...then condition, then the old  rand variable contents are used, without a new random number being generated.

  • Like 2

Share this post


Link to post
Share on other sites

Excellent - thank you both!  I still have a ton of work to do, but it looks like I have rand() ported with the values I'm expecting.  A function like this is easy enough to port from C to Java, so the code was understandable (with a function like this it's basically like converting from American English to British English).

 

6 hours ago, Karl G said:

Another point is that the random value that is used is what is left in the accumulator at the end of the function, not what is saved in rand.

 

As for rand16, I don't see where that is ever seeded, or that the value of it ever changes in a bB program, so I'm not sure what use it has.

 

Ah!  Returning a separate value that's different from the updated rand makes sense.

 

I have the rand and rand16 seeds initialized elsewhere, and constantly use seeding for things like level generation, so they're separated from the rand() function itself.  With the 16-bit version I often end up seeding them separately, or lock one while changing the other, which has proven handy a few times.

 

2 hours ago, RevEng said:

If the ultimate goal here is to port a bB game, one needs to keep in mind that bB only runs the random number routine if "rand" is referenced in a variable assignment. If you use rand as a loop end condition or if...then condition, then the old  rand variable contents are used, without a new random number being generated.

-That- is good to know, and makes sense now that you pointed it out.

 

I still have a lot to do, but being confident about the accuracy of rand/rand16 means I can focus on other stuff without worrying about the initial algorithms.  I am indeed doing some porting (or at least testing how feasible it is), and keeping rand() intact is a crucial part of that process.  This is also helping me get my feet wet with assembly code.

  • Like 1

Share this post


Link to post
Share on other sites

Note also that rand16 starts at zero, so for complete bB accuracy, you won't want to seed that one at the beginning.

Share this post


Link to post
Share on other sites

There!  I built a script that can output Dragon's Descent levels - I prototyped it in Processing/Java, but it shouldn't be hard to port to C/C++/C#, especially now that I have a better idea of how the two bytes are interacting.  Here are maps of the first 3 levels.

 

p5_001001.png.f4969a36179a6b653f28bde56c4aee0d.pngp5001002.png.dafaa45bbf5428724b437eb72108cc8b.pngp5001003.png.97dc13c51098e15926f4ce732860b0bb.png

  • Like 1

Share this post


Link to post
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.

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...
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...