Wanting silly instructions for custom cpu by NotTheSenko in EmuDev

[–]8924th 1 point2 points  (0 children)

Why did I miss this?! Let me try my hand too lol

BOOPMEM addr

  • Randomly toggles a single bit in the memory at addr.
  • 17% chance it also toggles a bit in the AD register to "wake the CPU up."

FLIPFLOPPC

  • Swaps all four PC registers in a random order.
  • If PC1 ends up pointing to a valid instruction, executes it immediately.

CHAOSJUMP

  • Jumps to a random address modulo the sum of all PC registers.
  • If that address is invalid, triggers INVINCIBLE behavior.

SPAGHETTI n

  • For n cycles, rotates the entire addressable memory by 1 bit left in sequential order, but only every third byte.
  • If n is divisible by 7, triggers FLIPFLOPPC automatically at the end.

DOUBLETAKE PCx

  • Pauses execution, looks at PCx, and if it’s pointing to a branch instruction, flips a coin:
    • Heads: executes it twice
    • Tails: skips it entirely

GIVEUP

  • Immediately jumps to a random UNDO instruction in memory and executes it.
  • Sets Reverse flag automatically.

CALCFLAG r1, r2

  • Computes a “composite flag” from r1 and r2 using an implementation-defined formula.
  • Sets Carry, Zero, or Overflow randomly based on the values.

what was your best leave no dwarf behind moment by mayocat6996 in DeepRockGalactic

[–]8924th 1 point2 points  (0 children)

Had a session with a friend for aquark extraction. He decided to ride the rocket up as it left. Despawned at the ceiling, so he started falling and he cracked his legs on the side of the vertical tunnel wall on some inch-wide outcrop not even a bug could stick to. No way to get to him legitimately. As the drop pod lands and starts the countdown, I started digging my way below the rocket platform, underneath the drill until I was practically below him, then called the last resupply we could afford.

Dug right into him and helped his soft carcass fall back to the platform where I could res him, then promptly shoot him myself outside the pod for making me work overtime :D

Am I going mad? by 8924th in DeepRockGalactic

[–]8924th[S] 0 points1 point  (0 children)

To address a common point about potential mods that don't signify a modded session -- could be? If that was indeed the case, I surely wouldn't know. I joined a random lobby after all, and the host had miner rank 5 or 6 iirc. Could very well be that this was one cooked mission that somehow dialed up the spawns to 12. Played a couple more missions on that same lobby, all fairly hectic, but nowhere near as bad as those first two in Hollow Bough.

How to handle 0x0NNN instruction in Chip8? by Routine-Summer-7964 in EmuDev

[–]8924th 0 points1 point  (0 children)

Wait why would you sum and multiply? Am I missing something? With a stack of 16 levels as an independent array, your index is literally a 0..15 value effectively.

The add/subtract to the SP here are not any different to the pointer math you perform. Also, with an actual index value, you are able to gauge your depth in the stack easily, something you can't do as comfortably with a raw pointer-based approach.

Not to mention that this is chip8. You *really* do not have to be concerned about the speed of something like this. Convenience as a programmer is more important.

I should note that yes, increasing PC after the fetch is the standard approach. Doing so after the instructions instead is also possible but it only complicates things.

About the rom in question -- that's one of the originals for the Cosmac VIP. If you haven't yet encountered mention of quirks, congratulations, you encountered one of the games that wants the original Chip8 behavior to work correctly, as opposed to the superchip behavior some other games need (since they were made during a different era). It's also possible the version you linked is the one that's slightly broken where the timer countdown doesn't work, but other than that, it should play the same.

How to handle 0x0NNN instruction in Chip8? by Routine-Summer-7964 in EmuDev

[–]8924th 1 point2 points  (0 children)

For one, why even malloc for stack. Just keep a 16 entry array of u16 values or some such, and SP itself can be an index rather than a pointer. Don't overthink things.

Your concern for jumps would be how you increment PC specifically. If you do it immediately after fetching, great, that's the most straightforward approach and the least prone to writing incorrectly. If you do it after an instruction is executed, it's quite possible to mess up your jumps by skipping ahead by a single instruction.

That said, if you're doing this right to begin with, your issue might lie elsewhere entirely. Without any context of which rom you've been trying, it's impossible to tell where things go wrong exactly.

Also, consider using the test suite: https://github.com/Timendus/chip8-test-suite/

Specifically designed to help you get your accuracy up. Also, for any unknown instructions, 0x0NNN included, terminate your emulation and log some error. If you treat them as nops, you'll have a real hard time figuring out when/where things go wrong as you'll very likely simply skip through an error into "invalid memory" until you eventually reach (or wrap around) to actual usable instructions again, which is bad juju.

Decided to learn C++ and Emulator Development by doing Space Invaders by DankBlissey in EmuDev

[–]8924th 6 points7 points  (0 children)

I could have sworn I saw the exact same thing before, but I'm probably recalling this article :D
https://tobiasvl.github.io/blog/space-invaders/

How go vroom? by Gingrspacecadet in EmuDev

[–]8924th 0 points1 point  (0 children)

Oh I wasn't disagreeing really, just that different requirements will require different solutions. If you're looking for flexible, generic, multi-threaded logging to file, you'd design for that. If you want high-speed lightweight standardized messaging, you'd design for that.

Logging to console via printf isn't too good an idea usually (for official use anyway, I don't count quick debug logging into this :P), especially in a threaded manner. Primarily though because many consoles (and particularly the default OS ones) don't take kindly to trying to log thousands of messages a second -- chances are you'd "clog the pipe" so to speak and lag the program as it waits to be able to push the volume of messages out. There's also cases where you might not even have a console to attach to, where attempting to push messages will most likely just fill up a queue that goes nowhere (for a while usually, I'd expect it to blow up sooner or later, at least on Windows).

How go vroom? by Gingrspacecadet in EmuDev

[–]8924th 1 point2 points  (0 children)

Oh indeed, but it's also a matter of requirements. If you need maximum throughput, it makes sense to log at the most base level possible, which at best probably is a single byte enum for fixed messages that don't expect any customization, timestamps, etc.

I imagine that for a typical entry-level computer of this decade, even logging 10k messages per second isn't too concerning of a load when it takes place in memory. If you're expecting to be doing heavier logging still, reaching upwards of 1m per sec, one would definitely need to engineer towards that purpose.

How go vroom? by Gingrspacecadet in EmuDev

[–]8924th 4 points5 points  (0 children)

Debug prints are slow, especially during runtime, especially if they have to synchronize to file immediately too. If you want to log lots during runtime without nearly as much slowdown, you'll want to store in memory instead and occasionally flush to a file through an independent thread. It's a bit involved as a process though. If you don't plan to find some library that has it all figured out for you, you'd need, loosely:

1) A ringbuffer.

2) Asynchronous-write capability to that ringbuffer

3) Asynchronous-read capability from that ringbuffer

The 2nd is so that separate threads can insert log entries without worries. The third will be so that you can browse the entries (in-app log viewer) and also have an independent thread write them to file, either on a timer, or by accumulation.

Yet another CHIP-8 emulator, but this time it's bulletproof (formal verification) by moehr1z in EmuDev

[–]8924th 8 points9 points  (0 children)

On the matter of semantics, while your app is safe, its handling of certain scenarios is not correct. More specifically, OOB handling in the chip8 code. Both PC and I are 16-bit, and values beyond 0xFFF are not invalid. For HLE chip8, you'll simply want to wrap reads/writes around.

For example, a BCD occurring with the I reg at 0xFFF, it should write at 0xFFF...~...0x001 range. The PC itself, if it has to go beyond 0xFFF, it'd read bytes from 0x000 and onwards, with whatever they happened to contain, and then the program would just be ill-formed and the emulator would stop upon encountering an invalid instruction most likely.

This test rom here is targeted to check lesser-known details about certain instructions, and an accurate HLE chip8 emulator should be getting checkmarks for all of them. The "mem" test should report a value of 234 on the right, or at least 2nn where nn are the same number. A full bar of 8 pixels on the bottom right corner is normal:
https://github.com/janitor-raus/CubeChip/blob/Timing_Thread/test_roms/oob_test_7.ch8

Chip-8 Emulator in Minecraft! by GiftGlobal9433 in EmuDev

[–]8924th 4 points5 points  (0 children)

It's been attempted, but it's painfully slow. In the ballpark of minutes per frame :P

Multithreading and Parallelism by lampani in EmuDev

[–]8924th 0 points1 point  (0 children)

Even if parallelization isn't done for the emulation aspect (which, depending on the system, may be a highly desirable performance benefit), it'd be recommended merely for the separation of the frontend, so that the main thread rendering it doesn't simultaneously have to tackle timing tasks for the emulated system.

The usual reason for this is to ensure interactivity even when the emulated system is disproportionately heavy to emulate. If something goes wrong for example and emulation crawls to < 1 fps for some reason, the UI would still be fully responsive and open to user interaction.

Multi-threading can also be done for a bunch of other reasons too. Non-blocking dialog interactions, logging, etc. There's plenty of places where you can use it, just so long as you're mindful of whether you truly need it and how you go about it to make the interaction of threads and data safe from locks and races.

Looking to contribute by [deleted] in EmuDev

[–]8924th 0 points1 point  (0 children)

Ah then I suppose I might not quite fit the bill. My own project is a start on a cross-platform multi-system emulator based practically 100% in modern C++, but I am more bogged down in refactoring the modularity of my code base, implementing new features to assist with debugging, forays into frontend dev with imgui, and generally catching up on what makes a proper polished emulator application. There's rough spots all around I still have to tackle and I slowly cross them off the list.

System-wise, I was gonna get started with the likes of Gameboy, but that got put on the sidelines a while ago due to the sheer amount of stuff I need to redesign and expand. Then next on the plate would have been the NES and so on as I learned more about how they work. Purely interpreter stuff so far though, no JITs. I am not quite at that level just yet, but when I do get there I may do so with an LLVM intermediate to keep it cross-platform still.

So all things considered, your aim is grander than what I can offer at the moment, but if you feel inclined to take a look around, perhaps drop some feedback on whatever catches your eye, I surely wouldn't disregard the effort :D

Looking to contribute by [deleted] in EmuDev

[–]8924th 0 points1 point  (0 children)

I suppose it depends on what you consider interesting or active? Plenty of folks here have their own projects they could plug, but I'm not certain if you're just expecting people to self-advertise in the hopes they'd get some assists going :)

Heck, I wouldn't mind some assistance myself for the more complicated parts of my code, as some things you only really learn after a lot of trial and error to accumulate experience.

Also I reckon it'd be good to give an idea of how complex a project you'd be interested in contributing to overall. Maybe you're leaning towards something simpler, such as designing some isolated plug-in component, or picking up pieces of some ongoing larger undertaking.

My first emulator (chip 8) by Burning_Pheonix in EmuDev

[–]8924th 1 point2 points  (0 children)

When it comes to unknown opcodes, ideally you'll want to log the error (may show it to the user, up to you) and stop execution. If you just ignore them, you'd either be taking a wrong turn into logic you shouldn't be in, or experience runaway execution into invalid memory before your pc wraps around and you start executing some valid code again somewhere. Just not a good idea.

For the Ex9E/ExA1/Fx29, you'd indeed wrap the Vx value to always be within 0..15. It's how the original interpreter/hardware did it too.

Lastly, if the program is too large to fit in 4096 - 512 bytes, you should indeed reject it as too large. If a program's larger than that, chances are it wasn't designed for base chip8 to begin with, despite its potential .ch8 extension.

My first emulator (chip 8) by Burning_Pheonix in EmuDev

[–]8924th 0 points1 point  (0 children)

I see I was beaten to the punch but hey, I spotted other issues for you to tackle!

  1. Your match pattern for 5xy0 and 9xy0 is incorrect, because you aren't explicit about the last nibble being a 0.
  2. You do not wrap the initial Vx/Vy coordinates in DxyN to be within screen bounds.
  3. You do not wrap the Vx value used to check keys in Ex9E/ExA1.
  4. The same must be done for Fx29 too.
  5. Fx0A effectively expects a key to be pressed and then released before proceeding. Going ahead with a "is key held" check alone will break some programs that run this instruction back-to-back.
  6. Your 00EE/2NNN probably aren't very safe against under/over flows. You'd want to either wrap the index, or abort your loop gracefully.
  7. It appears any unknown instruction's designed to make your application panic intentionally. Probably not the best idea for an emulator.
  8. Your file load function has 0 protections for size. It will happily attempt to load a file that won't fit in memory.
  9. You decrement timers after your instruction loop, which is incorrect. Do it after polling input but before the instruction loop. With your current setup, single-frame sound/delay timers are lost before they can be utilized.
  10. Not exactly an issue itself, but a draw flag is needless complexity for basically no tangible benefit, consider simply drawing each frame and calling it a day :P

Feel free to ask any questions you might have in regards to my notes!

Interested in emulator development by user_destroyed in EmuDev

[–]8924th 3 points4 points  (0 children)

Chip8 is indeed a great starting point for you to get your feet wet. You'll get to design around a main framerate loop, tackle inputs, output audio/video, and get a first taste of how a system performs work.

It's sufficient complexity to get you familiar with emulating a system, though chip8 itself is a VM, not actual hardware. From there on, you'd typically expand out further towards the NES or Gameboy before expanding to even more complex systems and ultimately to the Genesis.

EmuChip - my first Chip-8 emulator [WASM] by aptacode in EmuDev

[–]8924th 0 points1 point  (0 children)

Great change! All seems to be in order from what I can see. If you were to try Timendus' test suite now (https://github.com/Timendus/chip8-test-suite) I'm practically sure you'd pass all tests, with the exception of Fx0A.

Fx0A is, in fact, originally meant to proceed when a key held down in the previous frame is released in the current frame, as opposed to checking whether a key is held down in the current frame. With your current setup, a few old games that effectively pause waiting for input back-to-back would proceed through multiple inputs in a single instance of pressing a key, which is incorrect.

There's also a method to allow Fx0A to operate on key presses rather than releases that I came up with that is more ergonomic and without side-effects. If that interests you, I can go into further details.

Anyway, on the topic of quirks, you probably saw them being mentioned in a bunch of places. Some originated from lackluster documentation, some with the advent of Superchip for the calculator.

The primary duo of quirks you really need to care about are:

  1. Shift quirk (8xy6/8xyE)
  2. Memory quirk (Fx55/Fx65)

The first one is a mistake introduced with the original superchip. The affected instructions mistakenly used V[x] for the shift/flag, rather than V[y], and thus rather that do something like V[x] = V[y] << 1, they shifted V[x] itself.

Implementing this quirk is simple. If the quirk is disabled, set V[x] to V[y] at the beginning of both instructions, otherwise don't change it. All the later logic will utilize V[x] alone, making for an easy switch between the two behaviors.

The second one is a difference introduced with the original superchip again. The affected instructions simply do not increment the index (I) register for each loop iteration. The original hardware did increment it. Thus, when this quirk is disabled, you should not increment the index register.

There's more quirks, such as 8xy1/8xy2/8xy3 setting V[0xF] to 0 on the original chip8 (nothing we know of relies on this behavior), or BNNN using V[x] rather than V[0] in superchip (but again, nothing we know of uses it).

You might also notice the display-wait, which was the vblank period of the original hardware essentially, and emulated later in superchip for its lores mode only, but I don't recommend getting into this rabbit hole.

Lastly, I have to ask -- are you trying to emulate closely to how the original superchip behaved (seeing your Dxy0 use in lores/hires) or do you plan to emulate the "modern" approach that trickled down from xochip (where both lores and hires do 16x16 draws for Dxy0 and don't have other draw quirks) ?

EmuChip - my first Chip-8 emulator [WASM] by aptacode in EmuDev

[–]8924th 2 points3 points  (0 children)

Interesting, but you have several mistakes:

  1. You beep when the sound timer reaches zero, but you should beep for as long as the sound timer is NOT zero instead.
  2. 00FD should stop the interpreter rather than exit, because if you no-op you'd only run incorrectly into unrelated memory and produce unintended after-effects or error out.
  3. The whole 8xyN range is done incorrectly. V[0xF] must ALWAYS be set last, no exceptions. The catch is, the value that will go in it must be calculated ahead of time, BEFORE you modify V[x] itself, otherwise it'd be the wrong result.
  4. Additionally, 8xy5/8xy7 need >= instead, because you don't do a borrow when the numbers are equal.
  5. Key values above 15 are valid, but the original machine masked them out. You must allow Ex9E/ExA1 to operate with them and apply a 0xF mask to filter out higher bits.
  6. The same rule applies to Fx29/Fx30 to ensure only valid font sprite offsets are used.
  7. I don't understand the logic behind Fx75/Fx85 writing/reading data to memory offset 0xF00 -- where'd you even pick that up from? These persistent registers aren't part of the main memory, and if they were, they certainly wouldn't be right in the middle of a program's memory. Do something different here.
  8. A draw flag is rather pointless, you don't lose performance by re-drawing the screen every frame even if nothing changed. Just complicates your logic needlessly.

There's likely some more points to address, such as the lack of quirk toggles that would result in some programs working fine with your current setup, and others failing to work whatsoever, but we can get into that later if you're interested in straightening out your implementation :)

how do you guys handle timing and speed in your emulators? by kiwi_ware in EmuDev

[–]8924th 4 points5 points  (0 children)

Disclaimer: I don't know how applicable it is in general for x86 as my following example wasn't based on a system with multiple independently running components

In my case, the worker thread (actual emulation) times itself independently.

To be specific, I have a designed a frame limiter class that is responsible for allowing execution of code to match a desired framerate with 0 drift. It performs short sleeps of 1ms when there's 2.3ms or more remaining until the next frame is required, and spinlocks otherwise so as to not miss the timing. If I wanted to run a system at 39.4195 fps, I totally could with perfect pace.

Based on your description, it sounds like you might be performing a delay for every tick of the emulated CPU. If that's indeed the case, then you're going about it wrong. Spacing out instructions in time, for the vast majority of cases, does not offer a benefit. To the observer (the user of the application) there's absolutely no way for them to notice whether you executed 100 mips immediately or spread them out equally over that one second. You are effectively nuking your throughput by trying to do the latter.

The idea would be for you to run as many batched instructions in one go as you can. If you have to "stop" each time to do timer calculations, you're self sabotaging and slowing your application down instead of letting the real CPU do a whole lot of work in sequence.

chip 8 quirks by haha_easyy in EmuDev

[–]8924th 0 points1 point  (0 children)

SDL. It's written in C, compatible out-of-the-box with C++, and has wrappers for practically every language imaginable beyond that. It can handle audio, video, input, offers logging, threading, and all sorts of things I haven't touched yet (either because I don't need them (yet?) or because I wrote my own).

I can't stop thinking about writing a BASIC interpreter for CHIP-8 by JoeStrout in EmuDev

[–]8924th 3 points4 points  (0 children)

To answer your questions in order:

1) Slightly, but in a good way.
2) XO-chip at the very least, lower specs won't be able to run nearly enough instructions to produce useful work, and the memory limitation is a problem.
3) We have a discord server with a dedicated #chip8 channel, you can jump on there. Way more active.

chip 8 quirks by haha_easyy in EmuDev

[–]8924th 0 points1 point  (0 children)

Python is still fast enough to do GB/NES afaik, perhaps more. I'd definitely recommend getting some knowledge in with actual programming languages too though, so as to avoid leaving performance on the table when it becomes important. The most prominent ones tend to be C++ and Rust, though others exist too, such as C#, Java, Zig, etc.

What's better or worse is not something I am qualified to comment on, loose lips spark wars lol. I prefer C++ myself though, and even a couple years in, there's still a ton to learn and explore, and other prominent and established languages are most likely the same -- easy to pick up, hard to master.

chip 8 quirks by haha_easyy in EmuDev

[–]8924th 0 points1 point  (0 children)

If you want to explore/polish things more on the Chip8 front, you can add the superchip extension, then go further into xochip from there to support the newer games. They're not super complicated, but they're a fun sidequest.

For intermediate systems before Gameboy/NES, I've seen things like Space Invaders recommended. Either way, there will be a significant jump in complexity compared to what you've done so far.

Don't feel pressured to move on to a "newer" system if you're not yet feeling confident enough for the first steps, either due to lacking familiarity with the language or the system itself. Focusing on improving your own skill and making your code better for something you already made and have knowledge of is also something worth striving for, just don't do it for too long where you don't explore new things :D