// actions.h header file, contains all the asm macros that actually do the work // to emulate 6502 instructions. //--------------------------------------------------------------------------------------------- // Define macros to do 6502 addressing modes. Addressing modes determine // the ways in which memory location can be addressed and accessed. // The xxxx_SET macros retrieve the value to be operated upon stored at the target address. // The xxxx_ADDR macros retrieve the target address. // Note that the 6502 is little endian. // X indexed indirect - take address from instruction, add X to it, to // get the location of the least sign byte (LSB) of the target 16 bit address. // So target address (op_addr) = value at address location (rX + instr[1]). #define XIND_SET \ ind_addr = rX + instr[1];\ op_addr = mem((unsigned int)ind_addr);\ ((unsigned char*)&op_addr)[1] = mem((unsigned int)ind_addr+1);\ operand = mem(op_addr); // Indirect indexed using index register Y - instruction contains the // location of the LSB of the target address, and Y is added to the // to the value stored at that address. So target address (op_addr) = (value // at address location instr[1]) + rY #define INDY_SET \ op_addr = mem((unsigned int)instr[1]); \ ((unsigned char*)&op_addr)[1] = mem((unsigned int)instr[1]+1); \ operand = mem(op_addr + rY); // Immediate addressing allows 8 bit constant values to be specified #define IMM_SET operand = instr[1]; // Zero page address mode, target address = 8 bit address contained in the instruction #define ZP_SET operand = mem((unsigned int)instr[1]); // Zero page X, target address = 8 bit address operand + X #define ZPX_SET operand = mem((unsigned int)(instr[1]+rX)); // Same as zero page X but using Y register; can only be used with LDX and STX instructions. #define ZPY_SET operand = mem((unsigned int)(instr[1]+rY)); // Absolute addressing, target address = 16 bit address contained in the instruction #define ABS_SET \ op_addr = instr[1]; \ ((unsigned char*)&op_addr)[1] = instr[2]; \ operand = mem(op_addr); // Absolute addressing X, target address = 16 bit address contained in the instruction + X #define ABSX_SET \ op_addr = instr[1]; \ ((unsigned char*)&op_addr)[1] = instr[2]; \ operand = mem(op_addr+rX); // Same as absolute addressing X but using Y register #define ABSY_SET \ op_addr = instr[1]; \ ((unsigned char*)&op_addr)[1] = instr[2]; \ operand = mem(op_addr+rY); // Accumulator addressing mode. Similar to immediate addressing except operating // on the value stored in the accumulator instead of the immediate. #define ACC_SET operand = rA; //--------------------------------------------------------------------------------------------- // For some instructions (like those that store data into memory), we only need the target // address (still determined by the addressing mode) and we don't need the values stored // in that address. Otherwise these macros are similar to the xxxx_SET macros. #define XIND_ADDR \ ind_addr = rX + instr[1];\ op_addr = mem((unsigned int)ind_addr);\ ((unsigned char*)&op_addr)[1] = mem((unsigned int)ind_addr+1);\ #define INDY_ADDR op_addr = (unsigned int)mem((unsigned int)instr[1]) + rY; //#define IMM_SET op_addr = instr[1]; //NOT USED, left here for future reference #define ZP_ADDR op_addr = (unsigned int)instr[1]; #define ZPX_ADDR op_addr = (unsigned int)(instr[1]+rX); #define ZPY_ADDR op_addr = (unsigned int)(instr[1]+rY); #define ABS_ADDR \ op_addr = instr[1]; \ ((unsigned char*)&op_addr)[1] = instr[2]; \ #define ABSX_ADDR \ op_addr = instr[1]; \ ((unsigned char*)&op_addr)[1] = instr[2]; \ op_addr = op_addr+rX; #define ABSY_ADDR \ op_addr = instr[1]; \ ((unsigned char*)&op_addr)[1] = instr[2]; \ op_addr = op_addr+rY; // this gives the relative address to be added to a PC; only used for branch instructions #define REL_ADDR op_addr = instr[1]; // only used by the JMP instruction; cause the next instruction execution to occur at the // address referenced by the 16 bit address contained in the instruction. #define IND_ADDR \ ((unsigned char*)&ind_addr2)[0] = instr[1]; \ ((unsigned char*)&ind_addr2)[1] = instr[2]; \ ((unsigned char*)&op_addr)[0] = mem(ind_addr2); \ ((unsigned char*)&op_addr)[1] = mem(ind_addr2+1); //--------------------------------------------------------------------------------------------- // Define assembly macros that sets the status register flags N, V, Z, and C // by copying the corresponding status flags set in the mega32 sreg. This means that // before using any of these macros to set a status flag, the mega32 sreg must have correctly // set the same status flag. To prevent the mega32 sreg from being corrupted by other instructions, // it must be copied into REG_OP first before using any of these macros. #asm .macro set_sreg_n ; set N bit of 6502 sreg out SREG, REG_OP ; restore mega32 sreg from copy in op brmi @0 ; branch if negative bit set andi REG_SR, ASR_NI ; negative bit not set so set 6502 sreg N bit low rjmp @1 ; continue on @0: ori REG_SR, ASR_N ; negative bit set so set 6502 sreg N bit high @1: .endm .macro set_sreg_v ; set V bit of 6502 sreg out SREG, REG_OP ; restore mega32 sreg from copy in op brvs @0 ; branch if overflow bit set andi REG_SR, ASR_VI ; overflow bit not set so set 6502 sreg V bit low rjmp @1 ; continue on @0: ori REG_SR, ASR_V ; overflow bit set so set 6502 sreg V bit high @1: .endm .macro set_sreg_z ; set Z bit of 6502 sreg out SREG, REG_OP ; restore mega32 sreg from copy in op brbs 1,@0 ; branch if zero bit set andi REG_SR, ASR_ZI ; zero bit not set so set 6502 sreg Z bit low rjmp @1 ; continue on @0: ori REG_SR, ASR_Z ; zero bit set so set 6502 sreg bit Z high @1: .endm .macro set_sreg_c ; set C bit of 6502 sreg out SREG, REG_OP ; restore mega32 sreg from copy in op brcs @0 ; branch if carry bit set andi REG_SR, ASR_CI ; carry bit not set so set 6502 sreg C bit low rjmp @1 ; continue on @0: ori REG_SR, ASR_C ; carry bit set so set 6502 sreg C bit high @1: .endm #endasm //--------------------------------------------------------------------------------------------- // Macro declared to make setting a bit in the emulated 6502 sreg // directly easier; sets bit @1 of REG_SR to @0 #asm .macro SET_SREG_BIT ldi REG_TMP, @0 bst REG_TMP, 0 bld REG_SR, @1 .endm #endasm //--------------------------------------------------------------------------------------------- // Define assembly macros that execute the equivalent operation to emulate 6502 instructions, with // the exception that instructions that access memory are defined using C macros instead. // // Parameters into the macros are usually labels (which must be different for each use of the macro) // in which branches are used to set the appropriate status register bits. // or #asm .macro EX_OR OR REG_A, REG_OP ;rA = rA | operand in REG_OP, SREG ;store mega sreg in op reg (used as temp) set_sreg_n @0, @1 set_sreg_z @2, @3 .endm #endasm // and #asm .macro EX_AND AND REG_A, REG_OP ;rA = rA & operand in REG_OP, SREG ;store mega sreg in op reg (used as temp) set_sreg_n @0, @1 set_sreg_z @2, @3 .endm #endasm // exclusive or #asm .macro EX_EOR EOR REG_A, REG_OP ;rA = rA ^ operand in REG_OP, SREG ;store mega sreg in op reg (used as temp) set_sreg_n @0, @1 set_sreg_z @2, @3 .endm #endasm // store accumulator into memory #define EX_STA store_mem(rA,op_addr); // compare accumulator with a value in memory #asm .macro EX_CMP CP @6, REG_OP ;compare A/X/Y and M in REG_OP, SREG ;store mega sreg in op reg (used as temp), except carry bit is different brmi @4 ;carrybit is opposite of the negative bit SET_SREG_BIT 1, ASR_CB ;set carry bit if neg bit was not set rjmp @5 @4: SET_SREG_BIT 0, ASR_CB ;clear carry bit if neg bit was set @5: set_sreg_n @0, @1 ;set N bit for 6502 sreg set_sreg_z @2, @3 ;set Z bit for 6502 sreg .endm #endasm // add with carry, multiple macros are required due to asm macro constraint of 10 parameters // and implementation of decimal mode #asm ;The following macros should be called as follows (or similar): ;EX_ADC1 ;rjmp ADCXINDIRECTD ;SET_NVZC ADCXINDIRECT1, ADCXINDIRECT2, ADCXINDIRECT3, ADCXINDIRECT4, ADCXINDIRECT5, ADCXINDIRECT6, ADCXINDIRECT7, ADCXINDIRECT8 ;rjmp ADCXINDIRECTEND ;ADCXINDIRECTD: ;EX_ADC2 ADCXINDIRECTD1, ADCXINDIRECTD2, ADCXINDIRECTD3, ADCXINDIRECTD4, ADCXINDIRECTD5, ADCXINDIRECTD6, ADCXINDIRECTD7 ;ADCXINDIRECTEND: .macro EX_ADC1 mov REG_TMP, REG_SR ;tmp = 6502 sreg andi REG_TMP, ASR_C ;get 6502 carry bit ldi REG_TMP2, 0xff ;store 0xff in tmp2 add REG_TMP, REG_TMP2 ;trigger mega32 carry if 6502 carry bit was set sbrs REG_SR, ASR_DB ;skip next instruction if decimal mode adc REG_A, REG_OP ;perform add w/ carry and store back to regA sbrc REG_SR, ASR_DB ;skip next instruction if binary mode .endm .macro SET_NVZC in REG_OP, SREG ;store mega32 sreg in op reg (used as temp) set_sreg_n @0, @1 ;set N bit for 6502 sreg set_sreg_v @2, @3 ;set V bit for 6502 sreg set_sreg_z @4, @5 ;set Z bit for 6502 sreg set_sreg_c @6, @7 ;set C bit for 6502 sreg .endm .macro EX_ADC2 mov REG_TMP2, REG_A ;can only call BCDADD on three high registers and not REG_TMP mov REG_TMP3, REG_OP BCDADD REG_TMP2, REG_TMP3, REG_TMP4, @0, @1, @2, @3, @4, @5, @6 mov REG_A, REG_TMP2 .endm #endasm // load a byte of memory into accumulator #asm .macro EX_LDA mov REG_A, REG_OP ;rA = operand ldi REG_TMP, 0x00 ;store 0x00 in tmp add REG_A, REG_TMP ;set mega32 sregs n bit and z bit in REG_OP, SREG ;store mega sreg in op reg (used as temp) set_sreg_n @0, @1 ;set N bit for 6502 sreg set_sreg_z @2, @3 ;set Z bit for 6502 sreg .endm #endasm // subtract with carry, multiple macros are required due to asm macro constraint of 10 parameters // and implementation of decimal mode #asm ;The following macros should be called like the following: ;EX_SBC1 SBCXINDB1, SBCXINDB2, SBCXINDB3 ;rjmp SBCXINDEND ;SBCXINDD: ;EX_SBC2 SBCXINDD1, SBCXINDD2, SBCXINDD3, SBCXINDD4, SBCXINDD5, SBCXINDD6, SBCXINDD7 ;SBCXINDEND: .macro EX_SBC1 sbrc REG_SR, ASR_DB ;if we are in decimal mode, get out of here rjmp @0 mov REG_TMP, REG_SR ;tmp = 6502 sreg andi REG_TMP, ASR_C ;get 6502 carry bit ldi REG_TMP2, ASR_C ;get negated carry bit eor REG_TMP, REG_TMP2 ldi REG_TMP2, 0xff ;store 0xff in tmp2 add REG_TMP, REG_TMP2 ;trigger mega32 carry if 6502 carry bit was not set SBC REG_A, REG_OP ;A = A - M - (1 - sC) in REG_OP, SREG ; save the sreg for later sets ldi REG_TMP4, 0x00 cp REG_A, REG_TMP4 brlt @1 ; set flags - this could be more efficient if needed breq @2 SET_SREG_BIT 1, ASR_CB ; greater SET_SREG_BIT 0, ASR_VB SET_SREG_BIT 0, ASR_NB SET_SREG_BIT 0, ASR_ZB out SREG, REG_OP rjmp @0 @1: SET_SREG_BIT 0, ASR_CB ; less than SET_SREG_BIT 1, ASR_VB SET_SREG_BIT 1, ASR_NB SET_SREG_BIT 0, ASR_ZB out SREG, REG_OP rjmp @0 @2: SET_SREG_BIT 1, ASR_CB ; equal to SET_SREG_BIT 0, ASR_VB SET_SREG_BIT 0, ASR_NB SET_SREG_BIT 1, ASR_ZB out SREG, REG_OP @0: sbrs REG_SR, ASR_DB ;skip next instruction if decimal mode .endm .macro EX_SBC2 cp REG_A, REG_OP brlt @4 breq @5 SET_SREG_BIT 1, ASR_CB; greater rjmp @6 @4: SET_SREG_BIT 0, ASR_CB; less rjmp @6 @5: SET_SREG_BIT 1, ASR_CB; equal @6: mov REG_TMP4, REG_SR mov REG_TMP, REG_SR ;tmp = 6502 sreg andi REG_TMP, ASR_C ;get 6502 carry bit ldi REG_TMP2, ASR_C ;get negated carry bit eor REG_TMP, REG_TMP2 ldi REG_TMP2, 0xff ;store 0xff in tmp2 add REG_TMP, REG_TMP2 ;trigger mega32 carry if 6502 carry bit was not set mov REG_TMP2, REG_A ;can only call BCDSUB on 2 high registers and not REG_TMP mov REG_TMP3, REG_OP BCDSUB REG_TMP2, REG_TMP3, @0, @1, @2 ;A = A - M - (1-C) mov REG_A, REG_TMP2 mov REG_SR, REG_TMP4 ; set z bit ldi REG_TMP4, 0x00 cp REG_A, REG_TMP4 brne @3 SET_SREG_BIT 1, ASR_ZB @3: SET_SREG_BIT 0, ASR_ZB .endm #endasm // arithmetic shift left - shift all bits in accumulator or memory (depending on the // addressing mode) one bit to the left. Bit0 = 0. Carry flag set to old bit7 value. // note: this macro needs memory set and stores surrounding it #asm .macro EX_ASL LSL REG_OP mov REG_TMP, REG_OP in REG_OP, SREG ;store mega sreg in op reg (used as temp) set_sreg_n @0, @1 ;set N bit for 6502 sreg set_sreg_z @2, @3 ;set Z bit for 6502 sreg set_sreg_c @4, @5 ;set C bit for 6502 sreg mov REG_OP, REG_TMP .endm #endasm // rotate left - shift all bits in accumulator or memory (depending on the // addressing mode) one bit to the left. Bit0 = Carry flag. Carry flag set to old bit7 value. // note: this macro needs memory set and stores surrounding it #asm .macro EX_ROL mov REG_TMP, REG_SR ;tmp = 6502 sreg andi REG_TMP, ASR_C ;get 6502 carry bit ldi REG_TMP2, 0xff ;store 0xff in tmp2 add REG_TMP, REG_TMP2 ;trigger mega32 carry if 6502 carry bit was set ROL REG_OP mov REG_TMP, REG_OP in REG_OP, SREG ;store mega sreg in op reg (used as temp) set_sreg_n @0, @1 ;set N bit for 6502 sreg set_sreg_z @2, @3 ;set Z bit for 6502 sreg set_sreg_c @4, @5 ;set C bit for 6502 sreg mov REG_OP, REG_TMP .endm #endasm // logic shift right - shift all bits in accumulator or memory (depending on the // addressing mode) one bit to the right. Bit7 = 0. Carry flag = old bit0. // note: this macro needs memory set and stores surrounding it #asm .macro EX_LSR LSR REG_OP mov REG_TMP2, REG_OP in REG_OP, SREG ;store mega sreg in op reg (used as temp) SET_SREG_BIT 0, ASR_NB ;set N bit to 0 set_sreg_z @0, @1 ;set Z bit for 6502 sreg set_sreg_c @2, @3 ;set C bit for 6502 sreg mov REG_OP, REG_TMP2 .endm #endasm // rotate right - shift all bits in accumulator or memory (depending on the // addressing mode) one bit to the right. Bit7 = Carry flag. Carry flag set to old bit0 value. // note: this macro needs memory set and stores surrounding it #asm .macro EX_ROR mov REG_TMP, REG_SR ;tmp = 6502 sreg andi REG_TMP, ASR_C ;get 6502 carry bit ldi REG_TMP2, 0xff ;store 0xff in tmp2 add REG_TMP, REG_TMP2 ;trigger mega32 carry if 6502 carry bit was set ROR REG_OP mov REG_TMP, REG_OP in REG_OP, SREG ;store mega sreg in op reg (used as temp) set_sreg_n @0, @1 ;set N bit for 6502 sreg set_sreg_z @2, @3 ;set Z bit for 6502 sreg set_sreg_c @4, @5 ;set C bit for 6502 sreg mov REG_OP, REG_TMP .endm #endasm // bit test - test whether some bits are set in a target memory location. // Zero flag = (A & M == 0) // Negative flag N = M7 // Overflow flag V = M6 #asm .macro EX_BIT ; set top two bits of sreg to top two bits of REG_OP mov REG_TMP, REG_OP andi REG_TMP, 0xC0 or REG_SR, REG_TMP mov REG_TMP, REG_OP ori REG_TMP, 0x3F and REG_SR, REG_TMP ; set zero flag to result of A & M and REG_OP, REG_A in REG_OP, SREG set_sreg_z @0, @1 .endm #endasm // jump #define EX_JMP rPC = op_addr; // store X into memory #define EX_STX store_mem(rX,op_addr); // store Y into memory #define EX_STY store_mem(rY,op_addr); // note: for the following branch instructions, we don't actually need to add the high byte // of opaddr; however, we do need the carry, and since the high byte is 0, this is faster // than other alternatives and works // branch if positive #asm .macro EX_BPL ; if(!SR_N) rPC += op_addr; bst REG_SR, ASR_NB brts @0 add REG_PCL, REG_OPADDRL adc REG_PCH, REG_OPADDRH @0: .endm #endasm // branch on minus #asm .macro EX_BMI ; if(SR_N) rPC += op_addr; bst REG_SR, ASR_NB brtc @0 add REG_PCL, REG_OPADDRL adc REG_PCH, REG_OPADDRH @0: .endm #endasm // branch on overflow clear #asm .macro EX_BVC ; if(!SR_V) rPC += op_addr; bst REG_SR, ASR_VB brts @0 add REG_PCL, REG_OPADDRL adc REG_PCH, REG_OPADDRH @0: .endm #endasm // branch on overflow set #asm .macro EX_BVS ; if(SR_V) rPC += op_addr; bst REG_SR, ASR_VB brtc @0 add REG_PCL, REG_OPADDRL adc REG_PCH, REG_OPADDRH @0: .endm #endasm // branch on carry clear #asm .macro EX_BCC ; if(!SR_C) rPC += op_addr; bst REG_SR, ASR_CB brts @0 add REG_PCL, REG_OPADDRL adc REG_PCH, REG_OPADDRH @0: .endm #endasm // branch on carry set #asm .macro EX_BCS ; if(SR_C) rPC += op_addr; bst REG_SR, ASR_CB brtc @0 add REG_PCL, REG_OPADDRL adc REG_PCH, REG_OPADDRH @0: .endm #endasm // branch on not equal #asm .macro EX_BNE ; if(!SR_Z) rPC += op_addr; bst REG_SR, ASR_ZB brts @0 add REG_PCL, REG_OPADDRL adc REG_PCH, REG_OPADDRH @0: .endm #endasm // branch on equal #asm .macro EX_BEQ ; if(SR_Z) rPC += op_addr; bst REG_SR, ASR_ZB brtc @0 add REG_PCL, REG_OPADDRL adc REG_PCH, REG_OPADDRH @0: .endm #endasm // decrement X register by 1; X = X-1 #asm .macro EX_DEX ldi REG_TMP, 1 sub REG_X, REG_TMP in REG_OP, SREG set_sreg_n @0, @1 set_sreg_z @2, @3 .endm #endasm // decrement Y register by 1; Y = Y-1 #asm .macro EX_DEY ldi REG_TMP, 1 sub REG_Y, REG_TMP in REG_OP, SREG set_sreg_n @0, @1 set_sreg_z @2, @3 .endm #endasm // transfer accumulator to Y; Y = A #asm .macro EX_TAY mov REG_Y, REG_A ldi REG_TMP, 0 add REG_Y, REG_TMP in REG_OP, SREG set_sreg_n @0, @1 set_sreg_z @2, @3 .endm #endasm // transfer accumulator to X; X = A #asm .macro EX_TAX mov REG_X, REG_A ; set n, z ldi REG_TMP, 0 add REG_X, REG_TMP in REG_OP, SREG ; store mega32 sreg in op reg(used as temp) set_sreg_n @0, @1 ; set N bit for 6502 sreg set_sreg_z @2, @3 ; set Z bit for 6502 sreg .endm #endasm // increment Y register; Y = Y+1 #asm .macro EX_INY ldi REG_TMP, 1 add REG_Y, REG_TMP in REG_OP, SREG set_sreg_n @0, @1 set_sreg_z @2, @3 .endm #endasm // increment X register; X = X+1 #asm .macro EX_INX ldi REG_TMP, 1 add REG_X, REG_TMP in REG_OP, SREG set_sreg_n @0, @1 set_sreg_z @2, @3 .endm #endasm // transfer Y to accumulator; A = Y #asm .macro EX_TYA mov REG_A, REG_Y ldi REG_TMP, 0 add REG_A, REG_TMP in REG_OP, SREG set_sreg_n @0, @1 set_sreg_z @2, @3 .endm #endasm // transfer X to accumulator; A = X #asm .macro EX_TXA mov REG_A, REG_X ldi REG_TMP, 0 add REG_A, REG_TMP in REG_OP, SREG set_sreg_n @0, @1 set_sreg_z @2, @3 .endm #endasm // transfer X to stack pointer; S = X #asm .macro EX_TXS mov REG_SP, REG_X .endm #endasm // transfer stack pointer to X; X = S #asm .macro EX_TSX mov REG_X, REG_SP ldi REG_TMP, 0 add REG_X, REG_TMP in REG_OP, SREG set_sreg_n @0, @1 set_sreg_z @2, @3 .endm #endasm // increment memory; value held at specified memory location = value + 1 // note: this needs memory set and stores surrounding it #asm .macro EX_INC mov REG_TMP, REG_OP inc REG_TMP in REG_OP, SREG set_sreg_n @0, @1 set_sreg_z @2, @3 mov REG_OP, REG_TMP .endm #endasm // decrement memory; value held at specified memory location = value - 1 // note: this needs memory set and stores surrounding it #asm .macro EX_DEC mov REG_TMP, REG_OP dec REG_TMP in REG_OP, SREG set_sreg_n @0, @1 set_sreg_z @2, @3 mov REG_OP, REG_TMP .endm #endasm // load X register; load a byte of memory into X #asm .macro EX_LDX mov REG_X, REG_OP ldi REG_TMP, 0 add REG_X, REG_TMP in REG_OP, SREG set_sreg_z @0, @1 set_sreg_n @0, @1 .endm #endasm // break forces interrupt to occur. PC is pushed onto the stack, then // the break flag is set to one, then the interrupt vectors at $FFFE $FFFF is // loaded into the PC #define EX_BRK \ rPC += 2; \ store_mem(rPCH, 0x0100 | (rSP--)); \ store_mem(rPCL, 0x0100 | (rSP--)); \ #asm("SET_SREG_BIT 1, ASR_BB"); \ #asm("mov REG_OP, REG_SR"); \ #asm("ldi REG_TMP, ASR_IB"); \ #asm("or REG_OP, REG_TMP"); \ store_mem(operand, 0x0100 | (rSP--)); \ rPCL = mem(0xFFFE); \ rPCH = mem(0xFFFF); #define EX_RTI \ operand = mem(0x0100 | (++rSP)); \ #asm("mov REG_SR, REG_OP"); \ #asm("SET_SREG_BIT 0, ASR_BB"); \ rPCL = mem(0x0100 | (++rSP)); \ rPCH = mem(0x0100 | (++rSP)); // load Y register; load a byte of memory into Y #asm .macro EX_LDY mov REG_Y, REG_OP ldi REG_TMP, 0 add REG_Y, REG_TMP in REG_OP, SREG set_sreg_z @0, @1 set_sreg_n @0, @1 .endm #endasm // push processor status; push copy of status flags (REG_SR) onto the stack #define EX_PHP\ #asm("SET");\ /* breakpoint must be set (bug in 6502) */ #asm("BLD REG_SR, ASR_BB");\ #asm("mov REG_OP, REG_SR");\ store_mem(operand,0x0100 | (rSP--)); // pull processor status; pull a byte from the stack and set the status flags (REG_SR) to it #define EX_PLP \ operand = mem(0x0100 | (++rSP)); \ #asm("mov REG_SR, REG_OP"); // push accumulator; push copy of accumulator onto the stack #define EX_PHA\ #asm("mov REG_OP, REG_A");\ store_mem(operand,0x0100 | (rSP--)); // pull accumulator; pull a byte from the stack and set the accumulator to it #define EX_PLA \ operand = mem(0x0100 | (++rSP)); \ #asm("mov REG_A, REG_OP"); \ #asm("ldi REG_TMP, 0"); \ #asm("add REG_A, REG_TMP"); \ #asm("in REG_OP, SREG"); \ #asm("set_sreg_z PLA1, PLA2"); \ #asm("set_sreg_n PLA2, PLA3"); \ // jump to subroutine; push PC+2 onto the stack (this stores return address + 2), // then set PC to target memory address #define EX_JSR \ rPC += 2; \ store_mem(rPCH, 0x0100 | (rSP--)); \ store_mem(rPCL, 0x0100 | (rSP--)); \ rPC = op_addr; // return from subroutine; pulls PC from the stack then adds 1 since JSR // is a 3 byte instruction #define EX_RTS \ rPCL = mem(0x0100 | (++rSP)); \ rPCH = mem(0x0100 | (++rSP)); \ rPC++; // BCD addition, used for decimal mode #asm ;*************************************************************************** ;* ;* "BCDADD" - 2-digit packed BCD addition ;* ;* This macro adds the two unsigned 2-digit BCD numbers ;* @0 and @1. The result is returned in @0, and the ;* carry set in REG_SR. No other SREG flags are set on the 6502. ;* ;* Note that @0,@1,nor @2 may be REG_TMP or REG_SR. ;* ;* This was adapted from AVR Application Note 204. ;* Changes made for compatibility and faster runtime. ;* ;*************************************************************************** ;***** Macro Parameters ; BCD operand 1 = @0 (8-bit high reg) ; BCD operand 2 = @1 (8-bit high reg) ; temp variable = @2 (8-bit high reg) ; internal lbls = @3,@4,5,6,7,8,9 .macro BCDADD ldi @2,6 ;value to be added later adc @0,@1 ;add the numbers binary ;clr @1 ;clear BCD carry SET_SREG_BIT 0, ASR_CB ; clear BCD carry brcc @4 ;if carry not clear ;ldi @1,1 ; set BCD carry SET_SREG_BIT 1, ASR_CB @4: brhs @5 ;if half carry not set add @0,@2 ;add 6 to LSD brhs @6 ;if half carry not set (LSD <= 9) subi @0,6 ;restore value rjmp @6 ;else @5: add @0,@2 ;add 6 to LSD @6: brcc @7 ldi @1,1 @7: swap @2 add @0,@2 ;add 6 to MSD brcs @9 ;if carry not set (MSD <= 9) ;sbrs @1,0 ;if previous carry not set sbrs REG_SR,ASR_CB ;if previous carry not set subi @0,$60 ;restore value @8: rjmp @3 ;else @9: SET_SREG_BIT 1, ASR_CB ;ldi @1,1 ; set BCD carry @3: .endm #endasm // BCD subtraction, used for decimal mode #asm ;*************************************************************************** ;* ;* "BCDSUB" - 2-digit packed BCD subtraction ;* ;* This subroutine subtracts the two unsigned 2-digit BCD numbers ;* @0 and @1 (@0 - @1 - C). The result is returned in @0, and ;* the underflow carry in REG_SR ;* ;* Note that neither @0 nor @1 may be REG_TMP or REG_SR. ;* ;* This was adapted from AVR Application Note 204. ;* Original contained bug on last branch; other changes made ;* for compatibility and faster runtime. ;* ;*************************************************************************** ;***** Macro Parameters ; BCD operand 1 = @0 (8-bit high reg) ; BCD operand 2 = @1 (8-bit high reg) ; internal lbls = @2,@3,@4 .macro BCDSUB sbc @0,@1 ;subtract the numbers binary ;clr @1 SET_SREG_BIT 0, ASR_CB ;clear BCD carry brcc @2 ;if carry not clear ;ldi @1,1 ;store carry in BCDB1, bit 0 SET_SREG_BIT 1, ASR_CB @2: brhc @3 ;if half carry not clear subi @0,$06 ; LSD = LSD - 6 @3: ;sbrs @1,0 ;if previous carry not set sbrs REG_SR,ASR_CB ;if previous carry not set rjmp @4 ;return subi @0,$60 ;subtract 6 from MSD ;ldi @1,1 ;set underflow carry SET_SREG_BIT 1, ASR_CB ;set underflow carry brcs @4 ;if carry clear ;ldi @1,0 ;clear underflow carry SET_SREG_BIT 0, ASR_CB ;clear underflow carry @4: .endm #endasm