Last active
June 28, 2017 16:26
-
-
Save PsichiX/4468e4cab8518e744aa0f5c6256075ad to your computer and use it in GitHub Desktop.
car program
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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