home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 2 / FFMCD02.bin / new / misc / emu / z80 / cache.txt < prev    next >
Text File  |  1993-12-21  |  6KB  |  114 lines

  1.  
  2.     About Using a Translation Cache for Emulation of the Z80
  3.  
  4.             Richard Carlsson
  5.  
  6.  
  7. The problem with using a 'cache' (or 'threaded code') with pointers to
  8. the corresponding instruction routines is that of self-modifying code.
  9.   A write to memory will set the corresponding word in the cache to zero,
  10. so the next time that instruction is executed it will be re-decoded (by
  11. the decoding routine at offset zero). Immediate data is not read from the
  12. cache, so no trouble there.
  13.   That far, things are easy. The real problems begin with the prefixed
  14. instructions. Assume that the instruction consists of more than one byte.
  15. If a write to memory changes an instruction byte after the first, the
  16. actual instruction will change, but its pointer (the one corresponding to
  17. the first byte) is still the same. So, we conclude: in every prefixed
  18. instruction we must first test if one of its other instruction bytes has
  19. changed, and re-decode it if that is the case, marking the pointers after
  20. the first as "unchanged but needing decoding before they themselves can
  21. be used" (just use a nonzero pointer to a decoding routine). That should
  22. not be too difficult.
  23.   But, wait... Prefixes are often used to modify the meaning of a more
  24. ordinary instruction. For example:
  25.  
  26.        21 00 00        means   Ld HL,0
  27.        DD 21 00 00     means   Ld IX,0
  28.  
  29. So, if you jump to the address after the DD-prefix in "Ld IX,0", you
  30. get the instruction "Ld HL,0". The decoding procedure will put a nonzero
  31. pointer in the cache address corresponding to the opcode 21. Jumping to
  32. the DD-prefix address again will do "Ld IX,0" as it should.
  33.   But if I first change the opcode 21 to 22, and then jump to the 22, the
  34. instruction is decoded as "Ld (0),HL" and there is a nonzero pointer
  35. corresponding to the 22. Now if I jump to the DD-prefix I want to get the
  36. instruction "Ld (0),IX", but the opcode pointer is nonzero and is not
  37. detected as having changed. Therefore "Ld IX,0" is executed as before.
  38.  
  39. So normal instruction pointers can be permitted only at the first byte
  40. of an instruction. How do I detect an invalid pointer? I can only test the
  41. pointers for Sign and Zero without being too slow to be practical.
  42.   Clr.w is the fastest way to mark a write to memory, so I'd like to keep
  43. the zero pointers for that purpose. Then I must split the pointer values
  44. in two: instruction pointers (positive), and 'unchanged' markers (negative).
  45. The test for prefixed instructions becomes: If any of the other instruction
  46. bytes have changed (has a nonnegative pointer), the instruction must be
  47. re-decoded. Sadly, this method halves the address space for valid pointers.
  48.   The alternative is to use a zero pointer as 'unchanged' marker and a
  49. pointer to a decoding routine as 'address written to' marker. The test
  50. for prefixed instructions would be: If any of the other instruction bytes
  51. is a nonzero pointer then re-decode. It sounds simpler than the above, but
  52. will be a bit slower, since it uses "move.w #changed,dest". I will not use
  53. it unless I run out of address space.
  54.  
  55.  
  56. The decoding routines must also cope with the wrap-around at +7fff. Reading
  57. immediate data is not a problem, since the wrap-around is automatic there,
  58. and if no prefix or opcode byte is over the limit there is no problem
  59. either. If the PPC runs over the limit, the padding 'out of bounds' pointers
  60. will cause a direct wrap-around. But if there are prefix or opcode bytes on
  61. both sides of the limit, we do have a problem:
  62.   When the decoding is finished and the instruction routine is jumped to,
  63. the PPC must actually be below the start of the cache - we need a buffer
  64. there as well.
  65.  
  66.        BEFORE               AFTER
  67.  
  68.            buf
  69.            ...
  70.            buf         <-PPC
  71.            buf
  72.            -8000   00
  73.            -7fff   FE
  74.            ...               DD CB 00 FE = Set 7,(IX+0)
  75.        PPC->   7ffe    DD
  76.            7fff    CB
  77.            pad
  78.            pad
  79.            ...
  80.            pad
  81.  
  82.   The decoding routines cannot look ahead to separate 'goto execution of
  83. an instruction' and 'goto another step in decoding', so what pointer value
  84. should be written into the cache at PPC before we move the PPC? In the
  85. case above, the current value is pointing to the (currently running)
  86. DDCB-decode routine, which has detected the 'out of bounds' pointer. (The
  87. prefix decoding routines must test for 'out of bounds' pointers before
  88. writing the 'unchanged' markers.)
  89. If we leave the pointer as it is, any changes to the CB-prefix will not be
  90. detected next time the instruction is executed. The whole instruction must
  91. be completely re-decoded every time it is executed. Therefore we should
  92. make the pointer a 'contents changed' marker, guaranteeing a re-decode.
  93. In the case of a DDCB (or FDCB) prefix, the DD (FD) decoding routine has
  94. marked the pointer corresponding to CB as 'unchanged' before it made the
  95. jump, so that pointer must also be re-marked as 'changed'.
  96.   After that, we move PPC to its corresponding place in the lower buffer
  97. and re-decode the complete instruction again, thus making sure that the
  98. contents of the buffer are valid for the prefix tests. The calculation of
  99. the real-PC assures that the instruction bytes will be taken from the
  100. correct address.
  101.  
  102. When we do the 'modified opcode?' tests, we can only address offsets from
  103. PPC without being unreasonably slow. We don't get automatic wrap-around,
  104. and could end up testing an address off the upper limit of the cache.
  105. (But should not, since the decoding routines ought to detect the 'out of
  106. bounds' before instruction execution.) If that happens, the instruction
  107. will simply be re-decoded. Everything ultimately depends on the prefix
  108. decoding routines to handle the wrap-around.
  109.  
  110. Obviously, an instruction with prefix or opcode bytes on both sides of the
  111. +7fff limit does not execute very quickly, being re-decoded twice each
  112. time. Hopefully, this will not happen often, and then not in any crucial
  113. inner loop.
  114.