The 1802 used a method known as SCRT, the standard call and return technique. The chip was actually endowed with a full complement of sixteen 16-bit general-purpose registers(a), but no dedicated stack pointer or program counter. The SCRT was one method for doing calls using this limitation.
You could actually use any register as the program counter with the
SEP Rn instruction (where
n was a value
0..F indicating which of the registers was to be used). This basically said to start using that register for the program counter, leaving whatever register was previously being used with its last contents (one byte beyond the
SEP instruction that switched the program counter over).
So, by using some of those sixteen registers in a dedicated manner, you could easily "emulate" the more conventional instructions found on other processors.
Note that the details below are from rather distant memories, they may not be exact (in terms of what registers were used for what, for example), but they should give you the basic idea.
There was a "usual" program counter (
R3), and the
R5 registers were set respectively to the addresses of the SCRT
R2 was used as a stack pointer.
In order to call another function, you therefore encoded a
SEP R4 followed by the address that you wanted to call. This immediately started using
R4 as the program counter, leaving
R3 pointing at the memory containing the address that you were calling.
call code running at
- store the return address (
R3, to be adjusted on return to skip over the address) onto the stack (controlled by
- load the address you wanted to call (pointed at by
R3; then finally
SEP R3 to continue execution at that new address.
It would, of course, have to ensure
R4 was set back to the start of the SCRT
call function and, from memory, this was done by placing the
SEP R3 instruction immediately before that function and then jumping to that as the final step. This would auto-magically leave
R4 set to the correct value for next time.
SEP R5 (return) in your code would start running the
return function which would:
- pull the return address off the stack at
R3, and adjust it to skip over the address.
SEP R3 (using the same jump trickery mentioned above to ensure
R5 once again pointed to the
return function) to return to the original code.
So, in terms of implementation, the set-up was something like:
; code for doing reg/mem manipulations for call
; code for doing reg/mem manipulations for ret
macro fcall %address:
Then, in terms of what you would see in your code, it would be as simple as:
; do other stuff then return
; do sub-function stuff
(a) This rather massive (at the time) register bank was actually one of the big selling features of the chip, despite the fact you immediately lost a large chunk of them (SCRT, DMA and interrupts, from memory). Truly the triumph of marketing over reality :-)