§ Описание

Процессор предназначен для встраивания в схемы Dendy.
  • clock — тактовая частота, 1.7 Мгц
  • locked — если 1, процессор активен
  • reset_n — сброс, если 0
  • address, in, out, we — интерфейс работы с памятью
  • rd — строб 1, если только что было запрошено чтение с памяти, необходимо для PPU

§ Исходный код

Последнее обновление: 22 мар 2024
1/* verilator lint_off WIDTH */
2/* verilator lint_off CASEX */
3/* verilator lint_off CASEOVERLAP */
4/* verilator lint_off CASEINCOMPLETE */
5
6module nes
7(
8    input               clock,
9    input               locked,
10    input               reset_n,
11    input               irq,
12    output      [15:0]  address,
13    input       [ 7:0]  in,
14    output reg  [ 7:0]  out,
15    output reg          we,
16    output reg          rd
17);
18
19// Источник памяти
20assign address = mm ? cp : pc;
21
22// Способы адресации
23localparam
24
25    MAIN = 8'h00,
26    ZP   = 8'h01, ZPX  = 8'h02, ZPY  = 8'h03,
27    ABS  = 8'h04, JSR  = 8'h05, IND  = 8'h06,
28    ABX  = 8'h07, ABY  = 8'h08, REL  = 8'h09,
29    NDX  = 8'h0A, NDY  = 8'h0B, RUN  = 8'h0C,
30    DLY  = 8'h0D, ABS2 = 8'h0E, ABX2 = 8'h0F,
31    NDX2 = 8'h10, NDX3 = 8'h11, NDY2 = 8'h12,
32    NDY3 = 8'h13, REL2 = 8'h14, REL3 = 8'h15,
33    RTS  = 8'h16, BRK  = 8'h17, RTI  = 8'h18;
34
35// АЛУ
36localparam
37    ORA =  0, AND =  1, EOR =  2, ADC =  3,
38    STA =  4, LDA =  5, CMP =  6, SBC =  7,
39    INC =  8, DEC =  9, ASL = 10, ROL = 11,
40    LSR = 12, ROR = 13, BIT = 14;
41
42localparam
43    DST_A = 0, DST_X = 1, DST_Y = 2, DST_D = 3,
44    SRC_D = 0, SRC_A = 1, SRC_X = 2, SRC_Y = 3;
45
46// Флаги
47localparam
48    CF = 0, ZF = 1, IF = 2, DF = 3,
49    BF = 4, XF = 5, VF = 6, NF = 7;
50
51// Регистры и состояние процессора
52// -----------------------------------------------------------------------------
53reg [ 7:0]  a, x, y, p, s;
54reg         mm, sta, utk, irq_prev;
55reg [15:0]  pc, cp;
56reg [ 7:0]  op, tm;
57reg [ 6:0]  t;
58reg [ 2:0]  m;
59reg [ 3:0]  alu;
60reg [ 1:0]  src, dst, vec;
61
62// Вычисление проводов
63// -----------------------------------------------------------------------------
64wire [15:0] pcn = pc + {{8{in[7]}},in} + 1;
65wire [15:0] pc2 = pc + 2;
66wire [ 7:0] zpx = x + in,
67            zpy = y + in;
68wire [ 8:0] ndy = tm + y;
69wire [ 7:0] sm  = s - 1,
70            sp  = s + 1;
71wire [ 3:0] brc = {p[NF], p[CF], p[VF], p[ZF]};
72
73// Учитывать дополнительный такт (utk=1 всегда учитывать)
74wire        nx1 = sta || cp[8]  || utk;
75wire        nx2 = sta || ndy[8] || utk;
76
77// Основная логика
78// -----------------------------------------------------------------------------
79always @(posedge clock)
80// Сброс процессора
81if (reset_n == 1'b0) begin
82
83    t   <= BRK;
84    vec <= 2'b10;       // RESET
85    m   <= 3;
86    pc  <= 16'h0000;
87    mm  <= 1;
88    we  <= 0;
89    a   <= 8'h84;
90    x   <= 8'h4F;
91    y   <= 8'h00;
92    s   <= 8'h3F;
93    //        NVxBDIZC
94    p   <= 8'b01000001;
95
96end
97// Исполнение опкодов
98else if (locked) begin
99
100    rd <= 1'b0;
101    we <= 1'b0;
102
103    case (t)
104
105        // Считывание и разбор опкода
106        MAIN: begin
107
108            t    <= 1;
109            m    <= 0;
110            op   <= in;
111            pc   <= pc + 1;
112            alu  <= in[7:5];
113            dst  <= DST_A;
114            src  <= SRC_D;
115            sta  <= 0;
116            utk  <= 0;
117            out  <= a;
118            vec  <= 2'b11;
119            irq_prev <= irq;
120
121            casex (in)
122            // Специальные
123            8'b000_000_00: t <= BRK;
124            8'b001_000_00: t <= JSR;
125            8'b010_000_00: t <= RTI;
126            8'b011_000_00: t <= RTS;
127            8'b011_011_00: t <= IND;
128            8'b10x_101_10: t <= ZPY;
129            // Общие
130            8'bxxx_001_xx: t <= ZP;
131            8'bxxx_101_xx: t <= ZPX;
132            8'bxxx_011_xx: t <= ABS;
133            8'bxxx_111_xx: t <= ABX;
134            8'bxxx_110_x1: t <= ABY;
135            8'bxxx_000_x1: t <= NDX;
136            8'bxxx_100_x1: t <= NDY;
137            8'bxxx_100_00: t <= REL;
138            default:       t <= RUN; // ACC, IMP, IMM , IND
139            endcase
140
141            // Выбор АЛУ
142            casex (in)
143            8'b1010_00x0,
144            8'b101x_x110, // LDX, LDY
145            8'b101x_x100: begin alu <= LDA; end
146            8'b1110_0000,
147            8'b1110_x100: begin dst <= DST_X; alu <= CMP; end
148            8'b1100_0000,
149            8'b1100_x100: begin dst <= DST_Y; alu <= CMP; end
150            // DEX, DEY
151            8'b1000_1000: begin dst <= DST_Y; alu <= DEC; end // 88 DEY
152            8'b1100_1000: begin dst <= DST_Y; alu <= INC; end // C8 INY
153            8'b1100_1010: begin dst <= DST_X; alu <= DEC; end // CA DEX
154            8'b1110_1000: begin dst <= DST_X; alu <= INC; end // E8 INX
155            // INC, DEC
156            8'b110x_x110: begin dst <= DST_D; alu <= DEC; utk <= 1; end
157            8'b111x_x110: begin dst <= DST_D; alu <= INC; utk <= 1; end
158            // ASL, ROR, LSR, ROL [MEM|ACC]
159            8'b0xxx_x110: begin alu <= ASL + in[6:5]; dst <= DST_D; utk <= 1; end
160            8'b0xx0_1010: begin alu <= ASL + in[6:5]; end
161            8'b0010_x100: begin alu <= BIT; end
162            // TRANSFER
163            8'b1001_1000: begin alu <= LDA; src <= SRC_Y; end // TYA
164            8'b1010_10x0: begin alu <= LDA; src <= SRC_A; end // TAX, TAY
165            8'b1000_1010: begin alu <= LDA; src <= SRC_X; end // TXA
166            endcase
167
168            // Вызов аппаратного прерывания NMI
169            if ({irq_prev, irq} == 2'b01) begin pc <= pc - 2; vec <= 2'b01; t <= BRK; end
170            else begin
171
172                // Дополнительная обработка инструкции
173                casex (in)
174                8'b100x_xx01: begin sta <= 1; end           // STA
175                8'b100x_x110: begin sta <= 1; out <= x; end // STX
176                8'b100x_x100: begin sta <= 1; out <= y; end // STY
177                // PHP, PHA
178                8'b0x00_1000: begin cp <= {8'h01, s};  s <= sm; out <= in[6] ? a : p | 8'h30; end
179                8'b0x10_1000: begin cp <= {8'h01, sp}; s <= sp; mm  <= 1; end
180                endcase
181
182            end
183
184        end
185
186        // Разбор метода адресации
187        // ---------------------------------------------------------------------
188
189        // 3*T ZEROPAGE
190        ZP:   begin t <= RUN; pc <= pc + 1; mm <= 1; cp <= in;  we <= sta; rd <= !sta; end
191        ZPX:  begin t <= DLY; pc <= pc + 1; mm <= 1; cp <= zpx; we <= sta; end
192        ZPY:  begin t <= DLY; pc <= pc + 1; mm <= 1; cp <= zpy; we <= sta; end
193
194        // 4T ABSOLUTE, JMP
195        ABS:  begin t <= ABS2; pc <= pc + 1; cp[7:0] <= in; end
196        ABS2: begin
197
198            if (op == 8'h4C) // JMP ABS
199                 begin t <= MAIN; pc <= {in, cp[7:0]}; end
200            else begin t <= RUN;  pc <= pc + 1; cp[15:8] <= in; mm <= 1; {we, rd} <= {sta, !sta}; end
201
202        end
203
204        // 4*T ABSOLUTE,INDEX
205        ABX:  begin t <= ABX2; pc <= pc + 1; cp <= in + x; end
206        ABY:  begin t <= ABX2; pc <= pc + 1; cp <= in + y; end
207        ABX2: begin t <= nx1 ? DLY : RUN; pc <= pc + 1; cp <= cp + {in, 8'h00}; mm <= 1; we <= sta; rd <= !nx1; end
208
209        // 6T INDIRECT,X
210        NDX:  begin t <= NDX2; pc <= pc + 1; cp <= zpx; mm <= 1; end
211        NDX2: begin t <= NDX3; cp[7:0] <= cp[7:0] + 1; tm <= in; end
212        NDX3: begin t <= DLY;  cp <= {in, tm}; we <= sta; end
213
214        // 5*T INDIRECT,Y
215        NDY:  begin t <= NDY2; mm <= 1;   cp <= in; pc <= pc + 1; end
216        NDY2: begin t <= NDY3; tm <= in;  cp[7:0] <= cp[7:0] + 1; end
217        NDY3: begin t <= nx2 ? DLY : RUN; cp <= {in, 8'h00} + ndy; we <= sta; rd <= !nx2; end
218
219        // 2**T BRANCH
220        REL:  begin if (brc[op[7:6]] == op[5]) t <= REL2; else begin t <= MAIN; pc <= pc + 1; end end
221        REL2: begin pc <= pcn; t <= (pc[15:8] != pcn[15:8]) ? REL3 : MAIN; end
222        REL3: begin t  <= MAIN; end
223
224        // 1T Задержка для ZPX, ZPY, ABX, ABY
225        DLY:  begin rd <= !sta; t <= RUN; end
226
227        // Выполнение инструкции
228        // ---------------------------------------------------------------------
229
230        RUN: begin
231
232            t  <= MAIN;
233            mm <= 0;
234
235            // Immediate: добавляется +1 к PC
236            casex (op) 8'b1xx_000_x0, 8'bxxx_010_x1: pc <= pc + 1; endcase
237
238            casex (op)
239            // Флаговые CLC, SEC, CLI, SEI, CLD, SED, CLV
240            8'b00x1_1000: p[CF] <= op[5];
241            8'b01x1_1000: p[IF] <= op[5];
242            8'b11x1_1000: p[DF] <= op[5];
243            8'b1011_1000: p[VF] <= 1'b0;
244            // TXS, TSX
245            8'b1001_1010: begin s <= x; end
246            8'b1011_1010: begin x <= s; {p[NF], p[ZF]} <= {s[7], s == 8'h00}; end
247            // Запись АЛУ в X
248            8'b101x_x110, // LDX ADDR
249            8'b1010_x010, // LDX IMM; TAX
250            8'b1100_1010, // DEX
251            8'b1110_1000: // INX
252                begin x <= alur; p <= aluf; end
253            // Запись АЛУ в Y
254            8'b101x_x100, // LDY ADDR
255            8'b1010_x000, // LDY IMM; TAY
256            8'b1x00_1000: // DEY, INY
257                begin y <= alur; p <= aluf; end
258            // Запись только флагов
259            8'b11x0_0000, // CPX, CPY IMM
260            8'b0010_x100, // BIT
261            8'b11x0_x100: // CPX, CPY ADDR
262                begin p <= aluf; end
263            // Запись в память
264            8'b11xx_x110, // INC, DEC
265            8'b0xxx_x110: // ASL, LSR, ROL, ROR
266                case (m)
267                0: begin m <= 1; t <= RUN; mm <= 1; we <= 1; out <= alur; p <= aluf; end
268                1: begin m <= 2; t <= RUN; end
269                endcase
270            // PHP, PHA
271            8'b0x00_1000:
272                case (m) 0: begin m <= 1; t <= RUN; mm <= 1; we <= 1; end endcase
273            // PLA, PLP
274            8'b0x10_1000:
275                case (m)
276                0: begin
277
278                    m <= 1;
279                    t <= RUN;
280                    if (op[6]) begin a <= in; {p[NF], p[ZF]} <= {in[7], in == 8'h00}; end
281                    else       begin p <= in; end
282
283                end
284                1: begin m <= 2; t <= RUN; end
285                endcase
286            // STA
287            8'b100x_xx01:
288                begin /* Иначе выполнится запись в A,P */ end
289            // Запись в [A,P]
290            8'bxxxx_xx01, // АЛУ
291            8'b0xx0_1010, // ASL, ROR, LSR, ROL
292            8'b1001_1000, // TYA
293            8'b1000_1010: // TXA
294                begin a <= (alu == CMP) ? a : alur; p <= aluf; end
295            endcase
296
297        end
298
299        // ---------------------------------------------------------------------
300
301        // 5T JMP (IND)
302        IND: case (m)
303        0: begin m  <= 1; cp[ 7:0] <= in; pc <= pc + 1; end
304        1: begin m  <= 2; cp[15:8] <= in; mm <= 1; end
305        2: begin m  <= 3; pc[ 7:0] <= in; cp <= cp + 1; end
306        3: begin mm <= 0; pc[15:8] <= in; t <= MAIN; end
307        endcase
308
309        // 6T JUMP SUB
310        JSR: case (m)
311        0: begin m <= 1; pc  <= pc + 1;   tm <= in; end
312        1: begin m <= 2; out <= pc[15:8]; tm <= pc[7:0]; cp <= s; s <= sm; pc <= {in, tm}; we <= 1; mm <= 1; end
313        2: begin m <= 3; out <= tm;       we <= 1;       cp <= s; s <= sm; end
314        3: begin m <= 4; mm <= 0; end
315        4: begin t <= MAIN; end
316        endcase
317
318        // 7T BREAK
319        BRK: case (m)
320        0: begin m <= 1; we <= 1; cp <= s; s <= sm; out <= pc2[15:8]; mm <= 1; end
321        1: begin m <= 2; we <= 1; cp <= s; s <= sm; out <= pc2[ 7:0]; end
322        2: begin m <= 3; we <= 1; cp <= s; s <= sm; out <= p | 8'h10; p <= p | 8'h14; end
323        3: begin m <= 4; cp    <= {12'hFFF, 1'b1, vec, 1'b0}; end
324        4: begin m <= 5; cp[0] <= 1'b1; pc[7:0] <= in; end
325        5: begin t <= MAIN; pc[15:8] <= in; mm <= 0; end
326        endcase
327
328        // 6T RETURN SUB
329        RTS: case (m)
330        0: begin m <= 1; mm <= 1;  cp <= sp; s <= sp; end
331        1: begin m <= 2; pc <= in; cp <= sp; s <= sp; end
332        2: begin m <= 3; pc <= pc + {in, 8'h01}; end
333        3: begin m <= 4; mm <= 0; end
334        4: begin t <= MAIN; end
335        endcase
336
337        // 6T RETURN BRK
338        RTI: case (m)
339        0: begin m <= 1; cp <= sp; s <= sp; mm <= 1; end
340        1: begin m <= 2; cp <= sp; s <= sp; p  <= in; end
341        2: begin m <= 3; cp <= sp; s <= sp; pc <= in; end
342        3: begin m <= 4; mm <= 0; pc[15:8] <= in; end
343        4: begin t <= MAIN; end
344        endcase
345
346    endcase
347
348end
349
350// -----------------------------------------------------------------------------
351// АЛУ
352// -----------------------------------------------------------------------------
353
354// Выбор операндов
355wire [7:0] op1 = dst == DST_A ? a  : (dst == DST_X ? x : (dst == DST_Y ? y : in));
356wire [7:0] op2 = src == SRC_D ? in : (src == SRC_A ? a : (src == SRC_X ? x : y));
357
358// Расчет результата
359wire [8:0] alur =
360    alu == ORA ? op1 | op2 :
361    alu == AND ? op1 & op2 :
362    alu == EOR ? op1 ^ op2 :
363    alu == ADC ? op1 + op2 + p[CF] :
364    alu == STA ? op1 :
365    alu == LDA ? op2 :
366    alu == BIT ? op1 & op2 :
367    alu == CMP ? op1 - op2 :
368    alu == SBC ? op1 - op2 - !p[CF] :
369    alu == INC ? op1 + 1 :
370    alu == DEC ? op1 - 1 :
371    alu == ASL ? {op1[6:0], 1'b0} :
372    alu == ROL ? {op1[6:0], p[CF]} :
373    alu == LSR ? {1'b0,  op1[7:1]} :
374    alu == ROR ? {p[CF], op1[7:1]} :
375        8'hFF;
376
377// Новые флаги после вычисления АЛУ
378wire zf = alur[7:0] == 8'h00;
379wire nf = alur[7];
380wire cf = alur[8];
381wire va = (op1[7] == op2[7]) & (op1[7] ^ alur[7]);
382wire vs = (op1[7] != op2[7]) & (op1[7] ^ alur[7]);
383
384wire [7:0] aluf =
385    alu == ADC ? {nf, va,   p[5:2], zf,  cf} :
386    alu == SBC ? {nf, vs,   p[5:2], zf, !cf} :
387    alu == CMP ? {nf,       p[6:2], zf, !cf} :
388    alu == BIT ? {op2[7:6], p[5:2], zf, p[CF]} :
389    alu == ASL ||
390    alu == ROL ? {nf,       p[6:2], zf, op1[7]} :
391    alu == LSR ||
392    alu == ROR ? {nf,       p[6:2], zf, op1[0]} :
393                 {nf,       p[6:2], zf, p[CF]};
394
395endmodule