Java, Java everywhere and all the code did shrink...
Posted: January 05, 2014
Java Grinder is a tool that gives the ability to write programs in Java to run natively on microcontrollers, game consoles, and computers. Java Grinder is not a JVM, but instead translates byte-code from Java .class files into native assembly code much like a JIT or an "ahead of time" compiler. On the microcontrollers, there is a Java API to take advantage of the I/O ports, UART, SPI, and more. On the game console / computer side, there are Java API's to manipulate the graphics and sound chips. Currently, the following CPUs / platforms are supported (click on links for videos):
An interesting mention, Theodoulos Liontakis has created his own CPU / computer called the Lion Computer and added Java Grinder support for it.
The entire Java Grinder code-base was implemented in a modular object-oriented way to make easy to add support for different CPUs. I started support with MSP430 and Joe Davisson helped out by adding code for 6502, Atmel AVR8, and W65C816, along with APIs for Commodore 64, Apple IIgs, and W65C265SXB. I added dsPIC, Z80, x86, 68000, and TMS9900 with API's for Sega Genesis and start of an API for the TI84 calculator. Recently, Amstrad CPC support was added by Carsten Dost, I added PIC32 support, and Joe is working on a full API for the W65C265SXB board. Other future plans are to possibly add other byte-code parsers such as .NET so Java Grinder can also compile C# programs into native code, but we'll see if that ever happens. For a more complete description of how Java Grinder works, read the last section of this page.
The purpose of this project (I think for Joe too) is to simply just to learn something new and to create electronic art projects such as graphics and sound demos. I've seen on some forums people talking about this project ripping on Java, so I should also say I'm not this big Java advocate or anything. I picked Java because the people who designed the language did such a slick job on the .class file format and byte-code language that it was pretty easy to write a program like this. Most of the complexity of Java Grinder comes from generators, outputting assembly for the different platforms, and creating easy to use APIs. This project is just flat out fun. For the people who hate Java, my rule is pick the right tool for the right job. Sometimes Java is the right tool.
Java Grinder can do bits of optimization to improve the speed of the generated code. For example, despite Java being a stack based language, for most of the implemented CPUs, Java Grinder keeps values in registers. It can also do 1 or 2 instruction look-aheads to find better instructions for certain CPU's. An example of this is explained below. Also, the API is written so that every method is static and when the assembly language is generated, many API methods are replaced with inline assembly instead of function calls.
There are some restrictions with how this works:
These features could be added in the future if I get more time to work on this.
Related Projects @mikekohn.net
Joe Davisson worked on the 6502
support (and Atmel AVR8 support) and made this Commodore 64 demo
for Java Grinder.
YouTube Link: https://youtu.be/VN-8Z4VUjzg
Above is a Java demo running on a Sega Genesis (in an emulator). More info on the Sega Genesis Java page.
YouTube link: https://youtu.be/FdMh0TN6SlA
Above is a demo written in Java running on a Playstation 2.
Here's a dsPIC computing Mandelbrots optimized with the dsPIC's DSP instructions. For a full write-up on how this works and a video see here: dsPIC Mandelbrots.
YouTube link: https://youtu.be/0DLUh5J-1Ho
Here's another example showing and MSP430 and a Java Grinder program controlling an LCD display. Source code is again included in the samples directory of the repository. A couple other examples of Java Grinder running on MSP430 are the MSP430 VGA project and Cyborg Chicken project.
YouTube link: http://youtu.be/q0PdsJ9YUkE
In the git repository in the testing directory there is a program called LedBlink.java. To test it out to see how it works (you'll need to download naken_asm first), do the following:
The code in LedBlink.java looks like this:
Running javap -c testing/LedBlink.class, the Java assembly looks like this:
And the code in out.asm looks like this:
When running the program looks like this: http://youtu.be/loHsatbB1dQ
So my first step in writing this was to take the source code for naken_java, changed the formatting, and added a generator directory. I started out doing the generator doing some object oriented C using function pointers, and then decided it would probably be better just doing this in C++, so I converted the whole thing to C++.
In the generator directory there is an abstract super-class called Generator that contains all the functions that each architecture must implement (any unimplemented generator function returns -1 to let the compiler (common/compile.cxx) know that this function isn't implemented). In the java directory there is a net/mikekohn/java_grinder directory that have the JavaGrinder API. All classes/methods from this package are implemented in the Generator modules as functions with the name of the class in lowercase, underscore, and the method name unchanged. These functions are called by the functions in the objects/invoke.cxx file.
The flow of the code basically looks like this:
So Java is a stack-based assembly language, in other words if you have a = 3 + 5, Java will push 3 on the stack, then push 5 on the stack, and then with an add instruction pop 3 and 5 off the stack, add them, and put the result back on the stack. To try and avoid using the stack on a microcontroller, which would probably be pretty slow, the generated code uses a "stack" of registers first (r4-r11 on MSP430) and when it runs out it will start using the real stack. This should help the speed of the generated code.
Update December 23, 2018: I've spent a little time cleaning up some of the code in Java Grinder. The biggest change comes from, well.. since the JavaClass.cxx code came from a C project I was working on a few years back, all the strings were being done by fixed sized char arrays. This could backfire if a class, method, or field name is longer than the fixed size. I decided to change all those arrays to std::string for this reason, and also because if C++ hiring managers see the code they probably would have looked down on the C style char strings.
Update August 18, 2018: Playstation 2 support is added to Java Grinder. Not sure what I'll add next, but I have some ideas in mind.
Update January 23, 2018: Just a small update... I've been working on other projects so I haven't done much here in the past year or so. I did start adding more code for Playstation 2 support. I'm hoping in the next couple weeks I'll have a smallish demo working proving that it's possible to do what I'm wanting to do... which is drawing object using vector unit 1 to do rotations and placement in 3D space. Users of Java Grinder will simply have to load an object and draw it in an X,Y,Z location with 3 rotation values.
Update March 25, 2017: I got a triangle to draw on the Playstation 2, so I'm putting that code into Java Grinder and going to continue to make an API around it.
Update November 20, 2015: The Sega Genesis Java demo is done.
Update August 1, 2015: I have Z80 program loading (with audio), pixel plotting, image displaying, and other stuff working on Sega Genesis. More to come.
Update May 20, 2015: I have a simple Sega Genesis program written in Java writting in the MESS emulator. More to come...
Update April 11, 2015: TMS9900 / TI99/4A is now working and I have a YouTube clip of Java running on TI99/4A on this Retro Console Java page.
Update January 2, 2015: I've been kind of eyeballing the Intel Edison and Intel Galileo boards, so yesterday for fun I added x86 and x86_64 files to Java Grinder. I filled in about half of the x86 code. All that's missing is array stuff. Not sure how to do arrays. Should I use the C function malloc()? Or should I use the bss region to create some kind of heap? I put a little sample program in samples/x86 that assembles with The Netwide Assembler (nasm) and links to a C program so it runs on Linux from the command line.
Update October 23, 2014: I have the TMS9900 code mostly written along with part of a TI99 API. Currently it's able to draw text on the screen and even render a Mandelbrot in text mode. I'm adding a higher resolution graphics mode with some plotting routines so a nicer demo can be made. I also need to finish with the array code in the TMS9900.
Update April 6, 2014: I have Z80 code complete along with a little API for the TI84 Plus C calculator, but I haven't figured out how to get a program running in the Tilem emulator yet to make sure it works right. Hopefully soon.
Update March 15, 2014: Joe has been working on optimizing the 6502 code and building the C64 package for his demo. I've also been asked a couple times about a C generator instead of assembly, which to me is kind of odd. Why not just write the code in C and not add another layer? The purpose of Java Grinder is to basically do what a JIT does inside of a JVM, except compile the code before being put onto the flash memory of a micro. A microcontroller wouldn't have enough memory to hold the JIT code anyway. Assembly text seemed like a good idea instead of straight byte code because it makes it easier to bug the assembled code and it takes advantage of naken_asm's features (elf output, label computing, etc). Either way I decided to drop it in quickly anyway just to see what the generated code looks like. It should be working properly except arrays.
Update March 23, 2014: In preparation for Atmel AVR8 support in Java Grinder, I added a simulator for Atmel AVR8 yesterday in naken_asm. Joe will most likely be doing the Java Grinder code for that this week. I'm also adding a simulator for Z80 so Z80 can more easily be added to Java Grinder. I have a TI-84 calculator which would be quite cool to write programs for in Java :).
Update March 4, 2014: Joe Davisson has 6502 working and has a CommodoreDemo.java in the testing directory. The demo currently generates a Mandelbrot image. A nice demonstration of Java on the Commodore 64 :).
Update February 10, 2014: I've been adding opimizations to the compiler. Many Java instructions that used to take 2 instructions now take one instruction on the MSP430 which usually saves a cycle or two along with bringing code density down. Also, when using some of Java Grinder's packages, if a constant is passed to the Java Grinder method I was able to avoid putting things on the stack and directly write a literal to the micro's register. This is for both dsPIC and MSP430. More to come :).
Update February 9, 2014: The mul/div/mod instructions are supported in hardware for the dsPIC, but there aren't similar instructions for MSP430. Joe Davisson has now implemented a software version of these instructions so they should work now. He also has plans to add the 65xx code to Java Grinder (since he's the one who wrote the 65xx assembler for naken_asm) so maybe in the near future we'll have Java on the Commodore 64 :).
Copyright 1997-2019 - Michael Kohn