Browse Source

First Commit

Godzil 5 years ago
commit
daee86c057
5 changed files with 521 additions and 0 deletions
  1. 200 0
      AY3910RegWrite/AY3910RegWrite.ino
  2. 3 0
      Makefile
  3. 68 0
      Readme.txt
  4. BIN
      Thrust.mym
  5. 250 0
      mym2serial.c

+ 200 - 0
AY3910RegWrite/AY3910RegWrite.ino

@@ -0,0 +1,200 @@
+/*******************************************************************
+ *               AY-3-3910 Register writer for Arduino
+ *                 (c) 2014 Manoel "Godzil" Trapier
+ *
+ * All the code is made by me apart from the the timer code that 
+ * was inspired from code found on the internet. I'm sorry, I can't
+ * remmember where.
+ **************************** Licence ******************************
+ * This file is licenced under the licence:
+ *                    WTFPL v2 Postal Card Edition:
+ *
+ *             DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ *                    Version 2, December 2004
+ *
+ * Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
+ *
+ * Everyone is permitted to copy and distribute verbatim or modified
+ * copies of this license document, and changing it is allowed as long
+ * as the name is changed.
+ *
+ *            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ *   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+ *
+ *  0. You just DO WHAT THE FUCK YOU WANT TO.
+ *  1. If you like this software you can send me a (virtual) postals
+ *     card. Details bellow:
+ *
+ *             < godzil-nospambot at godzil dot net >
+ *
+ * If you wan't to send a real postal card, send me an email, I'll
+ * give you my address. Of course remove the -nospambot from my
+ * e-mail address.
+ *
+ ******************************************************************/
+
+const int freqOutputPin = 11;   // OC2A output pin for ATmega328 boards
+// Constants are computed at compile time
+// If you change the prescale value, it affects CS22, CS21, and CS20
+// For a given prescale value, the eight-bit number that you
+// load into OCR2A determines the frequency according to the
+// following formulas:
+//
+// With no prescaling, an ocr2val of 3 causes the output pin to
+// toggle the value every four CPU clock cycles. That is, the
+// period is equal to eight slock cycles.
+//
+// With F_CPU = 16 MHz, the result is 2 MHz.
+//
+// Note that the prescale value is just for printing; changing it here
+// does not change the clock division ratio for the timer!  To change
+// the timer prescale division, use different bits for CS22:0 below
+const int ocr2aval  = 3; 
+// The following are scaled for convenient printing
+//
+void setup_clock()
+{
+    pinMode(freqOutputPin, OUTPUT); 
+    // Set Timer 2 CTC mode with no prescaling.  OC2A toggles on compare match
+    //
+    // WGM22:0 = 010: CTC Mode, toggle OC 
+    // WGM2 bits 1 and 0 are in TCCR2A,
+    // WGM2 bit 2 is in TCCR2B
+    // COM2A0 sets OC2A (arduino pin 11 on Uno or Duemilanove) to toggle on compare match
+    //
+    TCCR2A = ((1 << WGM21) | (1 << COM2A0));
+    // Set Timer 2  No prescaling  (i.e. prescale division = 1)
+    //
+    // CS22:0 = 001: Use CPU clock with no prescaling
+    // CS2 bits 2:0 are all in TCCR2B
+    TCCR2B = (1 << CS20);
+    // Make sure Compare-match register A interrupt for timer2 is disabled
+    TIMSK2 = 0;
+    // This value determines the output frequency
+    OCR2A = ocr2aval;
+}
+
+enum { INACTIVE = B00, READ = B01, WRITE = B10, ADDRESS = B11};
+void setup_data(int mode)
+{
+  switch(mode)
+  {
+    default:
+    case READ:
+    case INACTIVE:
+      DDRD = B00000000; // Set all D port as input
+      DDRB &= ~0x03;
+      break;
+    case ADDRESS:
+    case WRITE:
+      DDRD = B11111111; // Set all D port as output
+      DDRB |= 0x03;
+      break;
+  }
+}
+
+void setup_control()
+{
+  DDRC = DDRC | B00000011;
+  PORTC &= ~B00000011;
+}
+
+void set_control(int mode)
+{
+  PORTC = (PORTC & 111111100) | (mode);
+}
+
+void SetData(unsigned char data)
+{
+  PORTD = data & 0xFC;
+  PORTB = data & 0x03;
+} 
+ 
+unsigned char GetData(void)
+{
+  return (PORTD & 0xFC) | (PORTB & 0x03); 
+}
+  
+
+/* Registers */
+enum
+{
+  REG_FREQ_A_LO = 0,
+  REG_FREQ_A_HI,
+  REG_FREQ_B_LO,
+  REG_FREQ_B_HI,
+  REG_FREQ_C_LO,
+  REG_FREQ_C_HI,
+  
+  REG_FREQ_NOISE,
+  REG_IO_MIXER,
+  
+  REG_LVL_A,
+  REG_LVL_B,
+  REG_LVL_C,
+  
+  REG_FREQ_ENV_LO,
+  REG_FREQ_ENV_HI,
+  REG_ENV_SHAPE,
+  
+  REG_IOA,
+  REG_IOB
+};
+
+
+
+void write_2149_reg(uint8_t reg, uint8_t value)
+{
+  setup_data(ADDRESS);
+  SetData(reg & 0x0F);
+  set_control(ADDRESS);
+  delayMicroseconds(3);
+  set_control(INACTIVE);
+
+  delayMicroseconds(1);
+  setup_data(WRITE);
+  SetData(value);
+  delayMicroseconds(1);
+    
+  set_control(WRITE);
+  
+  delayMicroseconds(5);
+  
+  set_control(INACTIVE);
+  PORTD = 0;
+  //setup_data(INACTIVE);
+}
+
+uint8_t read_2149_reg(uint8_t reg)
+{
+  uint8_t ret = 0;
+
+  return ret;
+}
+
+char regs[14];
+int j;
+
+void setup()
+{
+  setup_clock();
+  setup_control();
+  setup_data(INACTIVE);
+ 
+  Serial.begin(115200);
+  // Be sure to kill all possible sound by setting volume to zero
+  write_2149_reg(REG_LVL_A, B00000000);
+  write_2149_reg(REG_LVL_B, B00000000);
+  write_2149_reg(REG_LVL_C, B00000000);
+  j = 0;
+}
+
+void loop()
+{
+  int i;
+  while (Serial.readBytes(regs, 14) != 14);
+  for(i = 0; i < 14; i++)
+  {
+    write_2149_reg(i, regs[i]);
+  }
+}

+ 3 - 0
Makefile

@@ -0,0 +1,3 @@
+mym2serial: mym2serial.c
+	$(CC) $< -O3 -g -o $@
+ 

+ 68 - 0
Readme.txt

@@ -0,0 +1,68 @@
+/*******************************************************************
+ *                     MYM Player to Serial port                   *
+ *                 (c) 2014 Manoel "Godzil" Trapier                *
+ *******************************************************************/
+ 
+First for the format things, this project is licensed under the
+
+                    WTFPL v2 Postal Card Edition:
+
+             DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+                    Version 2, December 2004
+
+ Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
+
+ Everyone is permitted to copy and distribute verbatim or modified
+ copies of this license document, and changing it is allowed as long
+ as the name is changed.
+
+            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. You just DO WHAT THE FUCK YOU WANT TO.
+  1. If you like this software you can send me a (virtual) postals
+     card. Details bellow:
+
+             < godzil-nospambot at godzil dot net >
+
+ If you wan't to send a real postal card, send me an email, I'll
+ give you my address. Of course remove the -nospambot from my
+ e-mail address.
+ 
+*********************************************************************
+
+Now the formal things are done.
+
+The small project is to provide a simple and easy way to play YM
+files on a computer using the less possible component.
+
+It is currently only using a resistor, a AY-3-3910 (but any version
+is fine for playing music), a femal audio jack connector to let plug
+something to ear the nice sound, an Arduino and a buch of wire,
+nothing more.
+
+Most project I saw before was using a quartz to provide a clock to
+the AY, but this is just useless and expensive when you have a µC
+with timers :)
+
+As the Arduino does not have enought memory to store teh YM file,
+it is somewhat streamed from the PC trought the USB cable.
+
+A Fritzing schematic is not ready yet but will come ASAP.
+
+This project may evolve in a fully autonoous Arduino capable of playing
+YM/MYM file without the need of a PC streaming. I may use the
+Gameduino2 for it's the display and SD part.
+
+*********************************************************************
+
+I've included a simple MYM file with this project: Thrust.mym
+It's the Title music of the game Thrust on the Atari ST, music
+composed by Rob Hubbard.
+
+To convert ym to mym I recommend the ym2mym tool that come with the
+OSDK (Oric SDK) that can be found here: http://osdk.defence-force.org
+
+Enjoy!
+
+Godzil / Manoel

BIN
Thrust.mym


+ 250 - 0
mym2serial.c

@@ -0,0 +1,250 @@
+/*******************************************************************
+ *                     MYM Player to Serial port
+ *                 (c) 2014 Manoel "Godzil" Trapier
+ *
+ * This file is base on the mym2ym by 
+ * Marq/Lieves!Tuore & Fit (marq@iki.fi)
+ *
+ * The YM writing part have been removed and replaced by code to 
+ * send register dump to the serial port.
+ **************************** Licence ******************************
+ * This file is licenced under the licence:
+ *                    WTFPL v2 Postal Card Edition:
+ *
+ *             DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ *                    Version 2, December 2004
+ *
+ * Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
+ *
+ * Everyone is permitted to copy and distribute verbatim or modified
+ * copies of this license document, and changing it is allowed as long
+ * as the name is changed.
+ *
+ *            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ *   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+ *
+ *  0. You just DO WHAT THE FUCK YOU WANT TO.
+ *  1. If you like this software you can send me a (virtual) postals
+ *     card. Details bellow:
+ *
+ *             < godzil-nospambot at godzil dot net >
+ *
+ * If you wan't to send a real postal card, send me an email, I'll
+ * give you my address. Of course remove the -nospambot from my
+ * e-mail address.
+ *
+ ******************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <errno.h>
+
+#define REGS 14
+#define FRAG 128    /*  Nuber of rows to compress at a time   */
+#define OFFNUM 14   /*  Bits needed to store off+num of FRAG  */
+
+unsigned readbits(int bits,FILE *f);
+
+int set_interface_attribs (int fd, int speed, int parity)
+{
+    struct termios tty;
+    memset (&tty, 0, sizeof tty);
+    if (tcgetattr (fd, &tty) != 0)
+    {
+        printf("error %d from tcgetattr", errno);
+        return -1;
+    }
+
+    cfsetospeed (&tty, speed);
+    cfsetispeed (&tty, speed);
+
+    tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars
+    // disable IGNBRK for mismatched speed tests; otherwise receive break
+    // as \000 chars
+    tty.c_iflag &= ~IGNBRK;         // disable break processing
+    tty.c_lflag = 0;                // no signaling chars, no echo,
+                                    // no canonical processing
+    tty.c_oflag = 0;                // no remapping, no delays
+    tty.c_cc[VMIN]  = 0;            // read doesn't block
+    tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout
+
+    tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
+
+    tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
+                                        // enable reading
+    tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
+    tty.c_cflag |= parity;
+    tty.c_cflag &= ~CSTOPB;
+    tty.c_cflag &= ~CRTSCTS;
+
+    if (tcsetattr (fd, TCSANOW, &tty) != 0)
+    {
+        printf ("error %d from tcsetattr", errno);
+        return -1;
+    }
+    return 0;
+}
+
+void set_blocking (int fd, int should_block)
+{
+    struct termios tty;
+    memset (&tty, 0, sizeof tty);
+    if (tcgetattr (fd, &tty) != 0)
+    {
+        printf ("error %d from tggetattr", errno);
+        return;
+    }
+
+    tty.c_cc[VMIN]  = should_block ? 1 : 0;
+    tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout
+
+    if (tcsetattr (fd, TCSANOW, &tty) != 0)
+        printf ("error %d setting term attributes", errno);
+}
+
+int main(int argc,char *argv[])
+{
+    unsigned char   *data[REGS],    /*  The unpacked YM data    */
+                    c;
+    int serial_fd;
+
+
+    unsigned    current[REGS];
+
+    FILE    *f;
+
+    long    n,i,row,index,compoff,compnum,
+            bytes=0,
+            regbits[REGS]={8,4,8,4, 8,4,5,8, 5,5,5,8, 8,8}; /* Bits per PSG reg */
+
+    unsigned long   rows;
+
+    if(argc!=3)
+    {
+        printf("Usage: mym2serial source.mym /dev/serial\n");
+        return(EXIT_FAILURE);
+    }
+
+    if((f=fopen(argv[1],"rb"))==NULL)
+    {
+        printf("File open error.\n");
+        return(EXIT_FAILURE);
+    }
+
+    rows=fgetc(f);    /*  Read the number of rows */
+    rows+=fgetc(f)<<8u;
+
+    for(n=0;n<REGS;n++)     /*  Allocate memory for rows    */
+    {
+        if((data[n]=(unsigned char *)malloc(rows+FRAG))==NULL)
+        {
+            printf("Out of memory.\n");
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    for(n=0;n<rows;n+=FRAG) /*  Go through rows...  */
+    {
+        for(i=0;i<REGS;i++) /*  ... and registers   */
+        {
+            index=0;
+            if(!readbits(1,f))  /*  Totally unchanged fragment */
+            {
+                for(row=0;row<FRAG;row++)
+                    data[i][n+row]=current[i];
+                continue;
+            }
+
+            while(index<FRAG)   /*  Packed fragment */
+            {
+                if(!readbits(1,f))  /*  Unchanged register  */
+                {
+                    
+                    data[i][n+index]=current[i];
+                    index++;
+                }
+                else
+                {
+                    if(readbits(1,f))   /*  Raw data    */
+                    {
+                        c=readbits(regbits[i],f);
+                        current[i]=data[i][n+index]=c;
+                        index++;
+                    }
+                    else    /*  Reference to previous data */
+                    {
+                        compoff=readbits(OFFNUM/2,f)+index;
+                        compnum=readbits(OFFNUM/2,f)+1;
+
+                        for(row=0;row<compnum;row++)
+                        {
+                            c=data[i][n-FRAG+compoff+row];
+                            data[i][n+index]=current[i]=c;
+                            index++;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    fclose(f);
+
+    serial_fd = open (argv[2], O_RDWR | O_NOCTTY | O_SYNC);
+    if (serial_fd > 0)
+    {
+        char buffer[14];
+        printf("Serial opened...\n");
+        set_interface_attribs (serial_fd, B115200, 0); // 115200 8n1
+        set_blocking (serial_fd, 0);
+        /* Wait a little bit to be sure that the other end is ready... */
+        sleep(2);
+        printf("Playing now!\n");
+        for(n=0;n<rows;n++)
+        {
+            for(i=0;i<REGS;i++)
+            {
+                buffer[i] = data[i][n];
+            }
+            write(serial_fd, buffer, 14);
+            // 50Hz so wait a little bit...
+            printf("VBL%ld\n", n);
+            usleep(16*1000);
+        }
+    }
+    else
+    {
+        printf("Opening error...\n");
+    }
+
+    return(EXIT_SUCCESS);
+}
+
+/*  Reads bits from a while */
+unsigned readbits(int bits,FILE *f)
+{
+    static unsigned char    byte;
+
+    static int  off=7;
+
+    unsigned    n,data=0;
+
+    /* Go through the bits and read a whole byte if needed */
+    for(n=0;n<bits;n++) 
+    {
+        data<<=1;
+        if(++off==8)
+        {
+            byte=fgetc(f);
+            off=0;
+        }
+        
+        if(byte&(0x80>>off))
+            data++;
+    }
+    return(data);
+}