Monday, April 13, 2020

Clocking a 6502 to 15GHz (!)

6-5-0-who?

The 6502 is an iconic processor that dominated home computing in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 late 70s and early to mid 80s. It was used in machines ranging from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Apple II to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Atari 2600 to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Commodore 64 to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Nintendo Entertainment System. In pop culture, it powered Bender, not to mention cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Terminator!



It often clocked at a modest 1MHz, with faster variants available. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 UK, a 2MHz variant powered cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 legendary BBC Micro, with a 3MHz 6502 co-processor box available to bolt on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 side if you so desired.



Introducing beebjit

It was not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 intention of this side project to produce a working, accurate emulator, but one appears to have popped out. The core of beebjit -- and original side project -- is a just-in-time style translation of 6502 code blocks to quasi-equivalent Intel x64 code blocks. Surrounding that core is emulation for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 various supporting chips of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 BBC Micro, such as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Hitachi 6845 CRTC for display; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 6522 VIA for timing and I/O; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Intel 8271 floppy disc controller; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Texas Instruments SN76489 for sound; etc.

beebjit is an open-source project on GitHub.

In tests, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 translated x64 code has been able to demonstrate up to 15GHz of effective 6502 speed, for purely computational tasks such as BBC BASIC programs. For games, things can be a lot slower due to heavy interactions with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 graphics, keyboard and timing hardware. To run games accurately (or in some cases at all) requires cycle perfect synchronization of different virtual hardware chips. Despite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se challenges, speeds in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GHz range can still be expected.

What does a mutli-GHz BBC Micro system look like? It’s a lot of fun, perhaps best illustrated with a video. Here, we see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 classic space shooter Galaforce. You can play it online here, in a JavaScript BBC Micro emulator called jsbeeb. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 video, we see Galaforce loading up from a disc simulated at real-time -- don’t forget to watch out for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 track-to-track floppy disc seeks, visible as stutters in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 loading of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 title screen image! Then, after we get a feel for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 speed at which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 demo screens are running and cycling, we switch up into high gear. Will you be able to tell at which moment that occurs? (Hint: yes.)



Translating 6502 to x64

When starting out, I had no idea what sort of speed to expect. A naive initial guess would be “fast” because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 6502 instruction set is simple. There are only 256 available combinations of opcode type and addressing mode and only 4 or so 8-bit registers. This is a tiny subset of what cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 x64 can do in its instruction set, so you’d expect a trivial, fast mapping.

As is often cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case, things are rarely so simple. A bit more thinking reveals a ton of factors why it might be fast and a ton why it might be slow:

Things that would tend to make 6502 -> x64 faster

  • In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “captain obvious” category, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 6502 in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 BBC runs at 2MHz where a modern Intel chip is clocked in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GHz range.
  • The simplest 6502 instructions take 2 clock cycles. One example would be LDA #10, which sets register A to 10. The Intel processor can dispatch such a triviality in 1 clock cycle.
  • Depending on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 level of parallelism inherent in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code, modern Intel processors can execute multiple instructions per clock cycle.
  • If you want to get into it, trivial optimizations are possible. For example, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 6502 can only shift its A register by 1 bit at a time with its LSR and ASL instructions, taking 2 clock cycles apiece. It’s common to see a few of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se in a row. By contrast, modern Intel processors can shift a register by an arbitrary number of bits in just one clock cycle!
  • The x64 architecture has an abundance of registers compared to 6502, perhaps offering furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r opportunities to speed things up.
  • With 32KB RAM on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 base model BBC model B, you could start to hope for a workload that fits into L1 caches.


Things that would tend to make 6502 -> x64 slower

  • Some 6502 instructions actually don’t map cleanly to a single x64 equivalent. There are some less common ones such as PHP (push processor flags), or SED (set decimal flag, eww!). A couple of extremely common ones, especially in critical loops, are LDA (),Y and STA (),Y. These read / write memory indirectly. The final read / write address is calculated based on issuing two preceding 8-bit reads to make a 16-bit pointer and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n adding cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Y register. Long story short, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “1 cycle per instruction” nirvana alluded to above certainly does not apply here. Even servicing reads from L1 cache, modern Intel processors take ~4 cycles.
  • The 6502 is from an era where performance was quite deterministic. Memory ran at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same speed (or in some cases faster!) cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 processor, so caches and pipelines and reordering and pain had not yet been inflicted upon us. So, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 5-cycle 6502 instruction LDA (),Y that reads 2 bytes of instructions and 3 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r bytes always takes 5 cycles. You won’t get icache misses, dcache misses, DLTB misses, ITLB misses, stalls for a million ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r reasons, etc. You get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 point.
  • Self-modifying code. This was not only supported, but actually very common, particularly in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most performance critical code. I have to suspect that coders were enjoying cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365mselves. Anyway, this poses huge problems. As we execute any 6502 instruction, we have to consider if it was blown away from under us. If we try to optimize anything, we have to beware of crazy stuff like an optimized-away instruction getting self-modify-nuked.
  • Synchronization and interrupts. At every 6502 instruction, we have to consider whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r an interrupt condition exists that would cause us to need to execute cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IRQ sequence instead. Related, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re may be ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r hardware chips requiring cycle perfect synchronization with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 6502.
  • Annoying 6502 variable cycle counts. Some 6502 instructions that would ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise translate very simply to x64 have a quirk where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y might take a cycle longer depending on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 X or Y register value. LDA ,Y and LDA (),Y are both affected. In a cycle perfect emulation, this needs accounting for.
  • Paging and hardware register access. Certain memory regions may not be stable. A lot of 6502 systems circumvented cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 64KB address space restriction by allowing parts of it to be paged. Additional parts of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address space are typically given over to hardware registers. Hitting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se under emulation is guaranteed to be painfully slow as some hardware chip or anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r will need to be emulated. ROM memory is anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r headache. Games can and do write to ROM memory, which must be detected and ignored!


We’ve already revealed that it’s possible to attain GHz speeds. Re-reading cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 above lists, it would seem that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “slows” outweigh cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “fasts” so it’s worth a look at some tricks pulled to do it!

Tricks for fast 6502 -> x64 translation

Trick #1: make good use of x64’s 16 registers

To get us going, we can take a simple 6502 sample and x64 it!

   0x0000000020100031: mov    $0xa,%eax LDA #10
   0x0000000020100036: test   %al,%al
   0x0000000020100038: mov    %al,%bl TAX
   0x000000002010003a: test   %bl,%bl
   0x000000002010003c: mov    %al,0x40(%rbp) STA $C0
   0x000000002010003f: jmpq   0x20400000 JMP $4000

In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 above (unoptimized) x64, we see that we’ve (permanently!) assigned cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 al register to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 6502 A register and bl to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 X register. Note how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 6502 LDA instruction sets zero and negative flags but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 x64 mov instruction does not, so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 x64 code needs an extra test instruction -- anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r potential source of slowdown but see below. Some 6502 flags are actually stored in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 x64 flags register. The rbp register is used to point to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 6502 memory (actually at offset 0x80 so that every zero page address can be accessed with a 1-byte displacement for a much shorter x64 instruction; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 JIT code is performance sensitive to code size). Finally, note how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 jmpq is jumping to a specific code location. By using a simple fixed mapping of 6502 code addresses to x64 code addresses, we can keep cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 generated code small and fast.

Trick #2: use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 host’s memory management for ROM and paging

As mentioned above, a lot of software seems to like to write to ROM. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 BBC, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 region $C000 - $FFFF is always cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OS ROM (with some hardware registers mapped towards cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end). The region $8000 - $BFFF is usually ROM, with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 paged-in ROM selectable at runtime. (For example, BASIC might usually be paged in but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Disc Filing System ROM would be paged in when doing I/O to disc.) However, a lot of BBCs were upgraded to have some extra RAM that could be paged into this region.
Obviously, writes to ROM must be ignored. But we really do not want to be adding a test and branch instructions to every JIT memory write! Memory management and paging in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 host comes to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rescue. JIT reads and JIT writes are aimed at two different 64KB mapping regions, representing views of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 emulated BBC address space. For cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 JIT reads, ROMs are present as expected. For JIT writes, ROM regions are replaced with “dummy” host pages that are writable but write to a scratch area of memory. (An earlier version of beebjit used fault + fixup for ROM writes instead but this was too slow for Galaforce, which just loves to write to ROM.)
This all works nicely because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ROM boundaries are at 4KB alignments, which joyously happens to match cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 host granularity! There’s a minor wart caused by Windows having worse alignment granularity than ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r operating systems, but creative straddling of boundaries saves cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 day.

Trick #3: do simple self-modify invalidation on write

One of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most important decisions is how to handle self-modifying code. We don’t want to add in code to perform a check before executing every 6502 opcode, so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 alternative is to consider doing something on writes. This is what we do and here is some code:

   0x000000002010003c: mov    %al,0x3001040(%rbp) STA $10C0
   0x0000000020100042: mov    0x4370(%rdi),%r8d
   0x0000000020100049: movw   $0x17ff,(%r8)

As can be seen, after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 actual write to $10C0 is dispatched via cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rbp register, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is a follow-up read and write. rdi permanently points to a context object that contains various important things.
In this instance, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 JIT code is looking up a compiler maintained map of 6502 addresses to host code. The final x64 instruction writes to host code for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 invalidation. But what is this magic constant 0x17ff? It is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 2 byte sequence for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 x64 instruction callq *(%rdi). This ensures that if a 6502 address is invalidated, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 x64 code representing it will cleanly call out of JIT and into some fix up and recompile routine.
It’s tempting to try and avoid cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 extra read and write: what if we can look up whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r a given write address contains code or not? Whereas cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se sorts of optimization may be possible, adding cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 check adds a branch (extra code; bloats branch caches; very slow if not a predictable branch) and will almost certainly be slower than just unconditionally doing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 write. It’s worth noting that nothing depends on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 extra read / write and that’s exactly cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sort of case where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 single core parallelism of modern Intel processors can give a boost.

Trick #4: monitor excessive recompiles

If you consider cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 above scheme for handling self-modification, you’ll immediately notice a serious performance issue: every self-modification appears to incur a huge cost! Firstly, although Intel processors are documented as supporting self-modifying code, it is not fast. You’ll invalidate all sorts of caches and pipelines. Secondly, calling out of JIT to some C routine involves all sorts of register and calling convention fixups. Thirdly, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 act of compiling is slow relative to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 act of executing.
Fortunately, this can all be cleared up with a compiler that generates code differently based on monitoring what is going on. If a given 6502 opcode is being repeatedly recompiled, we can maintain some metadata about cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exact situation and deal with it. To give a concrete example, here’s a couple of 6502 instructions from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Galaforce sprite routine at $B00:

   0B00: STA $0B4F
   …
   0B4E: LDA $2FA0,X

This sort of pattern covers many performance critical self-modifying situations and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 compiler can make it go very fast. Firstly, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hard-coded $2FA0 value is replaced by a fetch from $0B4F and $0B50 so that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 latest in-memory contents are used as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 operand. Secondly, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 write invalidations address table is set up so that 6502 writes to $0B4F and $0B50 no longer invalidate any x64 code because it no longer needs invalidation. And cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n execution proceeds, almost at 100% original speed but supporting self-modified operands!
beebjit doesn’t yet handle all self-modification situations in a recompile-free manner, but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is no fundamental reason it can’t / won’t in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 future.

Trick #5: compile to blocks and check timing once per block

Looking at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 JIT code for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 above Galaforce sprite routine at $0B00, it starts off like this:

   0x00000000200b0000: lea    -0x33(%r15),%r15
   0x00000000200b0004: bt     $0x3f,%r15
   0x00000000200b0009: jb     0x3100b000
   0x00000000200b000f: mov    %al,0x3000acf(%rbp) STA $0B4F
   0x00000000200b0015: mov    0x2dac(%rdi),%r8d
   0x00000000200b001c: movw   $0x17ff,(%r8)

What are those three instructions at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 start? The register r15 permanently stores a variable called “countdown”. Here it is being decremented by 51 (0x33) and checked to see if it goes negative, with a branch if so.
The whole timing model revolves around this countdown. As cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 6502 code interacts with hardware and sets up timer IRQs, vsync IRQs, timers tick down towards 0 when some event must occur (raise IRQ perhaps). There’s also a timer ticking that fires every now and again to check cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 status of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 physical keyboard for input. At any time, r15 tracks how soon cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 soonest timer is due to fire. Since we’re running a 2MHz processor, chances are that it could be thousands of processor ticks between interesting events.
In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 above block, it contains 51 6502 cycles worth of 6502 instructions. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 very common case, nothing interesting is due to happen in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se 51 cycles, so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 r15 decrement will not go negative and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 branch will not be taken. This is great news for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 performance of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 block! Since nothing interesting is happening, we know that nothing will be raising any IRQ and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 whole block can proceed without having to check for an interrupt condition every instruction. We also gain freedom to implement many optimizations without fear of some interrupt landing in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 middle of a couple of coalesced 6502 instructions, for example. It’s also worth noting that nothing else in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 block depends upon r15 so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Intel processor can do cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 check in parallel with ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r work.

Trick #6: optimize zero page writes until you can’t

This one is simple but highly effective. Zero page is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory region $0000 - $00FF on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 6502. Reads / writes here are faster than accessing addresses >= $0100. So, a lot of 6502 code puts fast path variables in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 zero page. We should make zero page handling as fast as possible. By default, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 assumption is that no 6502 code is executed in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 zero page, enabling zero page writes to go faster by not worrying about if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y might be modifying code. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 unusual case that code is ever compiled in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 zero page, it’s simple enough to handle it by invalidating all JIT code and regenerating on-demand with appropriate invalidation for zero page writes. (For an example of a game that triggers this, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s Wizadore. It could be making cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 clever observation that it’s slightly faster to modify code in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 zero page because zero page writes are faster.)
Note that very similar approaches apply to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack page, which is $0100 - $01FF on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 6502. These are not yet implemented in beebjit.

Trick #7: use slow path faults to turbocharge some fast paths

Let’s look at a couple of 6502 reads from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same address -- a hardware register at $FE00 -- but with different addressing modes.

   0x0000000020100031: [1] lea    0xc0(%rbx),%edx LDA ($C0,X)
   0x0000000020100037: [2] movzbl %dl,%edx
   0x000000002010003a: [3] mov    0x12017f01(%rdx),%r9b
   0x0000000020100041:     mov    %rdx,%r8
   0x0000000020100044: [4] mov    -0x7f(%rdx,%rbp,1),%dh
   0x0000000020100048:     mov    -0x80(%r8,%rbp,1),%dl
   0x000000002010004d: [5] movzbl -0x80(%rdx,%rbp,1),%eax
   0x0000000020100052: [6] test   %al,%al
   0x0000000020100054:     mov    $0x12009010,%r10d LDA $FE00
   0x000000002010005a:     jmpq   0x42d613

There is plenty going on here. Unfortunately, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 LDA (,X) addressing mode is not a trivial translation to x64 and implementing it correctly, as beebjit tries to do, incurs cost to handle cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corner cases. It is worth a brief digression to break down cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 different parts corresponding to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 numbers in blue above:

  1. Calculate 6502 X + cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 constant $C0.
  2. Corner case alert! Truncate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 calculation result to 8-bits, as a 6502 does.
  3. Corner case alert! Arrange for a host access violation / SIGSEGV if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 truncated result is exactly $FF. This is because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 upcoming 16-bit pointer read would cross a page boundary and should wrap.
  4. Load a 16-bit 6502 pointer. NOTE! This is done in two 8-bit x64 reads. If done instead as a single 16-bit read, which you assume could be faster, you likely assume wrong as I did initially. You run cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 risk of colliding with store-to-load forwarding because of mixing 16-bit reads with 8-byte writes at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same memory address.
  5. Load cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 actual result into 6502 A from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 16-bit 6502 pointer we constructed.
  6. 6502 LDA updates zero and negative flags, so do cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same in x64.

By contrast, look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 compiled code for LDA $FE00. The JIT engine has essentially given up on compiling this and is throwing it over to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 slower but very capable interpreter. For this 6502 addressing mode, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 read is statically known at compile time, and it is also known to be a hardware register. So we jump to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 interpreter to handle this complicated case.
However, for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 indirect read of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 LDA (,X) addressing mode, we don’t know what memory address will be eventually read so we don’t know if a hardware register will be hit or not. But wait -- cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re isn’t a check in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 compiled code on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 final address before it is read. So how does cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardware register work at all?
The answer is fault + fixup! The observation here is that it’s actually very uncommon for an indirect read to hit a hardware register, so we don’t check for that in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 common fast path. However, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 host virtual address space for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 read is a special “indirect mapping” version of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 main 6502. This mapping will cause a fault if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardware register space is touched. The fault is slow and needs fixing up but it almost never happens so it’s a win.
You can also see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fault + fixup concept used to trap cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exceptionally unusual case where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 LDA (,X) instruction can fetch a 16-bit pointer from $FF, which wraps back to $00. We don’t want to pollute every (,X) mode instruction with code to compare against $FF and possibly branch so we can replace that with a single read which will fault approximately never. (The game Camelot does manage to somehow hit this.)

Trick #8: optimize!

The goal here is not to write a modern optimizing compiler, but to look for and address a couple of common patterns:

  • Common 6502 sequences that compile to x64 sequences with opportunities for coalescing at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 x64 level.
  • Common 6502 sequences that can be expressed differently to take advantage of known details in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sequence.

In both cases, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 word “sequence” is key. Whereas a 6502 interpreter has to consider each instruction in isolation, a 6502 JIT compiler can look across sequences and make use of knowledge of concrete values and concrete nearby instructions. Optimizing across sequences is not as gnarly as it could be because we know interrupts won’t be coming in at arbitrary points, but self-modification is still a hazard, as is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fault + fixup usage described above. To briefly get a flavor of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sort of optimizations possible, let’s look at one last 6502 compilation:

  LDA #1
   0x0000000020100034: movb   $0x1,0x3000000(%rbp) STA $80
   0x000000002010003b: mov    $0x2,%eax LDA #2
   0x0000000020100040: or     0x1(%rbp),%al ORA $81
ASL
ASL
   0x0000000020100043: shl    $0x3,%al ASL
CLC
   0x0000000020100046: add    0x2(%rbp),%al ADC $82
   0x0000000020100049: setb   %r14b
   0x000000002010004d: seto   %r12b

As can be seen, entire 6502 instructions have been eliminated; some have been coalesced; and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 annoying test instructions after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 LDA instructions have been eliminated because those flags are never observable on account of subsequent flag overwrites.


While cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 above tricks are enough to turbocharge a wide range of BBC software, some corner cases remain where speed drops below cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GHz range, sometimes precipitously. These corner cases typically relate to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 interaction between cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 6502 and BBC hardware, not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 6502 -> x64 JIT compilation. There are also a few significant optimizations left on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 TODO list that may raise performance for broad swacá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365s of games. These optimizations will be undertaken as time and interest dictate.


Ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r beebjit tricks

Aside from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 boosted 6502 core, beebjit is a reasonably capable BBC Micro emulator. Currently in beta-ish status, it should be able to emulate any BBC Micro game including classics such as Exile, Elite, Revs, etc.
It is cycle accurate enough to run cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 horror show known as Kevin Edwards’ copy protection for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Nightshade tape.

There are some major things beebjit doesn’t do well or at all yet. To satisfy cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se needs, you may wish to try jsbeeb or one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 many ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r BBC emulators.

  • Portability. beebjit is Linux native. An early attempt at a Windows build does exist. Have a look in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 beebjit builds directory.
  • Broad model support. beebjit only emulates a BBC model B with a few optional bells and whistles such as sideways RAM support. Notable, BBC Master support is not (yet?) implemented.
  • GUI. beebjit renders to a window but does not have a GUI. There’s a fair amount of configurability but you will need to apply yourself to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 command line!
  • Peripherals. beebjit does not emulate peripherals unless cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y were very common back in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 day (e.g. disc drives and tape players). Things like joysticks, mice, second processors etc. are not (yet?) emulated because a vast majority of software does not need cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m. Ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r emulators do fill this need well, though.


There are also some things beebjit does that are not so common and may interest you:

  • Speed. As covered in this post.
  • Protected disc support. beebjit has a more accurate emulation of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 disc, disc drive and disc controller than you will typically find. This means it will load images of original, protected discs. (The archival status of original protected BBC discs is not satisfactory and is something I am working on with ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rs in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 community.)
  • Capture and replay. Like most emulators, beebjit executes deterministically. Only external input -- currently just cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 keyboard -- affects cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 execution path. Therefore, beebjit has a mode to record external input and replay it (deterministically). This feature combines well with speed. You can replay very quickly, effectively providing an alternative to save states but with very small files and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to exit a replay early for a many-in-one save state file.


15GHz

Courtesy of Matt Godbolt (@mattgodbolt), here’s beebjit hitting 15GHz on a couple of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “CLOCKSP” BBC Basic tests, and 12GHz+ overall. This is on an i9-9980XE, which has robust single-core performance: 9th gen Intel core, boost clock @ 4.5GHz. No-one has tried on a 5GHz beast yet, or any 10th gen parts.



Collage of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GHz



With thanks

Special thanks to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following communities for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir help, support and encouragement:

  • bitshifters. Pushing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 BBC Micro to perform feats that no-one worked out how to do “back in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 day”. I particularly recommend Twisted Brain.
  • StarDot.


No comments: