Platform library for RETRO

Dependents:   RETRO_RickGame

Files at this revision

API Documentation at this revision

Comitter:
Architect
Date:
Sun Mar 01 05:29:45 2015 +0000
Commit message:
RetroPlatform Library for RETRO gaming system

Changed in this revision

Display.cpp Show annotated file Show diff for this revision Revisions of this file
Display.h Show annotated file Show diff for this revision Revisions of this file
Retro.cpp Show annotated file Show diff for this revision Revisions of this file
Retro.h Show annotated file Show diff for this revision Revisions of this file
Sound.cpp Show annotated file Show diff for this revision Revisions of this file
Sound.h Show annotated file Show diff for this revision Revisions of this file
Utils.cpp Show annotated file Show diff for this revision Revisions of this file
Utils.h Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r 6f26c31d8573 Display.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Display.cpp	Sun Mar 01 05:29:45 2015 +0000
@@ -0,0 +1,142 @@
+/*
+ * (C) Copyright 2015 Valentin Ivanov. All rights reserved.
+ *
+ * This file is part of the RetroPlatform Library
+ *
+ * The RetroPlatform Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ * This library is inspired by Gamebuino Library (http://gamebuino.com)
+ * from Aurélien Rodot. 
+ */
+#include "mbed.h"
+#include "Display.h"
+
+Display::Display( PinName backlightPin, PinName resetPin, PinName dsPin,
+                  PinName mosiPin, PinName misoPin, PinName clkPin, PinName csPin, PanelColorFilter colorFilter )
+    : LCD_ST7735(backlightPin, resetPin, dsPin, mosiPin, misoPin, clkPin, csPin, colorFilter )
+{
+}
+
+
+void Display::drawBitmapIndexed(uint8_t x, uint8_t y, const uint8_t *pbmp)
+{
+    drawBitmapIndexed(x, y, pbmp[0], pbmp[1],  pbmp+2);
+}
+
+void Display::drawBitmapIndexed(uint8_t x, uint8_t y, uint8_t w, uint8_t h,  const uint8_t *pbmp)
+{
+    clip(x, y, w, h);
+    int bytes = w * h / 8;
+    beginBatchCommand(CMD_RAMWR);
+
+    while(bytes--) {
+//        writeBatchData(_pPalette[*pbmp>>7&0x1]);
+//        writeBatchData(_pPalette[*pbmp>>6&0x1]);
+//        writeBatchData(_pPalette[*pbmp>>5&0x1]);
+//        writeBatchData(_pPalette[*pbmp>>4&0x1]);
+//        writeBatchData(_pPalette[*pbmp>>3&0x1]);
+//        writeBatchData(_pPalette[*pbmp>>2&0x1]);
+//        writeBatchData(_pPalette[*pbmp>>1&0x1]);
+//        writeBatchData(_pPalette[(*pbmp++)&0x1]);
+    }
+    endBatchCommand();
+}
+
+void Display::drawBitmapIndexed(uint8_t x, uint8_t y, uint8_t w, uint8_t h,  const uint8_t *pbmp, const uint16_t *palette, bool mirrored)
+{
+    clip(x, y, w, h);
+    int pixels = w * h;
+    beginBatchCommand(CMD_RAMWR);
+
+    if( mirrored ) {
+        for( int iy =0; iy < h; iy++ ) {
+            const uint8_t * p  = pbmp + (w>>1)*iy + (w>>1)-1;
+            for( int ix =0; ix < w; ) {
+                writeBatchData(palette[(*p)&0x0F]);
+                writeBatchData(palette[((*p)>>4)&0x0F]);
+                ix+=2;
+                p--;
+            }
+        }
+    } else {
+        while(pixels) {
+            writeBatchData(palette[((*pbmp)>>4)&0x0F]);
+            writeBatchData(palette[(*pbmp)&0x0F]);
+            pixels-=2;
+            pbmp++;
+        }
+    }
+    endBatchCommand();
+}
+
+//void Display::drawBitmapTransparent(uint8_t x, uint8_t y, uint8_t w, uint8_t h,  const uint8_t *pbmp, const uint16_t *palette, uint8_t color, bool mirrored )
+//{
+//    clip(x, y, w, h);
+//    int pixels = w * h;
+//    beginBatchCommand(CMD_RAMWR);
+//
+//    if( mirrored ) {
+//        for( int iy =0; iy < h; iy++ ) {
+//            const uint8_t * p  = pbmp + w*iy + w-1;
+//            for( int ix =0; ix < w; ix++ ) {
+//            {
+//                writeBatchData(palette[*p--]);
+//             }
+//            }
+//        }
+//    } else {
+//        while(pixels--) {
+//            writeBatchData(palette[*pbmp++]);
+//        }
+//    }
+//    endBatchCommand();
+//}
+
+void Display::drawBitmapIndexed(uint8_t x, uint8_t y, int w, int h, int src_x, int src_y, int srcWidth, int srcHeight, const uint8_t *pbmp, const uint16_t *palette)
+{
+    clip(x, y, srcWidth, srcHeight);
+    beginBatchCommand(CMD_RAMWR);
+    const uint8_t *p = pbmp + src_x + (src_y * w);
+    for(int iy = 0; iy < srcHeight; ++iy) {
+        for(int ix = 0; ix < srcWidth; ++ix) {
+            writeBatchData(palette[*(p + ix)]);
+        }
+        p += w;
+    }
+    endBatchCommand();
+}
+
+void Display::eraseBitmapIndexed(uint8_t x, uint8_t y, const uint8_t *pbmp)
+{
+    eraseBitmapIndexed(x, y, pbmp[0], pbmp[1],  pbmp+2);
+}
+
+void Display::eraseBitmapIndexed(uint8_t x, uint8_t y, uint8_t w, uint8_t h,  const uint8_t *pbmp)
+{
+    clip(x, y, w, h);
+    int bytes = w * h / 8;
+    beginBatchCommand(CMD_RAMWR);
+
+    while(bytes--) {
+//        writeBatchData(_pPalette[0]);
+//        writeBatchData(_pPalette[0]);
+//        writeBatchData(_pPalette[0]);
+//        writeBatchData(_pPalette[0]);
+//        writeBatchData(_pPalette[0]);
+//        writeBatchData(_pPalette[0]);
+//        writeBatchData(_pPalette[0]);
+//        writeBatchData(_pPalette[0]);
+    }
+    endBatchCommand();
+}
\ No newline at end of file
diff -r 000000000000 -r 6f26c31d8573 Display.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Display.h	Sun Mar 01 05:29:45 2015 +0000
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright 2015 Valentin Ivanov. All rights reserved.
+ *
+ * This file is part of the RetroPlatform Library
+ *
+ * The RetroPlatform Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ * This library is inspired by Gamebuino Library (http://gamebuino.com)
+ * from Aurélien Rodot. 
+ */
+#ifndef DISPLAY_H
+#define DISPLAY_H
+
+#include "LCD_ST7735.h"
+
+
+class Display : public LCD_ST7735
+{
+
+public:
+    Display(
+        PinName backlightPin,
+        PinName resetPin,
+        PinName dsPin,
+        PinName mosiPin,
+        PinName misoPin,
+        PinName clkPin,
+        PinName csPin,
+        PanelColorFilter colorFilter = BGR
+    );
+
+    void drawBitmapIndexed(uint8_t x, uint8_t y, uint8_t w, uint8_t h,  const uint8_t *pbmp);
+    void drawBitmapIndexed(uint8_t x, uint8_t y, uint8_t w, uint8_t h,  const uint8_t *pbmp, const uint16_t *palette, bool mirrored = false);
+    //void drawBitmapTransparent(uint8_t x, uint8_t y, uint8_t w, uint8_t h,  const uint8_t *pbmp, const uint16_t *palette, uint8_t color, bool mirrored = false);
+    void drawBitmapIndexed(uint8_t x, uint8_t y, int w, int h, int src_x, int src_y, int srcWidth, int srcHeight, const uint8_t *pbmp, const uint16_t *palette);
+    
+    void drawBitmapIndexed(uint8_t x, uint8_t y, const uint8_t *pbmp);
+    void eraseBitmapIndexed(uint8_t x, uint8_t y, uint8_t w, uint8_t h,  const uint8_t *pbmp);
+    void eraseBitmapIndexed(uint8_t x, uint8_t y, const uint8_t *pbmp);
+};
+
+
+#endif //DISPLAY_H
\ No newline at end of file
diff -r 000000000000 -r 6f26c31d8573 Retro.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Retro.cpp	Sun Mar 01 05:29:45 2015 +0000
@@ -0,0 +1,179 @@
+/*
+ * (C) Copyright 2015 Valentin Ivanov. All rights reserved.
+ *
+ * This file is part of the RetroPlatform Library
+ *
+ * The RetroPlatform Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ * This library is inspired by Gamebuino Library (http://gamebuino.com)
+ * from Aurélien Rodot. 
+ */
+#include "Retro.h"
+#include "us_ticker_api.h"
+
+const uint16_t OKSequence[]  =           {0x0005,0x138,0x168,0x0000};
+const uint16_t CancelSequence[]  =       {0x0005,0x168,0x138,0x0000};
+const uint16_t TickSequence[]  =               {0x0045,0x168,0x0000};
+
+DigitalIn Retro::pin[NUM_BTN] = {
+    DigitalIn(P0_14, PullUp), //left
+    DigitalIn(P0_11, PullUp), //right
+    DigitalIn(P0_12, PullUp), //down
+    DigitalIn(P0_13, PullUp), //up
+    DigitalIn(P0_16, PullUp), //robot
+    DigitalIn(P0_1, PullUp)   //ship
+};
+
+Retro::Retro(): display(
+        P0_19,
+        P0_20,
+        P0_7,
+        P0_21,
+        P0_22,
+        P1_15,
+        P0_2,
+        LCD_ST7735::RGB), leftEye(P0_9,false), rightEye(P0_8,false)
+{
+    initialize();
+}
+
+
+void Retro::initialize()
+{
+    timePerFrame = 50000;
+    frameStartUs = us_ticker_read();
+
+    display.setOrientation(LCD_ST7735::Rotate270, false);
+    display.setForegroundColor(Color565::White);
+    display.setBackgroundColor(Color565::Black);
+
+    sound.initialize();
+    sound.setVolume(1);
+
+    setFrameRate(20);
+}
+
+void Retro::setFrameRate(uint8_t fps)
+{
+    timePerFrame = 1000000 / fps;
+    sound.prescaler = fps / 20;
+    sound.prescaler = max(1, sound.prescaler);
+}
+
+bool once = false;
+
+bool Retro::update()
+{
+    uint32_t current_time = us_ticker_read();
+    frameDurationUs = current_time - frameStartUs;
+
+    if( frameDurationUs > timePerFrame ) {
+        frameStartUs = current_time;
+
+
+        once = false;
+
+        readButtons();
+        return true;
+    } else {
+        if( !once ) {
+            once = true;
+            sound.update();
+
+        }
+        return false;
+    }
+}
+
+void Retro::playOK()
+{
+    sound.playSequence(OKSequence,0);
+}
+
+void Retro::playCancel()
+{
+    sound.playSequence(CancelSequence,0);
+}
+
+void Retro::playTick()
+{
+    sound.playSequence(TickSequence,0);
+}
+
+bool Retro::collideCheck( int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2)
+{
+    return !( x2 >=  x1+w1  || x1 >= x2+w2  || y2 >=  y1+h1  || y1 >= y2 + h2 );
+}
+
+//Buttons
+void Retro::readButtons()
+{
+    for (uint8_t thisButton = 0; thisButton < NUM_BTN; thisButton++) {
+        if ( pin[thisButton].read() == 0 ) {
+            _state[thisButton]++;
+        } else {
+            if (_state[thisButton] == 0)
+                continue;
+            if (_state[thisButton] == 0xFF)
+                _state[thisButton] = 0;
+            else
+                _state[thisButton] = 0xFF;
+        }
+    }
+}
+
+bool Retro::pressed(uint8_t button)
+{
+    if (_state[button] == 1)
+        return true;
+    else
+        return false;
+}
+
+bool Retro::released(uint8_t button)
+{
+    if (_state[button] == 0xFF)
+        return true;
+    else
+        return false;
+}
+
+bool Retro::held(uint8_t button, uint8_t time)
+{
+    if(_state[button] == (time+1))
+        return true;
+    else
+        return false;
+}
+
+bool Retro::repeat(uint8_t button, uint8_t period)
+{
+    if (period <= 1) {
+        if ((_state[button] != 0xFF) && (_state[button]))
+            return true;
+    } else {
+        if ((_state[button] != 0xFF) && ((_state[button] % period) == 1))
+            return true;
+    }
+    return false;
+}
+
+uint8_t Retro::timeHeld(uint8_t button)
+{
+    if(_state[button] != 0xFF)
+        return _state[button];
+    else
+        return 0;
+
+}
\ No newline at end of file
diff -r 000000000000 -r 6f26c31d8573 Retro.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Retro.h	Sun Mar 01 05:29:45 2015 +0000
@@ -0,0 +1,87 @@
+/*
+ * (C) Copyright 2015 Valentin Ivanov. All rights reserved.
+ *
+ * This file is part of the RetroPlatform Library
+ *
+ * The RetroPlatform Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ * This library is inspired by Gamebuino Library (http://gamebuino.com)
+ * from Aurélien Rodot. 
+ */
+#ifndef __RETRO_H__
+#define __RETRO_H__
+
+#include "mbed.h"
+#include "Sound.h"
+#include "Display.h"
+#include "Color565.h"
+
+#define LCDWIDTH    160
+#define LCDHEIGHT   128
+
+#define BTN_LEFT    0
+#define BTN_RIGHT   1
+#define BTN_DOWN    2
+#define BTN_UP      3
+#define BTN_ROBOT   4
+#define BTN_SHIP    5
+
+#define NUM_BTN     6
+
+class Retro
+{
+private:
+    
+    uint32_t frameStartUs, frameEndUs;
+    
+private:
+    static DigitalIn pin[NUM_BTN];
+    uint8_t _state[NUM_BTN];    
+        
+public:
+    uint32_t timePerFrame;
+    uint32_t frameDurationUs;
+
+    Sound   sound;
+    Display display;
+    
+    DigitalOut leftEye;
+    DigitalOut rightEye;
+    
+public:
+    Retro();
+    void initialize();
+    bool update();
+    
+    void setFrameRate(uint8_t fps);        
+    static bool collideCheck( int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2);
+    
+    void playOK();
+    void playCancel();
+    void playTick();
+    
+    void readButtons();
+    bool pressed(uint8_t button);
+    bool released(uint8_t button);
+    bool held(uint8_t button, uint8_t time);
+    bool repeat(uint8_t button, uint8_t period);
+    uint8_t timeHeld(uint8_t button);
+    
+};
+
+
+uint8_t max(uint8_t val1, uint8_t val2 );
+uint8_t min(uint8_t val1, uint8_t val2 );
+
+#endif  //__RETRO_H__
\ No newline at end of file
diff -r 000000000000 -r 6f26c31d8573 Sound.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sound.cpp	Sun Mar 01 05:29:45 2015 +0000
@@ -0,0 +1,515 @@
+/*
+ * (C) Copyright 2015 Valentin Ivanov. All rights reserved.
+ *
+ * This file is part of the RetroPlatform Library
+ *
+ * The RetroPlatform Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ * This library is inspired by Gamebuino Library (http://gamebuino.com)
+ * from Aurélien Rodot. 
+ */
+#include "Sound.h"
+#include "Utils.h"
+
+#if(NUM_CHANNELS > 0)
+uint8_t _rand = 1;
+const uint16_t squareWaveInstrument[] = {0x0101, 0x03F7};
+const uint16_t noiseInstrument[] = {0x0101, 0x03FF};
+const uint16_t* const defaultInstruments[] = {squareWaveInstrument,noiseInstrument};
+
+#define NUM_PITCH 59
+
+const uint8_t _halfPeriods[NUM_PITCH] = {
+    /*268,*/253,239,225,213,201,190,179,169,159,150,142,
+    134,127,119,113,106,100,95,89,84,80,75,71,67,63,60,
+    56,53,50,47,45,42,40,38,36,34,32,30,28,27,25,24,22,
+    21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6
+};
+
+#endif
+
+Sound::Sound() : speaker(P0_18)
+{
+    initialize();
+}
+
+void Sound::initialize()
+{
+#if(NUM_CHANNELS > 0)
+    volumeMax = VOLUME_GLOBAL_MAX;
+    globalVolume = VOLUME_GLOBAL_MAX;
+    prescaler = 1;
+
+    speaker.period_us(32);
+    speaker.write(0.0);
+
+    for(uint8_t channel=0; channel<NUM_CHANNELS; channel++) {
+        chanVolumes[channel] = VOLUME_CHANNEL_MAX;
+        changeInstrumentSet(defaultInstruments, channel);
+        command(CMD_INSTRUMENT, 0, 0, channel);
+    }
+    timer.attach_us(this, &Sound::generateOutput, 16); //62500Hz
+#endif
+}
+
+void Sound::playTrack(const uint16_t* track, uint8_t channel)
+{
+#if(NUM_CHANNELS > 0)
+    if(channel>=NUM_CHANNELS)
+        return;
+    stopTrack(channel);
+    tracks[channel].Cursor = 0;
+    tracks[channel].Data = (uint16_t*)track;
+    tracks[channel].IsPlaying = true;
+#endif
+}
+
+void Sound::stopTrack(uint8_t channel)
+{
+#if(NUM_CHANNELS > 0)
+    if(channel>=NUM_CHANNELS)
+        return;
+    tracks[channel].IsPlaying = false;
+    stopSequence(channel);
+#endif
+}
+
+void Sound::updateTrack(uint8_t channel)
+{
+#if(NUM_CHANNELS > 0)
+    if(channel>=NUM_CHANNELS)
+        return;
+    if(tracks[channel].IsPlaying && !sequences[channel].IsPlaying) {
+        uint16_t data = tracks[channel].Data[tracks[channel].Cursor];
+        if(data == 0xFFFF) {
+            tracks[channel].IsPlaying = false;
+            return;
+        }
+        uint8_t patternID = data & 0xFF;
+        data >>= 8;
+        patternPitch[channel] = data;
+        playSequence((const uint16_t*)patternSet[channel][patternID], channel);
+        tracks[channel].Cursor++;
+    }
+#endif
+}
+
+
+void Sound::changeSequenceSet(const uint16_t* const* patterns, uint8_t channel)
+{
+#if(NUM_CHANNELS > 0)
+    if(channel>=NUM_CHANNELS)
+        return;
+    patternSet[channel] = (uint16_t**)patterns;
+#endif
+}
+
+void Sound::playSequence(const uint16_t* pattern, uint8_t channel)
+{
+#if(NUM_CHANNELS > 0)
+    if(channel>=NUM_CHANNELS)
+        return;
+    stopSequence(channel);
+    sequences[channel].Data = (uint16_t*)pattern;
+    sequences[channel].Cursor = 0;
+    sequences[channel].IsPlaying = true;
+    noteVolume[channel] = 9;
+    //reinit commands
+    commands[channel].volumeSlideStepDuration = 0;
+    commands[channel].arpeggioStepDuration = 0;
+    commands[channel].tremoloStepDuration = 0;
+#endif
+}
+
+void Sound::changeInstrumentSet(const uint16_t* const* instruments, uint8_t channel)
+{
+#if(NUM_CHANNELS > 0)
+    if(channel>=NUM_CHANNELS)
+        return;
+    instrumentSet[channel] = (uint16_t**)instruments;
+#endif
+}
+
+void Sound::updateSequence(uint8_t channel)
+{
+#if(NUM_CHANNELS > 0)
+    if(channel>=NUM_CHANNELS)
+        return;
+    if(sequences[channel].IsPlaying) {
+        if(noteDuration[channel]==0) { //if the end of the previous note is reached
+
+            uint16_t data =  sequences[channel].Data[sequences[channel].Cursor];
+
+            if(data == 0) { //end of the pattern reached
+                if(sequences[channel].Looping == true) {
+                    sequences[channel].Cursor = 0;
+                    data =  sequences[channel].Data[sequences[channel].Cursor];
+                } else {
+                    sequences[channel].IsPlaying = false;
+                    if(tracks[channel].IsPlaying) { //if this pattern is part of a track, get the next pattern
+                        updateTrack(channel);
+                        data = sequences[channel].Data[sequences[channel].Cursor];
+                    } else {
+                        stopNote(channel);
+                        return;
+                    }
+                }
+            }
+
+            while (data & 0x0001) { //read all commands and instrument changes
+                data >>= 2;
+                uint8_t cmd = data & 0x0F;
+                data >>= 4;
+                uint8_t X = data & 0x1F;
+                data >>= 5;
+                int8_t Y = data - 16;
+                command(cmd,X,Y,channel);
+                sequences[channel].Cursor++;
+                data = sequences[channel].Data[sequences[channel].Cursor];
+            }
+            data >>= 2;
+
+            uint8_t pitch = data & 0x003F;
+            data >>= 6;
+
+            uint8_t duration = data;
+
+            playNote(pitch, duration, channel);
+
+            sequences[channel].Cursor++;
+        }
+    }
+#endif
+}
+
+void Sound::stopSequence(uint8_t channel)
+{
+#if(NUM_CHANNELS > 0)
+    if(channel>=NUM_CHANNELS)
+        return;
+    stopNote(channel);
+    sequences[channel].IsPlaying = false;
+#endif
+}
+
+void Sound::command(uint8_t cmd, uint8_t X, int8_t Y, uint8_t channel)
+{
+#if(NUM_CHANNELS > 0)
+    if(channel>=NUM_CHANNELS)
+        return;
+
+    switch(cmd) {
+        case CMD_VOLUME: //volume
+            X = constrain(X, 0, 10);
+            noteVolume[channel] = X;
+            break;
+        case CMD_INSTRUMENT: //instrument
+            instruments[channel].Data = (uint16_t*)instrumentSet[channel][X];
+            instruments[channel].Length = instruments[channel].Data[0] & 0x00FF; //8 LSB
+            instruments[channel].Length *= prescaler;
+            instruments[channel].Looping = min(instruments[channel].Data[0] >> 8, instruments[channel].Length); //8 MSB - check that the loop is shorter than the instrument length
+            instruments[channel].Looping *= prescaler;
+            break;
+        case CMD_SLIDE: //volume slide
+            commands[channel].volumeSlideStepDuration = X * prescaler;
+            commands[channel].volumeSlideStepSize = Y;
+            break;
+        case CMD_ARPEGGIO:
+            commands[channel].arpeggioStepDuration = X * prescaler;
+            commands[channel].arpeggioStepSize = Y;
+            break;
+        case CMD_TREMOLO:
+            commands[channel].tremoloStepDuration = X * prescaler;
+            commands[channel].tremoloStepSize = Y;
+            break;
+        default:
+            break;
+    }
+#endif
+}
+
+void Sound::playNote(uint8_t pitch, uint8_t duration, uint8_t channel)
+{
+#if(NUM_CHANNELS > 0)
+    if(channel>=NUM_CHANNELS)
+        return;
+
+    //set note
+    notePitch[channel] = pitch;
+    noteDuration[channel] = duration * prescaler;
+    notePlaying[channel] = true;
+
+    //reinit vars
+    instruments[channel].NextChange = 0;
+    instruments[channel].Cursor = 0;
+
+    commands[channel].Counter = 0;
+
+    _chanState[channel] = true;
+#endif
+}
+
+void Sound::stopNote(uint8_t channel)
+{
+#if(NUM_CHANNELS > 0)
+    if(channel>=NUM_CHANNELS)
+        return;
+    notePlaying[channel] = false;
+    //counters
+    noteDuration[channel] = 0;
+    instruments[channel].Cursor = 0;
+    commands[channel].Counter = 0;
+    //output
+    _chanOutput[channel] = 0;
+    _chanOutputVolume[channel] = 0;
+    _chanState[channel] = false;
+    updateOutput();
+#endif
+}
+
+void Sound::updateNote(uint8_t channel)
+{
+#if(NUM_CHANNELS > 0)
+    if(channel>=NUM_CHANNELS)
+        return;
+    if (notePlaying[channel]) {
+
+        if(noteDuration[channel] == 0) {
+            stopNote(channel);
+            return;
+        } else {
+            noteDuration[channel]--;
+        }
+
+        if (instruments[channel].NextChange == 0) {
+
+            //read the step data from the progmem and decode it
+            uint16_t thisStep = instruments[channel].Data[1 + instruments[channel].Cursor];
+
+            stepVolume[channel] = thisStep & 0x0007;
+            thisStep >>= 3;
+
+            uint8_t stepNoise = thisStep & 0x0001;
+            thisStep >>= 1;
+
+            uint8_t stepDuration = thisStep & 0x003F;
+            thisStep >>= 6;
+
+            stepPitch[channel] = thisStep;
+
+            //apply the step settings
+            instruments[channel].NextChange = stepDuration * prescaler;
+
+            _chanNoise[channel] = stepNoise;
+
+            instruments[channel].Cursor++;
+
+            if (instruments[channel].Cursor >= instruments[channel].Length) {
+                if (instruments[channel].Looping) {
+                    instruments[channel].Cursor = instruments[channel].Length - instruments[channel].Looping;
+                } else {
+                    stopNote(channel);
+                }
+            }
+        }
+        instruments[channel].NextChange--;
+
+        commands[channel].Counter++;
+
+        outputPitch[channel] = notePitch[channel] + stepPitch[channel] + patternPitch[channel];
+
+        if(commands[channel].arpeggioStepDuration)
+            outputPitch[channel] += commands[channel].Counter / commands[channel].arpeggioStepDuration * commands[channel].arpeggioStepSize;
+
+        outputPitch[channel] = outputPitch[channel] % NUM_PITCH; //wrap
+
+        //volume
+        outputVolume[channel] = noteVolume[channel];
+        if(commands[channel].volumeSlideStepDuration)
+            outputVolume[channel] += commands[channel].Counter / commands[channel].volumeSlideStepDuration * commands[channel].volumeSlideStepSize;
+
+        if(commands[channel].tremoloStepDuration)
+            outputVolume[channel] += ((commands[channel].Counter / commands[channel].tremoloStepDuration) % 2) * commands[channel].tremoloStepSize;
+
+        outputVolume[channel] = constrain(outputVolume[channel], 0, 9);
+
+        if(notePitch[channel] == 63)
+            outputVolume[channel] = 0;
+
+        __disable_irq();
+        _chanHalfPeriod[channel] = _halfPeriods[outputPitch[channel]];
+        _chanOutput[channel] = _chanOutputVolume[channel] = outputVolume[channel] * globalVolume * chanVolumes[channel] * stepVolume[channel];
+        __enable_irq();
+    }
+#endif
+}
+
+void Sound::setChannelHalfPeriod(uint8_t channel, uint8_t halfPeriod)
+{
+#if(NUM_CHANNELS > 0)
+    if(channel>=NUM_CHANNELS)
+        return;
+    _chanHalfPeriod[channel] = halfPeriod;
+    _chanState[channel] = false;
+    _chanCount[channel] = 0;
+    updateOutput();
+#endif
+}
+
+
+void Sound::generateOutput()
+{
+#if(NUM_CHANNELS > 0)
+    boolean outputChanged = false;
+    //no for loop here, for the performance sake (this function runs 15 000 times per second...)
+    //CHANNEL 0
+    if (_chanOutputVolume[0]) {
+        _chanCount[0]++;
+        if (_chanCount[0] >= _chanHalfPeriod[0]) {
+            outputChanged = true;
+            _chanState[0] = !_chanState[0];
+            _chanCount[0] = 0;
+            if (_chanNoise[0]) {
+                _rand = 67 * _rand + 71;
+                _chanOutput[0] = _rand % _chanOutputVolume[0];
+            }
+        }
+    }
+
+    //CHANNEL 1
+#if (NUM_CHANNELS > 1)
+    if (_chanOutputVolume[1]) {
+        _chanCount[1]++;
+        if (_chanCount[1] >= _chanHalfPeriod[1]) {
+            outputChanged = true;
+            _chanState[1] = !_chanState[1];
+            _chanCount[1] = 0;
+            if (_chanNoise[1]) {
+                _rand = 67 * _rand + 71;
+                _chanOutput[1] = _rand % _chanOutputVolume[1];
+            }
+        }
+    }
+#endif
+
+    //CHANNEL 2
+#if (NUM_CHANNELS > 2)
+    if (_chanOutputVolume[2]) {
+        _chanCount[2]++;
+        if (_chanCount[2] >= _chanHalfPeriod[2]) {
+            outputChanged = true;
+            _chanState[2] = !_chanState[2];
+            _chanCount[2] = 0;
+            if (_chanNoise[2]) {
+                _rand = 67 * _rand + 71;
+                _chanOutput[2] = _rand % _chanOutputVolume[2];
+            }
+        }
+    }
+#endif
+
+    //CHANNEL 3
+#if (NUM_CHANNELS > 3)
+    if (_chanOutputVolume[3]) {
+        _chanCount[3]++;
+        if (_chanCount[3] >= _chanHalfPeriod[3]) {
+            outputChanged = true;
+            _chanState[3] = !_chanState[3];
+            _chanCount[3] = 0;
+            if (_chanNoise[3]) {
+                _rand = 67 * _rand + 71;
+                _chanOutput[3] = _rand % _chanOutputVolume[3];
+            }
+        }
+    }
+#endif
+
+    if (outputChanged) {
+        updateOutput();
+    }
+#endif
+}
+
+void Sound::updateOutput()
+{
+#if(NUM_CHANNELS > 0)
+    uint8_t output = 0;
+
+    //CHANNEL 0
+    if (_chanState[0]) {
+        output += _chanOutput[0];
+    }
+
+    //CHANNEL 1
+#if (NUM_CHANNELS > 1)
+    if (_chanState[1]) {
+        output += _chanOutput[1];
+    }
+#endif
+
+    //CHANNEL 2
+#if (NUM_CHANNELS > 2)
+    if (_chanState[2]) {
+        output += _chanOutput[2];
+    }
+#endif
+
+    //CHANNEL 3
+#if (NUM_CHANNELS > 3)
+    if (_chanState[3]) {
+        output += _chanOutput[3];
+    }
+#endif
+    speaker.write(output/255.0);
+#endif
+}
+
+
+void Sound::setVolume(int8_t volume)
+{
+#if NUM_CHANNELS > 0
+    globalVolume = volume % (volumeMax+1);
+#endif
+}
+
+uint8_t Sound::getVolume()
+{
+#if NUM_CHANNELS > 0
+    return globalVolume;
+#else
+    return 0;
+#endif
+}
+
+void Sound::setVolume(int8_t volume, uint8_t channel)
+{
+#if(NUM_CHANNELS > 0)
+    if(channel>=NUM_CHANNELS)
+        return;
+    volume = (volume > VOLUME_CHANNEL_MAX) ? VOLUME_CHANNEL_MAX : volume;
+    volume = (volume < 0) ? 0 : volume;
+    chanVolumes[channel] = volume;
+#endif
+}
+
+uint8_t Sound::getVolume(uint8_t channel)
+{
+#if(NUM_CHANNELS > 0)
+    if(channel>=NUM_CHANNELS)
+        return 255;
+    return (chanVolumes[channel]);
+#else
+    return 0;
+#endif
+}
\ No newline at end of file
diff -r 000000000000 -r 6f26c31d8573 Sound.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sound.h	Sun Mar 01 05:29:45 2015 +0000
@@ -0,0 +1,224 @@
+/*
+ * (C) Copyright 2015 Valentin Ivanov. All rights reserved.
+ *
+ * This file is part of the RetroPlatform Library
+ *
+ * The RetroPlatform Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ * This library is inspired by Gamebuino Library (http://gamebuino.com)
+ * from Aurélien Rodot. 
+ */
+#ifndef SOUND_H
+#define SOUND_H
+
+#include <mbed.h>
+
+//commands
+#define CMD_VOLUME 0
+#define CMD_INSTRUMENT 1
+#define CMD_SLIDE 2
+#define CMD_ARPEGGIO 3
+#define CMD_TREMOLO 4
+
+
+#define NUM_CHANNELS 3 //number of sound channels (no more than 4)
+
+#define VOLUME_GLOBAL_MAX 1
+
+//7=instrument volume 9=note volume
+#define VOLUME_CHANNEL_MAX 255/NUM_CHANNELS/VOLUME_GLOBAL_MAX/7/9
+
+#define boolean bool
+
+//A set of sequences
+typedef struct {
+    uint16_t *Data;
+    uint8_t Cursor;
+    bool IsPlaying;
+} Track;
+
+//A sequence of commands and notes
+typedef struct {
+    uint16_t *Data;
+    bool Looping;
+    uint16_t Cursor;
+    bool IsPlaying;
+} Sequence;
+
+//A note of selected pitch, duration and volume
+typedef struct {
+    uint8_t Pitch;
+    uint8_t Duration;
+    int8_t  Volume;
+    bool    IsPlaying;
+} Note;
+
+typedef struct {
+    int8_t Counter;
+    int8_t volumeSlideStepDuration;
+    int8_t volumeSlideStepSize;
+    uint8_t arpeggioStepDuration;
+    int8_t arpeggioStepSize;
+    uint8_t tremoloStepDuration;
+    int8_t tremoloStepSize;
+
+} Command;
+
+typedef struct {
+    uint16_t **SequenceSet;
+    uint16_t **InstrumentSet;
+    
+    uint8_t Volume;
+    uint8_t Count;
+    bool    State;
+    uint8_t HalfPeriod;
+    uint8_t OutputVolume;
+    uint8_t Output;
+    bool    Noise;
+
+    
+    
+    int8_t SequencePitch;
+} AudioChannel;
+
+typedef struct {
+    uint16_t *Data;
+    uint8_t Length;
+    uint8_t Looping;
+    uint16_t Cursor;
+    uint8_t NextChange;
+} Instrument;
+
+class Sound {
+    
+    Ticker timer;
+    PwmOut speaker;
+    
+    Track tracks[NUM_CHANNELS];
+    Sequence sequences[NUM_CHANNELS];
+    Command commands[NUM_CHANNELS];
+    Instrument instruments[NUM_CHANNELS];
+    
+public:
+    Sound();
+    void initialize();
+    
+    void update()
+    {
+        updateTracks();
+        updateSequences();
+        updateNotes();
+    }
+    
+    void playTrack(const uint16_t* track, uint8_t channel);
+    void updateTrack(uint8_t channel);
+    void stopTrack(uint8_t channel);
+    void stopTracks() {
+        for(uint8_t channel=0; channel<NUM_CHANNELS; channel++)
+            stopTrack(channel);
+    }
+    void updateTracks() {
+        for(uint8_t channel=0; channel<NUM_CHANNELS; channel++)
+            updateTrack(channel);
+    }    
+    
+    void changeSequenceSet(const uint16_t* const* patterns, uint8_t channel);
+    
+    bool isTrackPlaying(uint8_t channel)
+    {
+        return tracks[channel].IsPlaying;
+    }
+    
+    void playSequence(const uint16_t* pattern, uint8_t channel);
+    void changeInstrumentSet(const uint16_t* const* instruments, uint8_t channel);
+    void updateSequence(uint8_t channel);
+    void stopSequence(uint8_t channel);
+    void stopSequences() {
+        for(uint8_t channel=0; channel<NUM_CHANNELS; channel++)
+            stopSequence(channel);
+    }
+    void updateSequences() {
+        for(uint8_t channel=0; channel<NUM_CHANNELS; channel++)
+            updateSequence(channel);
+    }
+    
+    
+    void playNote(uint8_t pitch, uint8_t duration, uint8_t channel);
+    void updateNote(uint8_t i);
+    void stopNote(uint8_t channel);
+    void updateNotes() {
+        for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++)
+            updateNote(channel);
+    }
+    void stopNotes() {
+        for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++)
+            stopNote(channel);
+    }    
+
+    void command(uint8_t cmd, uint8_t X, int8_t Y, uint8_t channel);
+    
+    uint8_t outputPitch[NUM_CHANNELS];
+    int8_t outputVolume[NUM_CHANNELS];
+
+    void setVolume(int8_t volume);
+    uint8_t getVolume();
+    void setVolume(int8_t volume, uint8_t channel);
+    uint8_t getVolume(uint8_t channel);
+    
+    
+    uint8_t prescaler;
+
+    void setChannelHalfPeriod(uint8_t channel, uint8_t halfPeriod);
+
+    
+    uint8_t globalVolume;
+    uint8_t volumeMax;
+    
+#if (NUM_CHANNELS > 0)
+    //tracks data
+    uint16_t **patternSet[NUM_CHANNELS];
+    int8_t patternPitch[NUM_CHANNELS];
+
+    // pattern data
+    uint16_t **instrumentSet[NUM_CHANNELS];
+
+    // note data
+    uint8_t notePitch[NUM_CHANNELS];
+    uint8_t noteDuration[NUM_CHANNELS];
+    int8_t noteVolume[NUM_CHANNELS];
+    boolean notePlaying[NUM_CHANNELS];
+      
+    
+    //current step data
+    int8_t stepVolume[NUM_CHANNELS];
+    uint8_t stepPitch[NUM_CHANNELS];
+    
+    uint8_t chanVolumes[NUM_CHANNELS];
+    
+    
+    uint8_t _chanCount[NUM_CHANNELS]; //counts until the next change of the waveform
+    boolean _chanState[NUM_CHANNELS]; //if the waveform is currently high or low
+    uint8_t _chanHalfPeriod[NUM_CHANNELS]; //duration of half the period of the waveform
+    uint8_t _chanOutputVolume[NUM_CHANNELS]; //amplitude of the outputted waveform
+    uint8_t _chanOutput[NUM_CHANNELS]; //current value of the outputted waveform
+    boolean _chanNoise[NUM_CHANNELS]; //if a random value should be added to the waveform to generate noise
+    
+#endif
+    void updateOutput();
+    
+private:
+    void generateOutput();    
+};
+
+#endif  /* SOUND_H */
\ No newline at end of file
diff -r 000000000000 -r 6f26c31d8573 Utils.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Utils.cpp	Sun Mar 01 05:29:45 2015 +0000
@@ -0,0 +1,82 @@
+/*
+ * (C) Copyright 2015 Valentin Ivanov. All rights reserved.
+ *
+ * This file is part of the RetroPlatform Library
+ *
+ * The RetroPlatform Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ * This library is inspired by Gamebuino Library (http://gamebuino.com)
+ * from Aurélien Rodot. 
+ */
+#include "mbed.h"
+#include "Utils.h"
+
+uint8_t constrain(uint8_t val, uint8_t minimum, uint8_t maximum )
+{
+    if( val < minimum )
+        return minimum;
+    if( val > maximum )
+        return maximum;
+
+    return val;
+}
+
+uint8_t max(uint8_t val1, uint8_t val2 )
+{
+    if( val1 < val2 )
+        return val2;
+    
+    return val1;
+}
+
+uint8_t min(uint8_t val1, uint8_t val2 )
+{
+    if( val1 < val2 )
+        return val1;
+
+    return val2;
+}
+
+#define     IAP_LOCATION    0x1fff1ff1
+typedef     void (*IAP_call)(unsigned int [], unsigned int []);
+
+IAP_call        iap_entry = reinterpret_cast<IAP_call>(IAP_LOCATION);
+unsigned int    IAP_command[ 5 ];
+unsigned int    IAP_result[ 5 ];
+
+int write_eeprom( char *source_addr, char *target_addr, int size )
+{
+    IAP_command[ 0 ]    = 61;
+    IAP_command[ 1 ]    = (unsigned int)target_addr;    //  Destination EEPROM address where data bytes are to be written. This address should be a 256 byte boundary.
+    IAP_command[ 2 ]    = (unsigned int)source_addr;    //  Source RAM address from which data bytes are to be read. This address should be a word boundary.
+    IAP_command[ 3 ]    = size;                         //  Number of bytes to be written. Should be 256 | 512 | 1024 | 4096.
+    IAP_command[ 4 ]    = SystemCoreClock / 1000;                     //  CPU Clock Frequency (CCLK) in kHz.
+
+    iap_entry( IAP_command, IAP_result );
+
+    return ( (int)IAP_result[ 0 ] );
+}
+
+int read_eeprom( char *source_addr, char *target_addr, int size )
+{
+    IAP_command[ 0 ]    = 62;
+    IAP_command[ 1 ]    = (unsigned int)source_addr;    //  Source EEPROM address from which data bytes are to be read. This address should be a word boundary.
+    IAP_command[ 2 ]    = (unsigned int)target_addr;    //  Destination RAM address where data bytes are to be written. This address should be a 256 byte boundary.
+    IAP_command[ 3 ]    = size;                         //  Number of bytes to be written. Should be 256 | 512 | 1024 | 4096.
+    IAP_command[ 4 ]    = SystemCoreClock / 1000;                     //  CPU Clock Frequency (CCLK) in kHz.
+
+    iap_entry( IAP_command, IAP_result );
+
+    return ( (int)IAP_result[ 0 ] );
+}
\ No newline at end of file
diff -r 000000000000 -r 6f26c31d8573 Utils.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Utils.h	Sun Mar 01 05:29:45 2015 +0000
@@ -0,0 +1,32 @@
+/*
+ * (C) Copyright 2015 Valentin Ivanov. All rights reserved.
+ *
+ * This file is part of the RetroPlatform Library
+ *
+ * The RetroPlatform Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ * This library is inspired by Gamebuino Library (http://gamebuino.com)
+ * from Aurélien Rodot. 
+ */
+#ifndef __UTILS_H__
+#define __UTILS_H__
+
+uint8_t constrain(uint8_t val, uint8_t minimum, uint8_t maximum );
+uint8_t max(uint8_t val1, uint8_t val2 );
+uint8_t min(uint8_t val1, uint8_t val2 );
+
+int write_eeprom( char *source_addr, char *target_addr, int size );
+int read_eeprom( char *source_addr, char *target_addr, int size );
+
+#endif  //__UTILS_H__
\ No newline at end of file