ilo-vm/source/ilo.swift

311 lines
10 KiB
Swift
Raw Permalink Normal View History

// -------------------------------------------------------------
// ilo.swift, (c) charles childers
// -------------------------------------------------------------
// compile with:
//
// swiftc -O vm/ilo.swift -o ilo
// -------------------------------------------------------------
import Foundation
// -------------------------------------------------------------
// Configuration
// -------------------------------------------------------------
let imageSize: Int = 65536
let stackSize: Int = 33
let addressSize: Int = 257
// -------------------------------------------------------------
// Globals
// -------------------------------------------------------------
// +----------+------------------------------------------------+
// | Name | Contains |
// +==========+================================================+
// | input | input buffer ("keyboard") |
// | ip | instruction pointer |
// | memory | random access memory |
// | data | data stack |
// | address | address stack |
// +----------+------------------------------------------------+
var input: String = ""
var ip: Int = 0
var memory = [Int32](repeating: 0, count: imageSize)
var data = Stack(stackSize)
var address = Stack(addressSize)
// -------------------------------------------------------------
// Support Functions
// -------------------------------------------------------------
func getInt32FromData(data: NSData, offset: Int) -> Int32 {
let raw = NSRange(location: offset * 4, length: 4)
var i = [Int32](repeating: 0, count: 1)
data.getBytes(&i, range: raw)
return Int32(i[0])
}
// -------------------------------------------------------------
// Instructions
// -------------------------------------------------------------
func inst_no() { }
func inst_li() { ip += 1; data.push(memory[ip]) }
func inst_du() { data.push(data.tos()) }
func inst_dr() { data.drop() }
func inst_sw() { data.swap() }
func inst_pu() { address.push(data.pop()) }
func inst_po() { data.push(address.pop()) }
func inst_ju() { ip = Int(data.pop() - 1) }
func inst_ca() { address.push(Int32(ip)); ip = Int(data.pop() - 1) }
func inst_cc() { let dest = data.pop(); if (data.pop() != 0) { address.push(Int32(ip)); ip = Int(dest - 1) } }
func inst_cj() { let dest = data.pop(); if (data.pop() != 0) { ip = Int(dest - 1) } }
func inst_re() { ip = Int(address.pop()) }
func inst_eq() { let tos = data.pop(), nos = data.pop(); data.push((nos == tos) ? -1 : 0) }
func inst_ne() { let tos = data.pop(), nos = data.pop(); data.push((nos != tos) ? -1 : 0) }
func inst_lt() { let tos = data.pop(), nos = data.pop(); data.push((nos < tos) ? -1 : 0) }
func inst_gt() { let tos = data.pop(), nos = data.pop(); data.push((nos > tos) ? -1 : 0) }
func inst_fe() { let target = Int(data.pop()); data.push(memory[target]) }
func inst_st() { let addr = data.pop(), value = data.pop(); memory[Int(addr)] = value }
func inst_ad() { let tos = data.pop(), nos = data.pop(); data.push(nos &+ tos) }
func inst_su() { let tos = data.pop(), nos = data.pop(); data.push(nos &- tos) }
func inst_mu() { let tos = data.pop(), nos = data.pop(); data.push(nos &* tos) }
func inst_di() { let a = data.pop(), b = data.pop(); data.push(b % a); data.push(b / a) }
func inst_an() { let tos = data.pop(), nos = data.pop(); data.push(tos & nos) }
func inst_or() { let tos = data.pop(), nos = data.pop(); data.push(nos | tos) }
func inst_xo() { let tos = data.pop(), nos = data.pop(); data.push(nos ^ tos) }
func inst_sl() { let tos = data.pop(), nos = data.pop(); data.push(nos << tos) }
func inst_sr() { let tos = data.pop(), nos = data.pop(); data.push(nos >> tos) }
func inst_cp() {
var len = Int(data.pop())
var dest = Int(data.pop())
var src = Int(data.pop())
while (len != 0) {
if (memory[dest] != memory[src]) { data.push(0); return }
len -= 1
dest += 1
src += 1
}
data.push(-1)
}
func inst_cy() {
var len = Int(data.pop())
var dest = Int(data.pop())
var src = Int(data.pop())
while (len != 0) {
memory[dest] = memory[src]
len -= 1
dest += 1
src += 1
}
}
func inst_io() {
let device = Int(data.pop())
let devices: [(()->Void)] = [
io_cput, io_cget, io_rb, io_wb,
io_si, io_pc, io_of, io_sd]
if device < 0 || device > 7 { return }
devices[device]()
}
// -------------------------------------------------------------
// I/O Operations
// -------------------------------------------------------------
// +----------+------------------------------------------------+
// | Function | Action |
// +==========+================================================+
// | io_cput | Display a character to standard output |
// | io_cget | Read character from standard input source |
// | io_rb | Read block from storage |
// | io_wb | Write block to storage |
// | io_si | Save image (not implemented) |
// | io_pc | Reset ilo to default state, restarting |
// | io_of | Power off (exit) ilo |
// | is_sd | Return stack depths |
// +----------+------------------------------------------------+
func io_cput() {
let v = UnicodeScalar(Int(data.pop())) ?? UnicodeScalar(32)
print(Character(v!), terminator: "")
}
func io_cget() {
if (input.count > 0) {
let c = input.first!
guard let v = c.asciiValue else { return }
data.push(Int32(v))
input = String(input.dropFirst())
} else {
data.push(32)
}
}
func io_rb() {
let buffer = data.pop(), block = data.pop()
load_block(Int(block), to: Int(buffer))
}
func io_wb() {
let buffer = data.pop(), block = data.pop()
write_block(Int(block), to: Int(buffer))
}
func io_si() { }
func io_pc() {
load_image()
data.empty()
address.empty()
ip = -1
}
func io_of() { ip = 65536; exit(0) }
func io_sd() {
data.push(Int32(data.depth()))
data.push(Int32(address.depth()))
}
// -------------------------------------------------------------
// Block I/O
// -------------------------------------------------------------
func blocks() -> URL {
return URL(fileURLWithPath: "./ilo.blocks")
}
func load_block(_ block: Int, to: Int) {
let storage = blocks()
let fd = try! FileHandle(forReadingFrom: storage)
fd.seek(toFileOffset: UInt64(block * 4096))
let data = fd.readData(ofLength: 4096) as NSData
for i in 0 ... 1023 {
memory[to + i] = getInt32FromData(data: data, offset: i)
}
fd.closeFile()
}
func write_block(_ block: Int, to: Int) {
let storage = blocks()
let fd = try! FileHandle(forUpdating: storage)
fd.seek(toFileOffset: UInt64(block * 4096))
for i in 0 ... 1023 {
let word = Data(bytes: &memory[to + i], count: 4)
fd.write(word)
}
fd.closeFile()
}
// -------------------------------------------------------------
// Instruction Processing
// -------------------------------------------------------------
func process_instruction(opcode: Int32) {
let instructions: [(()->Void)] = [
inst_no, inst_li, inst_du, inst_dr, inst_sw,
inst_pu, inst_po, inst_ju, inst_ca, inst_cc,
inst_cj, inst_re, inst_eq, inst_ne, inst_lt,
inst_gt, inst_fe, inst_st, inst_ad, inst_su,
inst_mu, inst_di, inst_an, inst_or, inst_xo,
inst_sl, inst_sr, inst_cp, inst_cy, inst_io]
// skip `no` and invalid opcodes
if opcode < 1 || opcode > 29 { return }
instructions[Int(opcode)]()
}
func process() {
while input.count > 0 {
for inst in memory[ip].bytes {
process_instruction(opcode: Int32(inst))
}
ip += 1
}
}
// -------------------------------------------------------------
// Initialization
// -------------------------------------------------------------
func load_image() {
let fileURL = URL(fileURLWithPath: "./ilo.rom")
let data = NSData(contentsOf: fileURL)!
var i: Int = 0
while (i < 65536) {
memory[i] = getInt32FromData(data: data, offset: i)
i += 1
}
}
func bootstrap() {
if memory[0] == 0 {
load_image()
ip = 0
data.empty()
address.empty()
}
input = " "
process()
}
// -------------------------------------------------------------
// Stacks
// -------------------------------------------------------------
class Stack {
private var limit: Int
private var data: [Int32]
private var sp: Int = 0
init(_ size: Int) {
data = [Int32](repeating: 0, count: size)
limit = size
}
public func tos() -> Int32 { return data[sp] }
public func nos() -> Int32 { return data[sp - 1] }
public func push(_ n: Int32) { sp += 1; data[sp] = n }
public func pop() -> Int32 { sp -= 1; return data[sp + 1] }
public func drop() { sp -= 1 }
public func swap() { let a = data[sp]; let b = data[sp - 1]; data[sp] = b; data[sp - 1] = a }
public func depth() -> Int { return sp }
public func item(_ n: Int) -> Int32 { return data[n] }
public func empty() { sp = 0 }
}
// -------------------------------------------------------------
// Extensions to Existing Classes
// -------------------------------------------------------------
extension FixedWidthInteger where Self: SignedInteger {
var bytes: [Int8] {
var _endian = littleEndian
let bytePtr = withUnsafePointer(to: &_endian) {
$0.withMemoryRebound(to: Int8.self, capacity: MemoryLayout<Self>.size) {
UnsafeBufferPointer(start: $0, count: MemoryLayout<Self>.size)
}
}
return [Int8](bytePtr)
}
}
// -------------------------------------------------------------
// Top-level System
// -------------------------------------------------------------
func main() {
bootstrap()
while ip < 65535 {
input = readLine() ?? ""
input = " \(input) \n"
process()
}
}
main()