javatari.js icon indicating copy to clipboard operation
javatari.js copied to clipboard

Accessing RAM?

Open suchow opened this issue 6 years ago • 9 comments

Hi, thanks for the amazing Javatari project — really impressive work.

Is there a way to access the state of the RAM using either the jt or Javatari JS objects? I tried jt.saveState(), but it always returns the same string throughout a game.

suchow avatar Dec 05 '17 22:12 suchow

Not directly. I am planning a documented API for exposing the main state of the machine and control most functions programatically, but is not ready yet.

BUT, for now, there is a way... You can try reading the bytes from the RAM object one by one with:

Javatari.room.console.eval("ram").read(0)

Just read all the positions in the range 0..127 and you can reconstruct the entire RAM state.

Paulo

ppeccin avatar Dec 05 '17 22:12 ppeccin

Thank you, this worked well!

function ram2json() {
  var ram = Array();
  for (var i = 0; i < 128; i++) {
    ram[i] = Javatari.room.console.eval("ram").read(i);
  }
  return JSON.stringify(ram);
}

Do you know what the maximum update rate of the RAM is? That is to say, if I wanted to save the entire history of the game state, at what rate would I need to sample the state of the RAM?

suchow avatar Dec 05 '17 22:12 suchow

You're trying to make some AI robot right? ;-)

The best would be to synchronize with the frame generation of the console. In the future API you will get a callback to hook to... But not yet.

BUT, again, there is a way... You could pause the machine clock and command the frame generation yourself. First, just do:

Javatari.room.console.mainVideoClock.pause()

and then:

Javatari.room.console.videoClockPulse()

to advance one frame at a time. This way you can achieve what you want, right?

Paulo

ppeccin avatar Dec 05 '17 22:12 ppeccin

I study humans, the fanciest robots of them all. 🤖 The goal is to allow people to play games in realtime, while I record everything I can about the game state.

suchow avatar Dec 05 '17 23:12 suchow

Cool!

But remember that there are other states involved, like CPU registers and other chips internal states, not only RAM.

There is another way of recording game sessions, with a LOT LESS information to log. The Atari 2600 is a completely deterministic machine, with no internal source of entropy or randomness.

You could save a complete state initially, and from there log only the CONTROLS state at each frame generated. This way you can later just reload that initial state and replay the exact same sequence.

Paulo

ppeccin avatar Dec 05 '17 23:12 ppeccin

Ah, that's good to keep in mind regarding CPU registers and other chips.

I like your idea of recording the initial state and the controls state at each frame. I am afraid, however, that following your advice means that I will just need to ask you how to record the complete state and controls state...

  1. I was able to figure out how to save a state file to disk:
Javatari.room.console.getSavestateSocket().saveStateFile()

but couldn't figure out how to get that same state stored in memory.

  1. I see some relevant-looking variables for storing the controls state, but again not sure which is exactly the right place to look.
Javatari.room.console.getConsoleControlsSocket

(Thanks for your help, and sorry if this is more than you signed up for — I'll keep digging and see how far I can get...)

suchow avatar Dec 05 '17 23:12 suchow

Well, yes. You would have to punch your way into the code to find the best places to get the data you need. Probably modify and write some custom functions to get/inject the data.

But getSavestateSocket() and getConsoleControlsSocket() are the right places to start looking.

Paulo

ppeccin avatar Dec 06 '17 00:12 ppeccin

Okay, I'll dig into this more later, but I have a super hacky version working now.

To get the checkpoints of the current machine state, I run the following every so often:

state = Javatari.room.console.eval("saveState")()

In addition, I modified DOMConsoleControls so that it stores a timestamp when powering on the device and then saves a timestamped entry for every call to processControl.

This leaves me with a series of state checkpoints and a history of all control actions.

In the future, I'd like to figure out how to:

  • [ ] Timestamp the actions based on the internal clock
  • [ ] Subscribe to the control socket without needing to modify the internals in the way I am
  • [ ] Replay the actions based on the starting state and history

In particular, timestamping control actions based on the internal clock will greatly improve the reproducibility of the recorded actions.

suchow avatar Dec 06 '17 04:12 suchow

The way I see it, you do not have to save a timestamp, which is very imprecise, but instead save a frame number. If you control the frame generation by calling videoClockPulse(), or just add a hook to your code just at the beginning of that function, all you need to do is log the STATE of the controls before each frame is generated, together with the frame number.

Then, to replay the session, your code would just check the logs and apply the correct controls state just before that frame is generated.

ppeccin avatar Dec 06 '17 14:12 ppeccin