Thread: MOS 6502 - More Advice on my implementation

  1. #1
    C lover
    Join Date
    Oct 2007
    Location
    Virginia
    Posts
    266

    MOS 6502 - More Advice on my implementation

    I had posted a thread about a week ago regarding tips and whatnot for creating one. A week later, I have a barebone implementation complete and I'm not sure how I should test it. How do people usually test and debug these? I haven't even written an assembler so I'd assume it'd be some sort of "ROM" that I load into the memory.

    The source code can be found at the svn repo below:

    https://sourceforge.net/p/mos6502emu/code/10/tree/6502/

    Could you kind folks critique it? I would like to hear from the more experienced folks about what things I'm doing wrong. I'd also like some advice on improving it (granted I can test it and it actually works :P )


    EDIT: Can't edit title of post to correct the misspelling of "Advice"...
    Last edited by Syscal; 07-05-2013 at 10:45 PM.

  2. #2
    Registered User
    Join Date
    Apr 2013
    Posts
    1,658
    Here's another link with stuff for the 6502.

    http://6502.org/tools

    On a side note, the Atari 400 / 800 / 65 XE / 130 XE family ran on 2mhz 6502 processors. There is at least one emulator for the Atari 6502 series. Looks like there are also games, but I'm not sure if you can still get stuff like Atari DOS and along with the utilities like an assembler, linker, ...

    The Apple II ran on a 1mhz 6502, but I never had one, so I don't know much about it.

  3. #3
    Registered User
    Join Date
    Nov 2011
    Posts
    161
    Also the Vic-20 used the 6502. I used to write assembler for it. Ah, the good old days.

  4. #4
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    O_o

    It looks like the start of a good emulator to me.

    I don't know if you did anything wrong, but have you considered building blocks of "decoded operations"? I admit I didn't look at the code a lot, but it looks like `decode_inst' and friends are already suited to the task.

    Just a little thing that may tease a little more performance depending on your compiler: for `TYPE_0' and `TYPE_2', from `decode_inst', you are missing, for lack of a better word, a couple of cases. Yes, I know; these cases don't really exist; that's fine, just insert an empty `case', would be `break;' or maybe `inst_error' for debugging, for the sake of a complete pattern (00-07). Having a complete pattern, even if some cases do nothing, may open up table optimizations for your compiler.

    I know you are just emulating a 6502 for now, but such simple things may help a bit if you do go on to build that NES emulator.

    I have to second on rcgldr; use existing tools that are known to work until you get far enough to even worry about making an assembler.

    Soma
    “Salem Was Wrong!” -- Pedant Necromancer
    “Four isn't random!” -- Gibbering Mouther

  5. #5
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,667
    https://en.wikipedia.org/wiki/MOS_Te...ugs_and_quirks
    On the face of it, it seems that your jmp (indirect) implements CMOS rules rather than NMOS rules.

    The area of "illegal opcodes" is a minefield for emulation.
    My first home computer was a BBC micro, based on the 6502. After a few years, I got a 65C02 upgrade, and a couple of games I had all of a sudden stopped working. This (I eventually found) was all down to using illegal instructions. After much research (this was pre-internet, so no easy web search to help), I figured out what the instructions did (it was an overly contrived 'nop' IIRC), so it was dead easy to patch in the end.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  6. #6
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    The area of "illegal opcodes" is a minefield for emulation.
    QFT! ;_;

    The NES emulator I worked on, with a GBA group, crashed hardcore with several games. We eventually traced it to the PPU and CPU not agreeing over a decoded instruction.

    *sigh*

    Soma
    “Salem Was Wrong!” -- Pedant Necromancer
    “Four isn't random!” -- Gibbering Mouther

  7. #7
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    I believe decode_inst is not always getting the right addressing mode.

    Below is a table of the instructions laid out as 3 8x8 tables.
    Each table corresponds to what you call TYPE.
    Each row corresponds to what you call ADDR_MODE.
    Code:
    . 0       1       2       3       4       5       6       7
    0 BRKi    JSRa    RTIi    RTSi    -       LDY#    CPY#    CPX#
    1 -       BITz    -       -       STYz    LDYz    CPYz    CPXz
    2 PHPi    PLPi    PHAi    PLAi    DEYi    TAYi    INYi    INXi
    3 -       BITa    JMPa    JMPn    STYa    LDYa    CPYa    CPXa
    4 BPLr    BMIr    BVCr    BVSr    BCCr    BCSr    BNEr    BEQr
    5 -       -       -       -       STYz,X  LDYz,X  -       -
    6 CLCi    SECi    CLIi    SEIi    TYAi    CLVi    CLDi    SEDi
    7 -       -       -       -       -       LDYa,X  -       -
    
      0       1       2       3       4       5       6       7
    0 ORAX,n  ANDX,n  EORX,n  ADCX,n  STAX,n  LDAX,n  CMPX,n  SBCX,n
    1 ORAz    ANDz    EORz    ADCz    STAz    LDAz    CMPz    SBCz
    2 ORA#    AND#    EOR#    ADC#    -       LDA#    CMP#    SBC#
    3 ORAa    ANDa    EORa    ADCa    STAa    LDAa    CMPa    SBCa
    4 ORAn,Y  ANDn,Y  EORn,Y  ADCn,Y  STAn,Y  LDAn,Y  CMPn,Y  SBCn,Y
    5 ORAz,X  ANDz,X  EORz,X  ADCz,X  STAz,X  LDAz,X  CMPz,X  SBCz,X
    6 ORAa,Y  ANDa,Y  EORa,Y  ADCa,Y  STAa,Y  LDAa,Y  CMPa,Y  SBCa,Y
    7 ORAa,X  ANDa,X  EORa,X  ADCa,X  STAa,X  LDAa,X  CMPa,X  SBCa,X
    
      0       1       2       3       4       5       6       7
    0 -       -       -       -       -       LDX#    -       -
    1 ASLz    ROLz    LSRz    RORz    STXz    LDXz    DECz    INCz
    2 ASLA    ROLA    LSRA    RORA    TXAi    TAXi    DEXi    NOPi
    3 ASLa    ROLa    LSRa    RORa    STXa    LDXa    DECa    INCa
    4 -       -       -       -       -       -       -       -
    5 ASLz,X  ROLz,X  LSRz,X  RORz,X  STXz,Y  LDXz,Y  DECz,X  INCz,X
    6 -       -       -       -       TXSi    TSXi    -       -
    7 ASLa,X  ROLa,X  LSRa,X  RORa,X  -       LDXa,Y  DECa,X  INCa,X
    Take the first row as an example. You give all of these a mode of immediate, but only 3 are immediate, 3 are implied, and one is absolute.

  8. #8
    C lover
    Join Date
    Oct 2007
    Location
    Virginia
    Posts
    266
    Hm I see what you mean ooga... I followed the table at the following link: The 6502 Instruction Set Decoded

    Looks like they all don't exactly follow the bit pattern. A bunch do though. I am thinking of ways to mitigate that (Like checking the pattern of all these instructions to see if any bits are common between the one-offs). Thanks guys!

  9. #9
    C lover
    Join Date
    Oct 2007
    Location
    Virginia
    Posts
    266
    Ok, I have a revised decode function... Comparing your table to some other documentation Ooga, I'd say one of the two tables is not correct as the TYPE_0 opcodes in the link in my above post are different from what you have as TYPE_0 in your table. I noticed that I was incorrectly decoding the single byte instructions as they don't follow the same bit pattern (as well as jsr absolute) so I have explicitly enumerated those cases:

    Code:
    void decode_inst(mos_6502_t * mos_ctx, opcode_t * op){
    
    
    
      switch(op->opcode){
    
      case 0x00: case 0x40: case 0x60: case 0x08:
      case 0x28: case 0x48: case 0x68: case 0x88: case 0xA8: case 0xC8: case 0xE8:
      case 0x18: case 0x38: case 0x58: case 0x78: case 0x98: case 0xB8: case 0xF8:
      case 0x8A: case 0x9A: case 0xAA: case 0xBA: case 0xCA: case 0xEA:
    
        op->addr_mode = MODE_IMPLIED;
    
      case: 0x20 //jsr
          op->addr_mode = MODE_ABSOLUTE;
    
      default:
        
        switch(op->opcode & TYPE_BITS){
    
        case TYPE_0:
          
          switch((op->opcode & ADDR_MODE_BITS) >> 2){
    
          case 0x00: op->addr_mode = MODE_IMMEDIATE; op->operand_size = 0x02; break;
          case 0x01: op->addr_mode = MODE_ZP; op->operand_size = 0x02; break;
          case 0x02: break;
          case 0x03: op->addr_mode = MODE_ABSOLUTE; op->operand_size = 0x03; break;
          case 0x04: op->addr_mode = MODE_RELATIVE; break;
          case 0x05: op->addr_mode = MODE_ZP_X; op->operand_size =0x02; break;
          case 0x06: break;
          case 0x07: op->addr_mode = MODE_ABSOLUTE_X; op->operand_size = 0x03; break;
    	
          }
    
          break;
    
        case TYPE_1:
    
          switch((op->opcode & ADDR_MODE_BITS) >> 2){
    	
          case 0x00: op->addr_mode = MODE_INDIRECT_X; op->operand_size = 0x02; break;
          case 0x01: op->addr_mode = MODE_ZP; op->operand_size = 0x02; break;
          case 0x02: op->addr_mode = MODE_IMMEDIATE; op->operand_size = 0x02; break;
          case 0x03: op->addr_mode = MODE_ABSOLUTE; op->operand_size = 0x03; break;
          case 0x04: op->addr_mode = MODE_INDIRECT_Y; op->operand_size = 0x02; 0x0; break;
          case 0x05: op->addr_mode = MODE_ZP_X; op->operand_size = 0x02; break;
          case 0x06: op->addr_mode = MODE_ABSOLUTE_Y; op->operand_size = 0x03; break;
          case 0x07: op->addr_mode = MODE_ABSOLUTE_X; op->operand_size = 0x03; break;
    	
          }
          
          break;
          
        case TYPE_2:
          
          switch((op->opcode & ADDR_MODE_BITS) >> 2){
    	
          case 0x00: op->addr_mode = MODE_IMMEDIATE; op->operand_size = 0x02; break;
          case 0x01: op->addr_mode = MODE_ZP; op->operand_size = 0x02; break;
          case 0x02: op->addr_mode = MODE_ACCUMULATOR; op->operand_size = 0x01; break;
          case 0x03: op->addr_mode = MODE_ABSOLUTE; op->operand_size = 0x03; break;
          case 0x04: break;
          case 0x05: op->addr_mode = MODE_ZP_X; op->operand_size = 0x02; break;
          case 0x06: break;
          case 0x07: op->addr_mode = MODE_ABSOLUTE_X; 0x03; break;
    	
          }
          
          break;
          
        }
                
      }
      
      fetch_data(mos_ctx, op);
    
    }

  10. #10
    C lover
    Join Date
    Oct 2007
    Location
    Virginia
    Posts
    266
    Sorry for the triple post but I wrote a little "driver" function to actually test the validity of opcode to address mode and the output seems correct. "trp" is a blackhole (Stands for trap) to allow me to handle the case of an invalid opcode.

    Code:
    Opcode 00(brk) has address mode Implied
    Opcode 01(ora) has address mode Indirect X
    Opcode 02(trp) is a trap
    Opcode 03(trp) is a trap
    Opcode 04(trp) is a trap
    Opcode 05(ora) has address mode Zero Page
    Opcode 06(asl) has address mode Zero Page
    Opcode 07(trp) is a trap
    Opcode 08(php) has address mode Implied
    Opcode 09(ora) has address mode Immediate
    Opcode 0A(asl) has address mode Accumulator
    Opcode 0B(trp) is a trap
    Opcode 0C(trp) is a trap
    Opcode 0D(ora) has address mode Absolute
    Opcode 0E(asl) has address mode Absolute
    Opcode 0F(trp) is a trap
    Opcode 10(bpl) has address mode Relative
    Opcode 11(ora) has address mode Indirect Y
    Opcode 12(trp) is a trap
    Opcode 13(trp) is a trap
    Opcode 14(trp) is a trap
    Opcode 15(ora) has address mode Zero Page X
    Opcode 16(asl) has address mode Zero Page X
    Opcode 17(trp) is a trap
    Opcode 18(clc) has address mode Implied
    Opcode 19(ora) has address mode Absolute Y
    Opcode 1A(trp) is a trap
    Opcode 1B(trp) is a trap
    Opcode 1C(trp) is a trap
    Opcode 1D(ora) has address mode Absolute X
    Opcode 1E(asl) has address mode Absolute X
    Opcode 1F(trp) is a trap
    Opcode 20(jsr) has address mode Absolute
    Opcode 21(and) has address mode Indirect X
    Opcode 22(trp) is a trap
    Opcode 23(trp) is a trap
    Opcode 24(bit) has address mode Zero Page
    Opcode 25(and) has address mode Zero Page
    Opcode 26(rol) has address mode Zero Page
    Opcode 27(trp) is a trap
    Opcode 28(plp) has address mode Implied
    Opcode 29(and) has address mode Immediate
    Opcode 2A(rol) has address mode Accumulator
    Opcode 2B(trp) is a trap
    Opcode 2C(bit) has address mode Absolute
    Opcode 2D(and) has address mode Absolute
    Opcode 2E(rol) has address mode Absolute
    Opcode 2F(trp) is a trap
    Opcode 30(bmi) has address mode Relative
    Opcode 31(and) has address mode Indirect Y
    Opcode 32(trp) is a trap
    Opcode 33(trp) is a trap
    Opcode 34(trp) is a trap
    Opcode 35(and) has address mode Zero Page X
    Opcode 36(rol) has address mode Zero Page X
    Opcode 37(trp) is a trap
    Opcode 38(sec) has address mode Implied
    Opcode 39(and) has address mode Absolute Y
    Opcode 3A(trp) is a trap
    Opcode 3B(trp) is a trap
    Opcode 3C(trp) is a trap
    Opcode 3D(and) has address mode Absolute X
    Opcode 3E(rol) has address mode Absolute X
    Opcode 3F(trp) is a trap
    Opcode 40(rti) has address mode Implied
    Opcode 41(eor) has address mode Indirect X
    Opcode 42(trp) is a trap
    Opcode 43(trp) is a trap
    Opcode 44(trp) is a trap
    Opcode 45(eor) has address mode Zero Page
    Opcode 46(lsr) has address mode Zero Page
    Opcode 47(trp) is a trap
    Opcode 48(pha) has address mode Implied
    Opcode 49(eor) has address mode Immediate
    Opcode 4A(lsr) has address mode Accumulator
    Opcode 4B(trp) is a trap
    Opcode 4C(jmp) has address mode Absolute
    Opcode 4D(eor) has address mode Absolute
    Opcode 4E(lsr) has address mode Absolute
    Opcode 4F(trp) is a trap
    Opcode 50(bvc) has address mode Relative
    Opcode 51(eor) has address mode Indirect Y
    Opcode 52(trp) is a trap
    Opcode 53(trp) is a trap
    Opcode 54(trp) is a trap
    Opcode 55(eor) has address mode Zero Page X
    Opcode 56(lsr) has address mode Zero Page X
    Opcode 57(trp) is a trap
    Opcode 58(cli) has address mode Implied
    Opcode 59(eor) has address mode Absolute Y
    Opcode 5A(trp) is a trap
    Opcode 5B(trp) is a trap
    Opcode 5C(trp) is a trap
    Opcode 5D(eor) has address mode Absolute X
    Opcode 5E(lsr) has address mode Absolute X
    Opcode 5F(trp) is a trap
    Opcode 60(rts) has address mode Implied
    Opcode 61(adc) has address mode Indirect X
    Opcode 62(trp) is a trap
    Opcode 63(trp) is a trap
    Opcode 64(trp) is a trap
    Opcode 65(adc) has address mode Zero Page
    Opcode 66(ror) has address mode Zero Page
    Opcode 67(trp) is a trap
    Opcode 68(pla) has address mode Implied
    Opcode 69(adc) has address mode Immediate
    Opcode 6A(ror) has address mode Accumulator
    Opcode 6B(trp) is a trap
    Opcode 6C(jmp) has address mode Absolute
    Opcode 6D(adc) has address mode Absolute
    Opcode 6E(ror) has address mode Absolute
    Opcode 6F(trp) is a trap
    Opcode 70(bvs) has address mode Relative
    Opcode 71(adc) has address mode Indirect Y
    Opcode 72(trp) is a trap
    Opcode 73(trp) is a trap
    Opcode 74(trp) is a trap
    Opcode 75(adc) has address mode Zero Page X
    Opcode 76(ror) has address mode Zero Page X
    Opcode 77(trp) is a trap
    Opcode 78(sei) has address mode Implied
    Opcode 79(adc) has address mode Absolute Y
    Opcode 7A(trp) is a trap
    Opcode 7B(trp) is a trap
    Opcode 7C(trp) is a trap
    Opcode 7D(adc) has address mode Absolute X
    Opcode 7E(ror) has address mode Absolute X
    Opcode 7F(trp) is a trap
    Opcode 80(trp) is a trap
    Opcode 81(sta) has address mode Indirect X
    Opcode 82(trp) is a trap
    Opcode 83(trp) is a trap
    Opcode 84(sty) has address mode Zero Page
    Opcode 85(sta) has address mode Zero Page
    Opcode 86(stx) has address mode Zero Page
    Opcode 87(trp) is a trap
    Opcode 88(dey) has address mode Implied
    Opcode 89(trp) is a trap
    Opcode 8A(txa) has address mode Implied
    Opcode 8B(trp) is a trap
    Opcode 8C(sty) has address mode Absolute
    Opcode 8D(sta) has address mode Absolute
    Opcode 8E(stx) has address mode Absolute
    Opcode 8F(trp) is a trap
    Opcode 90(bcc) has address mode Relative
    Opcode 91(sta) has address mode Indirect Y
    Opcode 92(trp) is a trap
    Opcode 93(trp) is a trap
    Opcode 94(sty) has address mode Zero Page X
    Opcode 95(sta) has address mode Zero Page X
    Opcode 96(stx) has address mode Zero Page X
    Opcode 97(trp) is a trap
    Opcode 98(tya) has address mode Implied
    Opcode 99(sta) has address mode Absolute Y
    Opcode 9A(txs) has address mode Implied
    Opcode 9B(trp) is a trap
    Opcode 9C(trp) is a trap
    Opcode 9D(sta) has address mode Absolute X
    Opcode 9E(trp) is a trap
    Opcode 9F(trp) is a trap
    Opcode A0(ldy) has address mode Immediate
    Opcode A1(lda) has address mode Indirect X
    Opcode A2(ldx) has address mode Immediate
    Opcode A3(trp) is a trap
    Opcode A4(ldy) has address mode Zero Page
    Opcode A5(lda) has address mode Zero Page
    Opcode A6(ldx) has address mode Zero Page
    Opcode A7(trp) is a trap
    Opcode A8(tay) has address mode Implied
    Opcode A9(lda) has address mode Immediate
    Opcode AA(tax) has address mode Implied
    Opcode AB(trp) is a trap
    Opcode AC(ldy) has address mode Absolute
    Opcode AD(lda) has address mode Absolute
    Opcode AE(ldx) has address mode Absolute
    Opcode AF(trp) is a trap
    Opcode B0(bcs) has address mode Relative
    Opcode B1(lda) has address mode Indirect Y
    Opcode B2(trp) is a trap
    Opcode B3(trp) is a trap
    Opcode B4(ldy) has address mode Zero Page X
    Opcode B5(lda) has address mode Zero Page X
    Opcode B6(ldx) has address mode Zero Page X
    Opcode B7(trp) is a trap
    Opcode B8(clv) has address mode Implied
    Opcode B9(lda) has address mode Absolute Y
    Opcode BA(tsx) has address mode Implied
    Opcode BB(trp) is a trap
    Opcode BC(ldy) has address mode Absolute X
    Opcode BD(lda) has address mode Absolute X
    Opcode BE(ldx) has address mode Absolute X
    Opcode BF(trp) is a trap
    Opcode C0(cpy) has address mode Immediate
    Opcode C1(cmp) has address mode Indirect X
    Opcode C2(trp) is a trap
    Opcode C3(trp) is a trap
    Opcode C4(cpy) has address mode Zero Page
    Opcode C5(cmp) has address mode Zero Page
    Opcode C6(dec) has address mode Zero Page
    Opcode C7(trp) is a trap
    Opcode C8(iny) has address mode Implied
    Opcode C9(cmp) has address mode Immediate
    Opcode CA(dex) has address mode Implied
    Opcode CB(trp) is a trap
    Opcode CC(cpy) has address mode Absolute
    Opcode CD(cmp) has address mode Absolute
    Opcode CE(dec) has address mode Absolute
    Opcode CF(trp) is a trap
    Opcode D0(bne) has address mode Relative
    Opcode D1(cmp) has address mode Indirect Y
    Opcode D2(trp) is a trap
    Opcode D3(trp) is a trap
    Opcode D4(trp) is a trap
    Opcode D5(cmp) has address mode Zero Page X
    Opcode D6(dec) has address mode Zero Page X
    Opcode D7(trp) is a trap
    Opcode D8(cld) has address mode Zero Page X
    Opcode D9(cmp) has address mode Absolute Y
    Opcode DA(trp) is a trap
    Opcode DB(trp) is a trap
    Opcode DC(trp) is a trap
    Opcode DD(cmp) has address mode Absolute X
    Opcode DE(dec) has address mode Absolute X
    Opcode DF(trp) is a trap
    Opcode E0(cpx) has address mode Immediate
    Opcode E1(sbc) has address mode Indirect X
    Opcode E2(trp) is a trap
    Opcode E3(trp) is a trap
    Opcode E4(cpx) has address mode Zero Page
    Opcode E5(sbc) has address mode Zero Page
    Opcode E6(inc) has address mode Zero Page
    Opcode E7(trp) is a trap
    Opcode E8(inx) has address mode Implied
    Opcode E9(sbc) has address mode Immediate
    Opcode EA(nop) has address mode Implied
    Opcode EB(trp) is a trap
    Opcode EC(cpx) has address mode Absolute
    Opcode ED(sbc) has address mode Absolute
    Opcode EE(inc) has address mode Absolute
    Opcode EF(trp) is a trap
    Opcode F0(beq) has address mode Relative
    Opcode F1(sbc) has address mode Indirect Y
    Opcode F2(trp) is a trap
    Opcode F3(trp) is a trap
    Opcode F4(trp) is a trap
    Opcode F5(sbc) has address mode Zero Page X
    Opcode F6(inc) has address mode Zero Page X
    Opcode F7(trp) is a trap
    Opcode F8(sed) has address mode Implied
    Opcode F9(sbc) has address mode Absolute Y
    Opcode FA(trp) is a trap
    Opcode FB(trp) is a trap
    Opcode FC(trp) is a trap
    Opcode FD(sbc) has address mode Absolute X
    Opcode FE(inc) has address mode Absolute X
    Opcode FF(trp) is a trap
    Last edited by Syscal; 07-06-2013 at 07:51 PM.

  11. #11
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,667
    > Opcode 6C(jmp) has address mode Absolute
    Should be jmp (indirect)
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  12. #12
    C lover
    Join Date
    Oct 2007
    Location
    Virginia
    Posts
    266
    Good catch! Looking at the bit patterns, it's because both actually have the same bit pattern in the address mode fields:

    Code:
    0110 1100 (jmp(6c) absolute)
    
    0100 1100 (jmp(4c) indirect)
    I'll have to add a special case for it

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Writing a MOS 6502 emulator
    By Syscal in forum C Programming
    Replies: 20
    Last Post: 07-03-2013, 07:04 PM
  2. 6502 assembler question
    By Syscal in forum C Programming
    Replies: 2
    Last Post: 09-21-2010, 10:25 PM
  3. Replies: 15
    Last Post: 08-30-2010, 09:26 AM
  4. Replies: 2
    Last Post: 08-29-2009, 06:10 AM
  5. Semaphores, need advice on implementation.
    By Swerve in forum C++ Programming
    Replies: 2
    Last Post: 01-13-2009, 01:54 AM