Coverage for MC6809/components/mc6809_base.py: 76%
333 statements
« prev ^ index » next coverage.py v7.2.1, created at 2023-03-06 19:50 +0100
« prev ^ index » next coverage.py v7.2.1, created at 2023-03-06 19:50 +0100
1#!/usr/bin/env python
3"""
4 MC6809 - 6809 CPU emulator in Python
5 =======================================
7 6809 is Big-Endian
9 Links:
10 http://dragondata.worldofdragon.org/Publications/inside-dragon.htm
11 http://www.burgins.com/m6809.html
12 http://koti.mbnet.fi/~atjs/mc6809/
14 :copyleft: 2013-2015 by the MC6809 team, see AUTHORS for more details.
15 :license: GNU GPL v3 or above, see LICENSE for more details.
17 Based on:
18 * ApplyPy by James Tauber (MIT license)
19 * XRoar emulator by Ciaran Anscomb (GPL license)
20 more info, see README
21"""
24import logging
25import sys
26import time
28from MC6809.components.cpu_utils.instruction_caller import OpCollection, opcode
29from MC6809.components.cpu_utils.MC6809_registers import (
30 ConcatenatedAccumulator,
31 UndefinedRegister,
32 ValueStorage8Bit,
33 ValueStorage16Bit,
34 convert_differend_width,
35)
36from MC6809.components.mc6809_tools import calc_new_count
37from MC6809.components.MC6809data.MC6809_op_data import (
38 REG_A,
39 REG_B,
40 REG_CC,
41 REG_D,
42 REG_DP,
43 REG_PC,
44 REG_S,
45 REG_U,
46 REG_X,
47 REG_Y,
48)
51log = logging.getLogger("MC6809")
54# HTML_TRACE = True
55HTML_TRACE = False
57undefined_reg = UndefinedRegister()
60class CPUBase:
62 SWI3_VECTOR = 0xfff2
63 SWI2_VECTOR = 0xfff4
64 FIRQ_VECTOR = 0xfff6
65 IRQ_VECTOR = 0xfff8
66 SWI_VECTOR = 0xfffa
67 NMI_VECTOR = 0xfffc
68 RESET_VECTOR = 0xfffe
70 STARTUP_BURST_COUNT = 100
71 min_burst_count = 10 # minimum outer op count per burst
72 max_burst_count = 10000 # maximum outer op count per burst
74 def __init__(self, memory, cfg):
75 self.memory = memory
76 self.memory.cpu = self # FIXME
77 self.cfg = cfg
79 self.running = True
80 self.cycles = 0
81 self.last_op_address = 0 # Store the current run opcode memory address
82 self.outer_burst_op_count = self.STARTUP_BURST_COUNT
84 # start_http_control_server(self, cfg) # TODO: Move into seperate Class
86 self.index_x = ValueStorage16Bit(REG_X, 0) # X - 16 bit index register
87 self.index_y = ValueStorage16Bit(REG_Y, 0) # Y - 16 bit index register
89 self.user_stack_pointer = ValueStorage16Bit(REG_U, 0) # U - 16 bit user-stack pointer
90 self.user_stack_pointer.counter = 0
92 # S - 16 bit system-stack pointer:
93 # Position will be set by ROM code after detection of total installed RAM
94 self.system_stack_pointer = ValueStorage16Bit(REG_S, 0)
96 # PC - 16 bit program counter register
97 self.program_counter = ValueStorage16Bit(REG_PC, 0)
99 self.accu_a = ValueStorage8Bit(REG_A, 0) # A - 8 bit accumulator
100 self.accu_b = ValueStorage8Bit(REG_B, 0) # B - 8 bit accumulator
102 # D - 16 bit concatenated reg. (A + B)
103 self.accu_d = ConcatenatedAccumulator(REG_D, self.accu_a, self.accu_b)
105 # DP - 8 bit direct page register
106 self.direct_page = ValueStorage8Bit(REG_DP, 0)
108 super().__init__()
110 self.register_str2object = {
111 REG_X: self.index_x,
112 REG_Y: self.index_y,
114 REG_U: self.user_stack_pointer,
115 REG_S: self.system_stack_pointer,
117 REG_PC: self.program_counter,
119 REG_A: self.accu_a,
120 REG_B: self.accu_b,
121 REG_D: self.accu_d,
123 REG_DP: self.direct_page,
124 REG_CC: self.cc_register,
126 undefined_reg.name: undefined_reg, # for TFR, EXG
127 }
129# log.debug("Add opcode functions:")
130 self.opcode_dict = OpCollection(self).get_opcode_dict()
132# log.debug("illegal ops: %s" % ",".join(["$%x" % c for c in ILLEGAL_OPS]))
133 # add illegal instruction
134# for opcode in ILLEGAL_OPS:
135# self.opcode_dict[opcode] = IllegalInstruction(self, opcode)
137 def get_state(self):
138 """
139 used in unittests
140 """
141 return {
142 REG_X: self.index_x.value,
143 REG_Y: self.index_y.value,
145 REG_U: self.user_stack_pointer.value,
146 REG_S: self.system_stack_pointer.value,
148 REG_PC: self.program_counter.value,
150 REG_A: self.accu_a.value,
151 REG_B: self.accu_b.value,
153 REG_DP: self.direct_page.value,
154 REG_CC: self.get_cc_value(),
156 "cycles": self.cycles,
157 "RAM": tuple(self.memory._mem) # copy of array.array() values,
158 }
160 def set_state(self, state):
161 """
162 used in unittests
163 """
164 self.index_x.set(state[REG_X])
165 self.index_y.set(state[REG_Y])
167 self.user_stack_pointer.set(state[REG_U])
168 self.system_stack_pointer.set(state[REG_S])
170 self.program_counter.set(state[REG_PC])
172 self.accu_a.set(state[REG_A])
173 self.accu_b.set(state[REG_B])
175 self.direct_page.set(state[REG_DP])
176 self.set_cc(state[REG_CC])
178 self.cycles = state["cycles"]
179 self.memory.load(address=0x0000, data=state["RAM"])
181 ####
183 def reset(self):
184 log.info("%04x| CPU reset:", self.program_counter.value)
186 self.last_op_address = 0
188 if self.cfg.__class__.__name__ == "SBC09Cfg":
189 # first op is:
190 # E400: 1AFF reset orcc #$FF ;Disable interrupts.
191 # log.debug("\tset CC register to 0xff")
192 # self.set_cc(0xff)
193 log.info("\tset CC register to 0x00")
194 self.set_cc(0x00)
195 else:
196 # log.info("\tset cc.F=1: FIRQ interrupt masked")
197 # self.F = 1
198 #
199 # log.info("\tset cc.I=1: IRQ interrupt masked")
200 # self.I = 1
202 log.info("\tset E - 0x80 - bit 7 - Entire register state stacked")
203 self.E = 1
205# log.debug("\tset PC to $%x" % self.cfg.RESET_VECTOR)
206# self.program_counter = self.cfg.RESET_VECTOR
208 log.info("\tread reset vector from $%04x", self.RESET_VECTOR)
209 ea = self.memory.read_word(self.RESET_VECTOR)
210 log.info(f"\tset PC to ${ea:04x}")
211 if ea == 0x0000:
212 log.critical("Reset vector is $%04x ??? ROM loading in the right place?!?", ea)
213 self.program_counter.set(ea)
215 ####
217 def get_and_call_next_op(self):
218 op_address, opcode = self.read_pc_byte()
219 # try:
220 self.call_instruction_func(op_address, opcode)
221 # except Exception as err:
222 # try:
223 # msg = "%s - op address: $%04x - opcode: $%02x" % (err, op_address, opcode)
224 # except TypeError: # e.g: op_address or opcode is None
225 # msg = "%s - op address: %r - opcode: %r" % (err, op_address, opcode)
226 # exception = err.__class__ # Use origin Exception class, e.g.: KeyError
227 # raise exception(msg)
229 def quit(self):
230 log.critical("CPU quit() called.")
231 self.running = False
233 def call_instruction_func(self, op_address, opcode):
234 self.last_op_address = op_address
235 try:
236 cycles, instr_func = self.opcode_dict[opcode]
237 except KeyError:
238 msg = f"${op_address:x} *** UNKNOWN OP ${opcode:x}"
239 log.error(msg)
240 sys.exit(msg)
242 instr_func(opcode)
243 self.cycles += cycles
245 ####
247 # TODO: Move to __init__
248 quickest_sync_callback_cycles = None
249 sync_callbacks_cyles = {}
250 sync_callbacks = []
252 def add_sync_callback(self, callback_cycles, callback):
253 """ Add a CPU cycle triggered callback """
254 self.sync_callbacks_cyles[callback] = 0
255 self.sync_callbacks.append([callback_cycles, callback])
256 if self.quickest_sync_callback_cycles is None or \
257 self.quickest_sync_callback_cycles > callback_cycles:
258 self.quickest_sync_callback_cycles = callback_cycles
260 def call_sync_callbacks(self):
261 """ Call every sync callback with CPU cycles trigger """
262 current_cycles = self.cycles
263 for callback_cycles, callback in self.sync_callbacks: 263 ↛ 265line 263 didn't jump to line 265, because the loop on line 263 never started
264 # get the CPU cycles count of the last call
265 last_call_cycles = self.sync_callbacks_cyles[callback]
267 if current_cycles - last_call_cycles > callback_cycles:
268 # this callback should be called
270 # Save the current cycles, to trigger the next call
271 self.sync_callbacks_cyles[callback] = self.cycles
273 # Call the callback function
274 callback(current_cycles - last_call_cycles)
276 # TODO: Move to __init__
277 inner_burst_op_count = 100 # How many ops calls, before next sync call
279 def burst_run(self):
280 """ Run CPU as fast as Python can... """
281 # https://wiki.python.org/moin/PythonSpeed/PerformanceTips#Avoiding_dots...
282 get_and_call_next_op = self.get_and_call_next_op
284 for __ in range(self.outer_burst_op_count):
285 for __ in range(self.inner_burst_op_count):
286 get_and_call_next_op()
288 self.call_sync_callbacks()
290 def run(self, max_run_time=0.1, target_cycles_per_sec=None):
291 now = time.time
293 start_time = now()
295 if target_cycles_per_sec is not None:
296 # Run CPU not faster than given speedlimit
297 self.delayed_burst_run(target_cycles_per_sec)
298 else:
299 # Run CPU as fast as Python can...
300 self.delay = 0
301 self.burst_run()
303 # Calculate the outer_burst_count new, to hit max_run_time
304 self.outer_burst_op_count = calc_new_count(
305 min_value=self.min_burst_count,
306 value=self.outer_burst_op_count,
307 max_value=self.max_burst_count,
308 trigger=now() - start_time - self.delay,
309 target=max_run_time
310 )
312 def test_run(self, start, end, max_ops=1000000):
313 # log.warning("CPU test_run(): from $%x to $%x" % (start, end))
314 self.program_counter.set(start)
315# log.debug("-"*79)
317 # https://wiki.python.org/moin/PythonSpeed/PerformanceTips#Avoiding_dots...
318 get_and_call_next_op = self.get_and_call_next_op
319 program_counter = self.program_counter
321 for __ in range(max_ops): 321 ↛ 325line 321 didn't jump to line 325, because the loop on line 321 didn't complete
322 if program_counter.value == end:
323 return
324 get_and_call_next_op()
325 log.critical("Max ops %i arrived!", max_ops)
326 raise RuntimeError(f"Max ops {max_ops:d} arrived!")
328 def test_run2(self, start, count):
329 # log.warning("CPU test_run2(): from $%x count: %i" % (start, count))
330 self.program_counter.set(start)
331# log.debug("-"*79)
333 _old_burst_count = self.outer_burst_op_count
334 self.outer_burst_op_count = count
336 _old_sync_count = self.inner_burst_op_count
337 self.inner_burst_op_count = 1
339 self.burst_run()
341 self.outer_burst_op_count = _old_burst_count
342 self.inner_burst_op_count = _old_sync_count
344 ####
346 @property
347 def get_info(self):
348 return "cc={:02x} a={:02x} b={:02x} dp={:02x} x={:04x} y={:04x} u={:04x} s={:04x}".format(
349 self.get_cc_value(),
350 self.accu_a.value, self.accu_b.value,
351 self.direct_page.value,
352 self.index_x.value, self.index_y.value,
353 self.user_stack_pointer.value, self.system_stack_pointer.value
354 )
356 ####
358 def read_pc_byte(self):
359 op_addr = self.program_counter.value
360 m = self.memory.read_byte(op_addr)
361 self.program_counter.value += 1
362# log.log(5, "read pc byte: $%02x from $%04x", m, op_addr)
363 return op_addr, m
365 def read_pc_word(self):
366 op_addr = self.program_counter.value
367 m = self.memory.read_word(op_addr)
368 self.program_counter.value += 2
369# log.log(5, "\tread pc word: $%04x from $%04x", m, op_addr)
370 return op_addr, m
372 # Op methods:
374 @opcode(
375 0x10, # PAGE 2 instructions
376 0x11, # PAGE 3 instructions
377 )
378 def instruction_PAGE(self, opcode):
379 """ call op from page 2 or 3 """
380 op_address, opcode2 = self.read_pc_byte()
381 paged_opcode = opcode * 256 + opcode2
382# log.debug("$%x *** call paged opcode $%x" % (
383# self.program_counter, paged_opcode
384# ))
385 self.call_instruction_func(op_address - 1, paged_opcode)
387 @opcode( # Add B accumulator to X (unsigned)
388 0x3a, # ABX (inherent)
389 )
390 def instruction_ABX(self, opcode):
391 """
392 Add the 8-bit unsigned value in accumulator B into index register X.
394 source code forms: ABX
396 CC bits "HNZVC": -----
397 """
398 self.index_x.increment(self.accu_b.value)
400 @opcode( # Add memory to accumulator with carry
401 0x89, 0x99, 0xa9, 0xb9, # ADCA (immediate, direct, indexed, extended)
402 0xc9, 0xd9, 0xe9, 0xf9, # ADCB (immediate, direct, indexed, extended)
403 )
404 def instruction_ADC(self, opcode, m, register):
405 """
406 Adds the contents of the C (carry) bit and the memory byte into an 8-bit
407 accumulator.
409 source code forms: ADCA P; ADCB P
411 CC bits "HNZVC": aaaaa
412 """
413 a = register.value
414 r = a + m + self.C
415 register.set(r)
416# log.debug("$%x %02x ADC %s: %i + %i + %i = %i (=$%x)" % (
417# self.program_counter, opcode, register.name,
418# a, m, self.C, r, r
419# ))
420 self.clear_HNZVC()
421 self.update_HNZVC_8(a, m, r)
423 @opcode( # Add memory to D accumulator
424 0xc3, 0xd3, 0xe3, 0xf3, # ADDD (immediate, direct, indexed, extended)
425 )
426 def instruction_ADD16(self, opcode, m, register):
427 """
428 Adds the 16-bit memory value into the 16-bit accumulator
430 source code forms: ADDD P
432 CC bits "HNZVC": -aaaa
433 """
434 assert register.WIDTH == 16
435 old = register.value
436 r = old + m
437 register.set(r)
438# log.debug("$%x %02x %02x ADD16 %s: $%02x + $%02x = $%02x" % (
439# self.program_counter, opcode, m,
440# register.name,
441# old, m, r
442# ))
443 self.clear_NZVC()
444 self.update_NZVC_16(old, m, r)
446 @opcode( # Add memory to accumulator
447 0x8b, 0x9b, 0xab, 0xbb, # ADDA (immediate, direct, indexed, extended)
448 0xcb, 0xdb, 0xeb, 0xfb, # ADDB (immediate, direct, indexed, extended)
449 )
450 def instruction_ADD8(self, opcode, m, register):
451 """
452 Adds the memory byte into an 8-bit accumulator.
454 source code forms: ADDA P; ADDB P
456 CC bits "HNZVC": aaaaa
457 """
458 assert register.WIDTH == 8
459 old = register.value
460 r = old + m
461 register.set(r)
462# log.debug("$%x %02x %02x ADD8 %s: $%02x + $%02x = $%02x" % (
463# self.program_counter, opcode, m,
464# register.name,
465# old, m, r
466# ))
467 self.clear_HNZVC()
468 self.update_HNZVC_8(old, m, r)
470 @opcode(0xf, 0x6f, 0x7f) # CLR (direct, indexed, extended)
471 def instruction_CLR_memory(self, opcode, ea):
472 """
473 Clear memory location
474 source code forms: CLR
475 CC bits "HNZVC": -0100
476 """
477 self.update_0100()
478 return ea, 0x00
480 @opcode(0x4f, 0x5f) # CLRA / CLRB (inherent)
481 def instruction_CLR_register(self, opcode, register):
482 """
483 Clear accumulator A or B
485 source code forms: CLRA; CLRB
486 CC bits "HNZVC": -0100
487 """
488 register.set(0x00)
489 self.update_0100()
491 def COM(self, value):
492 """
493 CC bits "HNZVC": -aa01
494 """
495 value = ~value # the bits of m inverted
496 self.clear_NZ()
497 self.update_NZ01_8(value)
498 return value
500 @opcode( # Complement memory location
501 0x3, 0x63, 0x73, # COM (direct, indexed, extended)
502 )
503 def instruction_COM_memory(self, opcode, ea, m):
504 """
505 Replaces the contents of memory location M with its logical complement.
506 source code forms: COM Q
507 """
508 r = self.COM(value=m)
509# log.debug("$%x COM memory $%x to $%x" % (
510# self.program_counter, m, r,
511# ))
512 return ea, r & 0xff
514 @opcode( # Complement accumulator
515 0x43, # COMA (inherent)
516 0x53, # COMB (inherent)
517 )
518 def instruction_COM_register(self, opcode, register):
519 """
520 Replaces the contents of accumulator A or B with its logical complement.
521 source code forms: COMA; COMB
522 """
523 register.set(self.COM(value=register.value))
524# log.debug("$%x COM %s" % (
525# self.program_counter, register.name,
526# ))
528 @opcode( # Decimal adjust A accumulator
529 0x19, # DAA (inherent)
530 )
531 def instruction_DAA(self, opcode):
532 """
533 The sequence of a single-byte add instruction on accumulator A (either
534 ADDA or ADCA) and a following decimal addition adjust instruction
535 results in a BCD addition with an appropriate carry bit. Both values to
536 be added must be in proper BCD form (each nibble such that: 0 <= nibble
537 <= 9). Multiple-precision addition must add the carry generated by this
538 decimal addition adjust into the next higher digit during the add
539 operation (ADCA) immediately prior to the next decimal addition adjust.
541 source code forms: DAA
543 CC bits "HNZVC": -aa0a
545 Operation:
546 ACCA' ← ACCA + CF(MSN):CF(LSN)
548 where CF is a Correction Factor, as follows:
549 the CF for each nibble (BCD) digit is determined separately,
550 and is either 6 or 0.
552 Least Significant Nibble
553 CF(LSN) = 6 IFF 1) C = 1
554 or 2) LSN > 9
556 Most Significant Nibble
557 CF(MSN) = 6 IFF 1) C = 1
558 or 2) MSN > 9
559 or 3) MSN > 8 and LSN > 9
561 Condition Codes:
562 H - Not affected.
563 N - Set if the result is negative; cleared otherwise.
564 Z - Set if the result is zero; cleared otherwise.
565 V - Undefined.
566 C - Set if a carry is generated or if the carry bit was set before the operation; cleared otherwise.
567 """
568 a = self.accu_a.value
570 correction_factor = 0
571 a_hi = a & 0xf0 # MSN - Most Significant Nibble
572 a_lo = a & 0x0f # LSN - Least Significant Nibble
574 if a_lo > 0x09 or self.H: # cc & 0x20:
575 correction_factor |= 0x06
577 if a_hi > 0x80 and a_lo > 0x09:
578 correction_factor |= 0x60
580 if a_hi > 0x90 or self.C: # cc & 0x01:
581 correction_factor |= 0x60
583 new_value = correction_factor + a
584 self.accu_a.set(new_value)
586 self.clear_NZ() # V is undefined
587 self.update_NZC_8(new_value)
589 def DEC(self, a):
590 """
591 Subtract one from the register. The carry bit is not affected, thus
592 allowing this instruction to be used as a loop counter in multiple-
593 precision computations. When operating on unsigned values, only BEQ and
594 BNE branches can be expected to behave consistently. When operating on
595 twos complement values, all signed branches are available.
597 source code forms: DEC Q; DECA; DECB
599 CC bits "HNZVC": -aaa-
600 """
601 r = a - 1
602 self.clear_NZV()
603 self.update_NZ_8(r)
604 if r == 0x7f:
605 self.V = 1
606 return r
608 @opcode(0xa, 0x6a, 0x7a) # DEC (direct, indexed, extended)
609 def instruction_DEC_memory(self, opcode, ea, m):
610 """ Decrement memory location """
611 r = self.DEC(m)
612# log.debug("$%x DEC memory value $%x -1 = $%x and write it to $%x \t| %s" % (
613# self.program_counter,
614# m, r, ea,
615# self.cfg.mem_info.get_shortest(ea)
616# ))
617 return ea, r & 0xff
619 @opcode(0x4a, 0x5a) # DECA / DECB (inherent)
620 def instruction_DEC_register(self, opcode, register):
621 """ Decrement accumulator """
622 a = register.value
623 r = self.DEC(a)
624# log.debug("$%x DEC %s value $%x -1 = $%x" % (
625# self.program_counter,
626# register.name, a, r
627# ))
628 register.set(r)
630 def INC(self, a):
631 r = a + 1
632 self.clear_NZV()
633 self.update_NZ_8(r)
634 if r == 0x80:
635 self.V = 1
636 return r
638 @opcode( # Increment accumulator
639 0x4c, # INCA (inherent)
640 0x5c, # INCB (inherent)
641 )
642 def instruction_INC_register(self, opcode, register):
643 """
644 Adds to the register. The carry bit is not affected, thus allowing this
645 instruction to be used as a loop counter in multiple-precision
646 computations. When operating on unsigned values, only the BEQ and BNE
647 branches can be expected to behave consistently. When operating on twos
648 complement values, all signed branches are correctly available.
650 source code forms: INC Q; INCA; INCB
652 CC bits "HNZVC": -aaa-
653 """
654 a = register.value
655 r = self.INC(a)
656 r = register.set(r)
658 @opcode( # Increment memory location
659 0xc, 0x6c, 0x7c, # INC (direct, indexed, extended)
660 )
661 def instruction_INC_memory(self, opcode, ea, m):
662 """
663 Adds to the register. The carry bit is not affected, thus allowing this
664 instruction to be used as a loop counter in multiple-precision
665 computations. When operating on unsigned values, only the BEQ and BNE
666 branches can be expected to behave consistently. When operating on twos
667 complement values, all signed branches are correctly available.
669 source code forms: INC Q; INCA; INCB
671 CC bits "HNZVC": -aaa-
672 """
673 r = self.INC(m)
674 return ea, r & 0xff
676 @opcode( # Load effective address into an indexable register
677 0x32, # LEAS (indexed)
678 0x33, # LEAU (indexed)
679 )
680 def instruction_LEA_pointer(self, opcode, ea, register):
681 """
682 Calculates the effective address from the indexed addressing mode and
683 places the address in an indexable register.
685 LEAU and LEAS do not affect the Z bit to allow cleaning up the stack
686 while returning the Z bit as a parameter to a calling routine, and also
687 for MC6800 INS/DES compatibility.
689 LEAU -10,U U-10 -> U Subtracts 10 from U
690 LEAS -10,S S-10 -> S Used to reserve area on stack
691 LEAS 10,S S+10 -> S Used to 'clean up' stack
692 LEAX 5,S S+5 -> X Transfers as well as adds
694 source code forms: LEAS, LEAU
696 CC bits "HNZVC": -----
697 """
698# log.debug(
699# "$%04x LEA %s: Set %s to $%04x \t| %s" % (
700# self.program_counter,
701# register.name, register.name, ea,
702# self.cfg.mem_info.get_shortest(ea)
703# ))
704 register.set(ea)
706 @opcode( # Load effective address into an indexable register
707 0x30, # LEAX (indexed)
708 0x31, # LEAY (indexed)
709 )
710 def instruction_LEA_register(self, opcode, ea, register):
711 """ see instruction_LEA_pointer
713 LEAX and LEAY affect the Z (zero) bit to allow use of these registers
714 as counters and for MC6800 INX/DEX compatibility.
716 LEAX 10,X X+10 -> X Adds 5-bit constant 10 to X
717 LEAX 500,X X+500 -> X Adds 16-bit constant 500 to X
718 LEAY A,Y Y+A -> Y Adds 8-bit accumulator to Y
719 LEAY D,Y Y+D -> Y Adds 16-bit D accumulator to Y
721 source code forms: LEAX, LEAY
723 CC bits "HNZVC": --a--
724 """
725# log.debug("$%04x LEA %s: Set %s to $%04x \t| %s" % (
726# self.program_counter,
727# register.name, register.name, ea,
728# self.cfg.mem_info.get_shortest(ea)
729# ))
730 register.set(ea)
731 self.Z = 0
732 self.set_Z16(ea)
734 @opcode( # Unsigned multiply (A * B ? D)
735 0x3d, # MUL (inherent)
736 )
737 def instruction_MUL(self, opcode):
738 """
739 Multiply the unsigned binary numbers in the accumulators and place the
740 result in both accumulators (ACCA contains the most-significant byte of
741 the result). Unsigned multiply allows multiple-precision operations.
743 The C (carry) bit allows rounding the most-significant byte through the
744 sequence: MUL, ADCA #0.
746 source code forms: MUL
748 CC bits "HNZVC": --a-a
749 """
750 r = self.accu_a.value * self.accu_b.value
751 self.accu_d.set(r)
752 self.Z = 1 if r == 0 else 0
753 self.C = 1 if r & 0x80 else 0
755 @opcode( # Negate accumulator
756 0x40, # NEGA (inherent)
757 0x50, # NEGB (inherent)
758 )
759 def instruction_NEG_register(self, opcode, register):
760 """
761 Replaces the register with its twos complement. The C (carry) bit
762 represents a borrow and is set to the inverse of the resulting binary
763 carry. Note that 80 16 is replaced by itself and only in this case is
764 the V (overflow) bit set. The value 00 16 is also replaced by itself,
765 and only in this case is the C (carry) bit cleared.
767 source code forms: NEG Q; NEGA; NEG B
769 CC bits "HNZVC": uaaaa
770 """
771 x = register.value
772 r = x * -1 # same as: r = ~x + 1
773 register.set(r)
774# log.debug("$%04x NEG %s $%02x to $%02x" % (
775# self.program_counter, register.name, x, r,
776# ))
777 self.clear_NZVC()
778 self.update_NZVC_8(0, x, r)
780 _wrong_NEG = 0
782 @opcode(0x0, 0x60, 0x70) # NEG (direct, indexed, extended)
783 def instruction_NEG_memory(self, opcode, ea, m):
784 """ Negate memory """
785 if opcode == 0x0 and ea == 0x0 and m == 0x0: 785 ↛ 786line 785 didn't jump to line 786, because the condition on line 785 was never true
786 self._wrong_NEG += 1
787 if self._wrong_NEG > 10:
788 raise RuntimeError("Wrong PC ???")
789 else:
790 self._wrong_NEG = 0
792 r = m * -1 # same as: r = ~m + 1
794# log.debug("$%04x NEG $%02x from %04x to $%02x" % (
795# self.program_counter, m, ea, r,
796# ))
797 self.clear_NZVC()
798 self.update_NZVC_8(0, m, r)
799 return ea, r & 0xff
801 @opcode(0x12) # NOP (inherent)
802 def instruction_NOP(self, opcode):
803 """
804 No operation
806 source code forms: NOP
808 CC bits "HNZVC": -----
809 """
810# log.debug("\tNOP")
812 @opcode( # Subtract memory from accumulator with borrow
813 0x82, 0x92, 0xa2, 0xb2, # SBCA (immediate, direct, indexed, extended)
814 0xc2, 0xd2, 0xe2, 0xf2, # SBCB (immediate, direct, indexed, extended)
815 )
816 def instruction_SBC(self, opcode, m, register):
817 """
818 Subtracts the contents of memory location M and the borrow (in the C
819 (carry) bit) from the contents of the designated 8-bit register, and
820 places the result in that register. The C bit represents a borrow and is
821 set to the inverse of the resulting binary carry.
823 source code forms: SBCA P; SBCB P
825 CC bits "HNZVC": uaaaa
826 """
827 a = register.value
828 r = a - m - self.C
829 register.set(r)
830# log.debug("$%x %02x SBC %s: %i - %i - %i = %i (=$%x)" % (
831# self.program_counter, opcode, register.name,
832# a, m, self.C, r, r
833# ))
834 self.clear_NZVC()
835 self.update_NZVC_8(a, m, r)
837 @opcode( # Sign Extend B accumulator into A accumulator
838 0x1d, # SEX (inherent)
839 )
840 def instruction_SEX(self, opcode):
841 """
842 This instruction transforms a twos complement 8-bit value in accumulator
843 B into a twos complement 16-bit value in the D accumulator.
845 source code forms: SEX
847 CC bits "HNZVC": -aa0-
849 // 0x1d SEX inherent
850 case 0x1d:
851 WREG_A = (RREG_B & 0x80) ? 0xff : 0;
852 CLR_NZ;
853 SET_NZ16(REG_D);
854 peek_byte(cpu, REG_PC);
856 #define SIGNED(b) ((Word)(b&0x80?b|0xff00:b))
857 case 0x1D: /* SEX */ tw=SIGNED(ibreg); SETNZ16(tw) SETDREG(tw) break;
858 """
859 b = self.accu_b.value
860 if b & 0x80 == 0:
861 self.accu_a.set(0x00)
863 d = self.accu_d.value
865# log.debug("SEX: b=$%x ; $%x&0x80=$%x ; d=$%x", b, b, (b & 0x80), d)
867 self.clear_NZ()
868 self.update_NZ_16(d)
870 @opcode( # Subtract memory from accumulator
871 0x80, 0x90, 0xa0, 0xb0, # SUBA (immediate, direct, indexed, extended)
872 0xc0, 0xd0, 0xe0, 0xf0, # SUBB (immediate, direct, indexed, extended)
873 0x83, 0x93, 0xa3, 0xb3, # SUBD (immediate, direct, indexed, extended)
874 )
875 def instruction_SUB(self, opcode, m, register):
876 """
877 Subtracts the value in memory location M from the contents of a
878 register. The C (carry) bit represents a borrow and is set to the
879 inverse of the resulting binary carry.
881 source code forms: SUBA P; SUBB P; SUBD P
883 CC bits "HNZVC": uaaaa
884 """
885 r = register.value
886 r_new = r - m
887 register.set(r_new)
888# log.debug("$%x SUB8 %s: $%x - $%x = $%x (dez.: %i - %i = %i)" % (
889# self.program_counter, register.name,
890# r, m, r_new,
891# r, m, r_new,
892# ))
893 self.clear_NZVC()
894 if register.WIDTH == 8:
895 self.update_NZVC_8(r, m, r_new)
896 else:
897 assert register.WIDTH == 16
898 self.update_NZVC_16(r, m, r_new)
900 # ---- Register Changes - FIXME: Better name for this section?!? ----
902 REGISTER_BIT2STR = {
903 0x0: REG_D, # 0000 - 16 bit concatenated reg.(A B)
904 0x1: REG_X, # 0001 - 16 bit index register
905 0x2: REG_Y, # 0010 - 16 bit index register
906 0x3: REG_U, # 0011 - 16 bit user-stack pointer
907 0x4: REG_S, # 0100 - 16 bit system-stack pointer
908 0x5: REG_PC, # 0101 - 16 bit program counter register
909 0x6: undefined_reg.name, # undefined
910 0x7: undefined_reg.name, # undefined
911 0x8: REG_A, # 1000 - 8 bit accumulator
912 0x9: REG_B, # 1001 - 8 bit accumulator
913 0xa: REG_CC, # 1010 - 8 bit condition code register as flags
914 0xb: REG_DP, # 1011 - 8 bit direct page register
915 0xc: undefined_reg.name, # undefined
916 0xd: undefined_reg.name, # undefined
917 0xe: undefined_reg.name, # undefined
918 0xf: undefined_reg.name, # undefined
919 }
921 def _get_register_obj(self, addr):
922 addr_str = self.REGISTER_BIT2STR[addr]
923 reg_obj = self.register_str2object[addr_str]
924# log.debug("get register obj: addr: $%x addr_str: %s -> register: %s" % (
925# addr, addr_str, reg_obj.name
926# ))
927# log.debug(repr(self.register_str2object))
928 return reg_obj
930 def _get_register_and_value(self, addr):
931 reg = self._get_register_obj(addr)
932 reg_value = reg.value
933 return reg, reg_value
935 @opcode(0x1f) # TFR (immediate)
936 def instruction_TFR(self, opcode, m):
937 """
938 source code forms: TFR R1, R2
939 CC bits "HNZVC": ccccc
940 """
941 high, low = divmod(m, 16)
942 dst_reg = self._get_register_obj(low)
943 src_reg = self._get_register_obj(high)
944 src_value = convert_differend_width(src_reg, dst_reg)
945 dst_reg.set(src_value)
946# log.debug("\tTFR: Set %s to $%x from %s",
947# dst_reg, src_value, src_reg.name
948# )
950 @opcode( # Exchange R1 with R2
951 0x1e, # EXG (immediate)
952 )
953 def instruction_EXG(self, opcode, m):
954 """
955 source code forms: EXG R1,R2
956 CC bits "HNZVC": ccccc
957 """
958 high, low = divmod(m, 0x10)
959 reg1 = self._get_register_obj(high)
960 reg2 = self._get_register_obj(low)
962 new_reg1_value = convert_differend_width(reg2, reg1)
963 new_reg2_value = convert_differend_width(reg1, reg2)
965 reg1.set(new_reg1_value)
966 reg2.set(new_reg2_value)
968# log.debug("\tEXG: %s($%x) <-> %s($%x)",
969# reg1.name, reg1_value, reg2.name, reg2_value
970# )