.cr scmp To load this cross overlay
Here's another walk along memory lane for you.
The SC/MP or INS8060 was very popular in the early days of DIY computer pioneers.
For instance in 1977 Sinclair came with its MK14 computer kit, which was based on the SC/MP processor.
Also Elektor (then called Elektuur) had a couple of articles dedicated to a SC/MP based computer.
SC/MP is an acronym of Simple Cost-effective Micro Processor.
Compared to the 6502, 6800 and 8080, all from the same era, I think the SC/MP was everything but simple.
Maybe NS was referring to the simplicity in the design of the processor, I.M.H.O. it sure wasn't simple to use.
For instance it lacked a dedicated stack, making subroutine calls very cumbersome to use. But the weirdest property of all is the way instructions are fetched from program memory. The processor increments the program counter before fetching the instruction. This means that if your program counter points at address $0000, the first instruction is fetched from address $0001 instead. You simply can't use program address $0000. This causes a lot of confusion while programming the SC/MP, making it far more difficult to understand how micro processors work.
Add the lack of a decent assembler for the processor back then and perhaps you can imagine how hard it must have been to program the thing. Fortunately Nibl Basic was available, which made programming the SC/MP a whole lot easier, but not as much fun as programming in assembly can be.
I have tried to create a SC/MP cross overlay for SB-Assembler 2 version before, but the lack of good documentation kept me from finishing it. Therefore the SC/MP cross overlay is only available on for version 3.
Sinclair's MK14 running an a SC/MP
The programming model in the picture below shows the most important registers of the SC/MP processor. I only include a little summary about the features of the SC/MP's programming model here. It is not my intention to make the original documentation obsolete, so please refer to the original documentation for further details.
The Accumulator is the most important register for 8 bit arithmetic operations. Its standard name is AC, which is a reserved word.
The Extension Register E is usually used to hold the second operand for instructions which require 2 operands.
In which case the Accumulator holds the other operand.
If the displacement value for indexed or auto-indexed memory instructions equals -128 the contents of E is used as displacement value.
The Extension Register is also used as serial input or output shift register.
The SR contains 3 system flags and 5 I/O flags:
|Bit 7||CY||The Carry/Link flag|
|Bit 6||OV||Overflow Flag|
|Bit 5||SB||Single bit input|
|Bit 4||SA||Single bit input / interrupt input|
|Bit 3||IE||Interrupt enabled if 1|
|Bit 2||F3||Single bit output|
|Bit 1||F2||Single bit output|
|Bit 0||F1||Single bit output|
Please note that the SC/MP doesn't have a Zero or Negative flag. These conditions are directly tested on the contents of the Accumulator.
This 16 bit register is the program counter.
It can either be named PC or P0, which are both reserved words.
Although this register is 16 bits long, it will access memory in pages of 4kB in size. When the program counter reaches the end of a page, it will wrap around to the beginning of that same page. You'll have to jump to another page to get out of the current page.
PC relative instructions are confined in the same memory page. Displacements which would cross a page boundary simply wrap back. The assembler will warn you if a displacement crosses a page boundary.
Two byte instructions which cross the page boundary will cause an error message by the assembler.
The SC/MP has a rather peculiar way in which it fetches instructions.
It increments the program counter before fetching the instruction.
Therefore you can't use the first address of each 4kB memory page for instructions.
It also affects the way you have to jump to other locations in memory. You'll have to manually subtract 1 from the address to which you want to jump.
There are three 16 bit pointer registers which can be used as memory or peripheral addressing, page pointers, stack pointers or index registers.
Typically these registers are dedicated to the following tasks, although you're free to use any register as you please.
P1 - ROM pointer
P2 - Stack pointer
P3 - Subroutine pointer
You can load each half of all three pointers independently. Or you can swap the contents from one of the pointers with the program counter. Swapping one of the pointers with the PC register effectively is a one level subroutine call. Swapping the registers back again is like a return from subroutine.
If you need deeper nesting of subroutines you'll have to implement a software stack and push the return address to that stack manually.
SB-Assembler Version 3 can show you the cycle times of each instruction when the TON list flag is switched on.
The numbers presented are the number of clock pulses needed to execute the instruction.
For conditional instructions 2 times are given. The lowest values are when the condition is false, while the highest times are for when the condition is true.
The SB-Assembler SC/MP Cross Overlay has a few reserved words.
Reserved words are all register names.
You better avoid these reserved words when you assign your own labels.
E.g. don't call your labels AC, E, PC.
If you do use the reserved words as label names you may expect unpredictable behaviour of the assembler sooner or later. Please note that the assembler will not warn you if you try to assign a label with a reserved name!
Reserved names can not be used in expressions, like label names can. An Undefined label error will be reported if you do try to use a reserved word in an expression because it is treated as a normal label in this case.
Here's the list of all reserved words:
AC, E, PC, P0, P1, P2, P3, SR
As already mentioned an offset of -128 will use the contents of the E register as offset. With the SB-Assembler you can also use E instead of an offset of -128 if you like.
If you don't specify an offset an offset of 0 is assumed.
Absolute address is assumed for PC relative instructions where the (PC) pointer is omitted.
The SC/MP is so simple that it doesn't even know subroutines.
You can simulate a one level subroutine call by swapping a pointer register with the program counter.
Swapping the pointer register again will simulate the return from subroutine.
Per convention pointer register P3 is used for this swapping purpose.
However the SB-Assembler allows you to use any of the 3 pointers, giving you a total of 3 separate one level subroutines.
I have added 2 instructions which will help simulate a one level subroutine system.
This instruction can be used to call to this one level subroutine.
It simply transfers control to the destination, using one of the 3 pointer registers P1 to P3.
The SB-Assembler translates this instruction into the following set of instructions:
LDI /destination-1 XPAH pntr LDI #destination-1 XPAL pntr XPPC pntr
The RET instruction is simply an alias for the XPPC instruction. It is included to make its function more obvious. So the RET instruction swaps the pntr with the program counter, giving control back to the code which called the subroutine.
Due to the limited addressing capability of the SC/MP processor itself and the rather primitive original SC/MP assembler, programmers were used to jump through all kinds of hoops in order to get their work done.
The SB-Assembler is far more advanced than the original SC/MP assembler.
Therefore I had to make some compromises.
I became aware of the quirks of the SC/MP assembler while adapting the Elbug monitor (Elektor SC/MP monitor) for the SB-Assembler. I had a list file and a hex file of this monitor, and I thought it would be a good test case for the SB-Assembler. If the SB-Assembler can produce the same code, I'm all set.
First of all the original SC/MP assembler had only limited calculation capabilities.
As you can see from the instruction set the E register is used as index when the offset is -128.
The Elbug source had a few lines which used a constant offset of $80 in stead of -128.
On a byte level this may be correct.
But the SB-Assembler rightfully disagrees here.
An offset of $80 is too high, it's 128, whereas the maximum positive offset is only 127. I could make an exception for $80, but that would mean that the assembler would not be able to raise an error if I accidentally entered an out of range offset any more.
So I had to make small adaptions to the source code of the Elbug monitor, changing the $80 offset to -128. B.T.W., I could also have used E as offset, to indicate the use of the E register.
Addressing RAM can be done with one of the three index registers P1 to P3.
The programmer of the Elbug monitor decided to use the PC registers instead in some situations.
I must say it's clever programming, but it does obscure things a lot, especially for beginners.
And I thought the S in SC/MP stood for Simple.
To cut a long story short the Elbug source deliberately uses the limitations of the original SC/MP assembler to calculate the offset from the current PC value to RAM locations. How is this possible? The program starts at address $0001, whereas the monitor RAM addresses are located from $FE0 to $FFF, which is at the end of the current page. By using a negative offset to the PC value, the effective address wraps back in the current page, ending up at addresses somewhere near the monitor RAM locations.
Please note that this trick is only possible right at the beginning of the program, where the RAM locations are placed right at the end of the current page. Only then you may find offsets between -1 up until -127. Quite tricky, especially if you consider that the assembler only calculates a byte value, whereas the SB-Assembler calculates a full 32 bit value. Needless to say that the SB-Assembler reported range errors. This was easily remedied by ignoring the high byte of the RAM locations, just like the original SC/MP assembler did.
The SC/MP Cross Overlay adds 1 extra error message and 2 extra warning messages to the standard list of possible errors/warnings.
Instruction crossed a page boundary
This is an error condition.
It is raised when the operand part of an instruction is written to a different memory page than its opcode part.
The processor is unable to get both, because the program counter wraps back to the beginning of the current page before it can get the operand.
Move your program away from the page end. Perhaps you should split your program into smaller pieces, which each fit into its own page.
Instruction starts at page boundary
This is a warning.
An instruction at the first byte of a memory page can not be executed.
But you may have done this deliberately.
Therefore this is only a warning.
You may wish to ignore it. But you may also wish to move your program one byte up in memory, to avoid this warning.
Offset is -128, E register conflict
This is also a warning.
You have provided an absolute address, which was translated into an offset of -128.
Offsets of -128 are normally replaced by the value of the E register.
So you may want to correct this, otherwise you may get unexpected results.
There are some differences between the SB-Assembler and other assemblers for the SC/MP processor. These differences require you to adapt existing source files before they can be assembled by the SB-Assembler. This is not too difficult though, and is the (small) price you have to pay for having a very universal cross assembler.