-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy path6502_Emu.asm
3780 lines (3691 loc) · 125 KB
/
6502_Emu.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
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
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;
;
; 6 5 0 2 E M U L A T O R
;
; An AVR emulates a 6502 with >2MHz speed
;
; Copyright (C) 2013-2018 Klaus Dormann
;
; This program is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program. If not, see <http://www.gnu.org/licenses/>.
;
; contact info at http://2m5.de or email [email protected]
;
#define version "0.84 " ;makes a printable version number
.set base_version = 840 ;version * 1000
;
; version history:
; 0.8 24-jan-13 1st version distributed for testing
; 0.81 04-mar-13 added binary load/save/autoload support for applications
; added 6502_Emu_config.inc for all reconfigurable items.
; split cpu core into 6502_Emu_NMOS.inc to support change to CMOS
; added timer 1 access, tested and fixed all interrupts
; added countdown timer & interrupt vector register
; added optional software flow control to rx buffer
; improved terminal support for backspace and delete key
; 0.82 20-jul-13 added CMOS core (65C02 instructions and disassembly)
; 0.83 17-may-14 added breakpoint support to debugger
; added SPI support to IO module, DMA for SPI & I2C
; 0.83a 17-aug-14 changed interrupt disabled in real mode to honor NMI & single step
; fixed diag stop continuing until 10ms interrupt, now immediate
; fixed invalid opcode message broken by check for breakpoint
; 0.83b 24-jan-15 fixed ATMega32 interrupt table, fixed break hanging on some terminals
; reduced interrupt latency caused by the monitor waiting for tx buffer
; 0.83c 24-nov-15 fixed dma load program corrupted message due to false count in
; dma save program
; 0.84 21-feb-18 improved ADC/SBC binary/decimal switch (thanks to Peter!)
; added a switch to force hardware compatible results on invalid BCD
; added MMU support to switch banks, copy or swap pages between banks
; added SPI/I2C DMA support to read, write, save or load banks
; added IO subprocessor support
;
;
; ATMEGA16 & ATMEGA32 fuse settings:
; 16 MHz crystal
; JTAGEN unprogrammed
; BOD enabled - 4,0V
; Preserve EEPROM - protect non volatile program memory, else clears all saved programs
;
; related docs: http://2m5.de/6502_Emu/index.htm
;
; description of hardware:
; porta0:7 -> address bus high : IO chip select latches
; portb0:7 -> address bus low : IO address & IO R/W : SPI & slave select latch
; portc0:7 <> data bus : I2C bus switch
; portd0:1 RS232 monitor & ACIA emulation
; portd2 <- ~NMI (INT0) / optional IO-select 3
; portd3 <- ~IRQ (INT1)
; portd4 -> ~RD_RAM (/OE at SRAM)
; portd5 -> ~WR_RAM (/WE at SRAM)
; /CE tied low at SRAM
; portd6 -> IO-select 1
; portd7 -> phi2 (OC2) / optional IO-select 2
;
; pullups required for MNI, IRQ, weak pullups for WR_RAM, RD_RAM
;
; optional weak pullups or pulldowns on IO selects and
; IO busses to reset external hardware during high-Z
;
; timer0 debugger 20ms timer: ctc interrupt
; debugger single step: overflow interrupt
; timer2 optional 1 MHz phase 2 output on OC2
;
; The following notes apply to the NMOS emulation core:
;
; Emulation is documented instructions only !
;
; Invalid 6502 instructions will cause a halt with message.
;
; Decimal mode emulation will emulate documented behavior only:
; a decimal result & carry-flag is valid if both operands were valid BCD
;
; negative, overflow & zero-flags may be altered after a decimal add
; or subtract, but have no valid meaning and may not be identical to real
; hardware. The result of a decimal operation with non decimal operands
; (any nibble >9) may not match the result of a hardware 6502, unless
; dec_mode_extended is configured.
;
; In the CMOS emulation core all instructions are valid and if not defined
; otherwise, will be executed as NOP instructions of various length in bytes
; and cycles depending on the decode mechanism in the original CMOS core.
;
; A decimal operation with valid decimal numbers will have a valid result
; and valid NZC flags. The result and flags of decimal add or subtract with
; invalid BCD operands may not match a real 65C02, unless dec_mode_extended
; is configured.
.NOLIST
.include "sam.inc"
.LIST
;****************************
;
; C O N F I G U R A T I O N
;
;****************************
.set config_part=1
.include "6502_Emu_config.inc"
;configuration based clock constants
.equ cycle_time_tns = (10000000000 / Osc_Hz) ;1/10 ns cpu clock duration
.equ ten_ms = ((Osc_Hz - 51200) / 102400) ;10ms OCR0 value rounded
;configuration based baudrate constants and error checking
.equ UBRR_value = ((Osc_Hz + Baud * 8) / (Baud * 16) - 1) ; rounded
.equ Baud_error = ((1000 * Osc_Hz / (16 * (UBRR_value + 1)) / Baud) - 1000) ;0/00 deviation
.if ((Baud_error > 10) || (Baud_error < -10)) ; +/-10 0/00 acceptable
.error "Unsafe baudrate - at current cpu clock the deviation is > 1%!"
.endif
;configuration based serial EEPROM constants
.ifdef eep_adr
.if eep_adr < 8
.ifdef spi_sel
.ifdef spi_vat
.equ spi_eep_adr = spi_idle^(1<<eep_adr)
.else
.equ spi_eep_adr = eep_adr
.endif
.equ eep_vld = 1
.endif
.else
.ifdef i2c_sel
.equ i2c_eep_adr = eep_adr
.equ eep_vld = 1
.endif
.endif
.endif
;config based dma valid
.ifdef iomap
.if defined(spi_sel) || defined(i2c_sel) || defined(mmu_sel)
.equ dma_vld = 1
.endif
.endif
;config based mmu parms & macros
.if defined(mmu_sel) && defined(iomap)
.set bank_sel = mmu_sel ;precharge mmu IO select pins for MMU
.set io_strobe_pol = 1 ;preset to error
.if bank_sel > 9 ;generate mmu strobe pin
.set bank_sel = bank_sel -10
.if bank_sel < 8
.set bank_sel_iox = ios1_default ;precharge IO-select register
.set io_strobe_pol = !((ios1_default >> bank_sel) & 1)
.set bank_sel_direct = 6 ;bit to enable extension register
.else
.set bank_sel = bank_sel -10
.if bank_sel < 8
.set bank_sel_iox = ios2_default ;precharge IO-select register
.set io_strobe_pol = !((ios2_default >> bank_sel) & 1)
.set bank_sel_direct = 7 ;bit to enable extension register
.else
.set bank_sel = bank_sel -10
.set bank_sel_iox = ios3_default ;precharge IO-select register
.set io_strobe_pol = !((ios3_default >> bank_sel) & 1)
.set bank_sel_direct = 2 ;bit to enable extension register
.endif
.endif
.endif
.if io_strobe_pol
.error "mmu_sel must be active low on an I/O expansion pin"
.endif
.equ mmu_vld = 1 ;valid mmu configured
.endif
; reserved registers
;
; (I): used during interrupt, (C): interrupt to program communication
; (E): Register of the emulated CPU
; (D) used by the debug monitor - must be preserved except Z (Z is saved
; before main is called)
;
;
; 0x00 non immediate
.DEF c =r0 ; GPR3
.DEF d =r1 ; GPR4
.DEF e =r2 ; GPR5
.DEF f =r3 ; GPR6
; 0x04
.DEF k =r4 ; (I) GPR2 during interrupt
.DEF allon =r5 ; const 0xff
.DEF one =r6 ; const 1
.DEF zero =r7 ; const 0
; 0x08
.DEF regx =r8 ; (E) index X
.DEF regy =r9 ; (E) index Y
.DEF operand =r10 ; memory operand
.DEF spointer =r11 ; (E) stack pointer
; 0x0c
.DEF clear =r12 ; precharged cbus clear
.DEF readmem =r13 ; precharged cbus read memory
.DEF writemem =r14 ; precharged cbus write memory
.DEF s =r15 ; (I) SREG save during interrupt
; 0x10 immediate
.DEF a =r16 ; immediate GPR
.DEF b =r17 ; immediate GPR2
.DEF i =r18 ; (I) immediate GPR during interrupt
.DEF flags =r19 ; (D) flags
.equ deb_on = 0 ; debugger running if set, else stopped
; acia emulation disconnected if set
.equ emu_run = 1 ; emulator running if set, else stopped
.equ deb_act = 2 ; deb instance started if set - locks T0 cmp start
.equ rvs_vid = 3 ; reverse video if set, else normal video
.equ op_ind = 4 ; operand indirect if set, operand direct if cleared
; needed for instruction retry in IO emulation (register busy)
.equ modify = 5 ; extended memory address locked until rewrite
.equ string = 6 ; allow lower case input for string
.equ dma_rpt = 7 ; DMA interrrupt modifier
; 0x14
.DEF unused_r20 =r20 ; unused
.DEF rega =r21 ; (E) accumulator
.DEF stat2 =r22 ; (E) processor flags 6502 only 0V1BDI00
.DEF stat =r23 ; (E) processor flags AVR compatible (ITHSV)NZC
; 0x18 immediate word
.DEF pcl =r24 ; (E) program counter low
.DEF pch =r25 ; (E) program counter high
; x ; word GPR / (I) buffer index (rx/tx)
; 0x1c
; y ; operand address, (D) buffer index
#define oplow yl
#define ophigh yh
#define opointer y
; z ; instruction decode
#define opcode zl
#define oc_tabh zh ; restore when used as index
#define oc_vector z
;
; internal EEPROM usage
;
.ESEG
.ifdef eep_adr
;non volatile program storage control structure for external I2C EEPROM
.org 1
eep_string: .byte 50 ;string to send to program on autoload
;0xff marks end
.org 0xff
eep_auto: .byte 1 ;autoload program number, 0xff = none
eep_pat: .byte 256 ;program allocation table, 0xff = slot free
;0x00 - 0xfe = program number in slot
.endif
.macro align ;align to (1<<@0)
alignfromhere:
.if (alignfromhere & ((1<<@0)-1)) ;if not already aligned
.org (alignfromhere & (0xffff<<@0)) + (1<<@0)
.endif
.endmacro
;
; SRAM usage
;
.DSEG
;(D) command line buffer
cmd_buf: .byte 44 ;44 character command line buffer
cmd_end: .byte 1 ;+1 for end of string marker
cmd_inx: .byte 1 ;input index to cmd_buf
cmd_esc_timer: ;10ms increments to discard esc sequence
.byte 1 ;bit 7 = 0:true escape, 1:discarded esc seq
cmd_reg_timer: ;timer to refresh register display
.byte 1
lmem_display:
.byte 2 ;last memory block displayed
lmem_write: .byte 2 ;last memory address written +1
lmem_disas: .byte 2 ;last memory address disassembled +1
sp_save: .byte 2 ;saved sp for mainloop to allow subs to exit
adr_limit: .byte 2 ;maximum address +1 accepted in deb_adr
rstflag: .byte 1 ;reset flags from mcucsr
prog_num: .byte 1 ;program number for EEP save/load, 0xff if closed
brkpt_lo: .byte 8 ;breakpoint address low
brkpt_hi: .byte 8 ;breakpoint address high
brkpt_op: .byte 8 ;breakpoint original opcode
brkpt_bs: .byte 8 ;breakpoint bank select
;I/O emulation
;global
irq_mask: .byte 1 ;interrupt mask, bit set if enabled, default 0b00000001
;bit 7=tx empty, 6=rx full, 0=10ms tick,
irq_flag: .byte 1 ;interrupt flags, bit set if pending
;bit 7=tx empty, 6=rx full, 0=10ms tick
;timer 1
timer_ena: .byte 1 ;t0 & t1 interrupt enable bits
t1_adr: .byte 1 ;index to t1 register
t1_ctrl: .byte 1 ;readback value of TCCR1
;tick (10ms) countdown timer
tcdn_count: .byte 1 ;countdown to flag/interrupt
tcdn_top: .byte 1 ;reload value at bottom
;diagnostic
selftest: .byte 1 ;selftest register, force bits 2=reset, 1=NMI, 0=IRQ
;I2C
i2c_statreg:
.byte 1 ;status, bit 7=interrupt, 6=ack, 5=stuck, 4=set EEP
;3=read ack pending, 2=speed 400kHz, 1=start, 0=stop
;LCD
lcd_flags: .byte 1 ;0xff=usable, 0=busy timeout
;SPI
spi_cmd: .byte 1 ;save for slave select hold
spi_rdat: .byte 1 ;data in SPI shifter after last write
;MMU
mmu_emu: .byte 4 ;emulator MMU bank select segments
mmu_mon: .byte 4 ;monitor MMU bank select segments
mmu_sync: .byte 1 ;monitor MMU synced to emulator MMU
;0=separate monitor settings, 0xff=synced
;DMA
dma_last_cmd:
.byte 1 ;command in progress | last successful command + 0x10
;| 0xff= can't continue
dma_tab_index:
.byte 1 ;index to block transfer table, 0xff if trashed
;dma block transfer table:
;
; SPI I2C EEPROM
; byte slave address -don't care- program number
; byte bank select bank select bank select (0xFF=current banks)
; word start address start address start address
; word byte count byte count end address
; word temp count temp count -don't care-
;
; XMEM copy setup (cmd=9) after XMEM copy command (stat=1A)
; byte -don't care- target bank select
; byte source bank select source bank select
; byte source page address source page address
; byte target bank select target page address
; byte target page address byte count always 0
; byte page count page count
; byte temp byte count temp byte count
; byte temp page count temp page count
;
; temp counts = remaining counts for interrupted transfers!
dma_tab: .byte 8
;RS232 Buffers
usart_ena: .byte 1 ;usart interrupt enable bits
tx_inx: .byte 1 ;input index to tx fifo
tx_fill: .byte 1 ;fill level in tx fifo (0 = empty)
rx_inx: .byte 1 ;input index to rx fifo
rx_fill: .byte 1 ;fill level in rx fifo (0 = empty)
flow_cmd: .byte 1 ;XON/XOFF - bit 7: 1=done, 0=pending
.if (pc > 0xff)
.error "lower RAM locations exceed 0x00ff (high address byte != 0)!"
.endif
align 8 ;buffers on page boundary
tx_buf: .byte 256 ;transmit fifo
rx_buf: .byte 256 ;receive fifo
; end of SRAM
.CSEG
;
; Definition of Hardware
;
; Address Bus
.EQU abuslo = portb
.EQU aloddr = ddrb
.EQU aloin = pinb
.EQU abushi = porta
.EQU ahiddr = ddra
; Data Bus
.EQU dbusin = pinc
.EQU dbusout = portc
.EQU dbusddr = ddrc
; Control Bus
.EQU cbusin = pind
.EQU cbus = portd
.EQU cbusddr = ddrd
; External Interrupts
.EQU ibus = gicr
.ifdef nmi_ena
.EQU IRQ_ena = 0b11000000 ;enable IRQ
.EQU IRQ_dis = 0b01000000 ;disable IRQ
.else
.EQU IRQ_ena = 0b10000000 ;enable IRQ
.EQU IRQ_dis = 0b00000000 ;disable IRQ
.endif
.EQU NMI_dis = 0 ;disable both during monitor
; USART
.EQU usart_txi_dis = 0b10011000 ;RXCIE, RXEN, TXEN
.EQU usart_txi_ena = 0b10111000 ;RXCIE, UDRIE, RXEN, TXEN
;
; definition of global macros
;
.macro addi
subi @0,-@1 ;subtract the negative of an immediate value
.endmacro
.macro addiw ;(RdL:RdH, k)
subi @0l,low(-@1) ;word subtract the negative of an immediate value
sbci @0h,high(-@1)
.endmacro
.ifdef mmu_vld
;
; rapid load of mmu registers
;
; addressing implemented as johnson counter to avoid change of 2 bits
; in opposite direction causing unwanted register writes
; load active bank select to emulator or monitor bank select
.macro load_mmu ;@0 = register set: mmu_emu or mmu_mon
out cbus,clear ;turn off -OE to RAM
lds a,@0
out dbusout,a
ldi a,bank_sel_iox^(1<<bank_sel)
out abushi,a
out abuslo,zero ;set address 0
out dbusddr,allon ;data write mode
sbi cbus,bank_sel_direct
out abuslo,one ;set next address 1
lds a,@0+1
out dbusout,a
out abuslo,allon ;set next address 3
lds a,@0+3
out dbusout,a
ldi a,2 ;set next address 2
out abuslo,a
lds a,@0+2
out dbusout,a
sbi abushi,bank_sel ;end enable bank select
; out dbusddr,zero
; out cbus,readmem
.endmacro
; load active bank select to single dma bank select
.macro load_mmu1
lds a,dma_tab+1
cpi a,0xff ;valid bank switch?
breq load_mmu1_bs
out cbus,clear ;turn off -OE to RAM
lds a,dma_tab+1 ;bank select in dma tab
out dbusout,a
ldi a,bank_sel_iox^(1<<bank_sel)
out abushi,a
out abuslo,zero ;set address 0
out dbusddr,allon ;data write mode
sbi cbus,bank_sel_direct
out abuslo,one ;set next address 1
out abuslo,allon ;set next address 3
ldi a,2 ;set next address 2
out abuslo,a
sbi abushi,bank_sel ;end enable bank select
out dbusddr,zero
out cbus,readmem
load_mmu1_bs:
.endmacro
; load active bank select to xmem copy dma bank select
.macro load_mmu2
out cbus,clear ;turn off -OE to RAM
lds a,dma_tab+1 ;source bank select in dma tab
out dbusout,a
ldi a,bank_sel_iox^(1<<bank_sel)
out abushi,a
out abuslo,zero ;set address 0
out dbusddr,allon ;data write mode
sbi cbus,bank_sel_direct
out abuslo,one ;set next address 1
out abuslo,allon ;set next address 3
lds a,dma_tab ;target bank select in dma tab
out dbusout,a
ldi a,2 ;set next address 2
out abuslo,a
sbi abushi,bank_sel ;end enable bank select
out dbusddr,zero
out cbus,readmem
.endmacro
; restore emulator bank select after dma ends
.macro load_mmu_end
lds a,dma_tab+1
cpi a,0xff ;valid bank switch?
breq load_mmu1_end_rest
load_mmu mmu_emu
out dbusddr,zero
load_mmu1_end_rest:
.endmacro
.else
; no MMU - do nothing
.macro load_mmu1
.endmacro
.macro load_mmu_end
.endmacro
.endif
; save DMA registers and restore MMU during interrupt if needed
.macro rupt_dma_mmu ;@0 = 1: restore z as op decode
.ifdef dma_vld
sbrs flags,dma_rpt ;interupt during dma transfer?
rjmp rupt_save_dma
cbr flags,(1<<dma_rpt)
sts dma_tab+2,zl ;save current dma memory address
sts dma_tab+3,zh
sts dma_tab+6,xl ;save dma count in progress
sts dma_tab+7,xh
cbr flags,(1<<modify) ;unlock extended memory address after modify
.ifdef mmu_vld
lds a,dma_tab+1
cpi a,0xff
breq rupt_restore_mmu
load_mmu mmu_emu
rupt_restore_mmu:
.endif
out dbusddr,zero
out cbus,readmem
.if @0 == 1
ldi oc_tabh,high(oc_tab) ;restore zh as opcode table
sbrc stat2,3 ;test decimal flag
ldi oc_tabh,high(oc_tabd)
.endif
sbiw pch:pcl,2 ;prepare for instruction retry
sbrs flags,op_ind ;3 byte instruction if direct
rupt_save_dma:
.endif
sbiw pch:pcl,1 ;adjust pc to be saved on stack
.endmacro
;
; wait_ns waittime in ns , cyles already used
;
; cycles already used will be subtracted from the delay
; the waittime resolution is 1 cycle (delay from exact to +1 cycle)
; the maximum delay at 20MHz (50ns/clock) is 750ns
;
.macro wait_ns
.set cycles = ((@0 * 10 + cycle_time_tns - 1) / cycle_time_tns - @1)
.if (cycles > 15)
.error "MACRO wait_ns - too many cycles to burn"
.else
.if (cycles > 0)
.if (cycles & 8)
rjmp pc+1
rjmp pc+1
rjmp pc+1
rjmp pc+1
.endif
.if (cycles & 4)
rjmp pc+1
rjmp pc+1
.endif
.if (cycles & 2)
rjmp pc+1
.endif
.if (cycles & 1)
nop
.endif
.endif
.endif
.endmacro
; wait_data_valid cycles already used
#define wait_data_valid wait_ns data_valid_ns,
; print string - debugger
.macro PrintStr ;@0 = message pointer
ldi zl,low(@0*2)
ldi zh,high(@0*2)
rcall prtstr
.endmacro
.macro PrintStr_far ;@0 = message pointer
ldi zl,low(@0*2)
ldi zh,high(@0*2)
call prtstr
.endmacro
;*****************************************************************
;
; reset and interrupt vectors according to µc type
;
;*****************************************************************
#ifdef _M16DEF_INC_
jmp reset ; Reset handler
rjmp NMI ; INT0 handler
nop
rjmp IRQ ; INT1 handler
nop
jmp illegalint ; Timer2 compare match
jmp illegalint ; Timer2 overflow
.ifdef iomap
jmp t1_icr ; Timer1 capture event
jmp t1_ocra ; Timer1 compare match A
jmp t1_ocrb ; Timer1 compare match B
jmp t1_ovi ; Timer1 overflow handler
.else
jmp illegalint ; Timer1 capture event
jmp illegalint ; Timer1 compare match A
jmp illegalint ; Timer1 compare match B
jmp illegalint ; Timer1 overflow handler
.endif
jmp t0_ovi ; Timer0 overflow handler - debugger single step
jmp illegalint ; SPI - Serial transfer complete
rjmp rx_int ; USART - RX complete
nop
rjmp tx_udre ; USART - data register empty
nop
jmp illegalint ; USART - TX complete
jmp illegalint ; ADC conversion complete
jmp illegalint ; EEPROM ready
jmp illegalint ; Analog comparator
jmp illegalint ; TWI serial interface
jmp illegalint ; INT2 handler
jmp t0_cmi ; Timer0 compare match - debugger 10ms timer
jmp illegalint ; SPM ready
#endif
#ifdef _M32DEF_INC_
jmp reset ; Reset handler
rjmp NMI ; INT0 handler
nop
rjmp IRQ ; INT1 handler
nop
jmp illegalint ; INT2 handler
jmp illegalint ; Timer2 compare match
jmp illegalint ; Timer2 overflow
.ifdef iomap
jmp t1_icr ; Timer1 capture event
jmp t1_ocra ; Timer1 compare match A
jmp t1_ocrb ; Timer1 compare match B
jmp t1_ovi ; Timer1 overflow handler
.else
jmp illegalint ; Timer1 capture event
jmp illegalint ; Timer1 compare match A
jmp illegalint ; Timer1 compare match B
jmp illegalint ; Timer1 overflow handler
.endif
jmp t0_cmi ; Timer0 compare match - debugger 10ms timer
jmp t0_ovi ; Timer0 overflow handler - debugger single step
jmp illegalint ; SPI - Serial transfer complete
rjmp rx_int ; USART - RX complete
nop
rjmp tx_udre ; USART - data register empty
nop
jmp illegalint ; USART - TX complete
jmp illegalint ; ADC conversion complete
jmp illegalint ; EEPROM ready
jmp illegalint ; Analog comparator
jmp illegalint ; TWI serial interface
jmp illegalint ; SPM ready
#endif
.if pc < 0x2a
.error "unknown device - interrupt vectors could not be built!"
.endif
;*****************************************************************
;
; RS232 buffer
;
;*****************************************************************
;
; RX complete interrupt - rx fifo input
;
rx_int:
in s,sreg
push xl
push xh
in k,ucsra
in i,udr
sbrs k,fe ;no stop bit = possible break
ifs_and rx_break
cpi i,0 ;break should have all bits 0
ifeq rx_break
sbr flags,(1<<deb_on) ;turn debugging on
lds i,irq_flag ;clear ACIA TDRE & RDRF
cbr i,0b11000000
sts irq_flag,i
lds k,irq_mask ;other interrupts pending & enabled?
and i,k
ifeq rx_break_irq
cbi cbusddr,3 ;clear any pending acia interrupts
end rx_break_irq
sts rx_fill,zero ;clear buffer
sts tx_fill,zero
do rx_brk_wait
sbis pind,0 ;exit after RX pin returns to idle
loop rx_brk_wait
push zl
push zh
push a
PrintStr_far rx_break_msg ;10,13,"Terminal break signal detected"
call stop_emu
call crlf
pop a
pop zh
pop zl
ldi i,27 ;force escape
end rx_break
lds k,rx_fill ;check buffer full
inc k
ifne rx_no_overrun
ldi xh,high(rx_buf) ;load buffer index
lds xl,rx_inx
st x+,i ;store data
sts rx_inx,xl ;store pointers
sts rx_fill,k
end rx_no_overrun
.ifdef flowlo
mov i,k
cpi i,flowhi ;buffer upper watermark?
ifsh rx_flow
lds i,flow_cmd
cpi i,0x93 ;xoff done?
ifne rx_send_xoff
ldi i,0x13 ;post xoff pending
sts flow_cmd,i
ldi i,usart_txi_ena ;notify transmitter
out ucsrb,i
.ifdef irq_dis_real
sts usart_ena,i
.endif
end rx_send_xoff
end rx_flow
.endif
.ifdef iomap ;acia emulation
sbrc flags,deb_on ;acia connected?
ifs rx_acia_on
lds i,irq_mask ;check acia RDRF IRQ enabled
andi i,0b01000000
ifne rx_int_ena
sbi cbusddr,3 ;set IRQ
end rx_int_ena
lds i,irq_flag
sbr i,0b01000000 ;set RDRF
sts irq_flag,i ;store status
end rx_acia_on
.endif
pop xh
pop xl
out sreg,s
reti
;
; RS232 transmit register empty interrupt, tx fifo output
;
tx_udre:
in s,sreg
push xl
push xh
lds k,tx_fill ;no more bytes available?
.ifdef flowlo
lds i,flow_cmd ;do flowcontrol
tst i ;already sent?
ifpl tx_flow
out udr,i ;send xon/xoff
ori i,0x80 ;mark sent
sts flow_cmd,i
tst k
else tx_flow
ldi xh,high(tx_buf) ;send data
lds xl,tx_inx ;calc output index
sub xl,k
ld i,x ;load from fifo head
out udr,i
dec k ;update pointer
end tx_flow
.else
ldi xh,high(tx_buf)
lds xl,tx_inx ;calc output index
sub xl,k
ld i,x ;load from fifo head
out udr,i
dec k ;update pointer
.endif
ifeq tx_fifo_empty
ldi i,usart_txi_dis ;tx buffer empty - stop rupt
out ucsrb,i
.ifdef irq_dis_real
sts usart_ena,i
.endif
end tx_fifo_empty
sts tx_fill,k
.ifdef iomap ;acia emulation
sbrc flags,deb_on ;acia connected?
ifs tx_acia_on
lds i,irq_mask ;check TDRE IRQ enabled
andi i,0b10000000
ifne tx_int_ena
sbi cbusddr,3 ;set IRQ
end tx_int_ena
lds i,irq_flag
sbr i,0b10000000 ;set TDRE
sts irq_flag,i ;store status
end tx_acia_on
.endif
pop xh
pop xl
out sreg,s
reti
;**************************************************
;
; 6502 emulation core
;
;**************************************************
.ifdef cmos_core
.include "6502_Emu_CMOS.inc"
.else
.include "6502_Emu_NMOS.inc"
.endif
;**************************************************
;
; I/O page (iomap) address decode - emulated I/O
;
;**************************************************
.ifdef iomap
.include "6502_Emu_IO.inc"
.endif
;**************************************************
;
; Monitor / Debugger Interrupts
;
;**************************************************
;
; an illegal interrupt has occured
;
illegalint:
PrintStr illegal_int
do i_stop
loop i_stop ; reset required
;
; Timer 0 compare match A - 10ms CTC interrupt
;
t0_cmi:
in s,sreg
lds i,cmd_esc_timer ;service esc sequence timer
mov k,i
andi i,0x7f
ifne t0_esc_expired
dec k
sts cmd_esc_timer,k
end t0_esc_expired
lds i,cmd_reg_timer ;service register refresh timer
tst i
ifne t0_reg_expired
dec i
sts cmd_reg_timer,i
end t0_reg_expired
lds i,irq_flag ;check 10 ms timer
sbr i,1 ;set tick flag
lds k,tcdn_count ;update tcdn count
dec k ;expired?
ifeq t0_tcdn_expired
lds k,tcdn_top ;reset tcdn count
sbr i,2 ;set tcdn flag
end t0_tcdn_expired
sts tcdn_count,k ;save count
sts irq_flag,i ;save flags
lds k,irq_mask ;tick or tcdn IRQ enabled?
and i,k
ifne t0_tcdn_irq
sbi cbusddr,3 ;set IRQ
end t0_tcdn_irq
sbrc flags,deb_act ;no debugger instance active
ifs_and debug_start
sbrs flags,deb_on ;debug mode
ifs debug_start
rupt_dma_mmu 1 ;handle DMA save & MMU restore
sbr flags,(1<<deb_act) ;prevent double_activation
ldi a,NMI_dis ;no emulator interrupts during monitor
out ibus,a
pop a ;discard return address from stack
pop a
sei
rjmp debugger
end debug_start
out sreg,s
reti
;
; Timer 0 overflow interrupt - single step
;
t0_ovi:
ldi a,NMI_dis ;no emulator interrupts during monitor
out ibus,a
sbiw pch:pcl,1 ;adjust PC to next opcode
pop a ;discard return address from stack
pop a
cbr flags,(1<<emu_run)
sei
rjmp end_command
.ifdef cmos_core
; STP instruction handling
deb_stop:
ldi flags,(1<<deb_on)|(1<<deb_act)
lds a,irq_flag ;disconnect acia, clear TDRE & RDRF
cbr a,0b11000000
sts irq_flag,zero
lds c,irq_mask ;no other internal IRQ pending & enabled?
and a,c
ifeq inv_op_irq_clear
cbi cbusddr,3 ;clear irq
end inv_op_irq_clear
ldi a,NMI_dis ;no emulator interrupts during monitor
out ibus,a
.ifdef irq_dis_real
ldi a,0b11 ;enable t0 interrupts (tick, single step)
out timsk,a
lds a,usart_ena ;enable usart interrupts
out ucsrb,a
.endif
sbiw pch:pcl,2 ;position to current instruction
out tifr,one ;cancel single step interrupt (tov0)
sei
rcall brkpt_chk_pc
iftc deb_stop_unexp ;skip if breakpoint
rcall show_regs
PrintStr stp_instr ;"STP - Emulator halted"
adiw pch:pcl,1 ;position to next instruction
end deb_stop_unexp
rjmp end_command
.else
; illegal opcode handling
deb_inv_op:
ldi flags,(1<<deb_on)|(1<<deb_act)
lds a,irq_flag ;disconnect acia, clear TDRE & RDRF
cbr a,0b11000000
sts irq_flag,zero
lds c,irq_mask ;no other internal IRQ pending & enabled?
and a,c
ifeq inv_op_irq_clear
cbi cbusddr,3 ;clear irq
end inv_op_irq_clear
ldi a,NMI_dis ;no emulator interrupts during monitor
out ibus,a
.ifdef irq_dis_real
ldi a,0b11 ;enable t0 interrupts (tick, single step)
out timsk,a
lds a,usart_ena ;enable usart interrupts
out ucsrb,a
.endif
sbiw pch:pcl,2 ;position to current instruction
out tifr,one ;cancel single step interrupt (tov0)
sei
cpi opcode,0xdb ;potential breakpoint
ifne_or deb_stop_unexp
rcall brkpt_chk_pc
iftc deb_stop_unexp ;skip if breakpoint
mov d,opcode ;temporary save opcode
rcall show_regs
PrintStr inv_instr ;"Illegal Opcode "
mov a,d
rcall PrintHex
adiw pch:pcl,1 ;position to next instruction
end deb_stop_unexp
rjmp end_command
.endif
;**************************************************
;
; Reset
;
;**************************************************
; reset all IO extension registers
rs_IO_ext:
.set io_sel_direct = 0
.set rs_ddr_done = 0
;set ddr for 1st IO register configured
.macro rs_set_ddr
.if rs_ddr_done == 0
out ahiddr,allon ;activate io select output
.ifdef nmi_ena
ldi a,0b11110000 ;Memory R/W signals + IO-select 1-2
.else
ldi a,0b11110100 ;Memory R/W signals + IO-select 1-3
.endif
out cbusddr,a ;activate io select strobe
.set rs_ddr_done = 1
.endif
.endmacro
;reset external IO select registers
.macro rs_set_iosel ;@0 = ios#_default, @1 = cbus_pin#
.ifdef @0
.if io_sel_direct != @1
ldi b,@0
out abushi,b
sbi cbus,@1