// ------------------------------------------------------------- // 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.size) { UnsafeBufferPointer(start: $0, count: MemoryLayout.size) } } return [Int8](bytePtr) } } // ------------------------------------------------------------- // Top-level System // ------------------------------------------------------------- func main() { bootstrap() while ip < 65535 { input = readLine() ?? "" input = " \(input) \n" process() } } main()