The new forums will be named Coin Return (based on the most recent vote)! You can check on the status and timeline of the transition to the new forums here.
The Guiding Principles and New Rules document is now in effect.

Making a computer for a friend. (I'm making one, not building one)

Note to mods: I talk about 6502 emulation, but my implementation is incompatible with any 6502 computer or game system currently in existence.

Hey guys, wanted to show off a little project I'm working on.

I wanted to make a 6502 computer for a friend of mine for his birthday. I started with a Kim-1 kit which, sadly failed. After putting the whole thing together, all it did was light it's power light and nothing happened. I tried to troubleshoot it the best I could, only to find that I soldered two caps in the wrong place and bent a few IC pins. Well, that was a lesson learned.

Discouraged, I put the project away.

About three weeks ago I was cleaning my apartment and found my Arduino 2560 Mega. I decided as opposed to making a 6502 computer from ICs, I would make a software version of the same thing using my Arduino. It came out awesome!

5WXrql.jpg

I designed it to be a kind of "Super Kim". The system emulates a 6502 with 512k of ram. the bottom 32k ($8000-$FFFF) is banked in depending on what is written to $FB ($00 to $0F) if you write a zero in $FB it will mirror the top half of memory ($0000-$7FFF) and I call this "32k mode".

When you plug in a USB, it becomes a com port on a computer and you can use this as a terminal running @ 9600-8-N-1.

The emulation code I used was found here however, I did a rewrite the memory portion (The original only could use 1024 bytes of memory), added registers for my LCD display and keypad, and implemented the BRK and RESET interrupt. (The original code does not have interrupts implemented)

With everything now implemented how I want, I'm implementing a cute mini-assembler, something like the system monitor from the Apple II. This is so you can write short little programs using the keypad. The keypad matrix looks like this.
{'1','2','3','U'},
{'4','5','6','V'},
{'7','8','9','W'},
{'+','0','-','X'},
{'A','B','C','Y'},
{'D','E','F','Z'} 

The letters down the right-hand side (U-Z) are just placeholders. I can make them whatever I want. I'll probably borrow from the Kim-1 keypad (GO, ST, RS, AD, DA, PC, and an extra one left over).

Programs are loaded via the USB Serial port. You load programs by uploading them over the com port using Xmodem. You save you programs by doing the reverse. (I originally planned to have it load actual punch cards, but couldn't find/make a cheap punch card peripheral)

I'm going be mounting this into a black plastic case with fake wood grain detail to complete the 1977 look of it.

Sadly, I'm having problems with my Monitor because my code-fu kind of weak. If anyone wants to help, I'll post my emulation core and Monitor code. It's all GPL anyway. I think I botched something up when I implemented my 65C02 opcodes and the monitor I'm using is being ported to ca65 from TASS (Which doesn't run on x64 machines)



Posts

  • ecco the dolphinecco the dolphin Registered User regular
    Interesting!

    What's going wrong with your monitor?

    I'd be interested in having a look - I have a few AVRs lying around with a dev kit or two that I could probably adapt to whatever the Arduino does. Not sure how many k of flash I'd need though - I tend to use quite small ones.

    Penny Arcade Developers at PADev.net.
  • halkunhalkun Registered User regular
    edited December 2012
    I've goy a thread going on over at 6502.org
    It's been an interesting adventure. The monitor, it turns out is fine. I'm porting that from TASS, a DOS assembler that doesn't run in x64 systems to ca65. That part was easy. My implementation of CMOS opcodes is a little more difficult. Turns out everyone wants to make NES emulators, which don't have decimal mode, and none of the 65C02 stuff, so most of the open source emulation cores are missing those. I found a cool NMOS core in Java, and ported that to C++, but I completely broke the flags and comparator. I also had to use my weak code-fu to implement the CMOS portion of it. The code and what I've done is posted in the link above.

    The system I'm using is an Auduino Mega 2560, that has 256k of flash-rom with a 512k RAM add-on board.

    halkun on
  • ecco the dolphinecco the dolphin Registered User regular
    edited December 2012
    Hmmmmmmm

    Interesting!

    Haha, the standard massive switch/case statement for an instruction decoder which grew from a much smaller one. Classic.

    Since you do use all your memory, it looks like I won't be able to put it into one of my dev kits, since I don't think I have one which has that much RAM.

    What I did do was remove the Arduino specific calls, and have it compiling under MSVC2010, and I got this warning:
    case 0x0c:                            // TSB ABS
    		value = memReadByte( popWord() );  //Setp 1: BIT oprand (Z flag only)
    		if( value & regA ) regP &= 0xfd; else regP |= 0x02;
    		currP = regP;					 // Step 2: PHP
    		currA = regA;					 // Step 3: PHA		
    		regA |= zp;						 // Step 4: ORA operand (Z flag only)  <<<<<< *** This line is the warning ***
    		if( regA ) regP &= 0xfd; else regP |= 0x02;
    		memStoreByte( zp, regA );		//Step 6. STA operand
    		regA = currA;					//Step 7. PLA
    		regP = currP;					//step 8. PLP
    		break;
    
    1>halkun_65c02\joshxcore.cpp(571): warning C4700: uninitialized local variable 'zp' used
    

    What is zp meant to be initialised to? It definitely looks like zp should be initialised to something sensible.

    I looked at the opcodes inside the rom, and 0x0c does appear, but I'm not entirely sure if it's an instruction opcode or data byte.

    Also, ov is currently unused in execute().

    And what is keypad_read() meant to return if key == NO_KEY?


    Edit: It looks like you implemented something from here?

    In which case, you're meant to be using the operand? So maybe:
    regA |= value;
    ...
    memStoreByte( value, regA );
    

    ?

    Not entirely familiar with the 6502, so take what I say with a grain of salt.

    ecco the dolphin on
    Penny Arcade Developers at PADev.net.
  • ecco the dolphinecco the dolphin Registered User regular
    edited December 2012
    Oh hey

    Slowly refactoring the code to more PC friendly (but not necessarily more Arduino friendly)

    Found this:
    // stackPush() - Push byte to stack
    void stackPush(int value ) {
    	byte save_bank=current_bank;
    	banksel(0);
    	if( regSP >= 0 ) {
    		regSP--;
    		memory[(regSP&0xff)+0x100] = value & 0xff;
    	}
    	banksel(save_bank);
    }
    

    I think it's slightly backwards. The code decrements the stack pointer, and *then* writes the value to stack memory.

    From what I can see, the instruction set should write the value to stack memory, and *then* decrements the stack pointer.

    Luckily, the pop equivalent also has this backwards as well (i.e. it reads the value from stack memory, and *then* increments the stack pointer), as opposed to doing it the other way around.

    I think the off by one value might end up messing up apps that use TSX/TXS to directly access the stack.

    Edit:
    Also just found this
    // popWord() - Pops a word using popByte() twice
    int popWord() {
    	return popByte() + (popByte() << 8);
    }
    

    This is a bit arcane, so bear with me, but there is no guarantee in C/C++ (or the variant that the Arduino is using) of which popByte() gets called first. The technical term is called a sequence point, and the '+' that's inbetween the two popByte() calls is not a sequence point.

    In other words, the compiler is free to call/evaluate (popByte() << 8) first, and *then* call/evaluate popByte(), and sum the results.

    It is also free to call the popByte() by itself first, and then go (popByte() << 8).

    This has the effect of accidentally making the 6502 big endian if the compiler chooses the first option, or little endian (the intended, I think) if the compiler chooses option two.

    A solution is to explicitly call it out:
    // popWord() - Pops a word using popByte() twice
    int popWord() {
    	int result = popByte();
    	result += (popByte() << 8);
    	return result;
    }
    

    ecco the dolphin on
    Penny Arcade Developers at PADev.net.
  • halkunhalkun Registered User regular
    Hey cool. That's for looking into that. I'm actually stuck on another bit. I have learn more about bitwise operations in C++ and 6502 code in a major way. I'm going to implment what you fond and see if it fixes any issues I'm having. If not, I'll post the relevant code here too.

  • halkunhalkun Registered User regular
    Here's a post I put on 6502.org I'll pop it over here too to see if any other eyes can look at this. I'm stuck again. I'll pull some arduino guys too on another forum. I didn't realize the my C++ is different than your typical PC stuff. I hope I'm not learning any bad habits from it.
    I'm trying to fix the display error above with the help command.
    I've obviously botched up the flags. The reason why I think that's the case is because it was a cute "addition" I added to the code without making sure the other code worked first.

    I also don't have a backup if the code that "worked" before I made the changes... :(

    The good news is what's broken is a small bit of code...
    Help_Cmd:       lda   #<HelpTxt         ;  lower byte - Menu of Commands
                   sta   AddrPtr           ;
                   lda   #>HelpTxt         ;  upper byte
                   sta   AddrPtr+1         ;
                   bra   Help_Cmd3         ;
    Help_Cmd4:      cmp   #$7e              ;  "~"
                   beq   Help_Cmd1         ;
                   jsr   Output            ;
                   bra   Help_Cmd2         ;
    Help_Cmd1:      jsr   Print_CR          ;     
    Help_Cmd2:      jsr   Inc_AddrPtr       ;    <--- This is where it's breaking
    Help_Cmd3:      lda   (AddrPtr)         ;
                   bne   Help_Cmd4         ;
                   rts                     ;
    
    Inc_AddrPtr:    INC   AddrPtr           ;  increments AddrPtr
                   BNE   Inc_addr1         ;
                   INC   AddrPtr+1         ;
    Inc_addr1:      RTS  
    

    Cool, just two opcodes to tear apart. INC and BNE

    The data it's using is here...
                   .byte "JMP"             ;1C
                   .byte "JSR"             ;1D
                   .byte "LDA"             ;1E
                   .byte "LDX"             ;1F
                   .byte "LDY"             ;20
                   .byte "LSR"             ;21
                   .byte "NOP"             ;22
                   .byte "ORA"             ;23
                   .byte "PHA"             ;24
                   .byte "PHP"             ;25
                   .byte "PHX"             ;26
                   .byte "PHY"             ;27
                   .byte "PLA"             ;28
                   .byte "PLP"             ;29
                   .byte "PLX"             ;2A
                   .byte "PLY"             ;2B
                   .byte "ROL"             ;2C
                   .byte "ROR"             ;2D
                   .byte "RTI"             ;2E
                   .byte "RTS"             ;2F
                   .byte "SBC"             ;30
                   .byte "SEC"             ;31
                   .byte "SED"             ;32
                   .byte "SEI"             ;33
                   .byte "STA"             ;34
                   .byte "STX"             ;35
                   .byte "STY"             ;36
                   .byte "STZ"             ;37
                   .byte "TAX"             ;38
                   .byte "TAY"             ;39
                   .byte "TRB"             ;3A
                   .byte "TSB"             ;3B
                   .byte "TSX"             ;3C
                   .byte "TXA"             ;3D
                   .byte "TXS"             ;3E
                   .byte "TYA"             ;3F
                   .byte "WAI"             ;40
                   .byte "STP"             ;41
                   .byte "BBR"             ;42 4Byte Opcodes
                   .byte "BBS"             ;43
                   .byte "RMB"             ;44
                   .byte "SMB"             ;45
                   .byte ".DB"             ;46 define 1 byte for assembler
                   .byte ".DW"             ;47 define 1 word for assembler
                   .byte ".DS"             ;48 define a string block for assembler
                   .byte "???"             ;49 for invalid opcode
    ;
    ;
    HelpTxt:        .byte "~Current commands are :~"
                   .byte "Syntax = {} required, [] optional, HHHH hex address, DD hex data~"
                   .byte "~"
                   .byte "[HHHH][ HHHH]{Return} - Hex dump address(s)(up to 16 if no address entered)~"
                   .byte "[HHHH]{.HHHH}{Return} - Hex dump range of addresses (16 per line)~"
                   .byte "[HHHH]{:DD}[ DD]{Return} - Change data bytes~"
    


    What is happening in the code is that when AddPtr is wrapping around, and as opposed to incrementing to the next line, it's pointing WAY up above in another text section. It then reads that all the way down and loops again.

    Here's my relevant code for INC ZP
    
    #define UMASK  0xFF
    #define C_FLAG	0b00000001
    #define Z_FLAG	0b00000010
    #define I_FLAG	0b00000100
    #define D_FLAG	0b00001000
    #define B_FLAG 	0b00010000
    #define X_FLAG	0b00100000
    #define V_FLAG	0b01000000
    #define N_FLAG	0b10000000
    
    void INC(int addr)
    {
    	int value;
    	value = memReadByte( addr );
    	++value;
    	memStoreByte( addr, value&UMASK ); //mask off the lower byte
    	setNVflags(value);
    }
    
    //set zero and negative processor flags based on result
    void setNVflags(int val)
    {
    	if( val ) 
    	{regP &= ~Z_FLAG;}
    	else 
    	{regP |= Z_FLAG;}
    	
    	if( val & N_FLAG )
    	{regP |= N_FLAG;}
    	else 
    	{regP &= ~N_FLAG;}
    
    }
    

    and to be complete, here's BNE
    	case 0xd0:                          // BNE
    		offset = popByte();
    		if (!zeroSet()) {jumpBranch(offset); }
    		break;
    

    which uses this call...
    int zeroSet() { return regP & Z_FLAG;}
    

    now, here's where it gets strange... These all should work, but I can't see what it's doing wrong :(
    Thinking it's the flag tester I replaced it with the old code...
    void setNVflags(int value)
    {
    		if( value ) regP &= 0xfd; else regP |= 0x02;
    		if( value & 0x80 ) regP |= 0x80; else regP &= 0x7f;
    }
    

    and it does the same thing.
    I'm stuck...

  • ecco the dolphinecco the dolphin Registered User regular
    Hmmmmmmmm

    At first blush, you're right - it does look like it should work.

    I wonder if there's something deeper going on?

    I'm just tidying up the code and giving it a more thorough look as I tidy it up - will hopefully have it set up soon so that I can step through it using the Visual Studio debugger, and then it should be pretty obvious what's going on.
    halkun wrote: »
    Here's a post I put on 6502.org I'll pop it over here too to see if any other eyes can look at this. I'm stuck again. I'll pull some arduino guys too on another forum. I didn't realize the my C++ is different than your typical PC stuff. I hope I'm not learning any bad habits from it.

    It's very very similar - similar enough that I can just do a straight compile on the PC for 99% of it.

    Penny Arcade Developers at PADev.net.
  • ecco the dolphinecco the dolphin Registered User regular
    edited December 2012
    Oh hey - just realised something - try this on for size:
    void setNVflags(int val)
    {
    	val &= UMASK; // You might be passing in 0x100, which should technically be treated as a 0
    
    	if( val ) 
    	{regP &= ~Z_FLAG;}
    	else 
    	{regP |= Z_FLAG;}
    	
    	if( val & N_FLAG )
    	{regP |= N_FLAG;}
    	else 
    	{regP &= ~N_FLAG;}
    }
    

    Alternatively, may I introduce you to uint8_t and uint16_t for 8-bit and 16-bit variables, respectively?

    Instead of using int (which I believe is 16-bit on Arduino platforms), and then masking, could I recommend that you use uint8_t (or Arduino's "byte" data type) for variables that should only ever be 8 bits?

    For example:
    #include <cstdint>
    
    // val will now be treated as an 8-bit value.
    void setNVflags(uint8_t val)
    {
    	if( val ) 
    	{regP &= ~Z_FLAG;}
    	else 
    	{regP |= Z_FLAG;}
    	
    	if( val & N_FLAG )
    	{regP |= N_FLAG;}
    	else 
    	{regP &= ~N_FLAG;}
    }
    

    Edit2: I see someone on the 6502 forums has beaten me quite handily to the punch!

    ecco the dolphin on
    Penny Arcade Developers at PADev.net.
  • halkunhalkun Registered User regular
    As I posted there, I though chars cast to ints when you do bitwise operations on them....

  • ecco the dolphinecco the dolphin Registered User regular
    halkun wrote: »
    As I posted there, I though chars cast to ints when you do bitwise operations on them....

    Yup, you're sort of right (the best kind of right) =P

    It's more accurate to say that the result of a bitwise operation on a char can be treated as if the char had been cast to an int before the bitwise operation.

    There's a key and subtle difference there!

    If it turns out that doing the bitwise operation directly as a char (without the cast to int) is guaranteed to give the exact same result as if it were cast to int, then the optimiser inside the compiler can choose to just operate on that char, and ignore the cast to int.

    This is less important on modern 32/64-bit PCs, but becomes useful on the AVR platform because its native data size is 8-bits.

    Penny Arcade Developers at PADev.net.
  • mightyjongyomightyjongyo Sour Crrm East Bay, CaliforniaRegistered User regular
    I would still use something like uint8_t anyways though - even if they evaluate the same, there's less ambiguity in terms of intent. Also, it may save a (teensy) bit of space because it would optimize to use less instructions.

  • ecco the dolphinecco the dolphin Registered User regular
    edited December 2012
    Yoooo

    Doing more testing:
    case 0x04:                            // TSB ZP
    		zp = popByte();					  // Step 1: BIT operand (Z flag only)
    		value = memReadByte( zp );
    		if( value & regA ) regP &= 0xfd; else regP |= 0x02;
    		currP = regP;					 // Step 2: PHP
    		currA = regA;					 // Step 3: PHA		
    		regA |= value;						 // Step 4: ORA operand (Z flag only)
    //		if( regA ) regP &= 0xfd; else regP |= 0x02; // <---- This line is redundant, and can be removed, because you're overwriting the value of regP 3 lines down
    		memStoreByte( zp, regA );		//Step 6. STA operand
    		regA = currA;					//Step 7. PLA
    		regP = currP;					//step 8. PLP
    		break;
    
    	case 0x06:                            // ASL ZP
    		zp = popByte();
    		value = memReadByte( zp );
    		setCarryFlagFromBit7(value);
    		value = value << 1;
    		memStoreByte( zp, value );
    		ASL( value ); // <--- Changed from ORA()
    		break;
    

    ^^ The above fix will also require the mask for setNVFlags()

    Edit:
    case 0x08:                            // PHP
    		stackPush( regP | 0x30);
    		break;
    

    This sets both the break and X flag - now, as I understand it, X is meant to be set, but it looks like you've implemented the break flag/instruction.

    Perhaps stackPush( regP | X_FLAG ); is closer to what you want now?

    Edit2:
    case 0x0a:                            // ASL IMPL
    		setCarryFlagFromBit7(regA);
    		regA = (regA<<1) & UMASK; // <<<< Add in this mask, otherwise on the edge case where regA == 0x80, you end up with regA == 0x100
    		ASL(regA);
    
    case 0x0c:                            // TSB ABS
            addr = popWord();
    		value = memReadByte( addr );  //Setp 1: BIT oprand (Z flag only)
    		if( value & regA ) regP &= 0xfd; else regP |= 0x02;
    		currP = regP;					 // Step 2: PHP
    		currA = regA;					 // Step 3: PHA		
            regA |= value;
    		memStoreByte( addr, regA );		//Step 6. STA operand
    		regA = currA;					//Step 7. PLA
    		regP = currP;					//step 8. PLP
    		break;
    

    ^^ Now that I've spent more time with the 6502, I think this is what you originally meant for TSB
    case 0x14:                            // TRB ZP
    		zp = popByte();					  // Step 1: BIT operand (only Z flag)
    		value = memReadByte( zp );
    		if( value & regA ) regP &= 0xfd; else regP |= 0x02;
    		currP = regP;					 // Step 2: PHP
    		currA = regA;					 // Step 3: PHA		
    		regA ^= 0xFF;					 // Step 4. EOR #$FF
    		regA &= value;				    //Step 5. AND operand <<<<<< you were and'ing against zp, which is the address of the data, not the actual value of the data
    		memStoreByte( zp, regA );		//Step 6. STA operand
    		regA = currA;					//Step 7. PLA
    		regP = currP;					//step 8. PLP
    		break;
    

    ^^ Also removed strange and exciting checks for setting the zero flags... because regP = currP resets the zero flag state immediately afterwards!

    Edit
    case 0x28:                            // PLP
    		regP = stackPop() | X_FLAG;		// There is no B bit! <<< You have now added a B bit
    		break;
    
    case 0x2a:                            // ROL A
    		sf = carrySet();
    		setCarryFlagFromBit7(regA);
            regA = (regA << 1) & UMASK; // Added this mask in, for the case where A = 0x80, and you end up with 0x100 in A
    		regA += sf;
    		ROL(regA);
    		break;
    
    case 0x40:                            // RTI
    		regP = stackPop() | X_FLAG;		// There is no B bit! <<<< Again, you have added the B bit
    		regPC = (stackPop() | (stackPop() << 8));
    		break;
    
    case 0x7e:                           // ROR ABSX
    		sf = carrySet();
    		addr = popWord() + regX;
    		value = memReadByte( addr );
    		setCarryFlagFromBit0(value);
    		value = value >> 1;
    		if( sf ) value |= N_FLAG; // <<< You want to set the top bit if *carry* is set, not if value is non-zero
    		memStoreByte( addr, value );
    		ROR(value);
    		break;
    
    case 0xae:                          // LDX ABS
    		regX = memReadByte( popWord() );
    		LDX();
            break; /// <<< This line was missing, so every time an LDX ABS was executed, it would fall through and execute a BCS as well!
    

    ecco the dolphin on
    Penny Arcade Developers at PADev.net.
  • ecco the dolphinecco the dolphin Registered User regular
    Right.

    So, I think I ran simple tests over all 65c02 instructions except BRK, ADC, or SBC.

    BRK was skipped because I haven't gotten around to implementing interrupts (yet?).

    ADC/SBC because decimal mode requires a bit more time to fully implement.

    Penny Arcade Developers at PADev.net.
  • ecco the dolphinecco the dolphin Registered User regular
    Just realised - RTS and RTI both have the same problem as popWord().

    They should probably be something like:
    case 0x60:                           // RTS
            value = stackPop();
            value |= (stackPop() << 8);
    		regPC = value + 1;
    		break;
    

    Similarly for RTI:
    case 0x40:                            // RTI
    		regP = stackPop() | X_FLAG;		// There is no B bit!
            value = stackPop();
            value |= stackPop() << 8;
    		regPC = value;
    		break;
    

    Which led me to have a quick look at BRK:
    case 0x00:                            // BRK implied
    		if (regP & 0x04) {break;}
    		else
    		{
    			regP |= B_FLAG;  //set break bit
    			// stackPush( regPC ); // <-- This is only saving the lower byte of PC!
    			stackPush( (regPC >> 8) & UMASK) ); // <-- This is probably more what you want
    			stackPush( (regPC & UMASK) );
    			stackPush( regP );
    			regP &= ~B_FLAG; //set brk back
    			regPC = memReadByte( 0xFFFE ) + (memReadByte( 0xFFFF) << 8);
    		}	
    		break;
    

    Penny Arcade Developers at PADev.net.
  • halkunhalkun Registered User regular
    Holy crap dude! I didn't think you would go that deep into fixing my screw-ups. Sorry I didn't reply earlier, I don't have much time to work on this during the week.

    That being said. I've always noticed that people are willing to bend over backwards to help you if you show effort that you are trying yourself. What you did makes me feel guilty for not following up...

    Also, the X flag is just a place holder, in the 6502, the flag doesn't exist and I just put it there as a place holder to complete the bit masks.

  • ecco the dolphinecco the dolphin Registered User regular
    edited December 2012
    halkun wrote: »
    Holy crap dude! I didn't think you would go that deep into fixing my screw-ups. Sorry I didn't reply earlier, I don't have much time to work on this during the week.

    That being said. I've always noticed that people are willing to bend over backwards to help you if you show effort that you are trying yourself. What you did makes me feel guilty for not following up...

    Also, the X flag is just a place holder, in the 6502, the flag doesn't exist and I just put it there as a place holder to complete the bit masks.

    It's cool. =)

    I ended up whipping up a 6502 core of my own, and did some tests between your opcodes and mine to find those. I only verified register values and the contents of the last byte written to memory during my tests, and didn't do order of memory access.

    Probably going to recycle most of my code into one of my own projects later, actually!

    Edit: One of my mates has asked me to put it on to github, so I'll put it there tonight after work so you can have a looksee through it, if you're interested.

    Edit2:

    Here we go:

    https://github.com/eccoecco/65c02/tree/master/Halkun_65C02_Refactor

    ecco the dolphin on
    Penny Arcade Developers at PADev.net.
  • StormwatcherStormwatcher Blegh BlughRegistered User regular
    I think this is amazingly interesting, and, as someone who has no idea what's going on, I need to ask: what is that machine you're making capable of doing? I really don't know.

    Steam: Stormwatcher | PSN: Stormwatcher33 | Switch: 5961-4777-3491
    camo_sig2.png
  • halkunhalkun Registered User regular
    edited December 2012
    It's a custom-made microcomputer. When you plug it into USB and connect to it with a terminal application (Like Hyperterm or Putty), you will be able to use it like an old Apple II computer. My system isn't Apple II compatible. It's my own computer and my own spec. The CPU is, however the same kind found in an Apple II, so the machine language is the same. It's also the same kind of CPU found in an Atari 2600, C64, and NES. Like I said, however, it's not program-compatible with those machines though.

    The CPU is being emulated on a programmable microcontroller called an AVR. I'm making the AVR behave like the 6502 CPU so I can run 6502 machine code on it.

    Right now my CPU has bugs in it because I'm a terrible programmer :) I'm writing/modifying a program called a "Monitor" to run on my computer so I can shake the bugs out. After that, I'll be implementing a version of BASIC on it and giving it to my friend as a gift.

    halkun on
Sign In or Register to comment.