├── asmWidget.py ├── bus.py ├── core.py ├── docs └── gbz80-decoding.html ├── functions.py ├── gz80.py ├── interface.py ├── kea.py ├── solver.py └── text.py /asmWidget.py: -------------------------------------------------------------------------------- 1 | from PySide.QtCore import * 2 | from PySide.QtGui import * 3 | from text import * 4 | 5 | class AsmArea(QAbstractScrollArea): 6 | def __init__(self, parent=None): 7 | super(AsmArea, self).__init__(parent) 8 | 9 | self.setBackgroundRole(QPalette.Base) 10 | self.setAutoFillBackground(True) 11 | 12 | text = [[Text("ROM0:0150", gray), Tab(100), Text("cp ", blue), Text("a", orange), Text(", ", blue), Text("0x11", green)],[Text("ROM0:0152", gray), Tab(100), Text("jr ", blue), Text("z", orange), Text(", ", blue), Text("0x157", green)]] 13 | self.setMouseTracking(True) 14 | self.setFocusPolicy(Qt.ClickFocus) 15 | 16 | self.font = QFont("Courier", 10) 17 | self.fm = QFontMetrics(self.font) 18 | self.h = self.fm.height() 19 | self.setFont(self.font) 20 | self.selectedLine = 0 21 | self.hoverLine = 0 22 | self.hover = None 23 | self.scroll = 0 24 | 25 | self.verticalScrollBar().setSingleStep(self.h) 26 | self.verticalScrollBar().setPageStep(self.height()) 27 | 28 | def resizeEvent(self, event): 29 | self.updateContent() 30 | self.verticalScrollBar().setPageStep(self.height()) 31 | 32 | def scrollContentsBy(self, dx, dy): 33 | self.scroll = self.verticalScrollBar().value() 34 | self.updateContent() 35 | 36 | def notify(self, subject): 37 | self.updateContent() 38 | 39 | def setContentSource(self, source): 40 | self.source = source 41 | source.addObserver(self) 42 | self.updateContent() 43 | 44 | def updateContent(self): 45 | line = self.scroll / self.h 46 | self.text = self.source.getText(line, self.height() / self.h + 1) 47 | for line in self.text: 48 | x = 5 49 | for word in line: 50 | word.left = x 51 | if type(word) is Tab: 52 | x = max(word.stop, x) 53 | else: 54 | x += self.fm.width(str(word)) 55 | word.right = x 56 | self.viewport().update() 57 | self.verticalScrollBar().setRange(0, self.source.getTextSize() * self.h - self.height()) 58 | 59 | def mouseMoveEvent(self, event): 60 | self.hoverLine = event.pos().y() / self.h 61 | x = event.pos().x() 62 | try: 63 | hover = filter(lambda w: w.left < x and w.right > x, self.text[self.hoverLine])[0] 64 | except IndexError: 65 | hover = None 66 | if hover is not self.hover: 67 | try: 68 | hover.mouseIn(self.viewport()) 69 | except AttributeError: 70 | pass 71 | try: 72 | self.hover.mouseOut(self.viewport()) 73 | except AttributeError: 74 | pass 75 | self.hover = hover 76 | 77 | def mousePressEvent(self, event): 78 | self.selectedLine = self.hoverLine 79 | self.viewport().update() 80 | 81 | def keyPressEvent(self, event): 82 | if event.key() == Qt.Key_Up: 83 | self.selectedLine -= 1 84 | self.viewport().update() 85 | elif event.key() == Qt.Key_Down: 86 | self.selectedLine += 1 87 | self.viewport().update() 88 | elif event.key() == Qt.Key_C: 89 | self.source.makeCode(self.selectedLine + self.scroll / self.h) 90 | else: 91 | super(AsmArea, self).keyPressEvent(event) 92 | 93 | def paintEvent(self, event): 94 | painter = QPainter(self.viewport()) 95 | 96 | painter.fillRect(0, self.h * self.selectedLine - self.scroll % self.h, self.width(), self.h, self.palette().alternateBase()) 97 | 98 | for i,line in enumerate(self.text, 1): 99 | for word in line: 100 | try: 101 | painter.setPen(QColor(*word.color)) 102 | except AttributeError: 103 | painter.setPen(QColor(0, 0, 0)) 104 | painter.drawText(word.left, i * self.h - self.fm.descent() - self.scroll % self.h, str(word)) 105 | 106 | -------------------------------------------------------------------------------- /bus.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | class Stream(object): 4 | """ This object provides an interface to a variable number of bytes 5 | at a fixed offset in memory for processor modules to use. 6 | 7 | It also takes care of endiness for larger reads. """ 8 | def __init__(self, block, offset): 9 | self.block = block 10 | self.offset = offset 11 | self.count = 0 12 | 13 | def read8(self): 14 | byte = struct.unpack_from(" 1: 48 | count -= 1 49 | try: 50 | del self.decoded[pc + count] 51 | except KeyError: 52 | pass 53 | if inst.stores(self.pc): # If the program counter gets stored somewhere (aka, a function call) 54 | # Push the computed pc value on the stack to be explored later 55 | self.stack.append(solver.solve(trace, inst.stores(self.pc))) 56 | try: # if there is a conditional jump, then we follow both paths. 57 | if inst.effects[self.pc].isConditional(): 58 | a, b = solver.solveBoth(trace, self.pc); 59 | self.stack.append(a) 60 | self.stack.append(b) 61 | self.notifyObservers() 62 | return 63 | except: 64 | pass 65 | pc = solver.solve(trace, self.pc) 66 | self.notifyObservers() 67 | try: 68 | print "pc = " + pc.infix() 69 | except: 70 | print "pc = 0x%x" % (pc) 71 | 72 | def addObserver(self, observer): 73 | self.observers.append(observer) 74 | 75 | def notifyObservers(self): 76 | for observer in self.observers: 77 | observer.notify(self) 78 | 79 | def getText(self, start, length): 80 | text = [] 81 | lines = sorted(self.decoded.keys()) 82 | for i in range(start, start + length): 83 | try: 84 | text.append([Text("ROM0:%04x" % lines[i], gray), Tab(100)] + self.decoded[lines[i]]) 85 | except KeyError: 86 | break 87 | return text 88 | 89 | def getTextSize(self): 90 | return len(self.decoded) 91 | 92 | def makeCode(self, line): 93 | lines = sorted(self.decoded.keys()) 94 | self.stack.append(lines[line]) 95 | while(len(self.stack)): 96 | self.startTrace(self.stack.pop()) 97 | 98 | if __name__ == "__main__": 99 | core = Core(gz80()) 100 | core.attachMemory(open("page00").read()) 101 | core.startTrace(0x88) 102 | -------------------------------------------------------------------------------- /docs/gbz80-decoding.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Decoding Gamboy Z80 Opcodes 5 | 6 | 7 | 263 | 264 |

DECODING Gameboy Z80 OPCODES

265 |
- of use to disassembler and emulator writers -
266 |
Revision 3 (gameboy)
267 |

By Scott Mansell, Based on the orignal by Cristian Dinu.

268 |
269 |

CONTENTS

270 | 271 | 272 | 281 | 282 |
273 |
    274 |
  1. Introduction
  2. 275 |
  3. Unprefixed opcodes
  4. 276 |
  5. CB‑prefixed opcodes
  6. 277 |
  7. Acknowledgements
  8. 278 |
  9. Revision history
  10. 279 |
280 |
283 |
284 | 285 |

1. INTRODUCTION

286 |

Instruction format and emulation notes

287 |

Z80 instructions are represented in memory as byte sequences of the form (items in brackets are optional):

288 |

289 | [prefix byte,]  opcode  [,displacement byte]  [,immediate data]
290 | - OR -
291 | two prefix bytes,  displacement byte,  opcode 292 |

293 |

The opcode 294 | (operation code) is a single byte whose bit pattern indicates the 295 | operation we need the Z80 to perform (register loading, arithmetic, 296 | I/O, etc.). The opcode may also contain information regarding the 297 | operation's parameters (operands), e.g. the registers which will be 298 | used/affected by the operation.

299 |

An optional prefix byte 300 | may appear before the opcode, changing its meaning and causing the Z80 301 | to look up the opcode in a different bank of instructions. The prefix 302 | byte, if present, may have the values CB, DD, ED, or FD 303 | (these are hexadecimal values). Although there are opcodes which have 304 | these values too, there is no ambiguity: the first byte in the 305 | instruction, if it has one of these values, is always a prefix byte.

306 |

The displacement byte 307 | is a signed 8-bit integer (-128..+127) used in some instructions to 308 | specifiy a displacement added to a given memory address. Its presence 309 | or absence depends on the instruction at hand, therefore, after reading 310 | the prefix and opcode, one has enough information to figure out whether 311 | to expect a displacement byte or not.

312 |

Similarly, immediate data 313 | consists of zero, one, or two bytes of additional information 314 | specifying explicit parameters for certain instructions (memory 315 | addresses, arithmetic operands, etc.). Its presence and number of bytes 316 | are also completely determined by the instruction at hand.

317 |

Note: Signed data is stored in 2's complement form. 16-bit data is stored LSB first.

318 |

A special class of instructions is accesed by using a DD or FD prefix, and then a CB byte. In this situation, the CB byte is also interpreted as a prefix, a mandatory 319 | displacement byte follows, and, finally, the actual opcode occurs. This 320 | is the situation that is described by the second byte pattern shown 321 | above.

322 |

Not all (prefix, opcode) combinations map to valid 323 | instructions. However, it is important to note that, unlike some other 324 | processors, upon encountering an invalid instruction, the Z80 will not 325 | 'crash' or signal an error - it will simply appear to do nothing (as if 326 | executing a NOP instruction), and continue with the next byte 327 | sequence in memory. There may also be several subtle effects, such as 328 | the temporary setting of some internal flags or the prevention of 329 | interrupts immediately after the read instruction. Invalid instructions 330 | are sometimes used to mark special commands and signals for emulators 331 | (e.g. Gerton Lunter's 'Z80' ZX Spectrum emulator).

332 |

There may be 333 | several combinations of bytes that map to the same instruction. The 334 | sequences will usually have different execution times and memory 335 | footprints. Additionally, there are many instructions (not necessarily 336 | 'invalid') which do virtually nothing meaningful, such as LD A, A, etc., and therefore are reasonable substitutes for NOP.

337 |

Some instructions and effects are undocumented 338 | in that they usually do not appear in 'official' Z80 references. 339 | However, by now, these have all been researched and described in 340 | unofficial documents, and they are also used by several programs, so 341 | emulator authors should strive to implement these too, with maximal 342 | accuracy.

343 |

Finally, it is important to note that the disassembly 344 | approach described in this document is a rather 'algorithmic one', 345 | focused on understanding the functional structure of the instruction 346 | matrix, and on how the Z80 figures out what to do upon reading the 347 | bytes. If space isn't a concern, it is faster and easier to use 348 | complete disassembly tables that cover all possible (prefix, opcode) 349 | combinations - with text strings for the instruction display, and 350 | microcode sequences for the actual execution.

351 |

Notations used in this document

352 |
353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 |
Bits in opcode (MSB → LSB)
76543210
x
y
z
p
q
377 |
378 |

Upon establishing the opcode, the Z80's path of action is generally dictated by these values:

379 |

380 | x = the opcode's 1st octal digit (i.e. bits 7-6)
381 | y = the opcode's 2nd octal digit (i.e. bits 5-3)
382 | z = the opcode's 3rd octal digit (i.e. bits 2-0)
383 | p = y rightshifted one position (i.e. bits 5-4)
384 | q = y modulo 2 (i.e. bit 3) 385 |

386 |

The following placeholders for instructions and operands are used:

387 |

388 | d = displacement byte (8-bit signed integer)
389 | n = 8-bit immediate operand (unsigned integer)
390 | nn = 16-bit immediate operand (unsigned integer)
391 | tab[x] = whatever is contained in the table named tab at index x (analogous for y and z and other table names) 392 |

393 |

Operand 394 | data may be interpreted as the programmer desires (either signed or 395 | unsigned), but, in disassembly displays, is generally displayed in 396 | unsigned integer format.

397 |

All instructions with d, n or nn in their expression are generally immediately followed by the displacement/operand (a byte or a word, respectively).

398 |

Although relative jump instructions are traditionally shown with a 16-bit address for an operand, here they will take the form JR/DJNZ d, where d 399 | is the signed 8-bit displacement that follows (as this is how they are 400 | actually stored). The jump's final address is obtained by adding the 401 | displacement to the instruction's address plus 2.

402 |

In this document, the 'jump to the address contained in HL' instruction is written in its correct form JP HL, as opposed to the traditional JP (HL).

403 |

IN (C)/OUT (C) instructions are displayed using the traditional form, although they actually use the full 16-bit port address contained in BC.

404 |

In the expression of an instruction, everything in bold should be taken ad literam, everything in italics should be evaluated.

405 |

This document makes use of an imaginary instruction with the mnemonic NONI 406 | (No Operation No Interrupts). Its interpretation is 'perform a 407 | no-operation (wait 4 T-states) and do not allow interrupts to occur 408 | immediately after this instruction'. The Z80 may actually do more than 409 | just a simple NOP, but the effects are irrelevant assuming normal 410 | operation of the processor.

411 |

Disassembly tables

412 |

These 413 | tables enable us to represent blocks of similar instructions in a 414 | compact form, taking advantage of the many obvious patterns in the 415 | Z80's instruction matrix.

416 | 417 | 418 | 463 | 464 |
419 | 420 | 421 | 422 | 423 | 424 |
Table "r"
8-bit registers
Index01234567
ValueBCDEHL(HL)A
425 | 426 | 434 | 435 | 443 |
427 | 428 | 429 | 430 | 431 | 432 |
Table "rp"
Register pairs featuring SP
Index0123
ValueBCDEHLSP
433 |
436 | 437 | 438 | 439 | 440 | 441 |
Table "rp2"
Register pairs featuring AF
Index0123
ValueBCDEHLAF
442 |
444 | 445 | 446 | 447 | 448 | 449 |
Table "cc"
Conditions
Index0123
ValueNZZNCC
450 | 451 | 452 | 453 | 454 | 455 |
Table "alu"
Arithmetic/logic operations
Index01234567
ValueADD A,ADC A,SUBSBC A,ANDXORORCP
456 | 457 | 458 | 459 | 460 | 461 |
Table "rot"
Rotation/shift operations
Index01234567
ValueRLCRRCRLRRSLASRASWAPSRL
462 |
465 |
466 | 467 |

2. UNPREFIXED OPCODES

468 | 469 | 470 | 471 | 472 | 477 | 478 | 479 | 480 | 481 | 485 | 486 | 487 | 488 | 489 | 495 | 496 | 497 | 498 | 499 | 503 | 504 | 505 | 506 | 507 | 510 | 511 | 512 | 513 | 514 | 517 | 518 | 519 | 520 | 521 | 524 | 525 | 526 | 527 | 528 | 534 | 535 | 536 | 537 | 538 | 539 | 542 | 543 | 544 | 545 | 546 | 549 | 550 | 551 | 552 | 553 | 554 | 557 | 558 | 559 | 560 | 561 | 562 | 568 | 569 | 570 | 571 | 572 | 577 | 578 | 579 | 580 | 581 | 586 | 587 | 588 | 589 | 590 | 596 | 597 | 598 | 599 | 600 | 603 | 604 | 605 | 606 | 607 | 611 | 612 | 613 | 614 | 615 | 618 | 619 | 620 | 621 | 622 | 625 | 626 | 627 | 628 |
FOR x=0
z=0 473 | 474 | 475 | 476 |
y=0NOPy=2STOP
y=1LD (nn), SPy=3JR d
y=4..7JR cc[y-4], d
Relative jumps and assorted ops
z=1 482 | 483 | 484 |
q=0LD rp[p], nn
q=1ADD HL, rp[p]
16-bit load immediate/add
z=2 490 | 491 | 492 | 493 | 494 |
q=0p=0LD (BC), Ap=2LD (HL+), A
p=1LD (DE), Ap=3LD (HL-), A
q=1p=0LD A, (BC)p=2LD A, (HL+)
p=1LD A, (DE)p=3LD A, (HL-)
Indirect loading
z=3 500 | 501 | 502 |
q=0INC rp[p]
q=1DEC rp[p]
16-bit INC/DEC
z=4 508 | 509 |
INC r[y]
8-bit INC
z=5 515 | 516 |
DEC r[y]
8-bit DEC
z=6 522 | 523 |
LD r[y], n
8-bit load immediate
z=7 529 | 530 | 531 | 532 | 533 |
y=0RLCAy=4DAA
y=1RRCAy=5CPL
y=2RLAy=6SCF
y=3RRAy=7CCF
Assorted operations on accumulator/flags
FOR x=1
540 | 541 |
LD r[y], r[z]
8-bit loading
z=6 547 | 548 |
y=6HALT
Exception (replaces LD (HL), (HL))
FOR x=2
555 | 556 |
alu[y] r[z]
Operate on accumulator and register/memory location
FOR x=3
z=0 563 | 564 | 565 | 566 | 567 |
y=0..3RET cc[y]
y=4LD (0xFF00 + n), Ay=6LD A, (0xFF00 + n)
y=5ADD SP, dy=7LD HL, SP+ d
Conditional return, mem-mapped register loads and stack operations
z=1 573 | 574 | 575 | 576 |
q=0POP rp2[p]
q=1p=0RETp=2JP HL
p=1RETIp=3LD SP, HL
POP & various ops
z=2 582 | 583 | 584 | 585 |
y=0..3JP cc[y], nn
y=4LD (0xFF00+C), Ay=6LD A, (0xFF00+C)
y=5LD (nn), Ay=7LD A, (nn)
Conditional jump
z=3 591 | 592 | 593 | 594 | 595 |
y=0JP nny=4(removed)
y=1(CB prefix)y=5(removed)
y=2(removed)y=6DI
y=3(removed)y=7EI
Assorted operations
z=4 601 | 602 |
y=0..3CALL cc[y], nny=4..7(removed)
Conditional call
z=5 608 | 609 | 610 |
q=0PUSH rp2[p]
q=1p=0CALL nnp=1..3(removed)
PUSH & various ops
z=6 616 | 617 |
alu[y] n
Operate on accumulator and immediate operand
z=7 623 | 624 |
RST y*8
Restart
 
629 |
630 | 631 |

3. CB-PREFIXED OPCODES

632 | 633 | 634 | 635 | 636 | 639 | 640 | 641 | 642 | 643 | 646 | 647 | 648 | 649 | 650 | 653 | 654 | 655 | 656 | 657 | 660 | 661 | 662 | 663 |
 
x=0 637 | 638 |
rot[y] r[z]
Roll/shift register or memory location
x=1 644 | 645 |
BIT y, r[z]
Test bit
x=2 651 | 652 |
RES y, r[z]
Reset bit
x=3 658 | 659 |
SET y, r[z]
Set bit
 
664 |
665 | 666 |

4. ACKNOWLEDGEMENTS

667 |

The 668 | original 'algorithm' described herein was constructed by studying an 669 | "instruction/flags affected/binary form/effect" list in a Romanian book 670 | called "Ghidul Programatorului ZX Spectrum" ("The ZX Spectrum 671 | Programmer's Guide").

672 |

Later, the opcodes was modified to represent the opcodes of 673 | the LR35902, a Z80 workalike used in the gameboy.

674 |

The exact effects and quirks of the 675 | CB/DD/ED/FD prefixes, as well as the undocumented ED and CB 676 | instructions, were learnt from "The Undocumented Z80 Documented" by 677 | Sean Young.

678 |

My sincere thanks to all those who have contributed 679 | with suggestions or corrections. They are mentioned in the following 680 | section.

681 |
682 | 683 |

5. REVISION HISTORY

684 |
685 |
Revision 1
686 |
Implemented a better representation for the DDCB instructions (thanks to Ven Reddy) and for certain "invalid" ED instructions, e.g. a more accurate NOP/NONI instead of an 8T NOP (thanks to Dr. Phillip Kendall). Fixed some typos.
687 |
Revision 2
688 |
Radically 689 | altered the presentation. Added an intro section, some diagrams, more 690 | comments and a Revision History section. Fixed some important typos and 691 | changed the wording in some places to avoid misunderstandings (thanks 692 | to BlueChip for his numerous and helpful suggestions; he also suggested 693 | that I add info on the signed number format and byte order).
694 |
Revison 3 (gameboy)
695 |
Alot of opcodes were stripped out to better represent the state of the gameboy's Z80 cpu. A few new ones were added
696 |
697 |
698 |

- EOF -

699 | 700 | -------------------------------------------------------------------------------- /functions.py: -------------------------------------------------------------------------------- 1 | def infix(fmt): 2 | def decorator(fn): 3 | def pp(t): 4 | return fmt % t 5 | fn.pp = pp 6 | return fn 7 | return decorator 8 | 9 | def conditional(fn): 10 | fn.conditional = True 11 | return fn 12 | 13 | @infix("%s + %s") 14 | def add(a, b): 15 | return a + b 16 | 17 | @infix("%s - %s") 18 | def sub(a, b): 19 | return a - b 20 | 21 | @infix("%s & %s") 22 | def andl(a, b): 23 | return a & b 24 | 25 | 26 | @infix("%s ^ %s") 27 | def xorl(a, b): 28 | return a ^ b 29 | 30 | @infix("%s | %s") 31 | def orl(a, b): 32 | return a | b 33 | 34 | def bit(bit, val): 35 | mask = ~(1 << bit) 36 | return int(not (val ^ mask)) 37 | 38 | @infix("%s > %s") 39 | def gt(a, b): 40 | return int(a > b) 41 | 42 | @infix("%s < %s") 43 | def lt(a, b): 44 | return int(a < b) 45 | 46 | @infix("%s == %s") 47 | def eq(a, b): 48 | return int(a == b) 49 | 50 | @infix("%s * %s") 51 | def mul(a, b): 52 | return a * b 53 | 54 | @infix("%s %% %s") 55 | def mod(val, d): 56 | return val % d 57 | 58 | class Memory8(object): 59 | def __init__(self, addr): 60 | self.addr = addr 61 | 62 | def __repr__(self): 63 | if type(self.addr) is int: 64 | return "mem8[0x%04x]" % self.addr 65 | return "mem8[%s]" % self.addr 66 | 67 | class Memory16(object): 68 | def __init__(self, addr): 69 | self.addr = addr 70 | 71 | def __repr__(self): 72 | if type(self.addr) is int: 73 | return "mem16[0x%04x]" % self.addr 74 | return "mem16[%s]" % self.addr 75 | 76 | def infix(self): 77 | return self.__repr__() 78 | 79 | def rotleft(shift, size, value): 80 | value = value << shift 81 | value = (value & ((1 << size) - 1)) | value >> size 82 | return value 83 | 84 | def rotright(shift, size, value): 85 | overflow = value & ((1 << shift) -1) 86 | value = (value >> shift) | overflow << (size - shift) 87 | return value 88 | 89 | def shiftleft(shift, value): 90 | return value << shift 91 | 92 | def shiftright(shift, value): 93 | return value >> shift 94 | 95 | @conditional 96 | @infix("if (%s): %s else %s") 97 | def if_(cond, expr1, expr2): 98 | if cond: 99 | return expr1 100 | else: 101 | return expr2 102 | if_.func_name = "if" 103 | -------------------------------------------------------------------------------- /gz80.py: -------------------------------------------------------------------------------- 1 | from interface import * 2 | from functions import * 3 | 4 | regs = ["b", "c", "d", "e", "h", "l", Memory8("hl"), "a"] 5 | regpairs = ["bc", "de", "hl", "sp"] 6 | 7 | x0z2 = [Instruction("ld (bc),a", {Memory8("bc"): "a", "pc": [add, "pc", 1]}), 8 | Instruction("ld a,(bc)", {"a": Memory8("bc"), "pc": [add, "pc", 1]}), 9 | Instruction("ld (de),a", {Memory8("de"): "a", "pc": [add, "pc", 1]}), 10 | Instruction("ld a,(de)", {"a": Memory8("de"), "pc": [add, "pc", 1]}), 11 | Instruction("ld (hl+),a", {Memory8("hl"): "a", "hl": [add, "hl", 1], "pc": [add, "pc", 1]}), 12 | Instruction("ld a,(hl+)", {"a": Memory8("hl"), "hl": [add, "hl", 1], "pc": [add, "pc", 1]}), 13 | Instruction("ld (hl-),a", {Memory8("hl"): "a", "hl": [sub, "hl", 1], "pc": [add, "pc", 1]}), 14 | Instruction("ld a,(hl-)", {"a": Memory8("hl"), "hl": [sub, "hl", 1], "pc": [add, "pc", 1]})] 15 | 16 | x0z7 = [Instruction("rlca", 17 | {"a": [rotleft, 1, 8, "a"], 18 | "pc": [add, "pc", 1], 19 | "z": 0, "n": 0, "h": 0, "c": [bit, 7, "a"]}), 20 | Instruction("rrca", 21 | {"a": [rotright, 1, 8, "a"], 22 | "pc": [add, "pc", 1], 23 | "z": 0, "n": 0, "h": 0, "c": [bit, 0, "a"]}), 24 | Instruction("rla", 25 | {"a": [orl, [shiftleft, 1, "a"], [shiftleft, 7, "c"]], 26 | "pc": [add, "pc", 1], 27 | "z": 0, "n": 0, "h": 0, "c": [bit, 7, "a"]}), 28 | Instruction("rra", 29 | {"a": [orl, [shiftright, 1, "a"], [shiftleft, 7, "c"]], 30 | "pc": [add, "pc", 1], 31 | "z": 0, "n": 0, "h": 0, "c": [bit, 0, "a"]}), 32 | Instruction("daa", #This instruction is going to give me nightmares 33 | {"a": [if_, [orl, "h", [gt, [xorl, "a", 0xf], 0x9]], 34 | [if_, [orl, [gt, [xorl, [add, "a", 0x06], 0xf0], 0x90], "c"], 35 | [add, "a", 0x66], 36 | [add, "a", 0x06]], 37 | [if_, [orl, [gt, [xorl, "a", 0xf0], 0x90], "c"], 38 | [add, "a", 0x60], 39 | "a"]], # Maybe this should be optimised later to get rid of the if statements 40 | "c": [orl, [gt, [xorl, [add, "a", [mul, 0x06, [orl, "h", [gt, [xorl, "a", 0xf], 0x9]]]], 0xf0], 0x90], "c"], 41 | "h": 0, 42 | "z": [eq, [if_, [orl, "h", [gt, [xorl, "a", 0xf], 0x9]], 43 | [if_, [orl, [gt, [xorl, [add, "a", 0x06], 0xf0], 0x90], "c"], 44 | [add, "a", 0x66], 45 | [add, "a", 0x06]], 46 | [if_, [orl, [gt, [xorl, "a", 0xf0], 0x90], "c"], 47 | [add, "a", 0x60], 48 | "a"]], 0], 49 | "pc": [add, "pc", 1]}), # end of daa 50 | Instruction("cpl", 51 | {"a": [xorl, "a", 0xff], 52 | "pc": [add, "pc", 1], 53 | "n": 1, "h": 1}), 54 | Instruction("scf", 55 | {"pc": [add, "pc", 1], 56 | "n": 0, "h": 0, "c": 1}), 57 | Instruction("ccf", 58 | {"pc": [add, "pc", 1], 59 | "n": 0, "h": 0, "c": 0})] 60 | 61 | class gz80(Processor): 62 | def getDescription(self): 63 | return { "pc" : ProgramCounter(16), 64 | "af" : Register(16), 65 | "bc" : Register(16), 66 | "de" : Register(16), 67 | "hl" : Register(16), 68 | "sp" : Register(16), 69 | "a" : SubRegister("af", 8, 7), 70 | "b" : SubRegister("bc", 8, 7), 71 | "c" : SubRegister("bc", 8, 0), 72 | "d" : SubRegister("de", 8, 7), 73 | "e" : SubRegister("de", 8, 0), 74 | "h" : SubRegister("hl", 8, 7), 75 | "l" : SubRegister("hl", 8, 0), 76 | # cpu state 77 | "interupts": Register(1), 78 | "halted": Register(1), 79 | # flags 80 | "z" : SubRegister("af", 1, 7), 81 | "n" : SubRegister("af", 1, 6), 82 | "h" : SubRegister("af", 1, 5), 83 | "c" : SubRegister("af", 1, 4)} 84 | 85 | def decode(self, state, stream): 86 | b = stream.read8() 87 | x = b >> 6 88 | y = (b >> 3) & 0x7 89 | z = b & 0x7 90 | p = y >> 1 91 | q = y & 1 92 | if x == 1: 93 | if y == 6 and z == 6: 94 | return Instruction("halt", {"pc": [add, "pc", 1], "halted": 1}) 95 | else: 96 | return Instruction("ld %s, %s" % (regs[y], regs[z]), 97 | {regs[y]: regs[z], 98 | "pc": [add, "pc", 1]}) 99 | if x == 2: 100 | if y == 0: 101 | return Instruction("add a,%s" % regs[z], 102 | {"pc": [add, "pc", 1], 103 | "a": [add, "a", regs[z]], 104 | "z": [eq, [xorl, [add, "a", regs[z]], 0xff], 0], 105 | "n": 0, 106 | "h": [gt, [add, [xorl, "a", 0xf], [xorl, regs[z], 0xf]], 0xf], 107 | "c": [gt, [add, "a", regs[z]], 0xff]}) 108 | if y == 1: 109 | return Instruction("adc a,%s" % regs[z], 110 | {"pc": [add, "pc", 1], 111 | "a": [add, [add, "a", regs[z]], "c"], 112 | "z": [eq, [xorl, [add, [add, "a", regs[z]], "c"], 0xff], 0], 113 | "n": 0, 114 | "h": [gt, [add, [add, [xorl, "a", 0xf], [xorl, regs[z], 0xf]], "c"], 0xf], 115 | "c": [gt, [add, [add, "a", regs[z]], "c"], 0xff]}) 116 | if y == 2: 117 | return Instruction("sub %s" % regs[z], 118 | {"pc": [add, "pc", 1], 119 | "a": [sub, "a", regs[z]], 120 | "z": [eq, [mod, [sub, "a", regs[z]], 0xff], 0], 121 | "n": 1, 122 | "h": [gt, [add, [xorl, "a", 0xf], [xorl, regs[z], 0xf]], 0xf], 123 | "c": [gt, [add, "a", regs[z]], 0xff]}) 124 | if y == 3: 125 | return Instruction("sbc a,%s" % regs[z], 126 | {"pc": [add, "pc", 1], 127 | "a": [sub, [sub, "a", regs[z]], "c"], 128 | "z": [eq, [mod, [sub, [sub, "a", regs[z]], "c"], 0xff], 0], 129 | "n": 1, 130 | "h": [gt, [sub, [sub, [xorl, "a", 0xf], [xorl, regs[z], 0xf]], "c"], 0xf], 131 | "c": [gt, [sub, [sub, "a", regs[z]], "c"], 0xff]}) 132 | if y == 4: 133 | return Instruction("and %s" % regs[z], 134 | {"pc": [add, "pc", 1], 135 | "a": [andl, "a", regs[z]], 136 | "z": [eq, [andl, "a", regs[z]], 0], 137 | "n": 0, "h": 1, "c": 0}) 138 | if y == 5: 139 | return Instruction("xor %s" % regs[z], 140 | {"pc": [add, "pc", 1], 141 | "a": [xorl, "a", regs[z]], 142 | "z": [eq, [xorl, "a", regs[z]], 0], 143 | "n": 0, "h": 0, "c": 0}) 144 | if y == 6: 145 | return Instruction("or %s" % regs[z], 146 | {"pc": [add, "pc", 1], 147 | "a": [orl, "a", regs[z]], 148 | "z": [eq, [orl, "a", regs[z]], 0], 149 | "n": 0, "h": 0, "c": 0}) 150 | if y == 7: 151 | return Instruction("cp %s" % regs[z], 152 | {"pc": [add, "pc", 1], 153 | "z": [eq, [mod, [sub, "a", regs[z]], 0xff], 0], 154 | "n": 1, 155 | "h": [gt, [add, [xorl, "a", 0xf], [xorl, regs[z], 0xf]], 0xf], 156 | "c": [gt, [add, "a", regs[z]], 0xff]}) 157 | if x == 0: 158 | if z == 0: 159 | if y == 0: 160 | return Instruction("nop", {"pc": [add, "pc", 1]}) 161 | if y == 1: 162 | nn = stream.read16() 163 | return Instruction("ld 0x%04x,sp" % nn, 164 | {Memory8(nn): "sp", "pc": [add, "pc", 3]}) 165 | if y == 2: 166 | return Instruction("stop", {"pc": [add, "pc", 1], "halted": 1}) 167 | d = stream.read8s() 168 | if y == 3: 169 | return Instruction("jr %i" % d, {"pc": [add, "pc", d+2]}) 170 | if y == 4: 171 | return Instruction("jr nz,%i" % d, 172 | {"pc": [if_, "z", 173 | [add, "pc", 2], 174 | [add, "pc", d+2]]}) 175 | if y == 5: 176 | return Instruction("jr z,%i" % d, 177 | {"pc": [if_, "z", 178 | [add, "pc", d+2], 179 | [add, "pc", 2]]}) 180 | if y == 6: 181 | return Instruction("jr nc,%i" % d, 182 | {"pc": [if_, "c", 183 | [add, "pc", 2], 184 | [add, "pc", d+2]]}) 185 | if y == 7: 186 | return Instruction("jr c,%i" % d, 187 | {"pc": [if_, "c", 188 | [add, "pc", d+2], 189 | [add, "pc", 2]]}) 190 | if z == 1: 191 | if q == 0: 192 | nn = stream.read16() 193 | return Instruction("ld %s,0x%04x" % (regpairs[p], nn), 194 | {regpairs[p]: nn, 195 | "pc": [add, "pc", 3]}) 196 | if q == 1: 197 | return Instruction("add hl,%s" % regpairs[p], 198 | {"hl": [add, "hl", regpairs[p]], 199 | "pc": [add, "pc", 1], 200 | "n": 0, 201 | # h is undefined for 16 bit adds 202 | "c": [gt, [add, "hl", regpairs[p]], 0xffff]}) 203 | if z == 2: 204 | return x0z2[y] 205 | if z == 3 and not q: 206 | return Instruction("inc %s" % regpairs[p], 207 | {regpairs[p]: [add, regpairs[p], 1], 208 | "pc": [add, "pc", 1]}) 209 | if z == 3 and q: 210 | return Instruction("dec %s" % regpairs[p], 211 | {regpairs[p]: [add, regpairs[p], 1], 212 | "pc": [add, "pc", 1]}) 213 | if z == 4: 214 | return Instruction("inc %s" % regs[y], 215 | {regs[y]: [add, regs[y], 1], 216 | "pc": [add, "pc", 1], 217 | "z": [eq, regs[y], 0xff], 218 | "n": 0, 219 | "h": [eq, [xorl, regs[y], 0xf], 0xf]}) 220 | if z == 5: 221 | return Instruction("dec %s" % regs[y], 222 | {regs[y]: [sub, regs[y], 1], 223 | "pc": [add, "pc", 1], 224 | "z": [eq, regs[y], 0x01], 225 | "n": 1, 226 | "h": [eq, [xorl, regs[y], 0xf], 0x0]}) 227 | if z == 6: 228 | n = stream.read8() 229 | return Instruction("ld %s,0x%02x" % (regs[y],n), 230 | {regs[y]: n, 231 | "pc": [add, "pc", 2]}) 232 | if z == 7: 233 | return x0z7[y] 234 | if x == 3: 235 | if z == 0: 236 | if y == 0: 237 | return Instruction("ret nz", 238 | {"pc": [if_, "z", 239 | [add, "pc", 1], 240 | Memory16("sp")], 241 | "sp": [if_, "z", 242 | None, # None means nothing happens 243 | [add, "sp", 2]]}) 244 | if y == 1: 245 | return Instruction("ret z", 246 | {"pc": [if_, "z", 247 | Memory16("sp"), 248 | [add, "pc", 1]], 249 | "sp": [if_, "z", 250 | [add, "sp", 2], 251 | None]}) # None means nothing happens 252 | if y == 2: 253 | return Instruction("ret nc", 254 | {"pc": [if_, "c", 255 | [add, "pc", 1], 256 | Memory16("sp")], 257 | "sp": [if_, "c", 258 | None, # None means nothing happens 259 | [add, "sp", 2]]}) 260 | if y == 3: 261 | return Instruction("ret c", 262 | {"pc": [if_, "c", 263 | Memory16("sp"), 264 | [add, "pc", 1]], 265 | "sp": [if_, "c", 266 | [add, "sp", 2], 267 | None]}) # None means nothing happens 268 | if y == 4: 269 | n = stream.read8() 270 | return Instruction("ld (0xff00 + 0x%02x), a" % n, 271 | {Memory8(0xff00+n): "a", 272 | "pc": [add, "pc", 2]}) 273 | if y == 5: 274 | d = stream.read8s() 275 | return Instruction("add sp,%i" % d, 276 | {"sp": [add, "sp", d], 277 | "pc": [add, "pc", 2], 278 | "z": 0, 279 | "n": 0, 280 | # h is undefined for 16 bit adds 281 | "c": [gt, [add, "sp", d], 0xffff]}) # Question, what happens on underflow? 282 | if y == 6: 283 | n = stream.read8() 284 | return Instruction("ld a,(0xff00+0x%02x)" % n, 285 | {"a": Memory8(0xff00+n), 286 | "pc": [add, "pc", 2]}) 287 | if y == 7: 288 | d = stream.read8s() 289 | return Instruction("ld hl,sp+%i" % d, 290 | {"hl": [add, "sp", d], 291 | "pc": [add, "pc", 2]}) 292 | if z == 1: 293 | if q == 0: 294 | return Instruction("pop %s" % regpairs[p], 295 | {"pc": [add, "pc", 1], 296 | regpairs[p]: Memory16("sp"), 297 | "sp": [add, "sp", 2]}) 298 | if p == 0: 299 | return Instruction("ret", 300 | {"pc": Memory16("sp"), 301 | "sp": [add, "sp", 2]}) 302 | if p == 1: 303 | return Instruction("reti", 304 | {"pc": Memory16("sp"), 305 | "sp": [add, "sp", 2]}) 306 | if p == 2: 307 | return Instruction("reti", 308 | {"pc": Memory16("sp"), 309 | "sp": [add, "sp", 2]}) 310 | if p == 3: 311 | return Instruction("ld sp, hl", 312 | {"sp": "hl", 313 | "pc": [add, "pc", 1]}) 314 | if z == 2: 315 | if y == 0: 316 | nn = stream.read16() 317 | return Instruction("jp nz,0x%04x" % nn, 318 | {"pc": [if_, "z", 319 | [add, "pc", 3], 320 | nn], 321 | "sp": [add, "sp", 2]}) 322 | if y == 1: 323 | nn = stream.read16() 324 | return Instruction("jp z,0x%04x" % nn, 325 | {"pc": [if_, "z", 326 | nn, 327 | [add, "pc", 3]]}) 328 | if y == 2: 329 | nn = stream.read16() 330 | return Instruction("jp nc,0x%04x" % nn, 331 | {"pc": [if_, "c", 332 | [add, "pc", 3], 333 | nn]}) 334 | if y == 3: 335 | nn = stream.read16() 336 | return Instruction("jp c,0x%04x" % nn, 337 | {"pc": [if_, "c", 338 | nn, 339 | [add, "pc", 3]]}) 340 | if y == 4: 341 | return Instruction("ld (0xff00+c),a", 342 | {Memory8([add, 0xff00, "c"]): "a", 343 | "pc": [add, "pc", 1]}) 344 | if y == 5: 345 | nn = stream.read16() 346 | return Instruction("ld (0x%04x),a" % nn, 347 | {Memory8(nn): "a", 348 | "pc": [add, "pc", 3]}) 349 | if y == 6: 350 | return Instruction("ld a,(0xff00+c)", 351 | {"a": Memory8([add, 0xff00, "c"]), 352 | "pc": [add, "pc", 1]}) 353 | if y == 7: 354 | nn = stream.read16() 355 | return Instruction("ld a,(0x%04x)" % nn, 356 | {"a": Memory8(nn), 357 | "pc": [add, "pc", 3]}) 358 | if z == 3: 359 | if y == 0: 360 | nn = stream.read16() 361 | return Instruction("jp 0x%04x" % nn, {"pc": nn}) 362 | if y == 1: 363 | return self.CBdecode(stream.read8()) 364 | if y == 6: 365 | return Instruction("di", 366 | {"interupts": 0, 367 | "pc": [add, "pc", 1]}) 368 | if y == 7: 369 | return Instruction("ei", 370 | {"interupts": 1, 371 | "pc": [add, "pc", 1]}) 372 | if z == 4: 373 | if y == 0: 374 | nn = stream.read16() 375 | return Instruction("call nz", 376 | {"pc": [if_, "z", 377 | [add, "pc", 3], 378 | nn], 379 | Memory16([sub, "sp", 2]): [if_, "z", 380 | None, # None means nothing happens 381 | [add, "pc", 3]], 382 | "sp": [if_, "z", 383 | None, 384 | [sub, "sp", 2]]}) 385 | if y == 1: 386 | nn = stream.read16() 387 | return Instruction("call z", 388 | {"pc": [if_, "z", 389 | nn, 390 | [add, "pc", 3]], 391 | Memory16([sub, "sp", 2]): [if_, "z", 392 | [add, "pc", 3], 393 | None], # None means nothing happens 394 | "sp": [if_, "z", 395 | [sub, "sp", 2], 396 | None]}) 397 | if y == 2: 398 | nn = stream.read16() 399 | return Instruction("call nc", 400 | {"pc": [if_, "c", 401 | [add, "pc", 3], 402 | nn], 403 | Memory16([sub, "sp", 2]): [if_, "c", 404 | None, 405 | [add, "pc", 3]], 406 | "sp": [if_, "c", 407 | None, 408 | [sub, "sp", 2]]}) 409 | if y == 3: 410 | nn = stream.read16() 411 | return Instruction("call c", 412 | {"pc": [if_, "c", 413 | nn, 414 | [add, "pc", 1]], 415 | Memory16([sub, "sp", 2]): [if_, "c", 416 | [add, "pc", 3], 417 | None], # None means nothing happens 418 | "sp": [if_, "c", 419 | [sub, "sp", 2], 420 | None]}) 421 | if z == 5: 422 | if q == 0: 423 | return Instruction("push %s" % regpairs[p], 424 | {"pc": [add, "pc", 1], 425 | Memory16([sub, "sp", 2]): regpairs[p], 426 | "sp": [sub, "sp", 2]}) 427 | if p == 0: 428 | nn = stream.read16() 429 | return Instruction("call 0x%04x" % nn, 430 | {"pc": nn, 431 | Memory16([sub, "sp", 2]): [add, "pc", 3], 432 | "sp": [sub, "sp", 2]}) 433 | if z == 6: 434 | n = stream.read8() 435 | if y == 0: 436 | return Instruction("add a,0x%02x" % n, 437 | {"pc": [add, "pc", 2], 438 | "a": [add, "a", n], 439 | "z": [eq, [xorl, [add, "a", n], 0xff], 0], 440 | "n": 0, 441 | "h": [gt, [add, [xorl, "a", 0xf], n ^ 0xf], 0xf], 442 | "c": [gt, [add, "a", n], 0xff]}) 443 | if y == 1: 444 | return Instruction("adc a,0x%02x" % n, 445 | {"pc": [add, "pc", 2], 446 | "a": [add, [add, "a", n], "c"], 447 | "z": [eq, [xorl, [add, [add, "a", n], "c"], 0xff], 0], 448 | "n": 0, 449 | "h": [gt, [add, [add, [xorl, "a", 0xf], n^0xf], "c"], 0xf], 450 | "c": [gt, [add, [add, "a", n], "c"], 0xff]}) 451 | if y == 2: 452 | return Instruction("sub 0x%02x" % n, 453 | {"pc": [add, "pc", 2], 454 | "a": [sub, "a", n], 455 | "z": [eq, [mod, [sub, "a", n], 0xff], 0], 456 | "n": 1, 457 | "h": [gt, [add, [xorl, "a", 0xf], n^0xf], 0xf], 458 | "c": [gt, [add, "a", n], 0xff]}) 459 | if y == 3: 460 | return Instruction("sbc a,0x%02x" % n, 461 | {"pc": [add, "pc", 2], 462 | "a": [sub, [sub, "a", n], "c"], 463 | "z": [eq, [mod, [sub, [sub, "a", n], "c"], 0xff], 0], 464 | "n": 1, 465 | "h": [gt, [sub, [sub, [xorl, "a", 0xf], n^0xf], "c"], 0xf], 466 | "c": [gt, [sub, [sub, "a", n], "c"], 0xff]}) 467 | if y == 4: 468 | return Instruction("and 0x%02x" % n, 469 | {"pc": [add, "pc", 2], 470 | "a": [andl, "a", n], 471 | "z": [eq, [andl, "a", n], 0], 472 | "n": 0, "h": 1, "c": 0}) 473 | if y == 5: 474 | return Instruction("xor 0x%02x" % n, 475 | {"pc": [add, "pc", 2], 476 | "a": [xorl, "a", n], 477 | "z": [eq, [xorl, "a", n], 0], 478 | "n": 0, "h": 0, "c": 0}) 479 | if y == 6: 480 | return Instruction("or 0x%02x" % n, 481 | {"pc": [add, "pc", 2], 482 | "a": [orl, "a", n], 483 | "z": [eq, [orl, "a", n], 0], 484 | "n": 0, "h": 0, "c": 0}) 485 | if y == 7: 486 | return Instruction("cp 0x%02x" % n, 487 | {"pc": [add, "pc", 2], 488 | "z": [eq, [mod, [sub, "a", n], 0xff], 0], 489 | "n": 1, 490 | "h": [gt, [add, [xorl, "a", 0xf], n^0xf], 0xf], 491 | "c": [gt, [add, "a", n], 0xff]}) 492 | if z==7: 493 | return Instruction("rst 0x%02x" % (y*8), 494 | {"pc": y*8, 495 | Memory16([sub, "sp", 2]): [add, "pc", 1], 496 | "sp": [sub, "sp", 2]}) 497 | 498 | def CBdecode(self, b): 499 | x = b >> 6 500 | y = (b >> 3) & 0x7 501 | z = b & 0x7 502 | if x == 0: 503 | if y == 0: 504 | return Instruction("rlc %s" % regs[z], 505 | {"pc": [add, "pc", 2], 506 | regs[z]: [rotleft, 1, 8, regs[z]], 507 | "z": [eq, regs[z], 0], 508 | "c": [bit, 7, regs[z]], 509 | "n": 0, "h": 0}) 510 | if y == 1: 511 | return Instruction("rrc %s" % regs[z], 512 | {"pc": [add, "pc", 2], 513 | regs[z]: [rotright, 1, 8, regs[z]], 514 | "z": [eq, regs[z], 0], 515 | "c": [bit, 0, regs[z]], 516 | "n": 0, "h": 0}) 517 | if y == 2: 518 | return Instruction("rl %s" % regs[z], 519 | {"pc": [add, "pc", 2], 520 | regs[z]: [orl, [shiftleft, 1, regs[z]], "c"], 521 | "z": [eq, [orl, [xorl, regs[z], 0x7f], "c"], 0], 522 | "c": [bit, 7, regs[z]], 523 | "n": 0, "h": 0}) 524 | if y == 3: 525 | return Instruction("rr %s" % regs[z], 526 | {"pc": [add, "pc", 2], 527 | regs[z]: [orl, [shiftright, 1, regs[z]], [shiftleft, "c", 7]], 528 | "z": [eq, [orl, [shiftright, 1, regs[z]], "c"], 0], 529 | "c": [bit, 0, regs[z]], 530 | "n": 0, "h": 0}) 531 | if y == 4: 532 | return Instruction("sla %s" % regs[z], 533 | {"pc": [add, "pc", 2], 534 | regs[z]: [shiftleft, 1, regs[z]], 535 | "z": [eq, [xorl, regs[z], 0x7f], 0], 536 | "c": [bit, 7, regs[z]], 537 | "n": 0, "h": 0}) 538 | if y == 5: 539 | return Instruction("sra %s" % regs[z], 540 | {"pc": [add, "pc", 2], 541 | regs[z]: [orl, [shiftright, 1, regs[z]], [xorl, regs[z], 0x80]], 542 | "z": [lt, regs[z], 2], 543 | "c": [bit, 0, regs[z]], 544 | "n": 0, "h": 0}) 545 | if y == 6: 546 | return Instruction("swap %s" % regs[z], 547 | {"pc": [add, "pc", 2], 548 | regs[z]: [orl, [shiftleft, 4, regs[z]], [shiftright, 4, regs[z]]], 549 | "z": [eq, regs[z], 0], 550 | "n": 0, "h": 0, "c": 0}) 551 | if y == 7: 552 | return Instruction("srl %s" % regs[z], 553 | {"pc": [add, "pc", 2], 554 | regs[z]: [shiftright, 1, regs[z]], 555 | "z": [lt, regs[z], 2], 556 | "c": [bit, 0, regs[z]], 557 | "n": 0, "h": 0}) 558 | if x == 1: 559 | return Instruction("bit %i,%s" % (y, regs[z]), 560 | {"pc": [add, "pc", 2], 561 | "z": [bit, y, regs[z]], 562 | "n": 0, "h": 0}) 563 | if x == 2: 564 | return Instruction("set %i,%s" % (y, regs[z]), 565 | {"pc": [add, "pc", 2], 566 | regs[z]: [orl, regs[z], 1 << y]}) 567 | if x == 3: 568 | return Instruction("res %i,%s" % (y, regs[z]), 569 | {"pc": [add, "pc", 2], 570 | regs[z]: [xorl, regs[z], ~(1 << y)]}) 571 | 572 | 573 | -------------------------------------------------------------------------------- /interface.py: -------------------------------------------------------------------------------- 1 | class Processor(object): 2 | """ All Processor modules implement this class.""" 3 | 4 | def getDescription(self): 5 | """ Returns the register layout of the cpu """ 6 | return None 7 | 8 | def decodeInstruction(self, state, stream): 9 | """ Decodes a single instruction. 10 | State contains any registers that might effect the decoding 11 | for example, thumb mode on arm. 12 | Stream is a source of bytes for the processor module to decode. 13 | Returns an Instruction object. 14 | """ 15 | return None 16 | 17 | class Register(object): 18 | def __init__(self, size): 19 | self.size = size 20 | 21 | class ProgramCounter(Register): 22 | pass 23 | 24 | class SubRegister(Register): 25 | def __init__(self, parent, size, offset): 26 | self.parent = parent 27 | self.size = size 28 | self.offset = offset 29 | 30 | class Sexpression(list): 31 | def __init__(self, l): 32 | for i, x in enumerate(l): 33 | if type(x) is list: 34 | l[i] = Sexpression(x) 35 | super(Sexpression, self).__init__(l) 36 | 37 | def __str__(self): 38 | string = "(" + self[0].func_name 39 | for x in self[1:]: 40 | string += " " 41 | string += str(x) 42 | return string + ")" 43 | 44 | def infix(self): 45 | t = tuple([x.infix() if type(x) is Sexpression else x for x in self[1:]]) 46 | return self[0].pp(t) 47 | 48 | def contains(self, reg): 49 | for x in self[1:]: 50 | if type(x) is Sexpression: 51 | if x.contains(reg): 52 | return True 53 | elif x == reg: 54 | return True 55 | return False 56 | 57 | def isConditional(self): 58 | return hasattr(self[0], "conditional") 59 | 60 | class Instruction(object): 61 | def __setattr__(self, *args): 62 | raise TypeError("can't modify immutable instance") 63 | __delattr__ = __setattr__ 64 | def __init__(self, asm, effects): 65 | super(Instruction, self).__setattr__('asm', asm) #self.asm = asm 66 | for e in effects.keys(): 67 | if type(effects[e]) is list: 68 | effects[e] = Sexpression(effects[e]) 69 | super(Instruction, self).__setattr__('effects', effects) #self.effects = effects 70 | 71 | def __repr__(self): 72 | return self.asm 73 | 74 | def prettyPrint(self): 75 | return self.asm 76 | 77 | def stores(self, reg): 78 | """ If this instruciton stores reg into another register or a 79 | memory address, this will return that memory address/register. 80 | Otherwise, returns None. 81 | """ 82 | for e in self.effects.keys(): 83 | if type(self.effects[e]) is Sexpression: 84 | if self.effects[e].contains(reg): 85 | if e is not reg: 86 | return e 87 | return None 88 | 89 | class Address(object): 90 | def __init__(self, addr): 91 | pass 92 | 93 | #class RealtiveAddress(Address): 94 | -------------------------------------------------------------------------------- /kea.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | 3 | import sys 4 | from PySide.QtCore import * 5 | from PySide.QtGui import * 6 | from asmWidget import AsmArea 7 | from core import Core 8 | from gz80 import gz80 9 | 10 | class MainWindow(QMainWindow): 11 | def __init__(self): 12 | super(MainWindow, self).__init__() 13 | 14 | self.createMenus() 15 | self.asmArea = AsmArea() 16 | self.core = Core(gz80()) 17 | self.core.attachMemory(open("page00").read()) 18 | self.asmArea.setContentSource(self.core) 19 | self.setCentralWidget(self.asmArea) 20 | 21 | self.setWindowTitle("kea - page00") 22 | self.resize(800,600) 23 | 24 | def createMenus(self): 25 | self.exitAct = QAction("E&xit", self, shortcut="Ctrl+Q", 26 | triggered=self.close) 27 | 28 | fileMenu = QMenu("&File", self) 29 | fileMenu.addAction(self.exitAct) 30 | self.menuBar().addMenu(fileMenu) 31 | 32 | app = QApplication(sys.argv) 33 | 34 | main = MainWindow() 35 | main.show() 36 | 37 | app.exec_() 38 | sys.exit() 39 | -------------------------------------------------------------------------------- /solver.py: -------------------------------------------------------------------------------- 1 | import types 2 | from interface import * 3 | import copy 4 | 5 | def solve(trace, var): 6 | equ = build_equ(trace, var) 7 | return execute(equ) 8 | 9 | def solveBoth(trace, var): 10 | equ = build_equ(trace, var) 11 | return (execute(equ[2]), execute(equ[3])) 12 | 13 | def build_equ(trace, var): 14 | equ = var 15 | need = [var] 16 | neededNext = [] 17 | for inst in reversed(trace): 18 | for v in need[:]: 19 | if v in inst.effects: 20 | equ = replace(v, inst.effects[v], equ) 21 | need.remove(v) 22 | neededNext += needed(inst.effects[v]) 23 | need = Sexpression(set(neededNext + need)) 24 | if len(need) == 0: 25 | return equ 26 | return equ 27 | 28 | def needed(eq): 29 | """ Iterates over an equation and returns a list of any missing variables 30 | """ 31 | need = [] 32 | if type(eq) is str: 33 | need.append(eq) 34 | elif type(eq) is Sexpression: 35 | for i in range(len(eq)): 36 | need += needed(eq[i]) 37 | return need 38 | 39 | 40 | def replace(var, with_equ, in_equ): 41 | out = in_equ 42 | if out == var: 43 | out = copy.deepcopy(with_equ) # Make sure we get a copy, otherwise solved equations might escape. 44 | elif type(out) is Sexpression: 45 | for i in range(len(out)): 46 | out[i] = replace(var, with_equ, out[i]) 47 | return out 48 | 49 | def execute(eq): 50 | if type(eq) is Sexpression: 51 | if type(eq[0]) is not types.FunctionType: 52 | raise Exception("Invalid s-expression doesn't start with function\n%s" % eq) 53 | for i in range(1, len(eq)): 54 | if type(eq[i]) is Sexpression: 55 | eq[i] = execute(eq[i]) 56 | for arg in eq[1:]: 57 | if type(arg) is not int: 58 | return eq 59 | return eq[0](*eq[1:]) 60 | return eq 61 | -------------------------------------------------------------------------------- /text.py: -------------------------------------------------------------------------------- 1 | class Text(object): 2 | def __init__(self, text, color=(0, 0, 0)): 3 | self.text = text 4 | self.color = color 5 | 6 | def __str__(self): 7 | return self.text 8 | 9 | def mouseIn(self, widget): 10 | self.oldColor = self.color 11 | self.color = (255, 0, 0) 12 | widget.update() 13 | 14 | def mouseOut(self, widget): 15 | self.color = self.oldColor 16 | widget.update() 17 | 18 | class Tab(object): 19 | def __init__(self, stop): 20 | self.stop = stop 21 | 22 | def __str__(self): 23 | return '' 24 | 25 | gray = (150, 150, 150) 26 | blue = (0, 60, 200) 27 | green = (0, 200, 60) 28 | orange = (255, 176, 31) 29 | 30 | --------------------------------------------------------------------------------