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

1#!/usr/bin/env python 

2 

3""" 

4 MC6809 - 6809 CPU emulator in Python 

5 ======================================= 

6 

7 6809 is Big-Endian 

8 

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/ 

13 

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. 

16 

17 Based on: 

18 * ApplyPy by James Tauber (MIT license) 

19 * XRoar emulator by Ciaran Anscomb (GPL license) 

20 more info, see README 

21""" 

22 

23 

24import logging 

25import sys 

26import time 

27 

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) 

49 

50 

51log = logging.getLogger("MC6809") 

52 

53 

54# HTML_TRACE = True 

55HTML_TRACE = False 

56 

57undefined_reg = UndefinedRegister() 

58 

59 

60class CPUBase: 

61 

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 

69 

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 

73 

74 def __init__(self, memory, cfg): 

75 self.memory = memory 

76 self.memory.cpu = self # FIXME 

77 self.cfg = cfg 

78 

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 

83 

84 # start_http_control_server(self, cfg) # TODO: Move into seperate Class 

85 

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 

88 

89 self.user_stack_pointer = ValueStorage16Bit(REG_U, 0) # U - 16 bit user-stack pointer 

90 self.user_stack_pointer.counter = 0 

91 

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) 

95 

96 # PC - 16 bit program counter register 

97 self.program_counter = ValueStorage16Bit(REG_PC, 0) 

98 

99 self.accu_a = ValueStorage8Bit(REG_A, 0) # A - 8 bit accumulator 

100 self.accu_b = ValueStorage8Bit(REG_B, 0) # B - 8 bit accumulator 

101 

102 # D - 16 bit concatenated reg. (A + B) 

103 self.accu_d = ConcatenatedAccumulator(REG_D, self.accu_a, self.accu_b) 

104 

105 # DP - 8 bit direct page register 

106 self.direct_page = ValueStorage8Bit(REG_DP, 0) 

107 

108 super().__init__() 

109 

110 self.register_str2object = { 

111 REG_X: self.index_x, 

112 REG_Y: self.index_y, 

113 

114 REG_U: self.user_stack_pointer, 

115 REG_S: self.system_stack_pointer, 

116 

117 REG_PC: self.program_counter, 

118 

119 REG_A: self.accu_a, 

120 REG_B: self.accu_b, 

121 REG_D: self.accu_d, 

122 

123 REG_DP: self.direct_page, 

124 REG_CC: self.cc_register, 

125 

126 undefined_reg.name: undefined_reg, # for TFR, EXG 

127 } 

128 

129# log.debug("Add opcode functions:") 

130 self.opcode_dict = OpCollection(self).get_opcode_dict() 

131 

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) 

136 

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, 

144 

145 REG_U: self.user_stack_pointer.value, 

146 REG_S: self.system_stack_pointer.value, 

147 

148 REG_PC: self.program_counter.value, 

149 

150 REG_A: self.accu_a.value, 

151 REG_B: self.accu_b.value, 

152 

153 REG_DP: self.direct_page.value, 

154 REG_CC: self.get_cc_value(), 

155 

156 "cycles": self.cycles, 

157 "RAM": tuple(self.memory._mem) # copy of array.array() values, 

158 } 

159 

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]) 

166 

167 self.user_stack_pointer.set(state[REG_U]) 

168 self.system_stack_pointer.set(state[REG_S]) 

169 

170 self.program_counter.set(state[REG_PC]) 

171 

172 self.accu_a.set(state[REG_A]) 

173 self.accu_b.set(state[REG_B]) 

174 

175 self.direct_page.set(state[REG_DP]) 

176 self.set_cc(state[REG_CC]) 

177 

178 self.cycles = state["cycles"] 

179 self.memory.load(address=0x0000, data=state["RAM"]) 

180 

181 #### 

182 

183 def reset(self): 

184 log.info("%04x| CPU reset:", self.program_counter.value) 

185 

186 self.last_op_address = 0 

187 

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 

201 

202 log.info("\tset E - 0x80 - bit 7 - Entire register state stacked") 

203 self.E = 1 

204 

205# log.debug("\tset PC to $%x" % self.cfg.RESET_VECTOR) 

206# self.program_counter = self.cfg.RESET_VECTOR 

207 

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) 

214 

215 #### 

216 

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) 

228 

229 def quit(self): 

230 log.critical("CPU quit() called.") 

231 self.running = False 

232 

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) 

241 

242 instr_func(opcode) 

243 self.cycles += cycles 

244 

245 #### 

246 

247 # TODO: Move to __init__ 

248 quickest_sync_callback_cycles = None 

249 sync_callbacks_cyles = {} 

250 sync_callbacks = [] 

251 

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 

259 

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] 

266 

267 if current_cycles - last_call_cycles > callback_cycles: 

268 # this callback should be called 

269 

270 # Save the current cycles, to trigger the next call 

271 self.sync_callbacks_cyles[callback] = self.cycles 

272 

273 # Call the callback function 

274 callback(current_cycles - last_call_cycles) 

275 

276 # TODO: Move to __init__ 

277 inner_burst_op_count = 100 # How many ops calls, before next sync call 

278 

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 

283 

284 for __ in range(self.outer_burst_op_count): 

285 for __ in range(self.inner_burst_op_count): 

286 get_and_call_next_op() 

287 

288 self.call_sync_callbacks() 

289 

290 def run(self, max_run_time=0.1, target_cycles_per_sec=None): 

291 now = time.time 

292 

293 start_time = now() 

294 

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() 

302 

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 ) 

311 

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) 

316 

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 

320 

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!") 

327 

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) 

332 

333 _old_burst_count = self.outer_burst_op_count 

334 self.outer_burst_op_count = count 

335 

336 _old_sync_count = self.inner_burst_op_count 

337 self.inner_burst_op_count = 1 

338 

339 self.burst_run() 

340 

341 self.outer_burst_op_count = _old_burst_count 

342 self.inner_burst_op_count = _old_sync_count 

343 

344 #### 

345 

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 ) 

355 

356 #### 

357 

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 

364 

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 

371 

372 # Op methods: 

373 

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) 

386 

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. 

393 

394 source code forms: ABX 

395 

396 CC bits "HNZVC": ----- 

397 """ 

398 self.index_x.increment(self.accu_b.value) 

399 

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. 

408 

409 source code forms: ADCA P; ADCB P 

410 

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) 

422 

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 

429 

430 source code forms: ADDD P 

431 

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) 

445 

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. 

453 

454 source code forms: ADDA P; ADDB P 

455 

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) 

469 

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 

479 

480 @opcode(0x4f, 0x5f) # CLRA / CLRB (inherent) 

481 def instruction_CLR_register(self, opcode, register): 

482 """ 

483 Clear accumulator A or B 

484 

485 source code forms: CLRA; CLRB 

486 CC bits "HNZVC": -0100 

487 """ 

488 register.set(0x00) 

489 self.update_0100() 

490 

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 

499 

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 

513 

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# )) 

527 

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. 

540 

541 source code forms: DAA 

542 

543 CC bits "HNZVC": -aa0a 

544 

545 Operation: 

546 ACCA' ← ACCA + CF(MSN):CF(LSN) 

547 

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. 

551 

552 Least Significant Nibble 

553 CF(LSN) = 6 IFF 1) C = 1 

554 or 2) LSN > 9 

555 

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 

560 

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 

569 

570 correction_factor = 0 

571 a_hi = a & 0xf0 # MSN - Most Significant Nibble 

572 a_lo = a & 0x0f # LSN - Least Significant Nibble 

573 

574 if a_lo > 0x09 or self.H: # cc & 0x20: 

575 correction_factor |= 0x06 

576 

577 if a_hi > 0x80 and a_lo > 0x09: 

578 correction_factor |= 0x60 

579 

580 if a_hi > 0x90 or self.C: # cc & 0x01: 

581 correction_factor |= 0x60 

582 

583 new_value = correction_factor + a 

584 self.accu_a.set(new_value) 

585 

586 self.clear_NZ() # V is undefined 

587 self.update_NZC_8(new_value) 

588 

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. 

596 

597 source code forms: DEC Q; DECA; DECB 

598 

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 

607 

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 

618 

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) 

629 

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 

637 

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. 

649 

650 source code forms: INC Q; INCA; INCB 

651 

652 CC bits "HNZVC": -aaa- 

653 """ 

654 a = register.value 

655 r = self.INC(a) 

656 r = register.set(r) 

657 

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. 

668 

669 source code forms: INC Q; INCA; INCB 

670 

671 CC bits "HNZVC": -aaa- 

672 """ 

673 r = self.INC(m) 

674 return ea, r & 0xff 

675 

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. 

684 

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. 

688 

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 

693 

694 source code forms: LEAS, LEAU 

695 

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) 

705 

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 

712 

713 LEAX and LEAY affect the Z (zero) bit to allow use of these registers 

714 as counters and for MC6800 INX/DEX compatibility. 

715 

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 

720 

721 source code forms: LEAX, LEAY 

722 

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) 

733 

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. 

742 

743 The C (carry) bit allows rounding the most-significant byte through the 

744 sequence: MUL, ADCA #0. 

745 

746 source code forms: MUL 

747 

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 

754 

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. 

766 

767 source code forms: NEG Q; NEGA; NEG B 

768 

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) 

779 

780 _wrong_NEG = 0 

781 

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 

791 

792 r = m * -1 # same as: r = ~m + 1 

793 

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 

800 

801 @opcode(0x12) # NOP (inherent) 

802 def instruction_NOP(self, opcode): 

803 """ 

804 No operation 

805 

806 source code forms: NOP 

807 

808 CC bits "HNZVC": ----- 

809 """ 

810# log.debug("\tNOP") 

811 

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. 

822 

823 source code forms: SBCA P; SBCB P 

824 

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) 

836 

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. 

844 

845 source code forms: SEX 

846 

847 CC bits "HNZVC": -aa0- 

848 

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); 

855 

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) 

862 

863 d = self.accu_d.value 

864 

865# log.debug("SEX: b=$%x ; $%x&0x80=$%x ; d=$%x", b, b, (b & 0x80), d) 

866 

867 self.clear_NZ() 

868 self.update_NZ_16(d) 

869 

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. 

880 

881 source code forms: SUBA P; SUBB P; SUBD P 

882 

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) 

899 

900 # ---- Register Changes - FIXME: Better name for this section?!? ---- 

901 

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 } 

920 

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 

929 

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 

934 

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# ) 

949 

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) 

961 

962 new_reg1_value = convert_differend_width(reg2, reg1) 

963 new_reg2_value = convert_differend_width(reg1, reg2) 

964 

965 reg1.set(new_reg1_value) 

966 reg2.set(new_reg2_value) 

967 

968# log.debug("\tEXG: %s($%x) <-> %s($%x)", 

969# reg1.name, reg1_value, reg2.name, reg2_value 

970# )