- DIY
- A
vm5277, example of compilation for AVR
We write one code - compile for different 8-bit MCUs!
About the project:
https://vm5277.ru is a universal solution for embedded development that significantly reduces the time required to create firmware for 8-bit microcontrollers.
How it works:
You write code in a Java-like language (pure OOP, no headaches with pointers and unreadable code)
The compiler automatically generates optimized assembly code for the chosen platform
The code runs on a lightweight RTOS written in assembly for maximum performance
The assembler linker finalizes the project into a binary firmware file
What is included in the solution:
High-level compiler with Java-like syntax
Maximally optimized RTOS, entirely in assembly for each platform
Unified drivers - one API for UART, SPI, I2C, GPIO on all MCUs
Standard libraries (Runtime): Basic data types (Float, String), mathematical functions (Math), memory management, and other platform-independent abstractions.
High-Level Drivers: Unified APIs for working with peripheral devices: sensors (I2C/SPI), displays, SD cards, wireless modules (ESP8266, Bluetooth).
Key advantages:
Development speed: Create functionality in a high-level language faster than in C
Performance: RTOS and drivers optimized to the level of pure assembly, saving every byte and cycle
Portability: Transitioning from AVR to PIC or STM8 is a matter of a few edits, not learning and adapting libraries
The project is in its early stages, but I am actively working on it. You can already see how high-level code in a Java-like language turns into clean and efficient assembly! This is not the final version yet, but progress is being made.
What is already working in this example:
Interface inheritance: Test implements Number
Dynamic memory allocation: new operator
Polymorphism: method call via interface type variable (Number.toByte())
Type checking at runtime: is operator (analogous to instanceof)
Full object support: constructors, methods, fields.
Integration with RTOS: calling system functions (System.out).
Source code custom.j8b:
import rtos.System;
import rtos.RTOSParam;
class Main {
interface Number {
byte toByte();
}
interface Test implements Number {
byte toByte();
void test();
}
public class Byte implements Test {
private byte value;
public Byte(byte value) {
this.value = value;
}
public byte toByte() {
return value;
}
public void test() {
}
}
public static void main() {
System.setParam(RTOSParam.STDOUT_PORT, 0x12);
Number b1 = new Byte(0x08);
if(b1 is Byte) {
System.out(b1.toByte());
}
}
}
Final assembly code (draft, may contain errors)
.equ stdout_port = 18
.set OS_FT_DRAM = 1
.set OS_FT_STDOUT = 1
.include "devices/atmega328p.def"
.include "core/core.asm"
.include "dmem/dram.asm"
.include "j8b/inc_refcount.asm"
.include "j8b/dec_refcount.asm"
.include "j8b/instanceof.asm"
.include "j8b/clear_fields.asm"
.include "j8b/invoke_method.asm"
.include "stdio/out_num8.asm"
Main:
jmp j8bCMainMmain
;======== enter CLASS Main ========================
;======== enter CLASS Byte ========================
_j8b_meta10:
.db 15,2,13,1,14,2
.dw j8bC14CByteMtoByte,j8bC14CByteMtoByte,0
j8bC11CByteMByte:
ldi r16,6
ldi r17,0
mcall os_dram_alloc
std z+0,r16
std z+1,r17
std z+2,c0x00
ldi r16,low(_j8b_meta10*2)
std z+3,r16
ldi r16,high(_j8b_meta10*2)
std z+4,r16
mcall j8bproc_clear_fields_nr
_j8b_cinit12:
ldd r16,y+0
std z+5,r16
_j8b_methodend13:
ret
j8bC14CByteMtoByte:
ldd r16,z+5
jmp _j8b_methodend15
_j8b_methodend15:
ret
;======== leave CLASS Byte ========================
j8bCmainMmain:
push_z
ldi zl,low(_j8b_retpoint20)
push zl
ldi zl,high(_j8b_retpoint20)
push zl
ldi zl,8
push zl
jmp j8bC11CByteMByte
_j8b_retpoint20:
pop_z
mov r20,r16
mov r21,r17
_j8b_ifbegin22:
push_z
mov r30,r20
mov r31,r21
ldi r17,15
mcall j8bproc_instanceof_nr
pop_z
brne _j8b_ifend25
_j8b_ifthen23:
push_z
ldi zl,low(_j8b_retpoint21)
push zl
ldi zl,high(_j8b_retpoint21)
push zl
mov r30,r20
mov r31,r21
ldi r16,13
ldi r17,0
jmp j8bproc_invoke_method_nr
_j8b_retpoint21:
pop_z
mcall os_out_num8
_j8b_methodend19:
_j8b_ifend25:
_j8b_methodend18:
ret
;======== leave CLASS Main ========================
One of the RTOS functions (raw code may contain errors)
.IFNDEF J8BPROC_INVOKE_METHOD_NR
;-----------------------------------------------------------
J8BPROC_INVOKE_METHOD_NR: ;NR-NO_RESTORE - do not restore registers
;-----------------------------------------------------------
;Jump to method code
;IN: Z-address HEAP, ACCUM_L-interface ID, ACCUM_H-method
;order number in interface
;-----------------------------------------------------------
PUSH_Z
ADIW ZL,0x03
LD ACCUM_EH,Z+
LD ZH,Z
MOV ZL,ACCUM_EH
ADIW ZL,0x01 ;Skip class type ID
LPM ACCUM_EH,Z+ ;Get number of pairs (interface ID + number of methods)
MOV YL,ACCUM_EH ;Calculate address of method address block
LDI YH,0x00
LSL YL
ROL YH
ADD YL,ZL
ADC YH,ZH
LDI TEMP_L,0x00 ;Method order number in class
_J8BPROC_INVOKE_METHOD_NR__IFACEIDS_LOOP:
LPM ACCUM_EL,Z+ ;Get interface ID
CP ACCUM_EL,ACCUM_L
BREQ _J8BPROC_INVOKE_METHOD_NR__GOT_IFACE
LPM ACCUM_EL,Z+ ;Get number of methods in interface
ADD ACCUM_H,ACCUM_EL
DEC ACCUM_EH
BRNE _J8BPROC_INVOKE_METHOD_NR__IFACEIDS_LOOP
_J8BPROC_INVOKE_METHOD_NR__GOT_IFACE:
POP_Z
LSL ACCUM_H ;Shift to address considering method order number in class
ADD YL,ACCUM_H
ADC YH,C0x00
PUSH_Y
RET
.ENDIF
Key fragments of the generated assembly code:
1. Class metadata:
The compiler automatically generates a structure to support RTTI (Run-Time Type Information), necessary for instanceof.
_j8b_meta10:
.db 15,2,13,1,14,2 ; <-- These are the class metadata for `Byte`
.dw j8bC14CByteMtoByte, j8bC14CByteMtoByte, 0
2. Dynamic object creation in heap:
The new Byte(0x08) constructor code translates into a call to the dynamic memory manager (os_dram_alloc) and field initialization.
j8bC11CByteMByte:
ldi r16,6 ; Object size: 6 bytes!
mcall os_dram_alloc
std z+5, r16 ; Initialization of the `value` field
3. Type checking (is / instanceof):
The if(b1 is Byte) operator compiles into a call to the j8bproc_instanceof_nr procedure, which checks the object's metadata.
mcall j8bproc_instanceof_nr ; The magic type check happens here!
brne _j8b_ifend25 ; Conditional jump based on the result
4. Polymorphic method call:
The call b1.toByte() through the Number interface transforms into a universal method lookup and dispatch mechanism.
; invokeInterfaceMethod byte Number.toByte
ldi r16, 13
ldi r17, 0
jmp j8bproc_invoke_method_nr ; Dynamic method call!
5. Integration with system services:
Output to the "console" (System.out) is a call to the OS's service.
mcall os_out_num8 ; Output number via RTOS system call
What does this mean?
This proves that the vm5277 approach is viable. We can write in a high-level OOP language, and under the hood, we get code that:
Efficiently uses memory: objects are allocated in the heap, metadata is compact.
Maintains performance: key operations (memory allocation, type checking) are implemented in optimized assembly procedures.
Is portable: this same Java-like code, once work on platforms is complete, can run on PIC and STM8 (almost no limitations).
Write comment