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

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

# Doxygen

I’ve generated a Doxygen file from the LMS 2012 C source. I haven’t read all of it yet and so far I think I’ve seen mostly C stuff in the documentation and not so much about the VM.

Anyways, here it is: http://ev3.fantastic.computer/doxygen/index.html

Update! I found a Doxygen settings file in the ev3 sources and generated a new Doxygen file. It seems to be more focused on using the LMS2012 Virtual Machine rather than the underlying stuff. The new Doxygen is at the location mentioned above.

The Doxygen I created first, from all of the source, is moved to here: http://ev3.fantastic.computer/doxygen-all/index.html

# The stack again

So I had trouble accepting a string that was 256 bytes big. Reducing the size of the string to 128 bytes worked perfectly.

So I tried creating a function that accepts three strings that are 128 bytes long, and see what that does to the stack. It seems to be perfectly ok.

I created this script:

vmthread MAIN
{
CALL( WriteLog, 'Hello log world' )
CALL( WriteLog, 'What is going on?' )

CALL( WriteLog2, 'String1', 'String2', 'String3' )
}

subcall WriteLog
{
IN_S	Message		128

DATA16 hLogFile
FILE( OPEN_APPEND, 'log.txt', hLogFile )
FILE( WRITE_TEXT, hLogFile, DEL_NONE, Message )
FILE( WRITE_BYTES, hLogFile, 1, 10 )
FILE( WRITE_BYTES, hLogFile, 1, 13 )
FILE( CLOSE, hLogFile )
}

subcall WriteLog2
{
IN_S Message1 128
IN_S Message2 128
IN_S Message3 128

DATA16 hLogFile
FILE( OPEN_APPEND, 'log.txt', hLogFile )
FILE( WRITE_TEXT, hLogFile, DEL_NONE, Message1 )
FILE( WRITE_TEXT, hLogFile, DEL_NONE, Message2 )
FILE( WRITE_TEXT, hLogFile, DEL_NONE, Message3 )
FILE( WRITE_BYTES, hLogFile, 1, 10 )
FILE( WRITE_BYTES, hLogFile, 1, 13 )
FILE( CLOSE, hLogFile )
}


And the log looks like:

Hello log world
What is going on?
String1String2String3


So great success!

My entire log script file looks like this:

vmthread MAIN
{
CALL( ClearLog )

CALL( WriteLog, 'Hello log world' )
CALL( WriteLog, 'What is going on?' )
}

subcall ClearLog
{
DATA16 hLogFile
FILE( OPEN_WRITE, 'log12.txt', hLogFile )
FILE( CLOSE, hLogFile )
}

subcall WriteLog
{
IN_S	Message		128

DATA16 hLogFile
FILE( OPEN_APPEND, 'log12.txt', hLogFile )
FILE( WRITE_TEXT, hLogFile, DEL_NONE, Message )
FILE( WRITE_BYTES, hLogFile, 1, 10 )
FILE( WRITE_BYTES, hLogFile, 1, 13 )
FILE( CLOSE, hLogFile )
}


I intend to include the log.lms into projects so I can get some logging in.

Having said that, I have recently found the FILE WRITE_LOG function that I’m curios if I should use. Perhaps it is simply writing a unified time stamp to a log file, but there is also a function called FILE OPEN_LOG, which indicates that it is not a regular text log written to a text file but something else. (The documentation explains FILE OPEN_LOG like this, “Create file for data logging (if name starts with ‘~’,’/’ or ‘.’ it is not from current folder) (see \ref cinputsample “Example”)”)

So that is it for me today. I have a fully functional little log code.

Happy coding!

# Passing string as parameter

I am still looking into the logging functionality.

It seems like it should be trivial enough. Create a function that accept a string parameter. Create a string parameter and pass it to the function. Et voila!

But! Life in Legoland isn’t always that easy.

In sjobris.lms I had already proven that I could write to a log file. So what I wanted to do next was to create a function that does all the opening and closing of the log file.

First I created this program, thinking it would work.

vmthread MAIN
{
CALL( WriteLog, 'Hello log world' )
}

subcall WriteLog
{
IN_S	Message		256

DATA16 hLogFile
FILE( OPEN_APPEND, 'log.txt', hLogFile )
FILE( WRITE_TEXT, hLogFile, DEL_NONE, Message )
FILE( WRITE_BYTES, hLogFile, 1, 10 )
FILE( WRITE_BYTES, hLogFile, 1, 13 )
FILE( CLOSE, hLogFile )
}

The function WroteLog accept a string of maximum length 256 characters. I call it and I pass a string that should be perfectly legal. But nothing is written to the log file.

So I fiddle around with accepting a IN_8 to WriteLog since IN_8 is what WRITE_TEXT accepts, but it didn’t help. Now I ended up with a log file that would simply read ‘H’. (Pretty good, but not good enough 🙂 )

So I suspect that it is when a string is implicitly created by the call to WriteLog that something breaks, so I create a local string and pass it to WriteLog, like so:

vmthread MAIN
{
DATAS String 256
STRINGS( DUPLICATE, 'Hello log world', String )

CALL( WriteLog, String )
}

subcall WriteLog
{
IN_S	Message		256

DATA16 hLogFile
FILE( OPEN_APPEND, 'log.txt', hLogFile )
FILE( WRITE_TEXT, hLogFile, DEL_NONE, Message )
FILE( WRITE_BYTES, hLogFile, 1, 10 )
FILE( WRITE_BYTES, hLogFile, 1, 13 )
FILE( CLOSE, hLogFile )
}

Still no string written to the log file. The ones of you who are paying attention may wonder why I only open and append text and ask if that have anything to do with it. It doesn’t. I clean the log file between executions, so it is always cleared before I run my test.

So then I created this:

DATAS String 256

{
STRINGS( DUPLICATE, 'Hello log world', String )
CALL( WriteLog )
}

subcall WriteLog
{
DATA16 hLogFile
FILE( OPEN_APPEND, 'log.txt', hLogFile )
FILE( WRITE_TEXT, hLogFile, DEL_NONE, String )
FILE( WRITE_BYTES, hLogFile, 1, 10 )
FILE( WRITE_BYTES, hLogFile, 1, 13 )
FILE( CLOSE, hLogFile )
}

And that works. So it seems to be a problem of passing a string as parameter that I currently can’t understand to save my life.

.. aaand update ..

Before I had even finished writing the blog post. 🙂 And it is great news!

This works:

vmthread MAIN
{
CALL( WriteLog, 'Hejje' )
}

subcall WriteLog
{
IN_S	Message		16

DATA16 hLogFile
FILE( OPEN_APPEND, 'log12.txt', hLogFile )
FILE( WRITE_TEXT, hLogFile, DEL_NONE, Message )
FILE( WRITE_BYTES, hLogFile, 1, 10 )
FILE( WRITE_BYTES, hLogFile, 1, 13 )
FILE( CLOSE, hLogFile )
}


Reducing the size of the in parameter fixed the problem. Wohoo!

I was reading around in the existing lms code and there where so many functions accepting strings as parameters so I figured it must be something I had done wrong. All the functions I found had a variable for defining the maximum size, like

IN_S Name FILENAMESIZE

and

IN_S Text LINE

So it didn’t immediately occur to me that perhaps all strings that does work are shorter than 256 bytes. The longest value of LINE I have found is 128.

This leads me to believe that I ran out of stack space and that would mean that the string is placed on the stack. That would be very weird. I will immediately try to create multiple string parameters that combined are bigger than 256 bytes, but individually less than 128 bytes and see how the stack reacts.

Brb!

# Handles and adresses

So now that you’re all up to speed with my findings I’m going to add more random blog entries on stuff I find.

I am still looking into passing a string into a function and passing that string onto a function that accept a IN_8 parameter. I had seen the @ character in other LMS code but didn’t really figure out what it was, so I decided to look it up in the .logo files, and this is what I found:

if (first :i) = "@ [output get-hnd intern bf :i]
if (first :i) = "& [output get-adr intern bf :i]


So the @ character seems to indicate that you want the handle of something, and the & seems to be the address, probably the address of the variable.

I’m still not closer to logging properly but I figured the @ and & was interesting enough to share.

# Functions, debugging the code and the current state of business

Drawing a picture was as simple as placing the picture in the correct folder on the Brick and call the UI_DRAW BMPFILE function. But I’m not sure about the performance so I still wanted to try to open the file and draw the content through one of the other functions.

The file functions seemed to be straight forward enough. There was a FILE OPEN_READ, FILE READ_BYTES and FILE CLOSE. So I loaded the file into memory and tried using one of the other draw functions, but it didn’t work. For some reason nothing was shown on screen and I still don’t know why.

That also made it abundantly clear that I had no way of debugging my app.

Which led me to investigating a way to get information about what was going on in my apps. As a first attempt I figured I could write text to a log file.

I used the file functions to open a file, write data to the file and close it. I tried it on the brick and saw that a file was indeed created. Happiness! I transferred the file from the brick to my computer and opened it and it contained the string I had written to it. “Hello log”

And this was the code to do so:

vmthread MAIN
{
DATA16 hLogFile
FILE( OPEN_APPEND, 'log.txt', hLogFile )
FILE( WRITE_TEXT, hLogFile, '', 'Hello log\n' )
FILE( CLOSE, hLogFile )
}

I wanted to wrap the logging code in a function so I had to write my very first function in lms code.

Generally functions seems to be quite easy. You define a label and at the top of the function you define in and out parameters. This is where the problems started. I had a system function that accepted a parameter type (DATA8) which was holding a string. So I created a function that would accept a string and simply pass it on to the system function but when I accepted a DATA8 (as IN8) and passed it on to the system function only the first byte in the string was printed to the file.

So the system function accepts a DATA8 but I can see in the native code that it is type casted to a char pointer.

And that pretty much sums up my work so far. This is where I’m at right now. I have found a way to append a string to a file but I need to find a way to abstract it into a function. When I have this function it will be a lot easier for me to debug future code.

# What about them graphical pictures?

So text is cool. But what about the pixels? I was thinking about what to do next. I had variables and text on screen. So what would the next step be?

I want to make a game for the Brick and if I made a game for it then it should make use of the fact that it is a Lego robot. The game should use of the sensors and motors somehow.  I still haven’t decided what game to make but I love adventure games and I feel like I am on an adventure of sorts trying to find my way through the EV3 VM, so I figured I wanted to do a tile based adventure game, Zelda style.

So I need to draw graphics to the screen!

The opcodes seems to provide a few options. I can for example draw an image or set individual pixels. Setting individual pixels wasn’t super interesting since it would probably be too slow to use in a game anyways, so I looked at drawing an image. The function accepted 4 parameters, where one was a string with what seemed like a name. I looked into the native implementation of the function and saw that what I thought was a file name was only used as a lookup into a dictionary of loaded images so I was confused. How would images end up in that dictionary?

I looked at other image functions to see if any of them would load pictures and I looked at the file functions, but the function that load images was nowhere to be found. After looking around for a while I started suspecting that images would be loaded automatically so after a while I just copied the image file to the same folder as my RBF and started it, and lo and behold, the image was drawn on screen at the coordinate I asked for. It was as simple as that. I don’t know about the performance for this, if it is faster to load the image into memory first if I intend to draw tons of files.

vmthread  MAIN
{
DATA8 Run
DATA8 DrawX
DATA8 Flag
DATA16 DrawX16

MOVE8_8(1, Run)
MOVE8_8(0, DrawX)

Loop:
UI_DRAW( FILLWINDOW,0x00,0,0 )                 // Clear screen
MOVE8_16( DrawX, DrawX16 )
UI_DRAW( BMPFILE,FG_COLOR,DrawX16,50,'Bomb' )  // Draw bomb image
UI_DRAW( UPDATE )                              // Show the stuff

//ADD8(1, DrawX, DrawX)                        // DrawX++

// Check for reset
CP_GTEQ8(DrawX, 20, Flag)                      // IF DrawX >= 20 THEN Flag=1
JR_FALSE(Flag, NoResetDrawX)                   // IF Flag==1 THEN JP NoResetDrawX

// Do reset
MOVE8_8( 0, DrawX )
NoResetDrawX:

// 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
}