TinyEMU Data FlowStartup and config

Startup turns files into a runnable machine

The native executable stages work in a strict order: parse CLI, load and parse config, load auxiliary blobs, open host-backed devices, initialize console/display, instantiate the selected machine class, then enter the run loop.

1. CLI options seed defaults and overrides`main()` captures runtime choices before loading the VM config.

`drive_mode` defaults to snapshot, `ram_size` and `accel_enable` start unset, and `cmdline` is optional. After config loading, these values override the JSON-derived values so command-line flags win.

main(argc, argv)
  parse -m, -rw, -ro, -append, -no-accel
  path = config_file
  virt_machine_set_defaults(¶ms)
  virt_machine_load_config_file(¶ms, path, NULL, NULL)
  apply CLI overrides to params
2. JSON becomes `VirtMachineParams`Config parsing selects the machine class and records devices without opening them yet.

`virt_machine_parse_config()` validates `version`, resolves `machine` to a `VirtMachineClass`, then reads memory, BIOS/kernel/initrd, drives, 9P filesystems, ethernet interfaces, display, input, acceleration, and RTC options.

config JSON
  -> machine_name + vmc
  -> ram_size
  -> files[BIOS/KERNEL/INITRD/VGA_BIOS]
  -> tab_drive[]
  -> tab_fs[]
  -> tab_eth[]
  -> display/input/accel/rtc
3. Auxiliary files are loaded before machine initBIOS, kernel, initrd, and VGA BIOS bytes are pulled into memory buffers.

`virt_machine_load_config_file()` loads the config itself. `config_file_loaded()` parses it, then `config_additional_file_load()` walks the `files[]` table. Each relative path is made relative to the config path by `get_file_path()`.

config_load_file(config)
  -> config_file_loaded()
  -> virt_machine_parse_config()
  -> for each VM_FILE_* filename:
       get_file_path(cfg_filename, filename)
       config_load_file(file)
       copy bytes to files[index].buf / len
4. Host device handles are opened after overridesDrives, filesystems, network, and console/display become concrete host-backed objects.

The parser records descriptions, but `main()` opens the actual backends. Drives become `BlockDevice` callbacks, filesystems become `FSDevice`, ethernet entries become tap or slirp devices, and the UI path chooses SDL display or terminal console.

for each drive:
  local file -> block_device_init()
  URL        -> block_device_init_http()

for each fs:
  local dir -> fs_disk_init()
  URL       -> fs_net_init()

for each eth:
  user -> slirp_open()
  tap  -> tun_open()

display0 ? sdl_init() : console_init()
5. The selected machine class receives a complete parameter bundle`virt_machine_init()` is just a dispatch through `VirtMachineClass`.

The common startup code stops owning the machine topology once `virt_machine_init()` calls the class-specific initializer. For RISC-V, this is `riscv_machine_init()`; for x86 builds, `pc_machine_class` is also available.

VirtMachineParams p
  -> p.vmc->virt_machine_init(&p)
  -> returns VirtMachine*
  -> config buffers are freed
  -> carrier is enabled if net exists
  -> forever: virt_machine_run(machine)