Monthly Archives: July 2014

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

vmthread MAIN
{
	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
}

VM opcodes and their implementation

If I understand everything correctly the EV3 Brick is basically a 300 MHz ARM processor running some form of Linux, and on top of that there is a program running that is the LEGO MindStorms VM. So all the opcodes I use have been implemented in C and compiled to native code and that C source code is included in the GitHub repository, as well as the source code to the Linux distribution.

So whenever I’m looking for a function or want to know what parameter a function takes and what the parameter is used for I look into the C source. There is a great overview of all opcodes in bytecodes.c/h so that is usually my first stop. That let’s me look for opcodes that seem to be the one I want and I can easily find permutations of opcodes.

The parameters are listed by type but not described, so if I need more information than that I look for the native implementation of an opcode. In the native implementation the parameters are named, there’s usually a useful comment on the function, and if I still don’t understand I can read the C code to try to understand what it does.

I intend to create my own reference of the opcodes, together with descriptions, examples and comments from my experiments.

Data is also cool

I had created (copied) my first lms program. It was a small script with a few instructions. There was no branching or states or anything. So I figured the next step should be a bit more complex. I didn’t want to take on too much at once so I figured I could send a variable as parameter to UI_DRAW TEXT. So I had to create a variable. I had looked at other lms sources and had a vague idea of how it could be done, but I had to go back and look at the other scripts.

While looking into it I found what seems to be 5 different data types. DATA8, DATA16, DATA32, DATAF and DATAS. 8 bit, 16 bit and 32 bit signed (I assume) integers, a float which is probably 32 bit, and a string.

Defining a variable was as easy as adding “DATA8 VariableName” in the code. The tricky part was to match the variable type to the in parameter of the print function. I’m only assuming that the type have to match exactly, I doubt there will be any conversion done automatically.

So I had created my first variable. Assigning a value to it was a function called MOVE8_8( x, y ) where y gets the value of x, MOVE8_8( 3, VariableName ) is equivalent to VariableName = 3; in C. It was somewhere around this point I realized the syntax is similar to assembler. In X86 assembler you write “mov ax, bx” to assign the value of register bx into register ax. In ARM assembly you would write “mov r0, #3” to assign the value 3 to the register r0. And, the syntax that is the most similar, is the 68000 where you would type “move.b #3, a0” if you want to assign the byte value 3 to register a0.

Now I had my initialized variable and I was setting up the loop. Loops in LMS are also identical to most assembler syntax. You just insert your label anywhere in the code and end it with a : sign, so to create a label called MyLabel you would write MyLabel: and you would be able to jump to it. I also found the ADD8_8( x, y, z ) which is equivalent to z=x+y and I passed my variable into the print function. At the end of the loop I also wanted to reset my variable if it had become too big because I didn’t know how the VM would react if I tried to print text that would go outside the screen, so that was added.

I could simply jump back to my label, but doing so would mean there was no way out of the program. I still don’t know what would happen if I create a program that doesn’t have an exit of its own, if the VM always let the user exit programs with a certain key press, or if each program must have a way to exit gracefully. In any case I wanted to exit gracefully so I looked into the input functions available.

The function I had used in my hello world had a built in loop and would stall until the button was pressed, but I wanted to once per iteration check the status of a key and depending on the status either loop again or exit the loop. I found the function UI_BUTTON( EventType, Button, Status ) that would poll the key Button for the event type EventType and store it’s state in Status.

Being used to assembler I looked for a compare-function that would store the result in a flag internal to the VM and then do a conditional branch, so I found CP_GTEQ8( x, y, status ) and JR_FALSE( Status, Label ). Together they composed the conditional part of my loop and the program was done! Would it compile though?

I saved it to a new file called Vaxholm to keep a record of what I had done. (I was on a boat cruise while doing this so I named my three next experiments after which island I was on at the time) I had no idea what to expect from the compiler if something was wrong, so I ran it on my new app and once again I was informed of the size of the binary. I transferred the binary and started it and there it was, my first scrolling text on the brick!

And this is the source code:

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( TEXT,FG_COLOR,DrawX16,50,'hello world' )   //  Write 'hello world' at 10,50 (X,Y)
  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
}

I still need to figure out how to do timing on the brick. I’m hoping there is some form of vsync possible. If there is no opcode called something similar to vsyncwait then perhaps there is some function that does a vsync implicitly, like how setting the color value of a palette index works in qbasic. :) If there is no vsync available I will at least look into the existing timer/sleep functions.

Assemble ahoy!

I had stumbled upon what seemed like the holy grail!

I had found a tool that could compile source code to VM byte code. It was written in Java, so odds where that it would work on my computer even though all documentation for it only mentioned Windows and Linux. It was light weight, only a JAR and some Logo files.

All I needed to do was to get it to run. That includes two things.

Starting the assembler, and providing it with something to assemble. I had already found how to start it while looking through the make files. There where tons of LMS file to experiment with but they where too complicated for my comfort. Fortunately I had also looked through a file called apps.c, which seems like it would be a C file used by an app somewhere. It was in fact a C file but there is no C code in there, only a bunch of Doxygen style comments. At the very top of that file was an lms example that looked like this:

vmthread  MAIN
{
  UI_DRAW(FILLWINDOW,0x00,0,0)                  //  Clear screen
  UI_DRAW(TEXT,FG_COLOR,10,50,'hello world')    //  Write "hello world" at 10,50 (X,Y)
  UI_DRAW(UPDATE)                               //  Show the stuff
  UI_BUTTON(WAIT_FOR_PRESS)                     //  Wait for button press
}

I pasted it into a new file called nosleep.lms. (It was past 1am, I had to go to work the next day and I knew I wouldn’t get much sleep that night)

With my simple Hello World script ready I started the command line

Magnus-starke-dator:lmsasm magnusrunesson$ java -jar assembler.jar ~/Projects/ev3zipper/nosleep

And I got the error message “Can’t open file bytecodes.h in preprocess”

The assembler needed additional files. I looked around and found that fileread.logo needed bytecodes.h and I don’t know exactly how this is supposed to be setup because it just didn’t exist in the right folder. I had seen the bytecodes.c and bytecodes.h previously in a folder so I copied the .h file into the same folder as assembler.jar, tried again and got the error message that bytecodes.c was missing. I did the same procedure for that file and tried again.

Now the output read “57 bytes”

And there it was. nosleep.rbf. I had created my first .rbf file and my excitement was through the roof! Quickly I opened the memory browser in LabVIEW and transferred nosleep.rbf file to the Brick, used the file browser on the Brick and opened the nosleep.rbf, and this is what I saw on the device:

Hello world on the Brick

I was there! I had been able to create some source code that would compile to some sort of EV3 code, transfer it to the Brick and launch it on the Brick.

I had opened the door to my kind of programming for the Brick and I was about to embark on a fantastic journey of EV3 programming!

The reboot

It’s been done to Tomb Raider. It has also been done to Castlevania. Doom, Medal of Honor, SimCity, Prince of Persia, they’ve all faced the same fate.

The reboot!

Being a video games developer at heart, what would an epic adventure like exploring the EV3 be without a reboot.

My project, that used to be to create my own tools that mimicked the official tools, have hit what seemed like a brick wall. I had plenty of information to start writing my tools, and I had some sort of possibility to verify that the tools work, but my geist to create these tools had faded. I was interested in playing around with the Brick, not playing around with tools that would allow me to play around with the Brick. I was one step away from what I wanted to do, but it was a giant leap of a step.

While looking through the interwebs for inspiration to go on with programming the Brick I revisited an old link. https://github.com/mindboards/ev3sources/ but this time I had done my homework on the VM byte code.

I was reading through random files in the sources and stumbled upon a makefile with the variable LMS_TO_RBF. LMS to RBF. Converting LMS files to RBF files. In the world of make files you often store the name of the tools in variable names, that way you can write your make file using variables like LMS_TO_RBF and then you can change the value of LMS_TO_RBF if you want to, say, change the compiler depending on which platform you compile for.

With sweaty palms and a pounding heart I looked for the definition of LMS_TO_RBF, hoping it would lead me to a tool that could convert LMS to RBF, and by extension to an explanation of what LMS files are.

After some looking around I found this:
LMS_TO_RBF = ../adk/cmp

LMS_TO_RBF was pointing at a shell script called cmp. This shell script in turn looked like this:
java -jar assembler.jar “$src”/”$1”

There it was. An assembler that could compile LMS sources to EV3 VM byte code. Now I just needed to get it running!