Monthly Archives: August 2014

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.

Data types, signed vs. unsigned. Or confused vs. informed.

I am trying to read bytes from a file. So I open the file, read 1 byte, and read another byte.

Then I print the read bytes through my own logging functions. And I get bizarre numbers written to the log.

The first number read from the file is 0xB2 and is printed as -78 to the log. That is ok since the variable that holds the read byte is a signed 8 bit variable. But the next number that is read is stored as 0x80 in the file, but printed as -3 in the log. This is very strange to me. Why is it not printed as -128, as you’d expect?

In any case, while digging around in the LMS2012 source I found this snippet:

//        VM DATA TYPES

typedef   SBYTE                 DATA8;  //!< VM Type for 1 byte signed value
typedef   SWORD                 DATA16; //!< VM Type for 2 byte signed value
typedef   SLONG                 DATA32; //!< VM Type for 4 byte signed value
typedef   FLOAT                 DATAF;  //!< VM Type for 4 byte floating point value

//        VM VARIABLE TYPES

typedef   UBYTE             VARDATA;    //!< Variable base type
typedef   UBYTE             IMGDATA;    //!< Image base type

typedef   UWORD             PRGID;      //!< Program id type

typedef   UWORD             OBJID;      //!< Object id type
typedef   IMGDATA*          IP;         //!< Instruction pointer type
typedef   VARDATA*          LP;         //!< Local variable pointer type
typedef   VARDATA*          GP;         //!< global variable pointer type

typedef   ULONG             IMINDEX;    //!< ImageData index type
typedef   ULONG             GBINDEX;    //!< GlobalBytes index type
typedef   ULONG             LBINDEX;    //!< LocalBytes index type
typedef   UWORD             TRIGGER;    //!< TriggerCount type
typedef   UBYTE             PARS;       //!< NoOfParameters type
typedef   SLONG             IMOFFS;     //!< ImageData offset type

typedef   DATA16            HANDLER;    //!< Memory list index

So it seems there are no unsigned numbers exposed to the VM. So if I read 0xB2 from file, it will forever be stored as -78, even when moved from a signed 8 bit variable to a signed 16 bit variable. Unless I convert it myself of course, but there is no built in converting from signed to unsigned so I need to make my own functions to handle that too.

Unity + EV3 = not true yet but maybe next project?

Thinking back on what I’d done previously I remembered that I had included the LabVIEW DLL’s into a Unity project to peek at the symbols in the DLL’s. And today I remembered one of the very first things I was asked when I told people I had bought my Mindstorms. “Does it work with Unity?”

It can’t be that hard to write a simple program that runs on the Brick and relay the information from the ports back to a C# plugin used by Unity.

I think I’ll add that to my to do list too. :)

Moar log!

So logging strings is cool. But I also need to log numbers. So I’ve created a function that accepts a string and a 16 bit number, converts the number to a string, concatinates the message with the number and pass the resulting string on to the log function I’ve shown previously.

This is the resulting function:

subcall WriteLogNumber
{
  IN_S  Message 100
  IN_16 Number

  DATAS   NumberString  7
  DATAS   FullString    128
  DATA16  FiguresCounterNumber
  DATA16  FiguresCounter

  // Check if value is negative. In that case we need to make it positive
  JR_LT16( Number, 0, NegateNumber )
  MOVE16_16( Number, FiguresCounterNumber )
  JR( StartCounting )

NegateNumber:
  // Value is negative. Negate it back to positive and get going
  MUL16( Number, -1, FiguresCounterNumber )

StartCounting:
  JR_LT16( FiguresCounterNumber, 10, Figures10 )
  JR_LT16( FiguresCounterNumber, 100, Figures100 )
  JR_LT16( FiguresCounterNumber, 1000, Figures1000 )
  JR_LT16( FiguresCounterNumber, 10000, Figures10000 )
  MOVE16_16( 5, FiguresCounter )
  JR( CheckNegative )

Figures10:
  MOVE16_16( 1, FiguresCounter )
  JR( CheckNegative )

Figures100:
  MOVE16_16( 2, FiguresCounter )
  JR( CheckNegative )

Figures1000:
  MOVE16_16( 3, FiguresCounter )
  JR( CheckNegative )

Figures10000:
  MOVE16_16( 4, FiguresCounter )
  JR( CheckNegative )

CheckNegative:
  JR_GTEQ16( Number, 0, StartPrinting )   // If number is >= 0 then we can simply start printing
  ADD16( 1, FiguresCounter, FiguresCounter )

StartPrinting:
  STRINGS( NUMBER_TO_STRING, Number, FiguresCounter, NumberString )
  STRINGS( DUPLICATE, Message, FullString )
  STRINGS( ADD, FullString, NumberString, FullString )

  CALL( WriteLog, FullString )
}

And to try it out I did this code:

CALL( WriteLog, 'Hello log world' )
CALL( WriteLogNumber, 'Hello int16: ', 0 )
CALL( WriteLogNumber, 'Hello int16: ', 1 )
CALL( WriteLogNumber, 'Hello int16: ', 9 )
CALL( WriteLogNumber, 'Hello int16: ', 10 )
CALL( WriteLogNumber, 'Hello int16: ', 11 )
CALL( WriteLogNumber, 'Hello int16: ', 99 )
CALL( WriteLogNumber, 'Hello int16: ', 100 )
CALL( WriteLogNumber, 'Hello int16: ', 101 )
CALL( WriteLogNumber, 'Hello int16: ', 999 )
CALL( WriteLogNumber, 'Hello int16: ', 1000 )
CALL( WriteLogNumber, 'Hello int16: ', 1001 )
CALL( WriteLogNumber, 'Hello int16: ', 9999 )
CALL( WriteLogNumber, 'Hello int16: ', 10000 )
CALL( WriteLogNumber, 'Hello int16: ', 10001 )
CALL( WriteLog, 'And now, the negatives!' )
CALL( WriteLogNumber, 'Hello int16: ', -1 )
CALL( WriteLogNumber, 'Hello int16: ', -9 )
CALL( WriteLogNumber, 'Hello int16: ', -10 )
CALL( WriteLogNumber, 'Hello int16: ', -11 )
CALL( WriteLogNumber, 'Hello int16: ', -99 )
CALL( WriteLogNumber, 'Hello int16: ', -100 )
CALL( WriteLogNumber, 'Hello int16: ', -101 )
CALL( WriteLogNumber, 'Hello int16: ', -999 )
CALL( WriteLogNumber, 'Hello int16: ', -1000 )
CALL( WriteLogNumber, 'Hello int16: ', -1001 )
CALL( WriteLogNumber, 'Hello int16: ', -9999 )
CALL( WriteLogNumber, 'Hello int16: ', -10000 )
CALL( WriteLogNumber, 'Hello int16: ', -10001 )

That generates this log file:
Hello log world
Hello int16: 0
Hello int16: 1
Hello int16: 9
Hello int16: 10
Hello int16: 11
Hello int16: 99
Hello int16: 100
Hello int16: 101
Hello int16: 999
Hello int16: 1000
Hello int16: 1001
Hello int16: 9999
Hello int16: 10000
Hello int16: 10001
And now, the negatives!
Hello int16: -1
Hello int16: -9
Hello int16: -10
Hello int16: -11
Hello int16: -99
Hello int16: -100
Hello int16: -101
Hello int16: -999
Hello int16: -1000
Hello int16: -1001
Hello int16: -9999
Hello int16: -10000
Hello int16: -10001

Feeling inclusive?

I’ve just started looking into including LMS files from other LMS files and there doesn’t seem to be a pre processor directive in the assembler that does it. I’m not very experienced with logo but from what I can tell the assembler is made up of three passes blllurb.

  • pass 0: process definitions
  • pass 1: fill in opcode, etc
  • pass 2: process labels
  • (copied from assembler.logo)

    Considering how the C pre processors handle the include directive I looked into pass 0. It seems to be four things that the assembler looks for in pass 0.

  • vmthread
  • subcall
  • define
  • label
  • So there doesn’t seem to be an include directive for LMS. I’ve quickly looked through the other passes but none of them seemed to do anything like it.

    After that setback I remembered that I had found a file called template.lms. It was used by a LMS called Brick Program.lms, so I opened that one up and looked into it. Template.lms seems to include template code for the programming blocks you use when you use the Block Programmer on the EV3 Brick, so to the best of my knowledge it seems like the Block Program is the UI for programming the EV3 Brick on the brick, and it use the template.lms to include code snippets that in the end will make up the code that the user generated through the flow chart.

    Then I remembered the LOAD_IMAGE function, that loads byte code for execution. But LOAD_IMAGE doesn’t seem to allow execution of selected functions in the loaded file, only executing the full file.

    So for now there doesn’t seem to be a way to reuse LMS code in different projects. But since that is a matter of a simple text processing directive then perhaps I’ll look into writing my own pre processor that manages it. I’ll add it to my to do list. :)