From 4109905bc6ef26d8f0ea3e06badafe6ea0147eb4 Mon Sep 17 00:00:00 2001 From: root <182859762@qq.com> Date: Sat, 11 May 2024 10:33:53 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E7=94=A8=E9=A2=84=E6=8E=92?= =?UTF-8?q?=E5=BA=8F=E6=95=B0=E7=BB=84=E6=9D=A5=E6=9B=BF=E6=8D=A2=E5=B0=8F?= =?UTF-8?q?=E9=A1=B6=E5=A0=86=EF=BC=8C=E6=95=88=E6=9E=9C=E4=B8=8D=E7=90=86?= =?UTF-8?q?=E6=83=B3=20=E5=A6=82=E6=9E=9C=E7=9C=9F=E7=9A=84=E8=A6=81?= =?UTF-8?q?=E8=B7=91=E7=9A=84=E5=A5=BD=EF=BC=8C=E8=BF=98=E6=98=AF=E9=9C=80?= =?UTF-8?q?=E8=A6=81=E5=AF=B9=E4=B8=89=E5=B7=A8=E5=A4=B4=EF=BC=9ADrawBackg?= =?UTF-8?q?round/DrawWindow/DrawMerge=E8=BF=9B=E8=A1=8C=E6=94=B9=E8=BF=9B?= =?UTF-8?q?=EF=BC=8C=E6=9C=80=E5=A5=BD=E6=98=AF=E6=89=B9=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/nba/include/nba/log.hpp | 2 +- src/nba/include/nba/scheduler-arrimpl.hpp | 365 ++++++++++++++++++++++ 2 files changed, 366 insertions(+), 1 deletion(-) create mode 100644 src/nba/include/nba/scheduler-arrimpl.hpp diff --git a/src/nba/include/nba/log.hpp b/src/nba/include/nba/log.hpp index e13e023..e7fb0a9 100644 --- a/src/nba/include/nba/log.hpp +++ b/src/nba/include/nba/log.hpp @@ -29,7 +29,7 @@ enum Level { namespace detail { #if defined(NDEBUG) - static constexpr int kLogMask = Info | Warn | Error | Fatal; + static constexpr int kLogMask = 0; //Info | Warn | Error | Fatal; #else static constexpr int kLogMask = All; #endif diff --git a/src/nba/include/nba/scheduler-arrimpl.hpp b/src/nba/include/nba/scheduler-arrimpl.hpp new file mode 100644 index 0000000..7f7472d --- /dev/null +++ b/src/nba/include/nba/scheduler-arrimpl.hpp @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2023 fleroviux + * + * Licensed under GPLv3 or any later version. + * Refer to the included LICENSE file. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace nba::core { + +struct Scheduler { + template + using EventMethod = void (T::*)(); + + template + using EventMethodWithUserData = void (T::*)(u64); + + enum class EventClass : u16 { + // ARM + ARM_ldm_usermode_conflict, + + // PPU + PPU_hdraw_vdraw, + PPU_hblank_vdraw, + PPU_hdraw_vblank, + PPU_hblank_vblank, + PPU_begin_sprite_fetch, + PPU_update_vcount_flag, + PPU_video_dma, + PPU_latch_dispcnt, + + PPU_hblank_irq, + PPU_vblank_irq, + PPU_vcount_irq, + + // APU + APU_mixer, + APU_sequencer, + APU_PSG1_generate, + APU_PSG2_generate, + APU_PSG3_generate, + APU_PSG4_generate, + + // IRQ controller + IRQ_write_io, + IRQ_update_ie_and_if, + IRQ_update_irq_line, + + // Timers + TM_overflow, + TM_write_reload, + TM_write_control, + + // DMA + DMA_activated, + + // Backup memory + EEPROM_ready, + + SIO_transfer_done, + + KeyPad_Poll, + + EndOfQueue, + Count + }; + + struct Event { + u64 timestamp; + + u64 UID() const { return uid; } + + private: + friend class Scheduler; + int handle; + u64 key; + u64 uid; + u64 user_data; + EventClass event_class; + }; + + Scheduler() { + Register(EventClass::EndOfQueue, this, &Scheduler::EndOfQueue); + + for(int i = 0; i < kMaxEvents; i++) { + pool[i].handle = -1; + heap[i] = nullptr; + } + + for(int i = 0; i < (int)EventClass::Count; i++) { + callbacks[i] = [i](u64 user_data) { + Assert(false, "Scheduler: unhandled event class: {}", i); + }; + } + + Reset(); + } + + void Reset() { + heap_size = 0; + timestamp_now = 0; + next_uid = 1; + + Add(std::numeric_limits::max(), EventClass::EndOfQueue); + } + + auto ALWAYS_INLINE GetTimestampNow() const -> u64 { + return timestamp_now; + } + + void SetTimestampNow(u64 timestamp) { + timestamp_now = timestamp; + } + + auto ALWAYS_INLINE GetTimestampTarget() const -> u64 { + return heap[ heap_size-1 ]->timestamp; + } + + auto ALWAYS_INLINE GetRemainingCycleCount() const -> int { + return int(GetTimestampTarget() - GetTimestampNow()); + } + + void AddCycles(int cycles) { + if( !cycles ) return; + auto timestamp_next = timestamp_now + cycles; + Step(timestamp_next); + timestamp_now = timestamp_next; + } + + template + void Register(EventClass event_class, T* object, EventMethod method, uint priority = 0) { + callbacks[(int)event_class] = [object, method](u64 user_data) { + (object->*method)(); + }; + } + + template + void Register(EventClass event_class, T* object, EventMethodWithUserData method, uint priority = 0) { + callbacks[(int)event_class] = [object, method](u64 user_data) { + (object->*method)(user_data); + }; + } + + auto Add(u64 delay, EventClass event_class, uint priority = 0, u64 user_data = 0) -> Event* { + int n = heap_size++; + //int p = Parent(n); + + Assert( + heap_size <= kMaxEvents, + "Scheduler: reached maximum number of events." + ); + + Assert(priority <= 3, "Scheduler: priority must be between 0 and 3."); + + // auto event = heap[n]; + // event->timestamp = GetTimestampNow() + delay; + // event->key = (event->timestamp << 2) | priority; + // event->uid = next_uid++; + // event->user_data = user_data; + // event->event_class = event_class; + u64 timestamp = GetTimestampNow() + delay; + u64 evkey = (timestamp << 2) | priority; + + // while(n != 0 && heap[p]->key > heap[n]->key) { + // Swap(n, p); + // n = p; + // p = Parent(n); + // } + Event *in = nullptr; + for( int i=0; i < kMaxEvents; ++i ){ + if( pool[i].handle < 0 ) in = pool + i; + } + + Assert( in != nullptr, "You should enlarge the kMaxEvent for queueing" ); + + if( heap_size > 1 ){ + auto next = &heap[ n ]; + auto head = &heap[0]; + for( auto curr = next-1; next != head; curr-- ){ + if( likely( (*curr)->key < evkey ) ){ + (*curr)->handle = n--; + *next = *curr; + next = curr; + } + else { + *next = in; + (*next)->handle = (*curr)->handle+1; + (*next)->timestamp = timestamp; + (*next)->key = evkey; + (*next)->uid = next_uid++; + (*next)->user_data = user_data; + (*next)->event_class = event_class; + return *next; + } + } + } + + auto event = heap[0] = in; + event->timestamp = timestamp; + event->key = evkey; + event->uid = next_uid++; + event->user_data = user_data; + event->event_class = event_class; + event->handle = 0; + return event; + } + + template + auto Add(u64 delay, T* object, EventMethod method, uint priority = 0) -> Event* { + return Add(delay, [object, method](int cycles_late) { + (object->*method)(cycles_late); + }, priority); + } + + void Cancel(Event* event) { + Remove(event->handle); + } + + auto GetEventByUID(u64 uid) -> Event* { + for(int i = 0; i < heap_size; i++) { + auto event = heap[i]; + + if(event->uid == uid) { + return event; + } + } + + return nullptr; + } + + void LoadState(SaveState const& state) { + auto& ss_scheduler = state.scheduler; + + for(int i = 0; i < ss_scheduler.event_count; i++) { + auto& event = ss_scheduler.events[i]; + + u64 timestamp = event.key >> 2; + int priority = event.key & 3; + u64 uid = event.uid; + u64 user_data = event.user_data; + EventClass event_class = (EventClass)event.event_class; + + // This event already was created on Scheduler::Reset() + if(event_class == EventClass::EndOfQueue) { + continue; + } + + Add(timestamp - state.timestamp, event_class, priority, user_data)->uid = uid; + } + + // This must happen after deserializing all events, because calling Add() modifies `next_uid`. + next_uid = ss_scheduler.next_uid; + } + + void CopyState(SaveState& state) { + auto& ss_scheduler = state.scheduler; + + for(int i = 0; i < heap_size; i++) { + auto event = heap[i]; + + ss_scheduler.events[i] = { event->key, event->uid, event->user_data, (u16)event->event_class }; + } + + ss_scheduler.event_count = heap_size; + ss_scheduler.next_uid = next_uid; + } + +private: + static constexpr int kMaxEvents = 64; + + static constexpr int Parent(int n) { return (n - 1) / 2; } + static constexpr int LeftChild(int n) { return n * 2 + 1; } + static constexpr int RightChild(int n) { return n * 2 + 2; } + + Event pool[kMaxEvents]; + + // void ALWAYS_INLINE Swap(int i, int j) { + // auto tmp = heap[i]; + // heap[i] = heap[j]; + // heap[j] = tmp; + // heap[i]->handle = i; + // heap[j]->handle = j; + // } + + void inline Step(u64 timestamp_next) { + while( GetTimestampTarget() <= timestamp_next && heap_size > 0) { + auto event = heap[ heap_size-1 ]; // heap[0]; + timestamp_now = event->timestamp; + callbacks[(int)event->event_class](event->user_data); + Remove(event->handle); + } + } + + void Remove(int n) { + auto out = heap[n]; + if( n+1 == heap_size ){ + heap[n] = nullptr; + } + else { + auto end = &heap[ heap_size-1 ]; + auto out = heap[n]; + auto curr = &heap[n]; + for( auto next = curr+1; curr != end; ++next ){ + (*next)->handle = n++; + *curr = *next; + curr = next; + } + *end = nullptr; + } + --heap_size; + out->handle = -1; + // Swap(n, --heap_size); + + // int p = Parent(n); + // if(n != 0 && heap[p]->key > heap[n]->key) { + // do { + // Swap(n, p); + // n = p; + // p = Parent(n); + // } while(n != 0 && heap[p]->key > heap[n]->key); + // } else { + // Heapify(n); + // } + } + + // void Heapify(int n) { + // int l = LeftChild(n); + // int r = RightChild(n); + + // if(l < heap_size && heap[l]->key < heap[n]->key) { + // Swap(l, n); + // Heapify(l); + // } + + // if(r < heap_size && heap[r]->key < heap[n]->key) { + // Swap(r, n); + // Heapify(r); + // } + // } + + void EndOfQueue() { + Assert(false, "Scheduler: reached end of the event queue."); + } + + Event* heap[kMaxEvents]; + int heap_size; + u64 timestamp_now; + u64 next_uid; + + std::function callbacks[(int)EventClass::Count]; +}; + +inline u64 GetEventUID(Scheduler::Event* event) { + return event ? event->UID() : 0; +} + +} // namespace nba::core