mirror of
https://github.com/jscrane/r65emu.git
synced 2026-04-26 22:18:15 +00:00
replace SimpleTimer with DeltaQueue (#136)
* replace SimpleTimer with DeltaQueue * pull Arduino timer methods up * update library dependencies
This commit is contained in:
1 parent
a3b1b96e5f
commit
f9e8aaade3
10 files changed
+159
-37
No files matched your search
@@ -42,7 +42,6 @@ Some emulations require a filesystem image containing programs to be uploaded:
|
||||
Libraries
|
||||
---------
|
||||
|
||||
- [SimpleTimer](https://github.com/jscrane/SimpleTimer)
|
||||
- [PS2KeyAdvanced](https://github.com/techpaul/PS2KeyAdvanced), [PS2KeyRaw](https://github.com/techpaul/PS2KeyRaw) and [PS2KeyMap](https://github.com/techpaul/PS2KeyMap)
|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -1,11 +1,11 @@
|
||||
name=r65emu
|
||||
version=1.2.1
|
||||
version=1.5.3
|
||||
author=Stephen Crane <jscrane@gmail.com>
|
||||
maintainer=Stephen Crane <jscrane@gmail.com>
|
||||
sentence=8-bit Microcomputer Emulation Library
|
||||
paragraph=Supports 6502, 8080 and Z80 processors
|
||||
category=Other
|
||||
url=https://github.com/jscrane/r65emu
|
||||
architectures=esp32, esp8266, tivac, rp2040
|
||||
depends=PS2KeyAdvanced, PS2KeyMap, PS2KeyRaw, SimpleTimer
|
||||
architectures=esp32, esp8266, rp2040
|
||||
depends=PS2KeyAdvanced, PS2KeyMap, PS2KeyRaw
|
||||
includes=r65emu.h
|
||||
@@ -1,5 +1,4 @@
|
||||
#include <Arduino.h>
|
||||
#include <SimpleTimer.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
@@ -31,8 +30,6 @@
|
||||
spiram sram(SPIRAM_SIZE);
|
||||
#endif
|
||||
|
||||
static SimpleTimer timers;
|
||||
|
||||
bool Arduino::reset() {
|
||||
|
||||
DBG_INI("machine reset");
|
||||
@@ -85,11 +82,6 @@ bool Arduino::reset() {
|
||||
return success;
|
||||
}
|
||||
|
||||
static class simple_timer_pollable: public Pollable {
|
||||
public:
|
||||
void poll() { timers.run(); }
|
||||
} stp;
|
||||
|
||||
void Arduino::begin() {
|
||||
|
||||
#if DEBUGGING != DEBUG_NONE
|
||||
@@ -125,20 +117,6 @@ void Arduino::begin() {
|
||||
pinMode(SPIRAM_CS, OUTPUT);
|
||||
digitalWrite(SPIRAM_CS, HIGH);
|
||||
#endif
|
||||
|
||||
register_pollable(stp);
|
||||
}
|
||||
|
||||
int Arduino::interval_timer(uint32_t interval, std::function<void(void)> cb) {
|
||||
return timers.setInterval(interval, cb);
|
||||
}
|
||||
|
||||
int Arduino::oneshot_timer(uint32_t interval, std::function<void(void)> cb) {
|
||||
return timers.setTimeout(interval, cb);
|
||||
}
|
||||
|
||||
void Arduino::cancel_timer(int timer) {
|
||||
timers.deleteTimer(timer);
|
||||
}
|
||||
|
||||
uint32_t Arduino::microseconds() { return micros(); }
|
||||
|
||||
@@ -33,9 +33,6 @@ public:
|
||||
_reset_handler = handler;
|
||||
}
|
||||
|
||||
int interval_timer(uint32_t micros, std::function<void(void)> cb) override;
|
||||
int oneshot_timer(uint32_t micros, std::function<void(void)> cb) override;
|
||||
void cancel_timer(int timer) override;
|
||||
uint32_t microseconds() override;
|
||||
void sleep(uint32_t dt) override;
|
||||
void yield() override;
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
#include <stdint.h>
|
||||
#include <functional>
|
||||
|
||||
#include "deltaq.h"
|
||||
|
||||
void DeltaQueue::cancel(int8_t id) {
|
||||
if (id < 0 || id >= 32 || (freeSlots & (1UL << id))) return;
|
||||
|
||||
int8_t prev = -1, curr = head;
|
||||
while (curr != -1 && curr != id) {
|
||||
prev = curr;
|
||||
curr = pool[curr].next;
|
||||
}
|
||||
|
||||
if (curr == id) {
|
||||
if (prev == -1) head = pool[id].next;
|
||||
else pool[prev].next = pool[id].next;
|
||||
|
||||
if (pool[id].next != -1) pool[pool[id].next].delta += pool[id].delta;
|
||||
releaseNode(id);
|
||||
}
|
||||
}
|
||||
|
||||
void DeltaQueue::update(uint32_t currentTime) {
|
||||
uint32_t elapsed = currentTime - lastTime;
|
||||
lastTime = currentTime;
|
||||
|
||||
while (head != -1 && elapsed >= pool[head].delta) {
|
||||
int8_t id = head;
|
||||
elapsed -= pool[id].delta;
|
||||
head = pool[id].next;
|
||||
|
||||
if (pool[id].callback) pool[id].callback();
|
||||
|
||||
// Re-insert the SAME node to keep the ID constant
|
||||
if (pool[id].period > 0)
|
||||
insertNode(id, pool[id].period);
|
||||
else
|
||||
releaseNode(id);
|
||||
}
|
||||
if (head != -1)
|
||||
pool[head].delta -= elapsed;
|
||||
}
|
||||
|
||||
void DeltaQueue::insertNode(int8_t id, uint32_t delay) {
|
||||
int8_t prev = -1, curr = head;
|
||||
uint32_t remaining = delay;
|
||||
|
||||
while (curr != -1 && remaining >= pool[curr].delta) {
|
||||
remaining -= pool[curr].delta;
|
||||
prev = curr;
|
||||
curr = pool[curr].next;
|
||||
}
|
||||
|
||||
pool[id].delta = remaining;
|
||||
pool[id].next = curr;
|
||||
|
||||
if (prev == -1) head = id;
|
||||
else pool[prev].next = id;
|
||||
|
||||
if (curr != -1) pool[curr].delta -= remaining;
|
||||
}
|
||||
|
||||
int8_t DeltaQueue::insert(uint32_t delay, uint32_t period, Callback cb) {
|
||||
int8_t id = allocateNode();
|
||||
if (id == -1) return -1;
|
||||
|
||||
pool[id].period = period;
|
||||
pool[id].callback = cb;
|
||||
|
||||
int8_t prev = -1, curr = head;
|
||||
uint32_t remaining = delay;
|
||||
|
||||
while (curr != -1 && remaining >= pool[curr].delta) {
|
||||
remaining -= pool[curr].delta;
|
||||
prev = curr;
|
||||
curr = pool[curr].next;
|
||||
}
|
||||
|
||||
pool[id].delta = remaining;
|
||||
pool[id].next = curr;
|
||||
|
||||
if (prev == -1) head = id;
|
||||
else pool[prev].next = id;
|
||||
|
||||
if (curr != -1) pool[curr].delta -= remaining;
|
||||
return id;
|
||||
}
|
||||
|
||||
int8_t DeltaQueue::allocateNode() {
|
||||
if (freeSlots == 0) return -1;
|
||||
// __builtin_ctz finds the index of the first '1' bit (O(1) on most CPUs)
|
||||
int8_t id = __builtin_ctz(freeSlots);
|
||||
freeSlots &= ~(1UL << id);
|
||||
return id;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
// this class was written by Gemini under direction
|
||||
class DeltaQueue {
|
||||
public:
|
||||
typedef std::function<void()> Callback;
|
||||
|
||||
DeltaQueue(): head(-1), freeSlots(0xFFFFFFFF), lastTime(0) {}
|
||||
|
||||
int8_t setTimeout(uint32_t delay, Callback cb) { return insert(delay, 0, cb); }
|
||||
|
||||
int8_t setInterval(uint32_t period, Callback cb) { return insert(period, period, cb); }
|
||||
|
||||
void cancel(int8_t id);
|
||||
|
||||
void update(uint32_t currentTime);
|
||||
|
||||
private:
|
||||
struct {
|
||||
uint32_t delta;
|
||||
uint32_t period;
|
||||
Callback callback;
|
||||
int8_t next;
|
||||
} pool[32];
|
||||
|
||||
int8_t head;
|
||||
uint32_t freeSlots; // Bitmask: 1 = free, 0 = used
|
||||
uint32_t lastTime;
|
||||
|
||||
int8_t insert(uint32_t delay, uint32_t period, Callback cb);
|
||||
|
||||
void insertNode(int8_t id, uint32_t delay);
|
||||
|
||||
int8_t allocateNode();
|
||||
|
||||
inline void releaseNode(int8_t id) { freeSlots |= (1UL << id); }
|
||||
};
|
||||
|
||||
@@ -5,9 +5,12 @@
|
||||
#include "machine.h"
|
||||
#include "memory.h"
|
||||
#include "CPU.h"
|
||||
#include "deltaq.h"
|
||||
|
||||
Machine *_machine;
|
||||
|
||||
static DeltaQueue timers;
|
||||
|
||||
Machine::Machine(CPU &cpu): _cpu(cpu) {
|
||||
_cpu_debug = debug_never;
|
||||
_machine = this;
|
||||
@@ -37,6 +40,8 @@ void Machine::run(uint32_t clock_speed_hz) {
|
||||
|
||||
uint32_t start_time = microseconds();
|
||||
|
||||
timers.update(start_time);
|
||||
|
||||
for (uint8_t i = 0; i < num_pollable; i++)
|
||||
devices[i]->poll();
|
||||
|
||||
@@ -69,3 +74,15 @@ void Machine::run(uint32_t clock_speed_hz) {
|
||||
_speed = (_cpu.cycles() - start_cycles) * (1000000 / (microseconds() - start_time));
|
||||
#endif
|
||||
}
|
||||
|
||||
int8_t Machine::interval_timer(uint32_t interval, std::function<void(void)> cb) {
|
||||
return timers.setInterval(interval, cb);
|
||||
}
|
||||
|
||||
int8_t Machine::oneshot_timer(uint32_t interval, std::function<void(void)> cb) {
|
||||
return timers.setTimeout(interval, cb);
|
||||
}
|
||||
|
||||
void Machine::cancel_timer(int8_t timer) {
|
||||
timers.cancel(timer);
|
||||
}
|
||||
+4
-3
@@ -57,9 +57,10 @@ public:
|
||||
void run(uint32_t clock_speed_hz = CLK_MAX);
|
||||
uint32_t current_speed() const { return _speed; }
|
||||
|
||||
virtual int interval_timer(uint32_t micros, std::function<void(void)> cb) =0;
|
||||
virtual int oneshot_timer(uint32_t micros, std::function<void(void)> cb) =0;
|
||||
virtual void cancel_timer(int timer) =0;
|
||||
int8_t interval_timer(uint32_t micros, std::function<void(void)> cb);
|
||||
int8_t oneshot_timer(uint32_t micros, std::function<void(void)> cb);
|
||||
void cancel_timer(int8_t timer);
|
||||
|
||||
virtual uint32_t microseconds() =0;
|
||||
virtual void sleep(uint32_t) =0;
|
||||
virtual void yield() =0;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
CPPFLAGS:= -I.. -DUNDOCUMENTED_OPS -DDEBUGGING=0x40
|
||||
CXXFLAGS:= -g -fno-operator-names -Wall
|
||||
OBJECTS := z80test.o z80.o memory.o machine.o CPU.o
|
||||
OBJECTS := z80test.o z80.o memory.o machine.o deltaq.o
|
||||
|
||||
all: z80test tests.me sum
|
||||
|
||||
|
||||
@@ -17,10 +17,6 @@ class TestMachine: public Machine {
|
||||
public:
|
||||
TestMachine(CPU &c): Machine(c) {}
|
||||
|
||||
int interval_timer(uint32_t micros, std::function<void(void)> cb) override { return -1; }
|
||||
int oneshot_timer(uint32_t micros, std::function<void(void)> cb) override { return -1; }
|
||||
void cancel_timer(int timer) override {}
|
||||
|
||||
uint32_t microseconds() override { return 0; }
|
||||
void sleep(uint32_t) override {}
|
||||
void yield() override {}
|
||||
|
||||
Reference in new issue
Block a user