Timers and frame rate

Yesterday I wanted to try out if it was possible to simulate different levels of grayscale on the screen by using the trick Nintendo used to use to achieve semi transparency on NES games.

The trick is simple. On even frames the pixel should be on, and on odd frames the pixel should be off. If you look at games like Metroid on the NES, when you spawn in at the very beginning Samus’ sprite is semi transparent. It is achieved by turning the sprite on and off every other frame. The TV screen, being unresponsive and interlaced is trying to keep up with what the hardware is trying to show, but it can’t and in the end the sprite comes out semi transparent.

When I did an earlier test I wrote a scrolling text and I noticed that the screen was quite unresponsive. When pixels where fading from on to off was clearly visible. So I figured that if I would flicker the pixel on and off the screen, being so unresponsive, should end up stuck in the fade between on and off.

So I wrote this little test

vmthread  MAIN
{
  DATA8 Flag
  DATA8 Draw_1of2

  MOVE8_8(0, Draw_1of2)

Loop:
  UI_DRAW( FILLWINDOW,0x00,0,0 )                  // Clear screen
  UI_DRAW( FILLRECT, FG_COLOR, 0, 0, 10, 10)      // Draw reference rect, fully opaque


  //
  // Draw 1 of 2
  //
  JR_NEQ8( Draw_1of2, 0, DrawDone_1of2 )          // if( Draw_1of2 != 0 )
  UI_DRAW( FILLRECT, FG_COLOR, 10, 0, 10, 10)     //     Draw full rect (every second frame)
DrawDone_1of2:
  ADD8( Draw_1of2, 1, Draw_1of2 )                 //
  JR_LT8( Draw_1of2, 2, Done_1of2 )               // if( Draw_1of2 >= 2 )
  MOVE8_8( 0, Draw_1of2 )                         //     Draw_1of2 = 0
Done_1of2:
  UI_DRAW( UPDATE )                               //  Show the stuff

  // Check for key to exit
  UI_BUTTON( SHORTPRESS, BACK_BUTTON, Flag )      // IF backbutton pressed THEN Flag=1
  JR_FALSE( Flag, Loop )                          // IF Flag==1 then JR Loop
}

But instead of seeing a semi transparent rectangle I saw a rectangle that was flickering out of control. The interval of the flickering wasn’t steady but seemed to vary. Some intervals the rectangle was shown multiple frames and some intervals it was only shown a single frame. It is obviously quite difficult to see exactly what is going on and for how long the rect was shown and hidden, but I could tell it was not flickering at a fixed and steady interval.

There where some timing issues.

I wanted the app to run in 60 FPS (old habit) and I didn’t know if my app was running faster or slower than that. In fact, I don’t even know the refresh rate of the screen. Now that I think about it, I guess what I really want to do is to sync my app with the refresh rate of the screen.

In any case, I wanted to take control of the timing of my app, so I looked into the TIMER functions.

The timer function would let me setup a timer and then stall the app until the timer had been reached. So I could say that, at this point in my code I want a timer that runs no longer than X milliseconds, and at another point in the code I could ask to wait for the timer to catch up with X.

So I added this:

vmthread  MAIN
{
  DATA8 Flag
  DATA8 Draw_1of2
  DATA32  Timer

  MOVE8_8(0, Draw_1of2)

Loop:
  TIMER_WAIT( 16, Timer )                         // Aim for 60 FPS

  UI_DRAW( FILLWINDOW,0x00,0,0 )                  // Clear screen
  UI_DRAW( FILLRECT, FG_COLOR, 0, 0, 10, 10)      // Draw reference rect, fully opaque


  //
  // Draw 1 of 2
  //
  JR_NEQ8( Draw_1of2, 0, DrawDone_1of2 )          // if( Draw_1of2 != 0 )
  UI_DRAW( FILLRECT, FG_COLOR, 10, 0, 10, 10)     //     Draw full rect (every second frame)
DrawDone_1of2:
  ADD8( Draw_1of2, 1, Draw_1of2 )                 //
  JR_LT8( Draw_1of2, 2, Done_1of2 )               // if( Draw_1of2 >= 2 )
  MOVE8_8( 0, Draw_1of2 )                         //     Draw_1of2 = 0
Done_1of2:
  UI_DRAW( UPDATE )                               //  Show the stuff

  TIMER_READY( Timer )                            // Wait for 60 FPS

  // Check for key to exit
  UI_BUTTON( SHORTPRESS, BACK_BUTTON, Flag )      // IF backbutton pressed THEN Flag=1
  JR_FALSE( Flag, Loop )                          // IF Flag==1 then JR Loop
}

In the added code the TIMER_WAIT( 16, Timer ) will store the current time + 16 in the variable Timer and TIMER_READY( Timer ) will wait until the current time have reached Timer. Like so:

/*! \page cTimer Timer
 *  <hr size="1"/>
 *  <b>     opTIMER_WAIT (TIME, TIMER)  </b>
 *
 *- Setup timer to wait TIME mS\n
 *- Dispatch status unchanged
 *
 *  \param  (DATA32)  TIME    - Time to wait [mS]
 *  \param  (DATA32)  TIMER   - Variable used for timing
 */
/*! \brief  opTIMER_WAIT byte code
 *
 */
void      cTimerWait(void)
{
  ULONG   Time;

  Time  =  *(ULONG*)PrimParPointer();

  *(ULONG*)PrimParPointer()  =  cTimerGetmS() + Time;
}


/*! \page cTimer
 *  <hr size="1"/>
 *  <b>     opTIMER_READY (TIMER) </b>
 *
 *- Wait for timer ready (wait for timeout)\n
 *- Dispatch status can change to BUSYBREAK
 *
 *  \param  (DATA32)  TIMER   - Variable used for timing
 */
/*! \brief  opTIMER_READY byte code
 *
 */
void      cTimerReady(void)
{
  IP      TmpIp;
  DSPSTAT DspStat = BUSYBREAK;

  TmpIp   =  GetObjectIp();

  if (*(ULONG*)PrimParPointer() <= cTimerGetmS())
  {
    DspStat  =  NOBREAK;
  }
  if (DspStat == BUSYBREAK)
  { // Rewind IP

    SetObjectIp(TmpIp - 1);
  }
  SetDispatchStatus(DspStat);

}

But even with that code the flickering wasn’t smooth 60 FPS. So I played around with the desired frame rate. I only achieved a stead flickering when I got as far down as 10 FPS. And at 10 FPS the flickering didn’t look like semi transparency, it was just flickering.

I guess at least that makes it easy for my future projects, because since I won’t be able to have semi transparency or gradients I won’t have to care about hitting a solid 60 FPS for my projects. :)

SSH (again)

There is a way to start an SSH server on the Brick, simply by executing
/etc/init.d/dropbear start

So now I can scp files to and from the Brick, and I can cat files on the Brick so I can easily

And that’s it for today.

scp

I was hoping I could use scp to copy files from my computer to the Brick but it turns out that scp is based on ssh. So that’s one difference between telnet and ssh. :)

Also, I was cocky and killed and restarted the lms2012 process on my Brick via telnet and now the Brick won’t shutdown. When I started lms2012 again it was like my prompt was waiting for the new process. When I tried to shut down the Brick via LMS2012 on the Brick I got a few error messages in the telnet prompt and a message about something being saved, and then nothing happened. It seemed like the Brick got stuck somewhere. I had to press the back and the middle button to reboot the Brick.

Anyhow, Brick is up in wifi and I can connect to it through Telnet. Happy and excited!

Wi-Fi pt. 2

Well .. that was easy.

Last login: Mon Aug 11 22:13:18 on ttys001
Magnus-starke-dator:~ magnusrunesson$ ssh root@192.168.1.246
ssh: connect to host 192.168.1.246 port 22: Connection refused
Magnus-starke-dator:~ magnusrunesson$ ping 192.168.1.246
PING 192.168.1.246 (192.168.1.246): 56 data bytes
64 bytes from 192.168.1.246: icmp_seq=0 ttl=64 time=3.038 ms
64 bytes from 192.168.1.246: icmp_seq=1 ttl=64 time=3.213 ms
^C
--- 192.168.1.246 ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 3.038/3.125/3.213/0.088 ms
Magnus-starke-dator:~ magnusrunesson$ telnet 192.168.1.246
Trying 192.168.1.246...
Connected to 192.168.1.246.
Escape character is '^]'.

 _____       _     _ ___
|  _  |_ _ _| |___| | __|
|    _| | | . | . | | _|
|__|__|___|___|___|_|_|

Rudolf 2011.01 EV3

login: root
root@EV3:~# ls -la
drwxr-xr-x    1 root     root          108 Jan  1 00:00 lms2012
root@EV3:~# cwd
-sh: cwd: not found
root@EV3:~# 

So, not SSH but Telnet. I don’t even know the difference so what do I care, I’m on the Brick! :)

UPDATE! Having wi-fi drain battery like nobodies business. I went from almost full to almost empty in a couple of minutes. Hmm.. I think my next purchase/project will be something to replace the batteries. :)

Transferring files to and from the Brick

So far I have been using LabVIEW to transfer files to and from my Brick. Besides swapping the SD card between the Brick and my computer that was the only way I knew of that would work without creating a special cable or getting a wifi dongle. (Which I have ordered but it has not yet appeared. Cursed you Webhallen! )

Yesterday I was poked regarding MonoBrick and I had a look at it, and what does it provide if not a way to communicate with the Brick over Bluetooth! The MonoBrick Communications Library is a C# library that allow you to create an app in C# that can communicate with a EV3 or NXT Brick.

Excited about the possibilities I downloaded the DLL’s and imported them in my favorite C# environment, Unity. Unfortunately Unity isn’t the best C# environment as it only supports .NET 2.0, and it turns out the MonoBrick Communications Library require a higher .NET version. I don’t know which one but it works with .NET 4.0 at least. (I use Unity at work everyday and I still don’t really know my way around the .NET environment and all the different versions of .NET and Mono etc.. Sweet ignorance!)

So after realizing that MonoBrick doesn’t work with Unity I decided to try and build a stand alone app with MonoDevelop and it sort of worked. I booted up the MonoDevelop that comes with Unity and created a solution that built beautifully. But it wouldn’t start. The app was set to build to x86, and there was no way to change to build to Mac. (Who BTW use Intel Core i7 which are x86 compatible, so when MonoDevelop say x86 it doesn’t mean it will build something that run on any x86, MonoDevelop mean it will build a Windows executable)

Since I didn’t find a way to switch to build for OSX I decided to download Xamarin Studio and Mono MDK, but once those where downloaded and installed I could finally have another go at the sample apps that comes with MonoBrick.

But no!

After loading the sample apps I realize the project files are made specifically for x86 (i.e. Windows) and there is no way to switch target platform once it has been set. Seriously! What is the point of installing a hardware abstraction layer like Mono if I can’t build apps that run in Mono on all platforms that Mono supports? I’d be super happy if someone who knows this stuff could fill me in!

In any case, I decided to create a completely new project and chose that it should be targeted towards Mac. When doing that I couldn’t find a way to create a console app for OSX, all I found was a template that looks a lot like the template app you get with Xcode. All the classes and files are the same. Except these where written in C#.

Anyhoo, now I had a C# app up and running on my computer and I started to copy the code from the MonoBrick sample project, and I ran into the next problem. The MonoBrick namespace wasn’t recognized. I had added the MonoBrick DLL into my project by simply adding all files from the downloaded MonoBrick package, but for some reason Xamarin Studio didn’t seem to parse the DLL’s when doing that, so what I ended up doing was right click on the “References” folder in my solution and chose “Edit references…”. I went to the “.Net Assembly” tab which seemed to be some form of file browser. I could highlight MonoBrick.dll and LibUsbDotNet.dll and click “Add” and they where properly added and parsed into my project, and now the sample program started.

And now, my lovely friends, the app connected to the Brick! Happy fun time! My first program was this:

var brick = new Brick<Sensor,Sensor,Sensor,Sensor>("/dev/tty.EV3-SerialPort");
brick.Connection.Open();
BrickFile[] files;
string[] folders;
brick.FileSystem.GetFolderInfo( ".", out files, out folders );

Console.WriteLine( "Files" );
foreach( BrickFile file in files )
	Console.WriteLine( "File: " + file.FullName );

Console.WriteLine( "Folders" );
foreach( string folder in folders )
	Console.WriteLine( "Folder: " + folder );

I don’t remember what the output was but I was listing some folder somewhere on the Brick. The joy and excited I had over this is indescribable! This could mean I have another way of synching my app to the Brick so I don’t have to go through LabVIEW!

After that I spent a good amount of time browsing the files on the Brick by passing in different folders. First I looked at “/”, and then I found my way to “/mnt/card/” and “/home/root/lms2012/” and I had a generally good feeling about it. Sometimes I had the issue of files showing up in the memory browser on the Brick but not when I list files over bluetooth. I still don’t know why and most of the time it seemed to work so I haven’t bothered looking into it.

After that I attempted to poke the Brick, not just peek into it. So I added this:

brick.FileSystem.CreateDirectory( "/mnt/card/bluetooth/" );

And it worked! When listing files in “/mnt/card/” I could see there was a bluetooth folder, and when using the memory browser on the brick the bluetooth folder was there as well. Next I added this:

brick.FileSystem.WriteFile( "/Users/magnusrunesson/Projects/ev3zipper/log.rbf", "/mnt/card/bluetooth/log.rbf" );

And I ran into problems! I got a “permission denied” error message. I suspected there was problems accessing the local file, I was thinking that perhaps there was a security thing going on with Mono not being allowed to access any ol’ file on my drive, so I removed the brick.FileSystem.WriteFile() call and used System.IO.File to open the same local file, and when trying the app again I got the same permission denied error message.

Now I was sure that it was a Mono problem, but then I noticed that the brick.FileSystem.CreateDirectory() call was still there, so I removed it, put the CreateFile back in and tried again, and this time it worked beautifully to create a file. I had transferred my first RBF file to the Brick without using LabVIEW. It worked fine to start the RBF on the Brick using the memory browser. The program generated a log file that for some reason didn’t show up in the memory browser on the Brick, but when listing files remotely it did show up.

The next step was to try and download the log file from Brick to my computer, by using this piece of code:

brick.FileSystem.ReadFile( "/mnt/card/bluetooth/log.txt", "/Users/magnusrunesson/Projects/ev3zipper/megabluetoothlog.txt" );

And again that worked like a charm. So now I have a C# app that at it’s very basic core can upload files to the Brick and download files from the Brick.

With the C# FileSystemWatcher class I’m hoping to have a way of automatically assembling and downloading my app to the Brick as I work with it, so when the FileSystemWatcher notice a change in my LMS file it could automatically assemble it into an RBF file and transfer it to the Brick. That way all I have to do is press Save in my text editor (which is Sublime btw, it kicks ass) wait a few seconds and then start the app on the Brick. Or even automate starting the app, MonoBrick seems to support starting programs remotely through the brick.StartProgram() function. It could be extended so the C# app also notices changes in source assets like graphics and automatically convert a source BMP file to RGF, for example. Same with sound.

A downside to this though seems to be battery usage. I’ve been using the Brick quite extensively for the last week, but only connected via USB, and the batteries have been quite charged still, but after a few hours of being connected with bluetooth I can actually see a drop in battery charge. So I am a bit concerned that bluetooth use a bit too much batteries. But then on the other hand, MonoBrick does support connection over USB so if that use less battery then yay, go USB!

To rotate or to shift, that is the question

While thinking about ways to print a 32 bit value with only a 16 bit NUMBER_TO_STRING I found an opcode called RLx. Happily I thought it meant Rotate Left, and that is also what it says in the documentation.

/*! \page Logic
*
* opRL16 (SOURCE1, SOURCE2, DESTINATION)
*
*- Rotate left 16 bit value DESTINATION = SOURCE1 << SOURCE2\n
*- Dispatch status unchanged
*
* \param (DATA16) SOURCE1
* \param (DATA16) SOURCE2
* \return (DATA16) DESTINATION
*/

Happily I figured I could rotate left and pick the lowest byte four times and convert those into figures that could be converted, but unfortunately it doesn’t really rotate left. By looking at the implementation I can see that it shifts left.

void cMathRl16(void)
{
  DATA16  Tmp;

  Tmp   =  *(DATA16*)PrimParPointer();
  Tmp <<=  *(DATA16*)PrimParPointer();
  *(DATA16*)PrimParPointer()  =  Tmp;
}

So unless the left shift operator on the Brick is actually a rotate the RLx function won’t be of much use.

I guess what I can do is MOVE32_8 the 32 bit number to an 8 bit number, and then sign convert it to a 16 bit value that I convert and add to a string, then DIV32 the original 32 bit number by 256 and do the same procedure again until I’ve fetched all of the 4 bytes individually.

Luckily I love these kinds of quirky limitations. It just makes me more inspired to keep learning about things and think about what to build for the device. It changes my own expectations of the game I’ll make and takes some of the pressure off.

Tomorrow I’ll look into printing that 32 bit value.

UPDATE!

After looking at the documentation generated by Doxygen I found the NUMBER_FORMATTED function that accept a 32 bit number and converts into a string. So that seems like it’ll do the trick. :)

MAX_FRAMES_PER_SEC and other rendering related findings

What started out as a quick in and out mission to see if there was any way of having transparency in bitmap files turned out to not only tell me if it is possible with transparency, (it is not, dLcdDrawBitmap always set pixels to on or off) it also got me looking into the swapping of display buffer to be displayed on screen and that led me to this define:

#define   MAX_FRAMES_PER_SEC    10                    //!< Max frames per second update in display

For a while I’ve been wondering if it is possible to wait for vertical blank somehow, to avoid tearing, and that is why I found that define interesting. So it seems like there is a pretty low framerate cap in the Mindstorms VM. When following the define a bit more I eventually found my way into a function called dLcdExec that looks like this:

void      dLcdExec(LCD *pDisp)
{
  if (memcmp((const void*)pDisp,(const void*)&VMInstance.LcdBuffer,sizeof(LCD)) != 0)
  {
    XPutImage(pDisplay,hWindow,hGC,&Image,0,0,LCD_LEFT,LCD_TOP,LCD_WIDTH,LCD_HEIGHT);
    XFlush(pDisplay);
    usleep(25);

    LCDCopy(&UiInstance.LcdBuffer,&VMInstance.LcdBuffer,sizeof(LCD));
    VMInstance.LcdUpdated  =  1;
  }
}

I am not familiar with XPutImage or XFlush but it seems to point to that the Mindstorms VM is running X11 for rendering to screen. Since I’m not very good with Linux I don’t know if that means any app could be written as long as it use X11, but permaybe…

Also, another thing I started thinking about that I find weird is that the screen width is 178 pixels wide. The LCD buffer is defined as:

#define   LCD_BUFFER_SIZE (((LCD_WIDTH + 7) / 8) * LCD_HEIGHT)

And the width and height are defined (but hidden behind another define) as:

#define   vmLCD_WIDTH                   178                           //!< LCD horizontal pixels
#define   vmLCD_HEIGHT                  128                           //!< LCD vertical pixels

So that means the buffer contains (178+7/8)*8 pixels for each line. That means 184 pixels for each line. Which is 6 pixels more than 178. I wonder what thos 6 pixels / bits mean?

Oh, and one final note. dLcdDrawBitmap dLcdDrawPixel, the same as UI_DRAW PIXEL use, so I could make my own sprite renderer in LMS code that sorts out transparency.

Ok, so there is one more thing! I also found the UI_DRAW subcommand STORE and RESTORE that seem to take a snapshot of the buffer and restore that snapshot to the buffer, so if one would build for example a game with static backgrounds the background could be rendered once, with slower operations like say UI_DRAW BMPFILE onto the screen buffer and then a snapshot could be taken, and for each iteration in the game loop the snapshot containing the background is restored and then each transparent sprite is rendered on top of that. The STORE and RESTORE subcommands are simply memcpy’s, so they should be tons faster than whatever I could cook up in LMS code.

Going back to MAX_FRAMES_PER_SEC, I may have been wrong in the beginning of this post. When looking at what the define is used for it actually seems to automatically flip the render buffer to be displayed at this interval, it doesn’t necessarily seem to enforce a cap at this framerate. I’ll have to investigate more into this to be sure though..

SELECT functions

I mentioned the SELECTx functions previously. That means SELECT8, SELECT16 and SELECT32. I think I’ll stick to the SELECTx or MOVEx_y to denote that the function have variations for different data types.

In any case, the SELECTx functions. I was on the lookout for bitmask operations and hoping that it would select portions of a variable into another variable. (Naive and stupid, but I wanted them bitmask operations so bad :) )

So what the SELECTx function does is to pick one or the other of two parameters based on a third parameter. So it is like a C conditional operator.

This is how the SELECTx function works in LMS:

SELECT8( flag, 10, 20, newvar )

And this is how the conditional operator works in C:

newvar = flag ? 10 : 20;

And that’s it.

0x80 == -3?

The bug that gave me -3 when reading 0x80 from a file was confusing me. Why did it convert to -3 and not -127? When I read from a file that started with the bytes 0xB2,0x80 I could read 0xB2 as -78, but 0x80 seemed to be read as -3.

First I thought there was something wrong with the read function, so I looked through the implementation of READ_BYTES in c_memory.c. The function cMemoryReadFile to be more specific. I suspected that perhaps there was a problem reading byte by byte with the READ_BYTES because I couldn’t find any other LMS file that did it, except for some test LMS code. So to verify that reading byte by byte was ok I swapped the data in the file so it read 0x80,0xB2 and I ran into the same problem.

The data I got from 0x80,0xB2 was -3 and -78. So there didn’t seem to be a problem with the READ_BYTES function after all. After that I decided to create a 256 byte file that contained every value from 0x00 to 0xff, and see what data I got. This is the result:

Print loop 0
Print loop 1
Print loop 2
Print loop 3
Print loop 4
Print loop 5
Print loop 6
Print loop 7
Print loop 8
Print loop 9
Print loop 10
Print loop 11
Print loop 12
Print loop 13
Print loop 14
Print loop 15
Print loop 16
Print loop 17
Print loop 18
Print loop 19
Print loop 20
Print loop 21
Print loop 22
Print loop 23
Print loop 24
Print loop 25
Print loop 26
Print loop 27
Print loop 28
Print loop 29
Print loop 30
Print loop 31
Print loop 32
Print loop 33
Print loop 34
Print loop 35
Print loop 36
Print loop 37
Print loop 38
Print loop 39
Print loop 40
Print loop 41
Print loop 42
Print loop 43
Print loop 44
Print loop 45
Print loop 46
Print loop 47
Print loop 48
Print loop 49
Print loop 50
Print loop 51
Print loop 52
Print loop 53
Print loop 54
Print loop 55
Print loop 56
Print loop 57
Print loop 58
Print loop 59
Print loop 60
Print loop 61
Print loop 62
Print loop 63
Print loop 64
Print loop 65
Print loop 66
Print loop 67
Print loop 68
Print loop 69
Print loop 70
Print loop 71
Print loop 72
Print loop 73
Print loop 74
Print loop 75
Print loop 76
Print loop 77
Print loop 78
Print loop 79
Print loop 80
Print loop 81
Print loop 82
Print loop 83
Print loop 84
Print loop 85
Print loop 86
Print loop 87
Print loop 88
Print loop 89
Print loop 90
Print loop 91
Print loop 92
Print loop 93
Print loop 94
Print loop 95
Print loop 96
Print loop 97
Print loop 98
Print loop 99
Print loop 100
Print loop 101
Print loop 102
Print loop 103
Print loop 104
Print loop 105
Print loop 106
Print loop 107
Print loop 108
Print loop 109
Print loop 110
Print loop 111
Print loop 112
Print loop 113
Print loop 114
Print loop 115
Print loop 116
Print loop 117
Print loop 118
Print loop 119
Print loop 120
Print loop 121
Print loop 122
Print loop 123
Print loop 124
Print loop 125
Print loop 126
Print loop 127
Print loop -3
Print loop -127
Print loop -126
Print loop -125
Print loop -124
Print loop -123
Print loop -122
Print loop -121
Print loop -120
Print loop -119
Print loop -118
Print loop -117
Print loop -116
Print loop -115
Print loop -114
Print loop -113
Print loop -112
Print loop -111
Print loop -110
Print loop -109
Print loop -108
Print loop -107
Print loop -106
Print loop -105
Print loop -104
Print loop -103
Print loop -102
Print loop -101
Print loop -100
Print loop -99
Print loop -98
Print loop -97
Print loop -96
Print loop -95
Print loop -94
Print loop -93
Print loop -92
Print loop -91
Print loop -90
Print loop -89
Print loop -88
Print loop -87
Print loop -86
Print loop -85
Print loop -84
Print loop -83
Print loop -82
Print loop -81
Print loop -80
Print loop -79
Print loop -78
Print loop -77
Print loop -76
Print loop -75
Print loop -74
Print loop -73
Print loop -72
Print loop -71
Print loop -70
Print loop -69
Print loop -68
Print loop -67
Print loop -66
Print loop -65
Print loop -64
Print loop -63
Print loop -62
Print loop -61
Print loop -60
Print loop -59
Print loop -58
Print loop -57
Print loop -56
Print loop -55
Print loop -54
Print loop -53
Print loop -52
Print loop -51
Print loop -50
Print loop -49
Print loop -48
Print loop -47
Print loop -46
Print loop -45
Print loop -44
Print loop -43
Print loop -42
Print loop -41
Print loop -40
Print loop -39
Print loop -38
Print loop -37
Print loop -36
Print loop -35
Print loop -34
Print loop -33
Print loop -32
Print loop -31
Print loop -30
Print loop -29
Print loop -28
Print loop -27
Print loop -26
Print loop -25
Print loop -24
Print loop -23
Print loop -22
Print loop -21
Print loop -20
Print loop -19
Print loop -18
Print loop -17
Print loop -16
Print loop -15
Print loop -14
Print loop -13
Print loop -12
Print loop -11
Print loop -10
Print loop -9
Print loop -8
Print loop -7
Print loop -6
Print loop -5
Print loop -4
Print loop -3
Print loop -2
Print loop -1

Each line number correspond to the value read from the file, so line 40 read the value 40 (0x28) from file, and line 220 read the value -36 (0xDC). As you can see on line 128 we read the value 0x80 and for some reason the VM gives us the value -3.

That lead me to investigate the -128 thing more. Trying to simply assign -128 to a DATA8 variable give me the VM PROGRAM VALIDATION error on the Brick. I also tried to assign 128 to a DATA8 just to see there was no funny business going on and that gave the same error.

So there you have it. The VM doesn’t seem to support -128 for DATA8.

Normally I wouldn’t really care as -128 works with DATA16 but the fact that the RGF file format start with two unsigned bytes that just cannot be read and trusted from the VM is a bit annoying. It means I would have to create my own file format to blit images from memory instead of blitting from “file”.

This, in combination with all values being signed, means I need to store values greater than 127 as DATA16, and values I want to have greater than 32767 is DATA32.

Also, the NUMBER_TO_STRING function also only accept DATA16, and there doesn’t seem to be any bitmask operations, so I still don’t know how to convert a 32 bit number to a string. There is a SELECTx function that I’ll look into.