311 lines
10 KiB
Swift
311 lines
10 KiB
Swift
|
// -------------------------------------------------------------
|
||
|
// 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()
|
||
|
|