Jump to content

A full-blown newb on the way to a POKEY synth


nILS
 Share

Recommended Posts

As some of you might have read in the chat or across the forum, I started working on a POKEY synth. My plan is to start with a board designed by Mike Hill way back in time that hooks up to the LPT Port of a computer to test the POKEY and fiddle around with it a bit and hopefully finally come up with an mbPokey board and the MIOS app to go with it.

I'll try to use this thread as a blog, mainly to keep me from quitting the project when that damn little chip does what it wants rather than what I want it to =)

First up, a few links (most of them have been posted around here somewhere before):

http://en.wikipedia.org/wiki/Atari_POKEY - Wikipedia entry for the POKEY chip (polish version is much more in-depth, but my polish isn't really all that good)

http://krap.pl/mirrorz/atari/homepage.ntlworld.com/kryten_droid/Atari/800XL/atari_hw/pokey.htm - HTML version of the datasheet of the Pokey. The original datasheet can be found on the web as well - only god and smashTV now where they got it =)

http://www.hillsoftware.com/atari/index.html . Mike Hill's website. He's the guy who created the LPT-Pokey schem and wrote a DOS app for it (won't work on Windows obviously)

http://www.pokey.nl/xoops/modules/books/index.php?op=viewarticle&artid=10 - Some information on what to do with the POKEY

So far, I built the LPT interface on a piece of perfboard and ported Mike's application to Windows and added a complete (well sorta) CS to it, so that I can access all registers. I have a small and rather random sound test right here.

I really want to thank SmashTV, Mike Hill, stryd_one and jimp at this point - without those guys I wouldn't even have gotten this far :D

I'll post some pictures later on today =)

Cheerio,

nILS

Link to comment
Share on other sites

  • Replies 149
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

Posted Images

s1: Yeah, I did read the "read before you post"-thread =) It's just not anything *new* worth documenting here yet. I will put it all down in the wiki when I get a bit further.

Wild_Weasel: I bought a "Ballblazer" cartridge for the ATARI 7800 on eBay for ~$5. The commando "Commando" cartridge has a Pokey chip inside as well. Most of the 8bit ATARI computers have one inside, they're a lot more expansive though. You can buy the chips at a lot of outlet type stores (www.batescomponents.com,  www.loadparts.com, ...) for about $5-7 a chip. Problem with that is, you have to order >100 chips.

Link to comment
Share on other sites

I think you can rest assured that's not likely to happen anytime soon! The Pokey is a very limited chip for musical applications. Doesn't it have problems in terms of pitch? Personally, I think it has some limited application for bass sounds and chiptune leads, but not much else. Still, if it was simple enough to build I think it would be a neat toy.

Link to comment
Share on other sites

SLP: I seriously doubt that the pokey will become anything as hyped as the SID is =) It *is* a pretty simple chip. Yet I find it to create some nice sounds if handled right. Since availability of the pokey is great due to ATARI's overproduction right before the crash, not being able to order bulks isn't that big a problem.

jaicen: That is very true =) Especially the NTSC version clocked at ~1.79MHz has serious problems with the pitch. I did some calculations today (thanks stryd  ;)) and found the PAL version at ~1.77MHz to be way more accurate. At the moment I'm running the pokey on 1.8432 since it was the closest crystal available w/o any frequency dividers - the pitch difference is noticeable =)

About the "simple to build" part: Well, the core module is obviously the same and the pokey module is extremely close to the SID board. What would/will keep this simple is the fact that the CS (which is really the only "hard to build" thing) will be a lot smaller and simpler than for the SID. Basically, all you would need is 5x8 buttons to control the 5 relevant registers - that's it =) This is obviously not the most usable version, but it gives you an idea of the dimension we're talking about.

Link to comment
Share on other sites

Some minor updates:

  • Since the PAL version isn't that much better when it comes to the pitch and due to the fact that it is virtually impossible to get a hand on a matching crystal, I'm going with the NTSC version. (Thx to SmashTV, stryd_one, jimp and DrBunsen for looking for alternative solutions)
  • Port of my test application (screenshot below) has been ported to C for easier translation to the PIC.
  • Since there's got to be a stereo version I'm looking into ways to drive (at least) 2 pokeys from a core atm.

EDIT: I attached the pokey test app so you guys can fiddle around with it, too. Apparently I'm not the only one: sd2000/sparx has a Pokey board as well ;-)

Cheerio,

nILS

pokey_app_thumb.png

pokey_test_app.zip

1659_pokey_app_pngf721e25fa4790219a55852

pokey_test_app.zip

Link to comment
Share on other sites

jaicen: "More or less" is exactly the problem. I really wanted to go for "more" rather than "less" The pokey uses a divide-by-n counter to generate the output frequency from the oscillator it's clocked with. Therefore, there's a direct correllation between Fosc and Fout.

In "default mode" the clock frequency gets internally divided by 28 leaving you with ~64kHz as Finternal. The formula for the output frequency in that case is rather simple:

Fout =  Finternal / (AUDF+1) * 2

AUDF being a register holding a byte determining the frequency. Even though the difference in the Finternal is rather small between NTSC and PAL (633337.39Hz for PAL, 63920.45Hz for NTSC), NTSC tends to be a little bit more off. Not enough to justify going through custom-crystal-shaving or anything like that though.

Link to comment
Share on other sites

I finally put sth. in the wiki (http://www.midibox.org/dokuwiki/midibox_pokey). It needs to and will be extended :)

For everyone interested, the first of the pictures below shows what the preliminary version of the mbPokey board looks like. A later version will probably sport a serial out to allow for multiple pokeys.

The second one is a picture of the schematic that shows how to hook up a pokey to a printer port on a PC (this is the schematic assuming a 1.77Mhz or 1.79Mhz crystal. It works "kinda" well with a 1.8432Mhz crystal, which is widely available).

EDIT: Some feedback on the first schematic would be greatly appreciated.

pokey_ntsc_thumb.png

pokey_lpt_schem_thumb.png

1680_pokey_ntsc_pngbfc12207ce2e03c4fbf56

1682_pokey_lpt_schem_png0006d8be7a209722

Link to comment
Share on other sites

The second one is a picture of the schematic that shows how to hook up a pokey to a printer port on a PC (this is the schematic assuming a 1.77Mhz or 1.79Mhz crystal. It works "kinda" well with a 1.8432Mhz crystal, which is widely available).

Have you tried diving an OPL3 crystal?

The OPL3 board uses a 14.318 MHz crystal.

14.318 / 8 = 1.78975...

also, I hope later today to upload a crystal to oscillator schem onthe wiki. stand by.

Link to comment
Share on other sites

Have you tried diving an OPL3 crystal?

The OPL3 board uses a 14.318 MHz crystal.

Yes, we've discussed that in the chat. There's no need to use a 14.318Mhz crystal and divide it by 8 (for instance by using 3 flip flops), since 3.579545Mhz oscillators are widely available and only need to be divided by two (one flip flop) as seen in the first schematic.

Using a crystal and building an oscillator circuit has been discussed as well, but it doesn't really lead to any better results as basically all crystal "speeds" come as oscillators, as well.

So, the bottom line is that I think adding a single IC (a dual D-Flip-Flop (i.e. 74HC74)) to the circuit is the best and easiest solution.

Link to comment
Share on other sites

At the moment I'm trying to set up the Core -> Pokey module data transfer. I figure it's going to be similar to what TK did in the SID app.

Buuuut, I'm kinda stuck. So here's for all code gurus:

The parallel output source:

#define ACTIVE 0 
#define INACTIVE 1
#define STROBE 2

#define PORT 0x378
#define PORTCONTROL PORT
#define PORTDATA PORT + 1

void WritePokey(unsigned char addr, unsigned char byte)
{
    PLatchAddress(addr);
    PLatchData(byte);
    PChipSelect(STROBE);
}

void PLatchData(unsigned char val)
{
    Pokey_Out(PORTCONTROL, 0);
    Pokey_Out(PORTDATA, val);
    Pokey_Out(PORTCONTROL, 2);
    Pokey_Out(PORTCONTROL, 0);
} // PLatchData

void PLatchAddress(int add)
{
    Pokey_Out(PORTCONTROL, 0);
    Pokey_Out(PORTDATA, add);
    Pokey_Out(PORTCONTROL, 8);
    Pokey_Out(PORTCONTROL, 0);
} // PLatchAddress

void PChipSelect(int mode)
{
    if (mode == INACTIVE) Pokey_Out(PORTCONTROL,0);
    if (mode == ACTIVE)   Pokey_Out(PORTCONTROL,1);
    if (mode == STROBE) 
    {
        Pokey_Out(PORTCONTROL,1);
        Pokey_Out(PORTCONTROL,0);
    }
} // PChipSelect[/code]

This is basically what I need to port to serial output for the Core.

Any pointers would be greatly appreciated =)

Link to comment
Share on other sites

So far what's happened is I took the source from the SID v1.7303 (sid_sr.inc) and thinned it out a bit (taking out the WT and reducing it to the 10 registers needed). This is the result so far:

;
; MIDIbox POKEY
; POKEY Shift Register Service Routine
;
; ==========================================================================
;
;  Copyright 1998-2006 Thorsten Klose (tk@midibox.org)
;  Licensed for personal non-commercial use only.
;  All other rights reserved.
;
; ==========================================================================
;
; define the pins to which the MBHPS_POKEY module is connected
;
SID_SR_LAT_SCLK        EQU        LATD
SID_SR_PIN_SCLK        EQU        5                ; Pin D.5
SID_SR_LAT_RCLK        EQU        LATC
SID_SR_PIN_RCLK        EQU        4                ; Pin C.4
SID_SR_LAT_OUT         EQU        LATD
SID_SR_PIN_OUT         EQU        6                ; Pin D.6

SID_SR_LAT_WR          EQU        LATC
SID_SR_PIN_WR          EQU        5                ; Pin C.5


;; --------------------------------------------------------------------------
;;  Initialize the MBHP_POKEY module
;; --------------------------------------------------------------------------
SID_SR_Init
        ;; reset the POKEY
        clrf    MIOS_PARAMETER1
        clrf    MIOS_PARAMETER2
        call    SID_SR_Write
movlw   0x0F
movwf   MIOS_PARAMETER1
movlw   0x03
movwf   MIOS_PARAMETER2
        rgoto   SID_SR_Write
        ;; reset will be released with first call of SID_SR_Handler

;; --------------------------------------------------------------------------
;;  Check for changes in POKEY registers, transfer values to SID
;; --------------------------------------------------------------------------
SID_SR_Handler
        IFSET   SID_STAT, SID_STAT_ENGINE_DISABLE, return

_SID_SR_Handler
SID_SR_Start

        TABLE_ADDR SID_SR_REGWRITE_TABLE    ; contains order of register accesses
        movlw   0x0A                        ; number of registers (0x0A)
        movwf   TMP1                        ; TMP1 is the loop counter
        lfsr    FSR0, SID_BASE              ; store base address of SID registers in FSR0
        lfsr    FSR1, SID_SHADOW_BASE       ; store base address of shadow registers in FSR1
        IRQ_DISABLE                         ; disable interrupts

SID_SR_Loop
        tblrd*+
        movf TABLAT, W
        movff   PLUSW0, MIOS_PARAMETER2     ; store value of SID in MIOS_PARAMETER1 and increment FSR0
        IFSET   SID_STAT, SID_STAT_FORCE_REFRESH, rgoto SID_SR_Transfer; don't skip if refresh has been forced
        movf    PLUSW1, W                   ; get content of appr. shadow register
        IFNEQ   MIOS_PARAMETER2, ACCESS, rgoto SID_SR_Transfer  ; transfer to SID if not equal
        rgoto   SID_SR_Next                 ; skip following code if equal

SID_SR_Transfer
        ;; register change: write value into shadow register and transfer it to SID
        movf TABLAT, W
        movff   MIOS_PARAMETER2, PLUSW1     ; store value in shadow register

        movf    TABLAT, W                   ; extract address offset
        iorlw   0xe0                        ; (note: reset line must stay 1)
        movwf   MIOS_PARAMETER1             ; store in address register
        rcall   SID_SR_Write                ; transfer to SID

SID_SR_Next
        decfsz  TMP1, F                     ; decrement loop counter until it is zero
        rgoto   SID_SR_Loop

        return

;; --------------------------------------------------------------------------
;;  SID Write: write to SID register
;; --------------------------------------------------------------------------
SID_SR_Write
        ;; SID signals:
        ;; MIOS_PARAMETER2[7..0]: Data
        ;; MIOS_PARAMETER1[4..0]: Address
        ;; MIOS_PARAMETER1[5]   : Reset
        ;; temporary used as counter: MIOS_PARAMETER3

        bcf SID_SR_LAT_SCLK, SID_SR_PIN_SCLK     ; clear clock

        ;; superfast transfer with unrolled loop (takes some memory, but guarantees the
        ;; lowest system load :)
SID_SR_WRITE_BIT MACRO reg, bit
        bcf SID_SR_LAT_OUT, SID_SR_PIN_OUT    ; set out pin depending on register content (reg.bit)
        btfsc   reg, bit
        bsf     SID_SR_LAT_OUT, SID_SR_PIN_OUT
        bsf     SID_SR_LAT_SCLK, SID_SR_PIN_SCLK     ; rising clock edge
        bcf     SID_SR_LAT_SCLK, SID_SR_PIN_SCLK     ; falling clock edge
        ENDM

        SID_SR_WRITE_BIT MIOS_PARAMETER2, 0
        SID_SR_WRITE_BIT MIOS_PARAMETER2, 1
        SID_SR_WRITE_BIT MIOS_PARAMETER2, 2
        SID_SR_WRITE_BIT MIOS_PARAMETER2, 3
        SID_SR_WRITE_BIT MIOS_PARAMETER2, 4
        SID_SR_WRITE_BIT MIOS_PARAMETER2, 5
        SID_SR_WRITE_BIT MIOS_PARAMETER2, 6
        SID_SR_WRITE_BIT MIOS_PARAMETER2, 7

        SID_SR_WRITE_BIT MIOS_PARAMETER1, 0
        SID_SR_WRITE_BIT MIOS_PARAMETER1, 1
        SID_SR_WRITE_BIT MIOS_PARAMETER1, 2
        SID_SR_WRITE_BIT MIOS_PARAMETER1, 3
        SID_SR_WRITE_BIT MIOS_PARAMETER1, 4
        SID_SR_WRITE_BIT MIOS_PARAMETER1, 5
        SID_SR_WRITE_BIT MIOS_PARAMETER1, 6
        SID_SR_WRITE_BIT MIOS_PARAMETER1, 7

        bsf SID_SR_LAT_RCLK, SID_SR_PIN_RCLK     ; latch SID values
        bcf     SID_SR_LAT_OUT, SID_SR_PIN_OUT    ; clear out pin (standby)
        bcf     SID_SR_LAT_RCLK, SID_SR_PIN_RCLK     ; release latch

        ;; synchronize with rising edge of SID clock to avoid setup or hold violation
        ;; note: due to pipeline effects, the "bcf" will be executed 3 instruction cycles after
        ;; the polling loop. Therefore we are waiting for the falling edge
        IFCLR   PORTC, 2, bra $-2               ; wait for falling clock edge
        IFSET   PORTC, 2, bra $-2
        bcf     SID_SR_LAT_WR, SID_SR_PIN_WR      ; enable write (MBHP_SID: chip select)
        bra     $+2                               ; to ensure compatibility with on-board oscillator,
        bra     $+2                               ; wait for 1.2 uS (> one SID clock cycle)
        bra     $+2
        bra     $+2
        bra     $+2
        bra     $+2
        bsf  SID_SR_LAT_WR, SID_SR_PIN_WR      ; disable write (MBHP_SID: chip select)
        return

;; --------------------------------------------------------------------------
;;  SID Phase sync: called by the SID_SR_Handler after a register update
;;  expects pointer to SID_Vx_CTRL in FSR0 and to the appr. shadow register in FSR1
;;  checks if test bit set - if so, clears the test bit, sets the gate bit
;;  and transfers the updated control register to the SID
;; --------------------------------------------------------------------------
SID_SR_OscPhaseSync
        IFCLR INDF0, 3, return            ; no phase sync if test bit not set

        bcf     INDF0, 3                    ; clear test bit
        bsf     INDF0, 0                    ; set gate bit

        ;; transfer new value into shadow register
        movf    INDF0, W                    ; transfer new value into shadow register
        movwf   INDF1

        ;; and transfer it to the SID
        movwf   MIOS_PARAMETER2             ; data
        movf    FSR0L, W                    ; extract address offset
        addlw   -(SID_BASE & 0xff)
        iorlw   0xe0                        ; (note: reset line must stay 1)
        movwf   MIOS_PARAMETER1             ; address
        rgoto   SID_SR_Write

;; --------------------------------------------------------------------------
;;  This routine writes to the SR register
;;  used by SID_SW_Note in sid_sw.inc
;;  expecting offset to AD register in WREG, and data in MIOS_PARAMETER2
;; --------------------------------------------------------------------------
SID_SR_Write_SR
        iorlw   0xe0                        ; refresh SID AD register
        movwf   MIOS_PARAMETER1             ; address
        rgoto   SID_SR_Write

;; --------------------------------------------------------------------------
SID_SR_REGWRITE_TABLE
        ;; order in which the SID registers are written
        ;; note that the control register (which contains the gate flag) is not written before
        ;; the other OSC registers have been initialized
        db        0x01, 0x00, 0x03, 0x02, 0x05, 0x04
        db        0x07, 0x06, 0x08, 0x0F, 0x0c, 0x0d
[/code]

Link to comment
Share on other sites

So, the bottom line is that I think adding a single IC (a dual D-Flip-Flop (i.e. 74HC74)) to the circuit is the best and easiest solution.

Sorry to drop in with more hardware stuff, but might i suggest a 74HC393 Dual 4-bit Binary Counter instead of a dual d-flip-flop.

as there are two four-bit counters, you can divide the clock source by up to 256. I guess this means you could use a clock source up to tens or hundreds of MHz... I'm just saying this because pitch seems to be a serious concern with this chip.

Link to comment
Share on other sites

/tilted/: Right. We could do that, but since there are 2x[desiredMhz] and 8x[desiredMhz] oscillators all over the place I don't think, that this will be necessary. I'll probably replace the Dual Flip Flop with Quad Flip Flop and some jumpers to allow using 1x, 2x, 4x and 8x [desiredMhz] oscillators.

It's a matter of taste really. But this should give us enough room to optimize the circuit =)

Link to comment
Share on other sites

Hi Nils,

I know you are just getting the single POKEY implementation down, but Atari developed a Quad Pokey chip (and the ever popular Quad Pokey eliminator board which replaced the single Quad Pokey chip with four individual Pokeys). Four Pokeys can do quite a bit sonically. Please investigate :)

Please see the MAME source code for the Pokey implementation. There are some undocumented commands, etc.

http://www.mamedev.org/source/src/emu/sound/pokey.txt.html

http://www.mamedev.org/source/src/emu/sound/pokey.h.html

http://www.mamedev.org/source/src/emu/sound/pokey.c.html

The SID stuff is also interesting...

http://www.mamedev.org/source/src/emu/sound/index.html

tm

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share


×
×
  • Create New...