├── 8080 ├── init.lua ├── opnames.lua ├── opbs.lua └── ops.lua ├── 8080.lua ├── .gitignore ├── tests ├── zpu │ ├── reb_old.bin │ ├── bench.sh │ ├── test_input.bas │ └── emu_bench.lua └── test-bitop-shift.lua ├── tools ├── cpm.sh ├── si.sh └── spcvid.c ├── gen ├── 8080_names_gen.sh ├── 8080_opslen_gen.sh ├── 8080_ops_format.txt ├── 8080_ops.lua └── 8080_ops_gen.lua ├── Makefile ├── 8080_dis.lua ├── emu_8080.lua ├── programs.cfg ├── emu_zpu.lua ├── emu_8080_si.lua ├── emu_zpu_sinmmu.lua ├── emu_8080_cpm.lua ├── zpu_emus.lua ├── bitops.lua ├── zpu.lua ├── doc └── 8080_ops.txt └── memlib.lua /8080.lua: -------------------------------------------------------------------------------- 1 | 8080/init.lua -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | a.bin 2 | invaders.rom 3 | tools/spcvid 4 | /roms 5 | -------------------------------------------------------------------------------- /tests/zpu/reb_old.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vifino/lua-cpuemus/HEAD/tests/zpu/reb_old.bin -------------------------------------------------------------------------------- /tools/cpm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | stty raw -echo 3 | luajit emu_8080_cpm.lua "$@" 4 | stty cooked sane 5 | -------------------------------------------------------------------------------- /tests/zpu/bench.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | LUA=lua5.3 3 | cat tests/zpu/test_input.bas | time $LUA tests/zpu/emu_bench.lua 4 | -------------------------------------------------------------------------------- /tests/zpu/test_input.bas: -------------------------------------------------------------------------------- 1 | print "Hello World. This is a rather long string. Why? Cause it's slow!" 2 | print "Another string. Yay strings. This 'benchmark' is completely useless." 3 | 4 | end 5 | -------------------------------------------------------------------------------- /gen/8080_names_gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "-- Autogenerated by doc/8080_names_gen.sh. Don't touch." 3 | echo "return {" 4 | cat doc/8080_ops.txt | awk -F'\t' 'BEGIN {OFS = FS} { 5 | if ($2 != "-") printf "\t[%s] = \"%s\",\n", $1, $2 6 | }' 7 | echo "}" 8 | -------------------------------------------------------------------------------- /gen/8080_opslen_gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "-- Autogenerated by doc/8080_ops_gen.sh. Don't touch." 3 | echo "return {" 4 | cat doc/8080_ops.txt | awk -F'\t' 'BEGIN {OFS = FS} { 5 | if ($2 != "-") printf "\t[%s] = {%i, %i, %i},\n", $1, $3, $4, $5 6 | }' 7 | echo "}" 8 | -------------------------------------------------------------------------------- /tools/si.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | [ -f tools/spcvid ] || cc -o tools/spcvid -lSDL tools/spcvid.c 3 | rm -f /tmp/si_8080_input_fifo 4 | mkfifo /tmp/si_8080_input_fifo 5 | cat /tmp/si_8080_input_fifo | luajit emu_8080_si.lua roms/8080/invaders.rom | ./tools/spcvid > /tmp/si_8080_input_fifo 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile. 2 | 3 | # 8080 stuff 4 | 5 | 8080: 8080/opbs.lua 8080/opnames.lua 8080/ops.lua 6 | 8080/opbs.lua: doc/8080_ops.txt gen/8080_opslen_gen.sh 7 | gen/8080_opslen_gen.sh > 8080/opbs.lua 8 | 9 | 8080/opnames.lua: doc/8080_ops.txt gen/8080_names_gen.sh 10 | gen/8080_names_gen.sh > 8080/opnames.lua 11 | 12 | 8080/ops.lua: doc/8080_ops.txt gen/8080_ops_gen.lua gen/8080_ops.lua 8080/opnames.lua 13 | lua gen/8080_ops_gen.lua > 8080/ops.lua 14 | 15 | clean: 16 | rm -f 8080/opbs.lua 8080/opnames.lua 8080/ops.lua 17 | -------------------------------------------------------------------------------- /tests/test-bitop-shift.lua: -------------------------------------------------------------------------------- 1 | local s = require("bitops") 2 | assert(s.arshift(0x80000000, 32) == 0xFFFFFFFF, "arshift 32") 3 | assert(s.arshift(0x00000001, -31) == 0x80000000, "arshift -31") 4 | assert(s.arshift(0x00000001, -32) == 0, "arshift -32") 5 | assert(s.rshift(0x80000000, 32) == 0, "rshift 32") 6 | assert(s.rshift(0x00000001, -31) == 0x80000000, "rshift -31") 7 | assert(s.rshift(0x00000001, -32) == 0, "rshift -32") 8 | assert(s.lshift(0x00000001, 32) == 0, "lshift 32") 9 | assert(s.lshift(0x80000000, -32) == 0, "lshift -32") 10 | -------------------------------------------------------------------------------- /8080_dis.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- 8080 disassembler. 3 | 4 | local arg = arg or {...} 5 | 6 | local fname = arg[1] 7 | if not fname then 8 | error("Need filename") 9 | end 10 | local f, err = io.open(fname, "rb") 11 | if err then error(err) end 12 | 13 | -- Load bitops 14 | local bitops = require("bitops") 15 | -- Load 8080 16 | local l8080 = require("8080") 17 | -- Install bitops 18 | l8080.set_bit32(bitops) 19 | 20 | local memlib = require("memlib") 21 | 22 | -- Memory: ROM, RAM and peripherals. 23 | local s = f:read("*a") 24 | local memsz = #s 25 | local rom = memlib.backend.rostring(s, memsz) 26 | f:close() 27 | 28 | local function getb(inst, i, v) 29 | return rom:get(i) 30 | end 31 | local function setb(inst, i, v) 32 | return rom:set(i, v) 33 | end 34 | 35 | -- Get 8080 instance and set up. 36 | local inst = l8080.new(getb, setb) 37 | 38 | local pc = 0 39 | local name 40 | while pc < memsz do 41 | pc, name = inst:disasm(pc) 42 | print(name) 43 | end 44 | -------------------------------------------------------------------------------- /emu_8080.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- 8080 Emulator: Example usage. 3 | 4 | local arg = arg or {...} 5 | 6 | local fname = arg[1] 7 | if not fname then 8 | error("Need filename") 9 | end 10 | local f, err = io.open(fname, "rb") 11 | if err then error(err) end 12 | 13 | local memsz = 0x10000 14 | 15 | -- Load bitops 16 | local bitops = require("bitops") 17 | -- Load 8080 CPU 18 | local l8080 = require("8080") 19 | -- Install bitops 20 | l8080.set_bit32(bitops) 21 | 22 | local memlib = require("memlib") 23 | 24 | -- Memory: ROM, RAM and peripherals. 25 | local t = f:read(memsz) 26 | local rom = memlib.new("rostring", t, memsz) 27 | f:close() 28 | 29 | local mem = memlib.new("rwoverlay", rom, memsz) 30 | 31 | -- Address handlers/Peripherals 32 | --local addr_handlers = {} 33 | --local comp = memlib.compose(mem, addr_handlers) 34 | 35 | local function get(inst, i) 36 | return mem:get(i) 37 | end 38 | local function set(inst, i, v) 39 | return mem:set(i, v) 40 | end 41 | 42 | local function iog(inst, i) 43 | if i == 0 then return string.byte(io.read(1)) end 44 | return 0 45 | end 46 | local function ios(inst, i, v) 47 | if i == 0 then 48 | print(string.char(v)) 49 | end 50 | end 51 | 52 | local inst = l8080.new(get, set, iog, ios) 53 | 54 | local fmt = string.format 55 | while true do 56 | local pc = inst.PC 57 | local n, c = inst:run() 58 | print(fmt("0x%04x: %s -> 0x%04x (%i cycles)", pc, n, inst.PC, c)) 59 | end 60 | -------------------------------------------------------------------------------- /programs.cfg: -------------------------------------------------------------------------------- 1 | { 2 | ["bitops"] = { 3 | files = { 4 | ["master/bitops.lua"] = "/lib", 5 | }, 6 | dependencies = { 7 | }, 8 | name = "bitops chooser", 9 | description = "Automatic BitOps selection and sane-ifier", 10 | authors = "vifino", 11 | repo = "blob/master/bitops.lua", 12 | }, 13 | ["memlib"] = { 14 | files = { 15 | ["master/memlib.lua"] = "/lib", 16 | }, 17 | dependencies = { 18 | }, 19 | name = "memory library", 20 | description = "Low-level pluggable memory library", 21 | authors = "vifino", 22 | repo = "blob/master/memlib.lua", 23 | }, 24 | ["8080-emu"] = { 25 | files = { 26 | ["master/8080_dis.lua"] = "/bin", 27 | ["master/emu_8080.lua"] = "/bin", 28 | ["master/emu_8080_cpm.lua"] = "/bin", 29 | ["master/emu_8080_si.lua"] = "/bin", 30 | ["master/8080/init.lua"] = "/lib/8080", 31 | ["master/8080/opbs.lua"] = "/lib/8080", 32 | ["master/8080/opnames.lua"] = "/lib/8080", 33 | ["master/8080/ops.lua"] = "/lib/8080", 34 | }, 35 | dependencies = { 36 | ["bitops"] = "/", 37 | ["memlib"] = "/", 38 | }, 39 | name = "8080 emulator", 40 | description = "An 8080 emulator for Lua", 41 | authors = "vifino", 42 | repo = "tree/master", 43 | }, 44 | ["zpu-emu"] = { 45 | files = { 46 | ["master/emu_zpu.lua"] = "/bin", 47 | ["master/zpu.lua"] = "/lib", 48 | ["master/zpu_emus.lua"] = "/lib", 49 | }, 50 | dependencies = { 51 | ["bitops"] = "/", 52 | ["memlib"] = "/", 53 | }, 54 | name = "zpu emulator", 55 | description = "A ZPU emulator for Lua", 56 | authors = "vifino", 57 | repo = "tree/master", 58 | }, 59 | } -------------------------------------------------------------------------------- /emu_zpu.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- ZPU Emulator: Example usage. 3 | 4 | local arg = arg or {...} 5 | 6 | local fname = arg[1] 7 | if not fname then 8 | error("Need filename") 9 | end 10 | local f, err = io.open(fname, "rb") 11 | if err then error(err) end 12 | 13 | local memsz = 0x80000 14 | 15 | -- Load bitops 16 | local bitops = require("bitops") 17 | -- Load ZPU 18 | local zpu = require("zpu") 19 | -- Install bitops 20 | zpu.set_bit32(bitops) 21 | -- Load ZPU emulates and apply them 22 | local zpu_emulates = require("zpu_emus") 23 | zpu:apply(zpu_emulates) 24 | 25 | local memlib = require("memlib") 26 | 27 | -- Memory: ROM, RAM and peripherals. 28 | local t = f:read(memsz) 29 | local rom = memlib.new("rostring", t) 30 | f:close() 31 | 32 | local mem = memlib.new("rwoverlay32", rom, memsz) 33 | 34 | -- Address handlers/Peripherals 35 | local addr_handlers = {} 36 | addr_handlers[0x80000024] = function(comp, method, i, v) 37 | -- UART(O) 38 | if method == "get32be" then return 0x100 end 39 | if method == "set32be" then 40 | io.write(string.char(bitops.band(v, 0xFF))) 41 | io.flush() 42 | return 43 | end 44 | end 45 | 46 | addr_handlers[0x80000028] = function(comp, method, i, v) 47 | -- UART(I) 48 | if method == "get32be" then 49 | local inp = io.read(1) 50 | local ret = (inp and string.byte(inp)) or 0 51 | return bitops.bor(ret, 0x100) 52 | end 53 | end 54 | 55 | local comp = memlib.compose(mem, addr_handlers) 56 | 57 | local function get32(zpu_inst, i, v) 58 | return comp:get32be(i) 59 | end 60 | local function set32(zpu_inst, i, v) 61 | return comp:set32be(i, v) 62 | end 63 | 64 | -- Get ZPU instance and set up. 65 | local zpu_inst = zpu.new(get32, set32) 66 | zpu_inst.rSP = memsz 67 | 68 | while zpu_inst:run() do 69 | end 70 | -------------------------------------------------------------------------------- /tests/zpu/emu_bench.lua: -------------------------------------------------------------------------------- 1 | -- ZPU Emulator: Example usage. 2 | 3 | local fname = "tests/zpu/reb_old.bin" -- old version of reb, quite slow. 4 | local f, err = io.open(fname, "rb") 5 | if err then error(err) end 6 | 7 | local memsz = 0x80000 8 | 9 | -- Load bitops 10 | local bitops = loadfile("bitops.lua")(false, true) 11 | -- Load ZPU 12 | local zpu = dofile("zpu.lua") 13 | -- Install bitops 14 | zpu.set_bit32(bitops) 15 | -- Load ZPU emulates and apply them 16 | local zpu_emulates = dofile("zpu_emus.lua") 17 | zpu:apply(zpu_emulates) 18 | 19 | local memlib = require("memlib") 20 | 21 | -- Memory: ROM, RAM and peripherals. 22 | local t = f:read(memsz) 23 | local rom = memlib.backend.rostring(t, memsz) 24 | f:close() 25 | 26 | local mem = memlib.backend.rwoverlay(rom, memsz) 27 | 28 | -- Address handlers/Peripherals 29 | local addr_handlers = {} 30 | addr_handlers[0x80000024] = function(comp, method, i, v) 31 | -- UART(O) 32 | if method == "get32be" then return 0x100 end 33 | if method == "set32be" then 34 | io.write(string.char(bitops.band(v, 0xFF))) 35 | io.flush() 36 | return 37 | end 38 | end 39 | 40 | addr_handlers[0x80000028] = function(comp, method, i, v) 41 | -- UART(I) 42 | if method == "get32be" then 43 | local inp = io.read(1) 44 | local ret = (inp and string.byte(inp)) or 0 45 | return bitops.bor(ret, 0x100) 46 | end 47 | end 48 | 49 | local comp = memlib.compose(mem, addr_handlers) 50 | 51 | local function get32(zpu_inst, i, v) 52 | return comp:get32be(i) 53 | end 54 | local function set32(zpu_inst, i, v) 55 | return comp:set32be(i, v) 56 | end 57 | 58 | -- Get ZPU instance and set up. 59 | local zpu_inst = zpu.new(get32, set32) 60 | zpu_inst.rSP = memsz 61 | 62 | local st = os.clock() 63 | while zpu_inst:run() do 64 | end 65 | print("Ran for "..tostring(os.clock()-st).."s.") 66 | -------------------------------------------------------------------------------- /tools/spcvid.c: -------------------------------------------------------------------------------- 1 | // This is released into the public domain. 2 | // No warranty is provided, implied or otherwise. 3 | // 4 | // Screen-displayer application for Space Invaders emulator, 5 | // by 20kdc 6 | // Might be a bit broken 7 | // 8 | // Compile with: 9 | // gcc -lSDL -o spcvid spcvid.c 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | static char main_videodata[7168]; 16 | 17 | // Change these 3 to 1 to disable colour overlay 18 | #define COLOUR_WHITE 1 19 | #define COLOUR_RED 2 20 | #define COLOUR_GREEN 3 21 | 22 | #define COLOUR_COUNT 4 23 | static SDL_Color colourmap[COLOUR_COUNT] = { 24 | {0, 0, 0}, 25 | {255, 255, 255}, 26 | {255, 0, 0}, 27 | {0, 255, 0}, 28 | }; 29 | 30 | static void push_input(Uint8 * a, Uint8 * b) { 31 | write(1, a, 1); 32 | write(1, b, 1); 33 | } 34 | 35 | static void pull_video() { 36 | size_t wanted = 7168; 37 | char * point = main_videodata; 38 | while (wanted) { 39 | ssize_t p = read(0, point, wanted); 40 | if (p < 1) 41 | return; 42 | wanted -= p; 43 | point += p; 44 | } 45 | } 46 | 47 | static int get_video_colour(int x, int y) { 48 | if (y >= 32) { 49 | if (y < 64) 50 | return COLOUR_RED; 51 | if (y >= 184) { 52 | if (y >= 240) { 53 | if ((x >= 16) && (x < 134)) 54 | return COLOUR_GREEN; 55 | return COLOUR_RED; 56 | } 57 | return COLOUR_GREEN; 58 | } 59 | } 60 | return COLOUR_WHITE; 61 | } 62 | 63 | static void submit_video_char(int x, int y, Uint8 * px, char c) { 64 | for (int i = 0; i < 8; i++) { 65 | px[x + (y * 224)] = c & 1 ? get_video_colour(x, y) : 0; 66 | c >>= 1; 67 | y--; 68 | } 69 | } 70 | 71 | static void submit_video(SDL_Surface * video) { 72 | SDL_LockSurface(video); 73 | Uint8 * px = video->pixels; 74 | for (int i = 0; i < 7168; i++) { 75 | int x = i >> 5; 76 | int y = 255 - ((i & 31) << 3); 77 | submit_video_char(x, y, px, main_videodata[i]); 78 | } 79 | SDL_UnlockSurface(video); 80 | SDL_Flip(video); 81 | } 82 | 83 | int main(int argc, char ** argv) { 84 | 85 | // start SDL 86 | if (SDL_Init(SDL_INIT_EVERYTHING)) 87 | return 1; 88 | SDL_Surface * video = SDL_SetVideoMode(224, 256, 8, 0); 89 | 90 | // prep. palette 91 | SDL_SetColors(video, colourmap, 0, COLOUR_COUNT); 92 | 93 | // main loop. boring. 94 | int running = 1; 95 | int time = SDL_GetTicks(); 96 | Uint8 buttons1 = 1; 97 | Uint8 buttons2 = 0; 98 | while (running) { 99 | while (SDL_GetTicks() < time) 100 | SDL_Delay(5); 101 | time += (1000 / 60); 102 | SDL_Event se; 103 | while (SDL_PollEvent(&se)) { 104 | if ((se.type == SDL_KEYDOWN) || (se.type == SDL_KEYUP)) { 105 | int mask1 = 0; 106 | int mask2 = 0; 107 | switch (se.key.keysym.sym) { 108 | case SDLK_c: 109 | mask1 = 1; // COIN 110 | break; 111 | case SDLK_x: 112 | mask1 = 2; // P2 Start 113 | break; 114 | case SDLK_z: 115 | mask1 = 4; // P1 Start 116 | break; 117 | case SDLK_s: 118 | mask1 = 16; // P1 Fire 119 | break; 120 | case SDLK_a: 121 | mask1 = 32; // P1 Left 122 | break; 123 | case SDLK_d: 124 | mask1 = 64; // P1 Right 125 | break; 126 | 127 | case SDLK_t: 128 | mask2 = 4; // TILT 129 | break; 130 | case SDLK_k: 131 | mask2 = 16; // P2 Fire 132 | break; 133 | case SDLK_j: 134 | mask2 = 32; // P2 Left 135 | break; 136 | case SDLK_l: 137 | mask2 = 64; // P2 Right 138 | break; 139 | } 140 | if (mask1) { 141 | int v = (se.type == SDL_KEYDOWN) ^ (mask1 == 1); 142 | if (v) { 143 | buttons1 |= mask1; 144 | } else { 145 | buttons1 &= ~mask1; 146 | } 147 | } else { 148 | if (se.type == SDL_KEYDOWN) { 149 | buttons2 |= mask2; 150 | } else { 151 | buttons2 &= ~mask2; 152 | } 153 | } 154 | } 155 | if (se.type == SDL_QUIT) 156 | running = 0; 157 | } 158 | push_input(&buttons1, &buttons2); 159 | pull_video(); 160 | submit_video(video); 161 | } 162 | SDL_Quit(); 163 | return 0; 164 | } 165 | -------------------------------------------------------------------------------- /emu_8080_si.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- 8080 Emu: Space Invaders. 3 | -- Run tools/si.sh with spaceinvaders.rom in the base directory. 4 | 5 | local arg = arg or {...} 6 | 7 | local fname = arg[1] 8 | if not fname then 9 | error("Need filename") 10 | end 11 | local f, err = io.open(fname, "rb") 12 | if err then error(err) end 13 | 14 | local memsz = 0x4000 15 | 16 | -- Load bitops 17 | local bitops = require("bitops") 18 | 19 | local l8080 = require("8080") 20 | -- Install bitops 21 | l8080.set_bit32(bitops) 22 | 23 | local memlib = require("memlib") 24 | 25 | -- Localize functions 26 | local band, rshift, lshift = bitops.band, bitops.rshift, bitops.lshift 27 | local iowr, iord = io.write, io.read 28 | local sbyte, schar = string.byte, string.char 29 | local mfloor = math.floor 30 | local ioflush = io.flush 31 | 32 | -- Memory: ROM, RAM and peripherals. 33 | local t = f:read(memsz) 34 | local rom = memlib.new("rostring", t, memsz) 35 | f:close() 36 | 37 | local mem = memlib.new("rwoverlay", rom, memsz) 38 | 39 | local function get(inst, i) 40 | return mem:get(band(i, 0x3FFF)) 41 | end 42 | local function set(inst, i, v) 43 | local p = band(i, 0x3FFF) 44 | if p < 0x2000 then 45 | --error("Game wrote into ROM ~ decimal " .. p) 46 | --mem:set(bitops.band(p, 0x1FFF) + 0x2000, v) 47 | return 48 | end 49 | mem:set(p, v) 50 | end 51 | 52 | local shiftreg = 0 53 | local shiftregofs = 0 54 | 55 | local buttons1, buttons2 = 1, 0 56 | local function iog(inst, i) 57 | if i == 1 then 58 | return buttons1 59 | end 60 | if i == 2 then 61 | return buttons2 62 | end 63 | if i == 3 then 64 | --io.stderr:write("TEST\n") 65 | return rshift(band(lshift(shiftreg, shiftregofs), 0xFF00), 8) 66 | end 67 | return 0 68 | end 69 | local function ios(inst, i, v) 70 | if i == 4 then 71 | shiftreg = mfloor(shiftreg / 256) 72 | shiftreg = shiftreg + (v * 256) 73 | end 74 | if i == 2 then 75 | shiftregofs = v % 8 -- unsure if this should be % 8 76 | end 77 | end 78 | 79 | -- Get 8080 instance and set up. 80 | local inst = l8080.new(get, set, iog, ios) 81 | 82 | -- 2Mhz == 1000000 cycles per second (or maybe 2000000) 83 | -- Rate is 60 hz 84 | -- 16666 cycles per frame??? 85 | -- for now I'm just assuming vblank takes as long as draw 86 | -- bleh, int.fail errors. just giving it 1000000 cycles per frame (ridiculous I know) 87 | -- ok, new plan, interrupt some amount of instructions after it's ready. 88 | local vblank = true 89 | 90 | local cycleratio = 2000000 91 | local timeframe = 1 / 60 92 | local timer_vblank = mfloor((timeframe * 0.5) * cycleratio) 93 | local timer_draw = mfloor((timeframe * 0.5) * cycleratio) 94 | 95 | local timerval = timer_draw 96 | local nexttimer = timerval 97 | 98 | while true do 99 | --if inst.PC == 0x15D3 then io.stderr:write("BFR\n") inst:dump() end 100 | --if inst.PC == 0x15D6 then io.stderr:write("AFT\n") inst:dump() end 101 | local t = nexttimer 102 | if not inst.halted then 103 | local pc = inst.PC 104 | local n, c = inst:run() 105 | t = c 106 | --print(string.format("0x%04x: %s -> 0x%04x (%i cycles)", pc, n, inst.PC, c)) 107 | end 108 | if not inst.int_enable then 109 | nexttimer = timerval 110 | end 111 | if nexttimer > 0 then 112 | nexttimer = nexttimer - t 113 | else 114 | if vblank then 115 | --io.stderr:write("Interrupt VBLK_LEAVE\n") 116 | if not inst:interrupt(0xD7) then error("Int failed") end 117 | -- Dump upper half of frame data 118 | -- 96 * 32 = 3072 (0xC00) 119 | for i = 0x2400, 0x2FFF do 120 | iowr(schar(get(inst, i))) 121 | end 122 | ioflush() 123 | timerval = timer_draw 124 | else 125 | --io.stderr:write("Interrupt VBLK_ENTER\n") 126 | if not inst:interrupt(0xCF) then error("int failed") end 127 | -- Dump lower half of frame data. 128 | for i = 0x3000, 0x3FFF do 129 | iowr(schar(get(inst, i))) 130 | end 131 | ioflush() 132 | buttons1 = sbyte(iord(1)) 133 | buttons2 = sbyte(iord(1)) 134 | timerval = timer_vblank 135 | end 136 | nexttimer = timerval 137 | vblank = not vblank 138 | --io.stderr:write("Next timer " .. nexttimer .. ", point " .. systmr .. "\n") 139 | end 140 | end 141 | -------------------------------------------------------------------------------- /8080/init.lua: -------------------------------------------------------------------------------- 1 | -- Lua 8080 library. 2 | -- Hopefully, one day, it'll work. 3 | 4 | local _M = {} 5 | _M.__index = _M 6 | 7 | -- Local function cache. 8 | local fmt = string.format 9 | local bnot, band, bor, bxor, lshift, rshift 10 | local pcall = pcall 11 | 12 | -- Lookup tables. Hallelujah. 13 | -- Big as hell, so not stored here. 14 | local opbs = require("8080.opbs") 15 | _M.opbs = opbs 16 | local opnames = require("8080.opnames") 17 | _M.opnames = opnames 18 | 19 | -- Actual OP implementations, also in a table. 20 | local opslib = require("8080.ops") 21 | local ops = opslib.ops 22 | _M.ops = ops 23 | 24 | local function assert_bitfn(bit, name) 25 | assert(bit[name], "8080: Did not find function "..name.." in bitlib. We need it.") 26 | end 27 | function _M.set_bit32(bitlib) 28 | assert_bitfn(bitlib, "bnot") bnot = bitlib.bnot 29 | assert_bitfn(bitlib, "band") band = bitlib.band 30 | assert_bitfn(bitlib, "bor") bor = bitlib.bor 31 | assert_bitfn(bitlib, "bxor") bxor = bitlib.bxor 32 | assert_bitfn(bitlib, "lshift") lshift = bitlib.lshift 33 | assert_bitfn(bitlib, "rshift") rshift = bitlib.rshift 34 | opslib.inst_bitops(bitlib) 35 | _M.bit32 = bitlib 36 | end 37 | 38 | -- Helpers 39 | local function fmtaddr(a, b) 40 | -- Can't contain capitals because of D8/D16 41 | return fmt("$%02x%02x", a, b) 42 | end 43 | 44 | function _M.disasm(inst, pco) 45 | local pc = pco 46 | local b = inst:getb(pc) 47 | local l = opbs[b][1] 48 | local name = opnames[b] 49 | if name == nil then 50 | return pco+1, fmt("%04x ???", pco) 51 | end 52 | local name = name:gsub("adr", function() 53 | local addr = fmtaddr(inst:getb(pc+2), inst:getb(pc+1)) 54 | pc = pc + 2 55 | return addr 56 | end):gsub("D8", function() 57 | pc = pc + 1 58 | return fmt("0x%02x", inst:getb(pc)) 59 | end):gsub("D16", function() 60 | local res = fmt("0x%02x%02x", inst:getb(pc+2), inst:getb(pc+1)) 61 | pc = pc + 2 62 | return res 63 | end) 64 | return pco+l, fmt("%04x %s", pco, name) 65 | end 66 | 67 | local function callop(inst, op, p1, p2) 68 | local opfn = ops[op] 69 | if opfn == nil then 70 | error(fmt("NYI OP: 0x%02x (PC after exec would be 0x%02x): %s", op, inst.PC, opnames[op])) 71 | end 72 | local r, r2 = pcall(opfn, inst, p1, p2) 73 | if r then 74 | return r2 75 | end 76 | error(fmt("Error in op 0x%02x (%s) @ (PC after exec would be 0x%02x): %s", op, opnames[op], inst.PC, tostring(r2))) 77 | end 78 | 79 | function _M.interrupt(inst, ...) 80 | if inst.int_enable then 81 | -- The interrupt occurs. 82 | inst.int_enable = false 83 | callop(inst, ...) 84 | return true 85 | else 86 | -- 'Try again later'. 87 | return false 88 | end 89 | end 90 | 91 | -- Run 92 | function _M.run(inst) 93 | if inst.halted then 94 | error("The machine halted. You're supposed to stop executing now, or run time forward to the next interrupt.") 95 | end 96 | 97 | local pc = inst.PC 98 | local getb = inst.getb 99 | local op = getb(inst, pc) 100 | local opl = opbs[op] 101 | if opl == nil then 102 | error(fmt("l8080: Unknown OP 0x%02x", op)) 103 | end 104 | pc = band(pc + 1, 0xFFFF) 105 | local p1, p2 106 | if opl[1] == 2 then 107 | p1 = getb(inst, pc) 108 | pc = band(pc + 1, 0xFFFF) 109 | elseif opl[1] == 3 then 110 | p1 = getb(inst, pc) 111 | pc = band(pc + 1, 0xFFFF) 112 | p2 = getb(inst, pc) 113 | pc = band(pc + 1, 0xFFFF) 114 | end 115 | inst.PC = pc 116 | if not callop(inst, op, p1, p2) then 117 | return opnames[op], opl[2] 118 | end 119 | return opnames[op], opl[3] 120 | end 121 | 122 | local function dumpflag(inst, f, outf) 123 | if inst[f] then 124 | outf:write(f .. ":Y ") 125 | else 126 | outf:write(f .. ":N ") 127 | end 128 | end 129 | 130 | function _M.dump(inst, outf) 131 | outf = outf or io.stderr 132 | outf:write(fmt("PC %04x SP %04x A %02x\n", inst.PC, inst.SP, inst.A)) 133 | outf:write(fmt("BC %02x%02x DE %02x%02x HL %02x%02x\n", inst.B, inst.C, inst.D, inst.E, inst.H, inst.L)) 134 | dumpflag(inst, "s", outf) 135 | dumpflag(inst, "p", outf) 136 | dumpflag(inst, "z", outf) 137 | dumpflag(inst, "cy", outf) 138 | dumpflag(inst, "ac", outf) 139 | outf:write("\n") 140 | end 141 | 142 | -- Create a new 8080 instance 143 | function _M.new(getb, setb, iogb, iosb) 144 | assert(_M.bit32, "8080: Did not set bit32 library. Bailing out.") 145 | 146 | local l8080 = {} 147 | setmetatable(l8080, _M) 148 | 149 | -- Memory 150 | l8080.setb = setb 151 | l8080.getb = getb 152 | 153 | l8080.iosb = iosb 154 | l8080.iogb = iogb 155 | 156 | -- Registers 157 | l8080.A = 0 158 | l8080.B = 0 159 | l8080.C = 0 160 | l8080.D = 0 161 | l8080.E = 0 162 | l8080.H = 0 163 | l8080.L = 0 164 | 165 | l8080.SP = 0 166 | l8080.PC = 0 167 | 168 | -- Internal flags 169 | l8080.halted = false 170 | l8080.int_enable = false 171 | 172 | -- Flags 173 | 174 | l8080.z = true 175 | l8080.s = true 176 | l8080.p = true 177 | l8080.cy = false 178 | l8080.ac = true 179 | 180 | return l8080 181 | end 182 | 183 | return _M 184 | -------------------------------------------------------------------------------- /gen/8080_ops_format.txt: -------------------------------------------------------------------------------- 1 | Firstly, everything starts with the names in 8080_ops.txt. 2 | These are translated from, for example, "MVI C, D8" to "MVI C D8", and then to "MVI RB". 3 | This translation is done by splitop and arg_types for the most part. 4 | The translations are as follows: 5 | adr : X 6 | D8 : B 7 | D16 : BB 8 | M : M 9 | !FZ, FZ, !FC, FC, !FPE, FPE, !FS, FS : F 10 | : R (or alternatively the value directly. 11 | Two translations are performed - one with unknowns set to R, one 12 | otherwise. The non-R form is preferred, as it's more specific.) 13 | 14 | Next, the resulting 'form' ("RB" in the example case) is looked up in the table opfnargs. 15 | This table describes what additional ('s' is always passed, and refers to the machine register state) 16 | arguments are passed to the function. 17 | For most cases, the answer is ")" - only the 's' argument is passed. 18 | 19 | However, in cases that contain B or X, either 'b' or 'b2'/'b3' arguments are passed. 20 | For RB and B, this is just b - for RBB and X, this is b2 and b3. 21 | In X's case, "local addr = pair(b3, b2)" is added before the function code. 22 | (Note that b3, b2 is used because the processor is little-endian.) 23 | 24 | Next, a lookup of the table in 8080_ops.lua is performed with the mnemonic-form (ex. "MVI RB") name. 25 | There are three cases that can occur for how the code is translated, mapped via the table opfnregs: 26 | 27 | nil: Nothing happens. This is the case for unknown forms, assuming opfnargs doesn't cause issues. 28 | 29 | 1: "RP" is replaced with the register pair R is part of (see table "regpair"), 30 | "R" is replaced with the register name, 31 | and anything remaining preceding a "P" that is NOT "S" is translated to the 'partner' register (see table "regpartner"). 32 | 33 | 2: "RP1" is replaced with the register pair the first register is part of (see table "regpair"), 34 | "R1" is replaced with the first register name, 35 | "RP2" is replaced with the register pair the second register is part of (again, see "regpair"), 36 | and finally "R2" is replaced with the second register name. 37 | 38 | Note that the replacement rules occur in the order specified. 39 | Finally, if the first parameter is a valid flag, then a replacement 40 | of "F" with "s. == " occurs. 41 | 42 | The tables "regpair" and "regpartner" are provided here in a table form 43 | (idealized - "B,C" -> "pair(s.B, s.C)", and thus cannot be written to) 44 | 45 | REG|PAIR|PARTNER| 46 | ---|----|-------| 47 | B |B,C |C | 48 | C | |B | 49 | ---|----|-------| 50 | D |D,E |E | 51 | E | |D | 52 | ---|----|-------| 53 | H |H,L |L | 54 | L | |H | 55 | ---|----|-------| 56 | M |H,L |H,L | 57 | SP |SP |SP | 58 | 59 | --- The 's' class fields --- 60 | (Note: Keep in mind the R/RP/etc. autoreplaces, 61 | which SHOULD be prefixed with "s." for proper use.) 62 | 63 | s.A s.B s.C s.D s.E s.H s.L: Registers 64 | s.SP: Stack Pointer 65 | s.PC: Whatdayathink? 66 | (Increased by the appropriate amount after execution, no matter what. 67 | This should be expected.) 68 | 69 | s.cy: Carry Flag 70 | s.ac: Aux. Carry Flag 71 | s.p: Parity Flag 72 | s.s: Sign Flag 73 | s.z: Zero Flag 74 | s.int_enable: Interrupt Enable flag 75 | 76 | --- Available utility functions --- 77 | flaghandle(s, n, dont_set_carry): 78 | Takes the (unANDed) 8-bit result of a calculation and adjusts: 79 | Z: Is the ANDed value zero? 80 | S: What is bit 7 (sign bit)? 81 | then returns the ANDed value. 82 | 83 | addcda(a, b, cf) / subcda(a, b, cf): 84 | Returns ANDed result, and AC. 85 | addcdn(a, b, cf) / subcdn(a, b, cf): 86 | Returns ANDed result, and CY. 87 | addcdb(a, b, cf) / subcdb(a, b, cf): 88 | Returns ANDed result, AC, and CY. 89 | 90 | Note that in the above functions, "cf" should *generally* be omitted, 91 | unless this is ADC/SBB, in which case it should be the CY flag. 92 | 93 | applyb(s, r, ac, cy) 94 | Used with addcdb/subcdb to store ac/cy 95 | 96 | pair(high, low) 97 | ((high << 8) | low) 98 | 99 | spair(s, "regname1", "regname2", v) 100 | Writes to pair, and sets Carry accordingly. 101 | (example: spair(s, "B", "C", 0x1234) sets B to 0x12 and C to 0x34, 102 | turning off CY.) 103 | 104 | s_push16(s) 105 | Pushes a 16-bit word onto stack. 106 | 107 | s_pop16(s) 108 | Pops and returns a 16-bit word from stack. 109 | 110 | s_call(s, a) 111 | Pushes s.PC to stack, then sets s.PC to a. 112 | 113 | encode_psw(s) 114 | Returns an encoded PSW flags byte. 115 | 116 | decode_psw(s, f) 117 | Sets the flags according to a PSW flags byte. 118 | 119 | b_lsft(a) 120 | Shifts a left by 1 bit logically (leaving 0), returns ANDed a, and the boolean value of the bit that fell off. 121 | 122 | b_rsft(a) 123 | Shifts a right by 1 bit logically (leaving 0), returns ANDed a, and the boolean value of the bit that fell off. 124 | -------------------------------------------------------------------------------- /gen/8080_ops.lua: -------------------------------------------------------------------------------- 1 | -- OP template table. 2 | -- This is the actual logic. 3 | -- It gets generated into actual usable ops by the generator. 4 | return { 5 | -- Misc. 6 | ["NOP"] = "", 7 | 8 | ["LXI RBB"] = "s.R = b3 s.P = b2", 9 | ["LXI SPbb"] = "s.SP = pair(b3, b2)", 10 | 11 | ["MVI RB"] = "s.R = b", 12 | ["MVI MB"] = "s:setb(RP, b)", 13 | 14 | ["MOV RR"] = "s.R1 = s.R2", 15 | ["MOV MR"] = "s:setb(pair(s.H, s.L), s.R2)", 16 | ["MOV RM"] = "s.R1 = s:getb(pair(s.H, s.L))", 17 | -- "MOV MM" is HLT 18 | 19 | ["CMA"] = "s.A = bxor(s.A, 0xFF)", 20 | 21 | ["STC"] = "s.cy = true", 22 | ["CMC"] = "s.cy = not s.cy", 23 | 24 | ["LDA X"] = "s.A = s:getb(addr)", 25 | ["STA X"] = "s:setb(addr, s.A)", 26 | ["LDAX R"] = "s.A = s:getb(RP)", 27 | ["STAX R"] = "s:setb(RP, s.A)", 28 | ["LHLD X"] = "s.L = s:getb(addr) s.H = s:getb(band(addr + 1, 0xFFFF))", 29 | ["SHLD X"] = "s:setb(addr, s.L) s:setb(band(addr + 1, 0xFFFF), s.H)", 30 | 31 | ["SPHL"] = "s.SP = pair(s.H, s.L)", 32 | 33 | ["DAA"] = 34 | "if band(s.A, 0x0F) > 9 or s.ac then" .. 35 | " s.A, s.ac = addcda(s.A, 6) " .. 36 | "else s.ac = false end " .. 37 | "if band(s.A, 0xF0) > 0x90 or s.cy then" .. 38 | " local na, ncy = addcdn(s.A, 0x60)" .. 39 | " s.A = na s.cy = s.cy or ncy " .. 40 | "end " .. -- CY is not affected otherwise for whatever reason 41 | "s.A = flaghandle(s, s.A)", -- clean up remaining flags 42 | 43 | -- Increment/decrement (all forms). These don't do anything with carry, but they DO with Aux. Carry. 44 | -- TODO: Implement aux.carry for these. 45 | ["INR R"] = "s.R = flaghandle(s, s.R + 1)", 46 | ["INR M"] = "local loc = RP s:setb(loc, flaghandle(s, s:getb(loc) + 1))", 47 | ["INX R"] = "local t = a8(s.P + 1) if t == 0 then s.R = a8(s.R + 1) end s.P = t", 48 | 49 | ["DCR R"] = "s.R = flaghandle(s, s.R - 1)", 50 | ["DCR M"] = "local loc = RP s:setb(loc, flaghandle(s, s:getb(loc) - 1))", 51 | ["DCX R"] = "local t = a8(s.P - 1) if t == 0xFF then s.R = a8(s.R - 1) end s.P = t", 52 | 53 | -- Addition and stuff. 54 | ["ADD R"] = "s.A = flaghandle(s, applyb(s, addcdb(s.A, s.R)))", 55 | ["ADD M"] = "s.A = flaghandle(s, applyb(s, addcdb(s.A, s:getb(RP))))", 56 | 57 | ["ADC R"] = "s.A = flaghandle(s, applyb(s, addcdb(s.A, s.R, s.cy)))", 58 | ["ADC M"] = "s.A = flaghandle(s, applyb(s, addcdb(s.A, s:getb(RP), s.cy)))", 59 | 60 | ["ADI B"] = "s.A = flaghandle(s, applyb(s, addcdb(s.A, b)))", 61 | ["ACI B"] = "s.A = flaghandle(s, applyb(s, addcdb(s.A, b, s.cy)))", 62 | 63 | ["DAD R"] = "spair(s, 'H', 'L', pair(s.H, s.L) + RP)", 64 | 65 | -- Subtraction and stuff. 66 | ["SUB R"] = "s.A = flaghandle(s, applyb(s, subcdb(s.A, s.R)))", 67 | ["SBB R"] = "s.A = flaghandle(s, applyb(s, subcdb(s.A, s.R, s.cy)))", 68 | ["SUB M"] = "s.A = flaghandle(s, applyb(s, subcdb(s.A, s:getb(RP))))", 69 | ["SBB M"] = "s.A = flaghandle(s, applyb(s, subcdb(s.A, s:getb(RP), s.cy)))", 70 | 71 | ["SUI B"] = "s.A = flaghandle(s, applyb(s, subcdb(s.A, b)))", 72 | ["SBI B"] = "s.A = flaghandle(s, applyb(s, subcdb(s.A, b, s.cy)))", 73 | 74 | -- Comparisons 75 | ["CPI B"] = "flaghandle(s, applyb(s, subcdb(s.A, b)))", 76 | ["CMP R"] = "flaghandle(s, applyb(s, subcdb(s.A, s.R)))", 77 | ["CMP M"] = "flaghandle(s, applyb(s, subcdb(s.A, s:getb(RP))))", 78 | 79 | -- Bitops 80 | ["ANA R"] = "s.A = flaghandle(s, band(s.A, s.R)) s.cy = false", 81 | ["ANA M"] = "s.A = flaghandle(s, band(s.A, s:getb(RP))) s.cy = false", 82 | 83 | ["ORA R"] = "s.A = flaghandle(s, bor(s.A, s.R)) s.cy = false", 84 | ["ORA M"] = "s.A = flaghandle(s, bor(s.A, s:getb(RP))) s.cy = false", 85 | 86 | ["XRA R"] = "s.A = flaghandle(s, bxor(s.A, s.R)) s.cy = false", 87 | ["XRA M"] = "s.A = flaghandle(s, bxor(s.A, s:getb(RP))) s.cy = false", 88 | 89 | ["ANI B"] = "s.A = flaghandle(s, band(s.A, b)) s.cy = false", 90 | ["ORI B"] = "s.A = flaghandle(s, bor(s.A, b)) s.cy = false", 91 | ["XRI B"] = "s.A = flaghandle(s, bxor(s.A, b)) s.cy = false", 92 | 93 | -- Rotation bitops 94 | ["RLC"] = "s.A, s.cy = b_lsft(s.A) if s.cy then s.A = bor(s.A, 1) end", 95 | ["RRC"] = "s.A, s.cy = b_rsft(s.A) if s.cy then s.A = bor(s.A, 128) end", 96 | 97 | ["RAL"] = "local na, nc = b_lsft(s.A) if s.cy then s.A = bor(na, 1) else s.A = na end s.cy = nc", 98 | ["RAR"] = "local na, nc = b_rsft(s.A) if s.cy then s.A = bor(na, 128) else s.A = na end s.cy = nc", 99 | 100 | -- Jumps / Calls 101 | ["PCHL"] = "s.PC = pair(s.H, s.L) return true", 102 | ["JMP X"] = "s.PC = addr return true", 103 | ["JMP FX"] = "if F then s.PC = addr return true end", 104 | 105 | ["CALL X"] = "s_call(s, addr) return true", 106 | ["CALL FX"] = "if F then s_call(s, addr) return true end", 107 | 108 | ["RET"] = "s.PC = s_pop16(s) return true", 109 | ["RET F"] = "if F then s.PC = s_pop16(s) return true end", 110 | 111 | -- RSTs 112 | ["RST 0"] = "s_call(s, 0x00) return true", 113 | ["RST 1"] = "s_call(s, 0x08) return true", 114 | ["RST 2"] = "s_call(s, 0x10) return true", 115 | ["RST 3"] = "s_call(s, 0x18) return true", 116 | ["RST 4"] = "s_call(s, 0x20) return true", 117 | ["RST 5"] = "s_call(s, 0x28) return true", 118 | ["RST 6"] = "s_call(s, 0x30) return true", 119 | ["RST 7"] = "s_call(s, 0x38) return true", 120 | 121 | -- PUSH/POP 122 | ["PUSH R"] = "s_push8(s, s.R) s_push8(s, s.P)", 123 | ["POP R"] = "s.P = s_pop8(s) s.R = s_pop8(s)", 124 | 125 | -- See comment on encode_psw for the specific order and reasoning 126 | ["PUSH PSW"] = "s_push8(s, s.A) s_push8(s, encode_psw(s))", 127 | ["POP PSW"] = "decode_psw(s, s_pop8(s)) s.A = s_pop8(s)", 128 | 129 | -- Exchangers 130 | ["XCHG"] = "local oh, ol = s.H, s.L s.H = s.D s.D = oh s.L = s.E s.E = ol", 131 | ["XTHL"] = "local oh, ol, a2 = s.H, s.L, band(s.SP + 1, 0xFFFF) s.L = s:getb(s.SP) s:setb(s.SP, ol) s.H = s:getb(a2) s:setb(a2, oh)", 132 | 133 | -- IO 134 | -- Normally, this would also prepend A to the address, 135 | -- but this is unnecessary here. One has access to A. 136 | ["IN B"] = "s.A = s:iogb(b)", 137 | ["OUT B"] = "s:iosb(b, s.A)", 138 | 139 | -- Interrupts 140 | ["HLT"] = "s.halted = true", 141 | ["EI"] = "s.int_enable = true", 142 | ["DI"] = "s.int_enable = false", 143 | } 144 | -------------------------------------------------------------------------------- /8080/opnames.lua: -------------------------------------------------------------------------------- 1 | -- Autogenerated by doc/8080_names_gen.sh. Don't touch. 2 | return { 3 | [0x00] = "NOP", 4 | [0x01] = "LXI B,D16", 5 | [0x02] = "STAX B", 6 | [0x03] = "INX B", 7 | [0x04] = "INR B", 8 | [0x05] = "DCR B", 9 | [0x06] = "MVI B, D8", 10 | [0x07] = "RLC", 11 | [0x08] = "NOP", 12 | [0x09] = "DAD B", 13 | [0x0a] = "LDAX B", 14 | [0x0b] = "DCX B", 15 | [0x0c] = "INR C", 16 | [0x0d] = "DCR C", 17 | [0x0e] = "MVI C,D8", 18 | [0x0f] = "RRC", 19 | [0x10] = "NOP", 20 | [0x11] = "LXI D,D16", 21 | [0x12] = "STAX D", 22 | [0x13] = "INX D", 23 | [0x14] = "INR D", 24 | [0x15] = "DCR D", 25 | [0x16] = "MVI D, D8", 26 | [0x17] = "RAL", 27 | [0x18] = "NOP", 28 | [0x19] = "DAD D", 29 | [0x1a] = "LDAX D", 30 | [0x1b] = "DCX D", 31 | [0x1c] = "INR E", 32 | [0x1d] = "DCR E", 33 | [0x1e] = "MVI E,D8", 34 | [0x1f] = "RAR", 35 | [0x20] = "NOP", 36 | [0x21] = "LXI H,D16", 37 | [0x22] = "SHLD adr", 38 | [0x23] = "INX H", 39 | [0x24] = "INR H", 40 | [0x25] = "DCR H", 41 | [0x26] = "MVI H,D8", 42 | [0x27] = "DAA", 43 | [0x28] = "NOP", 44 | [0x29] = "DAD H", 45 | [0x2a] = "LHLD adr", 46 | [0x2b] = "DCX H", 47 | [0x2c] = "INR L", 48 | [0x2d] = "DCR L", 49 | [0x2e] = "MVI L, D8", 50 | [0x2f] = "CMA", 51 | [0x30] = "NOP", 52 | [0x31] = "LXI SP, D16", 53 | [0x32] = "STA adr", 54 | [0x33] = "INX SP", 55 | [0x34] = "INR M", 56 | [0x35] = "DCR M", 57 | [0x36] = "MVI M,D8", 58 | [0x37] = "STC", 59 | [0x38] = "NOP", 60 | [0x39] = "DAD SP", 61 | [0x3a] = "LDA adr", 62 | [0x3b] = "DCX SP", 63 | [0x3c] = "INR A", 64 | [0x3d] = "DCR A", 65 | [0x3e] = "MVI A,D8", 66 | [0x3f] = "CMC", 67 | [0x40] = "MOV B,B", 68 | [0x41] = "MOV B,C", 69 | [0x42] = "MOV B,D", 70 | [0x43] = "MOV B,E", 71 | [0x44] = "MOV B,H", 72 | [0x45] = "MOV B,L", 73 | [0x46] = "MOV B,M", 74 | [0x47] = "MOV B,A", 75 | [0x48] = "MOV C,B", 76 | [0x49] = "MOV C,C", 77 | [0x4a] = "MOV C,D", 78 | [0x4b] = "MOV C,E", 79 | [0x4c] = "MOV C,H", 80 | [0x4d] = "MOV C,L", 81 | [0x4e] = "MOV C,M", 82 | [0x4f] = "MOV C,A", 83 | [0x50] = "MOV D,B", 84 | [0x51] = "MOV D,C", 85 | [0x52] = "MOV D,D", 86 | [0x53] = "MOV D,E", 87 | [0x54] = "MOV D,H", 88 | [0x55] = "MOV D,L", 89 | [0x56] = "MOV D,M", 90 | [0x57] = "MOV D,A", 91 | [0x58] = "MOV E,B", 92 | [0x59] = "MOV E,C", 93 | [0x5a] = "MOV E,D", 94 | [0x5b] = "MOV E,E", 95 | [0x5c] = "MOV E,H", 96 | [0x5d] = "MOV E,L", 97 | [0x5e] = "MOV E,M", 98 | [0x5f] = "MOV E,A", 99 | [0x60] = "MOV H,B", 100 | [0x61] = "MOV H,C", 101 | [0x62] = "MOV H,D", 102 | [0x63] = "MOV H,E", 103 | [0x64] = "MOV H,H", 104 | [0x65] = "MOV H,L", 105 | [0x66] = "MOV H,M", 106 | [0x67] = "MOV H,A", 107 | [0x68] = "MOV L,B", 108 | [0x69] = "MOV L,C", 109 | [0x6a] = "MOV L,D", 110 | [0x6b] = "MOV L,E", 111 | [0x6c] = "MOV L,H", 112 | [0x6d] = "MOV L,L", 113 | [0x6e] = "MOV L,M", 114 | [0x6f] = "MOV L,A", 115 | [0x70] = "MOV M,B", 116 | [0x71] = "MOV M,C", 117 | [0x72] = "MOV M,D", 118 | [0x73] = "MOV M,E", 119 | [0x74] = "MOV M,H", 120 | [0x75] = "MOV M,L", 121 | [0x76] = "HLT", 122 | [0x77] = "MOV M,A", 123 | [0x78] = "MOV A,B", 124 | [0x79] = "MOV A,C", 125 | [0x7a] = "MOV A,D", 126 | [0x7b] = "MOV A,E", 127 | [0x7c] = "MOV A,H", 128 | [0x7d] = "MOV A,L", 129 | [0x7e] = "MOV A,M", 130 | [0x7f] = "MOV A,A", 131 | [0x80] = "ADD B", 132 | [0x81] = "ADD C", 133 | [0x82] = "ADD D", 134 | [0x83] = "ADD E", 135 | [0x84] = "ADD H", 136 | [0x85] = "ADD L", 137 | [0x86] = "ADD M", 138 | [0x87] = "ADD A", 139 | [0x88] = "ADC B", 140 | [0x89] = "ADC C", 141 | [0x8a] = "ADC D", 142 | [0x8b] = "ADC E", 143 | [0x8c] = "ADC H", 144 | [0x8d] = "ADC L", 145 | [0x8e] = "ADC M", 146 | [0x8f] = "ADC A", 147 | [0x90] = "SUB B", 148 | [0x91] = "SUB C", 149 | [0x92] = "SUB D", 150 | [0x93] = "SUB E", 151 | [0x94] = "SUB H", 152 | [0x95] = "SUB L", 153 | [0x96] = "SUB M", 154 | [0x97] = "SUB A", 155 | [0x98] = "SBB B", 156 | [0x99] = "SBB C", 157 | [0x9a] = "SBB D", 158 | [0x9b] = "SBB E", 159 | [0x9c] = "SBB H", 160 | [0x9d] = "SBB L", 161 | [0x9e] = "SBB M", 162 | [0x9f] = "SBB A", 163 | [0xa0] = "ANA B", 164 | [0xa1] = "ANA C", 165 | [0xa2] = "ANA D", 166 | [0xa3] = "ANA E", 167 | [0xa4] = "ANA H", 168 | [0xa5] = "ANA L", 169 | [0xa6] = "ANA M", 170 | [0xa7] = "ANA A", 171 | [0xa8] = "XRA B", 172 | [0xa9] = "XRA C", 173 | [0xaa] = "XRA D", 174 | [0xab] = "XRA E", 175 | [0xac] = "XRA H", 176 | [0xad] = "XRA L", 177 | [0xae] = "XRA M", 178 | [0xaf] = "XRA A", 179 | [0xb0] = "ORA B", 180 | [0xb1] = "ORA C", 181 | [0xb2] = "ORA D", 182 | [0xb3] = "ORA E", 183 | [0xb4] = "ORA H", 184 | [0xb5] = "ORA L", 185 | [0xb6] = "ORA M", 186 | [0xb7] = "ORA A", 187 | [0xb8] = "CMP B", 188 | [0xb9] = "CMP C", 189 | [0xba] = "CMP D", 190 | [0xbb] = "CMP E", 191 | [0xbc] = "CMP H", 192 | [0xbd] = "CMP L", 193 | [0xbe] = "CMP M", 194 | [0xbf] = "CMP A", 195 | [0xc0] = "RET !FZ", 196 | [0xc1] = "POP B", 197 | [0xc2] = "JMP !FZ adr", 198 | [0xc3] = "JMP adr", 199 | [0xc4] = "CALL !FZ adr", 200 | [0xc5] = "PUSH B", 201 | [0xc6] = "ADI D8", 202 | [0xc7] = "RST 0", 203 | [0xc8] = "RET FZ", 204 | [0xc9] = "RET", 205 | [0xca] = "JMP FZ adr", 206 | [0xcb] = "JMP adr", 207 | [0xcc] = "CALL FZ adr", 208 | [0xcd] = "CALL adr", 209 | [0xce] = "ACI D8", 210 | [0xcf] = "RST 1", 211 | [0xd0] = "RET !FC", 212 | [0xd1] = "POP D", 213 | [0xd2] = "JMP !FC adr", 214 | [0xd3] = "OUT D8", 215 | [0xd4] = "CALL !FC adr", 216 | [0xd5] = "PUSH D", 217 | [0xd6] = "SUI D8", 218 | [0xd7] = "RST 2", 219 | [0xd8] = "RET FC", 220 | [0xd9] = "RET", 221 | [0xda] = "JMP FC adr", 222 | [0xdb] = "IN D8", 223 | [0xdc] = "CALL FC adr", 224 | [0xdd] = "CALL adr", 225 | [0xde] = "SBI D8", 226 | [0xdf] = "RST 3", 227 | [0xe0] = "RET !FPE", 228 | [0xe1] = "POP H", 229 | [0xe2] = "JMP !FPE adr", 230 | [0xe3] = "XTHL", 231 | [0xe4] = "CALL !FPE adr", 232 | [0xe5] = "PUSH H", 233 | [0xe6] = "ANI D8", 234 | [0xe7] = "RST 4", 235 | [0xe8] = "RET FPE", 236 | [0xe9] = "PCHL", 237 | [0xea] = "JMP FPE adr", 238 | [0xeb] = "XCHG", 239 | [0xec] = "CALL FPE adr", 240 | [0xed] = "CALL adr", 241 | [0xee] = "XRI D8", 242 | [0xef] = "RST 5", 243 | [0xf0] = "RET !FS", 244 | [0xf1] = "POP PSW", 245 | [0xf2] = "JMP !FS adr", 246 | [0xf3] = "DI", 247 | [0xf4] = "CALL !FS adr", 248 | [0xf5] = "PUSH PSW", 249 | [0xf6] = "ORI D8", 250 | [0xf7] = "RST 6", 251 | [0xf8] = "RET FS", 252 | [0xf9] = "SPHL", 253 | [0xfa] = "JMP FS adr", 254 | [0xfb] = "EI", 255 | [0xfc] = "CALL FS adr", 256 | [0xfd] = "CALL adr", 257 | [0xfe] = "CPI D8", 258 | [0xff] = "RST 7", 259 | } 260 | -------------------------------------------------------------------------------- /8080/opbs.lua: -------------------------------------------------------------------------------- 1 | -- Autogenerated by doc/8080_ops_gen.sh. Don't touch. 2 | return { 3 | [0x00] = {1, 4, 4}, 4 | [0x01] = {3, 10, 10}, 5 | [0x02] = {1, 7, 7}, 6 | [0x03] = {1, 5, 5}, 7 | [0x04] = {1, 5, 5}, 8 | [0x05] = {1, 5, 5}, 9 | [0x06] = {2, 7, 7}, 10 | [0x07] = {1, 4, 4}, 11 | [0x08] = {1, 4, 4}, 12 | [0x09] = {1, 10, 10}, 13 | [0x0a] = {1, 7, 7}, 14 | [0x0b] = {1, 5, 5}, 15 | [0x0c] = {1, 5, 5}, 16 | [0x0d] = {1, 5, 5}, 17 | [0x0e] = {2, 7, 7}, 18 | [0x0f] = {1, 4, 4}, 19 | [0x10] = {1, 4, 4}, 20 | [0x11] = {3, 10, 10}, 21 | [0x12] = {1, 7, 7}, 22 | [0x13] = {1, 5, 5}, 23 | [0x14] = {1, 5, 5}, 24 | [0x15] = {1, 5, 5}, 25 | [0x16] = {2, 7, 7}, 26 | [0x17] = {1, 4, 4}, 27 | [0x18] = {1, 4, 4}, 28 | [0x19] = {1, 10, 10}, 29 | [0x1a] = {1, 7, 7}, 30 | [0x1b] = {1, 5, 5}, 31 | [0x1c] = {1, 5, 5}, 32 | [0x1d] = {1, 5, 5}, 33 | [0x1e] = {2, 7, 7}, 34 | [0x1f] = {1, 4, 4}, 35 | [0x20] = {1, 4, 4}, 36 | [0x21] = {3, 10, 10}, 37 | [0x22] = {3, 16, 16}, 38 | [0x23] = {1, 5, 5}, 39 | [0x24] = {1, 5, 5}, 40 | [0x25] = {1, 5, 5}, 41 | [0x26] = {2, 7, 7}, 42 | [0x27] = {1, 4, 4}, 43 | [0x28] = {1, 4, 4}, 44 | [0x29] = {1, 10, 10}, 45 | [0x2a] = {3, 16, 16}, 46 | [0x2b] = {1, 5, 5}, 47 | [0x2c] = {1, 5, 5}, 48 | [0x2d] = {1, 5, 5}, 49 | [0x2e] = {2, 7, 7}, 50 | [0x2f] = {1, 4, 4}, 51 | [0x30] = {1, 4, 4}, 52 | [0x31] = {3, 10, 10}, 53 | [0x32] = {3, 13, 13}, 54 | [0x33] = {1, 5, 5}, 55 | [0x34] = {1, 10, 10}, 56 | [0x35] = {1, 10, 10}, 57 | [0x36] = {2, 10, 10}, 58 | [0x37] = {1, 4, 4}, 59 | [0x38] = {1, 4, 4}, 60 | [0x39] = {1, 10, 10}, 61 | [0x3a] = {3, 13, 13}, 62 | [0x3b] = {1, 5, 5}, 63 | [0x3c] = {1, 5, 5}, 64 | [0x3d] = {1, 5, 5}, 65 | [0x3e] = {2, 7, 7}, 66 | [0x3f] = {1, 4, 4}, 67 | [0x40] = {1, 5, 5}, 68 | [0x41] = {1, 5, 5}, 69 | [0x42] = {1, 5, 5}, 70 | [0x43] = {1, 5, 5}, 71 | [0x44] = {1, 5, 5}, 72 | [0x45] = {1, 5, 5}, 73 | [0x46] = {1, 7, 7}, 74 | [0x47] = {1, 5, 5}, 75 | [0x48] = {1, 5, 5}, 76 | [0x49] = {1, 5, 5}, 77 | [0x4a] = {1, 5, 5}, 78 | [0x4b] = {1, 5, 5}, 79 | [0x4c] = {1, 5, 5}, 80 | [0x4d] = {1, 5, 5}, 81 | [0x4e] = {1, 7, 7}, 82 | [0x4f] = {1, 5, 5}, 83 | [0x50] = {1, 5, 5}, 84 | [0x51] = {1, 5, 5}, 85 | [0x52] = {1, 5, 5}, 86 | [0x53] = {1, 5, 5}, 87 | [0x54] = {1, 5, 5}, 88 | [0x55] = {1, 5, 5}, 89 | [0x56] = {1, 7, 7}, 90 | [0x57] = {1, 5, 5}, 91 | [0x58] = {1, 5, 5}, 92 | [0x59] = {1, 5, 5}, 93 | [0x5a] = {1, 5, 5}, 94 | [0x5b] = {1, 5, 5}, 95 | [0x5c] = {1, 5, 5}, 96 | [0x5d] = {1, 5, 5}, 97 | [0x5e] = {1, 7, 7}, 98 | [0x5f] = {1, 5, 5}, 99 | [0x60] = {1, 5, 5}, 100 | [0x61] = {1, 5, 5}, 101 | [0x62] = {1, 5, 5}, 102 | [0x63] = {1, 5, 5}, 103 | [0x64] = {1, 5, 5}, 104 | [0x65] = {1, 5, 5}, 105 | [0x66] = {1, 7, 7}, 106 | [0x67] = {1, 5, 5}, 107 | [0x68] = {1, 5, 5}, 108 | [0x69] = {1, 5, 5}, 109 | [0x6a] = {1, 5, 5}, 110 | [0x6b] = {1, 5, 5}, 111 | [0x6c] = {1, 5, 5}, 112 | [0x6d] = {1, 5, 5}, 113 | [0x6e] = {1, 7, 7}, 114 | [0x6f] = {1, 5, 5}, 115 | [0x70] = {1, 7, 7}, 116 | [0x71] = {1, 7, 7}, 117 | [0x72] = {1, 7, 7}, 118 | [0x73] = {1, 7, 7}, 119 | [0x74] = {1, 7, 7}, 120 | [0x75] = {1, 7, 7}, 121 | [0x76] = {1, 7, 7}, 122 | [0x77] = {1, 7, 7}, 123 | [0x78] = {1, 5, 5}, 124 | [0x79] = {1, 5, 5}, 125 | [0x7a] = {1, 5, 5}, 126 | [0x7b] = {1, 5, 5}, 127 | [0x7c] = {1, 5, 5}, 128 | [0x7d] = {1, 5, 5}, 129 | [0x7e] = {1, 7, 7}, 130 | [0x7f] = {1, 5, 5}, 131 | [0x80] = {1, 4, 4}, 132 | [0x81] = {1, 4, 4}, 133 | [0x82] = {1, 4, 4}, 134 | [0x83] = {1, 4, 4}, 135 | [0x84] = {1, 4, 4}, 136 | [0x85] = {1, 4, 4}, 137 | [0x86] = {1, 7, 7}, 138 | [0x87] = {1, 4, 4}, 139 | [0x88] = {1, 4, 4}, 140 | [0x89] = {1, 4, 4}, 141 | [0x8a] = {1, 4, 4}, 142 | [0x8b] = {1, 4, 4}, 143 | [0x8c] = {1, 4, 4}, 144 | [0x8d] = {1, 4, 4}, 145 | [0x8e] = {1, 7, 7}, 146 | [0x8f] = {1, 4, 4}, 147 | [0x90] = {1, 4, 4}, 148 | [0x91] = {1, 4, 4}, 149 | [0x92] = {1, 4, 4}, 150 | [0x93] = {1, 4, 4}, 151 | [0x94] = {1, 4, 4}, 152 | [0x95] = {1, 4, 4}, 153 | [0x96] = {1, 7, 7}, 154 | [0x97] = {1, 4, 4}, 155 | [0x98] = {1, 4, 4}, 156 | [0x99] = {1, 4, 4}, 157 | [0x9a] = {1, 4, 4}, 158 | [0x9b] = {1, 4, 4}, 159 | [0x9c] = {1, 4, 4}, 160 | [0x9d] = {1, 4, 4}, 161 | [0x9e] = {1, 7, 7}, 162 | [0x9f] = {1, 4, 4}, 163 | [0xa0] = {1, 4, 4}, 164 | [0xa1] = {1, 4, 4}, 165 | [0xa2] = {1, 4, 4}, 166 | [0xa3] = {1, 4, 4}, 167 | [0xa4] = {1, 4, 4}, 168 | [0xa5] = {1, 4, 4}, 169 | [0xa6] = {1, 7, 7}, 170 | [0xa7] = {1, 4, 4}, 171 | [0xa8] = {1, 4, 4}, 172 | [0xa9] = {1, 4, 4}, 173 | [0xaa] = {1, 4, 4}, 174 | [0xab] = {1, 4, 4}, 175 | [0xac] = {1, 4, 4}, 176 | [0xad] = {1, 4, 4}, 177 | [0xae] = {1, 7, 7}, 178 | [0xaf] = {1, 4, 4}, 179 | [0xb0] = {1, 4, 4}, 180 | [0xb1] = {1, 4, 4}, 181 | [0xb2] = {1, 4, 4}, 182 | [0xb3] = {1, 4, 4}, 183 | [0xb4] = {1, 4, 4}, 184 | [0xb5] = {1, 4, 4}, 185 | [0xb6] = {1, 7, 7}, 186 | [0xb7] = {1, 4, 4}, 187 | [0xb8] = {1, 4, 4}, 188 | [0xb9] = {1, 4, 4}, 189 | [0xba] = {1, 4, 4}, 190 | [0xbb] = {1, 4, 4}, 191 | [0xbc] = {1, 4, 4}, 192 | [0xbd] = {1, 4, 4}, 193 | [0xbe] = {1, 7, 7}, 194 | [0xbf] = {1, 4, 4}, 195 | [0xc0] = {1, 5, 11}, 196 | [0xc1] = {1, 10, 10}, 197 | [0xc2] = {3, 10, 10}, 198 | [0xc3] = {3, 10, 10}, 199 | [0xc4] = {3, 11, 17}, 200 | [0xc5] = {1, 11, 11}, 201 | [0xc6] = {2, 7, 7}, 202 | [0xc7] = {1, 11, 11}, 203 | [0xc8] = {1, 5, 11}, 204 | [0xc9] = {1, 10, 10}, 205 | [0xca] = {3, 3, 3}, 206 | [0xcb] = {3, 3, 3}, 207 | [0xcc] = {3, 11, 17}, 208 | [0xcd] = {3, 17, 17}, 209 | [0xce] = {2, 7, 7}, 210 | [0xcf] = {1, 11, 11}, 211 | [0xd0] = {1, 5, 11}, 212 | [0xd1] = {1, 10, 10}, 213 | [0xd2] = {3, 10, 10}, 214 | [0xd3] = {2, 10, 10}, 215 | [0xd4] = {3, 11, 17}, 216 | [0xd5] = {1, 11, 11}, 217 | [0xd6] = {2, 7, 7}, 218 | [0xd7] = {1, 11, 11}, 219 | [0xd8] = {1, 5, 11}, 220 | [0xd9] = {1, 10, 10}, 221 | [0xda] = {3, 10, 10}, 222 | [0xdb] = {2, 10, 10}, 223 | [0xdc] = {3, 11, 17}, 224 | [0xdd] = {3, 17, 17}, 225 | [0xde] = {2, 7, 7}, 226 | [0xdf] = {1, 11, 11}, 227 | [0xe0] = {1, 5, 11}, 228 | [0xe1] = {1, 10, 10}, 229 | [0xe2] = {3, 10, 10}, 230 | [0xe3] = {1, 18, 18}, 231 | [0xe4] = {3, 11, 17}, 232 | [0xe5] = {1, 11, 11}, 233 | [0xe6] = {2, 7, 7}, 234 | [0xe7] = {1, 11, 11}, 235 | [0xe8] = {1, 5, 11}, 236 | [0xe9] = {1, 5, 5}, 237 | [0xea] = {3, 10, 10}, 238 | [0xeb] = {1, 5, 5}, 239 | [0xec] = {3, 11, 17}, 240 | [0xed] = {3, 17, 17}, 241 | [0xee] = {2, 7, 7}, 242 | [0xef] = {1, 11, 11}, 243 | [0xf0] = {1, 5, 11}, 244 | [0xf1] = {1, 10, 10}, 245 | [0xf2] = {3, 10, 10}, 246 | [0xf3] = {1, 4, 4}, 247 | [0xf4] = {3, 11, 17}, 248 | [0xf5] = {1, 11, 11}, 249 | [0xf6] = {2, 7, 7}, 250 | [0xf7] = {1, 11, 11}, 251 | [0xf8] = {1, 5, 11}, 252 | [0xf9] = {1, 5, 5}, 253 | [0xfa] = {3, 10, 10}, 254 | [0xfb] = {1, 4, 4}, 255 | [0xfc] = {3, 11, 17}, 256 | [0xfd] = {3, 17, 17}, 257 | [0xfe] = {2, 7, 7}, 258 | [0xff] = {1, 11, 11}, 259 | } 260 | -------------------------------------------------------------------------------- /emu_zpu_sinmmu.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- ZPU Emulator, providing the zpu-sinkrn environment 3 | 4 | local arg = arg or {...} 5 | 6 | local fname = arg[1] 7 | if not fname then 8 | error("Need filename") 9 | end 10 | local f, err = io.open(fname, "rb") 11 | if err then error(err) end 12 | 13 | local memsz = 0x80000 14 | 15 | -- Load bitops 16 | local bitops = require("bitops") 17 | -- Load ZPU 18 | local zpu = require("zpu") 19 | -- Install bitops 20 | zpu.set_bit32(bitops) 21 | -- Load ZPU emulates and apply them 22 | local zpu_emulates = require("zpu_emus") 23 | zpu:apply(zpu_emulates) 24 | 25 | local memlib = require("memlib") 26 | 27 | -- Memory: ROM, RAM and peripherals. 28 | local t = f:read(memsz) 29 | local rom = memlib.new("rostring", t) 30 | f:close() 31 | 32 | local mem = memlib.new("rwoverlay32", rom, memsz) 33 | 34 | -- Address handlers/Peripherals 35 | local addr_handlers = {} 36 | addr_handlers[0x80000024] = function(comp, method, i, v) 37 | -- UART(O) 38 | if method == "get32be" then return 0x100 end 39 | if method == "set32be" then 40 | io.write(string.char(bitops.band(v, 0xFF))) 41 | io.flush() 42 | return 43 | end 44 | end 45 | 46 | addr_handlers[0x80000028] = function(comp, method, i, v) 47 | -- UART(I) 48 | if method == "get32be" then 49 | local inp = io.read(1) 50 | local ret = (inp and string.byte(inp)) or 0 51 | return bitops.bor(ret, 0x100) 52 | end 53 | end 54 | 55 | local comp = memlib.compose(mem, addr_handlers) 56 | 57 | -- MMU Emulation goes here. 58 | 59 | local mmu_debug = false 60 | 61 | -- This is nil in kernelmode, otherwise it is the amount of instructions to go. 62 | -- <= 0 means "quit ASAP". 63 | local mmu_usermode = nil 64 | local mmu_usermode_quitmode = -1 65 | -- Flag to enter usermode at EOI 66 | local mmu_entering_usermode = false 67 | 68 | local mmu_km_bst = 0 69 | local mmu_km_bip = 0 70 | local mmu_km_bsp = 0 71 | 72 | -- "Next" p1 through p4 registers to be used next time usermode is enabled. 73 | local mmu_km_np1 = 0 74 | local mmu_km_np2 = 0 75 | local mmu_km_np3 = 0 76 | local mmu_km_np4 = 0 77 | 78 | local mmu_st = 0 79 | local mmu_p1 = 0 80 | local mmu_p2 = 0 81 | local mmu_p3 = 0 82 | local mmu_p4 = 0 83 | 84 | -- real start addr, virtual start addr, size in words 85 | local mmu_segs = {{0, 0, 0}, {0, 0, 0}} 86 | 87 | local function mmu_map(i) 88 | for _, v in ipairs(mmu_segs) do 89 | if i >= v[2] then 90 | if i < (v[2] + (v[3] * 4)) then 91 | return v[1] + (i - v[2]) 92 | end 93 | end 94 | end 95 | end 96 | 97 | local function mmu_err() 98 | -- M.A.E. (memory access error, unrelated to any other MAE) 99 | mmu_usermode = -1 100 | mmu_usermode_quitmode = 0xFFFFFFFD 101 | end 102 | 103 | local function get32_um(k) 104 | local map = mmu_map(k) 105 | if not map then mmu_err() return 0 end 106 | return comp:get32be(map) 107 | end 108 | local function set32_um(k, v) 109 | local map = mmu_map(k) 110 | if not map then mmu_err() return end 111 | comp:set32be(map, v) 112 | end 113 | 114 | local function mmu_cmd(c) 115 | -- This is never correct 116 | if c >= 0x80000000 then return end 117 | if mmu_usermode then 118 | mmu_usermode = -1 119 | mmu_usermode_quitmode = c 120 | else 121 | -- Kernel commands 122 | if c == 0 then 123 | mmu_entering_usermode = true 124 | end 125 | if c == 1 then 126 | if mmu_debug then print("S1 ", mmu_p1, mmu_p2, mmu_p3) end 127 | mmu_segs[1] = {mmu_p1, mmu_p2, mmu_p3} 128 | end 129 | if c == 2 then 130 | if mmu_debug then print("S2 ", mmu_p1, mmu_p2, mmu_p3) end 131 | mmu_segs[2] = {mmu_p1, mmu_p2, mmu_p3} 132 | end 133 | if c == 3 then 134 | mmu_km_np1 = mmu_p1 135 | mmu_km_np2 = mmu_p2 136 | mmu_km_np3 = mmu_p3 137 | mmu_km_np4 = mmu_p4 138 | end 139 | end 140 | end 141 | 142 | local function mmu_getreg(k) 143 | if k == 0 then 144 | return mmu_st 145 | end 146 | if k == 1 then 147 | return mmu_p1 148 | end 149 | if k == 2 then 150 | return mmu_p2 151 | end 152 | if k == 3 then 153 | return mmu_p3 154 | end 155 | if k == 4 then 156 | return mmu_p4 157 | end 158 | if not mmu_usermode then 159 | if k == 5 then 160 | return mmu_km_bst 161 | end 162 | if k == 6 then 163 | return mmu_km_bip 164 | end 165 | if k == 7 then 166 | return mmu_km_bsp 167 | end 168 | end 169 | return 0 170 | end 171 | local function mmu_setreg(k, v) 172 | if k == 0 then 173 | mmu_cmd(v) 174 | end 175 | if k == 1 then 176 | mmu_p1 = v 177 | end 178 | if k == 2 then 179 | mmu_p2 = v 180 | end 181 | if k == 3 then 182 | mmu_p3 = v 183 | end 184 | if k == 4 then 185 | mmu_p4 = v 186 | end 187 | end 188 | 189 | local function get32(zpu_inst, i) 190 | if i == 0x80000000 then 191 | return mmu_getreg(0) 192 | end 193 | if i == 0x80000004 then 194 | return mmu_getreg(1) 195 | end 196 | if i == 0x80000008 then 197 | return mmu_getreg(2) 198 | end 199 | if i == 0x8000000C then 200 | return mmu_getreg(3) 201 | end 202 | if i == 0x80000010 then 203 | return mmu_getreg(4) 204 | end 205 | if i == 0x80000014 then 206 | return mmu_getreg(5) 207 | end 208 | if i == 0x80000018 then 209 | return mmu_getreg(6) 210 | end 211 | if i == 0x8000001C then 212 | return mmu_getreg(7) 213 | end 214 | if mmu_usermode then 215 | return get32_um(i) 216 | end 217 | return comp:get32be(i) 218 | end 219 | local function set32(zpu_inst, i, v) 220 | if i == 0x80000000 then 221 | mmu_setreg(0, v) 222 | return 223 | end 224 | if i == 0x80000004 then 225 | mmu_setreg(1, v) 226 | return 227 | end 228 | if i == 0x80000008 then 229 | mmu_setreg(2, v) 230 | return 231 | end 232 | if i == 0x8000000C then 233 | mmu_setreg(3, v) 234 | return 235 | end 236 | if i == 0x80000010 then 237 | mmu_setreg(4, v) 238 | return 239 | end 240 | if mmu_usermode then 241 | set32_um(i, v) 242 | return 243 | end 244 | comp:set32be(i, v) 245 | end 246 | 247 | -- Get ZPU instance and set up. 248 | local zpu_inst = zpu.new(get32, set32) 249 | zpu_inst.rSP = memsz 250 | 251 | while true do 252 | if not zpu_inst:run() then 253 | -- Breakpoint. Quit if in kernel mode, else M.A.E 254 | if mmu_usermode then 255 | mmu_err() 256 | else 257 | error("Kernelmode breakpoint occurred, rIP dec " .. zpu_inst.rIP) 258 | end 259 | end 260 | if mmu_usermode then 261 | if mmu_usermode > 0 then 262 | mmu_usermode = mmu_usermode - 1 263 | end 264 | -- It can't keep executing IMs forever - it WILL run out of code sooner or later. 265 | if (mmu_usermode <= 0) and (not zpu_inst.fLastIM) then 266 | if mmu_debug then print("Leaving usermode...") end 267 | mmu_km_bst = mmu_st 268 | mmu_km_bip = zpu_inst.rIP 269 | mmu_km_bsp = zpu_inst.rSP 270 | zpu_inst.rIP = 0x20 271 | zpu_inst.rSP = memsz 272 | mmu_usermode = nil 273 | mmu_st = mmu_usermode_quitmode 274 | end 275 | else 276 | if mmu_entering_usermode then 277 | mmu_entering_usermode = false 278 | mmu_usermode = 1024 279 | mmu_usermode_quitmode = 0xFFFFFFFF 280 | zpu_inst.rIP = mmu_p2 281 | zpu_inst.rSP = mmu_p3 282 | mmu_st = mmu_p1 283 | mmu_p1 = mmu_km_np1 284 | mmu_p2 = mmu_km_np2 285 | mmu_p3 = mmu_km_np3 286 | mmu_p4 = mmu_km_np4 287 | if mmu_debug then print("entering usermode @ IP" .. zpu_inst.rIP .. "SP" .. zpu_inst.rSP) end 288 | end 289 | end 290 | end 291 | -------------------------------------------------------------------------------- /emu_8080_cpm.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- 8080 Emulator: CP/M specific example. 3 | 4 | -- Optional debug stuff. 5 | local debugf 6 | -- debugf = io.open("debug", "w") 7 | local debug = function (s) 8 | if debugf then debugf:write(s .. "\n") end 9 | end 10 | 11 | local arg = arg or {...} 12 | 13 | local fname = arg[1] 14 | if not fname then 15 | error("Need filename") 16 | end 17 | local f, err = io.open(fname, "rb") 18 | if err then error(err) end 19 | 20 | local memsz = 0x10000 21 | 22 | -- Load bitops 23 | local bitops = require("bitops") 24 | -- Load CPU 25 | local l8080 = require("8080") 26 | -- Install bitops 27 | l8080.set_bit32(bitops) 28 | 29 | local memlib = require("memlib") 30 | 31 | -- Memory: ROM, RAM and peripherals. 32 | local rom_data = f:read(128) 33 | f:close() 34 | 35 | local mem = memlib.new("table", memsz) 36 | for i=1, 128 do 37 | mem:set(i-1, rom_data:byte(i)) 38 | end 39 | 40 | local drive_data = {} 41 | drive_data[0] = memlib.new("file", fname) 42 | for i = 1, 3 do 43 | if arg[i + 1] then 44 | drive_data[i] = memlib.new("file", arg[i + 1]) 45 | end 46 | end 47 | 48 | -- Address handlers/Peripherals 49 | local function get(inst, i) 50 | return mem:get(i) 51 | end 52 | local function set(inst, i, v) 53 | return mem:set(i, v) 54 | end 55 | 56 | local fdcDrive, fdcTrack, fdcSector = 0, 0, 1 57 | local fdcStatus = 1 58 | local dmaHigh, dmaLow = 0, 0 59 | 60 | local function get_absolute_sector_address() 61 | --print(fdcTrack, fdcSector) 62 | if fdcSector < 1 then return nil, 3 end 63 | if fdcSector > 26 then return nil, 3 end 64 | local p = (fdcTrack * 26) + (fdcSector - 1) 65 | return p * 128 66 | end 67 | 68 | function membatch_read(mem, addr, n) 69 | local t = {} 70 | addr = addr - 1 71 | for i=1, n do 72 | t[i] = mem:get(addr+i) 73 | end 74 | t.n = n 75 | return t 76 | end 77 | 78 | function membatch_write(mem, addr, data) 79 | addr = addr - 1 80 | for i=1, (data.n or #data) do 81 | mem:set(addr+i, data[i]) 82 | end 83 | return true 84 | end 85 | 86 | local function get_sector() 87 | local dd = drive_data[fdcDrive] 88 | if not dd then return nil, 1 end 89 | local sp, ns2 = get_absolute_sector_address() 90 | if not sp then return nil, ns2 end 91 | return membatch_read(dd, sp, 128), 0 92 | end 93 | local function put_sector(data) 94 | local dd = drive_data[fdcDrive] 95 | if not dd then return 1 end 96 | local sp, ns2 = get_absolute_sector_address() 97 | if not sp then return ns2 end 98 | membatch_write(dd, sp, data) 99 | return 0 100 | end 101 | 102 | local function dma_write(buf) 103 | local target = dmaLow + (dmaHigh * 256) 104 | --print(string.format("DMA: %04x %i: %x %x %x %x", target, #buf, buf[1], buf[2], buf[3], buf[4])) 105 | for i = 1, (buf.n or #buf) do 106 | mem:set(target, buf[i]) 107 | target = target + 1 108 | end 109 | end 110 | 111 | local function dma_read(len) 112 | local target = dmaLow + (dmaHigh * 256) - 1 113 | --print(string.format("DMA: %04x %i: %x %x %x %x", target, #buf, buf[1], buf[2], buf[3], buf[4])) 114 | local tmp = {n = len} 115 | for i = 1, len do 116 | tmp[i] = mem:get(target + i) 117 | end 118 | return tmp 119 | end 120 | 121 | local availfn 122 | if jit then -- LuaJIT alternative check. 123 | -- UNIX-only, I believe. Possibly Linux only. 124 | -- But hey, who cares? 125 | local ffi = require("ffi") 126 | ffi.cdef [[ 127 | struct pollfd { 128 | int fd; 129 | short events; 130 | short revents; 131 | }; 132 | 133 | int poll(struct pollfd *fds, unsigned long int nfds, int timeout); 134 | ]] 135 | local C = ffi.C 136 | local pollfds = ffi.new("struct pollfd[1]") 137 | pollfds[0].fd = 0 138 | pollfds[0].events = 1 139 | availfn = function() 140 | local hasdata = C.poll(pollfds, 1, 1) == 1 141 | if hasdata then return 0xFF else return 0x00 end 142 | end 143 | else 144 | -- Fake available/unavailable counter. Dirty hack, but it works. 145 | -- Because we have no way to check if there are actually bytes available, 146 | -- we set it to only be available once every availevery read checks. 147 | local availevery = 10000 -- fake having a data byte every 10k calls. 148 | local availn = 1 149 | 150 | availfn = function() 151 | if availn == (availevery - 1) then 152 | availn = 1 153 | return 0xFF -- available 154 | end 155 | availn = availn + 1 156 | return 0x00 157 | end 158 | end 159 | 160 | local function iog(inst, i) 161 | if i == 0 then -- Console input status 162 | return availfn() 163 | elseif i == 1 then 164 | -- Console data. 165 | local c = io.read(1) 166 | if c == "\n" then return 13 end -- CP/M and Zork seem to prefer \r to \n, \r\n or \n\r. 167 | return string.byte(c) 168 | elseif i == 10 then return fdcDrive 169 | elseif i == 11 then return fdcTrack 170 | elseif i == 12 then return fdcSector 171 | 172 | elseif i == 13 then return 0xFF -- FDC-CMD-COMPLETE 173 | elseif i == 14 then return fdcStatus -- FDC-STATUS 174 | 175 | elseif i == 15 then return dmaLow 176 | elseif i == 16 then return dmaHigh end 177 | return 0 178 | end 179 | 180 | local function ios(inst, i, v) 181 | if i == 1 then 182 | debug("WR: " .. string.char(v)) 183 | io.write(string.char(v)) 184 | io.flush() 185 | elseif i == 10 then fdcDrive = v 186 | elseif i == 11 then fdcTrack = v 187 | elseif i == 12 then fdcSector = v 188 | elseif i == 13 then 189 | if v == 0 then 190 | local b, ns = get_sector() 191 | if b then 192 | dma_write(b) 193 | --else 194 | --print("Failed Read", fdcDrive, fdcTrack, fdcSector) 195 | end 196 | fdcStatus = ns 197 | elseif v == 1 then 198 | local s, ns = pcall(put_sector, dma_read(128)) 199 | if not s then 200 | --io.stderr:write("ERR: "..tostring(ns).."\n") 201 | fdcStatus = 6 202 | else 203 | fdcStatus = ns 204 | end 205 | else 206 | fdcStatus = 7 207 | end 208 | elseif i == 15 then dmaLow = v 209 | elseif i == 16 then dmaHigh = v end 210 | end 211 | 212 | local inst = l8080.new(get, set, iog, ios) 213 | 214 | -- Sleep 215 | local sleep 216 | if os.sleep then 217 | sleep = os.sleep 218 | elseif jit then 219 | local ffi = require("ffi") 220 | ffi.cdef [[ 221 | void Sleep(int ms); 222 | // int poll(struct pollfd *fds, unsigned long nfds, int timeout); // already defined above. 223 | ]] 224 | local C = ffi.C 225 | if ffi.os == "Windows" then 226 | sleep = function(s) 227 | C.Sleep(s*1000) 228 | end 229 | else 230 | sleep = function(s) 231 | C.poll(nil, 0, s*1000) 232 | end 233 | end 234 | end 235 | 236 | -- Clock speed limiting 237 | local clockspeed = 2 * 1000 * 1000 -- 2MHz 238 | local sleepdur = 0.05 239 | 240 | -- Probably fucked up the math. 241 | local cps = clockspeed/(1/sleepdur) 242 | 243 | local fmt = string.format 244 | 245 | if sleep then -- has a sleep function, which allows us to limit execution speed. 246 | local i=0 247 | while true do 248 | i = i + 1 249 | if i == cps then sleep(sleepdur) i = 0 end 250 | if debugf then 251 | local n2, txt = inst:disasm(inst.PC) 252 | debug(txt) 253 | end 254 | inst:run() 255 | if debugf then 256 | inst:dump(debugf) 257 | end 258 | end 259 | else 260 | while true do -- Fallback. 261 | if debugf then 262 | local n2, txt = inst:disasm(inst.PC) 263 | debug(txt) 264 | end 265 | inst:run() 266 | if debugf then 267 | inst:dump(debugf) 268 | end 269 | end 270 | end 271 | -------------------------------------------------------------------------------- /gen/8080_ops_gen.lua: -------------------------------------------------------------------------------- 1 | -- We have already generated the op inst len and op names. 2 | local opnames = require("8080.opnames") 3 | 4 | local smatch, fmt = string.match, string.format 5 | local tinsert = table.insert 6 | 7 | -- Prelude 8 | io.write([[ 9 | -- Actual implementation of OPs. 10 | -- Good god, this is... huge. 11 | -- Luckily, it is generated. 12 | 13 | local band, bor, bxor, rshift, lshift 14 | 15 | -- Helpers 16 | local function a8(x) 17 | return band(x, 0xFF) 18 | end 19 | 20 | local function pair(X, Y) 21 | return bor(lshift(X, 8), Y) 22 | end 23 | 24 | local function spair(s, Xn, Yn, res) 25 | s[Xn] = rshift(band(res, 0xFF00), 8) 26 | s[Yn] = band(res, 0xFF) 27 | s.cy = (band(res, 0xFFFF0000) > 0) 28 | end 29 | 30 | -- Parity is counting the number of set bits. 31 | -- If the number of set bits is odd, it will return true. 32 | -- If the number bits is even, it will return false. 33 | -- Notably, size should be the amount of bits *minus 1*. So parity(res, 7) for the common 8-bit case. 34 | local function parity_r(x, size) 35 | local p = 0 36 | x = band(x, lshift(1, size) - 1) 37 | for i=0, size do 38 | if band(x, 1) ~= 0 then 39 | p = p + 1 40 | end 41 | x = rshift(x, 1) 42 | end 43 | return band(p, 1) == 0 44 | end 45 | 46 | -- Because bitops are rather slow, 47 | -- caching them could be a good advantage. 48 | local paritycache = {} 49 | local function parity(x) 50 | local cx = paritycache[x] 51 | if cx then 52 | return cx 53 | end 54 | local r = parity_r(x, 7) 55 | paritycache[x] = r 56 | return r 57 | end 58 | 59 | local function flaghandle(inst, res) 60 | res = band(res, 0xFF) 61 | inst.z = (res == 0) -- is zero 62 | inst.s = (band(res, 0x80) ~= 0) -- sign flag, if bit 7 set 63 | inst.p = parity(res) 64 | return res 65 | end 66 | 67 | local function mdc(b, c) 68 | if c then 69 | return b + 1 70 | end 71 | return b 72 | end 73 | 74 | local function addcda(a, b, c) 75 | b = mdc(b, c) 76 | local b1 = (a % 16) + (b % 16) 77 | return band(a + b, 0xFF), b1 > 0x0F 78 | end 79 | local function addcdn(a, b, c) 80 | b = mdc(b, c) 81 | return band(a + b, 0xFF), (a + b) > 0xFF 82 | end 83 | local function addcdb(a, b, c) 84 | b = mdc(b, c) 85 | local b1 = (a % 16) + (b % 16) 86 | return band(a + b, 0xFF), b1 > 0x0F, (a + b) > 0xFF 87 | end 88 | 89 | local function subcda(a, b, c) 90 | b = mdc(b, c) 91 | local b1 = (a % 16) + (b % 16) 92 | return band(a - b, 0xFF), b1 > 0xF 93 | end 94 | local function subcdn(a, b, c) 95 | b = mdc(b, c) 96 | return band(a - b, 0xFF), (a - b) < 0 97 | end 98 | local function subcdb(a, b, c) 99 | b = mdc(b, c) 100 | local b1 = (a % 16) + (b % 16) 101 | return band(a - b, 0xFF), b1 > 0xF, (a - b) < 0 102 | end 103 | local function applyb(s, r, a, c) 104 | s.ac = a 105 | s.cy = c 106 | return r 107 | end 108 | 109 | local function s_push16(s, res) 110 | local high, low = rshift(band(res, 0xFF00), 8), band(res, 0xFF) 111 | s.SP = band(s.SP - 1, 0xFFFF) 112 | s:setb(s.SP, high) 113 | s.SP = band(s.SP - 1, 0xFFFF) 114 | s:setb(s.SP, low) 115 | end 116 | 117 | local function s_pop16(s) 118 | local low = s:getb(s.SP) 119 | s.SP = band(s.SP + 1, 0xFFFF) 120 | local high = s:getb(s.SP) 121 | s.SP = band(s.SP + 1, 0xFFFF) 122 | return pair(high, low) 123 | end 124 | 125 | local function s_push8(s, res) 126 | s.SP = band(s.SP - 1, 0xFFFF) 127 | s:setb(s.SP, res) 128 | end 129 | 130 | local function s_pop8(s) 131 | local res = s:getb(s.SP) 132 | s.SP = band(s.SP + 1, 0xFFFF) 133 | return res 134 | end 135 | 136 | local function s_call(s, t) 137 | s_push16(s, band(s.PC, 0xFFFF)) 138 | s.PC = t 139 | end 140 | 141 | -- PSW Accumulator is in the 'big' byte. 142 | -- (First when pushing, last when popping) 143 | -- 0739: POP BC 144 | -- 073A: MOV C, B 145 | -- 073B: ORA C 146 | 147 | local function encode_psw(s) 148 | -- SZ0A0P1C 149 | local n = 2 150 | if s.cy then n = n + 1 end 151 | if s.p then n = n + 4 end 152 | if s.ac then n = n + 16 end 153 | if s.z then n = n + 64 end 154 | if s.s then n = n + 128 end 155 | return n 156 | end 157 | 158 | local function decode_psw(s, n) 159 | s.cy = band(n, 1) ~= 0 160 | s.p = band(n, 4) ~= 0 161 | s.ac = band(n, 16) ~= 0 162 | s.z = band(n, 64) ~= 0 163 | s.s = band(n, 128) ~= 0 164 | end 165 | 166 | local function b_lsft(a) 167 | local n = band(a * 2, 0xFF) 168 | return n, band(a, 0x80) ~= 0 169 | end 170 | 171 | local function b_rsft(a) 172 | local n = band(math.floor(a / 2), 0x7F) 173 | return n, band(a, 1) ~= 0 174 | end 175 | 176 | -- OPS 177 | local ops = { 178 | ]]) 179 | 180 | -- Helpers 181 | local T_ADDR = 1 182 | local T_BYTE = 2 183 | 184 | -- Used to identify and translate flags 185 | local flags = { 186 | ["!FZ"] = "s.z == false", 187 | ["FZ"] = "s.z == true", 188 | ["!FC"] = "s.cy == false", 189 | ["FC"] = "s.cy == true", 190 | ["!FPE"] = "s.p == false", 191 | ["FPE"] = "s.p == true", 192 | ["!FS"] = "s.s == false", 193 | ["FS"] = "s.s == true" 194 | -- The "aux. carry" flag is unreadable directly. 195 | } 196 | 197 | local function arg_types(argstr) 198 | local tstr, rstr = "", "" 199 | local real = {} 200 | for T in string.gmatch(argstr, '([^, ]+)') do 201 | if T == "adr" then 202 | tstr = tstr .. "X" 203 | rstr = rstr .. "x" 204 | tinsert(real, "X") 205 | elseif T == "D8" then 206 | tstr = tstr .. "B" 207 | rstr = rstr .. "b" 208 | tinsert(real, "B") 209 | elseif T == "D16" then 210 | tstr = tstr .. "BB" 211 | rstr = rstr .. "bb" 212 | tinsert(real, "BB") 213 | elseif T == "M" then 214 | tstr = tstr .. T 215 | rstr = rstr .. T 216 | tinsert(real, T) 217 | elseif flags[T] then 218 | tstr = tstr .. "F" 219 | rstr = rstr .. "f" 220 | tinsert(real, T) 221 | else 222 | tstr = tstr .. "R" 223 | rstr = rstr .. T 224 | tinsert(real, T) 225 | end 226 | end 227 | return tstr, rstr, real 228 | end 229 | 230 | local function splitop(opn) 231 | local n, arg = smatch(opn, "^(.-) (.*)$") 232 | if arg then 233 | return n, arg_types(arg) 234 | else 235 | return opn 236 | end 237 | end 238 | 239 | -- function arg lookup table 240 | local opfnargs = { 241 | R = ")", 242 | RR = ")", 243 | RM = ")", 244 | RB = ", b)", 245 | RBB = ", b2, b3)", 246 | M = ")", 247 | MR = ")", 248 | MB = ", b)", 249 | B = ", b)", 250 | X = ", b2, b3) local addr = pair(b3, b2)", 251 | F = ")", 252 | FX = ", b2, b3) local addr = pair(b3, b2)" 253 | } 254 | 255 | local opfnregs = { 256 | R = 1, 257 | RR = 2, 258 | RM = 2, 259 | RB = 1, 260 | RBB = 1, 261 | M = 1, 262 | MR = 2, 263 | MB = 1, 264 | } 265 | 266 | local regpartner = { 267 | B = "C", 268 | C = "B", 269 | D = "E", 270 | E = "D", 271 | H = "L", 272 | L = "H", 273 | M = "pair(s.H, s.L)", 274 | SP = "SP" 275 | } 276 | local regpair = { 277 | B = "pair(s.B, s.C)", 278 | D = "pair(s.D, s.E)", 279 | H = "pair(s.H, s.L)", 280 | M = "pair(s.H, s.L)", -- hax. 281 | SP = "s.SP", 282 | } 283 | 284 | local function genop(list, op, args, rargs, real) 285 | if not args then 286 | local opf = list[op] 287 | if not opf then return nil end 288 | return "function(s) "..opf.." end" 289 | end 290 | 291 | local opf = list[op .. " " .. rargs] 292 | if opf == false then 293 | return nil 294 | end 295 | if opf == nil then 296 | opf = list[op .. " " .. args] 297 | end 298 | 299 | if not opf then 300 | return nil 301 | end 302 | 303 | local str = "function(s"..opfnargs[args].." " 304 | local noregs = opfnregs[args] 305 | if noregs == 1 then 306 | local R = real[1] 307 | str = str .. opf:gsub("RP", tostring(regpair[R])):gsub("R", R) 308 | --str = str:gsub("PS(", fmt("spair(s, '%s', %s, ", )) 309 | str = str:gsub("([^S])P", "%1" .. tostring(regpartner[R])) 310 | elseif noregs == 2 then 311 | str = str .. opf:gsub("RP1", tostring(regpair[real[1]])):gsub("R1", real[1]) 312 | str = str:gsub("RP2", tostring(regpair[real[2]])):gsub("R2", real[2]) 313 | else 314 | str = str .. opf 315 | end 316 | if real[1] and flags[real[1]] then 317 | str = str:gsub("F", flags[real[1]]) 318 | end 319 | return str .. " end" 320 | end 321 | 322 | -- Load our Op table 323 | local optbl = dofile("gen/8080_ops.lua") -- file location is... questionable at best. 324 | 325 | -- Parsing loop 326 | for opb=0x00, 0xFF do 327 | local opn = opnames[opb] 328 | if opn then 329 | local n, args, rargs, real = splitop(opn) 330 | local gen = genop(optbl, n, args, rargs, real) 331 | if gen then 332 | print(fmt("\t[0x%02x] = %s, -- %s", opb, gen, opn)) 333 | else 334 | print(fmt("\t-- Missing 0x%02x: %s (%s)", opb, opn, args)) 335 | end 336 | else 337 | --print(fmt("\t-- Missing 0x%02x", opb)) 338 | end 339 | end 340 | 341 | -- Post generation 342 | io.write([[ 343 | } 344 | 345 | return { 346 | inst_bitops = function(bit32) 347 | band, bor, bxor = bit32.band, bit32.bor, bit32.bxor 348 | rshift, lshift = bit32.rshift, bit32.lshift 349 | end, 350 | ops = ops 351 | } 352 | ]]) 353 | -------------------------------------------------------------------------------- /zpu_emus.lua: -------------------------------------------------------------------------------- 1 | -- ZPU Emulator: EMULATE implementations. 2 | -- Quite nice speed up. Used to avoid needing crt0.5 and co. 3 | -- Original by gamemanj, heavily tweaked/rewritten by vifino. 4 | -- 5 | -- require this and pass the result in to the ZPU library's apply. 6 | -- Example: 7 | -- zpu:apply(require("zpu_emus")) -- now globally loaded. 8 | 9 | --[[ 10 | The MIT License (MIT) 11 | 12 | Copyright (c) 2016 Adrian Pistol 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy 15 | of this software and associated documentation files (the "Software"), to deal 16 | in the Software without restriction, including without limitation the rights 17 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | copies of the Software, and to permit persons to whom the Software is 19 | furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in all 22 | copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | SOFTWARE. 31 | --]] 32 | 33 | 34 | -- Debug config 35 | -- For figuring out if there's something horribly wrong with the testcase. 36 | local usage_trace = false 37 | 38 | -- Place holders for bit functions 39 | local band, bor, bxor, lshift, rshift 40 | 41 | -- Localized functions 42 | local mceil, mfloor = math.ceil, math.floor 43 | 44 | -- Utils 45 | local function a32(v) 46 | return band(v, 0xFFFFFFFF) 47 | end 48 | local function sflip(v) 49 | v = a32(v) 50 | if band(v, 0x80000000) ~= 0 then 51 | return v - 0x100000000 52 | end 53 | return v 54 | end 55 | local function mkbool(v) 56 | return v and 1 or 0 57 | end 58 | local function advip(zpu_emu) 59 | zpu_emu.rIP = a32(zpu_emu.rIP + 1) 60 | end 61 | 62 | -- getb and setb are the internal implementation of LOADB and STOREB 63 | -- and are thus heavily endianess dependant 64 | local function getb(zpu_emu, a) 65 | local s = (24 - lshift(band(a, 3), 3)) 66 | local av = zpu_emu:get32(band(a, 0xFFFFFFFC)) 67 | return band(rshift(av, s), 0xFF) 68 | end 69 | local function setb(zpu_emu, a, v) 70 | local s = (24 - lshift(band(a, 3), 3)) 71 | local b = bxor(lshift(0xFF, s), 0xFFFFFFFF) 72 | local av = band(zpu_emu:get32(band(a, 0xFFFFFFFC)), b) 73 | zpu_emu:set32(band( a, 0xFFFFFFFC), bor(av, lshift(band(v, 0xFF), s))) 74 | end 75 | 76 | -- geth and seth are the same but for halfwords. 77 | -- This implementation will just mess up if it gets a certain kind of misalignment. 78 | -- (I have no better ideas. There is no reliable way to error-escape.) 79 | 80 | local function geth(zpu_emu, a) 81 | local s = (24 - lshift(band(a, 3), 3)) 82 | local av = zpu_emu:get32(band(a, 0xFFFFFFFC)) 83 | return band(rshift(av, s), 0xFFFF) 84 | end 85 | 86 | local function seth(zpu_emu, a, v) 87 | local s = (24 - lshift(band(a, 3), 3)) 88 | local b = bxor(lshift(0xFFFF, s), 0xFFFFFFFF) 89 | local av = band(zpu_emu:get32(band(a, 0xFFFFFFFC)), b) 90 | zpu_emu:set32(band(a, 0xFFFFFFFC), bor(av, lshift(band(v, 0xFFFF), s))) 91 | end 92 | 93 | local function eqbranch(zpu_emu, bcf) 94 | local br = a32(zpu_emu.rIP + zpu_emu:v_pop()) 95 | if bcf(zpu_emu:v_pop()) then 96 | zpu_emu.rIP = br 97 | else 98 | advip(zpu_emu) 99 | end 100 | end 101 | 102 | -- Generic L/R shifter, logical-only. 103 | local function gpi_shift(v, lShift) 104 | if (lShift >= 32) or (lShift <= -32) then return 0 end 105 | if lShift > 0 then return lshift(v, lShift) end 106 | if lShift < 0 then return rshift(v, -lShift) end 107 | end 108 | -- Generic multifunction shifter. Should handle any case with ease. 109 | local function gp_shift(v, lShift, arithmetic) 110 | arithmetic = arithmetic and band(v, 0x80000000) ~= 0 111 | v = gpi_shift(v, lShift) 112 | if arithmetic and (lShift < 0) then 113 | return bor(v, bxor(gpi_shift(0xFFFFFFFF, lShift), 0xFFFFFFFF)) 114 | end 115 | return v 116 | end 117 | 118 | -- EMULATE building 119 | local emulates = {} 120 | local unused_emulates = 0 121 | 122 | local function make_emu(id, name, code) 123 | local unused = true 124 | if usage_trace then 125 | emulates[id] = {name, function(...) 126 | if unused then 127 | unused = false 128 | io.stderr:write(name .. " used, " .. unused_emulates .. " to go\n") 129 | unused_emulates = unused_emulates - 1 130 | end 131 | return code(...) 132 | end} 133 | else 134 | emulates[id] = {name, code} 135 | end 136 | unused_emulates = unused_emulates + 1 137 | end 138 | local function make_pair(id, name, code) 139 | make_emu(id, name, function(zpu_emu) 140 | local a = zpu_emu:v_pop() 141 | local b = zpu_emu:get32(zpu_emu.rSP) 142 | zpu_emu:set32(zpu_emu.rSP, code(a, b)) 143 | advip(zpu_emu) 144 | end) 145 | end 146 | 147 | -- Actual emulates! 148 | -- Yay! 149 | 150 | make_emu(2, "LOADH", function(zpu_emu) zpu_emu:set32(zpu_emu.rSP, geth(zpu_emu, zpu_emu:get32(zpu_emu.rSP))) advip(zpu_emu) end) 151 | make_emu(3, "STOREH", function(zpu_emu) seth(zpu_emu, zpu_emu:v_pop(), zpu_emu:v_pop()) advip(zpu_emu) end) 152 | 153 | make_pair(4, "LESSTHAN", function(a, b) return mkbool(sflip(a) < sflip(b)) end) 154 | make_pair(5, "LESSTHANEQUAL", function(a, b) return mkbool(sflip(a) <= sflip(b)) end) 155 | make_pair(6, "ULESSTHAN", function(a, b) return mkbool(a < b) end) 156 | make_pair(7, "ULESSTHANEQUAL", function(a, b) return mkbool(a <= b) end) 157 | 158 | make_pair(9, "SLOWMULT", function(a, b) return band(a * b, 0xFFFFFFFF) end) 159 | 160 | make_pair(10, "LSHIFTRIGHT", function(a, b) 161 | return gp_shift(b, -sflip(a), false) 162 | end) 163 | make_pair(11, "ASHIFTLEFT", function(a, b) 164 | return gp_shift(b, sflip(a), true) 165 | end) 166 | make_pair(12, "ASHIFTRIGHT", function(a, b) 167 | return gp_shift(b, -sflip(a), true) 168 | end) 169 | 170 | make_pair(14, "EQ", function(a, b) return mkbool(a == b) end) 171 | make_pair(15, "NEQ", function(a, b) return mkbool(a ~= b) end) 172 | 173 | make_emu(16, "NEQ", function(zpu_emu) 174 | zpu_emu:set32(zpu_emu.rSP, a32(-sflip(zpu_emu:get32(zpu_emu.rSP)))) 175 | advip(zpu_emu) 176 | end) 177 | 178 | make_pair(17, "SUB", function(b, a) return band(a - b, 0xFFFFFFFF) end) 179 | make_pair(18, "XOR", function(b, a) return band(bxor(a, b), 0xFFFFFFFF) end) 180 | 181 | make_emu(19, "LOADB", function(zpu_emu) zpu_emu:set32(zpu_emu.rSP, getb(zpu_emu, zpu_emu:get32(zpu_emu.rSP))) advip(zpu_emu) end) 182 | make_emu(20, "STOREB", function(zpu_emu) 183 | setb(zpu_emu, zpu_emu:v_pop(), zpu_emu:v_pop()) 184 | advip(zpu_emu) 185 | end) 186 | 187 | local function rtz(v) 188 | if v < 0 then return mceil(v) end 189 | return mfloor(v) 190 | end 191 | local function cmod(a, b) 192 | return a - (rtz(a / b) * b) 193 | end 194 | make_pair(21, "DIV", function (a, b) return a32(rtz(sflip(a) / sflip(b))) end) 195 | make_pair(22, "MOD", function (a, b) return a32(cmod(sflip(a), sflip(b))) end) 196 | 197 | make_emu(23, "EQBRANCH", function(zpu_emu) return eqbranch(zpu_emu, function(b) return b == 0 end) end) 198 | make_emu(24, "NEQBRANCH", function(zpu_emu) return eqbranch(zpu_emu, function(b) return b ~= 0 end) end) 199 | 200 | make_emu(25, "POPPCREL", function(zpu_emu) zpu_emu.rIP = band(zpu_emu.rIP + zpu_emu:v_pop(), 0xFFFFFFFF) end) 201 | 202 | make_emu(29, "PUSHSPADD", function(zpu_emu) 203 | zpu_emu:set32(zpu_emu.rSP, band(band(lshift(zpu_emu:get32(zpu_emu.rSP), 2), 0xFFFFFFFF) + zpu_emu.rSP, 0xFFFFFFFC)) 204 | advip(zpu_emu) 205 | end) 206 | 207 | make_emu(31, "CALLPCREL", function(zpu_emu) 208 | local routine = band(zpu_emu.rIP + zpu_emu:get32(zpu_emu.rSP), 0xFFFFFFFF) 209 | zpu_emu:set32(zpu_emu.rSP, band(zpu_emu.rIP + 1, 0xFFFFFFFF)) 210 | zpu_emu.rIP = routine 211 | end) 212 | 213 | -- Installation helper 214 | local function assert_bitfn(bit, name) 215 | assert(bit[name], "zpu_emus: Did not find function "..name.." in bitlib. We need it.") 216 | end 217 | local function install_bit(bl) 218 | assert_bitfn(bl, "band") band = bl.band 219 | assert_bitfn(bl, "bor") bor = bl.bor 220 | assert_bitfn(bl, "bxor") bxor = bl.bxor 221 | assert_bitfn(bl, "lshift") lshift = bl.lshift 222 | assert_bitfn(bl, "rshift") rshift = bl.rshift 223 | end 224 | 225 | return function(zpu) 226 | -- check bitlib, we need it too. 227 | install_bit(zpu.bit32) 228 | local old_emu = zpu.op_emulate 229 | zpu.op_emulate = function(zpu_emu, op) 230 | local emulate = emulates[op] 231 | if emulate then 232 | emulate[2](zpu_emu) 233 | return emulate[1] 234 | end 235 | if usage_trace then io.stderr:write("zpu_emus: usage trace found "..op.." hasn't been written yet.") end 236 | return old_emu(zpu_emu, op) 237 | end 238 | return true 239 | end 240 | 241 | -------------------------------------------------------------------------------- /bitops.lua: -------------------------------------------------------------------------------- 1 | -- bitops selection 2 | -- since the available bitops vary, we need to pick a working one. 3 | 4 | -- This library tries it's hardest to get a proper bit32 compatible library going. 5 | -- It tries the real bit32, wraps luajit's bit to be safe, uses Lua 5.3 operators 6 | -- and if all before fail, it'll use pure lua fallbacks. slow, but working. 7 | 8 | --[[ 9 | The MIT License (MIT) 10 | 11 | Copyright (c) 2016 Adrian Pistol 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in all 21 | copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | SOFTWARE. 30 | --]] 31 | 32 | local bitlib 33 | 34 | -- args and debug 35 | local enable_debug, force_fallback = ... 36 | local debug 37 | if enable_debug then 38 | debug = function(msg) io.stderr:write(msg.."\n") end 39 | else 40 | debug = function() end 41 | end 42 | 43 | local function get_bit32() 44 | if bit32 then 45 | return bit32 46 | else 47 | -- try to require bit32 48 | local success, res = pcall(require, "bit32") 49 | if success then 50 | return res 51 | end 52 | end 53 | return {} 54 | end 55 | 56 | local loadstring = loadstring or load 57 | 58 | local function bit_l53(op, single) 59 | if not single then 60 | return loadstring("return function(a, b) return a "..op.." b end end") 61 | end 62 | return loadstring("return function(x) return "..op.."x end end") 63 | end 64 | 65 | -- pick bit op from a list of candidates 66 | local function bit_pick_fn(name, res, fn_list) 67 | for fni=1, #fn_list do 68 | local fn = fn_list[fni] 69 | if fn then 70 | local success, ret = pcall(fn, 5, 4) 71 | if success then 72 | if ret == res then 73 | return fn 74 | end 75 | debug("bitops: "..name..": Tried candidate no. "..tostring(fni)..", yielded "..tostring(ret).." but wanted "..tostring(res)) 76 | end 77 | end 78 | end 79 | end 80 | 81 | -- generate a bit library of all the available options 82 | local function bit_select(inst_tests) 83 | local lib = {} 84 | for inst, candidates in pairs(inst_tests) do 85 | local name, test, fixup = inst[1], inst[2], inst[3] 86 | local tmp = bit_pick_fn(name, test, candidates) 87 | if not tmp then 88 | error("bitops: No working candidate for "..name) 89 | end 90 | if fixup then 91 | tmp = fixup(tmp) 92 | end 93 | lib[name] = tmp 94 | end 95 | return lib 96 | end 97 | 98 | -- fallback bit library 99 | local fallback = {} 100 | local mfloor = math.floor 101 | 102 | local function checkint32(x) 103 | return mfloor(x) % 0x100000000 104 | end 105 | 106 | local function fallback_bitconv(na, nb, f) 107 | na = checkint32(na) 108 | nb = checkint32(nb) 109 | local r = 0 110 | local p = 1 111 | for i = 1, 32 do 112 | local pa = na * 0.5 113 | local pb = nb * 0.5 114 | na = mfloor(pa) 115 | nb = mfloor(pb) 116 | if f(pa ~= na, pb ~= nb) then 117 | r = r + p 118 | end 119 | p = p * 2 120 | end 121 | return r 122 | end 123 | 124 | -- NOT 125 | function fallback.bnot(x) 126 | return (-1 - x) % 0x100000000 127 | end 128 | 129 | -- AND 130 | local fb_band_r = function(a, b) return a and b end 131 | function fallback.band(a, b) 132 | -- This is often used where the situation warrants a reconvert back to bit32. 133 | -- So, don't assume it will receive sane input. 134 | 135 | -- However, there are a lot of cases where the "convert to bit32" behavior is used. 136 | -- Which are currently rather slow in the "no need to convert" case. 137 | -- These neatly cover a good portion of these "there was no need to do this" cases, 138 | -- speeds things up enormously. 139 | 140 | if (b == 0xFFFFFFFF) and (a >= 0) and (a <= 0xFFFFFFFF) then return mfloor(a) end 141 | if (a == 0xFFFFFFFF) and (b >= 0) and (b <= 0xFFFFFFFF) then return mfloor(b) end 142 | if (b == 0xFFFFFFFF) and (a < 0) then 143 | a = mfloor(0x100000000 + a) 144 | if (a >= 0) and (a <= 0xFFFFFFFF) then return a end 145 | end 146 | if (a == 0xFFFFFFFF) and (b < 0) then 147 | b = mfloor(0x100000000 + b) 148 | if (b >= 0) and (b <= 0xFFFFFFFF) then return b end 149 | end 150 | 151 | return fallback_bitconv(a, b, fb_band_r) 152 | end 153 | 154 | -- OR 155 | local fb_bor_r = function(a, b) return a or b end 156 | function fallback.bor(a, b) 157 | return fallback_bitconv(a, b, fb_bor_r) 158 | end 159 | 160 | -- XOR 161 | local fb_bxor_r = function(a, b) return (a or b) and (not (a and b)) end 162 | function fallback.bxor(a, b) 163 | return fallback_bitconv(a, b, fb_bxor_r) 164 | end 165 | 166 | -- lshift 167 | function fallback.lshift(v, s) 168 | v = checkint32(v) 169 | if s < 0 then return fallback.rshift(v, -s) end 170 | if s > 31 then return 0 end 171 | return mfloor(checkint32(v) * 2^s) % 0x100000000 172 | end 173 | 174 | -- rshift 175 | function fallback.rshift(v, s) 176 | v = checkint32(v) 177 | if s < 0 then return fallback.lshift(v, -s) end 178 | if s > 31 then return 0 end 179 | return mfloor(checkint32(v) / 2^s) % 0x100000000 180 | end 181 | 182 | -- arshift 183 | function fallback.arshift(v, s) 184 | v = checkint32(v) 185 | if s > 31 then 186 | return ((v >= 0x80000000) and 0xFFFFFFFF) or 0 187 | end 188 | if s <= 0 then return mfloor(v * 2^(-s)) % 0x100000000 end 189 | if v < 0x80000000 then return mfloor(v / 2^s) % 0x100000000 end 190 | return mfloor(v / 2^s) + (0x100000000 - 2^(32 - s)) 191 | end 192 | 193 | -- exit early if we are supposed to only use the fallbacks 194 | if force_fallback == true then 195 | debug("bitops: Forced fallback. Hope you know what you are doing.") 196 | return fallback 197 | end 198 | 199 | -- Fixups! 200 | local function fix_negvalwrap(f) 201 | return function(a, b) 202 | -- These functions are still accepting of unsigned values, and we don't want to break shifters 203 | return f(a, b) % 0x100000000 204 | end 205 | end 206 | 207 | local function fixupall_signed(bl) 208 | if bl.band(-1, -2) == -2 then 209 | debug("bitops: Logic functions are SIGNED (not unsigned) 32-bit. Really. Most likely LuaJIT. Patching.") 210 | local newbl = {} 211 | for name, fn in pairs(bl) do 212 | newbl[name] = fix_negvalwrap(fn) 213 | end 214 | return newbl 215 | end 216 | return bl 217 | end 218 | 219 | local function fixup_lshift(lshift) 220 | if lshift(0x80000000, 1) == 0x100000000 then 221 | debug("bitops: lshift can return <32 bits, applied workaround.") 222 | local o = lshift 223 | lshift = function(v, s) 224 | return checkint32(o(v, s)) 225 | end 226 | end 227 | if lshift(1, 32) == 1 then 228 | debug("bitops: lshift wraps at 32, patched.") 229 | local o = lshift 230 | lshift = function(v, s) 231 | if s > 31 then return 0 end 232 | return o(v, s) 233 | end 234 | end 235 | if lshift(2, -1) ~= 1 then 236 | debug("bitops: lshift can't handle negative shifts, patched.") 237 | local o = lshift 238 | lshift = function(v, s) 239 | if s < 0 then return bitlib.rshift(v, -s) end 240 | return o(v, s) 241 | end 242 | end 243 | return lshift 244 | end 245 | 246 | local function fixup_rshift(rshift) 247 | if rshift(1, 32) == 1 then 248 | debug("bitops: rshift wraps at 32, patched.") 249 | local o = rshift 250 | rshift = function(v, s) 251 | if s > 31 then return 0 end 252 | return o(v, s) 253 | end 254 | end 255 | if rshift(1, -1) ~= 2 then 256 | debug("bitops: rshift can't handle negative shifts, patched.") 257 | local o = rshift 258 | rshift = function(v, s) 259 | if s < 0 then return bitlib.lshift(v, -s) end 260 | return o(v, s) 261 | end 262 | end 263 | return rshift 264 | end 265 | local function fixup_arshift(arshift) 266 | if arshift(1, 32) == 1 then 267 | debug("bitops: arshift wraps at 32, patched.") 268 | local o = arshift 269 | arshift = function(v, s) 270 | if s > 31 then return ((v >= 0x80000000) and 0xFFFFFFFF) or 0 end 271 | return o(v, s) 272 | end 273 | end 274 | if arshift(1, -1) ~= 2 then 275 | debug("bitops: arshift can't handle negative shifts, patched.") 276 | local o = arshift 277 | arshift = function(v, s) 278 | if s < 0 then return bitlib.lshift(v, -s) end 279 | return o(v, s) 280 | end 281 | end 282 | return arshift 283 | end 284 | 285 | -- actual bit op assembly 286 | local bit32 = get_bit32() 287 | local ljbit = bit and fixupall_signed(bit) or {} 288 | 289 | bitlib = bit_select({ 290 | [{'bnot', 0xFFFFFFFA}] = {bit_l53('~', true), bit32.bnot, ljbit.bnot, fallback.bnot}, 291 | [{'band', 4}] = {bit_l53('&'), bit32.band, ljbit.band, fallback.band}, 292 | [{'bor', 5}] = {bit_l53('|'), bit32.bor, ljbit.bor, fallback.bor}, 293 | [{'bxor', 1}] = {bit_l53('~'), bit32.bxor, ljbit.bxor, fallback.bxor}, 294 | [{'lshift', 80, fixup_lshift}] = {bit_l53('<<'), bit32.lshift, ljbit.lshift, fallback.lshift}, 295 | [{'rshift', 0, fixup_rshift}] = {bit_l53('>>'), bit32.rshift, ljbit.rshift, fallback.rshift}, 296 | [{'arshift', 0, fixup_arshift}] = {bit32.arshift, ljbit.arshift, fallback.arshift}, 297 | }) 298 | 299 | return bitlib 300 | -------------------------------------------------------------------------------- /zpu.lua: -------------------------------------------------------------------------------- 1 | -- ZPU Emulator V3 2 | -- Based on ZPU Emulator V2.1 by gamemanj. 3 | -- Thank you. 4 | 5 | -- To get a zpu instance, call zpu.new(f:memget32, f:memset32) -> t 6 | -- You need to provide memget32, memset32 and a bit32 compatible bit library. 7 | -- To set the bit32 compatible library: zpu.set_bit32( i:val: Memory reader. 9 | -- memset32(t:zpu_inst, i:addr, i:val): Memory setter. 10 | -- 11 | -- You can also apply changes, like EMULATE implementations, globally like this: 12 | -- zpu:apply(f:changee) 13 | -- Or locally: 14 | -- zpu_inst:apply(f:changee) 15 | -- The 'changee' is a function that takes the zpu library/instance and modifies it. 16 | -- See emu.lua and zpu_emus.lua, too. 17 | -- 18 | -- All further calls/elements are on the returned zpu instance, not the library. 19 | 20 | -- Other things: 21 | -- zpu.rSP: Stack pointer. Set this to the top of the memory. 22 | -- zpu.rIP: Instruction Pointer. 23 | -- zpu:op_emulate(i:op) -> s:disassembly: Run one EMULATE opcode, can be overridden. 24 | -- zpu:run() -> s:disassembly: Run one opcode. Returns nil if unknown. 25 | -- zpu:run_trace(f:file, i:stackdump) -> s:disassembly: Run one opcode, giving an instruction and stack trace to the file. 26 | -- zpu:v_pop/zpu:v_push: helper functions 27 | 28 | --[[ 29 | The MIT License (MIT) 30 | 31 | Copyright (c) 2016 Adrian Pistol 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining a copy 34 | of this software and associated documentation files (the "Software"), to deal 35 | in the Software without restriction, including without limitation the rights 36 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 37 | copies of the Software, and to permit persons to whom the Software is 38 | furnished to do so, subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included in all 41 | copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 45 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 46 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 47 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 48 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 49 | SOFTWARE. 50 | --]] 51 | 52 | 53 | local zpu = {} 54 | zpu.__index = zpu 55 | 56 | -- Local cache of some functions. 57 | -- Many may be set at runtime. 58 | local bnot, band, bor, bxor, lshift, rshift -- set at runtime. 59 | 60 | -- sets local cache of bit functions 61 | local function assert_bitfn(bit, name) 62 | assert(bit[name], "zpuemu: Did not find function "..name.." in bitlib. We need it.") 63 | end 64 | function zpu.set_bit32(bitlib) 65 | assert_bitfn(bitlib, "bnot") bnot = bitlib.bnot 66 | assert_bitfn(bitlib, "band") band = bitlib.band 67 | assert_bitfn(bitlib, "bor") bor = bitlib.bor 68 | assert_bitfn(bitlib, "bxor") bxor = bitlib.bxor 69 | assert_bitfn(bitlib, "lshift") lshift = bitlib.lshift 70 | assert_bitfn(bitlib, "rshift") rshift = bitlib.rshift 71 | zpu.bit32 = bitlib 72 | end 73 | 74 | -- Used for byte extraction by the opcode getter. 75 | -- This ZPU implementation does not yet implement any instruction cache. 76 | local function split32(v) 77 | return { 78 | band(rshift(v, 24), 0xFF), 79 | band(rshift(v, 16), 0xFF), 80 | band(rshift(v, 8), 0xFF), 81 | band(v, 0xFF) 82 | } 83 | end 84 | 85 | -- helpers 86 | local function v_push(self, v) 87 | self.rSP = band(self.rSP - 4, 0xFFFFFFFF) 88 | self:set32(self.rSP, v) 89 | end 90 | zpu.v_push = v_push 91 | local function v_pop(self) 92 | local v = self:get32(self.rSP) 93 | self.rSP = band(self.rSP + 4, 0xFFFFFFFC) 94 | return v 95 | end 96 | zpu.v_pop = v_pop 97 | 98 | -- OPs! 99 | local function op_im(self, i, last) 100 | if last then 101 | v_push(self, bor(lshift(band(v_pop(self), 0x1FFFFFFF), 7), i)) 102 | else 103 | if band(i, 0x40) ~= 0 then i = bor(i, 0xFFFFFF80) end 104 | v_push(self, i) 105 | end 106 | end 107 | local function op_loadsp(self, i) 108 | local addr = band(self.rSP + lshift(bxor(i, 0x10), 2), 0xFFFFFFFC) 109 | v_push(self, self:get32(addr)) 110 | end 111 | local function op_storesp(self, i) 112 | -- Careful with the ordering! Documentation suggests the OPPOSITE of what it should be! 113 | -- https://github.com/zylin/zpugcc/blob/master/toolchain/gcc/libgloss/zpu/crt0.S#L836 114 | -- This is a good testpoint: 115 | -- 0x81 0x3F 116 | -- This should leave zpuinst.rSP + 4 on stack. 117 | -- You can work it out from the sources linked. 118 | local bsp = band(self.rSP + lshift(bxor(i, 0x10), 2), 0xFFFFFFFC) 119 | self:set32(bsp, v_pop(self)) 120 | end 121 | local function op_addsp(self, i) 122 | local addr = band(self.rSP + lshift(i, 2), 0xFFFFFFFC) 123 | v_push(self, band(self:get32(addr) + v_pop(self), 0xFFFFFFFF)) 124 | end 125 | local function op_load(self) 126 | self:set32(self.rSP, self:get32(band(self:get32(self.rSP), 0xFFFFFFFC))) 127 | end 128 | local function op_store(self) 129 | self:set32(band(v_pop(self), 0xFFFFFFFC), v_pop(self)) 130 | end 131 | local function op_add(self) 132 | local a = v_pop(self) 133 | self:set32(self.rSP, band(a + self:get32(self.rSP), 0xFFFFFFFF)) 134 | end 135 | local function op_and(self) 136 | v_push(self, band(v_pop(self), v_pop(self))) 137 | end 138 | local function op_or(self) 139 | v_push(self, bor(v_pop(self), v_pop(self))) 140 | end 141 | local function op_not(self) 142 | v_push(self, bnot(v_pop(self))) 143 | end 144 | 145 | local op_flip_tb = { 146 | [0] = 0, 147 | [1] = 2, 148 | [2] = 1, 149 | [3] = 3 150 | } 151 | local function op_flip_byte(i) 152 | local a = op_flip_tb[rshift(band(i, 0xC0), 6)] 153 | local b = op_flip_tb[rshift(band(i, 0x30), 4)] 154 | local c = op_flip_tb[rshift(band(i, 0x0C), 2)] 155 | local d = op_flip_tb[band(i, 0x03)] 156 | return bor(bor(a, lshift(b, 2)), bor(lshift(c, 4), lshift(d, 6))) 157 | end 158 | local function op_flip(self) 159 | local v = v_pop(self) 160 | local a = op_flip_byte(band(rshift(v, 24), 0xFF)) 161 | local b = op_flip_byte(band(rshift(v, 16), 0xFF)) 162 | local c = op_flip_byte(band(rshift(v, 8), 0xFF)) 163 | local d = op_flip_byte(band(v, 0xFF)) 164 | v_push(self, bor(bor(lshift(d, 24), lshift(c, 16)), bor(lshift(b, 8), a))) 165 | end 166 | function zpu.op_emulate(self, op) 167 | v_push(self, band(self.rIP + 1, 0xFFFFFFFF)) 168 | self.rIP = lshift(op, 5) 169 | return "EMULATE ".. op .. "/" .. bor(op, 0x20) 170 | end 171 | 172 | local function ip_adv(self) 173 | self.rIP = band(self.rIP + 1, 0xFFFFFFFF) 174 | end 175 | 176 | -- OP lookup tables 177 | local op_table_basic = { 178 | -- basic ops 179 | [0x04] = function(self) self.rIP = v_pop(self) return "POPPC" end, 180 | [0x08] = function(self) op_load(self) ip_adv(self) return "LOAD" end, 181 | [0x0C] = function(self) op_store(self) ip_adv(self) return "STORE" end, 182 | [0x02] = function(self) v_push(self, self.rSP) ip_adv(self) return "PUSHSP" end, 183 | [0x0D] = function(self) self.rSP = band(v_pop(self), 0xFFFFFFFC) ip_adv(self) return "POPSP" end, 184 | [0x05] = function(self) op_add(self) ip_adv(self) return "ADD" end, 185 | [0x06] = function(self) op_and(self) ip_adv(self) return "AND" end, 186 | [0x07] = function(self) op_or(self) ip_adv(self) return "OR" end, 187 | [0x09] = function(self) op_not(self) ip_adv(self) return "NOT" end, 188 | [0x0A] = function(self) op_flip(self) ip_adv(self) return "FLIP" end, 189 | [0x0B] = function(self) ip_adv(self) return "NOP" end 190 | } 191 | local op_table_advanced = { 192 | -- "advanced" ops, their lookup is more... involved 193 | [0x80] = function(self, op, lim) local tmp = band(op, 0x7F) op_im(self, tmp, lim) self.fLastIM = true ip_adv(self) return "IM "..tmp end, 194 | [0x60] = function(self, op) local tmp = band(op, 0x1F) op_loadsp(self, tmp) ip_adv(self) return "LOADSP " .. (bxor(0x10, tmp) * 4) end, 195 | [0x40] = function(self, op) local tmp = band(op, 0x1F) op_storesp(self, tmp) ip_adv(self) return "STORESP " .. (bxor(0x10, tmp) * 4) end, 196 | [0x20] = function(self, op) return self:op_emulate(band(op, 0x1F)) end, -- EMULATE 197 | [0x10] = function(self, op) local tmp = band(op, 0xF) op_addsp(self, tmp) ip_adv(self) return "ADDSP " .. tmp end, 198 | } 199 | 200 | -- Run a single instruction 201 | function zpu.run(self) 202 | -- NOTE: The ZPU porbably can't be trusted to have a consistent memory 203 | -- access pattern, *unless* it is accessing memory in the IO range. 204 | -- In the case of the IO range, it is specifically 205 | -- assumed MMIO will happen there, so the processor bypasses caches. 206 | -- For now, we're just using the behavior that would be used for 207 | -- a naive processor, which is exactly what this is. 208 | local op = split32(self:get32(band(self.rIP, 0xFFFFFFFC)))[band(self.rIP, 3) + 1] 209 | local lim = self.fLastIM 210 | self.fLastIM = false 211 | 212 | -- check if OP is found in lookup tables 213 | -- By the amount of ops we have, a lookup table is a good thing. 214 | -- For a few ifs, it would probably be slower. But for many, it is faster on average. 215 | local opimpl = op_table_basic[op] 216 | if opimpl then 217 | return opimpl(self), op 218 | end 219 | 220 | opimpl = op_table_advanced[band(op, 0x80)] or op_table_advanced[band(op, 0xE0)] or op_table_advanced[(band(op, 0xF0))] 221 | if opimpl then -- "advanced" op 222 | return opimpl(self, op, lim), op 223 | end 224 | return nil, op 225 | end 226 | 227 | function zpu.run_trace(self, fh, tracestack) 228 | fh:write(self.rIP .. " (" .. string.format("%x", self.rSP)) 229 | fh:flush() 230 | local cSP = self.rSP 231 | for i=1, tracestack do 232 | local success, val = pcall(self.get32, self, cSP) 233 | val = success and val or 0 234 | fh:write(string.format("/%x", val)) 235 | cSP = cSP + 4 236 | end 237 | fh:write(") ("..string.format("%x", self:get32(self.rIP)).."): ") 238 | local op, opb = self:run() 239 | if op == nil then 240 | fh:write("UNKNOWN\n") 241 | else 242 | fh:write(op .. "\n") 243 | end 244 | return op, opb 245 | end 246 | 247 | -- Create a new ZPU instance 248 | function zpu.new(memget32, memset32) 249 | assert(zpu.bit32, "zpuemu: Did not set bit32 library. Bailing out.") 250 | 251 | local zpu_instance = {} 252 | setmetatable(zpu_instance, zpu) 253 | zpu_instance.get32 = memget32 254 | zpu_instance.set32 = memset32 255 | 256 | zpu_instance.rSP = 0 257 | zpu_instance.rIP = 0 258 | zpu_instance.fLastIM = false 259 | 260 | return zpu_instance 261 | end 262 | 263 | -- Apply changes 264 | function zpu.apply(self, changee) 265 | self = changee(self) 266 | return self 267 | end 268 | 269 | -- Hooray! We're done! 270 | return zpu 271 | -------------------------------------------------------------------------------- /doc/8080_ops.txt: -------------------------------------------------------------------------------- 1 | 0x00 NOP 1 4 4 2 | 0x01 LXI B,D16 3 10 10 B <- byte 3, C <- byte 2 3 | 0x02 STAX B 1 7 7 (BC) <- A 4 | 0x03 INX B 1 5 5 BC <- BC+1 5 | 0x04 INR B 1 5 5 Z, S, P, AC B <- B+1 6 | 0x05 DCR B 1 5 5 Z, S, P, AC B <- B-1 7 | 0x06 MVI B, D8 2 7 7 B <- byte 2 8 | 0x07 RLC 1 4 4 CY A = A << 1; bit 0 = prev bit 7; CY = prev bit 7 9 | 0x08 NOP 1 4 4 10 | 0x09 DAD B 1 10 10 CY HL = HL + BC 11 | 0x0a LDAX B 1 7 7 A <- (BC) 12 | 0x0b DCX B 1 5 5 BC = BC-1 13 | 0x0c INR C 1 5 5 Z, S, P, AC C <- C+1 14 | 0x0d DCR C 1 5 5 Z, S, P, AC C <-C-1 15 | 0x0e MVI C,D8 2 7 7 C <- byte 2 16 | 0x0f RRC 1 4 4 CY A = A >> 1; bit 7 = prev bit 0; CY = prev bit 0 17 | 0x10 NOP 1 4 4 18 | 0x11 LXI D,D16 3 10 10 D <- byte 3, E <- byte 2 19 | 0x12 STAX D 1 7 7 (DE) <- A 20 | 0x13 INX D 1 5 5 DE <- DE + 1 21 | 0x14 INR D 1 5 5 Z, S, P, AC D <- D+1 22 | 0x15 DCR D 1 5 5 Z, S, P, AC D <- D-1 23 | 0x16 MVI D, D8 2 7 7 D <- byte 2 24 | 0x17 RAL 1 4 4 CY A = A << 1; bit 0 = prev CY; CY = prev bit 7 25 | 0x18 NOP 1 4 4 26 | 0x19 DAD D 1 10 10 CY HL = HL + DE 27 | 0x1a LDAX D 1 7 7 A <- (DE) 28 | 0x1b DCX D 1 5 5 DE = DE-1 29 | 0x1c INR E 1 5 5 Z, S, P, AC E <-E+1 30 | 0x1d DCR E 1 5 5 Z, S, P, AC E <- E-1 31 | 0x1e MVI E,D8 2 7 7 E <- byte 2 32 | 0x1f RAR 1 4 4 CY A = A >> 1; bit 7 = prev bit 7; CY = prev bit 0 33 | 0x20 NOP 1 4 4 34 | 0x21 LXI H,D16 3 10 10 H <- byte 3, L <- byte 2 35 | 0x22 SHLD adr 3 16 16 (adr) <-L; (adr+1)<-H 36 | 0x23 INX H 1 5 5 HL <- HL + 1 37 | 0x24 INR H 1 5 5 Z, S, P, AC H <- H+1 38 | 0x25 DCR H 1 5 5 Z, S, P, AC H <- H-1 39 | 0x26 MVI H,D8 2 7 7 H <- byte 2 40 | 0x27 DAA 1 4 4 Z, S, P, CY, AC If ((A >= 0x0A) || flag.AC) A += 6; flag.AC = carried outside of the lower 4 bits; if ((A >= 0xA0) || flag.CY) {A += 0x60; flag.CY = carry;} 41 | 0x28 NOP 1 4 4 42 | 0x29 DAD H 1 10 10 CY HL = HL + HI 43 | 0x2a LHLD adr 3 16 16 L <- (adr); H<-(adr+1) 44 | 0x2b DCX H 1 5 5 HL = HL-1 45 | 0x2c INR L 1 5 5 Z, S, P, AC L <- L+1 46 | 0x2d DCR L 1 5 5 Z, S, P, AC L <- L-1 47 | 0x2e MVI L, D8 2 7 7 L <- byte 2 48 | 0x2f CMA 1 4 4 A <- !A 49 | 0x30 NOP 1 4 4 50 | 0x31 LXI SP, D16 3 10 10 SP.hi <- byte 3, SP.lo <- byte 2 51 | 0x32 STA adr 3 13 13 (adr) <- A 52 | 0x33 INX SP 1 5 5 SP = SP + 1 53 | 0x34 INR M 1 10 10 Z, S, P, AC (HL) <- (HL)+1 54 | 0x35 DCR M 1 10 10 Z, S, P, AC (HL) <- (HL)-1 55 | 0x36 MVI M,D8 2 10 10 (HL) <- byte 2 56 | 0x37 STC 1 4 4 CY CY = 1 57 | 0x38 NOP 1 4 4 58 | 0x39 DAD SP 1 10 10 CY HL = HL + SP 59 | 0x3a LDA adr 3 13 13 A <- (adr) 60 | 0x3b DCX SP 1 5 5 SP = SP-1 61 | 0x3c INR A 1 5 5 Z, S, P, AC A <- A+1 62 | 0x3d DCR A 1 5 5 Z, S, P, AC A <- A-1 63 | 0x3e MVI A,D8 2 7 7 A <- byte 2 64 | 0x3f CMC 1 4 4 CY CY=!CY 65 | 0x40 MOV B,B 1 5 5 B <- B 66 | 0x41 MOV B,C 1 5 5 B <- C 67 | 0x42 MOV B,D 1 5 5 B <- D 68 | 0x43 MOV B,E 1 5 5 B <- E 69 | 0x44 MOV B,H 1 5 5 B <- H 70 | 0x45 MOV B,L 1 5 5 B <- L 71 | 0x46 MOV B,M 1 7 7 B <- (HL) 72 | 0x47 MOV B,A 1 5 5 B <- A 73 | 0x48 MOV C,B 1 5 5 C <- B 74 | 0x49 MOV C,C 1 5 5 C <- C 75 | 0x4a MOV C,D 1 5 5 C <- D 76 | 0x4b MOV C,E 1 5 5 C <- E 77 | 0x4c MOV C,H 1 5 5 C <- H 78 | 0x4d MOV C,L 1 5 5 C <- L 79 | 0x4e MOV C,M 1 7 7 C <- (HL) 80 | 0x4f MOV C,A 1 5 5 C <- A 81 | 0x50 MOV D,B 1 5 5 D <- B 82 | 0x51 MOV D,C 1 5 5 D <- C 83 | 0x52 MOV D,D 1 5 5 D <- D 84 | 0x53 MOV D,E 1 5 5 D <- E 85 | 0x54 MOV D,H 1 5 5 D <- H 86 | 0x55 MOV D,L 1 5 5 D <- L 87 | 0x56 MOV D,M 1 7 7 D <- (HL) 88 | 0x57 MOV D,A 1 5 5 D <- A 89 | 0x58 MOV E,B 1 5 5 E <- B 90 | 0x59 MOV E,C 1 5 5 E <- C 91 | 0x5a MOV E,D 1 5 5 E <- D 92 | 0x5b MOV E,E 1 5 5 E <- E 93 | 0x5c MOV E,H 1 5 5 E <- H 94 | 0x5d MOV E,L 1 5 5 E <- L 95 | 0x5e MOV E,M 1 7 7 E <- (HL) 96 | 0x5f MOV E,A 1 5 5 E <- A 97 | 0x60 MOV H,B 1 5 5 H <- B 98 | 0x61 MOV H,C 1 5 5 H <- C 99 | 0x62 MOV H,D 1 5 5 H <- D 100 | 0x63 MOV H,E 1 5 5 H <- E 101 | 0x64 MOV H,H 1 5 5 H <- H 102 | 0x65 MOV H,L 1 5 5 H <- L 103 | 0x66 MOV H,M 1 7 7 H <- (HL) 104 | 0x67 MOV H,A 1 5 5 H <- A 105 | 0x68 MOV L,B 1 5 5 L <- B 106 | 0x69 MOV L,C 1 5 5 L <- C 107 | 0x6a MOV L,D 1 5 5 L <- D 108 | 0x6b MOV L,E 1 5 5 L <- E 109 | 0x6c MOV L,H 1 5 5 L <- H 110 | 0x6d MOV L,L 1 5 5 L <- L 111 | 0x6e MOV L,M 1 7 7 L <- (HL) 112 | 0x6f MOV L,A 1 5 5 L <- A 113 | 0x70 MOV M,B 1 7 7 (HL) <- B 114 | 0x71 MOV M,C 1 7 7 (HL) <- C 115 | 0x72 MOV M,D 1 7 7 (HL) <- D 116 | 0x73 MOV M,E 1 7 7 (HL) <- E 117 | 0x74 MOV M,H 1 7 7 (HL) <- H 118 | 0x75 MOV M,L 1 7 7 (HL) <- L 119 | 0x76 HLT 1 7 7 120 | 0x77 MOV M,A 1 7 7 (HL) <- A 121 | 0x78 MOV A,B 1 5 5 A <- B 122 | 0x79 MOV A,C 1 5 5 A <- C 123 | 0x7a MOV A,D 1 5 5 A <- D 124 | 0x7b MOV A,E 1 5 5 A <- E 125 | 0x7c MOV A,H 1 5 5 A <- H 126 | 0x7d MOV A,L 1 5 5 A <- L 127 | 0x7e MOV A,M 1 7 7 A <- (HL) 128 | 0x7f MOV A,A 1 5 5 A <- A 129 | 0x80 ADD B 1 4 4 Z, S, P, CY, AC A <- A + B 130 | 0x81 ADD C 1 4 4 Z, S, P, CY, AC A <- A + C 131 | 0x82 ADD D 1 4 4 Z, S, P, CY, AC A <- A + D 132 | 0x83 ADD E 1 4 4 Z, S, P, CY, AC A <- A + E 133 | 0x84 ADD H 1 4 4 Z, S, P, CY, AC A <- A + H 134 | 0x85 ADD L 1 4 4 Z, S, P, CY, AC A <- A + L 135 | 0x86 ADD M 1 7 7 Z, S, P, CY, AC A <- A + (HL) 136 | 0x87 ADD A 1 4 4 Z, S, P, CY, AC A <- A + A 137 | 0x88 ADC B 1 4 4 Z, S, P, CY, AC A <- A + B + CY 138 | 0x89 ADC C 1 4 4 Z, S, P, CY, AC A <- A + C + CY 139 | 0x8a ADC D 1 4 4 Z, S, P, CY, AC A <- A + D + CY 140 | 0x8b ADC E 1 4 4 Z, S, P, CY, AC A <- A + E + CY 141 | 0x8c ADC H 1 4 4 Z, S, P, CY, AC A <- A + H + CY 142 | 0x8d ADC L 1 4 4 Z, S, P, CY, AC A <- A + L + CY 143 | 0x8e ADC M 1 7 7 Z, S, P, CY, AC A <- A + (HL) + CY 144 | 0x8f ADC A 1 4 4 Z, S, P, CY, AC A <- A + A + CY 145 | 0x90 SUB B 1 4 4 Z, S, P, CY, AC A <- A - B 146 | 0x91 SUB C 1 4 4 Z, S, P, CY, AC A <- A - C 147 | 0x92 SUB D 1 4 4 Z, S, P, CY, AC A <- A - D 148 | 0x93 SUB E 1 4 4 Z, S, P, CY, AC A <- A - E 149 | 0x94 SUB H 1 4 4 Z, S, P, CY, AC A <- A - H 150 | 0x95 SUB L 1 4 4 Z, S, P, CY, AC A <- A - L 151 | 0x96 SUB M 1 7 7 Z, S, P, CY, AC A <- A - (HL) 152 | 0x97 SUB A 1 4 4 Z, S, P, CY, AC A <- A - A 153 | 0x98 SBB B 1 4 4 Z, S, P, CY, AC A <- A - B - CY 154 | 0x99 SBB C 1 4 4 Z, S, P, CY, AC A <- A - C - CY 155 | 0x9a SBB D 1 4 4 Z, S, P, CY, AC A <- A - D - CY 156 | 0x9b SBB E 1 4 4 Z, S, P, CY, AC A <- A - E - CY 157 | 0x9c SBB H 1 4 4 Z, S, P, CY, AC A <- A - H - CY 158 | 0x9d SBB L 1 4 4 Z, S, P, CY, AC A <- A - L - CY 159 | 0x9e SBB M 1 7 7 Z, S, P, CY, AC A <- A - (HL) - CY 160 | 0x9f SBB A 1 4 4 Z, S, P, CY, AC A <- A - A - CY 161 | 0xa0 ANA B 1 4 4 Z, S, P, CY, AC A <- A & B 162 | 0xa1 ANA C 1 4 4 Z, S, P, CY, AC A <- A & C 163 | 0xa2 ANA D 1 4 4 Z, S, P, CY, AC A <- A & D 164 | 0xa3 ANA E 1 4 4 Z, S, P, CY, AC A <- A & E 165 | 0xa4 ANA H 1 4 4 Z, S, P, CY, AC A <- A & H 166 | 0xa5 ANA L 1 4 4 Z, S, P, CY, AC A <- A & L 167 | 0xa6 ANA M 1 7 7 Z, S, P, CY, AC A <- A & (HL) 168 | 0xa7 ANA A 1 4 4 Z, S, P, CY, AC A <- A & A 169 | 0xa8 XRA B 1 4 4 Z, S, P, CY, AC A <- A ^ B 170 | 0xa9 XRA C 1 4 4 Z, S, P, CY, AC A <- A ^ C 171 | 0xaa XRA D 1 4 4 Z, S, P, CY, AC A <- A ^ D 172 | 0xab XRA E 1 4 4 Z, S, P, CY, AC A <- A ^ E 173 | 0xac XRA H 1 4 4 Z, S, P, CY, AC A <- A ^ H 174 | 0xad XRA L 1 4 4 Z, S, P, CY, AC A <- A ^ L 175 | 0xae XRA M 1 7 7 Z, S, P, CY, AC A <- A ^ (HL) 176 | 0xaf XRA A 1 4 4 Z, S, P, CY, AC A <- A ^ A 177 | 0xb0 ORA B 1 4 4 Z, S, P, CY, AC A <- A | B 178 | 0xb1 ORA C 1 4 4 Z, S, P, CY, AC A <- A | C 179 | 0xb2 ORA D 1 4 4 Z, S, P, CY, AC A <- A | D 180 | 0xb3 ORA E 1 4 4 Z, S, P, CY, AC A <- A | E 181 | 0xb4 ORA H 1 4 4 Z, S, P, CY, AC A <- A | H 182 | 0xb5 ORA L 1 4 4 Z, S, P, CY, AC A <- A | L 183 | 0xb6 ORA M 1 7 7 Z, S, P, CY, AC A <- A | (HL) 184 | 0xb7 ORA A 1 4 4 Z, S, P, CY, AC A <- A | A 185 | 0xb8 CMP B 1 4 4 Z, S, P, CY, AC A - B 186 | 0xb9 CMP C 1 4 4 Z, S, P, CY, AC A - C 187 | 0xba CMP D 1 4 4 Z, S, P, CY, AC A - D 188 | 0xbb CMP E 1 4 4 Z, S, P, CY, AC A - E 189 | 0xbc CMP H 1 4 4 Z, S, P, CY, AC A - H 190 | 0xbd CMP L 1 4 4 Z, S, P, CY, AC A - L 191 | 0xbe CMP M 1 7 7 Z, S, P, CY, AC A - (HL) 192 | 0xbf CMP A 1 4 4 Z, S, P, CY, AC A - A 193 | 0xc0 RET !FZ 1 5 11 if NZ, RET 194 | 0xc1 POP B 1 10 10 C <- (sp); B <- (sp+1); sp <- sp+2 195 | 0xc2 JMP !FZ adr 3 10 10 if NZ, PC <- adr 196 | 0xc3 JMP adr 3 10 10 PC <= adr 197 | 0xc4 CALL !FZ adr 3 11 17 if NZ, CALL adr 198 | 0xc5 PUSH B 1 11 11 (sp-2)<-C; (sp-1)<-B; sp <- sp - 2 199 | 0xc6 ADI D8 2 7 7 Z, S, P, CY, AC A <- A + byte 200 | 0xc7 RST 0 1 11 11 CALL $0 201 | 0xc8 RET FZ 1 5 11 if Z, RET 202 | 0xc9 RET 1 10 10 PC.lo <- (sp); PC.hi<-(sp+1); SP <- SP+2 203 | 0xca JMP FZ adr 3 3 3 if Z, PC <- adr 204 | 0xcb JMP adr 3 3 3 205 | 0xcc CALL FZ adr 3 11 17 if Z, CALL adr 206 | 0xcd CALL adr 3 17 17 (SP-1)<-PC.hi;(SP-2)<-PC.lo;SP<-SP+2;PC=adr 207 | 0xce ACI D8 2 7 7 Z, S, P, CY, AC A <- A + data + CY 208 | 0xcf RST 1 1 11 11 CALL $8 209 | 0xd0 RET !FC 1 5 11 if NCY, RET 210 | 0xd1 POP D 1 10 10 E <- (sp); D <- (sp+1); sp <- sp+2 211 | 0xd2 JMP !FC adr 3 10 10 if NCY, PC<-adr 212 | 0xd3 OUT D8 2 10 10 special 213 | 0xd4 CALL !FC adr 3 11 17 if NCY, CALL adr 214 | 0xd5 PUSH D 1 11 11 (sp-2)<-E; (sp-1)<-D; sp <- sp - 2 215 | 0xd6 SUI D8 2 7 7 Z, S, P, CY, AC A <- A - data 216 | 0xd7 RST 2 1 11 11 CALL $10 217 | 0xd8 RET FC 1 5 11 if CY, RET 218 | 0xd9 RET 1 10 10 219 | 0xda JMP FC adr 3 10 10 if CY, PC<-adr 220 | 0xdb IN D8 2 10 10 special 221 | 0xdc CALL FC adr 3 11 17 if CY, CALL adr 222 | 0xdd CALL adr 3 17 17 223 | 0xde SBI D8 2 7 7 Z, S, P, CY, AC A <- A - data - CY 224 | 0xdf RST 3 1 11 11 CALL $18 225 | 0xe0 RET !FPE 1 5 11 if PO, RET 226 | 0xe1 POP H 1 10 10 L <- (sp); H <- (sp+1); sp <- sp+2 227 | 0xe2 JMP !FPE adr 3 10 10 if PO, PC <- adr 228 | 0xe3 XTHL 1 18 18 L <-> (SP); H <-> (SP+1) 229 | 0xe4 CALL !FPE adr 3 11 17 if PO, CALL adr 230 | 0xe5 PUSH H 1 11 11 (sp-2)<-L; (sp-1)<-H; sp <- sp - 2 231 | 0xe6 ANI D8 2 7 7 Z, S, P, CY, AC A <- A & data 232 | 0xe7 RST 4 1 11 11 CALL $20 233 | 0xe8 RET FPE 1 5 11 if PE, RET 234 | 0xe9 PCHL 1 5 5 PC.hi <- H; PC.lo <- L 235 | 0xea JMP FPE adr 3 10 10 if PE, PC <- adr 236 | 0xeb XCHG 1 5 5 H <-> D; L <-> E 237 | 0xec CALL FPE adr 3 11 17 if PE, CALL adr 238 | 0xed CALL adr 3 17 17 239 | 0xee XRI D8 2 7 7 Z, S, P, CY, AC A <- A ^ data 240 | 0xef RST 5 1 11 11 CALL $28 241 | 0xf0 RET !FS 1 5 11 if S=0, RET 242 | 0xf1 POP PSW 1 10 10 flags <- (sp); A <- (sp+1); sp <- sp+2 243 | 0xf2 JMP !FS adr 3 10 10 if S=0, PC <- adr 244 | 0xf3 DI 1 4 4 special 245 | 0xf4 CALL !FS adr 3 11 17 if S=0, CALL adr 246 | 0xf5 PUSH PSW 1 11 11 (sp-2)<-flags; (sp-1)<-A; sp <- sp - 2 247 | 0xf6 ORI D8 2 7 7 Z, S, P, CY, AC A <- A | data 248 | 0xf7 RST 6 1 11 11 CALL $30 249 | 0xf8 RET FS 1 5 11 if S=1, RET 250 | 0xf9 SPHL 1 5 5 SP=HL 251 | 0xfa JMP FS adr 3 10 10 if S=1, PC <- adr 252 | 0xfb EI 1 4 4 special 253 | 0xfc CALL FS adr 3 11 17 if S=1, CALL adr 254 | 0xfd CALL adr 3 17 17 255 | 0xfe CPI D8 2 7 7 Z, S, P, CY, AC A - data 256 | 0xff RST 7 1 11 11 CALL $38 257 | -------------------------------------------------------------------------------- /memlib.lua: -------------------------------------------------------------------------------- 1 | -- Memlib 2 | -- Composable memory blocks with different backends. 3 | 4 | -- Basic 5 | local _M = { 6 | backend = {} 7 | } 8 | 9 | local bitops = require("bitops") 10 | local band, bor, blshift, brshift = bitops.band, bitops.bor, bitops.lshift, bitops.rshift 11 | local strbyte, strsub, strfind, strchar = string.byte, string.sub, string.find, string.char 12 | 13 | -- Helpers. 14 | local function and32(v, addr) 15 | if addr then return band(v, 0xFFFFFFFC) end 16 | return band(v, 0xFFFFFFFF) 17 | end 18 | 19 | local function comb4b_be(a, b, c, d) 20 | return bor(bor(blshift(a, 24), blshift(b, 16)), bor(blshift(c, 8), d)) 21 | end 22 | 23 | local function dis32(v) 24 | local a = band(brshift(v, 24), 0xFF) 25 | local b = band(brshift(v, 16), 0xFF) 26 | local c = band(brshift(v, 8), 0xFF) 27 | local d = band(v, 0xFF) 28 | return a, b, c, d 29 | end 30 | 31 | -- Backend implementation "shortcuts" 32 | local function get32_get(memory, i) 33 | local a = memory:get(i) 34 | local b = memory:get(i+1) 35 | local c = memory:get(i+2) 36 | local d = memory:get(i+3) 37 | return comb4b_be(a, b, c, d) 38 | end 39 | 40 | local function get_get32(memory, i) 41 | local a, _, _, _ = memory:get32(i) 42 | return a 43 | end 44 | 45 | local function set32_set(memory, i, v) 46 | local a, b, c, d = dis32(v) 47 | memory:set(i, a) 48 | memory:set(and32(i + 1), b) 49 | memory:set(and32(i + 2), c) 50 | memory:set(and32(i + 3), d) 51 | end 52 | 53 | local function set_set32(memory, i, v) 54 | local a, b, c, d = memory:get32(i) 55 | memory:set32(i, comb4b_be(v, b, c, d)) 56 | end 57 | 58 | function strreplace(str, pos, r) 59 | local n = #r 60 | return strsub(str, 1, pos-n) .. r .. strsub(str, pos+n) 61 | end 62 | 63 | -- Public Helpers 64 | function _M.new(driver, ...) 65 | local drv = _M.backend[driver] 66 | if drv then 67 | return drv(...) 68 | end 69 | error("No such driver: "..tostring(driver), 1) 70 | end 71 | 72 | function _M.copy(mem1, mem2) 73 | for i=0, mem1.size do 74 | mem2:set(i, mem1:get(i)) 75 | end 76 | end 77 | 78 | function _M.copyto(memsrc, addroff, memdst) 79 | for i=0, memsrc.size do 80 | local v = memsrc:get(i) 81 | memdst:set(addroff + i, v) 82 | end 83 | end 84 | 85 | -- Memory "Backends", where things are actually stored. 86 | 87 | -- Simplistic table backend. 88 | -- Loads of overhead. But works. And is okay fast. 89 | local function setifeanz(t, k, v) 90 | if v == 0 then 91 | if t[k] then 92 | t[k] = nil 93 | end 94 | else 95 | t[k] = v 96 | end 97 | end 98 | 99 | local fns_tbackend = { 100 | get32be = function(memory, i) 101 | if ((memory.size - 3) < i) or (0 > i) then 102 | error("Bad Access (" .. string.format("%08x", i) .. ")") 103 | end 104 | 105 | local a = memory[i] or 0 106 | local b = memory[and32(i + 1)] or 0 107 | local c = memory[and32(i + 2)] or 0 108 | local d = memory[and32(i + 3)] or 0 109 | return comb4b_be(a, b, c, d) 110 | end, 111 | set32be = function(memory, i, v) 112 | if ((memory.size - 3) < i) or (0 > i) then 113 | error("Bad Access (" .. string.format("%08x", i) .. ")") 114 | end 115 | 116 | local a = band(brshift(v, 24), 0xFF) 117 | setifeanz(memory, i, a) 118 | local b = band(brshift(v, 16), 0xFF) 119 | setifeanz(memory, and32(i + 1), b) 120 | 121 | local c = band(brshift(v, 8), 0xFF) 122 | setifeanz(memory, and32(i + 2), c) 123 | 124 | local d = band(v, 0xFF) 125 | setifeanz(memory, and32(i + 3), d) 126 | end, 127 | 128 | 129 | get = function(memory, i) 130 | if (memory.size < i) or (0 > i) then 131 | error("Bad Access (" .. string.format("%08x", i) .. ")") 132 | end 133 | return memory[i] or 0 134 | end, 135 | set = function(memory, i, v) 136 | if (memory.size < i) or (0 > i) then 137 | error("Bad Access (" .. string.format("%08x", i) .. ")") 138 | end 139 | setifeanz(memory, i, v) 140 | end 141 | } 142 | 143 | function _M.backend.table(memsz, prealloc) 144 | local memory = { 145 | get32be = fns_tbackend.get32be, 146 | set32be = fns_tbackend.set32be, 147 | set = fns_tbackend.set, 148 | get = fns_tbackend.get, 149 | size = memsz 150 | } 151 | if prealloc then 152 | for i = 0, memsz do 153 | memory[i] = 0 154 | end 155 | end 156 | return memory 157 | end 158 | 159 | -- Simple read-only string backend. 160 | -- Incapable of writing, but efficient for readonly operation! 161 | 162 | local function rostr_werr() 163 | error("rostring memory backend is incapable of writing.") 164 | end 165 | 166 | -- Returns two values: 167 | -- 1. The last non-NULL memory index, or -1 if the memory is empty. 168 | -- 2. The stripped string. 169 | local function rostr_striptrailingnulls(str) 170 | local l2, _ = strfind(str, "%z+$") 171 | if not l2 then return str end 172 | return strsub(str, 1, l2 - 1) 173 | end 174 | 175 | local fns_rostring = { 176 | get = function(memory, i) 177 | if (memory.size < i) or (0 > i) then 178 | error("Bad Access (" .. string.format("%08x", i) .. ")") 179 | end 180 | return strbyte(memory.str, i + 1) or 0 181 | end, 182 | get32be = function(memory, i) 183 | if ((memory.size - 3) < i) or (0 > i) then 184 | error("Bad Access (" .. string.format("%08x", i) .. ")") 185 | end 186 | 187 | local a, b, c, d = strbyte(memory.str, i + 1, i + 4) 188 | return comb4b_be(a or 0, b or 0, c or 0, d or 0) 189 | end, 190 | } 191 | 192 | function _M.backend.rostring(string, memsz) 193 | local size = memsz or #string 194 | string = rostr_striptrailingnulls(string) 195 | return { 196 | -- Don't go changing any ofthese. 197 | str = string, 198 | size = size, 199 | 200 | get = fns_rostring.get, 201 | get32be = fns_rostring.get32be, 202 | 203 | -- Incapable of writing 204 | set = rostr_werr, 205 | set32be = rostr_werr, 206 | } 207 | end 208 | 209 | -- Read/Write overlay for existing memory backend. 210 | -- Useful for ROM/RAM. 211 | local function rwovl_read(romem, ovlt, i) 212 | local oval = ovlt[i] 213 | if oval then return oval end 214 | if romem.size >= i then return romem:get(i) end 215 | return 0 216 | end 217 | 218 | local fns_rwovl = { 219 | get32be = function(memory, i) 220 | if ((memory.size - 3) < i) or (0 > i) then 221 | error("Bad Access (" .. string.format("%08x", i) .. ")") 222 | end 223 | 224 | local a = rwovl_read(memory.romem, memory, i) 225 | local b = rwovl_read(memory.romem, memory, and32(i + 1)) 226 | local c = rwovl_read(memory.romem, memory, and32(i + 2)) 227 | local d = rwovl_read(memory.romem, memory, and32(i + 3)) 228 | return comb4b_be(a, b, c, d) 229 | end, 230 | set32be = function(memory, i, v) 231 | if ((memory.size - 3) < i) or (0 > i) then 232 | error("Bad Access (" .. string.format("%08x", i) .. ")") 233 | end 234 | 235 | memory[i] = band(brshift(v, 24), 0xFF) 236 | memory[and32(i + 1)] = band(brshift(v, 16), 0xFF) 237 | memory[and32(i + 2)] = band(brshift(v, 8), 0xFF) 238 | memory[and32(i + 3)] = band(v, 0xFF) 239 | end, 240 | 241 | 242 | get = function(memory, i) 243 | if (memory.size < i) or (0 > i) then 244 | error("Bad Access (" .. string.format("%08x", i) .. ")") 245 | end 246 | return rwovl_read(memory.romem, memory, i) 247 | end, 248 | set = function(memory, i, v) 249 | if (memory.size < i) or (0 > i) then 250 | error("Bad Access (" .. string.format("%08x", i) .. ")") 251 | end 252 | memory[i] = v 253 | end 254 | } 255 | 256 | function _M.backend.rwoverlay(existing_mem, memsz) 257 | return { 258 | -- Don't go changing any of these. 259 | romem = existing_mem, 260 | get32be = fns_rwovl.get32be, 261 | set32be = fns_rwovl.set32be, 262 | set = fns_rwovl.set, 263 | get = fns_rwovl.get, 264 | size = memsz or existing_mem.size, 265 | } 266 | end 267 | 268 | -- Read/Write overlay for existing memory backend. 32bit read version. 269 | -- Useful for ROM/RAM. 270 | 271 | local fns_rwovl32 = { 272 | get32be = function(memory, i) 273 | if ((memory.size - 3) < i) or (0 > i) then 274 | error("Bad Access (" .. string.format("%08x", i) .. ")") 275 | end 276 | 277 | local romem = memory.romem 278 | local oval = memory[i / 4] 279 | if oval then return oval end 280 | if romem.size >= i then return romem:get32be(i) end 281 | return 0 282 | end, 283 | set32be = function(memory, i, v) 284 | if ((memory.size - 3) < i) or (0 > i) then 285 | error("Bad Access (" .. string.format("%08x", i) .. ")") 286 | end 287 | 288 | memory[i / 4] = v 289 | end, 290 | 291 | errorout = function() 292 | error("memlib: rwoverlay32 doesn't support byte accesses.") 293 | end 294 | } 295 | 296 | function _M.backend.rwoverlay32(existing_mem, memsz) 297 | return { 298 | -- Don't go changing any of these. 299 | romem = existing_mem, 300 | get32be = fns_rwovl32.get32be, 301 | set32be = fns_rwovl32.set32be, 302 | set = fns_rwovl32.errorout, 303 | get = fns_rwovl32.errorout, 304 | size = memsz or existing_mem.size, 305 | } 306 | end 307 | 308 | -- Rather simple cached file backend. 309 | -- Little fancy, not much. 310 | -- Warning: May not write back data unless one reads data after it. 311 | 312 | local function file_getcached(s, i, n) 313 | local blksz = s.blksz 314 | local ind = i % blksz 315 | local iblk = i - ind 316 | local cblk = s.cblk 317 | local cached = s.cached 318 | local fh = s.fh 319 | 320 | if s.didmod then 321 | fh:seek("set", cblk) 322 | fh:write(cached) 323 | fh:flush() 324 | s.didmod = false 325 | end 326 | 327 | if cblk == iblk then -- current page is the cached one 328 | if not cached then 329 | fh:seek("set", iblk) 330 | cached = fh:read(blksz) 331 | s.cached = cached 332 | end 333 | else -- current cache is for another block. 334 | fh:seek("set", iblk) 335 | cached = fh:read(blksz) 336 | s.cached = cached 337 | s.cblk = iblk 338 | end 339 | return strbyte(cached, ind+1) 340 | end 341 | 342 | local function file_setcached(s, i, v) 343 | local blksz = s.blksz 344 | local ind = i % blksz 345 | local iblk = i - ind 346 | local cblk = s.cblk 347 | local cached = s.cached 348 | local fh = s.fh 349 | if cblk == iblk then -- current page is the cached one 350 | fh:seek("set", iblk) 351 | if not cached then 352 | cached = fh:read(blksz) 353 | s.cached = cached 354 | end 355 | else -- current cache is for another block. 356 | if s.didmod then 357 | fh:seek("set", cblk) 358 | fh:write(cached) 359 | fh:flush() 360 | s.didmod = false 361 | end 362 | fh:seek("set", iblk) 363 | cached = fh:read(blksz) 364 | s.cached = cached 365 | s.cblk = iblk 366 | end 367 | s.didmod = true 368 | s.cached = strreplace(s.cached, ind+1, strchar(v)) 369 | end 370 | 371 | local fns_file = { 372 | get = function(self, i) 373 | if (self.size < i) or (0 > i) then 374 | error("Bad Access (" .. string.format("%08x", i) .. ")") 375 | end 376 | return file_getcached(self, i) 377 | end, 378 | get32be = function(self, i) 379 | if ((self.size - 3) < i) or (0 > i) then 380 | error("Bad Access (" .. string.format("%08x", i) .. ")") 381 | end 382 | return comb4b_be(file_getcached(self, i), file_getcached(self, i+1), file_getcached(self, i+2), file_getcached(self, i+3)) 383 | end, 384 | 385 | set = function(self, i, v) 386 | if (self.size < i) or (0 > i) then 387 | error("Bad Access (" .. string.format("%08x", i) .. ")") 388 | end 389 | file_setcached(self, i, v) 390 | end, 391 | set32be = function(self, i, v) 392 | if (self.size < i) or (0 > i) then 393 | error("Bad Access (" .. string.format("%08x", i) .. ")") 394 | end 395 | fh_setcached(self, i, strchar(band(brshift(v, 24), 0xFF), band(brshift(v, 16), 0xFF), band(brshift(v, 8), 0xFF), band(v, 0xFF))) 396 | end, 397 | } 398 | 399 | local function fh_size(fh) 400 | local current = fh:seek() 401 | local size = fh:seek("end") 402 | fh:seek("set", current) 403 | return size 404 | end 405 | 406 | function _M.backend.file(file, blksz) 407 | local fh = file 408 | if type(file) == "string" then 409 | fh = assert(io.open(file, "r+b")) 410 | end 411 | 412 | local realsize = fh_size(fh) 413 | 414 | return { 415 | fh = fh, 416 | size = realsize, 417 | blksz = blksz or 128, 418 | cblk = 0, 419 | cached = nil, 420 | didmod = false, 421 | 422 | get = fns_file.get, 423 | get32be = fns_file.get32be, 424 | 425 | set = fns_file.set, 426 | set32be = fns_file.set32be, 427 | } 428 | end 429 | 430 | local function rofile_werr() 431 | error("rofile memory backend is incapable of writing.") 432 | end 433 | 434 | function _M.backend.rofile(file, blksz) 435 | local fh = file 436 | if type(file) == "string" then 437 | fh = assert(io.open(file, "rb")) 438 | end 439 | 440 | local realsize = fh_size(fh) 441 | 442 | return { 443 | fh = fh, 444 | size = realsize, 445 | blksz = blksz or 128, 446 | cblk = 0, 447 | cached = nil, 448 | didmod = false, 449 | 450 | 451 | get = fns_file.get, 452 | get32be = fns_file.get32be, 453 | 454 | set = rofile_werr, 455 | set32be = rofile_werr, 456 | } 457 | end 458 | 459 | -- Memory block composition. 460 | local function run_handlers(self, method, i, v) 461 | local addr_handler = self.addr_handlers[i] 462 | if addr_handler then return addr_handler(self, method, i, v) end 463 | 464 | local handlers = self.handlers 465 | local hlen = #handlers 466 | if hlen < 0 then 467 | for i=1, hlen do 468 | local res = handlers[i](self, method, i, v) 469 | if res then return res end 470 | end 471 | end 472 | 473 | return self.backend[method](self.backend, i, v) 474 | end 475 | 476 | function _M.compose(memory, addrhandlers, handlers) 477 | local composed = { 478 | backend = memory, 479 | addr_handlers = addrhandlers or {}, 480 | handlers = handlers or {}, 481 | } 482 | 483 | setmetatable(composed, {__index=function(this, name) 484 | local fn = function(self, i, v) 485 | return run_handlers(self, name, i, v) 486 | end 487 | rawset(this, name, fn) 488 | return fn 489 | end}) 490 | 491 | return composed 492 | end 493 | 494 | return _M 495 | -------------------------------------------------------------------------------- /8080/ops.lua: -------------------------------------------------------------------------------- 1 | -- Actual implementation of OPs. 2 | -- Good god, this is... huge. 3 | -- Luckily, it is generated. 4 | 5 | local band, bor, bxor, rshift, lshift 6 | 7 | -- Helpers 8 | local function a8(x) 9 | return band(x, 0xFF) 10 | end 11 | 12 | local function pair(X, Y) 13 | return bor(lshift(X, 8), Y) 14 | end 15 | 16 | local function spair(s, Xn, Yn, res) 17 | s[Xn] = rshift(band(res, 0xFF00), 8) 18 | s[Yn] = band(res, 0xFF) 19 | s.cy = (band(res, 0xFFFF0000) > 0) 20 | end 21 | 22 | -- Parity is counting the number of set bits. 23 | -- If the number of set bits is odd, it will return true. 24 | -- If the number bits is even, it will return false. 25 | -- Notably, size should be the amount of bits *minus 1*. So parity(res, 7) for the common 8-bit case. 26 | local function parity_r(x, size) 27 | local p = 0 28 | x = band(x, lshift(1, size) - 1) 29 | for i=0, size do 30 | if band(x, 1) ~= 0 then 31 | p = p + 1 32 | end 33 | x = rshift(x, 1) 34 | end 35 | return band(p, 1) == 0 36 | end 37 | 38 | -- Because bitops are rather slow, 39 | -- caching them could be a good advantage. 40 | local paritycache = {} 41 | local function parity(x) 42 | local cx = paritycache[x] 43 | if cx then 44 | return cx 45 | end 46 | local r = parity_r(x, 7) 47 | paritycache[x] = r 48 | return r 49 | end 50 | 51 | local function flaghandle(inst, res) 52 | res = band(res, 0xFF) 53 | inst.z = (res == 0) -- is zero 54 | inst.s = (band(res, 0x80) ~= 0) -- sign flag, if bit 7 set 55 | inst.p = parity(res) 56 | return res 57 | end 58 | 59 | local function mdc(b, c) 60 | if c then 61 | return b + 1 62 | end 63 | return b 64 | end 65 | 66 | local function addcda(a, b, c) 67 | b = mdc(b, c) 68 | local b1 = (a % 16) + (b % 16) 69 | return band(a + b, 0xFF), b1 > 0x0F 70 | end 71 | local function addcdn(a, b, c) 72 | b = mdc(b, c) 73 | return band(a + b, 0xFF), (a + b) > 0xFF 74 | end 75 | local function addcdb(a, b, c) 76 | b = mdc(b, c) 77 | local b1 = (a % 16) + (b % 16) 78 | return band(a + b, 0xFF), b1 > 0x0F, (a + b) > 0xFF 79 | end 80 | 81 | local function subcda(a, b, c) 82 | b = mdc(b, c) 83 | local b1 = (a % 16) + (b % 16) 84 | return band(a - b, 0xFF), b1 > 0xF 85 | end 86 | local function subcdn(a, b, c) 87 | b = mdc(b, c) 88 | return band(a - b, 0xFF), (a - b) < 0 89 | end 90 | local function subcdb(a, b, c) 91 | b = mdc(b, c) 92 | local b1 = (a % 16) + (b % 16) 93 | return band(a - b, 0xFF), b1 > 0xF, (a - b) < 0 94 | end 95 | local function applyb(s, r, a, c) 96 | s.ac = a 97 | s.cy = c 98 | return r 99 | end 100 | 101 | local function s_push16(s, res) 102 | local high, low = rshift(band(res, 0xFF00), 8), band(res, 0xFF) 103 | s.SP = band(s.SP - 1, 0xFFFF) 104 | s:setb(s.SP, high) 105 | s.SP = band(s.SP - 1, 0xFFFF) 106 | s:setb(s.SP, low) 107 | end 108 | 109 | local function s_pop16(s) 110 | local low = s:getb(s.SP) 111 | s.SP = band(s.SP + 1, 0xFFFF) 112 | local high = s:getb(s.SP) 113 | s.SP = band(s.SP + 1, 0xFFFF) 114 | return pair(high, low) 115 | end 116 | 117 | local function s_push8(s, res) 118 | s.SP = band(s.SP - 1, 0xFFFF) 119 | s:setb(s.SP, res) 120 | end 121 | 122 | local function s_pop8(s) 123 | local res = s:getb(s.SP) 124 | s.SP = band(s.SP + 1, 0xFFFF) 125 | return res 126 | end 127 | 128 | local function s_call(s, t) 129 | s_push16(s, band(s.PC, 0xFFFF)) 130 | s.PC = t 131 | end 132 | 133 | -- PSW Accumulator is in the 'big' byte. 134 | -- (First when pushing, last when popping) 135 | -- 0739: POP BC 136 | -- 073A: MOV C, B 137 | -- 073B: ORA C 138 | 139 | local function encode_psw(s) 140 | -- SZ0A0P1C 141 | local n = 2 142 | if s.cy then n = n + 1 end 143 | if s.p then n = n + 4 end 144 | if s.ac then n = n + 16 end 145 | if s.z then n = n + 64 end 146 | if s.s then n = n + 128 end 147 | return n 148 | end 149 | 150 | local function decode_psw(s, n) 151 | s.cy = band(n, 1) ~= 0 152 | s.p = band(n, 4) ~= 0 153 | s.ac = band(n, 16) ~= 0 154 | s.z = band(n, 64) ~= 0 155 | s.s = band(n, 128) ~= 0 156 | end 157 | 158 | local function b_lsft(a) 159 | local n = band(a * 2, 0xFF) 160 | return n, band(a, 0x80) ~= 0 161 | end 162 | 163 | local function b_rsft(a) 164 | local n = band(math.floor(a / 2), 0x7F) 165 | return n, band(a, 1) ~= 0 166 | end 167 | 168 | -- OPS 169 | local ops = { 170 | [0x00] = function(s) end, -- NOP 171 | [0x01] = function(s, b2, b3) s.B = b3 s.C = b2 end, -- LXI B,D16 172 | [0x02] = function(s) s:setb(pair(s.B, s.C), s.A) end, -- STAX B 173 | [0x03] = function(s) local t = a8(s.C + 1) if t == 0 then s.B = a8(s.B + 1) end s.C = t end, -- INX B 174 | [0x04] = function(s) s.B = flaghandle(s, s.B + 1) end, -- INR B 175 | [0x05] = function(s) s.B = flaghandle(s, s.B - 1) end, -- DCR B 176 | [0x06] = function(s, b) s.B = b end, -- MVI B, D8 177 | [0x07] = function(s) s.A, s.cy = b_lsft(s.A) if s.cy then s.A = bor(s.A, 1) end end, -- RLC 178 | [0x08] = function(s) end, -- NOP 179 | [0x09] = function(s) spair(s, 'H', 'L', pair(s.H, s.L) + pair(s.B, s.C)) end, -- DAD B 180 | [0x0a] = function(s) s.A = s:getb(pair(s.B, s.C)) end, -- LDAX B 181 | [0x0b] = function(s) local t = a8(s.C - 1) if t == 0xFF then s.B = a8(s.B - 1) end s.C = t end, -- DCX B 182 | [0x0c] = function(s) s.C = flaghandle(s, s.C + 1) end, -- INR C 183 | [0x0d] = function(s) s.C = flaghandle(s, s.C - 1) end, -- DCR C 184 | [0x0e] = function(s, b) s.C = b end, -- MVI C,D8 185 | [0x0f] = function(s) s.A, s.cy = b_rsft(s.A) if s.cy then s.A = bor(s.A, 128) end end, -- RRC 186 | [0x10] = function(s) end, -- NOP 187 | [0x11] = function(s, b2, b3) s.D = b3 s.E = b2 end, -- LXI D,D16 188 | [0x12] = function(s) s:setb(pair(s.D, s.E), s.A) end, -- STAX D 189 | [0x13] = function(s) local t = a8(s.E + 1) if t == 0 then s.D = a8(s.D + 1) end s.E = t end, -- INX D 190 | [0x14] = function(s) s.D = flaghandle(s, s.D + 1) end, -- INR D 191 | [0x15] = function(s) s.D = flaghandle(s, s.D - 1) end, -- DCR D 192 | [0x16] = function(s, b) s.D = b end, -- MVI D, D8 193 | [0x17] = function(s) local na, nc = b_lsft(s.A) if s.cy then s.A = bor(na, 1) else s.A = na end s.cy = nc end, -- RAL 194 | [0x18] = function(s) end, -- NOP 195 | [0x19] = function(s) spair(s, 'H', 'L', pair(s.H, s.L) + pair(s.D, s.E)) end, -- DAD D 196 | [0x1a] = function(s) s.A = s:getb(pair(s.D, s.E)) end, -- LDAX D 197 | [0x1b] = function(s) local t = a8(s.E - 1) if t == 0xFF then s.D = a8(s.D - 1) end s.E = t end, -- DCX D 198 | [0x1c] = function(s) s.E = flaghandle(s, s.E + 1) end, -- INR E 199 | [0x1d] = function(s) s.E = flaghandle(s, s.E - 1) end, -- DCR E 200 | [0x1e] = function(s, b) s.E = b end, -- MVI E,D8 201 | [0x1f] = function(s) local na, nc = b_rsft(s.A) if s.cy then s.A = bor(na, 128) else s.A = na end s.cy = nc end, -- RAR 202 | [0x20] = function(s) end, -- NOP 203 | [0x21] = function(s, b2, b3) s.H = b3 s.L = b2 end, -- LXI H,D16 204 | [0x22] = function(s, b2, b3) local addr = pair(b3, b2) s:setb(addr, s.L) s:setb(band(addr + 1, 0xFFFF), s.H) end, -- SHLD adr 205 | [0x23] = function(s) local t = a8(s.L + 1) if t == 0 then s.H = a8(s.H + 1) end s.L = t end, -- INX H 206 | [0x24] = function(s) s.H = flaghandle(s, s.H + 1) end, -- INR H 207 | [0x25] = function(s) s.H = flaghandle(s, s.H - 1) end, -- DCR H 208 | [0x26] = function(s, b) s.H = b end, -- MVI H,D8 209 | [0x27] = function(s) if band(s.A, 0x0F) > 9 or s.ac then s.A, s.ac = addcda(s.A, 6) else s.ac = false end if band(s.A, 0xF0) > 0x90 or s.cy then local na, ncy = addcdn(s.A, 0x60) s.A = na s.cy = s.cy or ncy end s.A = flaghandle(s, s.A) end, -- DAA 210 | [0x28] = function(s) end, -- NOP 211 | [0x29] = function(s) spair(s, 'H', 'L', pair(s.H, s.L) + pair(s.H, s.L)) end, -- DAD H 212 | [0x2a] = function(s, b2, b3) local addr = pair(b3, b2) s.L = s:getb(addr) s.H = s:getb(band(addr + 1, 0xFFFF)) end, -- LHLD adr 213 | [0x2b] = function(s) local t = a8(s.L - 1) if t == 0xFF then s.H = a8(s.H - 1) end s.L = t end, -- DCX H 214 | [0x2c] = function(s) s.L = flaghandle(s, s.L + 1) end, -- INR L 215 | [0x2d] = function(s) s.L = flaghandle(s, s.L - 1) end, -- DCR L 216 | [0x2e] = function(s, b) s.L = b end, -- MVI L, D8 217 | [0x2f] = function(s) s.A = bxor(s.A, 0xFF) end, -- CMA 218 | [0x30] = function(s) end, -- NOP 219 | [0x31] = function(s, b2, b3) s.SP = pair(b3, b2) end, -- LXI SP, D16 220 | [0x32] = function(s, b2, b3) local addr = pair(b3, b2) s:setb(addr, s.A) end, -- STA adr 221 | [0x33] = function(s) local t = a8(s.SP + 1) if t == 0 then s.SP = a8(s.SP + 1) end s.SP = t end, -- INX SP 222 | [0x34] = function(s) local loc = pair(s.H, s.L) s:setb(loc, flaghandle(s, s:getb(loc) + 1)) end, -- INR M 223 | [0x35] = function(s) local loc = pair(s.H, s.L) s:setb(loc, flaghandle(s, s:getb(loc) - 1)) end, -- DCR M 224 | [0x36] = function(s, b) s:setb(pair(s.H, s.L), b) end, -- MVI M,D8 225 | [0x37] = function(s) s.cy = true end, -- STC 226 | [0x38] = function(s) end, -- NOP 227 | [0x39] = function(s) spair(s, 'H', 'L', pair(s.H, s.L) + s.SP) end, -- DAD SP 228 | [0x3a] = function(s, b2, b3) local addr = pair(b3, b2) s.A = s:getb(addr) end, -- LDA adr 229 | [0x3b] = function(s) local t = a8(s.SP - 1) if t == 0xFF then s.SP = a8(s.SP - 1) end s.SP = t end, -- DCX SP 230 | [0x3c] = function(s) s.A = flaghandle(s, s.A + 1) end, -- INR A 231 | [0x3d] = function(s) s.A = flaghandle(s, s.A - 1) end, -- DCR A 232 | [0x3e] = function(s, b) s.A = b end, -- MVI A,D8 233 | [0x3f] = function(s) s.cy = not s.cy end, -- CMC 234 | [0x40] = function(s) s.B = s.B end, -- MOV B,B 235 | [0x41] = function(s) s.B = s.C end, -- MOV B,C 236 | [0x42] = function(s) s.B = s.D end, -- MOV B,D 237 | [0x43] = function(s) s.B = s.E end, -- MOV B,E 238 | [0x44] = function(s) s.B = s.H end, -- MOV B,H 239 | [0x45] = function(s) s.B = s.L end, -- MOV B,L 240 | [0x46] = function(s) s.B = s:getb(pair(s.H, s.L)) end, -- MOV B,M 241 | [0x47] = function(s) s.B = s.A end, -- MOV B,A 242 | [0x48] = function(s) s.C = s.B end, -- MOV C,B 243 | [0x49] = function(s) s.C = s.C end, -- MOV C,C 244 | [0x4a] = function(s) s.C = s.D end, -- MOV C,D 245 | [0x4b] = function(s) s.C = s.E end, -- MOV C,E 246 | [0x4c] = function(s) s.C = s.H end, -- MOV C,H 247 | [0x4d] = function(s) s.C = s.L end, -- MOV C,L 248 | [0x4e] = function(s) s.C = s:getb(pair(s.H, s.L)) end, -- MOV C,M 249 | [0x4f] = function(s) s.C = s.A end, -- MOV C,A 250 | [0x50] = function(s) s.D = s.B end, -- MOV D,B 251 | [0x51] = function(s) s.D = s.C end, -- MOV D,C 252 | [0x52] = function(s) s.D = s.D end, -- MOV D,D 253 | [0x53] = function(s) s.D = s.E end, -- MOV D,E 254 | [0x54] = function(s) s.D = s.H end, -- MOV D,H 255 | [0x55] = function(s) s.D = s.L end, -- MOV D,L 256 | [0x56] = function(s) s.D = s:getb(pair(s.H, s.L)) end, -- MOV D,M 257 | [0x57] = function(s) s.D = s.A end, -- MOV D,A 258 | [0x58] = function(s) s.E = s.B end, -- MOV E,B 259 | [0x59] = function(s) s.E = s.C end, -- MOV E,C 260 | [0x5a] = function(s) s.E = s.D end, -- MOV E,D 261 | [0x5b] = function(s) s.E = s.E end, -- MOV E,E 262 | [0x5c] = function(s) s.E = s.H end, -- MOV E,H 263 | [0x5d] = function(s) s.E = s.L end, -- MOV E,L 264 | [0x5e] = function(s) s.E = s:getb(pair(s.H, s.L)) end, -- MOV E,M 265 | [0x5f] = function(s) s.E = s.A end, -- MOV E,A 266 | [0x60] = function(s) s.H = s.B end, -- MOV H,B 267 | [0x61] = function(s) s.H = s.C end, -- MOV H,C 268 | [0x62] = function(s) s.H = s.D end, -- MOV H,D 269 | [0x63] = function(s) s.H = s.E end, -- MOV H,E 270 | [0x64] = function(s) s.H = s.H end, -- MOV H,H 271 | [0x65] = function(s) s.H = s.L end, -- MOV H,L 272 | [0x66] = function(s) s.H = s:getb(pair(s.H, s.L)) end, -- MOV H,M 273 | [0x67] = function(s) s.H = s.A end, -- MOV H,A 274 | [0x68] = function(s) s.L = s.B end, -- MOV L,B 275 | [0x69] = function(s) s.L = s.C end, -- MOV L,C 276 | [0x6a] = function(s) s.L = s.D end, -- MOV L,D 277 | [0x6b] = function(s) s.L = s.E end, -- MOV L,E 278 | [0x6c] = function(s) s.L = s.H end, -- MOV L,H 279 | [0x6d] = function(s) s.L = s.L end, -- MOV L,L 280 | [0x6e] = function(s) s.L = s:getb(pair(s.H, s.L)) end, -- MOV L,M 281 | [0x6f] = function(s) s.L = s.A end, -- MOV L,A 282 | [0x70] = function(s) s:setb(pair(s.H, s.L), s.B) end, -- MOV M,B 283 | [0x71] = function(s) s:setb(pair(s.H, s.L), s.C) end, -- MOV M,C 284 | [0x72] = function(s) s:setb(pair(s.H, s.L), s.D) end, -- MOV M,D 285 | [0x73] = function(s) s:setb(pair(s.H, s.L), s.E) end, -- MOV M,E 286 | [0x74] = function(s) s:setb(pair(s.H, s.L), s.H) end, -- MOV M,H 287 | [0x75] = function(s) s:setb(pair(s.H, s.L), s.L) end, -- MOV M,L 288 | [0x76] = function(s) s.halted = true end, -- HLT 289 | [0x77] = function(s) s:setb(pair(s.H, s.L), s.A) end, -- MOV M,A 290 | [0x78] = function(s) s.A = s.B end, -- MOV A,B 291 | [0x79] = function(s) s.A = s.C end, -- MOV A,C 292 | [0x7a] = function(s) s.A = s.D end, -- MOV A,D 293 | [0x7b] = function(s) s.A = s.E end, -- MOV A,E 294 | [0x7c] = function(s) s.A = s.H end, -- MOV A,H 295 | [0x7d] = function(s) s.A = s.L end, -- MOV A,L 296 | [0x7e] = function(s) s.A = s:getb(pair(s.H, s.L)) end, -- MOV A,M 297 | [0x7f] = function(s) s.A = s.A end, -- MOV A,A 298 | [0x80] = function(s) s.A = flaghandle(s, applyb(s, addcdb(s.A, s.B))) end, -- ADD B 299 | [0x81] = function(s) s.A = flaghandle(s, applyb(s, addcdb(s.A, s.C))) end, -- ADD C 300 | [0x82] = function(s) s.A = flaghandle(s, applyb(s, addcdb(s.A, s.D))) end, -- ADD D 301 | [0x83] = function(s) s.A = flaghandle(s, applyb(s, addcdb(s.A, s.E))) end, -- ADD E 302 | [0x84] = function(s) s.A = flaghandle(s, applyb(s, addcdb(s.A, s.H))) end, -- ADD H 303 | [0x85] = function(s) s.A = flaghandle(s, applyb(s, addcdb(s.A, s.L))) end, -- ADD L 304 | [0x86] = function(s) s.A = flaghandle(s, applyb(s, addcdb(s.A, s:getb(pair(s.H, s.L))))) end, -- ADD M 305 | [0x87] = function(s) s.A = flaghandle(s, applyb(s, addcdb(s.A, s.A))) end, -- ADD A 306 | [0x88] = function(s) s.A = flaghandle(s, applyb(s, addcdb(s.A, s.B, s.cy))) end, -- ADC B 307 | [0x89] = function(s) s.A = flaghandle(s, applyb(s, addcdb(s.A, s.C, s.cy))) end, -- ADC C 308 | [0x8a] = function(s) s.A = flaghandle(s, applyb(s, addcdb(s.A, s.D, s.cy))) end, -- ADC D 309 | [0x8b] = function(s) s.A = flaghandle(s, applyb(s, addcdb(s.A, s.E, s.cy))) end, -- ADC E 310 | [0x8c] = function(s) s.A = flaghandle(s, applyb(s, addcdb(s.A, s.H, s.cy))) end, -- ADC H 311 | [0x8d] = function(s) s.A = flaghandle(s, applyb(s, addcdb(s.A, s.L, s.cy))) end, -- ADC L 312 | [0x8e] = function(s) s.A = flaghandle(s, applyb(s, addcdb(s.A, s:getb(pair(s.H, s.L)), s.cy))) end, -- ADC M 313 | [0x8f] = function(s) s.A = flaghandle(s, applyb(s, addcdb(s.A, s.A, s.cy))) end, -- ADC A 314 | [0x90] = function(s) s.A = flaghandle(s, applyb(s, subcdb(s.A, s.B))) end, -- SUB B 315 | [0x91] = function(s) s.A = flaghandle(s, applyb(s, subcdb(s.A, s.C))) end, -- SUB C 316 | [0x92] = function(s) s.A = flaghandle(s, applyb(s, subcdb(s.A, s.D))) end, -- SUB D 317 | [0x93] = function(s) s.A = flaghandle(s, applyb(s, subcdb(s.A, s.E))) end, -- SUB E 318 | [0x94] = function(s) s.A = flaghandle(s, applyb(s, subcdb(s.A, s.H))) end, -- SUB H 319 | [0x95] = function(s) s.A = flaghandle(s, applyb(s, subcdb(s.A, s.L))) end, -- SUB L 320 | [0x96] = function(s) s.A = flaghandle(s, applyb(s, subcdb(s.A, s:getb(pair(s.H, s.L))))) end, -- SUB M 321 | [0x97] = function(s) s.A = flaghandle(s, applyb(s, subcdb(s.A, s.A))) end, -- SUB A 322 | [0x98] = function(s) s.A = flaghandle(s, applyb(s, subcdb(s.A, s.B, s.cy))) end, -- SBB B 323 | [0x99] = function(s) s.A = flaghandle(s, applyb(s, subcdb(s.A, s.C, s.cy))) end, -- SBB C 324 | [0x9a] = function(s) s.A = flaghandle(s, applyb(s, subcdb(s.A, s.D, s.cy))) end, -- SBB D 325 | [0x9b] = function(s) s.A = flaghandle(s, applyb(s, subcdb(s.A, s.E, s.cy))) end, -- SBB E 326 | [0x9c] = function(s) s.A = flaghandle(s, applyb(s, subcdb(s.A, s.H, s.cy))) end, -- SBB H 327 | [0x9d] = function(s) s.A = flaghandle(s, applyb(s, subcdb(s.A, s.L, s.cy))) end, -- SBB L 328 | [0x9e] = function(s) s.A = flaghandle(s, applyb(s, subcdb(s.A, s:getb(pair(s.H, s.L)), s.cy))) end, -- SBB M 329 | [0x9f] = function(s) s.A = flaghandle(s, applyb(s, subcdb(s.A, s.A, s.cy))) end, -- SBB A 330 | [0xa0] = function(s) s.A = flaghandle(s, band(s.A, s.B)) s.cy = false end, -- ANA B 331 | [0xa1] = function(s) s.A = flaghandle(s, band(s.A, s.C)) s.cy = false end, -- ANA C 332 | [0xa2] = function(s) s.A = flaghandle(s, band(s.A, s.D)) s.cy = false end, -- ANA D 333 | [0xa3] = function(s) s.A = flaghandle(s, band(s.A, s.E)) s.cy = false end, -- ANA E 334 | [0xa4] = function(s) s.A = flaghandle(s, band(s.A, s.H)) s.cy = false end, -- ANA H 335 | [0xa5] = function(s) s.A = flaghandle(s, band(s.A, s.L)) s.cy = false end, -- ANA L 336 | [0xa6] = function(s) s.A = flaghandle(s, band(s.A, s:getb(pair(s.H, s.L)))) s.cy = false end, -- ANA M 337 | [0xa7] = function(s) s.A = flaghandle(s, band(s.A, s.A)) s.cy = false end, -- ANA A 338 | [0xa8] = function(s) s.A = flaghandle(s, bxor(s.A, s.B)) s.cy = false end, -- XRA B 339 | [0xa9] = function(s) s.A = flaghandle(s, bxor(s.A, s.C)) s.cy = false end, -- XRA C 340 | [0xaa] = function(s) s.A = flaghandle(s, bxor(s.A, s.D)) s.cy = false end, -- XRA D 341 | [0xab] = function(s) s.A = flaghandle(s, bxor(s.A, s.E)) s.cy = false end, -- XRA E 342 | [0xac] = function(s) s.A = flaghandle(s, bxor(s.A, s.H)) s.cy = false end, -- XRA H 343 | [0xad] = function(s) s.A = flaghandle(s, bxor(s.A, s.L)) s.cy = false end, -- XRA L 344 | [0xae] = function(s) s.A = flaghandle(s, bxor(s.A, s:getb(pair(s.H, s.L)))) s.cy = false end, -- XRA M 345 | [0xaf] = function(s) s.A = flaghandle(s, bxor(s.A, s.A)) s.cy = false end, -- XRA A 346 | [0xb0] = function(s) s.A = flaghandle(s, bor(s.A, s.B)) s.cy = false end, -- ORA B 347 | [0xb1] = function(s) s.A = flaghandle(s, bor(s.A, s.C)) s.cy = false end, -- ORA C 348 | [0xb2] = function(s) s.A = flaghandle(s, bor(s.A, s.D)) s.cy = false end, -- ORA D 349 | [0xb3] = function(s) s.A = flaghandle(s, bor(s.A, s.E)) s.cy = false end, -- ORA E 350 | [0xb4] = function(s) s.A = flaghandle(s, bor(s.A, s.H)) s.cy = false end, -- ORA H 351 | [0xb5] = function(s) s.A = flaghandle(s, bor(s.A, s.L)) s.cy = false end, -- ORA L 352 | [0xb6] = function(s) s.A = flaghandle(s, bor(s.A, s:getb(pair(s.H, s.L)))) s.cy = false end, -- ORA M 353 | [0xb7] = function(s) s.A = flaghandle(s, bor(s.A, s.A)) s.cy = false end, -- ORA A 354 | [0xb8] = function(s) flaghandle(s, applyb(s, subcdb(s.A, s.B))) end, -- CMP B 355 | [0xb9] = function(s) flaghandle(s, applyb(s, subcdb(s.A, s.C))) end, -- CMP C 356 | [0xba] = function(s) flaghandle(s, applyb(s, subcdb(s.A, s.D))) end, -- CMP D 357 | [0xbb] = function(s) flaghandle(s, applyb(s, subcdb(s.A, s.E))) end, -- CMP E 358 | [0xbc] = function(s) flaghandle(s, applyb(s, subcdb(s.A, s.H))) end, -- CMP H 359 | [0xbd] = function(s) flaghandle(s, applyb(s, subcdb(s.A, s.L))) end, -- CMP L 360 | [0xbe] = function(s) flaghandle(s, applyb(s, subcdb(s.A, s:getb(pair(s.H, s.L))))) end, -- CMP M 361 | [0xbf] = function(s) flaghandle(s, applyb(s, subcdb(s.A, s.A))) end, -- CMP A 362 | [0xc0] = function(s) if s.z == false then s.PC = s_pop16(s) return true end end, -- RET !FZ 363 | [0xc1] = function(s) s.C = s_pop8(s) s.B = s_pop8(s) end, -- POP B 364 | [0xc2] = function(s, b2, b3) local addr = pair(b3, b2) if s.z == false then s.PC = addr return true end end, -- JMP !FZ adr 365 | [0xc3] = function(s, b2, b3) local addr = pair(b3, b2) s.PC = addr return true end, -- JMP adr 366 | [0xc4] = function(s, b2, b3) local addr = pair(b3, b2) if s.z == false then s_call(s, addr) return true end end, -- CALL !FZ adr 367 | [0xc5] = function(s) s_push8(s, s.B) s_push8(s, s.C) end, -- PUSH B 368 | [0xc6] = function(s, b) s.A = flaghandle(s, applyb(s, addcdb(s.A, b))) end, -- ADI D8 369 | [0xc7] = function(s) s_call(s, 0x00) return true end, -- RST 0 370 | [0xc8] = function(s) if s.z == true then s.PC = s_pop16(s) return true end end, -- RET FZ 371 | [0xc9] = function(s) s.PC = s_pop16(s) return true end, -- RET 372 | [0xca] = function(s, b2, b3) local addr = pair(b3, b2) if s.z == true then s.PC = addr return true end end, -- JMP FZ adr 373 | [0xcb] = function(s, b2, b3) local addr = pair(b3, b2) s.PC = addr return true end, -- JMP adr 374 | [0xcc] = function(s, b2, b3) local addr = pair(b3, b2) if s.z == true then s_call(s, addr) return true end end, -- CALL FZ adr 375 | [0xcd] = function(s, b2, b3) local addr = pair(b3, b2) s_call(s, addr) return true end, -- CALL adr 376 | [0xce] = function(s, b) s.A = flaghandle(s, applyb(s, addcdb(s.A, b, s.cy))) end, -- ACI D8 377 | [0xcf] = function(s) s_call(s, 0x08) return true end, -- RST 1 378 | [0xd0] = function(s) if s.cy == false then s.PC = s_pop16(s) return true end end, -- RET !FC 379 | [0xd1] = function(s) s.E = s_pop8(s) s.D = s_pop8(s) end, -- POP D 380 | [0xd2] = function(s, b2, b3) local addr = pair(b3, b2) if s.cy == false then s.PC = addr return true end end, -- JMP !FC adr 381 | [0xd3] = function(s, b) s:iosb(b, s.A) end, -- OUT D8 382 | [0xd4] = function(s, b2, b3) local addr = pair(b3, b2) if s.cy == false then s_call(s, addr) return true end end, -- CALL !FC adr 383 | [0xd5] = function(s) s_push8(s, s.D) s_push8(s, s.E) end, -- PUSH D 384 | [0xd6] = function(s, b) s.A = flaghandle(s, applyb(s, subcdb(s.A, b))) end, -- SUI D8 385 | [0xd7] = function(s) s_call(s, 0x10) return true end, -- RST 2 386 | [0xd8] = function(s) if s.cy == true then s.PC = s_pop16(s) return true end end, -- RET FC 387 | [0xd9] = function(s) s.PC = s_pop16(s) return true end, -- RET 388 | [0xda] = function(s, b2, b3) local addr = pair(b3, b2) if s.cy == true then s.PC = addr return true end end, -- JMP FC adr 389 | [0xdb] = function(s, b) s.A = s:iogb(b) end, -- IN D8 390 | [0xdc] = function(s, b2, b3) local addr = pair(b3, b2) if s.cy == true then s_call(s, addr) return true end end, -- CALL FC adr 391 | [0xdd] = function(s, b2, b3) local addr = pair(b3, b2) s_call(s, addr) return true end, -- CALL adr 392 | [0xde] = function(s, b) s.A = flaghandle(s, applyb(s, subcdb(s.A, b, s.cy))) end, -- SBI D8 393 | [0xdf] = function(s) s_call(s, 0x18) return true end, -- RST 3 394 | [0xe0] = function(s) if s.p == false then s.PC = s_pop16(s) return true end end, -- RET !FPE 395 | [0xe1] = function(s) s.L = s_pop8(s) s.H = s_pop8(s) end, -- POP H 396 | [0xe2] = function(s, b2, b3) local addr = pair(b3, b2) if s.p == false then s.PC = addr return true end end, -- JMP !FPE adr 397 | [0xe3] = function(s) local oh, ol, a2 = s.H, s.L, band(s.SP + 1, 0xFFFF) s.L = s:getb(s.SP) s:setb(s.SP, ol) s.H = s:getb(a2) s:setb(a2, oh) end, -- XTHL 398 | [0xe4] = function(s, b2, b3) local addr = pair(b3, b2) if s.p == false then s_call(s, addr) return true end end, -- CALL !FPE adr 399 | [0xe5] = function(s) s_push8(s, s.H) s_push8(s, s.L) end, -- PUSH H 400 | [0xe6] = function(s, b) s.A = flaghandle(s, band(s.A, b)) s.cy = false end, -- ANI D8 401 | [0xe7] = function(s) s_call(s, 0x20) return true end, -- RST 4 402 | [0xe8] = function(s) if s.p == true then s.PC = s_pop16(s) return true end end, -- RET FPE 403 | [0xe9] = function(s) s.PC = pair(s.H, s.L) return true end, -- PCHL 404 | [0xea] = function(s, b2, b3) local addr = pair(b3, b2) if s.p == true then s.PC = addr return true end end, -- JMP FPE adr 405 | [0xeb] = function(s) local oh, ol = s.H, s.L s.H = s.D s.D = oh s.L = s.E s.E = ol end, -- XCHG 406 | [0xec] = function(s, b2, b3) local addr = pair(b3, b2) if s.p == true then s_call(s, addr) return true end end, -- CALL FPE adr 407 | [0xed] = function(s, b2, b3) local addr = pair(b3, b2) s_call(s, addr) return true end, -- CALL adr 408 | [0xee] = function(s, b) s.A = flaghandle(s, bxor(s.A, b)) s.cy = false end, -- XRI D8 409 | [0xef] = function(s) s_call(s, 0x28) return true end, -- RST 5 410 | [0xf0] = function(s) if s.s == false then s.PC = s_pop16(s) return true end end, -- RET !FS 411 | [0xf1] = function(s) decode_psw(s, s_pop8(s)) s.A = s_pop8(s) end, -- POP PSW 412 | [0xf2] = function(s, b2, b3) local addr = pair(b3, b2) if s.s == false then s.PC = addr return true end end, -- JMP !FS adr 413 | [0xf3] = function(s) s.int_enable = false end, -- DI 414 | [0xf4] = function(s, b2, b3) local addr = pair(b3, b2) if s.s == false then s_call(s, addr) return true end end, -- CALL !FS adr 415 | [0xf5] = function(s) s_push8(s, s.A) s_push8(s, encode_psw(s)) end, -- PUSH PSW 416 | [0xf6] = function(s, b) s.A = flaghandle(s, bor(s.A, b)) s.cy = false end, -- ORI D8 417 | [0xf7] = function(s) s_call(s, 0x30) return true end, -- RST 6 418 | [0xf8] = function(s) if s.s == true then s.PC = s_pop16(s) return true end end, -- RET FS 419 | [0xf9] = function(s) s.SP = pair(s.H, s.L) end, -- SPHL 420 | [0xfa] = function(s, b2, b3) local addr = pair(b3, b2) if s.s == true then s.PC = addr return true end end, -- JMP FS adr 421 | [0xfb] = function(s) s.int_enable = true end, -- EI 422 | [0xfc] = function(s, b2, b3) local addr = pair(b3, b2) if s.s == true then s_call(s, addr) return true end end, -- CALL FS adr 423 | [0xfd] = function(s, b2, b3) local addr = pair(b3, b2) s_call(s, addr) return true end, -- CALL adr 424 | [0xfe] = function(s, b) flaghandle(s, applyb(s, subcdb(s.A, b))) end, -- CPI D8 425 | [0xff] = function(s) s_call(s, 0x38) return true end, -- RST 7 426 | } 427 | 428 | return { 429 | inst_bitops = function(bit32) 430 | band, bor, bxor = bit32.band, bit32.bor, bit32.bxor 431 | rshift, lshift = bit32.rshift, bit32.lshift 432 | end, 433 | ops = ops 434 | } 435 | --------------------------------------------------------------------------------