teek 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +21 -0
  3. data/Rakefile +121 -22
  4. data/ext/teek/extconf.rb +19 -1
  5. data/ext/teek/tcltkbridge.c +44 -2
  6. data/ext/teek/tcltkbridge.h +3 -0
  7. data/ext/teek/tkdrop.c +66 -0
  8. data/ext/teek/tkdrop.h +26 -0
  9. data/ext/teek/tkdrop_macos.m +141 -0
  10. data/ext/teek/tkdrop_win.c +232 -0
  11. data/ext/teek/tkdrop_x11.c +337 -0
  12. data/ext/teek/tkwin.c +42 -0
  13. data/lib/teek/platform.rb +29 -0
  14. data/lib/teek/version.rb +1 -1
  15. data/lib/teek.rb +248 -7
  16. data/teek.gemspec +3 -2
  17. metadata +7 -45
  18. data/sample/calculator.rb +0 -255
  19. data/sample/debug_demo.rb +0 -43
  20. data/sample/goldberg.rb +0 -1803
  21. data/sample/goldberg_helpers.rb +0 -170
  22. data/sample/minesweeper/assets/MINESWEEPER_0.png +0 -0
  23. data/sample/minesweeper/assets/MINESWEEPER_1.png +0 -0
  24. data/sample/minesweeper/assets/MINESWEEPER_2.png +0 -0
  25. data/sample/minesweeper/assets/MINESWEEPER_3.png +0 -0
  26. data/sample/minesweeper/assets/MINESWEEPER_4.png +0 -0
  27. data/sample/minesweeper/assets/MINESWEEPER_5.png +0 -0
  28. data/sample/minesweeper/assets/MINESWEEPER_6.png +0 -0
  29. data/sample/minesweeper/assets/MINESWEEPER_7.png +0 -0
  30. data/sample/minesweeper/assets/MINESWEEPER_8.png +0 -0
  31. data/sample/minesweeper/assets/MINESWEEPER_F.png +0 -0
  32. data/sample/minesweeper/assets/MINESWEEPER_M.png +0 -0
  33. data/sample/minesweeper/assets/MINESWEEPER_X.png +0 -0
  34. data/sample/minesweeper/minesweeper.rb +0 -452
  35. data/sample/optcarrot/vendor/optcarrot/apu.rb +0 -856
  36. data/sample/optcarrot/vendor/optcarrot/config.rb +0 -257
  37. data/sample/optcarrot/vendor/optcarrot/cpu.rb +0 -1162
  38. data/sample/optcarrot/vendor/optcarrot/driver.rb +0 -144
  39. data/sample/optcarrot/vendor/optcarrot/mapper/cnrom.rb +0 -14
  40. data/sample/optcarrot/vendor/optcarrot/mapper/mmc1.rb +0 -105
  41. data/sample/optcarrot/vendor/optcarrot/mapper/mmc3.rb +0 -153
  42. data/sample/optcarrot/vendor/optcarrot/mapper/uxrom.rb +0 -14
  43. data/sample/optcarrot/vendor/optcarrot/nes.rb +0 -105
  44. data/sample/optcarrot/vendor/optcarrot/opt.rb +0 -168
  45. data/sample/optcarrot/vendor/optcarrot/pad.rb +0 -92
  46. data/sample/optcarrot/vendor/optcarrot/palette.rb +0 -65
  47. data/sample/optcarrot/vendor/optcarrot/ppu.rb +0 -1468
  48. data/sample/optcarrot/vendor/optcarrot/rom.rb +0 -143
  49. data/sample/optcarrot/vendor/optcarrot.rb +0 -14
  50. data/sample/optcarrot.rb +0 -354
  51. data/sample/paint/assets/bucket.png +0 -0
  52. data/sample/paint/assets/cursor.png +0 -0
  53. data/sample/paint/assets/eraser.png +0 -0
  54. data/sample/paint/assets/pencil.png +0 -0
  55. data/sample/paint/assets/spray.png +0 -0
  56. data/sample/paint/layer.rb +0 -255
  57. data/sample/paint/layer_manager.rb +0 -179
  58. data/sample/paint/paint_demo.rb +0 -837
  59. data/sample/paint/sparse_pixel_buffer.rb +0 -202
  60. data/sample/sdl2_demo.rb +0 -318
  61. data/sample/threading_demo.rb +0 -494
@@ -1,1162 +0,0 @@
1
- require_relative "opt"
2
-
3
- module Optcarrot
4
- # CPU implementation
5
- class CPU
6
- NMI_VECTOR = 0xfffa
7
- RESET_VECTOR = 0xfffc
8
- IRQ_VECTOR = 0xfffe
9
-
10
- IRQ_EXT = 0x01
11
- IRQ_FRAME = 0x40
12
- IRQ_DMC = 0x80
13
-
14
- CLK_1, CLK_2, CLK_3, CLK_4, CLK_5, CLK_6, CLK_7, CLK_8 = (1..8).map {|i| i * RP2A03_CC }
15
-
16
- def inspect
17
- "#<#{ self.class }>"
18
- end
19
-
20
- ###########################################################################
21
- # initialization
22
-
23
- def initialize(conf)
24
- @conf = conf
25
-
26
- # load the generated core
27
- if @conf.load_cpu
28
- eval(File.read(@conf.load_cpu))
29
- elsif @conf.opt_cpu
30
- eval(OptimizedCodeBuilder.new(@conf.loglevel, @conf.opt_cpu).build, nil, "(generated CPU core)")
31
- end
32
-
33
- # main memory
34
- @fetch = [nil] * 0x10000
35
- @store = [nil] * 0x10000
36
- @peeks = {}
37
- @pokes = {}
38
- @ram = [0] * 0x800
39
-
40
- # clock management
41
- @clk = 0 # the current clock
42
- @clk_frame = 0 # the next frame clock
43
- @clk_target = 0 # the goal clock for the current CPU#run
44
- @clk_nmi = FOREVER_CLOCK # the next NMI clock (FOREVER_CLOCK means "not scheduled")
45
- @clk_irq = FOREVER_CLOCK # the next IRQ clock
46
- @clk_total = 0 # the total elapsed clocks
47
-
48
- # interrupt
49
- @irq_flags = 0
50
- @jammed = false
51
-
52
- @poke_nop = CPU.method(:poke_nop)
53
-
54
- reset
55
-
56
- # temporary store (valid only during each operation)
57
- @addr = @data = 0
58
-
59
- @opcode = nil
60
- @ppu_sync = false
61
- end
62
-
63
- def reset
64
- # registers
65
- @_a = @_x = @_y = 0
66
- @_sp = 0xfd
67
- @_pc = 0xfffc
68
-
69
- # P register
70
- @_p_nz = 1
71
- @_p_c = 0
72
- @_p_v = 0
73
- @_p_i = 0x04
74
- @_p_d = 0
75
-
76
- # reset clocks
77
- @clk = @clk_total = 0
78
-
79
- # reset RAM
80
- @ram.fill(0xff)
81
-
82
- # memory mappings by self
83
- add_mappings(0x0000..0x07ff, @ram, @ram.method(:[]=))
84
- add_mappings(0x0800..0x1fff, method(:peek_ram), method(:poke_ram))
85
- add_mappings(0x2000..0xffff, method(:peek_nop), nil)
86
- add_mappings(0xfffc, method(:peek_jam_1), nil)
87
- add_mappings(0xfffd, method(:peek_jam_2), nil)
88
- end
89
-
90
- def peek_ram(addr)
91
- @ram[addr % 0x0800]
92
- end
93
-
94
- def poke_ram(addr, data)
95
- @ram[addr % 0x0800] = data
96
- end
97
-
98
- def peek_nop(addr)
99
- addr >> 8
100
- end
101
-
102
- def peek_jam_1(_addr)
103
- @_pc = (@_pc - 1) & 0xffff
104
- 0xfc
105
- end
106
-
107
- def peek_jam_2(_addr)
108
- 0xff
109
- end
110
-
111
- ###########################################################################
112
- # mapped memory API
113
-
114
- def add_mappings(addr, peek, poke)
115
- # filter the logically equivalent objects
116
- peek = @peeks[peek] ||= peek
117
- poke = @pokes[poke] ||= poke
118
-
119
- (addr.is_a?(Integer) ? [addr] : addr).each do |a|
120
- @fetch[a] = peek
121
- @store[a] = poke || @poke_nop
122
- end
123
- end
124
-
125
- def self.poke_nop(_addr, _data)
126
- end
127
-
128
- def fetch(addr)
129
- @fetch[addr][addr]
130
- end
131
-
132
- def store(addr, value)
133
- @store[addr][addr, value]
134
- end
135
-
136
- def peek16(addr)
137
- @fetch[addr][addr] + (@fetch[addr + 1][addr + 1] << 8)
138
- end
139
-
140
- ###########################################################################
141
- # other APIs
142
-
143
- attr_reader :ram
144
- attr_writer :apu, :ppu, :ppu_sync
145
-
146
- def current_clock
147
- @clk
148
- end
149
-
150
- def next_frame_clock
151
- @clk_frame
152
- end
153
-
154
- def next_frame_clock=(clk)
155
- @clk_frame = clk
156
- @clk_target = clk if clk < @clk_target
157
- end
158
-
159
- def steal_clocks(clk)
160
- @clk += clk
161
- end
162
-
163
- def odd_clock?
164
- (@clk_total + @clk) % CLK_2 != 0
165
- end
166
-
167
- def update
168
- @apu.clock_dma(@clk)
169
- @clk
170
- end
171
-
172
- def dmc_dma(addr)
173
- # This is inaccurate; it must steal *up to* 4 clocks depending upon
174
- # whether CPU writes in this clock, but this always steals 4 clocks.
175
- @clk += CLK_3
176
- dma_buffer = fetch(addr)
177
- @clk += CLK_1
178
- dma_buffer
179
- end
180
-
181
- def sprite_dma(addr, sp_ram)
182
- 256.times {|i| sp_ram[i] = @ram[addr + i] }
183
- 64.times {|i| sp_ram[i * 4 + 2] &= 0xe3 }
184
- end
185
-
186
- def boot
187
- @clk = CLK_7
188
- @_pc = peek16(RESET_VECTOR)
189
- end
190
-
191
- def vsync
192
- @ppu.sync(@clk) if @ppu_sync
193
-
194
- @clk -= @clk_frame
195
- @clk_total += @clk_frame
196
-
197
- @clk_nmi -= @clk_frame if @clk_nmi != FOREVER_CLOCK
198
- @clk_irq -= @clk_frame if @clk_irq != FOREVER_CLOCK
199
- @clk_irq = 0 if @clk_irq < 0
200
- end
201
-
202
- ###########################################################################
203
- # interrupts
204
-
205
- def clear_irq(line)
206
- old_irq_flags = @irq_flags & (IRQ_FRAME | IRQ_DMC)
207
- @irq_flags &= line ^ (IRQ_EXT | IRQ_FRAME | IRQ_DMC)
208
- @clk_irq = FOREVER_CLOCK if @irq_flags == 0
209
- old_irq_flags
210
- end
211
-
212
- def next_interrupt_clock(clk)
213
- clk += CLK_1 + CLK_1 / 2 # interrupt edge
214
- @clk_target = clk if @clk_target > clk
215
- clk
216
- end
217
-
218
- def do_irq(line, clk)
219
- @irq_flags |= line
220
- @clk_irq = next_interrupt_clock(clk) if @clk_irq == FOREVER_CLOCK && @_p_i == 0
221
- end
222
-
223
- def do_nmi(clk)
224
- @clk_nmi = next_interrupt_clock(clk) if @clk_nmi == FOREVER_CLOCK
225
- end
226
-
227
- def do_isr(vector)
228
- return if @jammed
229
- push16(@_pc)
230
- push8(flags_pack)
231
- @_p_i = 0x04
232
- @clk += CLK_7
233
- addr = vector == NMI_VECTOR ? NMI_VECTOR : fetch_irq_isr_vector
234
- @_pc = peek16(addr)
235
- end
236
-
237
- def fetch_irq_isr_vector
238
- fetch(0x3000) if @clk >= @clk_frame
239
- if @clk_nmi != FOREVER_CLOCK
240
- if @clk_nmi + CLK_2 <= @clk
241
- @clk_nmi = FOREVER_CLOCK
242
- return NMI_VECTOR
243
- end
244
- @clk_nmi = @clk + 1
245
- end
246
- return IRQ_VECTOR
247
- end
248
-
249
- ###########################################################################
250
- # instruction helpers
251
-
252
- ### P regeister ###
253
-
254
- def flags_pack
255
- # NVssDIZC
256
- ((@_p_nz | @_p_nz >> 1) & 0x80) | # N: Negative
257
- (@_p_nz & 0xff != 0 ? 0 : 2) | # Z: Zero
258
- @_p_c | # C: Carry
259
- (@_p_v != 0 ? 0x40 : 0) | # V: Overflow
260
- @_p_i | # I: Inerrupt
261
- @_p_d | # D: Decimal
262
- 0x20
263
- end
264
-
265
- def flags_unpack(f)
266
- @_p_nz = (~f & 2) | ((f & 0x80) << 1)
267
- @_p_c = f & 0x01
268
- @_p_v = f & 0x40
269
- @_p_i = f & 0x04
270
- @_p_d = f & 0x08
271
- end
272
-
273
- ### branch helper ###
274
- def branch(cond)
275
- if cond
276
- tmp = @_pc + 1
277
- rel = fetch(@_pc)
278
- @_pc = (tmp + (rel < 128 ? rel : rel | 0xff00)) & 0xffff
279
- @clk += tmp[8] == @_pc[8] ? CLK_3 : CLK_4
280
- else
281
- @_pc += 1
282
- @clk += CLK_2
283
- end
284
- end
285
-
286
- ### storers ###
287
- def store_mem
288
- store(@addr, @data)
289
- @clk += CLK_1
290
- end
291
-
292
- def store_zpg
293
- @ram[@addr] = @data
294
- end
295
-
296
- ### stack management ###
297
- def push8(data)
298
- @ram[0x0100 + @_sp] = data
299
- @_sp = (@_sp - 1) & 0xff
300
- end
301
-
302
- def push16(data)
303
- push8(data >> 8)
304
- push8(data & 0xff)
305
- end
306
-
307
- def pull8
308
- @_sp = (@_sp + 1) & 0xff
309
- @ram[0x0100 + @_sp]
310
- end
311
-
312
- def pull16
313
- pull8 + 256 * pull8
314
- end
315
-
316
- ###########################################################################
317
- # addressing modes
318
-
319
- # immediate addressing (read only)
320
- def imm(_read, _write)
321
- @data = fetch(@_pc)
322
- @_pc += 1
323
- @clk += CLK_2
324
- end
325
-
326
- # zero-page addressing
327
- def zpg(read, write)
328
- @addr = fetch(@_pc)
329
- @_pc += 1
330
- @clk += CLK_3
331
- if read
332
- @data = @ram[@addr]
333
- @clk += CLK_2 if write
334
- end
335
- end
336
-
337
- # zero-page indexed addressing
338
- def zpg_reg(indexed, read, write)
339
- @addr = (indexed + fetch(@_pc)) & 0xff
340
- @_pc += 1
341
- @clk += CLK_4
342
- if read
343
- @data = @ram[@addr]
344
- @clk += CLK_2 if write
345
- end
346
- end
347
-
348
- def zpg_x(read, write)
349
- zpg_reg(@_x, read, write)
350
- end
351
-
352
- def zpg_y(read, write)
353
- zpg_reg(@_y, read, write)
354
- end
355
-
356
- # absolute addressing
357
- def abs(read, write)
358
- @addr = peek16(@_pc)
359
- @_pc += 2
360
- @clk += CLK_3
361
- read_write(read, write)
362
- end
363
-
364
- # absolute indexed addressing
365
- def abs_reg(indexed, read, write)
366
- addr = @_pc + 1
367
- i = indexed + fetch(@_pc)
368
- @addr = ((fetch(addr) << 8) + i) & 0xffff
369
- if write
370
- addr = (@addr - (i & 0x100)) & 0xffff
371
- fetch(addr)
372
- @clk += CLK_4
373
- else
374
- @clk += CLK_3
375
- if i & 0x100 != 0
376
- addr = (@addr - 0x100) & 0xffff # for inlining fetch
377
- fetch(addr)
378
- @clk += CLK_1
379
- end
380
- end
381
- read_write(read, write)
382
- @_pc += 2
383
- end
384
-
385
- def abs_x(read, write)
386
- abs_reg(@_x, read, write)
387
- end
388
-
389
- def abs_y(read, write)
390
- abs_reg(@_y, read, write)
391
- end
392
-
393
- # indexed indirect addressing
394
- def ind_x(read, write)
395
- addr = fetch(@_pc) + @_x
396
- @_pc += 1
397
- @clk += CLK_5
398
- @addr = @ram[addr & 0xff] | @ram[(addr + 1) & 0xff] << 8
399
- read_write(read, write)
400
- end
401
-
402
- # indirect indexed addressing
403
- def ind_y(read, write)
404
- addr = fetch(@_pc)
405
- @_pc += 1
406
- indexed = @ram[addr] + @_y
407
- @clk += CLK_4
408
- if write
409
- @clk += CLK_1
410
- @addr = (@ram[(addr + 1) & 0xff] << 8) + indexed
411
- addr = @addr - (indexed & 0x100) # for inlining fetch
412
- fetch(addr)
413
- else
414
- @addr = ((@ram[(addr + 1) & 0xff] << 8) + indexed) & 0xffff
415
- if indexed & 0x100 != 0
416
- addr = (@addr - 0x100) & 0xffff # for inlining fetch
417
- fetch(addr)
418
- @clk += CLK_1
419
- end
420
- end
421
- read_write(read, write)
422
- end
423
-
424
- def read_write(read, write)
425
- if read
426
- @data = fetch(@addr)
427
- @clk += CLK_1
428
- if write
429
- store(@addr, @data)
430
- @clk += CLK_1
431
- end
432
- end
433
- end
434
-
435
- ###########################################################################
436
- # instructions
437
-
438
- # load instructions
439
- def _lda
440
- @_p_nz = @_a = @data
441
- end
442
-
443
- def _ldx
444
- @_p_nz = @_x = @data
445
- end
446
-
447
- def _ldy
448
- @_p_nz = @_y = @data
449
- end
450
-
451
- # store instructions
452
- def _sta
453
- @data = @_a
454
- end
455
-
456
- def _stx
457
- @data = @_x
458
- end
459
-
460
- def _sty
461
- @data = @_y
462
- end
463
-
464
- # transfer instructions
465
- def _tax
466
- @clk += CLK_2
467
- @_p_nz = @_x = @_a
468
- end
469
-
470
- def _tay
471
- @clk += CLK_2
472
- @_p_nz = @_y = @_a
473
- end
474
-
475
- def _txa
476
- @clk += CLK_2
477
- @_p_nz = @_a = @_x
478
- end
479
-
480
- def _tya
481
- @clk += CLK_2
482
- @_p_nz = @_a = @_y
483
- end
484
-
485
- # flow control instructions
486
- def _jmp_a
487
- @_pc = peek16(@_pc)
488
- @clk += CLK_3
489
- end
490
-
491
- def _jmp_i
492
- pos = peek16(@_pc)
493
- low = fetch(pos)
494
- pos = (pos & 0xff00) | ((pos + 1) & 0x00ff)
495
- high = fetch(pos)
496
- @_pc = high * 256 + low
497
- @clk += CLK_5
498
- end
499
-
500
- def _jsr
501
- data = @_pc + 1
502
- push16(data)
503
- @_pc = peek16(@_pc)
504
- @clk += CLK_6
505
- end
506
-
507
- def _rts
508
- @_pc = (pull16 + 1) & 0xffff
509
- @clk += CLK_6
510
- end
511
-
512
- def _rti
513
- @clk += CLK_6
514
- packed = pull8
515
- @_pc = pull16
516
- flags_unpack(packed)
517
- @clk_irq = @irq_flags == 0 || @_p_i != 0 ? FOREVER_CLOCK : @clk_target = 0
518
- end
519
-
520
- def _bne
521
- branch(@_p_nz & 0xff != 0)
522
- end
523
-
524
- def _beq
525
- branch(@_p_nz & 0xff == 0)
526
- end
527
-
528
- def _bmi
529
- branch(@_p_nz & 0x180 != 0)
530
- end
531
-
532
- def _bpl
533
- branch(@_p_nz & 0x180 == 0)
534
- end
535
-
536
- def _bcs
537
- branch(@_p_c != 0)
538
- end
539
-
540
- def _bcc
541
- branch(@_p_c == 0)
542
- end
543
-
544
- def _bvs
545
- branch(@_p_v != 0)
546
- end
547
-
548
- def _bvc
549
- branch(@_p_v == 0)
550
- end
551
-
552
- # math operations
553
- def _adc
554
- tmp = @_a + @data + @_p_c
555
- @_p_v = ~(@_a ^ @data) & (@_a ^ tmp) & 0x80
556
- @_p_nz = @_a = tmp & 0xff
557
- @_p_c = tmp[8]
558
- end
559
-
560
- def _sbc
561
- data = @data ^ 0xff
562
- tmp = @_a + data + @_p_c
563
- @_p_v = ~(@_a ^ data) & (@_a ^ tmp) & 0x80
564
- @_p_nz = @_a = tmp & 0xff
565
- @_p_c = tmp[8]
566
- end
567
-
568
- # logical operations
569
- def _and
570
- @_p_nz = @_a &= @data
571
- end
572
-
573
- def _ora
574
- @_p_nz = @_a |= @data
575
- end
576
-
577
- def _eor
578
- @_p_nz = @_a ^= @data
579
- end
580
-
581
- def _bit
582
- @_p_nz = ((@data & @_a) != 0 ? 1 : 0) | ((@data & 0x80) << 1)
583
- @_p_v = @data & 0x40
584
- end
585
-
586
- def _cmp
587
- data = @_a - @data
588
- @_p_nz = data & 0xff
589
- @_p_c = 1 - data[8]
590
- end
591
-
592
- def _cpx
593
- data = @_x - @data
594
- @_p_nz = data & 0xff
595
- @_p_c = 1 - data[8]
596
- end
597
-
598
- def _cpy
599
- data = @_y - @data
600
- @_p_nz = data & 0xff
601
- @_p_c = 1 - data[8]
602
- end
603
-
604
- # shift operations
605
- def _asl
606
- @_p_c = @data >> 7
607
- @data = @_p_nz = @data << 1 & 0xff
608
- end
609
-
610
- def _lsr
611
- @_p_c = @data & 1
612
- @data = @_p_nz = @data >> 1
613
- end
614
-
615
- def _rol
616
- @_p_nz = (@data << 1 & 0xff) | @_p_c
617
- @_p_c = @data >> 7
618
- @data = @_p_nz
619
- end
620
-
621
- def _ror
622
- @_p_nz = (@data >> 1) | (@_p_c << 7)
623
- @_p_c = @data & 1
624
- @data = @_p_nz
625
- end
626
-
627
- # increment and decrement operations
628
- def _dec
629
- @data = @_p_nz = (@data - 1) & 0xff
630
- end
631
-
632
- def _inc
633
- @data = @_p_nz = (@data + 1) & 0xff
634
- end
635
-
636
- def _dex
637
- @clk += CLK_2
638
- @data = @_p_nz = @_x = (@_x - 1) & 0xff
639
- end
640
-
641
- def _dey
642
- @clk += CLK_2
643
- @data = @_p_nz = @_y = (@_y - 1) & 0xff
644
- end
645
-
646
- def _inx
647
- @clk += CLK_2
648
- @data = @_p_nz = @_x = (@_x + 1) & 0xff
649
- end
650
-
651
- def _iny
652
- @clk += CLK_2
653
- @data = @_p_nz = @_y = (@_y + 1) & 0xff
654
- end
655
-
656
- # flags instructions
657
- def _clc
658
- @clk += CLK_2
659
- @_p_c = 0
660
- end
661
-
662
- def _sec
663
- @clk += CLK_2
664
- @_p_c = 1
665
- end
666
-
667
- def _cld
668
- @clk += CLK_2
669
- @_p_d = 0
670
- end
671
-
672
- def _sed
673
- @clk += CLK_2
674
- @_p_d = 8
675
- end
676
-
677
- def _clv
678
- @clk += CLK_2
679
- @_p_v = 0
680
- end
681
-
682
- def _sei
683
- @clk += CLK_2
684
- if @_p_i == 0
685
- @_p_i = 0x04
686
- @clk_irq = FOREVER_CLOCK
687
- do_isr(IRQ_VECTOR) if @irq_flags != 0
688
- end
689
- end
690
-
691
- def _cli
692
- @clk += CLK_2
693
- if @_p_i != 0
694
- @_p_i = 0
695
- if @irq_flags != 0
696
- clk = @clk_irq = @clk + 1
697
- @clk_target = clk if @clk_target > clk
698
- end
699
- end
700
- end
701
-
702
- # stack operations
703
- def _pha
704
- @clk += CLK_3
705
- push8(@_a)
706
- end
707
-
708
- def _php
709
- @clk += CLK_3
710
- data = flags_pack | 0x10
711
- push8(data)
712
- end
713
-
714
- def _pla
715
- @clk += CLK_4
716
- @_p_nz = @_a = pull8
717
- end
718
-
719
- def _plp
720
- @clk += CLK_4
721
- i = @_p_i
722
- flags_unpack(pull8)
723
- if @irq_flags != 0
724
- if i > @_p_i
725
- clk = @clk_irq = @clk + 1
726
- @clk_target = clk if @clk_target > clk
727
- elsif i < @_p_i
728
- @clk_irq = FOREVER_CLOCK
729
- do_isr(IRQ_VECTOR)
730
- end
731
- end
732
- end
733
-
734
- def _tsx
735
- @clk += CLK_2
736
- @_p_nz = @_x = @_sp
737
- end
738
-
739
- def _txs
740
- @clk += CLK_2
741
- @_sp = @_x
742
- end
743
-
744
- # undocumented instructions, rarely used
745
- def _anc
746
- @_p_nz = @_a &= @data
747
- @_p_c = @_p_nz >> 7
748
- end
749
-
750
- def _ane
751
- @_a = (@_a | 0xee) & @_x & @data
752
- @_p_nz = @_a
753
- end
754
-
755
- def _arr
756
- @_a = ((@data & @_a) >> 1) | (@_p_c << 7)
757
- @_p_nz = @_a
758
- @_p_c = @_a[6]
759
- @_p_v = @_a[6] ^ @_a[5]
760
- end
761
-
762
- def _asr
763
- @_p_c = @data & @_a & 0x1
764
- @_p_nz = @_a = (@data & @_a) >> 1
765
- end
766
-
767
- def _dcp
768
- @data = (@data - 1) & 0xff
769
- _cmp
770
- end
771
-
772
- def _isb
773
- @data = (@data + 1) & 0xff
774
- _sbc
775
- end
776
-
777
- def _las
778
- @_sp &= @data
779
- @_p_nz = @_a = @_x = @_sp
780
- end
781
-
782
- def _lax
783
- @_p_nz = @_a = @_x = @data
784
- end
785
-
786
- def _lxa
787
- @_p_nz = @_a = @_x = @data
788
- end
789
-
790
- def _rla
791
- c = @_p_c
792
- @_p_c = @data >> 7
793
- @data = (@data << 1 & 0xff) | c
794
- @_p_nz = @_a &= @data
795
- end
796
-
797
- def _rra
798
- c = @_p_c << 7
799
- @_p_c = @data & 1
800
- @data = (@data >> 1) | c
801
- _adc
802
- end
803
-
804
- def _sax
805
- @data = @_a & @_x
806
- end
807
-
808
- def _sbx
809
- @data = (@_a & @_x) - @data
810
- @_p_c = (@data & 0xffff) <= 0xff ? 1 : 0
811
- @_p_nz = @_x = @data & 0xff
812
- end
813
-
814
- def _sha
815
- @data = @_a & @_x & ((@addr >> 8) + 1)
816
- end
817
-
818
- def _shs
819
- @_sp = @_a & @_x
820
- @data = @_sp & ((@addr >> 8) + 1)
821
- end
822
-
823
- def _shx
824
- @data = @_x & ((@addr >> 8) + 1)
825
- @addr = (@data << 8) | (@addr & 0xff)
826
- end
827
-
828
- def _shy
829
- @data = @_y & ((@addr >> 8) + 1)
830
- @addr = (@data << 8) | (@addr & 0xff)
831
- end
832
-
833
- def _slo
834
- @_p_c = @data >> 7
835
- @data = @data << 1 & 0xff
836
- @_p_nz = @_a |= @data
837
- end
838
-
839
- def _sre
840
- @_p_c = @data & 1
841
- @data >>= 1
842
- @_p_nz = @_a ^= @data
843
- end
844
-
845
- # nops
846
- def _nop
847
- end
848
-
849
- # interrupts
850
- def _brk
851
- data = @_pc + 1
852
- push16(data)
853
- data = flags_pack | 0x10
854
- push8(data)
855
- @_p_i = 0x04
856
- @clk_irq = FOREVER_CLOCK
857
- @clk += CLK_7
858
- addr = fetch_irq_isr_vector # for inlining peek16
859
- @_pc = peek16(addr)
860
- end
861
-
862
- def _jam
863
- @_pc = (@_pc - 1) & 0xffff
864
- @clk += CLK_2
865
- unless @jammed
866
- @jammed = true
867
- # interrupt reset
868
- @clk_nmi = FOREVER_CLOCK
869
- @clk_irq = FOREVER_CLOCK
870
- @irq_flags = 0
871
- end
872
- end
873
-
874
- ###########################################################################
875
- # default core
876
-
877
- def r_op(instr, mode)
878
- send(mode, true, false)
879
- send(instr)
880
- end
881
-
882
- def w_op(instr, mode, store)
883
- send(mode, false, true)
884
- send(instr)
885
- send(store)
886
- end
887
-
888
- def rw_op(instr, mode, store)
889
- send(mode, true, true)
890
- send(instr)
891
- send(store)
892
- end
893
-
894
- def a_op(instr)
895
- @clk += CLK_2
896
- @data = @_a
897
- send(instr)
898
- @_a = @data
899
- end
900
-
901
- def no_op(_instr, ops, ticks)
902
- @_pc += ops
903
- @clk += ticks * RP2A03_CC
904
- end
905
-
906
- def do_clock
907
- clock = @apu.do_clock
908
-
909
- clock = @clk_frame if clock > @clk_frame
910
-
911
- if @clk < @clk_nmi
912
- clock = @clk_nmi if clock > @clk_nmi
913
- if @clk < @clk_irq
914
- clock = @clk_irq if clock > @clk_irq
915
- else
916
- @clk_irq = FOREVER_CLOCK
917
- do_isr(IRQ_VECTOR)
918
- end
919
- else
920
- @clk_nmi = @clk_irq = FOREVER_CLOCK
921
- do_isr(NMI_VECTOR)
922
- end
923
- @clk_target = clock
924
- end
925
-
926
- def run
927
- do_clock
928
- begin
929
- begin
930
- @opcode = fetch(@_pc)
931
-
932
- if @conf.loglevel >= 3
933
- @conf.debug("PC:%04X A:%02X X:%02X Y:%02X P:%02X SP:%02X CYC:%3d : OPCODE:%02X (%d, %d)" % [
934
- @_pc, @_a, @_x, @_y, flags_pack, @_sp, @clk / 4 % 341, @opcode, @clk, @clk_target
935
- ])
936
- end
937
-
938
- @_pc += 1
939
-
940
- send(*DISPATCH[@opcode])
941
-
942
- @ppu.sync(@clk) if @ppu_sync
943
- end while @clk < @clk_target
944
- do_clock
945
- end while @clk < @clk_frame
946
- end
947
-
948
- ADDRESSING_MODES = {
949
- ctl: [:imm, :zpg, :imm, :abs, nil, :zpg_x, nil, :abs_x],
950
- rmw: [:imm, :zpg, :imm, :abs, nil, :zpg_y, nil, :abs_y],
951
- alu: [:ind_x, :zpg, :imm, :abs, :ind_y, :zpg_x, :abs_y, :abs_x],
952
- uno: [:ind_x, :zpg, :imm, :abs, :ind_y, :zpg_y, :abs_y, :abs_y],
953
- }
954
-
955
- DISPATCH = []
956
-
957
- def self.op(opcodes, args)
958
- opcodes.each do |opcode|
959
- if args.is_a?(Array) && [:r_op, :w_op, :rw_op].include?(args[0])
960
- kind, op, mode = args
961
- mode = ADDRESSING_MODES[mode][opcode >> 2 & 7]
962
- send_args = [kind, op, mode]
963
- send_args << (mode.to_s.start_with?("zpg") ? :store_zpg : :store_mem) if kind != :r_op
964
- DISPATCH[opcode] = send_args
965
- else
966
- DISPATCH[opcode] = [*args]
967
- end
968
- end
969
- end
970
-
971
- # load instructions
972
- op([0xa9, 0xa5, 0xb5, 0xad, 0xbd, 0xb9, 0xa1, 0xb1], [:r_op, :_lda, :alu])
973
- op([0xa2, 0xa6, 0xb6, 0xae, 0xbe], [:r_op, :_ldx, :rmw])
974
- op([0xa0, 0xa4, 0xb4, 0xac, 0xbc], [:r_op, :_ldy, :ctl])
975
-
976
- # store instructions
977
- op([0x85, 0x95, 0x8d, 0x9d, 0x99, 0x81, 0x91], [:w_op, :_sta, :alu])
978
- op([0x86, 0x96, 0x8e], [:w_op, :_stx, :rmw])
979
- op([0x84, 0x94, 0x8c], [:w_op, :_sty, :ctl])
980
-
981
- # transfer instructions
982
- op([0xaa], :_tax)
983
- op([0xa8], :_tay)
984
- op([0x8a], :_txa)
985
- op([0x98], :_tya)
986
-
987
- # flow control instructions
988
- op([0x4c], :_jmp_a)
989
- op([0x6c], :_jmp_i)
990
- op([0x20], :_jsr)
991
- op([0x60], :_rts)
992
- op([0x40], :_rti)
993
- op([0xd0], :_bne)
994
- op([0xf0], :_beq)
995
- op([0x30], :_bmi)
996
- op([0x10], :_bpl)
997
- op([0xb0], :_bcs)
998
- op([0x90], :_bcc)
999
- op([0x70], :_bvs)
1000
- op([0x50], :_bvc)
1001
-
1002
- # math operations
1003
- op([0x69, 0x65, 0x75, 0x6d, 0x7d, 0x79, 0x61, 0x71], [:r_op, :_adc, :alu])
1004
- op([0xe9, 0xeb, 0xe5, 0xf5, 0xed, 0xfd, 0xf9, 0xe1, 0xf1], [:r_op, :_sbc, :alu])
1005
-
1006
- # logical operations
1007
- op([0x29, 0x25, 0x35, 0x2d, 0x3d, 0x39, 0x21, 0x31], [:r_op, :_and, :alu])
1008
- op([0x09, 0x05, 0x15, 0x0d, 0x1d, 0x19, 0x01, 0x11], [:r_op, :_ora, :alu])
1009
- op([0x49, 0x45, 0x55, 0x4d, 0x5d, 0x59, 0x41, 0x51], [:r_op, :_eor, :alu])
1010
- op([0x24, 0x2c], [:r_op, :_bit, :alu])
1011
- op([0xc9, 0xc5, 0xd5, 0xcd, 0xdd, 0xd9, 0xc1, 0xd1], [:r_op, :_cmp, :alu])
1012
- op([0xe0, 0xe4, 0xec], [:r_op, :_cpx, :rmw])
1013
- op([0xc0, 0xc4, 0xcc], [:r_op, :_cpy, :rmw])
1014
-
1015
- # shift operations
1016
- op([0x0a], [:a_op, :_asl])
1017
- op([0x06, 0x16, 0x0e, 0x1e], [:rw_op, :_asl, :alu])
1018
- op([0x4a], [:a_op, :_lsr])
1019
- op([0x46, 0x56, 0x4e, 0x5e], [:rw_op, :_lsr, :alu])
1020
- op([0x2a], [:a_op, :_rol])
1021
- op([0x26, 0x36, 0x2e, 0x3e], [:rw_op, :_rol, :alu])
1022
- op([0x6a], [:a_op, :_ror])
1023
- op([0x66, 0x76, 0x6e, 0x7e], [:rw_op, :_ror, :alu])
1024
-
1025
- # increment and decrement operations
1026
- op([0xc6, 0xd6, 0xce, 0xde], [:rw_op, :_dec, :alu])
1027
- op([0xe6, 0xf6, 0xee, 0xfe], [:rw_op, :_inc, :alu])
1028
- op([0xca], :_dex)
1029
- op([0x88], :_dey)
1030
- op([0xe8], :_inx)
1031
- op([0xc8], :_iny)
1032
-
1033
- # flags instructions
1034
- op([0x18], :_clc)
1035
- op([0x38], :_sec)
1036
- op([0xd8], :_cld)
1037
- op([0xf8], :_sed)
1038
- op([0x58], :_cli)
1039
- op([0x78], :_sei)
1040
- op([0xb8], :_clv)
1041
-
1042
- # stack operations
1043
- op([0x48], :_pha)
1044
- op([0x08], :_php)
1045
- op([0x68], :_pla)
1046
- op([0x28], :_plp)
1047
- op([0xba], :_tsx)
1048
- op([0x9a], :_txs)
1049
-
1050
- # undocumented instructions, rarely used
1051
- op([0x0b, 0x2b], [:r_op, :_anc, :uno])
1052
- op([0x8b], [:r_op, :_ane, :uno])
1053
- op([0x6b], [:r_op, :_arr, :uno])
1054
- op([0x4b], [:r_op, :_asr, :uno])
1055
- op([0xc7, 0xd7, 0xc3, 0xd3, 0xcf, 0xdf, 0xdb], [:rw_op, :_dcp, :alu])
1056
- op([0xe7, 0xf7, 0xef, 0xff, 0xfb, 0xe3, 0xf3], [:rw_op, :_isb, :alu])
1057
- op([0xbb], [:r_op, :_las, :uno])
1058
- op([0xa7, 0xb7, 0xaf, 0xbf, 0xa3, 0xb3], [:r_op, :_lax, :uno])
1059
- op([0xab], [:r_op, :_lxa, :uno])
1060
- op([0x27, 0x37, 0x2f, 0x3f, 0x3b, 0x23, 0x33], [:rw_op, :_rla, :alu])
1061
- op([0x67, 0x77, 0x6f, 0x7f, 0x7b, 0x63, 0x73], [:rw_op, :_rra, :alu])
1062
- op([0x87, 0x97, 0x8f, 0x83], [:w_op, :_sax, :uno])
1063
- op([0xcb], [:r_op, :_sbx, :uno])
1064
- op([0x9f, 0x93], [:w_op, :_sha, :uno])
1065
- op([0x9b], [:w_op, :_shs, :uno])
1066
- op([0x9e], [:w_op, :_shx, :rmw])
1067
- op([0x9c], [:w_op, :_shy, :ctl])
1068
- op([0x07, 0x17, 0x0f, 0x1f, 0x1b, 0x03, 0x13], [:rw_op, :_slo, :alu])
1069
- op([0x47, 0x57, 0x4f, 0x5f, 0x5b, 0x43, 0x53], [:rw_op, :_sre, :alu])
1070
-
1071
- # nops
1072
- op([0x1a, 0x3a, 0x5a, 0x7a, 0xda, 0xea, 0xfa], [:no_op, :_nop, 0, 2])
1073
- op([0x80, 0x82, 0x89, 0xc2, 0xe2], [:no_op, :_nop, 1, 2])
1074
- op([0x04, 0x44, 0x64], [:no_op, :_nop, 1, 3])
1075
- op([0x14, 0x34, 0x54, 0x74, 0xd4, 0xf4], [:no_op, :_nop, 1, 4])
1076
- op([0x0c], [:no_op, :_nop, 2, 4])
1077
- op([0x1c, 0x3c, 0x5c, 0x7c, 0xdc, 0xfc], [:r_op, :_nop, :ctl])
1078
-
1079
- # interrupts
1080
- op([0x00], :_brk)
1081
- op([0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, 0x92, 0xb2, 0xd2, 0xf2], :_jam)
1082
-
1083
- ###########################################################################
1084
- # optimized core generator
1085
- class OptimizedCodeBuilder
1086
- include CodeOptimizationHelper
1087
-
1088
- OPTIONS = [:method_inlining, :constant_inlining, :ivar_localization, :trivial_branches]
1089
-
1090
- LOCALIZE_IVARS = [:@addr, :@data, :@_a, :@_x, :@_y, :@_pc, :@_sp, :@fetch, :@store, :@ram, :@opcode]
1091
-
1092
- def build
1093
- depends(:ivar_localization, :method_inlining)
1094
-
1095
- mdefs = parse_method_definitions(__FILE__)
1096
- code = build_loop(mdefs)
1097
-
1098
- # optimize!
1099
- code = cpu_expand_methods(code, mdefs) if @method_inlining
1100
- code = remove_trivial_branches(code) if @trivial_branches
1101
- code = expand_constants(code) if @constant_inlining
1102
- code = localize_instance_variables(code, LOCALIZE_IVARS) if @ivar_localization
1103
-
1104
- gen(
1105
- "def self.run",
1106
- indent(2, code),
1107
- "end",
1108
- )
1109
- end
1110
-
1111
- # generate a main code
1112
- def build_loop(mdefs)
1113
- dispatch = gen(
1114
- "case @opcode",
1115
- *DISPATCH.map.with_index do |args, opcode|
1116
- if args.size > 1
1117
- mhd, instr, = args
1118
- code = expand_inline_methods("#{ mhd }(#{ args.drop(1).join(", ") })", mhd, mdefs[mhd])
1119
- code = code.gsub(/send\((\w+), (.*?)\)/) { "#{ $1 }(#{ $2 })" }
1120
- code = code.gsub(/send\((\w+)\)/) { $1 }
1121
- code = code[1..-2].split("; ")
1122
- else
1123
- instr = code = args[0]
1124
- end
1125
- "when 0x%02x # #{ instr }\n" % opcode + indent(2, gen(*code))
1126
- end,
1127
- "end"
1128
- )
1129
- main = mdefs[:run].body.sub("@conf.loglevel >= 3") { @loglevel >= 3 }
1130
- main.sub(/^ *send.*\n/) { indent(4, dispatch) }
1131
- end
1132
-
1133
- # inline method calls
1134
- def cpu_expand_methods(code, mdefs)
1135
- code = expand_methods(code, mdefs, mdefs.keys.grep(/^_/))
1136
- [
1137
- [:_adc, :_sbc, :_cmp, :store_mem, :store_zpg],
1138
- [:imm, :abs, :zpg, :abs_x, :abs_y, :zpg_x, :zpg_y, :ind_x, :ind_y],
1139
- [:abs_reg, :zpg_reg],
1140
- [:read_write],
1141
- [:do_clock],
1142
- [:do_isr],
1143
- [:branch, :push16],
1144
- [:push8],
1145
- ].each do |meths|
1146
- code = expand_methods(code, mdefs, meths)
1147
- end
1148
- [:fetch, :peek16, :store, :pull16, :pull8].each do |meth|
1149
- code = expand_inline_methods(code, meth, mdefs[meth])
1150
- end
1151
- code
1152
- end
1153
-
1154
- # inline constants
1155
- def expand_constants(handlers)
1156
- handlers = handlers.gsub(/CLK_(\d+)/) { eval($&) }
1157
- handlers = handlers.gsub(/FOREVER_CLOCK/) { "0xffffffff" }
1158
- handlers
1159
- end
1160
- end
1161
- end
1162
- end