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

Comments