[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Pitchforking timing code, paving the way to a Gold edition of Jace
- Subject: Pitchforking timing code, paving the way to a Gold edition of Jace
- From: BLuRry <brendan.robert@gmail.com>
- Date: Thu, 23 Aug 2012 11:43:07 -0700 (PDT)
- Bytes: 5380
- Complaints-to: groups-abuse@google.com
- Injection-info: glegroupsg2000goo.googlegroups.com; posting-host=70.123.154.97; posting-account=HyIOQgoAAAAfAUGOevdCSBhPYcDSPtM9
- Newsgroups: comp.sys.apple2
- Organization: http://groups.google.com
- User-agent: G2/1.0
The problem with being the sole maintainer of a program is that nobody is standing over my shoulder to call bull***t on some of the older bits of code. That being said, I've been trying to figure out why on earth the Mockingboard timing is off when the emulator starts, but then sounds OK after I reinitalize the mockingboard. Turns out it was a battle of code synchronization efforts that were all going head-to-head and I didn't realize it until running a profiler and observed the thread interactions.
1) The motherboard thread has its own timer which is used to keep the emulator running at the expected 1mhz timing. The CPU goes for 10,000 cycles or so and then after that interval, the timer takes a measurement of how many milliseconds have ellapsed. If the emulator completed over 10ms too early, then the CPU thread is told to sleep for a while to let the time catch up. In general practice, this works OK when sound isn't playing.
2) When speaker sound is active, a sample is taken every 24 cpu cycles. When enough samples fill a buffer, a separate playback thread wakes up and consumes the buffer. Likewise, the main CPU thread is blocked until the buffer is released. This has the effect of forcing the timing of the CPU to match speaker playback so the motherboard timing can be disabled. When sound is active, the effect is that the speaker sounds great. When going inactive there is a brief bump in time while the timing re-adjusts.
3) Mockingboard presents a whole new challenge. There is no need to sample the mockingboard every 24 cycles because it would be a waste of time. Because of this, the mockingboard playback thread can handle its own buffering. The problem was that the mockingboard was only in sync with the motherboard timer if they start at the same exact time. And this happened when I pressed "Apply" on the config screen -- this is not the case when the emulator starts.
So now I've improved the synchronization between the motherboard and the mockingboard (tongue twister?) so all I have to do is deactivate the motherboard timer when the mockingboard is active since the mockingboard will enforce timing sufficiently.
Along the way, I pitchforked a lot of older code synchronization messes. There are places in the emulation when a reconfiguration is made and this requires halting the emulator to avoid any concurrent modification errors. I think I did this in 5 different ways depending on the age of the code. Now I've coalesced all these to one solution: Computer.pause() and Computer.resume(). It makes the code more readable too, in addition to being extremely robust.
Another fun development is that I broke off the SmartPort and Prodos driver MLI handlers into their own abstract classes. This allows a developer a very straightforward way to implement other SmartPort or prodos-based devices without having to re-invent the wheel. I did this originally to make RamFactor, but as the difficulties mounted getting it to work, I ditched my ramfactor prodos drivers in favor of letting the card's ROM do all the work. If you want, though, there is an option to unclock the emulator while the RamFactor is in use. This will cause the emulator to go really fast when using the ram card, but only if the ROM of the card is used for MLI calls and such (it will not speed up the emulator if the I/O ports are used.)
Most of the code is the same, but there were a lot of changes and tweaks under the hood. All cards are now implemented as actual devices so that there is one unified set of methods to attach/detach devices from the system or suspend/resume them in the event they have their own threads (such as the Mockingboard and SSC do). This makes the code a lot more uniform and easier to read. It is now possible to trap C8 rom bank access for cards, should that ever be necessary. Maybe for Videx or CP/M? Not sure, haven't planned on those right now.
Anyway, it was a massive refactoring effort to keep the codebase maintainable and attractive for others to reuse or expand on. I can't wait to get another build out. Probably if I were into version numbers, it would be a 1.0 gold edition. But I'll stick with build dates.