Talks

It is time to build your mruby VM on the microcontroller?

In 2020, I find a mini-arcade maker product that uses ESP8622 and MicroPython. Since I know the mruby/c can run on the ESP32 but it doesn't support running on the ESP8622. Is it possible to implement our own mruby VM and execute Ruby on any microcontroller we want to use it? This talk will show my progress to run a simple mruby script on the ESP8622 by implementing my own small mruby VM.

RubyKaigi Takeout 2021: https://rubykaigi.org/2021-takeout/presentations/elct9620.html

RubyKaigi Takeout 2021

00:00:00.240 Hello, everyone.
00:00:01.520 This is my talk about how to build your own mruby VM on the microcontroller.
00:00:08.240 Let me share my journey with you.
00:00:11.280 In 2020, I came across a mini arcade maker product that uses an ESP8266 microcontroller.
00:00:17.199 I thought I could potentially run Ruby on it because it supports MicroPython.
00:00:22.240 However, when I looked for existing solutions, there were none available for this microcontroller.
00:00:27.119 So I decided to build my own Ruby VM to allow it to run Ruby scripts on the ESP8266.
00:00:39.920 I am from Taiwan and this is my contribution to the programming community.
00:00:44.079 Using Ruby and Ruby on Rails, I help clients build their businesses through consulting and software development. We have customers worldwide, including Japan, the U.S., Taiwan, and Singapore.
00:01:19.119 Currently, I am working on a game project with friends, where the server is written in Ruby and we are adapting the client to HTML5 or Unity.
00:01:29.200 This game was open-sourced on GitHub after the company in Japan ceased operations.
00:01:31.680 We took the source code and are trying to rebuild and relaunch the game.
00:01:39.520 Now, turning to my current project, I wanted to create an attractive experience for our players by implementing a small microcontroller.
00:01:45.360 Many people think building a programming language can be very difficult, and I agree that it requires a lot of learning.
00:01:56.720 However, with Ruby, the source code is more accessible than some other languages, making it easier to understand.
00:02:05.760 This encouraged me to start this project, despite it being my first time attempting something like this.
00:02:13.279 I had read about half of Ruby's source code but still didn't fully understand how to proceed, as its complexity is significant, especially compared to other languages.
00:02:25.280 After exploring various resources, I discovered the Adobe L1BN project, which seemed relevant to my goals, specifically in running a game on the web.
00:02:38.720 I intended to allow players to use Ruby for scripting within the game, but encountered challenges and wasn't successful initially.
00:02:45.680 Despite these difficulties, I found a small Ruby VM project that can run in a web browser, comprising only around 1,000 lines of code.
00:02:51.200 This small codebase made it very easy to read and understand the implementation of a Ruby VM, allowing me to focus on developing my own.
00:03:04.800 Now, let me discuss the core functionality of the VM, based on my understanding.
00:03:13.680 The programming language VM operates in a loop that reads each opcode and defines its behavior based on the instructions.
00:03:25.040 We start our program, reading the input from the compiled binary via the Adobe compiler that converts Ruby code into opcodes.
00:03:34.640 When we encounter an opcode, we determine the next action: whether to proceed, exit the program, or continue processing.
00:03:46.400 If we find a return opcode, we finish processing; otherwise, we keep looping until all tasks are completed.
00:03:58.480 Looking at the source code of the nWb VM, you'll find a loop that continuously reads opcodes and checks each one to determine the corresponding functionality.
00:04:07.360 To build a programming language with a VM, the first question is how to read the intermediate representation (IR) from a Ruby binary.
00:04:20.320 We can achieve this by utilizing the Adobe compiler with specific flags enabled to gather detailed information about the compilation process.
00:04:29.120 The compiler will showcase the opcodes, giving us critical insights into the implementation of Ruby scripts.
00:04:38.080 For example, if we compile a simple Ruby file that outputs 'Hello, World!', we can retrieve the associated opcodes and analyze them.
00:04:49.600 Next, I will explain the structure of the Adobe binary, which contains a header and the compiled intermediate representation.
00:05:00.639 If debugging is enabled, additional information will be available, but we should be aware that some data might be stripped in production.
00:05:11.960 In most cases, the header is generally 34 bytes, containing metadata that can be skipped when processing the binary.
00:05:19.920 The IR provides detailed data regarding opcodes and local variables necessary for executing your Ruby program.
00:05:30.000 The final section of the binary will contain the executable obcodes that the VM needs to process.
00:05:39.200 As we scan through the binary's header, we then turn our attention to the IR, which serves as the foundation for executing code.
00:05:48.800 By traversing the IR, we can identify the functions, strings, and any necessary data required by our Ruby programs.
00:06:03.200 When we receive the IR, we have to arrange the data properly to ensure that our code runs as intended.
00:06:15.600 Shifting our focus, let’s talk about how to effectively execute code within our Ruby VM.
00:06:21.680 After implementing the basic features, such as loading integers and performing addition, we can also include logic that allows us to return values.
00:06:32.000 The following sections will demonstrate how to execute Ruby code and process opcodes effectively.
00:06:41.680 In this simplified code sample, each opcode (e.g., loading an integer) will be executed directly by the VM.
00:06:49.600 Once we have implemented the essential features, we can proceed to more advanced operations.
00:07:01.320 It’s important to refine our VM to handle exceptions, variables, and return values efficiently.
00:07:12.560 Now, let’s cover how to define and implement methods within Ruby and how these are called during execution.
00:07:27.760 When we define a method in Ruby, the compiler will save information about the method's location and attributes.
00:07:36.120 We can locate method definitions through a symbol table and reference them during execution when they are called.
00:07:45.040 Let’s visualize how the compilation process works, particularly for an addition method.
00:07:50.400 Once the method is defined, any subsequent calls to this method will involve a lookup in the compiled code.
00:08:00.080 We’ll illustrate this with an example in which we compile and then analyze the resulting opcodes for a simple calculation.
00:08:14.560 The compiled output will reflect the actual operations performed, allowing us to observe the internal workings of our VM.
00:08:23.920 Next, we will examine how to call C functions from Ruby, which offers further capabilities within our VM.
00:08:37.360 By sourcing a method in C, we can improve performance for frequently used functions and operations.
00:08:48.160 Understanding how to bridge Ruby and C is crucial, as it deepens the interactions available to us when executing Ruby scripts.
00:09:00.080 We will analyze how to convert method calls into C method invocations, increasing efficiency and extending functionality.
00:09:12.640 In our implementation, every time we reach a method call, we will first check if it belongs to Ruby or C and respond accordingly.
00:09:25.560 When calling C methods, we can manipulate register values directly to pass arguments and retrieve results.
00:09:37.440 This flexibility allows our Ruby VM to leverage existing C libraries, making it more powerful and extensible.
00:09:48.120 Now, let’s turn our attention to error handling and ensuring our VM remains robust during execution.
00:10:00.080 Whenever an error occurs, our VM should handle it gracefully to prevent crashes.
00:10:11.160 We can accomplish this through well-placed error checks and response mechanisms that facilitate debugging.
00:10:23.440 By following these strategies, we can build a resilient Ruby VM tailored for microcontroller deployments.
00:10:34.960 Next, we’ll explore a simple demo showcasing my progress running a Ruby VM on a microcontroller.
00:10:48.640 I purchased a mini arcade station that utilizes a microcontroller unsupported by existing projects.
00:10:58.720 This motivated me to develop my own version of a Ruby VM tailored to the limitations of the hardware.
00:11:06.880 I also integrated APIs with Arduino, allowing me to render graphics generated by Ruby code on the microcontroller.
00:11:20.840 In the demonstration, I will showcase a simple animation built out of Ruby-based commands.
00:11:34.240 Please take a look as I run this Ruby code and watch it render directly on the mini arcade station.
00:11:48.080 Here, you can see the text being drawn on the screen as a result of the Ruby code executed by my VM.
00:12:02.000 This work involved careful interactions between the microcontroller and my Ruby VM implementation.
00:12:10.040 Additionally, I am currently writing a series of articles in Chinese detailing this project, featuring prototype code as well.
00:12:25.920 If you're interested, feel free to scan the QR code to access the articles and GitHub repo.
00:12:37.440 Looking ahead, I plan to enhance this Ruby VM with more features, continuing my exploration into embedded programming.
00:12:50.720 I've had a lot of fun working on this, but I also have other projects to balance, including IoT devices.
00:13:00.800 Overall, building your own Ruby VM and generating outputs from it can be an enjoyable endeavor.
00:13:13.440 Thank you all for listening! This concludes my talk about creating your own Ruby VM.
00:13:24.960 I hope you found this session insightful and feel empowered to explore similar paths.
00:13:34.880 I encourage you all to start your own projects and learn how to run Ruby in various environments. Thank you!