Skip to content

Instantly share code, notes, and snippets.

@PsichiX
Last active June 28, 2017 16:26
Show Gist options
  • Save PsichiX/4468e4cab8518e744aa0f5c6256075ad to your computer and use it in GitHub Desktop.
Save PsichiX/4468e4cab8518e744aa0f5c6256075ad to your computer and use it in GitHub Desktop.
car program
using I4.NET;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
namespace I4.Unity.Example
{
public class CarController : MonoBehaviour
{
#region Public types
public enum ControlMode
{
Driver,
Automatic
}
#endregion
#region Internal types
private class DebugDevice : Hardware.IDevice
{
public Guid Guid { get; private set; }
public CarController Owner { get; private set; }
public DebugDevice(CarController owner)
{
Guid = Guid.NewGuid();
Owner = owner;
}
public void Dispose() { }
public void OnBind(CPU.SerialBus.Port port) { }
public void OnMessage(IntPtr context)
{
var mode = NET.Sandbox.PopInt(context);
if (mode == 0) {
var addr = (long)Sandbox.PopInt(context);
var ptr = CPU.SerialBus.DecodePointerAddress(addr);
var msg = Marshal.PtrToStringAnsi(ptr);
WriteStatus(msg);
} else if (mode == 1) {
var value = Sandbox.PopSByte(context);
WriteStatus(value.ToString());
} else if (mode == 2) {
var value = Sandbox.PopShort(context);
WriteStatus(value.ToString());
} else if (mode == 3) {
var value = Sandbox.PopInt(context);
WriteStatus(value.ToString());
} else if (mode == 4) {
var value = Sandbox.PopLong(context);
WriteStatus(value.ToString());
} else if (mode == 5) {
var value = Sandbox.PopByte(context);
WriteStatus(value.ToString());
} else if (mode == 6) {
var value = Sandbox.PopUShort(context);
WriteStatus(value.ToString());
} else if (mode == 7) {
var value = Sandbox.PopUInt(context);
WriteStatus(value.ToString());
} else if (mode == 8) {
var value = Sandbox.PopULong(context);
WriteStatus(value.ToString());
} else if (mode == 9) {
var value = Sandbox.PopSingle(context);
WriteStatus(value.ToString());
} else if (mode == 10) {
var value = Sandbox.PopDouble(context);
WriteStatus(value.ToString());
}
}
public void OnTick() { }
public void OnUnbind(CPU.SerialBus.Port port) { }
private void WriteStatus(string message)
{
if (Owner)
UnityMainThreadDispatcher.Instance().Enqueue(() => Owner.WriteStatus(message, false));
}
}
private class SteeringWheelDevice : Hardware.IDevice
{
public Guid Guid { get; private set; }
public CarController Owner { get; private set; }
private volatile float m_steering;
public SteeringWheelDevice(CarController owner)
{
Guid = Guid.NewGuid();
Owner = owner;
}
public void Dispose() { }
public void OnBind(CPU.SerialBus.Port port) { }
public void OnMessage(IntPtr context)
{
var mode = Sandbox.PopInt(context);
if (mode == 0) {
Sandbox.Push(context, m_steering);
} else {
var value = Sandbox.PopSingle(context);
if (Owner)
m_steering = Owner.Steering = value;
}
}
public void OnTick()
{
if (!Owner)
return;
m_steering = Owner.Steering;
}
public void OnUnbind(CPU.SerialBus.Port port) { }
}
private class EngineControlDevice : Hardware.IDevice
{
public Guid Guid { get; private set; }
public CarController Owner { get; private set; }
private volatile float m_power;
public EngineControlDevice(CarController owner)
{
Guid = Guid.NewGuid();
Owner = owner;
}
public void Dispose() { }
public void OnBind(CPU.SerialBus.Port port) { }
public void OnMessage(IntPtr context)
{
var mode = Sandbox.PopInt(context);
if (mode == 0) {
Sandbox.Push(context, m_power);
} else if (mode == 1) {
var value = Sandbox.PopSingle(context);
if (Owner)
m_power = Owner.EnginePower = value;
}
}
public void OnTick()
{
if (!Owner)
return;
m_power = Owner.EnginePower;
}
public void OnUnbind(CPU.SerialBus.Port port) { }
}
private class ClockDevice : Hardware.IDevice
{
public Guid Guid { get; private set; }
private DateTime m_startTime;
private DateTime m_lastTickTime;
public ClockDevice()
{
Guid = Guid.NewGuid();
}
public void Dispose() { }
public void OnBind(CPU.SerialBus.Port port)
{
m_startTime = m_lastTickTime = DateTime.UtcNow;
}
public void OnMessage(IntPtr context)
{
var mode = Sandbox.PopInt(context);
if (mode == 0) {
var span = DateTime.UtcNow - m_startTime;
Sandbox.Push(context, (int)span.TotalMilliseconds);
} else if (mode == 1) {
var span = DateTime.UtcNow - m_lastTickTime;
Sandbox.Push(context, (int)span.TotalMilliseconds);
}
}
public void OnTick()
{
m_lastTickTime = DateTime.UtcNow;
}
public void OnUnbind(CPU.SerialBus.Port port) { }
}
#endregion
#region Public properties
public float Steering
{
get { return m_steer; }
set
{
if (control == ControlMode.Automatic)
m_steer = value;
}
}
public float EnginePower
{
get { return m_power; }
set
{
if (control == ControlMode.Automatic)
m_power = value;
}
}
#endregion
#region Inspector data
[Header("Vehicle")]
public ControlMode control = ControlMode.Driver;
public float enginePower = 1000;
public float maxSteer = 1;
[Header("UI")]
public Text statusText;
public Toggle runningToggle;
#endregion
#region Internal data
private Rigidbody m_rigidBody;
private float m_power;
private float m_steer;
private readonly List<GameObject> m_ground = new List<GameObject>();
private Hardware m_hardware;
private string m_bootFileCode = "";
#endregion
#region Life-cycle functionality
public void Start()
{
Application.logMessageReceivedThreaded += (string condition, string stackTrace, LogType type) => {
WriteStatus("* DEBUG: " + type.ToString() + "\n* MESSAGE: " + condition);
};
m_rigidBody = GetComponent<Rigidbody>();
var cpu = new CPU(
// CPU architecture (pointer size).
CPU.Architecture.X86,
// cache memory size.
1 << 20,
// bootload program cache size.
1 << 20,
// bootload file name.
"boot",
// Serial bus ports count.
4
);
cpu.OnError += error => UnityMainThreadDispatcher.Instance().Enqueue(() => WriteStatus(error.ToString()));
cpu.OnStatusChanged += status => UnityMainThreadDispatcher.Instance().Enqueue(() => {
if (status == Sandbox.Status.Stopped || status == Sandbox.Status.Idle) {
control = ControlMode.Driver;
}
});
cpu.OnLog += message => UnityMainThreadDispatcher.Instance().Enqueue(() => WriteStatus(message.ToString()));
m_hardware = new Hardware(1, 4);
m_hardware.InstallProcessor(0, cpu);
m_hardware.InstallDevice(0, new DebugDevice(this));
m_hardware.InstallDevice(1, new SteeringWheelDevice(this));
m_hardware.InstallDevice(2, new EngineControlDevice(this));
m_hardware.InstallDevice(3, new ClockDevice());
m_hardware.BindDeviceToProcessor(0, 0, 0);
m_hardware.BindDeviceToProcessor(1, 0, 1);
m_hardware.BindDeviceToProcessor(2, 0, 2);
m_hardware.BindDeviceToProcessor(3, 0, 3);
}
public void OnDestroy()
{
if (m_hardware != null) {
m_hardware.Dispose();
m_hardware = null;
}
}
public void Update()
{
if (control == ControlMode.Driver) {
if (m_hardware.IsRunning) {
WriteStatus("SHUTDOWN COMPUTER: " + m_hardware.Guid);
m_hardware.Shutdown();
}
m_power = Input.GetAxis("Vertical");
m_steer = Input.GetAxis("Horizontal");
if (runningToggle)
runningToggle.isOn = false;
} else {
if (!m_hardware.IsRunning) {
WriteStatus("BOOT PROGRAM");
m_hardware.GetProcessor(0).Boot.AddProgramData("boot", Encoding.UTF8.GetBytes(m_bootFileCode));
WriteStatus("START COMPUTER: " + m_hardware.Guid);
try {
if (!m_hardware.Start()) {
WriteStatus("COULD NOT START HARDWARE!");
control = ControlMode.Driver;
if (runningToggle)
runningToggle.isOn = false;
}
} catch (Exception error) {
WriteStatus(error.ToString());
control = ControlMode.Driver;
}
} else
m_hardware.ProcessTick();
}
if (m_ground.Count > 0) {
m_power = Mathf.Clamp01(m_power);
m_steer = Mathf.Clamp(m_steer, -1, 1);
transform.Rotate(Vector3.up, m_steer * Mathf.Abs(m_rigidBody.velocity.magnitude) * maxSteer * Time.deltaTime, Space.Self);
var force = transform.TransformVector(new Vector3(0, 0, m_power * enginePower * m_rigidBody.mass * Time.deltaTime));
m_rigidBody.AddForce(force);
}
}
public void OnCollisionEnter(Collision info)
{
if (info.gameObject.tag == "Ground" && !m_ground.Contains(info.gameObject))
m_ground.Add(info.gameObject);
}
public void OnCollisionLeave(Collision info)
{
if (info.gameObject.tag == "Ground" && m_ground.Contains(info.gameObject))
m_ground.Remove(info.gameObject);
}
#endregion
#region API
public void WriteStatus(string text, bool prependNewLine = true)
{
if (statusText)
statusText.text += (prependNewLine ? "\n" : "") + text;
}
#endregion
#region Event handlers
public void OnRunModeChanged(bool automatic)
{
control = automatic ? ControlMode.Automatic : ControlMode.Driver;
WriteStatus("RUN MODE CHANGED");
}
public void OnBootFileChanged(string content)
{
m_bootFileCode = content;
WriteStatus("BOOT FILE CHANGED");
}
#endregion
}
}
#!/usr/bin/env i4s
// every Intuicio program needs header.
#intuicio 4.0;
// you can tell how much stack you will need.
#stack 1k;
// declare architecture
// (can be 8, 16, 32 or 64 bits).
#pointersize 32;
// tell which routine is program entry point.
#entry @Main;
// entry point.
routine Main():i32
{
// call routine with parameters.
call @printString("3...\n":*i8);
call @wait(1000:i32);
call @printString("2...\n":*i8);
call @wait(1000:i32);
call @printString("1...\n":*i8);
call @wait(1000:i32);
call @printString(
"Engine start!\n":*i8
);
call @setEnginePower(0.7:f32);
call @wait(3000:i32);
call @printString(
"Turn left\n":*i8
);
call @setWheelSteering(-1.0:f32);
call @wait(3000:i32);
call @printString(
"Turn right\n":*i8
);
call @setWheelSteering(1.0:f32);
call @wait(3000:i32);
call @printString(
"Engine stop!\n":*i8
);
call @setEnginePower(0.0:f32);
call @wait(1000:i32);
// return value
// (0 means program end up with success).
ret 0:i32;
};
// wrapper routine that calls interruption signal.
// before calling signal you have to push some values
// on stack - this is how you communicate with game.
routine printString(text:*i8):
{
// push log value.
// (pointer to string - game will decode it's value).
push void $text;
// push debug log mode (0 means string value).
push void 0:i32;
// push CPU port index (to call device bound to it).
// 0 means debug log device.
push void 0:i32;
// push signal identifier (3 is reserved for user).
push void 3:i32;
// trigger interruption signal.
sig void;
};
routine setWheelSteering(steering:f32):
{
// push steering value.
push void $steering;
// push steering mode (1 means set value).
push void 1:i32;
// push CPU port index (to call device bound to it).
// 1 means wheel steering device.
push void 1:i32;
// push signal identifier (3 is reserved for user).
push void 3:i32;
// trigger interruption signal.
sig void;
};
routine setEnginePower(power:f32):
{
// push engine power value.
push void $power;
// push power mode (1 means set value).
push void 1:i32;
// push CPU port index (to call device bound to it).
// 2 means engine power device.
push void 2:i32;
// push signal identifier (3 is reserved for user).
push void 3:i32;
// trigger interruption signal.
sig void;
};
routine wait(ms:i32):
<test:i8, start:i32, current:i32>
{
// push clock mode (0 means get milliseconds from start).
push void 0:i32;
// push CPU port index (to call device bound to it).
// 3 means clock device.
push void 3:i32;
// push signal identifier (3 is reserved for user).
push void 3:i32;
// trigger interruption signal.
sig void;
// pop clock time pushed by clock device.
// store it into local variable.
pop i32 => $start;
// local jump space (label place where you can jump).
loop:
push void 0:i32;
push void 3:i32;
push void 3:i32;
sig void;
pop i32 => $current;
// subtract start time from current time
// to calculate time difference.
sub void $current $start => $current;
// check if time difference is less than `ms` value.
ls void $current $ms => $test;
// if it's less, then jump back to loop.
// if it's not, then exit routine (waiting is done).
jif $test %loop %exit;
exit:
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment