Difference between revisions of "PlayCity"

From CPCWiki - THE Amstrad CPC encyclopedia!
Jump to: navigation, search
(Technical description)
 
(50 intermediate revisions by 8 users not shown)
Line 1: Line 1:
 +
[[File:PlayCity.jpg|thumb|320px|]]
 
==Introduction==
 
==Introduction==
Write the introduction text...
+
The PlayCity is a CPC expansion previously known as the CTC-AY.
 
+
It has been released on April 2014 with these features:
 
+
Its most important features are:
+
 
* 6 audio channels sound with programmable frequency (stereo line out and speaker mono mix in).
 
* 6 audio channels sound with programmable frequency (stereo line out and speaker mono mix in).
 
* 4 counter/timer channels for programmable interrupt (including NMI).
 
* 4 counter/timer channels for programmable interrupt (including NMI).
Line 10: Line 9:
 
* CRTC hardware CURSOR interrupt support.
 
* CRTC hardware CURSOR interrupt support.
 
* The LIGHT PEN/GUN connector.
 
* The LIGHT PEN/GUN connector.
 +
 +
==Technical description==
 +
The board main feature is the Zilog Z80 Counter Timer Circuit, the best friend of the Zilog Z80 CPU.
 +
The CTC is linked to the CPU to allow it to manage vectorized interrupt (IM2) and handling triggered events.
 +
Programmers can only get benefit of that to save CPU time and make complex programs more easy to achieve.
 +
I can only invite them to read the Zilog CTC data-sheet for more informations. (it's really easy to use)
 +
 +
Because the CPC expansion port have some interesting signals on it, it had been a shame to not use them.
 +
* CTC channel 0 is a programable audio clock generator. i.e. allow to replay ST or ZX sound on a CPC using AUDIO pin.
 +
* CTC channel 1 is linked to the CURSOR pin and triggered by the NMI pin. Allow accurate hi priority rasters effects.
 +
* CTC channel 2 and 3 are linked together and offers 2x8bit or 1x16bit user purpose timer or counter.
 +
 +
ADDER is the ADDress decodER part of the board. It manage all the ICs.
 +
Because ports are direct access to the hardware, it's really fast.
 +
* CTC channels: #F880, 81, 82, 83
 +
* Audio right: #F884, #F984
 +
* Audio left: #F888, #F988
 +
* Soft reset: #F8FF
 +
 +
At end, the LIGHTPEN pin is rooted to a 4 pins connector for plugging compatible hardware through an adapter.
  
 
==Hardware Installation==
 
==Hardware Installation==
 
* Power OFF your Amstrad / Schneider CPC.
 
* Power OFF your Amstrad / Schneider CPC.
* Attach the PlayCity board to one free MotherX4 slog.
+
* Attach the PlayCity board to one free MotherX4 slot.
* Turn ON your computer, and enjoy! (if a grey screen appear, turn off your computer and check the connections)
+
* Turn ON your computer, and enjoy!
  
 
==Peripheral Soft Reset==
 
==Peripheral Soft Reset==
All expansion peripherals should be reset when an output is performed to I/O port $F8FF.  
+
All expansion peripherals should be reset when an output is performed to I/O port '''$F8FF'''.  
  
 
Used by the standard BIOS functions
 
Used by the standard BIOS functions
MC_BOOT_PROGRAM and MC_START_PROGRAM (vector BD13h and BD16h), in particular a peripheral that generates interrupts.
+
'''MC_BOOT_PROGRAM''' and '''MC_START_PROGRAM''' (vectors $BD13 and $BD16), in particular a peripheral that generates interrupts. Also used by [[FutureOS]].
  
The PlayCity board use this feature to be sure that the CTC and YMZs circuits are properly reseted before using them.
+
The PlayCity board use this feature to be sure that the CTC and YMZs circuits are properly reset before using them.
  
 
== Light Pen/Gun connector ==
 
== Light Pen/Gun connector ==
For making easier to adapt Light Pens/Guns from other systems or new ones, we added a 4 pins connector to the board. The connections are labeled in the board as VCC (+5V), TR (Trigger or D7), LS (Light Sensor) and GND.
+
For making it easier to adapt Light Pens/Guns from other systems or new ones, we added a 4-pin connector to the board. The connections are labeled in the board as VCC (+5V), TR (Trigger), LS (Light Sensor) and GND.
 +
 
 +
Trigger (TR) = Request via output to port &FBFE. Light Sensor (LS) = CRTC Light Pen input.
  
 
Making the system compatible with [[Amstrad_Magnum_Phaser|Amstrad Magnum Phaser]] games, the most accuracy light gun system for CPC.
 
Making the system compatible with [[Amstrad_Magnum_Phaser|Amstrad Magnum Phaser]] games, the most accuracy light gun system for CPC.
  
 
==Counter/Timer Circuit==
 
==Counter/Timer Circuit==
===Informations===
+
===Information===
 
The Z84C30 has four independently programmable counter/timer channels interfaced directly with the Z80 CPU. You can get full information in the CTC datasheet ([[File:Z80ctc.pdf]]).  
 
The Z84C30 has four independently programmable counter/timer channels interfaced directly with the Z80 CPU. You can get full information in the CTC datasheet ([[File:Z80ctc.pdf]]).  
  
Line 43: Line 64:
  
 
Used as a counter, it's synchronized with the CRTC CURSOR signal, generating a smooth high priority rasters interrupt. Used as a
 
Used as a counter, it's synchronized with the CRTC CURSOR signal, generating a smooth high priority rasters interrupt. Used as a
timer (prescaler set to 256), it's 15.625 kHz signal is scanline-synchronized. That means the time constant is the number of
+
timer (prescaler set to 256), its 15.625 kHz signal is scanline-synchronized. That means the time constant is the number of
 
scanlines to wait before to send an NMI.
 
scanlines to wait before to send an NMI.
 +
 
====Channel 2/3 ($F882/$F883)====
 
====Channel 2/3 ($F882/$F883)====
 
The channels 2 and 3 are dedicated to general purpose usages. Yes, it's for you! The input for the trigger (TRG2) is the 4 MHz
 
The channels 2 and 3 are dedicated to general purpose usages. Yes, it's for you! The input for the trigger (TRG2) is the 4 MHz
system clock. The output (ZC/TO0) is linked to the trigger for the channel 3 (TRG3). They can be used as 2x 8-bit or 1x 16-bit
+
system clock. The output (ZC/TO2) is linked to the trigger for the channel 3 (TRG3). They can be used as 2x 8-bit or 1x 16-bit
counter/timer. They generate normal interrupts and allow to use the Z80 vector interrupt (mode 2) too.
+
counter/timer. They generate normal interrupts and allow using the Z80 vector interrupt (mode 2) too.
 +
 
 
===Coding examples===
 
===Coding examples===
 
Each channel is programmed with two bytes; a third is necessary when interrupts are enabled. Once started, the CTC countdown
 
Each channel is programmed with two bytes; a third is necessary when interrupts are enabled. Once started, the CTC countdown
Line 106: Line 129:
 
Don't forget to use RETN for NMI handlers as well as RETI for normal interrupt handlers. In other case, the Z80 CPU will not
 
Don't forget to use RETN for NMI handlers as well as RETI for normal interrupt handlers. In other case, the Z80 CPU will not
 
aknowledge the next interrupt properly.
 
aknowledge the next interrupt properly.
 +
 
==Audio Channels==
 
==Audio Channels==
 
===Informations===
 
===Informations===
Line 115: Line 139:
 
the frequency by reprograming the CTC Channel 0.
 
the frequency by reprograming the CTC Channel 0.
 
===YMZ294 Registers===
 
===YMZ294 Registers===
They are exactly the same than the [[PSG]], only remember there is not PSG I/O registers ($0E-$0F9) in the YMZ.
+
They are exactly the same than the [[PSG]], only remember there is not PSG I/O registers ($0E-$0F) in the YMZ. The registers are write only.
 +
 
 
===Coding Examples===
 
===Coding Examples===
 
====YMZ Initialization====
 
====YMZ Initialization====
Line 142: Line 167:
 
{| class="wikitable"
 
{| class="wikitable"
 
|-
 
|-
! 4/N !! CTC Val !! CTC OUT (MHz) !! YMZ Div (MHz) !! Computer
+
! CTC !! CTC Out (MHz) !! YMZ (MHz) !! Computer
 
|-
 
|-
| 1 || $01 || 2,00 || 1,00 || CPC
+
| $1 || 2,00 || 1,00 || = CPC
 
|-
 
|-
| 2 || || 2,67 || 1,33 ||
+
| $2 || 3,00 || 1,50 ||
 
|-
 
|-
| 3 ||$02 || 3,00 || 1,50 ||
+
| $3 || 3,33 || 1,67 ||
 
|-
 
|-
| 4 || || 3,20 || 1,60 ||
+
| $4 || 3,50 || 1,75 || = ZX
 
|-
 
|-
| 5 || $03 || 3,33 || 1,67 ||
+
| $5 || 3,60 || 1,80 || ~ MSX
 
|-
 
|-
| 6 || || 3,43 || 1,71 ||
+
| $6 || 3,67 || 1,83 ||
 
|-
 
|-
| 7 || $04 || 3,50 || 1,75 || ZX
+
| $7 || 3,71 || 1,86 ||
 
|-
 
|-
| 8 || || 3,56 || 1,78 || MSX
+
| $8 || 3,75 || 1,88 ||
 
|-
 
|-
| 9 || $05 || 3,60 || 1,80 ||
+
 
 +
| $9 || 3,78 || 1,89 ||
 
|-
 
|-
| 10 || || 3,64 || 1,82 ||
+
 
 +
| $A || 3,80 || 1,90 ||
 
|-
 
|-
| 11 || $06 || 3,67 || 1,83 ||
+
| $B || 3,82 || 1,91 ||
 
|-
 
|-
| 12 || || 3,69 || 1,85 ||
+
 
 +
| $C || 3,83 || 1,92 ||
 
|-
 
|-
| 13 || $07 || 3,71 || 1,86 ||
+
 
 +
| $D || 3,85 || 1,92 ||
 
|-
 
|-
| 14 || || 3,73 || 1,87 ||
+
 
 +
| $E || 3,86 || 1,93 ||
 
|-
 
|-
| 15 || $08 || 3,75 || 1,88 ||
+
 
 +
| $F || 3,87 || 1,93 ||
 
|-
 
|-
| 16 || || 3,76 || 1,88 ||
+
| $0 || 3,98 || 1,99 || ~ ST
 
|-
 
|-
| ... || ... || ... || ... ||
+
| UNSET || 4,00 || 2,00 || = ST
|-
+
| 256 || $00 || 3,98 || 1,99 ||
+
|-
+
| UNSET || || 4,00 || 2,00 || ST
+
 
|}
 
|}
'''Informations''': The rows without a CTC Val were not tested. It may be needed to configure the CTC channel 0 with "falling edge" instead of
+
 
"rising edges".
+
==PlayCity coding tips==
 +
Interesting tips or "magic tricks" using the board should be documented here. If the code is long, you must put in other wiki page and link it here.
 +
 
 +
===PlayCity detection===
 +
We are going to use the NMI interrupt generator to check if our program is running in a CPC with a PlayCity board.
 +
<pre>
 +
; ---------------------------------------------------------------------------
 +
; PlayCity check
 +
; (c) 2013 SyX
 +
; ---------------------------------------------------------------------------
 +
 
 +
; Constantes
 +
CTC_TIM1            EQU $F881      ; Channel 1 (I: Cursor CRTC | O: NMI)
 +
CTC_START_TIMER256  EQU %00110111
 +
CTC_STOP_CHANNEL    EQU %00000011
 +
 
 +
; ---------------------------------------------------------------------------
 +
; NOTE: The lower ROM must be disabled before to run this code.
 +
check_playcity
 +
    ; Disable interrupts
 +
    DI
 +
   
 +
    ; Install NMI handler
 +
    LD  A,$C3                  ; JP $xxxx
 +
    LD  HL,nmi_interrupt
 +
    LD  ($0066),A
 +
    LD  ($0067),HL
 +
 
 +
    ; Initialize playcity variable to 0
 +
    XOR  A
 +
    LD  (playcity),A
 +
 
 +
    ; Wait VBlank
 +
    LD  B,$F5
 +
.wait_vbl
 +
    IN  A,(C)
 +
    RRA
 +
    JR  NC,.wait_vbl
 +
   
 +
    ; Initialize CTC timer 1 (NMI generator)
 +
    LD  HL,32                  ; 32 scanlines
 +
    LD  BC,CTC_TIM1
 +
    LD  A,CTC_START_TIMER256
 +
    OUT  (C),A                  ; Enable Timer
 +
    OUT  (C),L                  ; Set new time constant
 +
 
 +
    ; Extra delay
 +
    LD  IX,33 * 4 - 1          ; Wait 33 scanlines
 +
    CALL wait_scanlines_ix
 +
 
 +
    LD  A,(playcity)
 +
    OR  A
 +
    JR  NZ,.playcity_detected
 +
    ; No PlayCity detected
 +
    .
 +
    .
 +
    .
 +
 
 +
.playcity_detected
 +
    .
 +
    .
 +
    .
 +
 
 +
; ---------------------------------------------------------------------------
 +
nmi_interrupt
 +
    PUSH BC
 +
    PUSH AF
 +
 
 +
    ; Change playcity variable
 +
    LD  A,$FF
 +
    LD  (playcity),A
 +
 
 +
    ; Disable CTC timer 1 (NMI generator)
 +
    LD  BC,CTC_TIM1
 +
    LD  A,CTC_STOP_CHANNEL
 +
    OUT  (C),A                  ; Disable Timer
 +
 
 +
    POP  AF
 +
    POP  BC
 +
    EI
 +
    RETN
 +
 
 +
; ---------------------------------------------------------------------------
 +
; Wait scanlines
 +
; INPUT:
 +
;  IX: Scanlines to wait * 4 - 1
 +
; ---------------------------------------------------------------------------
 +
wait_scanlines_ix
 +
    DEFS 5,0                                ; (5)
 +
 
 +
.loop_wait_scanlines_ix                     
 +
    DEFS 6                                  ; (6)
 +
    DEC  IX                                ; (3)
 +
    LD  A,IXH                              ; (2)
 +
    OR  IXL                                ; (2)
 +
    JR  NZ,.loop_wait_scanlines_ix        ; (2/3)
 +
                                            ; Total loop --> 16 * (IX - 1) + 15
 +
    RET                                    ; (3)
 +
                                            ; Total Routine --> 64 * SCANLINES
 +
; ---------------------------------------------------------------------------
 +
playcity
 +
    DEFS 1
 +
</pre>
 +
 
 +
===PlayCity detection (alternative)===
 +
Faster and shorter, but untested or not working on all CPC.
 +
<pre>
 +
;=========================
 +
; PlayCity check
 +
; If it's present, A=1
 +
; Otherwise, A=0
 +
;-------------------------
 +
;
 +
;=========================
 +
macro PlaycityDetection
 +
    ; Code for NMI management
 +
    ld a,#3c    ; inc a
 +
    ld (#66),a
 +
    ld hl,#45ed ; (inverted) opcodes for retn
 +
    ld (#67),hl
 +
 
 +
    ; If a Playcity is present, the code generates a NMI then stops the counter.
 +
    ld bc,#F881            ; 3 NOPs CTC channel 1
 +
    ld hl,%00010111*256 + 2 ; 3 NOPs A NMI every 8 NOPs
 +
    out (c),h              ; 4 NOPs
 +
    out (c),l              ; 4 NOPs
 +
 
 +
    ld a,0                 ; 2 NOPs A is set to 0. If a CTC is present, it will be INCed by the INTerruption code
 +
    inc hl                  ; 2 NOPs L=3 => Stop CTC channel
 +
    nop                    ; 1 NOPs
 +
 +
    out (c),l              ; 4 NOPs <- If a Playcity is plugged, a NMI should be raised during this opcode
 +
mend
 +
</pre>
 +
 
 +
==Downloads==
 +
In [[File:playcity_examples.zip]], you will find more examples with full sources of using the CTC, a customized arkos player that let you play songs using an external YMZ and the ReSeT party demo disk that includes a CPC version of the PT3 Turbo Sound player (6 channels song format).
 +
 
 +
Another example, in [[File:test_sfx.zip]] you will find a 3 channels SFX player, you can choose the sound chip to be used by the player.
 +
 +
== Software Supporting PlayCity ==
 +
 
 +
*[[Software_Supporting_PlayCity|List of software supporting the board.]]
 +
 
 +
[[Category:FutureOS]]
 +
[[Category:Music and sound]]
 +
[[Category:Peripherals]]
 +
 
 +
Website: [http://centpourcent.net centpourcent.net]

Latest revision as of 16:48, 22 August 2023

PlayCity.jpg

Introduction

The PlayCity is a CPC expansion previously known as the CTC-AY. It has been released on April 2014 with these features:

  • 6 audio channels sound with programmable frequency (stereo line out and speaker mono mix in).
  • 4 counter/timer channels for programmable interrupt (including NMI).
  • IM2 vectorized interrupt support.
  • Raster lines interrupt support.
  • CRTC hardware CURSOR interrupt support.
  • The LIGHT PEN/GUN connector.

Technical description

The board main feature is the Zilog Z80 Counter Timer Circuit, the best friend of the Zilog Z80 CPU. The CTC is linked to the CPU to allow it to manage vectorized interrupt (IM2) and handling triggered events. Programmers can only get benefit of that to save CPU time and make complex programs more easy to achieve. I can only invite them to read the Zilog CTC data-sheet for more informations. (it's really easy to use)

Because the CPC expansion port have some interesting signals on it, it had been a shame to not use them.

  • CTC channel 0 is a programable audio clock generator. i.e. allow to replay ST or ZX sound on a CPC using AUDIO pin.
  • CTC channel 1 is linked to the CURSOR pin and triggered by the NMI pin. Allow accurate hi priority rasters effects.
  • CTC channel 2 and 3 are linked together and offers 2x8bit or 1x16bit user purpose timer or counter.

ADDER is the ADDress decodER part of the board. It manage all the ICs. Because ports are direct access to the hardware, it's really fast.

  • CTC channels: #F880, 81, 82, 83
  • Audio right: #F884, #F984
  • Audio left: #F888, #F988
  • Soft reset: #F8FF

At end, the LIGHTPEN pin is rooted to a 4 pins connector for plugging compatible hardware through an adapter.

Hardware Installation

  • Power OFF your Amstrad / Schneider CPC.
  • Attach the PlayCity board to one free MotherX4 slot.
  • Turn ON your computer, and enjoy!

Peripheral Soft Reset

All expansion peripherals should be reset when an output is performed to I/O port $F8FF.

Used by the standard BIOS functions MC_BOOT_PROGRAM and MC_START_PROGRAM (vectors $BD13 and $BD16), in particular a peripheral that generates interrupts. Also used by FutureOS.

The PlayCity board use this feature to be sure that the CTC and YMZs circuits are properly reset before using them.

Light Pen/Gun connector

For making it easier to adapt Light Pens/Guns from other systems or new ones, we added a 4-pin connector to the board. The connections are labeled in the board as VCC (+5V), TR (Trigger), LS (Light Sensor) and GND.

Trigger (TR) = Request via output to port &FBFE. Light Sensor (LS) = CRTC Light Pen input.

Making the system compatible with Amstrad Magnum Phaser games, the most accuracy light gun system for CPC.

Counter/Timer Circuit

Information

The Z84C30 has four independently programmable counter/timer channels interfaced directly with the Z80 CPU. You can get full information in the CTC datasheet (File:Z80ctc.pdf).

The first thing to consider is how those channels are linked to the PlayCity board.

Channel 0 ($F880)

The channel 0 is exclusively used to generate the frequency of the two embedded soundchips (YMZ294). The input for the trigger (TRG0) is the 4 MHz system clock of the CPC. The output signal (ZC/TO0) is sent to the clock pins of the soundchips.

Channel 1 ($F881)

The channel 1 is typically used to generate raster interrupts. The input (TRG1) is linked to the CRTC CURSOR signal. The output (ZC/TO1) is connected to the NMI pin of the Z80.

Used as a counter, it's synchronized with the CRTC CURSOR signal, generating a smooth high priority rasters interrupt. Used as a timer (prescaler set to 256), its 15.625 kHz signal is scanline-synchronized. That means the time constant is the number of scanlines to wait before to send an NMI.

Channel 2/3 ($F882/$F883)

The channels 2 and 3 are dedicated to general purpose usages. Yes, it's for you! The input for the trigger (TRG2) is the 4 MHz system clock. The output (ZC/TO2) is linked to the trigger for the channel 3 (TRG3). They can be used as 2x 8-bit or 1x 16-bit counter/timer. They generate normal interrupts and allow using the Z80 vector interrupt (mode 2) too.

Coding examples

Each channel is programmed with two bytes; a third is necessary when interrupts are enabled. Once started, the CTC countdown automaticaly reloads its time constant and resume counting. Interrupt processing is simplified because only one vector need to be specified; the CTC internally generates an unique vector for each channel.

Before programming the CTC channels, you need to set the lower byte for the vectorized interrupts.

Vector interrupts

; Set Vector lower byte to 0
LD BC,$F880
OUT (C),0

Channel 0 example

; Set both YMZ294 clocks to sound like the CPC AY-3-8912
LD BC,$F880
LD HL,$7F01
OUT (C),H ; $7F = Clock generator
OUT (C),L  ; $01 = CPC AY

Channel 2 example

; Start CTC channel 2 in timer mode (prescalar 256 and set new time constant)
LD H,%10110111 ; Timer mode and preescalar 256
LD L,1 ; Time constant
LD BC,$F882
OUT (C),H  ; Enable Timer
OUT (C),L ; Set new time constant
; Stop CTC channel 2
LD BC,$F882
LD A,%00000011
OUT (C),A ; Disable Timer 2

Channel 2/3 example

; Start CTC channel 2 and 3 in 16-bit counter mode
LD BC,$F882
LD HL,32768 ; Timer constant
LD A,%11110111 ; Counter mode
OUT (C),A ; Enable Timer 2
OUT (C),L ; Set new time constant (lower byte)
INC BC
OUT (C),A ; Enable Timer 3
OUT (C),H ; Set new time constant (high byte)
; Stop CTC channel 2 and 3 (16 bit mode)
LD BC,$F882
LD A,%00000011
OUT (C),A ; Disable Timer 2
INC BC
OUT (C),A ; Disable Timer 3

Don't forget to use RETN for NMI handlers as well as RETI for normal interrupt handlers. In other case, the Z80 CPU will not aknowledge the next interrupt properly.

Audio Channels

Informations

The PlayCity board is populated with two AY compatible soundchips, adding 6 stereo channels. The YMZ294 eliminate the I/O port and improve the CPU interface through /CS, /WR control signals and a 8-bit data bus. Each sound chip can be directly programmed using two dedicated I/O ports for registers and data. No initialization code is required.

By default, the YMZs are clocked at 4 MHz but run internaly at 2 MHz. So, they will sound like an Atari ST, while you don't change the frequency by reprograming the CTC Channel 0.

YMZ294 Registers

They are exactly the same than the PSG, only remember there is not PSG I/O registers ($0E-$0F) in the YMZ. The registers are write only.

Coding Examples

YMZ Initialization

YMZ_SELECT, select a YMZ register. Use the port $F984 for the right channels and port $F988 for the left channels.

YMZ_WRITE, write a byte in the selected register. Use the port $F884 for the right channels and port $F888 for the left channels.

; Initialization of the YMZ registers
LD A,$D
.loop_init_ymz
LD BC,YMZ_SELECT
OUT (C),A ; Register
LD BC,YMZ_WRITE
CP 7
JR NZ,.send_zero
LD A,$3F ; Noise and Tone disabled
OUT (C),A ; Write in YMZ R7
LD A,6
JR .loop_init_ymz
.send_zero
OUT (C),0 ; Write 0 in the selected YMZ register
DEC A
JP P,.loop_init_ymz

CTC/YMZ Useful Values

CTC CTC Out (MHz) YMZ (MHz) Computer
$1 2,00 1,00 = CPC
$2 3,00 1,50
$3 3,33 1,67
$4 3,50 1,75 = ZX
$5 3,60 1,80 ~ MSX
$6 3,67 1,83
$7 3,71 1,86
$8 3,75 1,88
$9 3,78 1,89
$A 3,80 1,90
$B 3,82 1,91
$C 3,83 1,92
$D 3,85 1,92
$E 3,86 1,93
$F 3,87 1,93
$0 3,98 1,99 ~ ST
UNSET 4,00 2,00 = ST

PlayCity coding tips

Interesting tips or "magic tricks" using the board should be documented here. If the code is long, you must put in other wiki page and link it here.

PlayCity detection

We are going to use the NMI interrupt generator to check if our program is running in a CPC with a PlayCity board.

; ---------------------------------------------------------------------------
; PlayCity check
; (c) 2013 SyX
; ---------------------------------------------------------------------------

; Constantes
CTC_TIM1            EQU $F881       ; Channel 1 (I: Cursor CRTC | O: NMI) 
CTC_START_TIMER256  EQU %00110111
CTC_STOP_CHANNEL    EQU %00000011

; ---------------------------------------------------------------------------
; NOTE: The lower ROM must be disabled before to run this code.
check_playcity
    ; Disable interrupts
    DI
    
    ; Install NMI handler
    LD   A,$C3                  ; JP $xxxx
    LD   HL,nmi_interrupt
    LD   ($0066),A
    LD   ($0067),HL

    ; Initialize playcity variable to 0
    XOR  A
    LD   (playcity),A

    ; Wait VBlank
    LD   B,$F5
.wait_vbl
    IN   A,(C)
    RRA
    JR   NC,.wait_vbl
    
    ; Initialize CTC timer 1 (NMI generator)
    LD   HL,32                  ; 32 scanlines
    LD   BC,CTC_TIM1
    LD   A,CTC_START_TIMER256
    OUT  (C),A                  ; Enable Timer 
    OUT  (C),L                  ; Set new time constant

    ; Extra delay
    LD   IX,33 * 4 - 1          ; Wait 33 scanlines
    CALL wait_scanlines_ix
   
    LD   A,(playcity)
    OR   A
    JR   NZ,.playcity_detected
    ; No PlayCity detected
    .
    .
    .

.playcity_detected
    .
    .
    .

; ---------------------------------------------------------------------------
nmi_interrupt
    PUSH BC
    PUSH AF

    ; Change playcity variable
    LD   A,$FF
    LD   (playcity),A

    ; Disable CTC timer 1 (NMI generator)
    LD   BC,CTC_TIM1
    LD   A,CTC_STOP_CHANNEL
    OUT  (C),A                  ; Disable Timer 

    POP  AF
    POP  BC
    EI
    RETN

; ---------------------------------------------------------------------------
; Wait scanlines
; INPUT:
;   IX: Scanlines to wait * 4 - 1
; ---------------------------------------------------------------------------
wait_scanlines_ix
    DEFS 5,0                                ; (5) 

.loop_wait_scanlines_ix                      
    DEFS 6                                  ; (6)
    DEC  IX                                 ; (3)
    LD   A,IXH                              ; (2)
    OR   IXL                                ; (2)
    JR   NZ,.loop_wait_scanlines_ix         ; (2/3)
                                            ; Total loop --> 16 * (IX - 1) + 15
    RET                                     ; (3)
                                            ; Total Routine --> 64 * SCANLINES
; ---------------------------------------------------------------------------
playcity
    DEFS 1

PlayCity detection (alternative)

Faster and shorter, but untested or not working on all CPC.

;=========================
; PlayCity check
; If it's present, A=1
; Otherwise, A=0
;-------------------------
;
;=========================
macro PlaycityDetection
    ; Code for NMI management
    ld a,#3c    ; inc a
    ld (#66),a
    ld hl,#45ed ; (inverted) opcodes for retn
    ld (#67),hl

    ; If a Playcity is present, the code generates a NMI then stops the counter.
    ld bc,#F881             ; 3 NOPs CTC channel 1
    ld hl,%00010111*256 + 2 ; 3 NOPs A NMI every 8 NOPs
    out (c),h               ; 4 NOPs
    out (c),l               ; 4 NOPs

    ld a,0                  ; 2 NOPs A is set to 0. If a CTC is present, it will be INCed by the INTerruption code
    inc hl                  ; 2 NOPs L=3 => Stop CTC channel
    nop                     ; 1 NOPs
 
    out (c),l               ; 4 NOPs <- If a Playcity is plugged, a NMI should be raised during this opcode
mend

Downloads

In File:Playcity examples.zip, you will find more examples with full sources of using the CTC, a customized arkos player that let you play songs using an external YMZ and the ReSeT party demo disk that includes a CPC version of the PT3 Turbo Sound player (6 channels song format).

Another example, in File:Test sfx.zip you will find a 3 channels SFX player, you can choose the sound chip to be used by the player.

Software Supporting PlayCity

Website: centpourcent.net