This document describes information about the CPC Plus hardware which is not documented in the official datasheet. These details have been found by running tests on a real CPC6128+ system.
There are two known versions of the CPC+ ASIC, but there may be more. It is possible to identify the version using the following procedure:
If you see a "green-yellow" colour, you have a CPC+ with ASIC revision 1.
If you see a "purple/magenta" colour, you have a CPC+ with ASIC revision 2.
Richard Wilson has reported, that his CPC+ shows a blue colour.
Thankyou to Offset/Futurs for details of the ASIC versions.
The circuit board (PCB) of the 464+ and 6128+ are identical, but, the 464+ curcuit board is missing the components for the disc interface, and the 6128+ is missing the components for the tape interface.
Hardware modifications exist which will add a cassette interface to the 6128+, and add a disc interface to the 464+. These modifications add the missing components to the circuit board.
Thankyou to Peter Sorensen for his information which has been used for this part of the document.
The cartridge is simple in design. It has a small PCB with 2 IC's and a capacitor on it.
The PCB is marked "AMSTRO1" and "(C) AMSTRAD PLC 1990".
IC1 is the Game EPROM containing the game program and data. (This data is not encrypted or protected in any way)
IC2 is the "ACID" protection chip. ACID stands for "Amstrad Cartridge Identification Device". This IC has 16 pins and has the markings AMSTRAD 40908 1L03P1003, 8030EA1).
There are also 6 option links labeled LK1 to LK6.
Looking into the cartridge slot, the pin assignments are, from left-to-right:
TOP
a1 | +5v |
a2 | clk4 |
a3 | a14 |
a4 | a15 |
a5 | a12 |
a6 | a7 |
a7 | a6 |
a8 | a5 |
a9 | a4 |
a10 | a10 |
a11 | nce |
a12 | d7 |
a13 | d6 |
a14 | d5 |
a15 | d4 |
a16 | d3 |
a17 | cclr |
a18 | 0v |
BOTTOM
b1 | +5v |
b2 | a18 |
b3 | a17 |
b4 | a14 |
b5 | a13 |
b6 | a8 |
b7 | a9 |
b8 | a11 |
b9 | a3 |
b10 | a2 |
b11 | a1 |
b12 | a0 |
b13 | d0 |
b14 | d1 |
b15 | d2 |
b16 | sin |
b17 | 0v |
b18 | 0v |
The following is not confirmed:
PLEASE NOTE THAT I CAN'T BE HELD RESPONSIBLE FOR ANY DAMAGE THAT MAY BE CAUSED TO YOUR CARTRIDGE OR COMPUTER FROM THE USE OF THIS CARTRIDGE CONNECTION INFORMATION!
This document provides additional "undocumented" information about the CPC+ ASIC custom chip.
The ASIC custom chip comprises:When the ASIC special features have not been enabled, the CPC+ acts almost identically to the old CPC series. There are however a few minor differences which cause software incompatibilities. But if these problems are considered software can be modified to run on all CPC/CPC+ computers without any problems.
The special features of the ASIC are documented in the official Amstrad document.
The CPC+ appears to be slightly different in respect to the timing. It appears all colour changes are 1/2 NOP later than they would be on a CPC.
When programming some CRTC changes some timings appear to be 2 us later!
The CPC+ has the same ports and port address decoding as the CPC with the following exceptions:
When a IN is performed from this port:
This weird bug can be seen on the game "Into the Eagles Nest". The code that exhibits the bug is:
;; before entering this code, B has been set to 0x07f 0x05c3: LD C,&8C ;; set video mode OUT (C),C LD C,&10 ;; select border OUT (C),C LD C,&54 ;; select black colour IN A,(C) ;; !!!!!!!!
This bug can be reproduced from basic using the following code:
OUT &BC00,6:? INP(&BD00)
The result of this code causes the vertical displayed to be 0x079 on a CPC6128+ or 0x078 on a CPC464+.
In interrupt mode 2, the CPU fetches a byte from the bus to form a lookup into a table of interrupt handler routines.
On the CPC this byte is normally 0x0ff, although this is not guaranteed.
On the CPC+ when the ASIC advanced features are *not* enabled, this byte is always 0x00.
Richard Wilson reports that on his CPC+, this byte is always 0x56.
On the CPC+ when the ASIC advanced features are enabled, this byte depends on the interrupt source (Raster Ints or DMA channel ints).
The official documents are not clear about the interrupt vector provided in mode 2, I found the following are correct:
Interrupt Source | Vector |
---|---|
DMA Channel 0 | 4 |
DMA Channel 1 | 2 |
DMA Channel 2 | 0 |
Raster Interrupts | 6 |
The interrupt vector is therefore ((IVR & 0x0f8) | Vector).
When the ASIC raster interrupt is active, there are no interrupts caused by the standard CPC raster int.
When a ASIC raster interrupt occurs, the top-bit of the Gate Array 6-bit interrupt counter is reset, so that if the CPC raster interrupt is re-enabled it will not occur closer than 32 lines.
It is possible to use the Z80 interrupt mode 0 with the CPC+ because the 8-bit interrupt-vector can be predicted and is reliable.
See the document about using interrupt mode 0 with the CPC+ for more information.
The Intel 8255 PPI is "emulated" in the ASIC. However, the emulation is not perfect, and this is the reason why some programs do not work correctly.
In the programs that are affected the keyboard does not respond, or there is no sound, or both.
There are two differences between the ASIC implementation of the 8255 PPI and the 8255 PPI inside the CPC:
The PPI in the ASIC is a cut-down version of the 8255PPI. It has all the operations used by the CPC including the bit set/reset feature. It may not have the functions to set the port operation (mode 0, 1 or 2) as these features are not used on the CPC (to my knowledge).
When this port is set to output, any byte written to this port can be read.
When this port is set to input mode, this port returns the input data only. Writing to this port does not change the value read.
When this port is set to input mode, the value present on the port to the PSG is 0x0ff. (I found this out by selecting a PSG register, setting port A to input and then performing a PSG write data). There is no change even when a write is performed to the port.
When this port is set to input or output, and data is read it will always return the same data. Writing to this port has no effect.
The data that can be read is the same as the data that can be read from the 8255 in the CPC.
When this port is set to input or output, any byte written to this port can be read back without any change. The byte is not affected by the input/output status of either half. It appears that you cannot define the input/output status of each half of this port as you can on a real 8255.
Writing to this port will set the I/O status or perform the bit set/reset feature.
Writing to this port and then reading the data back gives the following results:
Writing data in the range 0x00-0x07f returns 0x0ff, 0x080-0x08f returns 0, 0x090-0x09f returns 0x0ff, 0x0a0-0x0af returns 0, 0x0b0-0x0bf returns 0x0ff, 0x0c0-0x0cf returns 0, 0x0d0-0x0df returns 0x0ff, 0x0e0-0x0ef returns 0, 0x0f0-0x0ff returns 0x0ff.
Changing the input/output status of any port does not clear it.
Please see the document describing the basic operation of the CRTC.
Since this type exists only on the CPC+, many detection programs enable the ASIC ram, and detect if it is available. If it is, a CRTC type 3 is assumed.
When reading, the following register data is returned:
6845 Selected Register | 6845 Register data returned |
---|---|
0 | 16 |
1 | 17 |
2 | Status 1 (note 1) |
3 | Status 2 (note 1) |
4 | 12 |
5 | 13 |
6 | (note 2) |
7 | (note 2) |
8 | 16 |
9 | 17 |
10 | Status 1 (note 1) |
11 | Status 2 (note 1) |
12 | 12 |
13 | 13 |
14 | (note 2) |
15 | (note 2) |
16 | 16 |
17 | 17 |
18 | Status 1 (note 1) |
19 | Status 2 (note 1) |
20 | 12 |
21 | 13 |
22 | (note 2) |
23 | (note 2) |
24 | 16 |
25 | 17 |
26 | Status 1 (note 1) |
27 | Status 2 (note 1) |
28 | 12 |
29 | 13 |
30 | (note 2) |
31 | (note 2) |
Note:
When Status register 1 is read, the bits have the following meaning:
Bit 7 | (function of this bit is unknown) |
Bit 6 | (function of this bit is unknown) |
Bit 5 | 0: CRTC is on last line of VSYNC |
Bit 4 | 0: CRTC is on last char of HSYNC |
Bit 3 | 0: CRTC Horizontal Count == Horizontal Sync Position (Reg 2) |
Bit 2 | 0: CRTC Horizontal Count == Horizontal Displayed (Reg 1) |
Bit 1 | 0: CRTC Horizontal Count == (Horizontal Total/2) |
Bit 0 | 0: CRTC Horizontal Count != Horizontal Total |
When Status register 2 is read, the bits have the following meaning:
Bit 7 | 0: when RC!=0 |
Bit 6 | (function of this bit is unknown) |
Bit 5 | 0: when RC==R9 |
Bit 4 | (function of this bit is unknown) |
Bit 3 | (function of this bit is unknown) |
Bit 2 | (function of this bit is unknown) |
Bit 1 | (function of this bit is unknown) |
Bit 0 | (function of this bit is unknown) |
Before the ASIC ram can be accessed, the ASIC must be enabled. A special sequence must be programmed before the features are unlocked. (See the official ASIC documentation for details of this.)
When the ASIC Ram is paged in it occupies the address space from &4000-&7fff.
Not all of the memory in this 16k address space is used. In this document the areas that cannot be used will be called "invalid areas". The areas that can be used will be called "valid areas".
Over half of the memory in the 16k address range is invalid. No useful information can be stored in this area. (Writing to this memory has no effect. Data that is written cannot be read back, because this ram always returns the same data)
There are a number of limited ranges. The information below describes what happens when read and writes are performed in these valid areas. Please read the official documentation for furthur information on their exact functions.
In invalid areas, on my CPC+, the data is a mixture of 0x0b0 and 0x0b1, when read in a 16k block, but 0x07e or 0x07f, when reading the same location over and over again. (The mixture of 0x0b0 and 0x0b1 appear to be random)
From furthur investigation it appears that reading from a even address in the invalid area will always give an even result, reading from a odd address may give a odd or even result.
I have yet to confirm if the data is actually useful. Could this data be the result of data fetched from the screen?
Writing to any byte in the range &4000-&5000 will be masked with &0F. Therefore, when reading, the result is:
DATA_READ = DATA_WRITTEN & 0x0f
Offsets are given from the base of the sprite information for each sprite.
Offset 0 | Low byte of X coordinate |
Offset 1 | High byte of X coordinate |
Offset 2 | Low byte of Y coordinate |
Offset 3 | High byte of Y coordinate |
Offset 4 | Sprite magnification |
Offset 5 | Sprite magnification |
Offset 6 | Sprite magnification |
Offset 7 | Sprite magnification |
When read, offset 4,5,6 and 7 (in that order) are a mirror of reading offset 0,1,2 and 3 (in that order).
When reading offsets 0,1,2 and 3, the following apply:
Offset 0 and 2: any written byte is readable, the same value that is written is readable!
BYTE_READ = BYTE_WRITTEN;
Offset 1: If byte written & 0x03 is 0,1 or 2, the value & 0x03 is read back. If the byte written & 0x03 is 3 then 0x0ff is read back.
if ((BYTE_WRITTEN & 0x03)==0x03) { BYTE_READ = 0x0ff; } else { BYTE_READ = BYTE_WRITTEN & 0x03; }
The range is &000-&3FF (0-1023) or &FF00-&2FF (-256 to +767). The sprite coordinates are based on the internal CRTC horizontal character counter. If CRTC R0>64, then the sprites may repeat horizontally.
Offset 3: If byte written & 0x01 is 0, the value & 0x01 is read back. If the byte written & 0x01 is 1, then 0x0ff is read back.
if ((BYTE_WRITTEN & 0x01)==0x01) { BYTE_READ = 0x0ff; } else { BYTE_READ = BYTE_WRITTEN & 0x01; }
The range is &000-&1FF (0-512) or &FF00-&FF (-256 to +256). The sprite coordinates are based on the internal CRTC character row counter and CRTC raster row counter.
This value is *not* dependant on Vertical Displayed.
Entry Offset 0 | Upper nibble is Red, lower nibble is blue |
Entry Offset 1 | Lower nibble is Green. |
When a byte is written to offset 1 and then read back it's value is returned & 0x0f. When writing to offset 0, and then read back, it's value is the same as written.
For Offset 0:
BYTE_READ = BYTE_WRITTEN;
For Offset 1:
BYTE_READ = BYTE_WRITTEN & 0x0f;
When a colour is set using the old method (using I/O), the corresponding entry in the ASIC Ram is updated with the RGB of the colour chosen. The table below shows the "hardware colour index" and the corresponding R,G,B for the colour.
Hardware Colour Index | Colour Name | RGB | ||
---|---|---|---|---|
R | G | B | ||
0 | White | 6 | 6 | 6 |
1 | White | 6 | 6 | 6 |
2 | Sea Green | 0 | 15 | 6 |
3 | Pastel Yellow | 15 | 15 | 6 |
4 | Blue | 0 | 0 | 6 |
5 | Purple | 15 | 0 | 6 |
6 | Cyan | 0 | 6 | 6 |
7 | Pink | 15 | 6 | 6 |
8 | Purple | 15 | 0 | 6 |
9 | Pastel Yellow | 15 | 15 | 6 |
10 | Bright Yellow | 15 | 15 | 0 |
11 | Bright White | 15 | 15 | 15 |
12 | Bright Red | 15 | 0 | 0 |
13 | Bright Magenta | 15 | 0 | 15 |
14 | Orange | 15 | 6 | 0 |
15 | Pastel Magenta | 15 | 6 | 15 |
16 | Blue | 0 | 0 | 6 |
17 | Sea Green | 0 | 15 | 6 |
18 | Bright Green | 0 | 15 | 0 |
19 | Bright Cyan | 0 | 15 | 15 |
20 | Black | 0 | 0 | 0 |
21 | Bright Blue | 0 | 0 | 15 |
22 | Green | 0 | 6 | 0 |
23 | Sky Blue | 0 | 6 | 15 |
24 | Magenta | 6 | 0 | 6 |
25 | Pastel Green | 6 | 15 | 6 |
26 | Lime | 6 | 15 | 0 |
27 | Pastel Cyan | 6 | 15 | 15 |
28 | Red | 6 | 0 | 0 |
29 | Mauve | 6 | 0 | 15 |
30 | Yellow | 6 | 6 | 0 |
31 | Pastel Blue | 6 | 6 | 15 |
The sprite colours can not be changed using the old method. To change the sprite colours, the ASIC must be enabled and the ASIC ram paged into the memory space.
Reading from 0x06800-0x06807 will read the same values as for invalid data.
Writing to &6800 will set the interrupt raster line,
Writing to &6801 will set the screen split line,
Writing to &6802 and &6803 will set the screen split address,
Writing to &6804 will set the soft scroll,
Writing to &6805 will set the interrupt vector (Arnold V specification 1.4).
Writing to &6806 has no effect.
Writing to &6807 has no effect.
For the interrupt raster line, soft scroll and split line please see furthur in this document for details of how the ASIC generates the comparison value to compare against each of these programmed values.
There is a bug with the interrupt vector feature. In the 1.4 specification it states that if bit 0 is set to 0, then interrupts will automatically be cleared before executing the interrupt service routine.
However, I found that with this bit set to 0, there is a bug.
Example showing bug: [ highlighted | original ]
A demo program used a single DMA channel to issue an interrupt, a few lines later a raster interrupt would issue an interrupt. The first interrupt executed the correct interrupt handler routine (0,2 or 4), the second executed the interrupt handler for vector of 0 (the raster interrupt is vector 6). This was clearly wrong! My advice is that you should not use the automatic interrupt clear feature.
Without any peripherals attached, the following readings were observed:
ADC0: 0x03f
ADC1: 0x03f
ADC2: 0x03f
ADC3: 0x03f
ADC4: 0x03f
ADC5: 0x000
ADC6: 0x03f
ADC7: 0x000
Writing to these appears to have no effect, with the readings not changing.
I have tested the analogue port with different PC analogue controllers to see how well they work, but I have not been completely successful. See the results at the end of this document.
When reading &6c00-&6c0f will give result of reading DCSR register.
The DMA channels data are located at &6c00, &6c04 and &6c08, for channels 0, 1 and 2 in that order.
Writing to offset 0 and 1 will set the DMA channel pointer. Writing to offset 2 will set the channel prescalar. Writing to offset 3 has no effect.
The official documentation is not clear about the bits in the DCSR register (&6c0f), I found the following are correct:
Bit 7 | Raster Int |
Bit 6 | DMA Channel 0 Interrupt |
Bit 5 | DMA Channel 1 Interrupt |
Bit 4 | DMA Channel 2 Interrupt |
Bit 3 | not used |
Bit 2 | DMA Channel 2 Enable |
Bit 1 | DMA Channel 1 Enable |
Bit 0 | DMA Channel 0 Enable |
Each DMA opcode is two bytes, and is always fetched from an even address. The top 4-bits of the opcode fetched in the DMA cycle are used to specify the function to perform by the DMA.
I found the following rule:
bit 3 | not used |
Bit 2 | Nop/loop/Int/Stop instruction |
Bit 1 | Repeat N instruction |
Bit 0 | Pause instruction. |
If (opcode & 0x07 == 0) then write data to register else perform function defined by bits.
This part of the document explains how the comparison values are calculated which are compared against the programmed interrupt line number, split line number and sprite coordinates.
Since much of this part of the documentation relies on the operation of the CRTC, you are advised to read the documents on the CRTC, and specifically the documents describing the CRTC implementation in the ASIC (known as type 3).
The ASIC generates the "compare line" from the CRTC Line Counter and CRTC Raster Counter. The "compare line" is the line number that is compared to the sprite Y range to determine if a line of sprite should be displayed. The sprite Y range is defined as the Y coordinate range occupied by the sprite with magnification.
The following equation is used to generate the "compare line":
(LineCounter<<3) | (RasterCounter & 0x07)
The CRTC horizontal character count is used to determine where the sprite line should be displayed on a line it is active on.
The CRTC char containing the first pixel (leftmost pixel) is:
((Sprite_X & 0x0fff8)>>3)
The pixel within the char is:
Sprite_X & 0x07
The raster interrupt is not dependant on the width of the horizontal sync. It's position is dependant on the position of the horizontal sync. The raster interrupt occurs 10us following the HSYNC start.
The following comparison is made:
if ((LineCounter == ((InterruptLine>>3) & 0x01f) && ( (RasterCounter == ((InterruptLine & 0x07))
InterruptLine is the value programmed to ASIC interrupt line register. LineCounter is the CRTC character line counter. Raster counter is the CRTC Raster Counter.
A 8-bit number is formed from the LineCounter and RasterCounter in the following way:
((LineCounter & 0x01f)<<3) | (RasterCounter & 0x07)
This is then compared against the programmed split line.
The split line can be re-programmed at any time. If a split line is programmed and the CRTC is currently on that line, the split will still occur at the end of the line.
Since the comparison is 8-bit, in a frame with 312 lines, it is possible a screen split will occur twice.
The first at the programmed line, and the next after the counter has wrapped around (at 256 + programmed line).
There is a single case with a 312 line frame, with a programmed value of 56, where the screen split bug occurs.
With this bug, the first split occurs normally, but the second split occurs at the very end of the frame. The result is a single visible split after line 0 of the frame. (The second split effectively overrides the second split that would occur).
The only way to avoid this condition is to introduce extra lines into the frame, or to not program the value.
Extra lines can be appended by programming reg 5 of the CRTC to a value other than 0.
A second way to eliminate the problem without extra lines is to use the vertical scroll of the ASIC. When the programmed vertical scroll is 7, with the bug occuring, the split will occur normally. It appears that the scroll changes the screen address in preference to the screen split causing the problem to disapear.
The equation notation in this document uses standard C.
For those who do not know this language, here is a quick list of the operators and their functions:
| - bitwise/logical OR
& - bitwise/logical AND
<< - shift left by number of bits specified
>> - shift right by number of bits specified
== - equals comparison
= - assign value to variable
&& - and, as used in "if a==b and c==d ..."
Hex numbers are represented with a prefix of 0x. e.g. 0x0abcd is the hex number abcd.
In if statements, the operations enclosed in { } brackets following the if are executed if the condition is true, otherwise the operations enclosed in the brackets following the "else" statement are executed.
In the ASIC documentation it states that following the leading edge of the HSYNC, there is one dead cycle, followed by an instruction fetch for each channel. For each cycle 1 2 byte instruction is fetched.
(since an instruction is 2 bytes long, each instruction fetch will require 1us)
If all channels are enabled:
(in 1us, original gate array accessed two bytes of information)
0: dead cycle 1: opcode for channel 0 2: opcode for channel 1 3: opcode for channel 2 4: execute channel 0 5: execute channel 1 6: execute channel 2
1 cycle execute per channel, except load which requires 8 cycles.
For raster int it appears to start at least 10 cycles following HSYNC beginning. To be checked!!!!
When competition pro attached only.
6808 = left/right
6809 = up/down
680a = Z button
680b = y button
pressing X button sets bit 4 of all keys on keyboard matrix, pressing A button sets bit 5 of all keys on keyboard matrix
pressing delete causes all lines to go to 0x080, except line 15. All keys except delete work as expected.
keyboard normally
all keys pressed gives 0-9 0x0ff
10 is joystick
anything above 10 is zero.
pressing delete causes all key lines to go to 0x080. pressing joypad 0 fire 1,2 sets all lines to 0x010 or 0x020 (e.g. same as pressing X,A), left does nothing, right produces 0x08 in the upper lines (10->) and after a bit of time, the remaining keyboard lines reflect this. up gives 0x01 in the 10-15 range. Down gives nothing.
Joypad 1 works as expected.
Microsoft sidewinder doesn't work!