Device virtualization - retrotruestory/M1DEV GitHub Wiki

Here's how to implement device virtualization for Magic-1:

        .cseg
        .global _init_virtual_devices
        .global _register_virtual_device
        .global _device_trap_handler

// Initialize virtual device system
_init_virtual_devices:
        enter   8
        push    a
        push    b
        
        // Setup device trap vector
        ld.16   b,#DEVICE_TRAP_VECTOR
        ld.16   a,#_device_trap_handler
        st.16   (b),a
        
        // Clear device table
        ld.16   b,#MAX_VIRTUAL_DEVICES
        ld.16   a,#device_table
.clear:
        st.16   (a),#0     ; Clear entry
        add.16  a,#4       ; Next entry
        sub.16  b,#1
        br.ne   .clear
        
        pop     b
        pop     a
        leave
        ret

Create the virtual device interface:

#ifndef VIRTUAL_DEVICES_H
#define VIRTUAL_DEVICES_H

#include <stdint.h>

#define MAX_VIRTUAL_DEVICES 16

// Virtual device types
#define VDEV_UART      1
#define VDEV_DISK      2
#define VDEV_CONSOLE   3

// Device operations structure
typedef struct {
    int  (*init)(void* dev);
    int  (*read)(void* dev, uint16_t port);
    void (*write)(void* dev, uint16_t port, uint8_t value);
    void (*interrupt)(void* dev);
} device_ops_t;

// Virtual device structure
typedef struct {
    uint16_t base_addr;    // Virtual I/O base address
    uint16_t size;         // I/O space size
    uint8_t  type;         // Device type
    void*    priv;         // Private device data
    device_ops_t ops;      // Device operations
} virtual_device_t;

// Function declarations
extern void init_virtual_devices(void);
extern int register_virtual_device(virtual_device_t* dev);
extern void unregister_virtual_device(uint16_t base_addr);

#endif

Implement virtual UART device:

#include "virtual_devices.h"

#define UART_RX_BUF_SIZE 16

typedef struct {
    uint8_t  data_reg;     // Data register
    uint8_t  status_reg;   // Status register
    uint8_t  control_reg;  // Control register
    uint8_t  rx_buffer[UART_RX_BUF_SIZE];
    uint8_t  rx_head;
    uint8_t  rx_tail;
    uint8_t  int_enable;   // Interrupt enable flags
} virtual_uart_t;

// UART device operations
static int uart_init(void* dev) {
    virtual_uart_t* uart = (virtual_uart_t*)dev;
    uart->status_reg = 0x20;  // TX empty
    uart->rx_head = uart->rx_tail = 0;
    return 0;
}

static int uart_read(void* dev, uint16_t port) {
    virtual_uart_t* uart = (virtual_uart_t*)dev;
    uint8_t offset = port & 0x07;
    
    switch(offset) {
        case 0:  // Data register
            if(uart->rx_head != uart->rx_tail) {
                uint8_t data = uart->rx_buffer[uart->rx_tail++];
                uart->rx_tail %= UART_RX_BUF_SIZE;
                if(uart->rx_head == uart->rx_tail)
                    uart->status_reg &= ~0x01;  // Clear data ready
                return data;
            }
            break;
            
        case 5:  // Status register
            return uart->status_reg;
    }
    return 0;
}

static void uart_write(void* dev, uint16_t port, uint8_t value) {
    virtual_uart_t* uart = (virtual_uart_t*)dev;
    uint8_t offset = port & 0x07;
    
    switch(offset) {
        case 0:  // Data register
            // Forward to real UART
            hw_write(UART0_BASE, value);
            break;
            
        case 3:  // Control register
            uart->control_reg = value;
            uart->int_enable = (value & 0x08) ? 1 : 0;
            break;
    }
}

Create virtual device manager:

#include "virtual_devices.h"

static virtual_device_t* devices[MAX_VIRTUAL_DEVICES];
static int num_devices = 0;

// Find device by address
static virtual_device_t* find_device(uint16_t addr) {
    for(int i = 0; i < num_devices; i++) {
        virtual_device_t* dev = devices[i];
        if(addr >= dev->base_addr && 
           addr < dev->base_addr + dev->size) {
            return dev;
        }
    }
    return NULL;
}

// Handle device access trap
void device_trap_handler(uint16_t addr, uint8_t is_write, uint8_t value) {
    virtual_device_t* dev = find_device(addr);
    if(!dev) {
        printf("Invalid device access: 0x%04x\n", addr);
        return;
    }
    
    if(is_write)
        dev->ops.write(dev->priv, addr, value);
    else
        return dev->ops.read(dev->priv, addr);
}

Example usage:

#include <stdio.h>
#include "virtual_devices.h"

int main() {
    // Initialize virtual device system
    init_virtual_devices();
    
    // Create virtual UART
    virtual_uart_t uart1;
    virtual_device_t vdev = {
        .base_addr = 0xE000,
        .size = 8,
        .type = VDEV_UART,
        .priv = &uart1,
        .ops = {
            .init = uart_init,
            .read = uart_read,
            .write = uart_write,
            .interrupt = NULL
        }
    };
    
    // Register device
    register_virtual_device(&vdev);
    
    // Test virtual UART
    // Write to console through virtual device
    const char* msg = "Hello from virtual UART\r\n";
    for(const char* p = msg; *p; p++) {
        device_trap_handler(0xE000, 1, *p);
    }
    
    return 0;
}

Update Makefile:

CC = clcc
AS = m1_as
CFLAGS = -O2
OBJECTS = virtual_devices.o virtual_uart.o device_manager.o test_vdev.o

test_vdev: $(OBJECTS)
    $(CC) $(CFLAGS) -o $@ $(OBJECTS)

%.o: %.s
    $(AS) -o $@ $<

%.o: %.c
    $(CC) $(CFLAGS) -c $<

clean:
    rm -f test_vdev *.o

Key features:

  1. Device Abstraction

    • Virtual device interfaces
    • Device operation mapping
    • I/O port virtualization
  2. Device Management

    • Dynamic device registration
    • Address space management
    • Interrupt handling
  3. Resource Control

    • Device access validation
    • Port access restrictions
    • Interrupt virtualization
  4. Hardware Emulation

    • Register emulation
    • Status tracking
    • Buffer management

This implementation allows:

  • Multiple virtual devices
  • Device isolation
  • Resource sharing
  • Hardware abstraction
  • Safe device access
⚠️ **GitHub.com Fallback** ⚠️