-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path6502_cpu.txt
1534 lines (1223 loc) · 65.2 KB
/
6502_cpu.txt
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
#
# $Id: 64doc,v 1.8 1994/06/03 19:50:04 jopi Exp $
#
# This file is part of Commodore 64 emulator
# and Program Development System.
#
# See README for copyright notice
#
# This file contains documentation for 6502/6510/8500/8502 instruction set.
#
#
# Written by
# John West (john@ucc.gu.uwa.edu.au)
# Marko MŠkelŠ (msmakela@kruuna.helsinki.fi)
#
#
# $Log: 64doc,v $
# Revision 1.8 1994/06/03 19:50:04 jopi
# Patchlevel 2
#
# Revision 1.7 1994/04/15 13:07:04 jopi
# 65xx Register descriptions added
#
# Revision 1.6 1994/02/18 16:09:36 jopi
#
# Revision 1.5 1994/01/26 16:08:37 jopi
# X64 version 0.2 PL 1
#
# Revision 1.4 1993/11/10 01:55:34 jopi
#
# Revision 1.3 93/06/21 13:37:18 jopi
# X64 version 0.2 PL 0
#
# Revision 1.2 93/06/21 13:07:15 jopi
# *** empty log message ***
#
#
Note: To extract the uuencoded ML programs in this article most
easily you may use e.g. "uud" by Edwin Kremer ,
which extracts them all at once.
Documentation for the NMOS 65xx/85xx Instruction Set
6510 Instructions by Addressing Modes
6502 Registers
6510/8502 Undocumented Commands
Register selection for load and store
Decimal mode in NMOS 6500 series
6510 features
Different CPU types
6510 Instruction Timing
How Real Programmers Acknowledge Interrupts
Memory Management
Autostart Code
Notes
References
6510 Instructions by Addressing Modes
off- ++++++++++ Positive ++++++++++ ---------- Negative ----------
set 00 20 40 60 80 a0 c0 e0 mode
+00 BRK JSR RTI RTS NOP* LDY CPY CPX Impl/immed
+01 ORA AND EOR ADC STA LDA CMP SBC (indir,x)
+02 t t t t NOP*t LDX NOP*t NOP*t ? /immed
+03 SLO* RLA* SRE* RRA* SAX* LAX* DCP* ISB* (indir,x)
+04 NOP* BIT NOP* NOP* STY LDY CPY CPX Zeropage
+05 ORA AND EOR ADC STA LDA CMP SBC Zeropage
+06 ASL ROL LSR ROR STX LDX DEC INC Zeropage
+07 SLO* RLA* SRE* RRA* SAX* LAX* DCP* ISB* Zeropage
+08 PHP PLP PHA PLA DEY TAY INY INX Implied
+09 ORA AND EOR ADC NOP* LDA CMP SBC Immediate
+0a ASL ROL LSR ROR TXA TAX DEX NOP Accu/impl
+0b ANC** ANC** ASR** ARR** ANE** LXA** SBX** SBC* Immediate
+0c NOP* BIT JMP JMP () STY LDY CPY CPX Absolute
+0d ORA AND EOR ADC STA LDA CMP SBC Absolute
+0e ASL ROL LSR ROR STX LDX DEC INC Absolute
+0f SLO* RLA* SRE* RRA* SAX* LAX* DCP* ISB* Absolute
+10 BPL BMI BVC BVS BCC BCS BNE BEQ Relative
+11 ORA AND EOR ADC STA LDA CMP SBC (indir),y
+12 t t t t t t t t ?
+13 SLO* RLA* SRE* RRA* SHA** LAX* DCP* ISB* (indir),y
+14 NOP* NOP* NOP* NOP* STY LDY NOP* NOP* Zeropage,x
+15 ORA AND EOR ADC STA LDA CMP SBC Zeropage,x
+16 ASL ROL LSR ROR STX y) LDX y) DEC INC Zeropage,x
+17 SLO* RLA* SRE* RRA* SAX* y) LAX* y) DCP* ISB* Zeropage,x
+18 CLC SEC CLI SEI TYA CLV CLD SED Implied
+19 ORA AND EOR ADC STA LDA CMP SBC Absolute,y
+1a NOP* NOP* NOP* NOP* TXS TSX NOP* NOP* Implied
+1b SLO* RLA* SRE* RRA* SHS** LAS** DCP* ISB* Absolute,y
+1c NOP* NOP* NOP* NOP* SHY** LDY NOP* NOP* Absolute,x
+1d ORA AND EOR ADC STA LDA CMP SBC Absolute,x
+1e ASL ROL LSR ROR SHX**y) LDX y) DEC INC Absolute,x
+1f SLO* RLA* SRE* RRA* SHA**y) LAX* y) DCP* ISB* Absolute,x
ROR intruction is available on MC650x microprocessors after
June, 1976.
Legend:
t Jams the machine
*t Jams very rarely
* Undocumented command
** Unusual operation
y) indexed using Y instead of X
() indirect instead of absolute
Note that the NOP instructions do have other addressing modes than the
implied addressing. The NOP instruction is just like any other load
instruction, except it does not store the result anywhere nor affects the
flags.
6502 Registers
The NMOS 65xx processors are not ruined with too many registers. In addition
to that, the registers are mostly 8-bit. Here is a brief description of each
register:
PC Program Counter
This register points the address from which the next instruction
byte (opcode or parameter) will be fetched. Unlike other
registers, this one is 16 bits in length. The low and high 8-bit
halves of the register are called PCL and PCH, respectively. The
Program Counter may be read by pushing its value on the stack.
This can be done either by jumping to a subroutine or by causing
an interrupt.
S Stack pointer
The NMOS 65xx processors have 256 bytes of stack memory, ranging
from $0100 to $01FF. The S register is a 8-bit offset to the stack
page. In other words, whenever anything is being pushed on the
stack, it will be stored to the address $0100+S.
The Stack pointer can be read and written by transfering its value
to or from the index register X (see below) with the TSX and TXS
instructions.
P Processor status
This 8-bit register stores the state of the processor. The bits in
this register are called flags. Most of the flags have something
to do with arithmetic operations.
The P register can be read by pushing it on the stack (with PHP or
by causing an interrupt). If you only need to read one flag, you
can use the branch instructions. Setting the flags is possible by
pulling the P register from stack or by using the flag set or
clear instructions.
Following is a list of the flags, starting from the 8th bit of the
P register (bit 7, value $80):
N Negative flag
This flag will be set after any arithmetic operations
(when any of the registers A, X or Y is being loaded
with a value). Generally, the N flag will be copied from
the topmost bit of the register being loaded.
Note that TXS (Transfer X to S) is not an arithmetic
operation. Also note that the BIT instruction affects
the Negative flag just like arithmetic operations.
Finally, the Negative flag behaves differently in
Decimal operations (see description below).
V oVerflow flag
Like the Negative flag, this flag is intended to be used
with 8-bit signed integer numbers. The flag will be
affected by addition and subtraction, the instructions
PLP, CLV and BIT, and the hardware signal -SO. Note that
there is no SEV instruction, even though the MOS
engineers loved to use East European abbreviations, like
DDR (Deutsche Demokratische Republik vs. Data Direction
Register). (The Russian abbreviation for their former
trade association COMECON is SEV.) The -SO (Set
Overflow) signal is available on some processors, at
least the 6502, to set the V flag. This enables response
to an I/O activity in equal or less than three clock
cycles when using a BVC instruction branching to itself
($50 $FE).
The CLV instruction clears the V flag, and the PLP and
BIT instructions copy the flag value from the bit 6 of
the topmost stack entry or from memory.
After a binary addition or subtraction, the V flag will
be set on a sign overflow, cleared otherwise. What is a
sign overflow? For instance, if you are trying to add
123 and 45 together, the result (168) does not fit in a
8-bit signed integer (upper limit 127 and lower limit
-128). Similarly, adding -123 to -45 causes the
overflow, just like subtracting -45 from 123 or 123 from
-45 would do.
Like the N flag, the V flag will not be set as expected
in the Decimal mode. Later in this document is a precise
operation description.
A common misbelief is that the V flag could only be set
by arithmetic operations, not cleared.
1 unused flag
To the current knowledge, this flag is always 1.
B Break flag
This flag is used to distinguish software (BRK)
interrupts from hardware interrupts (IRQ or NMI). The B
flag is always set except when the P register is being
pushed on stack when jumping to an interrupt routine to
process only a hardware interrupt.
The official NMOS 65xx documentation claims that the BRK
instruction could only cause a jump to the IRQ vector
($FFFE). However, if an NMI interrupt occurs while
executing a BRK instruction, the processor will jump to
the NMI vector ($FFFA), and the P register will be
pushed on the stack with the B flag set.
D Decimal mode flag
This flag is used to select the (Binary Coded) Decimal
mode for addition and subtraction. In most applications,
the flag is zero.
The Decimal mode has many oddities, and it operates
differently on CMOS processors. See the description of
the ADC, SBC and ARR instructions below.
I Interrupt disable flag
This flag can be used to prevent the processor from
jumping to the IRQ handler vector ($FFFE) whenever the
hardware line -IRQ is active. The flag will be
automatically set after taking an interrupt, so that the
processor would not keep jumping to the interrupt
routine if the -IRQ signal remains low for several clock
cycles.
Z Zero flag
The Zero flag will be affected in the same cases than
the Negative flag. Generally, it will be set if an
arithmetic register is being loaded with the value zero,
and cleared otherwise. The flag will behave differently
in Decimal operations.
C Carry flag
This flag is used in additions, subtractions,
comparisons and bit rotations. In additions and
subtractions, it acts as a 9th bit and lets you to chain
operations to calculate with bigger than 8-bit numbers.
When subtracting, the Carry flag is the negative of
Borrow: if an overflow occurs, the flag will be clear,
otherwise set. Comparisons are a special case of
subtraction: they assume Carry flag set and Decimal flag
clear, and do not store the result of the subtraction
anywhere.
There are four kinds of bit rotations. All of them store
the bit that is being rotated off to the Carry flag. The
left shifting instructions are ROL and ASL. ROL copies
the initial Carry flag to the lowmost bit of the byte;
ASL always clears it. Similarly, the ROR and LSR
instructions shift to the right.
A Accumulator
The accumulator is the main register for arithmetic and logic
operations. Unlike the index registers X and Y, it has a direct
connection to the Arithmetic and Logic Unit (ALU). This is why
many operations are only available for the accumulator, not the
index registers.
X Index register X
This is the main register for addressing data with indices. It has
a special addressing mode, indexed indirect, which lets you to
have a vector table on the zero page.
Y Index register Y
The Y register has the least operations available. On the other
hand, only it has the indirect indexed addressing mode that
enables access to any memory place without having to use
self-modifying code.
6510/8502 Undocumented Commands
-- A brief explanation about what may happen while using don't care states.
ANE $8B A = (A | #$EE) & X & #byte
same as
A = ((A & #$11 & X) | ( #$EE & X)) & #byte
In real 6510/8502 the internal parameter #$11
may occasionally be #$10, #$01 or even #$00.
This occurs when the video chip starts DMA
between the opcode fetch and the parameter fetch
of the instruction. The value probably depends
on the data that was left on the bus by the VIC-II.
LXA $AB C=Lehti: A = X = ANE
Alternate: A = X = (A & #byte)
TXA and TAX have to be responsible for these.
SHA $93,$9F Store (A & X & (ADDR_HI + 1))
SHX $9E Store (X & (ADDR_HI + 1))
SHY $9C Store (Y & (ADDR_HI + 1))
SHS $9B SHA and TXS, where X is replaced by (A & X).
Note: The value to be stored is copied also
to ADDR_HI if page boundary is crossed.
SBX $CB Carry and Decimal flags are ignored but the
Carry flag will be set in substraction. This
is due to the CMP command, which is executed
instead of the real SBC.
ARR $6B This instruction first performs an AND
between the accumulator and the immediate
parameter, then it shifts the accumulator to
the right. However, this is not the whole
truth. See the description below.
Many undocumented commands do not use AND between registers, the CPU
just throws the bytes to a bus simultaneously and lets the
open-collector drivers perform the AND. I.e. the command called 'SAX',
which is in the STORE section (opcodes $A0...$BF), stores the result
of (A & X) by this way.
More fortunate is its opposite, 'LAX' which just loads a byte
simultaneously into both A and X.
$6B ARR
This instruction seems to be a harmless combination of AND and ROR at
first sight, but it turns out that it affects the V flag and also has
a special kind of decimal mode. This is because the instruction has
inherited some properties of the ADC instruction ($69) in addition to
the ROR ($6A).
In Binary mode (D flag clear), the instruction effectively does an AND
between the accumulator and the immediate parameter, and then shifts
the accumulator to the right, copying the C flag to the 8th bit. It
sets the Negative and Zero flags just like the ROR would. The ADC code
shows up in the Carry and oVerflow flags. The C flag will be copied
from the bit 6 of the result (which doesn't seem too logical), and the
V flag is the result of an Exclusive OR operation between the bit 6
and the bit 5 of the result. This makes sense, since the V flag will
be normally set by an Exclusive OR, too.
In Decimal mode (D flag set), the ARR instruction first performs the
AND and ROR, just like in Binary mode. The N flag will be copied from
the initial C flag, and the Z flag will be set according to the ROR
result, as expected. The V flag will be set if the bit 6 of the
accumulator changed its state between the AND and the ROR, cleared
otherwise.
Now comes the funny part. If the low nybble of the AND result,
incremented by its lowmost bit, is greater than 5, the low nybble in
the ROR result will be incremented by 6. The low nybble may overflow
as a consequence of this BCD fixup, but the high nybble won't be
adjusted. The high nybble will be BCD fixed in a similar way. If the
high nybble of the AND result, incremented by its lowmost bit, is
greater than 5, the high nybble in the ROR result will be incremented
by 6, and the Carry flag will be set. Otherwise the C flag will be
cleared.
To help you understand this description, here is a C routine that
illustrates the ARR operation in Decimal mode:
unsigned
A, /* Accumulator */
AL, /* low nybble of accumulator */
AH, /* high nybble of accumulator */
C, /* Carry flag */
Z, /* Zero flag */
V, /* oVerflow flag */
N, /* Negative flag */
t, /* temporary value */
s; /* value to be ARRed with Accumulator */
t = A & s; /* Perform the AND. */
AH = t >> 4; /* Separate the high */
AL = t & 15; /* and low nybbles. */
N = C; /* Set the N and */
Z = !(A = (t >> 1) | (C << 7)); /* Z flags traditionally */
V = (t ^ A) & 64; /* and V flag in a weird way. */
if (AL + (AL & 1) > 5) /* BCD "fixup" for low nybble. */
A = (A & 0xF0) | ((A + 6) & 0xF);
if (C = AH + (AH & 1) > 5) /* Set the Carry flag. */
A = (A + 0x60) & 0xFF; /* BCD "fixup" for high nybble. */
$CB SBX X <- (A & X) - Immediate
The 'SBX' ($CB) may seem to be very complex operation, even though it
is a combination of the subtraction of accumulator and parameter, as
in the 'CMP' instruction, and the command 'DEX'. As a result, both A
and X are connected to ALU but only the subtraction takes place. Since
the comparison logic was used, the result of subtraction should be
normally ignored, but the 'DEX' now happily stores to X the value of
(A & X) - Immediate. That is why this instruction does not have any
decimal mode, and it does not affect the V flag. Also Carry flag will
be ignored in the subtraction but set according to the result.
Proof:
begin 644 vsbx
M`0@9$,D'GL(H-#,IJC(U-JS"*#0T*:HR-@```*D`H#V1*Z`_D2N@09$KJ0>%
M^QBE^VEZJ+$KH#F1*ZD`2"BI`*(`RP`(:-B@.5$K*4#P`E@`H#VQ*SAI`)$K
JD-Z@/[$K:0"1*Y#4J2X@TO\XH$&Q*VD`D2N0Q,;[$+188/_^]_:_OK>V
`
end
and
begin 644 sbx
M`0@9$,D'GL(H-#,IJC(U-JS"*#0T*:HR-@```'BI`*!-D2N@3Y$KH%&1*ZD#
MA?L8I?M*2)`#J1@LJ3B@29$K:$J0`ZGX+*G8R)$K&/BXJ?2B8\L)AOP(:(7]
MV#B@3;$KH$\Q*Z!1\2L(1?SP`0!H1?TIM]#XH$VQ*SAI`)$KD,N@3[$K:0"1
9*Y#!J2X@TO\XH%&Q*VD`D2N0L<;[$))88-#X
`
end
These test programs show if your machine is compatible with ours
regarding the opcode $CB. The first test, vsbx, proves that SBX does
not affect the V flag. The latter one, sbx, proves the rest of our
theory. The vsbx test tests 33554432 SBX combinations (16777216
different A, X and Immediate combinations, and two different V flag
states), and the sbx test doubles that amount (16777216*4 D and C flag
combinations). Both tests have run successfully on a C64 and a Vic20.
They ought to run on C16, +4 and the PET series as well. The tests
stop with BRK, if the opcode $CB does not work as expected. Successful
operation ends in RTS. As the tests are very slow, they print dots on
the screen while running so that you know that the machine has not
jammed. On computers running at 1 MHz, the first test prints
approximately one dot every four seconds and a total of 2048 dots,
whereas the second one prints half that amount, one dot every seven
seconds.
If the tests fail on your machine, please let us know your processor's
part number and revision. If possible, save the executable (after it
has stopped with BRK) under another name and send it to us so that we
know at which stage the program stopped.
The following program is a Commodore 64 executable that Marko M"akel"a
developed when trying to find out how the V flag is affected by SBX.
(It was believed that the SBX affects the flag in a weird way, and
this program shows how SBX sets the flag differently from SBC.) You
may find the subroutine at $C150 useful when researching other
undocumented instructions' flags. Run the program in a machine
language monitor, as it makes use of the BRK instruction. The result
tables will be written on pages $C2 and $C3.
begin 644 sbx-c100
M`,%XH`",#L&,$,&,$L&XJ8*B@LL7AOL(:(7\N#BM#L$M$,'M$L$(Q?OP`B@`
M:$7\\`,@4,'N#L'0U.X0P=#/SB#0[A+!T,<``````````````)BJ\!>M#L$M
L$,'=_\'0":T2P=W_PM`!8,K0Z:T.P2T0P9D`PID`!*T2P9D`PYD`!
Other undocumented instructions usually cause two preceding opcodes
being executed. However 'NOP' seems to completely disappear from 'SBC'
code $EB.
The most difficult to comprehend are the rest of the instructions
located on the '$0B' line.
All the instructions located at the positive (left) side of this line
should rotate either memory or the accumulator, but the addressing
mode turns out to be immediate! No problem. Just read the operand, let
it be ANDed with the accumulator and finally use accumulator
addressing mode for the instructions above them.
RELIGION_MODE_ON
/* This part of the document is not accurate. You can
read it as a fairy tale, but do not count on it when
performing your own measurements. */
The rest two instructions on the same line, called 'ANE' and 'LXA'
($8B and $AB respectively) often give quite unpredictable results.
However, the most usual operation is to store ((A | #$ee) & X & #$nn)
to accumulator. Note that this does not work reliably in a real 64!
In the Commodore 128 the opcode $8B uses values 8C, CC, EE, and
occasionally 0C and 8E for the OR instead of EE,EF,FE and FF used in
the C64. With a C128 running at 2 MHz #$EE is always used. Opcode $AB
does not cause this OR taking place on 8502 while 6510 always performs
it. Note that this behaviour depends on processor and/or video chip
revision.
Let's take a closer look at $8B (6510).
A <- X & D & (A | VAL)
where VAL comes from this table:
X high D high D low VAL
even even --- $EE (1)
even odd --- $EE
odd even --- $EE
odd odd 0 $EE
odd odd not 0 $FE (2)
(1) If the bottom 2 bits of A are both 1, then the LSB of the result may
be 0. The values of X and D are different every time I run the test.
This appears to be very rare.
(2) VAL is $FE most of the time. Sometimes it is $EE - it seems to be random,
not related to any of the data. This is much more common than (1).
In decimal mode, VAL is usually $FE.
Two different functions have been discovered for LAX, opcode $AB. One
is A = X = ANE (see above) and the other, encountered with 6510 and
8502, is less complicated A = X = (A & #byte). However, according to
what is reported, the version altering only the lowest bits of each
nybble seems to be more common.
What happens, is that $AB loads a value into both A and X, ANDing the
low bit of each nybble with the corresponding bit of the old
A. However, there are exceptions. Sometimes the low bit is cleared
even when A contains a '1', and sometimes other bits are cleared. The
exceptions seem random (they change every time I run the test). Oops -
that was in decimal mode. Much the same with D=0.
What causes the randomness? Probably it is that it is marginal logic
levels - when too much wired-anding goes on, some of the signals get
very close to the threshold. Perhaps we're seeing some of them step
over it. The low bit of each nybble is special, since it has to cope
with carry differently (remember decimal mode). We never see a '0'
turn into a '1'.
Since these instructions are unpredictable, they should not be used.
There is still very strange instruction left, the one named SHA/X/Y,
which is the only one with only indexed addressing modes. Actually,
the commands 'SHA', 'SHX' and 'SHY' are generated by the indexing
algorithm.
While using indexed addressing, effective address for page boundary
crossing is calculated as soon as possible so it does not slow down
operation. As a result, in the case of SHA/X/Y, the address and data
are processed at the same time making AND between them to take place.
Thus, the value to be stored by SAX, for example, is in fact (A & X &
(ADDR_HI + 1)). On page boundary crossing the same value is copied
also to high byte of the effective address.
RELIGION_MODE_OFF
Register selection for load and store
bit1 bit0 A X Y
0 0 x
0 1 x
1 0 x
1 1 x x
So, A and X are selected by bits 1 and 0 respectively, while
~(bit1|bit0) enables Y.
Indexing is determined by bit4, even in relative addressing mode,
which is one kind of indexing.
Lines containing opcodes xxx000x1 (01 and 03) are treated as absolute
after the effective address has been loaded into CPU.
Zeropage,y and Absolute,y (codes 10x1 x11x) are distinquished by bit5.
Decimal mode in NMOS 6500 series
Most sources claim that the NMOS 6500 series sets the N, V and Z
flags unpredictably when performing addition or subtraction in decimal
mode. Of course, this is not true. While testing how the flags are
set, I also wanted to see what happens if you use illegal BCD values.
ADC works in Decimal mode in a quite complicated way. It is amazing
how it can do that all in a single cycle. Here's a C code version of
the instruction:
unsigned
A, /* Accumulator */
AL, /* low nybble of accumulator */
AH, /* high nybble of accumulator */
C, /* Carry flag */
Z, /* Zero flag */
V, /* oVerflow flag */
N, /* Negative flag */
s; /* value to be added to Accumulator */
AL = (A & 15) + (s & 15) + C; /* Calculate the lower nybble. */
AH = (A >> 4) + (s >> 4) + (AL > 15); /* Calculate the upper nybble. */
if (AL > 9) AL += 6; /* BCD fixup for lower nybble. */
Z = ((A + s + C) & 255 != 0); /* Zero flag is set just
like in Binary mode. */
/* Negative and Overflow flags are set with the same logic than in
Binary mode, but after fixing the lower nybble. */
N = (AH & 8 != 0);
V = ((AH << 4) ^ A) & 128 && !((A ^ s) & 128);
if (AH > 9) AH += 6; /* BCD fixup for upper nybble. */
/* Carry is the only flag set after fixing the result. */
C = (AH > 15);
A = ((AH << 4) | (AL & 15)) & 255;
The C flag is set as the quiche eaters expect, but the N and V flags
are set after fixing the lower nybble but before fixing the upper one.
They use the same logic than binary mode ADC. The Z flag is set before
any BCD fixup, so the D flag does not have any influence on it.
Proof: The following test program tests all 131072 ADC combinations in
Decimal mode, and aborts with BRK if anything breaks this theory.
If everything goes well, it ends in RTS.
begin 600 dadc
M 0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@ 'BI&* A/N$_$B@+)$KH(V1
M*Q@(I?PI#X7]I?LI#V7]R0J0 FD%J"D/A?VE^RGP9?PI\ C $) ":0^JL @H
ML ?)H) &""@X:5\X!?V%_0AH*3W@ ! ""8"HBD7[$ JE^T7\, 28"4"H**7[
M9?S0!)@) J@8N/BE^V7\V A%_= G:(3]1?W0(.;[T(?F_-"#:$D8\ )88*D=
0&&4KA?NI &4LA?RI.&S[ A%
end
All programs in this chapter have been successfully tested on a Vic20
and a Commodore 64 and a Commodore 128D in C64 mode. They should run on
C16, +4 and on the PET series as well. If not, please report the problem
to Marko M"akel"a. Each test in this chapter should run in less than a
minute at 1 MHz.
SBC is much easier. Just like CMP, its flags are not affected by
the D flag.
Proof:
begin 600 dsbc-cmp-flags
M 0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@ 'B@ (3[A/RB XH8:66HL2N@
M09$KH$R1*XII::BQ*Z!%D2N@4)$K^#BXI?OE_-@(:(7].+BE^^7\"&A%_? !
5 .;[T./F_-#?RA"_8!@X&#CEY<7%
end
The only difference in SBC's operation in decimal mode from binary mode
is the result-fixup:
unsigned
A, /* Accumulator */
AL, /* low nybble of accumulator */
AH, /* high nybble of accumulator */
C, /* Carry flag */
Z, /* Zero flag */
V, /* oVerflow flag */
N, /* Negative flag */
s; /* value to be added to Accumulator */
AL = (A & 15) - (s & 15) - !C; /* Calculate the lower nybble. */
if (AL & 16) AL -= 6; /* BCD fixup for lower nybble. */
AH = (A >> 4) - (s >> 4) - (AL & 16); /* Calculate the upper nybble. */
if (AH & 16) AH -= 6; /* BCD fixup for upper nybble. */
/* The flags are set just like in Binary mode. */
C = (A - s - !C) & 256 != 0;
Z = (A - s - !C) & 255 != 0;
V = ((A - s - !C) ^ s) & 128 && (A ^ s) & 128;
N = (A - s - !C) & 128 != 0;
A = ((AH << 4) | (AL & 15)) & 255;
Again Z flag is set before any BCD fixup. The N and V flags are set
at any time before fixing the high nybble. The C flag may be set in any
phase.
Decimal subtraction is easier than decimal addition, as you have to
make the BCD fixup only when a nybble overflows. In decimal addition,
you had to verify if the nybble was greater than 9. The processor has
an internal "half carry" flag for the lower nybble, used to trigger
the BCD fixup. When calculating with legal BCD values, the lower nybble
cannot overflow again when fixing it.
So, the processor does not handle overflows while performing the fixup.
Similarly, the BCD fixup occurs in the high nybble only if the value
overflows, i.e. when the C flag will be cleared.
Because SBC's flags are not affected by the Decimal mode flag, you
could guess that CMP uses the SBC logic, only setting the C flag
first. But the SBX instruction shows that CMP also temporarily clears
the D flag, although it is totally unnecessary.
The following program, which tests SBC's result and flags,
contains the 6502 version of the pseudo code example above.
begin 600 dsbc
M 0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@ 'BI&* A/N$_$B@+)$KH':1
M*S@(I?PI#X7]I?LI#^7]L /I!1@I#ZBE_"GPA?VE^RGP"#CE_2GPL KI7RBP
M#ND/.+ )*+ &Z0^P NE?A/T%_87]*+BE^^7\"&BH.+CXI?OE_-@(1?W0FVB$
8_47]T)3F^]">YOS0FFA)&- $J3C0B%A@
end
Obviously the undocumented instructions RRA (ROR+ADC) and ISB
(INC+SBC) have inherited also the decimal operation from the official
instructions ADC and SBC. The program droradc proves this statement
for ROR, and the dincsbc test proves this for ISB. Finally,
dincsbc-deccmp proves that ISB's and DCP's (DEC+CMP) flags are not
affected by the D flag.
begin 644 droradc
M`0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@```'BI&*``A/N$_$B@+)$KH(V1
M*S@(I?PI#X7]I?LI#V7]R0J0`FD%J"D/A?VE^RGP9?PI\`C`$)`":0^JL`@H
ML`?)H)`&""@X:5\X!?V%_0AH*3W@`!`""8"HBD7[$`JE^T7\,`28"4"H**7[
M9?S0!)@)`J@XN/BE^R;\9_S8"$7]T"=HA/U%_=`@YOO0A>;\T(%H21CP`EA@
2J1T892N%^ZD`92R%_*DX;/L`
`
end
begin 644 dincsbc
M`0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@```'BI&*``A/N$_$B@+)$KH':1
M*S@(I?PI#X7]I?LI#^7]L`/I!1@I#ZBE_"GPA?VE^RGP"#CE_2GPL`KI7RBP
M#ND/.+`)*+`&Z0^P`NE?A/T%_87]*+BE^^7\"&BH.+CXI?O&_.?\V`A%_="9
::(3]1?W0DN;[T)SF_-"8:$D8T`2I.-"&6&#\
`
end
begin 644 dincsbc-deccmp
M`0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@```'B@`(3[A/RB`XH8:7>HL2N@
M3Y$KH%R1*XII>ZBQ*Z!3D2N@8)$KBFE_J+$KH%61*Z!BD2OX.+BE^^;\Q_S8
L"&B%_3BXI?OF_,?\"&A%_?`!`.;[T-_F_-#;RA"M8!@X&#CFYL;&Q\?GYP#8
`
end
6510 features
o PHP always pushes the Break (B) flag as a `1' to the stack.
Jukka Tapanim"aki claimed in C=lehti issue 3/89, on page 27 that the
processor makes a logical OR between the status register's bit 4
and the bit 8 of the stack pointer register (which is always 1).
He did not give any reasons for this argument, and has refused to clarify
it afterwards. Well, this was not the only error in his article...
o Indirect addressing modes do not handle page boundary crossing at all.
When the parameter's low byte is $FF, the effective address wraps
around and the CPU fetches high byte from $xx00 instead of $xx00+$0100.
E.g. JMP ($01FF) fetches PCL from $01FF and PCH from $0100,
and LDA ($FF),Y fetches the base address from $FF and $00.
o Indexed zero page addressing modes never fix the page address on
crossing the zero page boundary.
E.g. LDX #$01 : LDA ($FF,X) loads the effective address from $00 and $01.
o The processor always fetches the byte following a relative branch
instruction. If the branch is taken, the processor reads then the
opcode from the destination address. If page boundary is crossed, it
first reads a byte from the old page from a location that is bigger
or smaller than the correct address by one page.
o If you cross a page boundary in any other indexed mode,
the processor reads an incorrect location first, a location that is
smaller by one page.
o Read-Modify-Write instructions write unmodified data, then modified
(so INC effectively does LDX loc;STX loc;INX;STX loc)
o -RDY is ignored during writes
(This is why you must wait 3 cycles before doing any DMA --
the maximum number of consecutive writes is 3, which occurs
during interrupts except -RESET.)
o Some undefined opcodes may give really unpredictable results.
o All registers except the Program Counter remain unmodified after -RESET.
(This is why you must preset D and I flags in the RESET handler.)
Different CPU types
The Rockwell data booklet 29651N52 (technical information about R65C00
microprocessors, dated October 1984), lists the following differences between
NMOS R6502 microprocessor and CMOS R65C00 family:
1. Indexed addressing across page boundary.
NMOS: Extra read of invalid address.
CMOS: Extra read of last instruction byte.
2. Execution of invalid op codes.
NMOS: Some terminate only by reset. Results are undefined.
CMOS: All are NOPs (reserved for future use).
3. Jump indirect, operand = XXFF.
NMOS: Page address does not increment.
CMOS: Page address increments and adds one additional cycle.
4. Read/modify/write instructions at effective address.
NMOS: One read and two write cycles.
CMOS: Two read and one write cycle.
5. Decimal flag.
NMOS: Indeterminate after reset.
CMOS: Initialized to binary mode (D=0) after reset and interrupts.
6. Flags after decimal operation.
NMOS: Invalid N, V and Z flags.
CMOS: Valid flag adds one additional cycle.
7. Interrupt after fetch of BRK instruction.
NMOS: Interrupt vector is loaded, BRK vector is ignored.
CMOS: BRK is executed, then interrupt is executed.
6510 Instruction Timing
The NMOS 6500 series processors always perform at least two reads
for each instruction. In addition to the operation code (opcode), they
fetch the next byte. This is quite efficient, as most instructions are
two or three bytes long.
The processors also use a sort of pipelining. If an instruction does
not store data in memory on its last cycle, the processor can fetch
the opcode of the next instruction while executing the last cycle. For
instance, the instruction EOR #$FF truly takes three cycles. On the
first cycle, the opcode $49 will be fetched. During the second cycle
the processor decodes the opcode and fetches the parameter #$FF. On
the third cycle, the processor will perform the operation and store
the result to accumulator, but simultaneously it fetches the opcode
for the next instruction. This is why the instruction effectively
takes only two cycles.
The following tables show what happens on the bus while executing
different kinds of instructions.
Interrupts
NMI and IRQ both take 7 cycles. Their timing diagram is much like
BRK's (see below). IRQ will be executed only when the I flag is
clear. IRQ and BRK both set the I flag, whereas the NMI does not
affect its state.
The processor will usually wait for the current instruction to
complete before executing the interrupt sequence. To process the
interrupt before the next instruction, the interrupt must occur
before the last cycle of the current instruction.
There is one exception to this rule: the BRK instruction. If a
hardware interrupt (NMI or IRQ) occurs before the fourth (flags
saving) cycle of BRK, the BRK instruction will be skipped, and
the processor will jump to the hardware interrupt vector. This
sequence will always take 7 cycles.
You do not completely lose the BRK interrupt, the B flag will be
set in the pushed status register if a BRK instruction gets
interrupted. When BRK and IRQ occur at the same time, this does
not cause any problems, as your program will consider it as a
BRK, and the IRQ would occur again after the processor returned
from your BRK routine, unless you cleared the interrupt source in
your BRK handler. But the simultaneous occurrence of NMI and BRK
is far more fatal. If you do not check the B flag in the NMI
routine and subtract two from the return address when needed, the
BRK instruction will be skipped.
If the NMI and IRQ interrupts overlap each other (one interrupt
occurs before fetching the interrupt vector for the other
interrupt), the processor will most probably jump to the NMI
vector in every case, and then jump to the IRQ vector after
processing the first instruction of the NMI handler. This has not
been measured yet, but the IRQ is very similar to BRK, and many
sources state that the NMI has higher priority than IRQ. However,
it might be that the processor takes the interrupt that comes
later, i.e. you could lose an NMI interrupt if an IRQ occurred in
four cycles after it.
After finishing the interrupt sequence, the processor will start
to execute the first instruction of the interrupt routine. This
proves that the processor uses a sort of pipelining: it finishes
the current instruction (or interrupt sequence) while reading the
opcode of the next instruction.
RESET does not push program counter on stack, and it lasts
probably 6 cycles after deactivating the signal. Like NMI, RESET
preserves all registers except PC.
Instructions accessing the stack
BRK
# address R/W description
--- ------- --- -----------------------------------------------
1 PC R fetch opcode, increment PC
2 PC R read next instruction byte (and throw it away),
increment PC
3 $0100,S W push PCH on stack (with B flag set), decrement S
4 $0100,S W push PCL on stack, decrement S
5 $0100,S W push P on stack, decrement S
6 $FFFE R fetch PCL
7 $FFFF R fetch PCH
RTI
# address R/W description
--- ------- --- -----------------------------------------------
1 PC R fetch opcode, increment PC
2 PC R read next instruction byte (and throw it away)
3 $0100,S R increment S
4 $0100,S R pull P from stack, increment S
5 $0100,S R pull PCL from stack, increment S
6 $0100,S R pull PCH from stack
RTS
# address R/W description
--- ------- --- -----------------------------------------------
1 PC R fetch opcode, increment PC
2 PC R read next instruction byte (and throw it away)
3 $0100,S R increment S
4 $0100,S R pull PCL from stack, increment S
5 $0100,S R pull PCH from stack
6 PC R increment PC
PHA, PHP
# address R/W description
--- ------- --- -----------------------------------------------
1 PC R fetch opcode, increment PC
2 PC R read next instruction byte (and throw it away)
3 $0100,S W push register on stack, decrement S
PLA, PLP
# address R/W description
--- ------- --- -----------------------------------------------
1 PC R fetch opcode, increment PC
2 PC R read next instruction byte (and throw it away)
3 $0100,S R increment S
4 $0100,S R pull register from stack
JSR
# address R/W description
--- ------- --- -------------------------------------------------
1 PC R fetch opcode, increment PC
2 PC R fetch low address byte, increment PC
3 $0100,S R internal operation (predecrement S?)
4 $0100,S W push PCH on stack, decrement S
5 $0100,S W push PCL on stack, decrement S
6 PC R copy low address byte to PCL, fetch high address
byte to PCH
Accumulator or implied addressing
# address R/W description
--- ------- --- -----------------------------------------------
1 PC R fetch opcode, increment PC
2 PC R read next instruction byte (and throw it away)
Immediate addressing
# address R/W description
--- ------- --- ------------------------------------------
1 PC R fetch opcode, increment PC
2 PC R fetch value, increment PC
Absolute addressing
JMP
# address R/W description
--- ------- --- -------------------------------------------------
1 PC R fetch opcode, increment PC
2 PC R fetch low address byte, increment PC
3 PC R copy low address byte to PCL, fetch high address
byte to PCH
Read instructions (LDA, LDX, LDY, EOR, AND, ORA, ADC, SBC, CMP, BIT,
LAX, NOP)
# address R/W description
--- ------- --- ------------------------------------------
1 PC R fetch opcode, increment PC
2 PC R fetch low byte of address, increment PC
3 PC R fetch high byte of address, increment PC
4 address R read from effective address
Read-Modify-Write instructions (ASL, LSR, ROL, ROR, INC, DEC,
SLO, SRE, RLA, RRA, ISB, DCP)
# address R/W description
--- ------- --- ------------------------------------------
1 PC R fetch opcode, increment PC
2 PC R fetch low byte of address, increment PC
3 PC R fetch high byte of address, increment PC
4 address R read from effective address