|
| 1 | +// |
| 2 | +// |
| 3 | +// File Name : MCL65.c |
| 4 | +// Used on : |
| 5 | +// Author : Ted Fried, MicroCore Labs |
| 6 | +// Creation : 3/22/2020 |
| 7 | +// Code Type : C code |
| 8 | +// |
| 9 | +// Description: |
| 10 | +// ============ |
| 11 | +// |
| 12 | +// C Code emulating the MCL65 Microsequencer of the MOS 6502 microprocessor |
| 13 | +// This is a C version of the MCL65 which is written in Verilog. It runs the |
| 14 | +// exact same microcode. NMI and Interrupts are suppored. |
| 15 | +// Each loop of the code below is equivalent to one microsequencer clock |
| 16 | +// and will execute a new micro-instruction each time. |
| 17 | +// The user code and data RAM are held in a 64KB array. |
| 18 | +// |
| 19 | +//------------------------------------------------------------------------ |
| 20 | +// |
| 21 | +// Modification History: |
| 22 | +// ===================== |
| 23 | +// |
| 24 | +// Revision 1 3/22/2020 |
| 25 | +// Initial revision |
| 26 | +// |
| 27 | +// |
| 28 | +//------------------------------------------------------------------------ |
| 29 | +// |
| 30 | +// Copyright (c) 2020 Ted Fried |
| 31 | +// |
| 32 | +// Permission is hereby granted, free of charge, to any person obtaining a copy |
| 33 | +// of this software and associated documentation files (the "Software"), to deal |
| 34 | +// in the Software without restriction, including without limitation the rights |
| 35 | +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 36 | +// copies of the Software, and to permit persons to whom the Software is |
| 37 | +// furnished to do so, subject to the following conditions: |
| 38 | +// |
| 39 | +// The above copyright notice and this permission notice shall be included in all |
| 40 | +// copies or substantial portions of the Software. |
| 41 | +// |
| 42 | +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 43 | +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 44 | +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 45 | +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 46 | +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 47 | +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 48 | +// SOFTWARE. |
| 49 | +// |
| 50 | +//------------------------------------------------------------------------ |
| 51 | + |
| 52 | +#include <stdio.h> |
| 53 | +#include "mcl65.h" |
| 54 | + |
| 55 | +#define rdwr_n ( system_output & 0x1 ) >> 0 // system_output[0] |
| 56 | +#define flag_i ( register_flags & 0x04 ) >> 2 // register_flags[2] |
| 57 | +#define opcode_type ( rom_data & 0xF0000000 ) >> 28 // rom_data[30:28]; |
| 58 | +#define opcode_dst_sel ( rom_data & 0x0F000000 ) >> 24 // rom_data[27:24]; |
| 59 | +#define opcode_op0_sel ( rom_data & 0x00F00000 ) >> 20 // rom_data[23:20]; |
| 60 | +#define opcode_op1_sel ( rom_data & 0x000F0000 ) >> 16 // rom_data[19:16]; |
| 61 | +#define opcode_immediate ( rom_data & 0x0000FFFF ) >> 00 // rom_data[15:0]; |
| 62 | +#define opcode_jump_call ( rom_data & 0x01000000 ) >> 24 // rom_data[24]; |
| 63 | +#define opcode_jump_src ( rom_data & 0x00700000 ) >> 20 // rom_data[22:20]; |
| 64 | +#define opcode_jump_cond ( rom_data & 0x000F0000 ) >> 16 // rom_data[16:19]; |
| 65 | + |
| 66 | + |
| 67 | +unsigned char attached_memory[65536]={ }; |
| 68 | +unsigned char register_flags=0; |
| 69 | +unsigned char add_carry7=0; |
| 70 | +unsigned char add_carry8=0; |
| 71 | +unsigned char nmi_asserted=0; |
| 72 | +unsigned char irq_gated=0; |
| 73 | +unsigned char add_overflow8=0; |
| 74 | +unsigned char nmi_debounce=0; |
| 75 | +unsigned char system_status=0; |
| 76 | +unsigned char register_a=0; |
| 77 | +unsigned char register_x=0; |
| 78 | +unsigned char register_y=0; |
| 79 | +unsigned short int register_r0=0; |
| 80 | +unsigned short int register_r1=0; |
| 81 | +unsigned short int register_r2=0; |
| 82 | +unsigned short int register_r3=0; |
| 83 | +unsigned short int register_pc=0x400; |
| 84 | +unsigned short int register_sp=0; |
| 85 | +unsigned short int address_out=0; |
| 86 | +unsigned short int data_in=0; |
| 87 | +unsigned short int data_out=0; |
| 88 | +unsigned short int system_output=0; |
| 89 | +unsigned short int alu_last_result=0; |
| 90 | +unsigned short int alu_out=0; |
| 91 | +unsigned short int rom_address=0x07D0; // Address for Microcode Reset procedure |
| 92 | +unsigned short int operand0=0; |
| 93 | +unsigned short int operand1=0; |
| 94 | +unsigned long rom_data; |
| 95 | +unsigned long calling_address; |
| 96 | + |
| 97 | + |
| 98 | +// Main loop |
| 99 | +// |
| 100 | +int main() |
| 101 | + { |
| 102 | + while (1) |
| 103 | + { |
| 104 | + // Optional INT and NMI Signals |
| 105 | + // irq_gated = P6502_IRQ_n & ~flag_i; |
| 106 | + // if (nmi_debounce==1) nmi_asserted=0; else if (P6502_NMI_n_old==1 && P6502_NMI_n==0) nmi_asserted=1; P6502_NMI_n_old = P6502_NMI_n; |
| 107 | + |
| 108 | + data_in = attached_memory[address_out]; // Read data from the Attached Memory RAM |
| 109 | + |
| 110 | + rom_data = microcode_rom[rom_address]; // Fetch the next microcode instruction |
| 111 | + |
| 112 | + system_status = ( add_carry8 << 0 | // system_status[0] |
| 113 | + nmi_asserted << 3 | // system_status[3] |
| 114 | + irq_gated << 5 | // system_status[5] |
| 115 | + add_overflow8 << 6 ); // system_status[6] |
| 116 | + |
| 117 | + |
| 118 | + switch (opcode_op0_sel) |
| 119 | + { |
| 120 | + case 0x0: operand0 = register_r0; break; |
| 121 | + case 0x1: operand0 = register_r1; break; |
| 122 | + case 0x2: operand0 = register_r2; break; |
| 123 | + case 0x3: operand0 = register_r3; break; |
| 124 | + case 0x4: operand0 = register_a & 0x00FF; break; |
| 125 | + case 0x5: operand0 = register_x & 0x00FF; break; |
| 126 | + case 0x6: operand0 = register_y & 0x00FF; break; |
| 127 | + case 0x7: operand0 = register_pc; break; |
| 128 | + case 0x8: operand0 = ((register_sp & 0x00FF) | 0x0100); break; |
| 129 | + case 0x9: operand0 = register_flags & 0x00FF; break; |
| 130 | + case 0xA: operand0 = address_out; break; |
| 131 | + case 0xB: operand0 = ((data_in&0xFF) << 8) | (data_in&0xFF); break; |
| 132 | + case 0xC: operand0 = system_status; break; |
| 133 | + case 0xD: operand0 = system_output; break; |
| 134 | + case 0xE: operand0 = 0; break; |
| 135 | + case 0xF: operand0 = 0; break; |
| 136 | + } |
| 137 | + |
| 138 | + |
| 139 | + switch (opcode_op1_sel) |
| 140 | + { |
| 141 | + case 0x0: operand1 = register_r0; break; |
| 142 | + case 0x1: operand1 = register_r1; break; |
| 143 | + case 0x2: operand1 = register_r2; break; |
| 144 | + case 0x3: operand1 = register_r3; break; |
| 145 | + case 0x4: operand1 = register_a & 0x00FF; break; |
| 146 | + case 0x5: operand1 = register_x & 0x00FF; break; |
| 147 | + case 0x6: operand1 = register_y & 0x00FF; break; |
| 148 | + case 0x7: operand1 = ( (register_pc<<8) | (register_pc>>8) ) ; break; |
| 149 | + case 0x8: operand1 = ((register_sp & 0x00FF) | 0x0100); break; |
| 150 | + case 0x9: operand1 = register_flags & 0x00FF; break; |
| 151 | + case 0xA: operand1 = address_out; break; |
| 152 | + case 0xB: operand1 =( ( data_in << 8) | data_in); break; |
| 153 | + case 0xC: operand1 = system_status; break; |
| 154 | + case 0xD: operand1 = system_output; break; |
| 155 | + case 0xE: operand1 = 0; break; |
| 156 | + case 0xF: operand1 = opcode_immediate; break; |
| 157 | + } |
| 158 | + |
| 159 | + |
| 160 | + switch (opcode_type) |
| 161 | + { |
| 162 | + case 0x2: // ADD |
| 163 | + alu_out = operand0 + operand1; |
| 164 | + add_carry7 = (( ((operand0&0x007F) + (operand1&0x007F)) & 0x0080) >> 7); |
| 165 | + add_carry8 = (( ((operand0&0x00FF) + (operand1&0x00FF)) & 0x0100) >> 8); |
| 166 | + add_overflow8 = (add_carry7 ^ add_carry8) & 0x1; |
| 167 | + break; |
| 168 | + case 0x3: alu_out = operand0 & operand1; break; // AND |
| 169 | + case 0x4: alu_out = operand0 | operand1; break; // OR |
| 170 | + case 0x5: alu_out = operand0 ^ operand1; break; // XOR |
| 171 | + case 0x6: alu_out = operand0 >> 1; break; // SHR |
| 172 | + default: alu_out = 0xEEEE; break; |
| 173 | + } |
| 174 | + |
| 175 | + |
| 176 | + // Register write-back |
| 177 | + if (opcode_type > 1) |
| 178 | + { |
| 179 | + alu_last_result = alu_out; |
| 180 | + switch (opcode_dst_sel) |
| 181 | + { |
| 182 | + case 0x0: register_r0 = alu_out; break; |
| 183 | + case 0x1: register_r1 = alu_out; break; |
| 184 | + case 0x2: register_r2 = alu_out; break; |
| 185 | + case 0x3: register_r3 = alu_out; break; |
| 186 | + case 0x4: register_a = alu_out & 0x00FF; break; |
| 187 | + case 0x5: register_x = alu_out & 0x00FF; break; |
| 188 | + case 0x6: register_y = alu_out & 0x00FF; break; |
| 189 | + case 0x7: register_pc = alu_out; break; |
| 190 | + case 0x8: register_sp = alu_out & 0x00FF; break; |
| 191 | + case 0x9: register_flags = alu_out | 0x30; break; |
| 192 | + case 0xA: address_out = alu_out; break; |
| 193 | + case 0xB: data_out = alu_out & 0x00FF; break; |
| 194 | + case 0xD: system_output = alu_out & 0x001F; break; |
| 195 | + default: break; |
| 196 | + } |
| 197 | + } |
| 198 | + |
| 199 | + |
| 200 | + // JUMP Opcode |
| 201 | + if ( ( opcode_type==0x1 && opcode_jump_cond==0x0 ) || // Unconditional jump |
| 202 | + ( opcode_type==0x1 && opcode_jump_cond==0x1 && alu_last_result!=0 ) || // Jump Not Zero |
| 203 | + ( opcode_type==0x1 && opcode_jump_cond==0x2 && alu_last_result==0 ) ) // Jump Zero |
| 204 | + { |
| 205 | + // For subroutine CALLs, store next opcode address |
| 206 | + if (opcode_jump_call==1) |
| 207 | + calling_address = (calling_address<<11) | (rom_address&0x07FF) ; |
| 208 | + |
| 209 | + switch (opcode_jump_src) |
| 210 | + { |
| 211 | + case 0x0: rom_address = opcode_immediate & 0x07FF; break; |
| 212 | + case 0x1: rom_address = data_in & 0x00FF; break; // Opcode Jump Table |
| 213 | + case 0x2: rom_address = (calling_address & 0x07FF) + 1; |
| 214 | + calling_address = calling_address>>11; |
| 215 | + break; |
| 216 | + } |
| 217 | + } |
| 218 | + else |
| 219 | + rom_address = rom_address + 1; |
| 220 | + |
| 221 | + |
| 222 | + // Writes to the 6502 Data RAM occur on the simulated rising edge of the clock |
| 223 | + if (opcode_type==0x1 && opcode_jump_cond==0x3 && rdwr_n==0) attached_memory[address_out] = data_out; |
| 224 | + } |
| 225 | + } |
| 226 | + |
0 commit comments