Commit Graph

14 Commits

Author SHA1 Message Date
Tim Allen 3517d5c4a4 Update to v103r02 release.
byuu says:

Changelog:

  - fc/apu: improved phase duty cycle emulation (mode 3 is 25% phase
    inverted; counter decrements)
  - md/apu: power/reset do not cancel 68K bus requests
  - md/apu: 68K is not granted bus access on Z80 power/reset
  - md/controller: replaced System::Peripherals with ControllerPort
    concept
  - md/controller: CTRL port is now read-write, maintains value across
    controller changes (and soon, soft resets)
  - md/psg: PSG sampling rate unintentionally modified¹
  - processor/spc700: improve cycle timing of (indirect),y instructions
    [Overload]
  - processor/spc700: idle() cycles actually read from the program
    counter; much like the 6502 [Overload]
      - some of the idle() cycles should read from other addresses; this
        still needs to be supported
  - processor/spc700: various cleanups to instruction function naming
  - processor/z80: prefix state (HL→IX,IY override) can now be
    serialized
  - icarus: fix install rule for certain platforms (it wasn't buggy on
    FreeBSD, but was on Linux?)

¹: the clock speed of the PSG is oscillator/15. But I was setting the
sampling rate to oscillator/15/16, which was around 223KHz. I am not
sure whether the PSG should be outputting at 3MHz or 223KHz. Amazingly
... I don't really hear a difference either way `o_O` I didn't actually
mean to make this change; I just noticed it after comparing the diff
between r01 and r02. If this turns out to be wrong, set

    stream = Emulator::audio.createStream(1, frequency() / 16.0);

in md/psg.cpp to revert this change.
2017-06-27 11:18:28 +10:00
Tim Allen b7006822bf Update to v103 WIP release.
byuu says (in the WIP forum):

Changelog:

  - higan: cheat codes accept = and ? separators now
      - the new preferred code format is: address=value or
        address=if-match?value
      - the old code format of address/value and address/if-match/value
        will continue to work
  - higan: cheats.bml is no longer included with the base distribution
      - mightymo stopped updating it in 2015, and it's not source code;
        it can still be pulled in from older releases
  - fc: improved PAL mode timing; use PAL APU timing tables; fix PAL
    noise period table [hex\_usr]
  - md: support aborting a Z80 bus wait in order to capture save states
    without freezing
      - note that this will violate accuracy; but in practice a slight
        desync is better than an emulator deadlock
  - sfc: revert DSP ENDX randomization for now (want to research it more
    before deploying in an official release)
  - sfc: fix Super Famicom.sys/manifest.bml APU RAM size [hex\_usr]
  - tomoko: cleaned up make install rules
  - hiro/cocoa: use ABGR for pixel data [Sintendo]

Note: I forgot to change the command-line and drag-and-drop separator
from : to | in this WIP. However, it is corrected in the v103 official
binary and source published on download.byuu.org. Sorry about that, I
know it makes the Git repository history more difficult. I'm not
concerned whether the : → | change is part of v103 or v103r01 in the
repository, and will leave this to your discretion, Screwtape.

I also still need to set the VDP bit to indicate PAL mode in the Mega
Drive core. This is what happens when I have 47 things I have to do,
given how lousy my memory is. I miss things.
2017-06-22 16:10:13 +10:00
Tim Allen 82c58527c3 Update to v102r17 release.
byuu says:

Changelog:

  - GBA: process audio at 2MHz instead of 32KHz¹
  - MD: do not allow the 68K to stop the Z80, unless it has been granted
    bus access first
  - MD: do not reset bus requested/granted signals when the 68K resets
    the Z80
      - the above two fix The Lost Vikings
  - MD: clean up the bus address decoding to be more readable
  - MD: add support for a13000-a130ff (#TIME) region; pass to cartridge
    I/O²
  - MD: emulate SRAM mapping used by >16mbit games; bank mapping used
    by >32mbit games³
  - MD: add 'reset pending' flag so that loading save states won't
    reload 68K PC, SP registers
      - this fixes save state support ... mostly⁴
  - MD: if DMA is not enabled, do not allow CD5 to be set [Cydrak]
      - this fixes in-game graphics for Ristar. Title screen still
        corrupted on first run
  - MD: detect and break sprite lists that form an infinite loop
    [Cydrak]
      - this fixes the emulator from dead-locking on certain games
  - MD: add DC offset to sign DAC PCM samples [Cydrak]
      - this improves audio in Sonic 3
  - MD: 68K TAS has a hardware bug that prevents writing the result back
    to RAM
      - this fixes Gargoyles
  - MD: 68K TRAP should not change CPU interrupt level
      - this fixes Shining Force II, Shining in the Darkness, etc
  - icarus: better SRAM heuristics for Mega Drive games

Todo:

  - need to serialize the new cartridge ramEnable, ramWritable, bank
    variables

¹: so technically, the GBA has its FIFO queue (raw PCM), plus a GB
chipset. The GB audio runs at 2MHz. However, I was being lazy and
running the sequencer 64 times in a row, thus decimating the audio to
32KHz. But simply discarding 63 out of every 64 samples resorts in
muddier sound with more static in it.

However ... increasing the audio thread processing intensity 64-fold,
and requiring heavy-duty three-chain lowpass and highpass filters is not
cheap. For this bump in sound quality, we're eating a loss of about 30%
of previous performance.

Also note that the GB audio emulation in the GBA core still lacks many
of the improvements made to the GB core. I was hoping to complete the GB
enhancements, but it seems like I'm never going to pass blargg's
psychotic edge case tests. So, first I want to clean up the GB audio to
my current coding standards, and then I'll port that over to the GBA,
which should further increase sound quality. At that point, it sound
exceed mGBA's audio quality (due to the ridiculously high sampling rate
and strong-attenuation audio filtering.)

²: word writes are probably not handled correctly ... but games are
only supposed to do byte writes here.

³: the SRAM mapping is used by games like "Story of Thor" and
"Phantasy Star IV." Unfortunately, the former wasn't released in the US
and is region protected. So you'll need to change the NTSU to NTSCJ in
md/system/system.cpp in order to boot it. But it does work nicely now.
The write protection bit is cleared in the game, and then it fails to
write to SRAM (soooooooo many games with SRAM write protection do this),
so for now I've had to disable checking that bit. Phantasy Star IV has a
US release, but sadly the game doesn't boot yet. Hitting some other bug.

The bank mapping is pretty much just for the 40mbit Super Street Fighter
game. It shows the Sega and Capcom logos now, but is hitting yet another
bug and deadlocking.

For now, I emulate the SRAM/bank mapping registers on all cartridges,
and set sane defaults. So long as games don't write to $a130XX, they
should all continue to work. But obviously, we need to get to a point
where higan/icarus can selectively enable these registers on a per-game
basis.

⁴: so, the Mega Drive has various ways to lock a chip until another
chip releases it. The VDP can lock the 68K, the 68K can lock the Z80,
etc. If this happens when you save a state, it'll dead-lock the
emulator. So that's obviously a problem that needs to be fixed. The fix
will be nasty ... basically, bypassing the dead-lock, creating a
miniature, one-instruction-long race condition. Extremely unlikely to
cause any issues in practice (it's only a little worse than the SNES
CPU/SMP desync), but ... there's nothing I can do about it. So you'll
have to take it or leave it. But yeah, for now, save states may lock up
the emulator. I need to add code to break the loops when in the process
of creating a save state still.
2017-03-10 21:23:29 +11:00
Tim Allen 04072b278b Update to v102r16 release.
byuu says:

Changelog:

  - Emulator::Stream now allows adding low-pass and high-pass filters
    dynamically
      - also accepts a pass# count; each pass is a second-order biquad
        butterworth IIR filter
  - Emulator::Stream no longer automatically filters out >20KHz
    frequencies for all streams
  - FC: added 20Hz high-pass filter; 20KHz low-pass filter
  - GB: removed simple 'magic constant' high-pass filter of unknown
    cutoff frequency (missed this one in the last WIP)
  - GB,SGB,GBC: added 20Hz high-pass filter; 20KHz low-pass filter
  - MS,GG,MD/PSG: added 20Hz high-pass filter; 20KHz low-pass filter
  - MD: added save state support (but it's completely broken for now;
    sorry)
  - MD/YM2612: fixed Voice#3 per-operator pitch support (fixes sound
    effects in Streets of Rage, etc)
  - PCE: added 20Hz high-pass filter; 20KHz low-pass filter
  - WS,WSC: added 20Hz high-pass filter; 20KHz low-pass filter

So, the point of the low-pass filters is to remove frequencies above
human hearing. If we don't do this, then resampling will introduce
aliasing that results in sounds that are audible to the human ear. Which
basically an annoying buzzing sound. You'll definitely hear the
improvement from these in games like Mega Man 2 on the NES. Of course,
these already existed before, so this WIP won't sound better than
previous WIPs.

The high-pass filters are a little more complicated. Their main role is
to remove DC bias and help to center the audio stream. I don't
understand how they do this at all, but ... that's what everyone who
knows what they're talking about says, thus ... so be it.

I have set all of the high-pass filters to 20Hz, which is below the
limit of human hearing. Now this is where it gets really interesting ...
technically, some of these systems actually cut off a lot of range. For
instance, the GBA should technically use an 800Hz high-pass filter when
output is done through the system's speakers. But of course, if you plug
in headphones, you can hear the lower frequencies.

Now 800Hz ... you definitely can hear. At that level, nearly all of the
bass is stripped out and the audio is very tinny. Just like the real
system. But for now, I don't want to emulate the audio being crushed
that badly.

I'm sticking with 20Hz everywhere since it won't negatively affect audio
quality. In fact, you should not be able to hear any difference between
this WIP and the previous WIP. But theoretically, DC bias should mostly
be removed as a result of these new filters. It may be that we need to
raise the values on some cores in the future, but I don't want to do
that until we know for certain that we have to.

What I can say is that compared to even older WIPs than r15 ... the
removal of the simple one-pole low-pass and high-pass filters with the
newer three-pass, second-order filters should result in much better
attenuation (less distortion of audible frequencies.) Probably not
enough to be noticeable in a blind test, though.
2017-03-09 07:20:40 +11:00
Tim Allen d76c0c7e82 Update to v102r08 release.
byuu says:

Changelog:

  - PCE: restructured VCE, VDCs to run one scanline at a time
  - PCE: bound VDCs to 1365x262 timing (in order to decouple the VDCs
    from the VCE)
  - PCE: the two changes above allow save states to function; also
    grants a minor speed boost
  - PCE: added cheat code support (uses 21-bit bus addressing; compare
    byte will be useful here)
  - 68K: fixed `mov *,ccr` to read two bytes instead of one [Cydrak]
  - Z80: emulated /BUSREQ, /BUSACK; allows 68K to suspend the Z80
    [Cydrak]
  - MD: emulated the Z80 executing instructions [Cydrak]
  - MD: emulated Z80 interrupts (triggered during each Vblank period)
    [Cydrak]
  - MD: emulated Z80 memory map [Cydrak]
  - MD: added stubs for PSG, YM2612 accesses [Cydrak]
  - MD: improved bus emulation [Cydrak]

The PCE core is pretty much ready to go. The only major feature missing
is FM modulation.

The Mega Drive improvements let us start to see the splash screens for
Langrisser II, Shining Force, Shining in the Darkness. I was hoping I
could get them in-game, but no such luck. My Z80 implementation is
probably flawed in some way ... now that I think about it, I believe I
missed the BusAPU::reset() check for having been granted access to the
Z80 first. But I doubt that's the problem.

Next step is to implement Cydrak's PSG core into the Master System
emulator. Once that's in, I'm going to add save states and cheat code
support to the Master System core.

Next, I'll add the PSG core into the Mega Drive. Then I'll add the
'easy' PCM part of the YM2612. Then the rest of the beastly YM2612 core.
Then finally, cap things off with save state and cheat code support.

Should be nearing a new release at that point.
2017-02-20 19:13:10 +11:00
Tim Allen 26bd7590ad Update to v101r32 release.
byuu says:

Changelog:

  - SMS: fixed controller connection bug
  - SMS: fixed Z80 reset bug
  - PCE: emulated HuC6280 MMU
  - PCE: emulated HuC6280 RAM
  - PCE: emulated HuCard ROM reading
  - PCE: implemented 178 instructions
  - tomoko: removed "soft reset" functionality
  - tomoko: moved "power cycle" to just above "unload" option

I'm not sure of the exact number of HuC6280 instructions, but it's less
than 260.

Many of the ones I skipped are HuC6280-originals that I don't know how
to emulate just yet.

I'm also really unsure about the zero page stuff. I believe we should be
adding 0x2000 to the addresses to hit page 1, which is supposed to be
mapped to the zero page (RAM). But when I look at turboEMU's source, I
have no clue how the hell it could possibly be doing that. It looks to
be reading from page 0, which is almost always ROM, which would be ...
really weird.

I also don't know if I've emulated the T mode opcodes correctly or not.
The documentation on them is really confusing.
2017-01-14 10:59:38 +11:00
Tim Allen 4c3f58150c Update to v101r15 release.
byuu says:

Changelog:

  - added (poorly-named) castable<To, With> template
  - Z80 debugger rewritten to make declaring instructions much simpler
  - Z80 has more instructions implemented; supports displacement on
    (IX), (IY) now
  - added `Processor::M68K::Bus` to mirror `Processor::Z80::Bus`
      - it does add a pointer indirection; so I'm not sure if I want to
        do this for all of my emulator cores ...
2016-09-04 23:51:27 +10:00
Tim Allen d91f3999cc Update to v101r14 release.
byuu says:
Changelog:

  - rewrote the Z80 core to properly handle 0xDD (IX0 and 0xFD (IY)
    prefixes
  - added Processor::Z80::Bus as a new type of abstraction
  - all of the instructions implemented have their proper T-cycle counts
    now
  - added nall/certificates for my public keys

The goal of `Processor::Z80::Bus` is to simulate the opcode fetches being
2-read + 2-wait states; operand+regular reads/writes being 3-read. For
now, this puts the cycle counts inside the CPU core. At the moment, I
can't think of any CPU core where this wouldn't be appropriate. But it's
certainly possible that such a case exists. So this may not be the
perfect solution.

The reason for having it be a subclass of Processor::Z80 instead of
virtual functions for the MasterSystem::CPU core to define is due to
naming conflicts. I wanted the core to say `in(addr)` and have it take
the four clocks. But I also wanted a version of the function that didn't
consume time when called. One way to do that would be for the core to
call `Z80::in(addr)`, which then calls the regular `in(addr)` that goes to
`MasterSystem::CPU::in(addr)`. But I don't want to put the `Z80::`
prefix on all of the opcodes. Very easy to forget it, and then end up not
consuming any time. Another is to use uglier names in the
`MasterSystem::CPU` core, like `read_`, `write_`, `in_`, `out_`, etc. But,
yuck.

So ... yeah, this is an experiment. We'll see how it goes.
2016-09-03 21:26:04 +10:00
Tim Allen 7c96826eb0 Update to v101r13 release.
byuu says:

Changelog:

  - MS: added ms/bus
  - Z80: implemented JP/JR/CP/DI/IM/IN instructions
  - MD/VDP: added window layer emulation
  - MD/controller/gamepad: fixed d2,d3 bits (Altered Beast requires
    this)

The Z80 is definitely a lot nastier than the LR35902. There's a lot of
table duplication with HL→IX→IY; and two of them nest two levels deep
(eg FD CB xx xx), so the design may change as I implement more.
2016-08-27 14:48:21 +10:00
Tim Allen 0b70a01b47 Update to v101r10 release.
byuu says:
Changelog:

  - 68K: MOVEQ is 8-bit signed
  - 68K: disassembler was print EOR for OR instructions
  - 68K: address/program-counter indexed mode had the signed-word/long
    bit backward
  - 68K: ADDQ/SUBQ #n,aN always works in long mode; regardless of size
  - 68K→VDP DMA needs to use `mode.bit(0)<<22|dmaSource`; increment by
    one instead of two
  - Z80: added registers and initial two instructions
  - MS: hooked up enough to load and start running games
      - Sonic the Hedgehog can execute exactly one instruction... whoo.
2016-08-20 00:11:26 +10:00
Tim Allen 1df2549d18 Update to v101r04 release.
byuu says:

Changelog:

  - pulled the (u)intN type aliases into higan instead of leaving them
    in nall
  - added 68K LINEA, LINEF hooks for illegal instructions
  - filled the rest of the 68K lambda table with generic instance of
    ILLEGAL
  - completed the 68K disassembler effective addressing modes
      - still unsure whether I should use An to decode absolute
        addresses or not
      - pro: way easier to read where accesses are taking place
      - con: requires An to be valid; so as a disassembler it does a
        poor job
      - making it optional: too much work; ick
  - added I/O decoding for the VDP command-port registers
  - added skeleton timing to all five processor cores
  - output at 1280x480 (needed for mixed 256/320 widths; and to handle
    interlace modes)

The VDP, PSG, Z80, YM2612 are all stepping one clock at a time and
syncing; which is the pathological worst case for libco. But they also
have no logic inside of them. With all the above, I'm averaging around
250fps with just the 68K core actually functional, and the VDP doing a
dumb "draw white pixels" loop. Still way too early to tell how this
emulator is going to perform.

Also, the 320x240 mode of the Genesis means that we don't need an aspect
correction ratio. But we do need to ensure the output window is a
multiple 320x240 so that the scale values work correctly. I was
hard-coding aspect correction to stretch the window an additional \*8/7.
But that won't work anymore so ... the main higan window is now 640x480,
960x720, or 1280x960. Toggling aspect correction only changes the video
width inside the window.

It's a bit jarring ... the window is a lot wider, more black space now
for most modes. But for now, it is what it is.
2016-08-12 11:07:04 +10:00
Tim Allen ca277cd5e8 Update to v100r14 release.
byuu says:

(Windows: compile with -fpermissive to silence an annoying error. I'll
fix it in the next WIP.)

I completely replaced the time management system in higan and overhauled
the scheduler.

Before, processor threads would have "int64 clock"; and there would
be a 1:1 relationship between two threads. When thread A ran for X
cycles, it'd subtract X * B.Frequency from clock; and when thread B ran
for Y cycles, it'd add Y * A.Frequency from clock. This worked well
and allowed perfect precision; but it doesn't work when you have more
complicated relationships: eg the 68K can sync to the Z80 and PSG; the
Z80 to the 68K and PSG; so the PSG needs two counters.

The new system instead uses a "uint64 clock" variable that represents
time in attoseconds. Every time the scheduler exits, it subtracts
the smallest clock count from all threads, to prevent an overflow
scenario. The only real downside is that rounding errors mean that
roughly every 20 minutes, we have a rounding error of one clock cycle
(one 20,000,000th of a second.) However, this only applies to systems
with multiple oscillators, like the SNES. And when you're in that
situation ... there's no such thing as a perfect oscillator anyway. A
real SNES will be thousands of times less out of spec than 1hz per 20
minutes.

The advantages are pretty immense. First, we obviously can now support
more complex relationships between threads. Second, we can build a
much more abstracted scheduler. All of libco is now abstracted away
completely, which may permit a state-machine / coroutine version of
Thread in the future. We've basically gone from this:

    auto SMP::step(uint clocks) -> void {
      clock += clocks * (uint64)cpu.frequency;
      dsp.clock -= clocks;
      if(dsp.clock < 0 && !scheduler.synchronizing()) co_switch(dsp.thread);
      if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
    }

To this:

    auto SMP::step(uint clocks) -> void {
      Thread::step(clocks);
      synchronize(dsp);
      synchronize(cpu);
    }

As you can see, we don't have to do multiple clock adjustments anymore.
This is a huge win for the SNES CPU that had to update the SMP, DSP, all
peripherals and all coprocessors. Likewise, we don't have to synchronize
all coprocessors when one runs, now we can just synchronize the active
one to the CPU.

Third, when changing the frequencies of threads (think SGB speed setting
modes, GBC double-speed mode, etc), it no longer causes the "int64
clock" value to be erroneous.

Fourth, this results in a fairly decent speedup, mostly across the
board. Aside from the GBA being mostly a wash (for unknown reasons),
it's about an 8% - 12% speedup in every other emulation core.

Now, all of this said ... this was an unbelievably massive change, so
... you know what that means >_> If anyone can help test all types of
SNES coprocessors, and some other system games, it'd be appreciated.

----

Lastly, we have a bitchin' new about screen. It unfortunately adds
~200KiB onto the binary size, because the PNG->C++ header file
transformation doesn't compress very well, and I want to keep the
original resource files in with the higan archive. I might try some
things to work around this file size increase in the future, but for now
... yeah, slightly larger archive sizes, sorry.

The logo's a bit busted on Windows (the Label control's background
transparency and alignment settings aren't working), but works well on
GTK. I'll have to fix Windows before the next official release. For now,
look on my Twitter feed if you want to see what it's supposed to look
like.

----

EDIT: forgot about ICD2::Enter. It's doing some weird inverse
run-to-save thing that I need to implement support for somehow. So, save
states on the SGB core probably won't work with this WIP.
2016-07-30 13:56:12 +10:00
Tim Allen 76a8ecd32a Update to v100r03 release.
byuu says:

Changelog:
- moved Thread, Scheduler, Cheat functionality into emulator/ for
  all cores
- start of actual Mega Drive emulation (two 68K instructions)

I'm going to be rather terse on MD emulation, as it's too early for any
meaningful dialogue here.
2016-07-10 15:28:26 +10:00
Tim Allen 3dd1aa9c1b Update to v100r02 release.
byuu says:

Sigh ... I'm really not a good person. I'm inherently selfish.

My responsibility and obligation right now is to work on loki, and
then on the Tengai Makyou Zero translation, and then on improving the
Famicom emulation.

And yet ... it's not what I really want to do. That shouldn't matter;
I should work on my responsibilities first.

Instead, I'm going to be a greedy, self-centered asshole, and work on
what I really want to instead.

I'm really sorry, guys. I'm sure this will make a few people happy,
and probably upset even more people.

I'm also making zero guarantees that this ever gets finished. As always,
I wish I could keep these things secret, so if I fail / give up, I could
just drop it with no shame. But I would have to cut everyone out of the
WIP process completely to make it happen. So, here goes ...

This WIP adds the initial skeleton for Sega Mega Drive / Genesis
emulation. God help us.

(minor note: apparently the new extension for Mega Drive games is .md,
neat. That's what I chose for the folders too. I thought it was .smd,
so that'll be fixed in icarus for the next WIP.)

(aside: this is why I wanted to get v100 out. I didn't want this code in
a skeleton state in v100's source. Nor did I want really broken emulation,
which the first release is sure to be, tarring said release.)

...

So, basically, I've been ruminating on the legacy I want to leave behind
with higan. 3D systems are just plain out. I'm never going to support
them. They're too complex for my abilities, and they would run too slowly
with my design style. I'm not willing to compromise my design ideals. And
I would never want to play a 3D game system at native 240p/480i resolution
... but 1080p+ upscaling is not accurate, so that's a conflict I want
to avoid entirely. It's also never going to emulate computer systems
(X68K, PC-98, FM-Towns, etc) because holy shit that would completely
destroy me. It's also never going emulate arcade machines.

So I think of higan as a collection of 2D emulators for consoles
and handhelds. I've gone over every major 2D gaming system there is,
looking for ones with games I actually care about and enjoy. And I
basically have five of those systems supported already. Looking at the
remaining list, I see only three systems left that I have any interest
in whatsoever: PC-Engine, Master System, Mega Drive. Again, I'm not in
any way committing to emulating any of these, but ... if I had all of
those in higan, I think I'd be content to really, truly, finally stop
writing more emulators for the rest of my life.

And so I decided to tackle the most difficult system first. If I'm
successful, the Z80 core should cover a lot of the work on the SMS. And
the HuC6280 should land somewhere between the NES and SNES in terms of
difficulty ... closer to the NES.

The systems that just don't appeal to me at all, which I will never touch,
include, but are not limited to:
* Atari 2600/5200/7800
* Lynx
* Jaguar
* Vectrex
* Colecovision
* Commodore 64
* Neo-Geo
* Neo-Geo Pocket / Color
* Virtual Boy
* Super A'can
* 32X
* CD-i
* etc, etc, etc.

And really, even if something were mildly interesting in there ... we
have to stop. I can't scale infinitely. I'm already way past my limit,
but I'm doing this anyway. Too many cores bloats everything and kills
quality on everything. I don't want higan to become MESS v2.

I don't know what I'll do about the Famicom Disk System, PC-Engine CD,
and Mega CD. I don't think I'll be able to achieve 60fps emulating the
Mega CD, even if I tried to.

I don't know what's going to happen here with even the Mega Drive. Maybe
I'll get driven crazy with the documentation and quit. Maybe it'll end
up being too complicated and I'll quit. Maybe the emulation will end up
way too slow and I'll give up. Maybe it'll take me seven years to get
any games playable at all. Maybe Steve Snake, AamirM and Mike Pavone
will pool money to hire a hitman to come after me. Who knows.

But this is what I want to do, so ... here goes nothing.
2016-07-09 14:21:37 +10:00