In these descriptions, several things are used throughout, that I need to explain, to make it all clearer. Here they are:
Rn - is one of the eight possible registers in the current register bank. Remember, there are four possible banks of registers, one of which is the currently selected bank. Each bank has 8 registers (r0-r7) that can be used.
direct - is one of the 256 possible direct addresses in the internal RAM of the DS5000. Remember that the DS5000 has two distinct sections of memory. One is the on-board internal RAM, which has 256 addresses, the first 128 being the scratchpad RAM (00-7fh), and the last 128 being the Special Function Registers (80-ffh). The other is the external RAM, of which, in our case, has 32,768 addresses (0 - 3fffh). There are a few instructions that can have two direct addresses. They are referred to as direct1 and direct2.
rel - is a relative offset from the next instruction's address. This can be +/- 127 from that address. Remember that the PC is incremented when the current instruction is loaded, so the offset is relative to the address of the next instruction. This doesn't matter to you, the programmer, because the assembler calculates this address based on the address of the label you placed in the instruction, referring to the relative address in question. I tell you this only for your complete understanding of what happens, and I hope it doesn't confuse you. Relative addressing is confusing if you are having to calculate the offset yourself, like I did in the past, before I started using an assembler. The assembler will, however, give an error if you try to reference an address further than +/- 127 from the next instruction's address.
Ri - is one of the two registers, in the current bank, that can be used to index (point to) an internal RAM location. This can be either r0 or r1. Only these two can be used, and is a hardwired feature of the DS5000.
#data - is immediate 8 bit (byte) data. This means that the instruction contains this data. In the case of a mov a,#data , a two byte instruction, the first byte is the mov a, command and the second byte is the data. In this case, that data would be placed in the accumulator.
#data16 - is immediate 16 bit (2 byte) data. This is used with instructions to load a 16 bit register, like dptr, with an address. The dptr is the only 16 bit register to use this type of data.
@ - means "at the address pointed to by". An example would be add a,@r0. This would add the contents of the accumulator, to the data at the address in internal RAM, pointed to by r0, and store the result back into the accumulator.
bit - is a single bit, within a bit addressable byte, in the internal RAM. Remember that the internal scratchpad RAM is divided up functionally into different sections. The first 32 bytes hold the 4 register banks, each with 8 registers. The next 16 bytes are the bit addressable locations, each location having 8 bits, for a total of 128 bits. These represent bits 00-7fh. These can be assigned labels with the .equ assembler directive, so that you don't have to remember their values. The value for a particular bit is determined by which bit position, in which byte, is being referenced. Bit 0, of the first bit addressable byte, is bit 00h. Bit 7, of the last bit addressable byte, is bit 7fh. There are also several bit addressable locations in the Special Function Registers. These occupy bit addresses 80-ffh, making a total possible of 256 bit addresses.
/bit (actually bit with a bar over it) - is the complement of a bit. I can't type something with a bar over it here, but it does exist. It simply means that if the actual state of a particular bit is 0, this would return a 1 instead. I've never used this feature, but it's there, none the less.
addr 11 - is an 11 bit, absolute, address. This is a somewhat diffult concept, but here goes. The actual addressing within the DS5000, with the exception of internal RAM, is 16 bits. This allows for 65,536 unique addresses. Using 11 bit addressing limits the number to 2048 (2K). To use this type of addressing, the assumption is made that the effected address resides within the 2K block you are currently in. There are thirty-two 2K blocks of memory inside the DS5000 address range, looking at it this way. The first is 0000-07ffh, the second is 0800-0fffh, the third is 1000-17ffh, and so forth. I never use this type of addressing, due to the problem of knowing which 2k block a particular instruction, or label, is in. Since, using an assembler, you don't know which actual address any particular instruction or label is at, it makes it risky to use this kind of addressing. The instruction takes the same amout of time to execute as a long address (16 bit) and the only savings is 1 byte of program memory. I guess if you were pressed for space, you could use this type of addressing, but I have never been pressed into that corner, so far.
addr 16 - is a 16 bit, long, address. This can be any address within the address space of the DS5000. This addressing takes a 3 byte instruction, as opposed to 2 bytes for absolute addressing.
( ) - means "the contents of". For instance (A) means the "contents of the accumulator".
(( )) - means "the contents of the location pointed to by the contents of". For instance ((Ri)) means the contents of the location pointed to by the contents of Ri.
Lastly, when using an assembler, a label can be assigned to any direct location, bit location, long address, absolute address, or relative address in the DS5000, so that you refer to it by the name instead of the actual value. This makes things much easier to keep up with. The following descriptions don't use this, but remember it's there.
Also, the assembler has some hardcoded names that can be used to refer to different bits and direct addresses in the Special Function Registers. For instance bit 0 of the accumulator (direct bit address e0h) is referred to as acc.0 .
There are four flags in the DS5000 that indicate various conditions as the result of an instruction. These are the C (carry), AC (auxillary carry), OV (overflow), and P (parity) flags.
C is set by either an overflow or underflow of the accumulator. If you add a number to the accumulator that results in a number greater than 255 (ffh), a carry will result. If you subtract a larger number from a smaller number, a borrow will result. If neither of these happen, the carry flag will be cleared. The carry flag can also be set, cleared, or complemented by boolean instructions.
AC is set when the previous operation resulted in a carry (addition) or a borrow (subtraction) from the high order nibble. This is used for BCD arithmetic.
OV is set when when a carry was generated into the high order bit, but not a carry out of the high order bit, or if there was a carry out of the high order bit, but not a carry into the high order bit. It is normally used in 2's complement arithmetic. OV is also set if a divide by zero was executed.
P is set if the modulo-2 sum of the eight bits of the accumulator is 1 (odd parity). It's cleared if even parity. This is an extravagant way of saying that if you add up the number of 1's in the accumulator, and they come out odd, P is set. If there is an even number, P is cleared.
There is one more flag (F0) that can be set by software for whatever purpose. It's really just another bit to twiddle, but it gets saved when the PSW is pushed.
All these flags reside in the PSW (Program Status Word) register. Here is how they are laid out.
PSW.0 (PSW bit 0) is P
PSW.1 is unused
PSW.2 is OV
PSW.5 is F0
PSW.6 is AC
PSW.7 is C
PSW.3 and 4 indicate what register bank is currently selected. PSW.3 is the low order bit and PSW.4 is the high order bit.
00 is bank 0
01 is bank 1
10 is bank 2
11 is bank 3
This is how the register bank is selected. You load the PSW with the value that corresponds to the register bank you want to select. a 00h selects bank 0, 08h selects bank 1, 10h selects bank 2, and 18h selects bank 3. At power up or after a reset, bank 0 is always selected, until you select another. The main reason to use bank switching is to allow for faster ISR's or subroutines and also lessen the need for a larger stack. By using a register bank to hold most of the variables used by a particular routine, that runs a lot, extra pushes and pops can be avoided that would have saved and restored these variables in the stack, also saving precious machine cycles.
I typically use bank 0 for the operating loop, and many of the subroutines. I use another bank for the RS-232 ISR, another for the 120 Hz ISR, and another for whatever.
The stack is a portion of internal RAM (typically towards the end of RAM) that is used for temporary storage of addresses and data. When a call or interrupt happens, 2 bytes are pushed for the return address. Then as many bytes as needed are also pushed, to save their contents, for the return. This usually includes the accumulator, since it's used by so many instructions, and the PSW, since it holds the state of which bank is being currently used and may be holding some flag contents that will be needed upon return from the routine. This means that, at a minimum, 4 bytes will be pushed for every interrupt or subroutine routine. If the stack overflows (runs out of internal RAM), your system is basically toast, and mumbles off to mama every time. So the stack is made as large as possible.
When the system boots up, the SP register is initialized to an address. This is called the "top of the stack". From that point on, the stack "grows downward" for each push (towards the end of RAM). As addresses and data are "popped off" the stack, those bytes are freed up, to be used again. The problem is that you may be inside a subroutine, when an interrupt occurs, and that ISR may be interrupted by a higher priority interrupt. Each of these events caused the stack to grow deeper. As each finishes, the addresses and data are popped off the stack, and eventually it returns to the top of the stack again. The trick is make enough room for as deep as the stack might ever get, without running out of memory. This is usually not a problem, but it must be considered, and enough room allowed for the stack to grow.
The instructions of the DS5000 are divided into five types. They are Arithmetic Operation, Logical Operation, Data Transfer, Boolean Variable Manipulation, and Program Branching. Capitalization is used in these descriptions, though no caps are used in my actual code. Also I've included a logical explaination to the right of each instruction. This gives a short visual summary of what the instruction does. Some are too complicated to describe this way, and I don't.
There are a total of 111 seperate instructions, but many variations.
I note any flags that are affected by the instruction. If no flags are
mentioned, then none are affected.
Arithmetic Operation
ADD A, Rn (A)=(A) + (Rn) Flags C, AC, OV
Adds the contents of a register to the contents of the accumulator and
stores the result back into the accumulator.
ADD A, direct (A)=(A) + (direct) Flags C, AC, OV
Adds the contents of a direct address to the contents of the accumulator
and stores the result back into the accumulator.
ADD A, @Ri (A)=(A) + ((Ri)) Flags C, AC, OV
Adds the contents of the location pointed to by Ri to the contents of
the accumulator and stores the result back into the accumulator.
ADD A, #data (A)=(A) + #data Flags C, AC, OV
Adds the immediate data in the instruction to the contents of the accumulator
and stores the result back into the accumulator.
ADDC A, Rn (A)=(A) + (C) + (Rn) Flags C, AC, OV
Adds the contents of the register plus the contents of the carry flag,
to the contents of the accumulator, and stores the result back into the
accumulator.
ADDC A, direct (A)=(A) + (C) + (direct) Flags C, AC, OV
Adds the contents of the carry flag, plus the contents of the direct
location, to the contents of the accumulator and stores the result back
into the accumulator.
ADDC A, @Ri (A)=(A) + (C) + ((Ri)) Flags C, AC, OV
Adds the contents of the location pointed to by Ri, plus the contents
of the carry flag, to the contents of the accumulator, and stores the result
back into the accumulator.
ADDC A, #data (A)=(A) + (C) + #data Flags C, AC, OV
Adds the contents of the carry flag, plus the immediate data, to the
contents of the accumulator and stores the result back into the accumulator.
SUBB A, Rn (A)= (A) - (C) - (Rn) Flags C, AC, OV
The contents of the carry and the contents of the register are subtracted
from the accumulator and the result is stored back into the accumulator.
SUBB A, direct (A)=(A) - (C) - (direct) Flags C, AC, OV
The contents of the carry and the contents of the direct location are
subtracted from the accumulator and the result is stored back into the
accumulator.
SUBB A, @Ri (A)=(A) - (C) - ((Ri)) Flags C, AC, OV
The contents of the carry and the contents of the location pointed to
by Ri, are subtracted from the accumlator and the result is stored back
into the accumulator.
SUBB A, #data (A)=(A) - (C) - #data Flags C, AC, OV
The contents of the carry and the immediate data are subtracted from
the accumulator and the result is stored back into the accumulator.
INC A (A)=(A) + 1
Increments the contents of the accumulator by 1.
INC Rn (Rn)=(Rn) + 1
Increments the contents of the register by 1.
INC direct (direct)=(direct) + 1
Increments the contents of the direct location by 1.
INC @Ri ((Ri))=((Ri)) + 1
Increments the contents, of the location pointed to by Ri, by 1.
INC DPTR (DPTR)=(DPTR) + 1
Increments the contents of the dptr register by 1.
DEC A (A)=(A) -1
Decrements the contents of the accumulator by 1.
DEC Rn (Rn)=(Rn) - 1
Decrements the contents of the register by 1.
DEC direct (direct)=(direct) - 1
Decrements the contents of the direct location by 1.
DEC @Ri ((Ri))=((Ri)) - 1
Decrements the contents of the location pointed to by Ri, by 1.
MUL AB (B15-8), (A7-0)=(A) * (B) Flags C, OV
Multiplies the contents of the accumulator by the contents of register
b and stores the low order byte back into the accumulator and the high
order byte back into register b. This multiplies two 8 bit numbers with
a 16 bit result. If the result is greater than 255, in other words the
b register has something other than zero in it, the OV flag will be set.
The C flag will always be cleared.
DIV AB Flags C, OV
Divides the accumulator by the b register and places the integer part
of the quotent in the accumulator. The integer remainder is placed in the
b register. The C flag is always cleared. In the event that the b register
was originally zero (divide by zero), the OV flag will be set, indicating
a divide by zero error.
DA A Flags C, AC
This instruction is VERY complicated. It is used to adjust the accumulator
after an add, that involved BCD (binary coded decimal) numbers, so that
the accumulator has the proper BCD result in it. I'm not going to discuss
this instruction any further, due to it's complexity and the use of BCD.
You can find a two page description of how it works in Intel's "MCS 51
Microcontroller Family User's Manual". It is a handy instruction if you
are going to be using BCD numbers, otherwise you will never use it. DA
A stands for "Decimal adjust Accumulator for Addition". It could be handy
for interfacing to a BCD display. It can be done other ways, which I do.
Logical Operation
ANL A, Rn (A)=(A) AND (Rn)
The contents of the accumulator are ANDED with the contents of the register
and the result is stored back into the accumulator.
ANL A, direct (A)=(A) AND (direct)
The contents of the accumulator are ANDED with the contents of the direct
location and the result is stored back into the accumulator.
ANL A, @Ri (A)=(A) AND ((Ri))
The contents of the accumulator are ANDED with the contents of the location
pointed to by Ri, and the result is stored back into the accumulator.
ANL A, #data (A)=(A) AND #data
The contents of the accumulator are ANDED with the immediate data and
the result is stored back into the accumulator.
ANL direct, A (direct)=(direct) AND (A)
The contents of the accumulator are ANDED with the direct location and
the result is stored back into the direct location.
ANL direct, #data (direct)=(direct) AND #data
The contents of the direct location are ANDED with the immediate data
and the result is stored back into the direct location.
ORL A, Rn (A)=(A) OR (Rn)
The contents of the accumulator are OR'ed with the contents of the register
and the result is stored back into the accumulator.
ORL A, direct (A)=(A) OR (direct)
The contents of the accumulator are OR'ed with the contents of the direct
location and the result is stored back into the accumulator.
ORL A, @Ri (A)=(A) OR ((Ri))
The contents of the accumulator are OR'ed with the contents of the location
pointed to by Ri, and the result is stored back into the accumulator.
ORL A, #data (A)=(A) OR #data
The contents of the accumulator are OR'ed with the immediate data and
the result is stored back into the accumulator.
ORL direct, A (direct)=(direct) OR (A)
The contents of the accumulator are OR'ed with the direct location and
the result is stored back into the direct location.
ORL direct, #data (direct)=(direct) OR #data
The contents of the direct location are OR'ed with the immediate data
and the result is stored back into the direct location.
XRL A, Rn (A)=(A) XOR (Rn)
The contents of the accumulator are XOR'ed with the contents of the
register and the result is stored back into the accumulator.
XRL A, direct (A)=(A) XOR (direct)
The contents of the accumulator are XOR'ed with the contents of the
direct location and the result is stored back into the accumulator.
XRL A, @Ri (A)=(A) XOR ((Ri))
The contents of the accumulator are XOR'ed with the contents of the
location pointed to by Ri, and the result is stored back into the accumulator.
XRL A, #data (A)=(A) XOR #data
The contents of the accumulator are XOR'ed with the immediate data and
the result is stored back into the accumulator.
XRL direct, A (direct)=(direct) XOR (A)
The contents of the accumulator are XOR'ed with the direct location
and the result is stored back into the direct location.
XRL direct, #data (direct)=(direct) XOR #data
The contents of the direct location are XOR'ed with the immediate data
and the result is stored back into the direct location.
CLR A (A)= 0
The accumulator is cleared, or zero'ed out.
CPL A (A)=(/A)
The contents of the accumulator is complemented. All one's become zero's
and all zero's become one's.
RL A
The contents of the accumulator are rotated left by one bit. Bit 7 goes
into bit 0.
RR A
The contents of the accumulator are rotated right by one bit. Bit 0
goes into bit 7.
RLC A Flags C
The contents of the accumulator are rotated left, through the carry
flag. Bit 7 goes into the carry, and the carry goes into bit 0.
RRC A Flags C
The contents of the accumulator are rotated right, through the carry
flag. The carry goes into bit 7 and bit 0 goes into the carry.
SWAP A (A3-0)=(A7-4)
The upper nibble of the accumulator is swapped with the lower nibble.
This can also be looked at as a 4 bit rotate. No flags are affected.
Data Transfer
MOV A, Rn (A)= (Rn)
The accumulator is loaded with the contents of the register.
MOV A, direct (A)=(direct)
The accumulator is loaded with the contents of the direct location.
MOV A, @Ri (A)=((Ri))
The accumulator is loaded with the contents of the location pointed
to by Ri.
MOV A, #data (A)= #data
The accumulator is loaded with the immediate data.
MOV Rn, A (Rn)=(A)
The register is loaded with the contents of the accumulator.
MOV Rn, direct (Rn)= (direct)
The register is loaded with the contents of the direct location.
MOV Rn, #data (Rn)= #data
The register is loaded with the immediate data.
MOV direct, A (direct)= (A)
The direct location is loaded with the contents of the accumulator.
MOV direct, Rn (direct)= (Rn)
The direct location is loaded with the contents of the register.
MOV direct1, direct2 (direct1)= (direct2)
The direct1 location is loaded with the contents of the direct2 location.
MOV direct, @Ri (direct)= ((Ri))
The direct location is loaded with the contents of the location pointed
to by Ri.
MOV direct, #data (direct)= #data
The direct location is loaded with the immediate data.
MOV @Ri, A ((Ri))= (A)
The location pointed to by Ri is loaded with the contents of the accumulator.
MOV @Ri, direct ((Ri))= (direct)
The location pointed to by Ri is loaded with the contents of the direct
location.
MOV @Ri, #data ((Ri))= #data
The location pointed to by Ri is loaded with the immediate data.
MOV DPTR, #data16 (DPTR)= #data16
The dptr register is loaded with the 16 bit immediate data.
MOVC A, @A + DPTR (A)= ((A) + (DPTR))
The accumulator is loaded with the location in program memory
pointed to by the original contents of the accumulator plus the contents
of the dptr register. This is a handy instruction for implementing a lookup
table in program memory. Say you created a table of ascii values that represent
the numbers 0 thru 9. The ascii value for 0 would be the first entry in
the table and the ascii value for 9 would be the last entry in the table.
By setting dptr to the start of the table, if the accumulator had a 0 in
it, the instruction would return with the first entry in the table, which
is the ascii value for 0, in the accumulator. Walla! I use this one regularly.
MOVC A, @A + PC (A)=((A) + (PC))
This instruction acts exactly as the previous one except the PC (program
conter) is the register used. I haven't came up with a good use for this
instruction yet. But it is there to use.
MOVX A, @Ri (A)= ((Ri))
Loads the accumulator with the contents of the location, in external
data memory, pointed to by Ri. This means that the first 256 locations
in external data memory could be used by this instruction, possibly for
frequently used variables or buffers. There are other options for this
instruction if you are using p2 and p0 of the DS5000 for an external memory
address/data bus. You could load p2 with the high order address bits and
then use this instruction to access the 256 locations, or the page of memory
pointed to by p2. This would allow you to create 256 pages of 256 locations,
using a 64K RAM chip. I haven't used this instruction for anything yet,
but it seems like it could be useful.
MOVX @Ri, A ((Ri))= (A)
This is just like the previous instruction, except that the external
location is loaded with the contents of the accumulator.
MOVX A, @DPTR (A)= ((DPTR))
Loads the accumulator with the contents of the location in external
data memory pointed to by dptr. This one works for all the locations in
external data memory, and I use it a lot.
MOVX @DPTR, A ((DPTR))= (A)
Loads the location in external data memory pointed to by dptr, with
the contents of the accumulator. Again, I use this one a lot for larger
buffers that I don't have space for in internal data RAM.
PUSH direct (SP)=(SP) + 1 ; ((SP))=(direct)
Increments the stack pointer (SP) and stores the contents of the direct
location into the location pointed to by sp. This is one of the most used
instructions. It, along with the POP instruction, implement the stack within
the DS5000. The stack is used to temporarily store addresses and data,
to allow the use of subroutines, with less memory overhead than would be
possible without it. It also allows for relatively fast temporary storage
and retrieval of information. The stack resides in internal RAM, usually
towards the end of the internal RAM.
POP direct (direct)=((SP)) ; (SP)=(SP) - 1
Loads the direct location with the contents of the location pointed
to by the sp, then decrememts the sp register.
XCH A, Rn (A)=(Rn)
The contents of the accumulator and the contents of the register are
swapped. This is handy for intermediate storage of the accumulator contents,
or for retrieving the contents of a register, while also saving the contents
of the accumulator.
XCH A, direct (A)=(direct)
The contents of the accumulator and the contents of a direct location
are swapped. Also handy.
XCH A, @Ri (A)=((Ri))
The contents of the accumulator and the contents of the location pointed
to by Ri are swapped.
XCHD A, @Ri (A3-0)=((Ri3-0)
The low order nibble of the accumulator and the low order nibble of
the location pointed to by Ri are swapped. The high order nibbles are not
affected.
Boolean Variable Manipulation
CLR C (C)=0
The carry flag is cleared.
CLR bit (bit)=0
The direct bit location is cleared.
SETB C (C)=1
The carry flag is set.
SETB bit (bit)=1
The direct bit location is set.
CPL C (C)=(/C)
The carry flag is complemented. If it was a 0, it's now a 1, and vise
versa.
CPL bit (bit)=(/bit)
The direct bit location is complemented.
ANL C, bit (C)=(C) AND (bit)
The carry flag is AND'ed with the direct bit location and the result
is stored back into the carry.
ANL C, /bit (C)=(C) AND (/bit)
The carry flag is AND'ed with the complement of the direct bit location
and the result is stored back into the carry.
ORL C, bit (C)=(C) OR (bit)
The carry is OR'ed with the direct bit location and the result is stored
back into the carry.
ORL C, /bit (C)=(C) OR (/bit)
The carry is OR'ed with the complement of the direct bit location and
the result is stored back into the carry.
MOV C, bit (C)= (bit)
The carry flag is loaded with the contents of the direct bit location.
MOV bit, C (bit)= (C)
The direct bit location is loaded with the contents of the carry.
Program Branching
ACALL addr 11
The PC is incremented by 2 and then it is pushed onto the stack, low
byte first (2 pushes, one for each byte) and the immediate 11 bits of the
instruction are cancantenated with (added in, but not ADDED to) the high
order 5 bits of the incremented PC, creating the 16 bit address. The sp
is incremented by 2 in the process. The incremented PC value and the address
to which the call is being made, must reside in the same 2K block of memory.
I never use this one.
LCALL addr 16
The PC is incremented by 2 and pushed (as the previous instruction)
on to the stack, then the PC is loaded with the immediate 16 bit address
in the instruction. This is the one I always use for a call.
RET
The PC is popped off of the stack, loading it with the address popped
off (2 pops, one for each byte). The sp is decremented by two in the process.
RETI
This does the same thing as the RET, but in addition, it re-enables the interrupts to accept another interrupt of the same or lower level (priority). This is used to return from an interrupt service routine. Should an interrupt of a higher level occur, it is serviced, even though it might be in the middle of servicing a lower level interrupt.
AJMP addr 11
This does the same thing as the ACALL, except no address is pushed onto
the stack.
LJMP addr 16 (PC)= addr 16
The PC is loaded with the immediate address in the instruction, which
causes the next instruction to come from the new address. Same as the LCALL
except no address is pushed onto the stack.
SJMP rel
The PC is incremented by 2 and then the relative offset is added to
the PC to get the new address. The next instruction will come from there.
This is good for short jumps (+/- 127 locations from the incremented PC
address).
JMP @A + DPTR (PC)=(A) + (DPTR)
The PC is loaded with the address resulting from adding the contents
of the accumulator to the contents of the dptr register. Neither the accumulator
nor dptr contents are changed. This could be used for making a jump table.
The accumulator contents would determine what jump was executed in the
table. If the dptr is set to the start of the table and the accumulator
has an even number in it, then the AJMP at that location would be executed.
If the accumulator has multiples of 3 in it, then you could use LJMP's
in the table. A seemingly handy, but tricky, instruction that I've never
used, so far.
JZ rel
The PC is incremented by two (to get to the address of the next instruction,
as normal). If the accumulator contents are zero, then the relative offset
in the instruction is added to the incremented PC and the next instruction
comes from there. If the accumulator isn't zero, the next instruction after
the JZ is executed.
JNZ rel
This acts just like the JZ, except the relative offset is added if the
accumulator isn't zero, or the next instruction after the JNZ is executed,
if it is zero.
JC rel
The PC incremented by 2, and if the carry is set, the relative
offset is added to the PC and the next instruction comes from there. Otherwise
the next instruction after the JC is executed.
JNC rel
Like the JC except the offset is added if the carry is 0, otherwise
the next instruction is executed.
JB bit, rel
As in previous examples the PC is incremented by two. If the direct
bit location is 1, the offset is added, otherwise the next instruction
is executed.
JNB bit, rel
Same as JB, except offset is added if the direct bit location is 0,
otherwise the next instruction is executed.
JBC bit, rel
Same as JB, except that if the bit is 1, it is cleared and the offset
added. Otherwise the next instruction is executed.
CJNE A,direct, rel Flags C
Compares the accumulator with the direct location and, if they're not
equal, adds the offset to the PC. Otherwise the next instruction is executed.
If A is less than direct, then C is set. Otherwise it's cleared.
CJNE A, #data, rel Flags C
Compares the accumulator with the immediate data and, if they're not
equal, adds the offset. Otherwise the next instruction is executed. If
A is less than #data, then C is set. Otherwise it's cleared.
CJNE Rn, #data, rel Flags C
Compares the register with the immediate data and, if they're not equal,
adds the offset. Otherwise the next instruction is executed. If Rn is less
than #data, then C is set. Otherwise it's cleared.
CJNE @Ri, #data, rel Flags C
Compares the location pointed to by Ri, with the immediate data and,
if they're not equal, adds the offset. Otherwise, the next instruction
is executed. If @Ri is less than #data, then C is set. Otherwise it's cleared.
DJNZ Rn, rel
Decrements the register and, if not zero, adds the offset to the PC.
Otherwise, the next instruction is executed. This is used for looping.
DJNZ direct, rel
Decrements the contents of the direct location and, if not zero, adds
the offset. Otherwise, the next instruction is executed. Used for looping.
NOP
This instruction does ablolutely nothing, except waste instruction cycles. That's it's sole purpose. Sometimes it's necessary to wait a short time after executing an instruction, before executing another. I use it for I/O accesses mostly. I output the select lines and then wait a small time to let the chips setup, then read or write. It uses up 1 instruction cycle.
The information presented here came from three sources, my experience, the Dallas Semiconductor Soft Microcontroller Data Book, and the Intel MCS 51 Microcontroller Family User's Manual.