From b2090aa242e892898f958efeab4894b5287a1fe6 Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara Date: Thu, 12 Aug 2010 12:48:22 +0300 Subject: [PATCH] Add read interface to event stream from Lua This adds jpcrr.events, which provodes functions to read PC event stream from Lua code. --- lua/eventstest.lua | 25 +++++++ org/jpc/emulator/EventRecorder.java | 137 ++++++++++++++++++++++++++++++++++++ org/jpc/luaextensions/Events.java | 100 ++++++++++++++++++++++++++ 3 files changed, 262 insertions(+) create mode 100644 lua/eventstest.lua create mode 100644 org/jpc/luaextensions/Events.java diff --git a/lua/eventstest.lua b/lua/eventstest.lua new file mode 100644 index 0000000..a3fb62b --- /dev/null +++ b/lua/eventstest.lua @@ -0,0 +1,25 @@ +count = jpcrr.events.count(); +current = jpcrr.events.current_sequence(); +print("Current event sequence: " .. current); + +print("-------- Start of event dump --------"); +for i = 0,count - 1 do + if current == i then + print("------------ You are here -----------"); + end + s = "Event #" .. i .. ": "; + ev = jpcrr.events.by_sequence(i); + if #ev == 0 then + s = s .. "(no such event)"; + else + s = s .. ev[2] .. "@" .. tostring(ev[1]); + for j = 3,#ev do + s = s .. " " .. ev[j]; + end + end + print(s); +end +if current < 0 then + print("------------ You are here -----------"); +end +print("--------- End of event dump ---------"); diff --git a/org/jpc/emulator/EventRecorder.java b/org/jpc/emulator/EventRecorder.java index b60dfc9..8a6e548 100644 --- a/org/jpc/emulator/EventRecorder.java +++ b/org/jpc/emulator/EventRecorder.java @@ -47,9 +47,16 @@ public class EventRecorder implements TimerResponsive public static final int EVENT_STATE_EFFECT = 2; public static final int EVENT_EXECUTE = 3; + public class ReturnEvent + { + public long timestamp; + public String[] eventData; + } + public class Event { public long timestamp; //Event timestamp (low bound) + public long sequenceNumber; //Sequence number. public int magic; //Magic type. public Class clazz; //Dispatch to where. public String[] args; //Arguments to dispatch. @@ -72,6 +79,7 @@ public class EventRecorder implements TimerResponsive private Event first; private Event current; private Event last; + private Event cache; private Event firstUndispatched; private Event lastUndispatched; private PC pc; @@ -224,6 +232,8 @@ public class EventRecorder implements TimerResponsive //Because of constraints to time, the event must go last. if(scan != null) { + //Sequence number for first event is 0 and then increments by 1 for each event. + scan.sequenceNumber = ((last != null) ? (last.sequenceNumber + 1) : 0); scan.next = null; scan.prev = last; if(last != null) @@ -267,6 +277,8 @@ public class EventRecorder implements TimerResponsive { if(current != null) { dirtyFlag = true; + if(current.sequenceNumber <= cache.sequenceNumber) + cache = null; //Flush cache as event got truncated out. last = current.prev; current = null; if(last != null) @@ -317,6 +329,7 @@ public class EventRecorder implements TimerResponsive first = null; current = null; last = null; + cache = null; firstUndispatched = null; lastUndispatched = null; directMode = true; @@ -407,6 +420,8 @@ public class EventRecorder implements TimerResponsive if(ev.magic != EVENT_MAGIC_NONE) { ev.prev = last; + //Sequence number for first event is 0 and then increments by 1 for each event. + ev.sequenceNumber = ((last != null) ? (last.sequenceNumber + 1) : 0); if(last == null) first = ev; else @@ -421,6 +436,14 @@ public class EventRecorder implements TimerResponsive directMode = true; } + private void iterateIncrementSequence(Event from) + { + while(from != null) { + from.sequenceNumber++; + from = from.next; + } + } + public void markSave(String id, long rerecords) throws IOException { if(!isDirty()) @@ -436,12 +459,17 @@ public class EventRecorder implements TimerResponsive ev.args = new String[]{id, (new Long(rerecords)).toString()}; ev.next = current; if(current != null) { + //Give it current's sequence number and increment the rest of sequence numbers. + ev.sequenceNumber = current.sequenceNumber; + iterateIncrementSequence(current); ev.prev = current.prev; if(ev.prev != null) ev.prev.next = ev; current.prev = ev; ev.next = current; } else { + //Sequence number for first event is 0 and then increments by 1 for each event. + ev.sequenceNumber = ((last != null) ? (last.sequenceNumber + 1) : 0); ev.prev = last; if(last != null) last.next = ev; @@ -558,6 +586,115 @@ public class EventRecorder implements TimerResponsive return (current == null); } + public synchronized long getEventCount() + { + return (last != null) ? (last.sequenceNumber + 1) : 0; + } + + public synchronized long getEventCurrentSequence() + { + return (current != null) ? current.sequenceNumber : -1; + } + + private String getReturnClass(Event ev) + { + if(ev.magic == EVENT_MAGIC_CLASS) + return ev.clazz.getName(); + else if(ev.magic == EVENT_MAGIC_SAVESTATE) + return "SAVESTATE"; + else + return ""; + } + + private ReturnEvent convertToReturn(Event ev) + { + ReturnEvent evr = new ReturnEvent(); + evr.timestamp = ev.timestamp; + if(ev.args == null) + evr.eventData = new String[1]; + else { + evr.eventData = new String[1 + ev.args.length]; + System.arraycopy(ev.args, 0, evr.eventData, 1, ev.args.length); + } + evr.eventData[0] = getReturnClass(ev); + return evr; + } + + private int sMinFour(long a, long b, long c, long d) + { + //Compute maximum, ignoring -'s. + long max = a; + max = (b > max) ? b : max; + max = (c > max) ? c : max; + max = (d > max) ? d : max; + if(max < 0) + return -1; + + //Now use max to get rid of -'s. + a = (a >= 0) ? a : (max + 1); + b = (b >= 0) ? b : (max + 1); + c = (c >= 0) ? c : (max + 1); + d = (d >= 0) ? d : (max + 1); + + long min = a; + int mpos = 0; + if(b < min) { + min = b; + mpos = 1; + } + if(c < min) { + min = c; + mpos = 2; + } + if(d < min) { + min = d; + mpos = 2; + } + return mpos; + } + + public synchronized ReturnEvent getEventBySequence(long sequence) + { + if(sequence < 0 || last == null || sequence > last.sequenceNumber) + return null; + + long distFirst = -1; + long distLast = -1; + long distCurrent = -1; + long distCache = -1; + + distFirst = Math.abs(first.sequenceNumber - sequence); + distLast = Math.abs(first.sequenceNumber - sequence); + if(current != null) + distCurrent = Math.abs(current.sequenceNumber - sequence); + if(cache != null) + distCache = Math.abs(cache.sequenceNumber - sequence); + + //Find the nearest entrypoint. + switch(sMinFour(distFirst, distLast, distCurrent, distCache)) { + case 0: + cache = first; + break; + case 1: + cache = last; + break; + case 2: + cache = current; + break; + case 3: + //Cache = cache; + break; + default: + return null; //Can't happen. + } + + while(sequence < cache.sequenceNumber) + cache = cache.prev; + while(sequence > cache.sequenceNumber) + cache = cache.next; + return convertToReturn(cache); + } + public void callback() { handleUndispatchedEvents(); diff --git a/org/jpc/luaextensions/Events.java b/org/jpc/luaextensions/Events.java new file mode 100644 index 0000000..6cec837 --- /dev/null +++ b/org/jpc/luaextensions/Events.java @@ -0,0 +1,100 @@ +/* + JPC-RR: A x86 PC Hardware Emulator + Release 1 + + Copyright (C) 2007-2009 Isis Innovation Limited + Copyright (C) 2009-2010 H. Ilari Liusvaara + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 as published by + the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + Based on JPC x86 PC Hardware emulator, + A project from the Physics Dept, The University of Oxford + + Details about original JPC can be found at: + + www-jpc.physics.ox.ac.uk + +*/ + +package org.jpc.luaextensions; + +import mnj.lua.*; + +import org.jpc.emulator.Clock; +import org.jpc.emulator.PC; +import org.jpc.emulator.DisplayController; +import org.jpc.emulator.EventRecorder; +import org.jpc.plugins.LuaPlugin; + +public class Events extends LuaPlugin.LuaResource +{ + public void destroy() + { + } + + private Events(LuaPlugin plugin) + { + super(plugin); + } + + private static EventRecorder getRecorder(Lua l, LuaPlugin plugin) + { + PC.ResetButton brb = ((PC.ResetButton)plugin.getComponent(PC.ResetButton.class)); + if(brb == null) { + l.pushNil(); + return null; + } + return brb.getRecorder(); + } + + public static int luaCB_count(Lua l, LuaPlugin plugin) + { + EventRecorder rec = getRecorder(l, plugin); + if(rec != null) { + long count = rec.getEventCount(); + l.pushNumber((double)count); + } + return 1; + } + + public static int luaCB_current_sequence(Lua l, LuaPlugin plugin) + { + EventRecorder rec = getRecorder(l, plugin); + if(rec != null) { + long count = rec.getEventCurrentSequence(); + l.pushNumber((double)count); + } + return 1; + } + + public static int luaCB_by_sequence(Lua l, LuaPlugin plugin) + { + EventRecorder rec = getRecorder(l, plugin); + if(rec != null) { + l.pushNil(); + long num = (long)l.checkNumber(1); + LuaTable ret = l.newTable(); + EventRecorder.ReturnEvent evr = rec.getEventBySequence(num); + if(evr != null) { + l.setTable(ret, new Double(1), new Double(evr.timestamp)); + int j = 2; + if(evr.eventData != null) + for(String p : evr.eventData) + l.setTable(ret, new Double(j++), p); + } + l.push(ret); + } + return 1; + } +} -- 2.11.4.GIT