├── README.md ├── inc ├── I2CSlave.h ├── IOEXTSlave.h ├── MIDI.h ├── PSG.h ├── SCC.h ├── SPISlave.h └── SoundCortex.h └── src ├── MIDI.c ├── PSG.c ├── PSGUpdate.S ├── SCC.c ├── SCCUpdate.S └── SoundCortex.c /README.md: -------------------------------------------------------------------------------- 1 | # SoundCortex 2 | A firmware that makes your microcomputer work as a historical sound chip. 3 | See [SoundCortexLPC](https://github.com/toyoshim/SoundCortexLPC) for LPC810/812, 4 | and [SoundCortexSTM](https://github.com/toyoshim/SoundCortexSTM) for STM32F042K6T6. 5 | 6 | ## How to use 7 | You can access to the chip through I2C, SPI, or IOEXT bus. 8 | 9 | If you build it with I2C support enabled, the slave address for PSG is 0x50, and one for SCC is 0x51. You can write two bytes data to write to internal registers for each chip emulation with each address. The first byte is the register address, and the second byte is data to write. Internal register map for PSG is compatible with AY-3-8910, and one for SCC is compatible with lower 8-bit address of memory mapped SCC+ cart. 10 | 11 | If you build it with SPI support enabled, you can send 16-bit data in MSG first over MOSI of SPI mode 0. The most significant 8-bit is assumed as a register address, and the reset 8-bit is assumed as data to write. Address 0xff is specially handled as a page setting register, i.e. sending 0xff50 maps internal PSG register into the 8-bit address space, and 0xff51 does one of SCC. 12 | 13 | If you build it with IOEXT support enabled, IO ports are assigned as 14 | - 0xA0: PSG address latch 15 | - 0xA1: PSG data read/write 16 | - 0xA2: SCC address latch 17 | - 0xA3: SCC data read/write 18 | 19 | To use this chip from PC, you may need something that allows your PC to send I2C transactions. 20 | You may be interested in [I2CBridge](https://github.com/toyoshim/I2CBridge) that converts USART serial to I2C. 21 | 22 | To use from Raspberry Pi, you can just use built-in I2C. [Here](https://youtu.be/buaCriXYXNY) is a demo movie that controls the chip from Raspberry Pi. 23 | -------------------------------------------------------------------------------- /inc/I2CSlave.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Takashi Toyoshima 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of the authors nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | #ifndef __I2CSlave_h__ 31 | #define __I2CSlave_h__ 32 | 33 | #include 34 | #include 35 | 36 | // Callbacks 37 | void I2CSlaveStart(uint8_t addr); 38 | bool I2CSlaveWrite(uint8_t data); 39 | bool I2CSlaveRead(uint8_t* data); 40 | void I2CSlaveStop(void); 41 | 42 | // Initialize I2C module as a slave with 7-bit address, 100kHz mode. 43 | void I2CSlaveInit(uint8_t address1, uint8_t address2); 44 | 45 | #endif // __I2CSlave_h__ 46 | -------------------------------------------------------------------------------- /inc/IOEXTSlave.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019, Takashi Toyoshima 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of the authors nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | #ifndef __IOEXTSlave_h__ 31 | #define __IOEXTSlave_h__ 32 | 33 | #include 34 | #include 35 | 36 | // Callbacks 37 | bool IOEXTSlaveAccess(uint8_t port); 38 | bool IOEXTSlaveWrite(uint8_t port, uint8_t data); 39 | bool IOEXTSlaveRead(uint8_t port, uint8_t* data); 40 | 41 | // Initialize IOEXT module 42 | void IOEXTSlaveInit(); 43 | 44 | #endif // __IOEXTSlave_h__ 45 | -------------------------------------------------------------------------------- /inc/MIDI.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017, Takashi Toyoshima 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of the authors nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | #ifndef __MIDI_h__ 31 | #define __MIDI_h__ 32 | 33 | #include 34 | 35 | bool MIDIInit(const uint8_t* data); 36 | bool MIDIUpdate(uint16_t tick_us, bool repeat, uint16_t gap); 37 | 38 | #endif // __MIDI_h__ 39 | -------------------------------------------------------------------------------- /inc/PSG.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Takashi Toyoshima 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of the authors nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | #ifndef __PSG_h__ 31 | #define __PSG_h__ 32 | 33 | #include 34 | #include 35 | 36 | void PSGInit(uint32_t sample_rate); 37 | bool PSGWrite(uint8_t reg, uint8_t value); 38 | bool PSGRead(uint8_t reg, uint8_t* value); 39 | int16_t PSGUpdate(); 40 | 41 | #endif // __PSG_h__ 42 | -------------------------------------------------------------------------------- /inc/SCC.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Takashi Toyoshima 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of the authors nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | #ifndef __SCC_h__ 31 | #define __SCC_h__ 32 | 33 | #include 34 | #include 35 | 36 | void SCCInit(uint32_t sample_rate); 37 | bool SCCWrite(uint8_t reg, uint8_t value); 38 | bool SCCRead(uint8_t reg, uint8_t* value); 39 | int16_t SCCUpdate(); 40 | 41 | #endif // __SCC_h__ 42 | -------------------------------------------------------------------------------- /inc/SPISlave.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017, Takashi Toyoshima 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of the authors nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | #ifndef __SPISlave_h__ 31 | #define __SPISlave_h__ 32 | 33 | #include 34 | 35 | // Callbacks 36 | void SPISlaveWrite16(uint16_t data); 37 | 38 | // Initialize SPI module as a slave with MSB first, mode 0. 39 | void SPISlaveInit(); 40 | 41 | #endif // __SPISlave_h__ 42 | -------------------------------------------------------------------------------- /inc/SoundCortex.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019, Takashi Toyoshima 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of the authors nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | #ifndef __SoundCortex_h__ 31 | #define __SoundCortex_h__ 32 | 33 | #include 34 | 35 | #if defined(BUILD_PSG) 36 | # include "PSG.h" 37 | #endif 38 | 39 | #if defined(BUILD_SCC) 40 | # include "SCC.h" 41 | #endif 42 | 43 | #if defined(BUILD_I2C) 44 | # include "I2CSlave.h" 45 | #endif 46 | 47 | #if defined(BUILD_SPI) 48 | # include "SPISlave.h" 49 | #endif 50 | 51 | #if defined(BUILD_IOEXT) 52 | # include "IOEXTSlave.h" 53 | #endif 54 | 55 | #if defined(BUILD_MIDI) 56 | # include "MIDI.h" 57 | # include "SMF.h" 58 | #endif 59 | 60 | void SoundCortexInit(uint32_t sample_rate); 61 | uint16_t SoundCortexUpdate(); 62 | 63 | #endif // __SoundCortex_h__ 64 | -------------------------------------------------------------------------------- /src/MIDI.c: -------------------------------------------------------------------------------- 1 | // Copyright 2017, Takashi Toyoshima 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of the authors nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | 31 | #include 32 | #include 33 | 34 | #include "PSG.h" 35 | 36 | static uint16_t note_param[128] = { 37 | 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 38 | 39 | 0xd5d, 0xc9c, 0xbe7, 0xb3c, 0xa9b, 0xa02, 0x973, 0x8eb, 0x86b, 0x7f2, 0x780, 0x714, 40 | 0x6af, 0x64e, 0x5f4, 0x59e, 0x54e, 0x501, 0x4ba, 0x476, 0x436, 0x3f9, 0x3c0, 0x38a, 41 | 0x357, 0x327, 0x2fa, 0x2cf, 0x2a7, 0x281, 0x25d, 0x23b, 0x21b, 0x1fd, 0x1e0, 0x1c5, 42 | 0x1ac, 0x194, 0x17d, 0x168, 0x153, 0x140, 0x12e, 0x11d, 0x10d, 0x0fe, 0x0f0, 0x0e3, 43 | 0x0d6, 0x0ca, 0x0be, 0x0b4, 0x0aa, 0x0a0, 0x097, 0x08f, 0x087, 0x07f, 0x078, 0x071, 44 | 0x06b, 0x065, 0x05f, 0x05a, 0x055, 0x050, 0x04c, 0x047, 0x043, 0x040, 0x03c, 0x039, 45 | 0x035, 0x032, 0x030, 0x02d, 0x02a, 0x028, 0x026, 0x024, 0x022, 0x020, 0x01e, 0x01c, 46 | 0x01b, 0x019, 0x018, 0x016, 0x015, 0x014, 0x013, 0x012, 0x011, 0x010, 0x00f, 0x00e, 47 | 48 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49 | 0, 0, 0, 0, 0, 0, 0, 0 50 | }; 51 | 52 | struct { 53 | const uint8_t* start; 54 | const uint8_t* cur; 55 | const uint8_t* end; 56 | uint32_t tempo; 57 | uint32_t tick_us; 58 | uint32_t tick; 59 | uint16_t division; 60 | } MIDIWork; 61 | 62 | static void MIDINoteOff(uint8_t ch, uint8_t note, uint8_t velocity) { 63 | if (ch > 2) 64 | return; 65 | PSGWrite(8 + ch, 0); 66 | } 67 | 68 | static void MIDINoteOn(uint8_t ch, uint8_t note, uint8_t velocity) { 69 | if (ch > 2) 70 | return; 71 | PSGWrite(ch * 2 + 0, note_param[note] & 0xff); 72 | PSGWrite(ch * 2 + 1, note_param[note] >> 8); 73 | PSGWrite(8 + ch, velocity >> 3); 74 | } 75 | 76 | static uint32_t MIDIDeltaTime() { 77 | uint32_t delta = 0; 78 | do { 79 | delta = (delta << 7) | (*MIDIWork.cur & 0x7f); 80 | } while ((*MIDIWork.cur++ & 0x80) != 0); 81 | return delta; 82 | } 83 | 84 | bool MIDIInit(const uint8_t* data) { 85 | if (data[0] != 'M' || data[1] != 'T' || data[2] != 'h' || data[3] != 'd') 86 | return false; // invalid magic 87 | if (data[4] != 0 || data[5] != 0 || data[6] != 0 || data[7] != 6) 88 | return false; // invalid size 89 | if (data[8] != 0 || data[9] != 0 || data[10] != 0 || data[11] != 1) 90 | return false; // ! format 0 91 | MIDIWork.division = (data[12] << 8) | data[13]; 92 | if (data[14] != 'M' || data[15] != 'T' || data[16] != 'r' || data[17] != 'k') 93 | return false; // invalid magic 94 | uint32_t size = 95 | (data[18] << 24) | (data[19] << 16) | (data[20] << 8) | data[21]; 96 | MIDIWork.start = MIDIWork.cur = &data[22]; 97 | MIDIWork.end = &data[22 + size - 1]; 98 | MIDIWork.tick_us = 0; 99 | MIDIWork.tick = 0; 100 | MIDIWork.tempo = 1000000; 101 | MIDIWork.tick_us = MIDIWork.tempo / MIDIWork.division; 102 | 103 | PSGWrite(7, 0x38); 104 | return true; 105 | } 106 | 107 | bool MIDIUpdate(uint16_t tick_us, bool repeat, uint16_t gap) { 108 | while (tick_us) { 109 | if (MIDIWork.tick == 0) 110 | MIDIWork.tick = MIDIDeltaTime() * MIDIWork.tick_us; 111 | if (MIDIWork.tick > tick_us) { 112 | MIDIWork.tick -= tick_us; 113 | return true; 114 | } 115 | tick_us -= MIDIWork.tick; 116 | MIDIWork.tick = 0; 117 | uint8_t status = *MIDIWork.cur++; 118 | switch (status & 0xf0) { 119 | case 0x80: 120 | MIDINoteOff(status & 0x0f, MIDIWork.cur[0], MIDIWork.cur[1]); 121 | MIDIWork.cur += 2; 122 | break; 123 | case 0x90: 124 | MIDINoteOn(status & 0x0f, MIDIWork.cur[0], MIDIWork.cur[1]); 125 | MIDIWork.cur += 2; 126 | break; 127 | case 0xf0: 128 | if (status == 0xff) { 129 | uint8_t type = *MIDIWork.cur++; 130 | uint8_t size = *MIDIWork.cur++; 131 | if (type == 0x2f && size == 0) { 132 | if (!repeat) 133 | return false; 134 | MIDIWork.cur = MIDIWork.start; 135 | MIDIWork.tick = (MIDIDeltaTime() + gap) * MIDIWork.tick_us; 136 | } else if (type == 0x51 && size == 3) { 137 | MIDIWork.tempo = (MIDIWork.cur[0] << 16) | (MIDIWork.cur[1] << 8) | 138 | (MIDIWork.cur[2]); 139 | MIDIWork.tick_us = MIDIWork.tempo / MIDIWork.division; 140 | MIDIWork.cur += 3; 141 | } else { 142 | MIDIWork.cur += size; 143 | } 144 | break; 145 | } 146 | // no break 147 | default: 148 | // not impl. 149 | // printf("$%02x\n", status); 150 | return false; 151 | } 152 | } 153 | return true; 154 | } 155 | -------------------------------------------------------------------------------- /src/PSG.c: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Takashi Toyoshima 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of the authors nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | #include "PSG.h" 31 | 32 | // Constant variables to improve readability. 33 | enum { 34 | CLK_MSX = 3579545UL, 35 | CLK_4MHZ = 4000000UL, 36 | }; 37 | 38 | const uint16_t vt[32] = { 39 | 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x04, 40 | 0x05, 0x06, 0x07, 0x08, 0x09, 0x0b, 0x0d, 0x10, 41 | 0x13, 0x17, 0x1b, 0x20, 0x26, 0x2d, 0x36, 0x40, 42 | 0x4c, 0x5a, 0x6b, 0x80, 0x98, 0xb4, 0xd6, 0xff 43 | }; 44 | 45 | typedef struct { 46 | uint16_t tp; 47 | uint16_t ml; 48 | } Channel; 49 | 50 | typedef struct { 51 | uint32_t limit; 52 | uint32_t count; 53 | uint32_t on; 54 | uint32_t out; 55 | uint32_t tone; 56 | uint32_t noise; 57 | } Synth; 58 | 59 | typedef struct { 60 | uint32_t np; 61 | uint32_t limit; 62 | uint32_t count; 63 | uint32_t seed; 64 | } Noise; 65 | 66 | struct { 67 | uint32_t step; 68 | Synth synth[3]; 69 | Noise noise; 70 | 71 | uint32_t fout; 72 | Channel channel[3]; 73 | } PSGWork; 74 | 75 | void PSGInit(uint32_t sample_rate) { 76 | PSGWork.step = CLK_MSX; 77 | PSGWork.fout = sample_rate; 78 | for (int i = 0; i < 3; ++i) { 79 | PSGWork.synth[i].limit = 0; 80 | PSGWork.synth[i].count = 0; 81 | PSGWork.synth[i].on = 0; 82 | PSGWork.synth[i].out = 0; 83 | PSGWork.synth[i].tone = 1; 84 | PSGWork.synth[i].noise = 1; 85 | } 86 | PSGWork.noise.limit = 0; 87 | PSGWork.noise.count = 0; 88 | PSGWork.noise.seed = 0xffff; 89 | } 90 | 91 | bool PSGWrite(uint8_t reg, uint8_t value) { 92 | switch (reg) { 93 | case 0x00: // TP[7:0] for Ch.A 94 | PSGWork.channel[0].tp = (PSGWork.channel[0].tp & 0x0f00) | value; 95 | PSGWork.synth[0].limit = (uint32_t)PSGWork.channel[0].tp * 16 * PSGWork.fout; 96 | break; 97 | case 0x01: // TP[11:8] for Ch.A 98 | PSGWork.channel[0].tp = (PSGWork.channel[0].tp & 0x00ff) | ((uint16_t)(value & 0x0f) << 8); 99 | PSGWork.synth[0].limit = (uint32_t)PSGWork.channel[0].tp * 16 * PSGWork.fout; 100 | break; 101 | case 0x02: // TP[7:0] for Ch.B 102 | PSGWork.channel[1].tp = (PSGWork.channel[1].tp & 0x0f00) | value; 103 | PSGWork.synth[1].limit = (uint32_t)PSGWork.channel[1].tp * 16 * PSGWork.fout; 104 | break; 105 | case 0x03: // TP[11:8] for Ch.B 106 | PSGWork.channel[1].tp = (PSGWork.channel[1].tp & 0x00ff) | ((uint16_t)(value & 0x0f) << 8); 107 | PSGWork.synth[1].limit = (uint32_t)PSGWork.channel[1].tp * 16 * PSGWork.fout; 108 | break; 109 | case 0x04: // TP[7:0] for Ch.C 110 | PSGWork.channel[2].tp = (PSGWork.channel[2].tp & 0x0f00) | value; 111 | PSGWork.synth[2].limit = (uint32_t)PSGWork.channel[2].tp * 16 * PSGWork.fout; 112 | break; 113 | case 0x05: // TP[11:8] for Ch.C 114 | PSGWork.channel[2].tp = (PSGWork.channel[2].tp & 0x00ff) | ((uint16_t)(value & 0x0f) << 8); 115 | PSGWork.synth[2].limit = (uint32_t)PSGWork.channel[2].tp * 16 * PSGWork.fout; 116 | break; 117 | case 0x06: // NP[4:0] 118 | PSGWork.noise.np = value & 0x1f; 119 | PSGWork.noise.limit = (uint32_t)PSGWork.noise.np * 2 * 16 * PSGWork.fout; 120 | break; 121 | case 0x07: // MIXER 122 | PSGWork.synth[0].tone = !!(value & (1 << 0)); 123 | PSGWork.synth[1].tone = !!(value & (1 << 1)); 124 | PSGWork.synth[2].tone = !!(value & (1 << 2)); 125 | PSGWork.synth[0].noise = !!(value & (1 << 3)); 126 | PSGWork.synth[1].noise = !!(value & (1 << 4)); 127 | PSGWork.synth[2].noise = !!(value & (1 << 5)); 128 | break; 129 | case 0x08: // M/L[3:0] for Ch.A 130 | PSGWork.channel[0].ml = value & 0x1f; 131 | PSGWork.synth[0].out = vt[1 + ((PSGWork.channel[0].ml & 0x0f) << 1)]; 132 | break; 133 | case 0x09: // M/L[3:0] for Ch.B 134 | PSGWork.channel[1].ml = value & 0x1f; 135 | PSGWork.synth[1].out = vt[1 + ((PSGWork.channel[1].ml & 0x0f) << 1)]; 136 | break; 137 | case 0x0a: // M/L[3:0] for Ch.C 138 | PSGWork.channel[2].ml = value & 0x1f; 139 | PSGWork.synth[2].out = vt[1 + ((PSGWork.channel[2].ml & 0x0f) << 1)]; 140 | break; 141 | case 0x0b: // EP[7:0] 142 | case 0x0c: // EP[15:8] 143 | case 0x0d: // CONT/ATT/ALT/HOLD 144 | // TODO: support envelope. 145 | break; 146 | case 0x0e: 147 | case 0x0f: 148 | break; 149 | case 0xff: // Virtual Clock 150 | if (value == 0) 151 | PSGWork.step = CLK_MSX; 152 | else 153 | PSGWork.step = CLK_4MHZ; 154 | break; 155 | default: 156 | return false; 157 | } 158 | return true; 159 | } 160 | 161 | bool PSGRead(uint8_t reg, uint8_t* value) { 162 | switch (reg) { 163 | case 0xfe: // minor version 164 | *value = 1; 165 | break; 166 | case 0xff: // major version 167 | *value = 1; 168 | break; 169 | default: 170 | return false; 171 | } 172 | return true; 173 | } 174 | -------------------------------------------------------------------------------- /src/PSGUpdate.S: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Takashi Toyoshima 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of the authors nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | .syntax unified 31 | .cpu cortex-m0 32 | .align 2 33 | .thumb 34 | .thumb_func 35 | 36 | #define rOut r0 37 | #define rWork r1 38 | #define rStep r2 39 | #define rNoise r3 40 | #define rTmp1 r4 41 | #define rTmp2 r5 42 | #define rTmp3 r6 43 | 44 | #define iStep 0 45 | #define iSynth 4 46 | 47 | #define iSynthLimit 0 48 | #define iSynthCount 4 49 | #define iSynthOn 8 50 | #define iSynthOut 12 51 | #define iSynthTone 16 52 | #define iSynthNoise 20 53 | #define iSynthSize 24 54 | 55 | #define iSynth0 iSynth 56 | #define iSynth1 (iSynth0 + iSynthSize) 57 | #define iSynth2 (iSynth1 + iSynthSize) 58 | 59 | #define iNoise (iSynth2 + iSynthSize) 60 | #define iNoiseLimit 4 61 | #define iNoiseCount 8 62 | #define iNoiseSeed 12 63 | 64 | .macro UpdateTone base 65 | ldr rTmp1, [rWork, #(\base + iSynthCount)] 66 | add rTmp1, rTmp1, rStep 67 | str rTmp1, [rWork, #(\base + iSynthCount)] 68 | ldr rTmp2, [rWork, #(\base + iSynthLimit)] 69 | subs rTmp3, rTmp1, rTmp2 70 | ldr rTmp1, [rWork, #(\base + iSynthOn)] 71 | bhi 1f 72 | str rTmp3, [rWork, #(\base + iSynthCount)] 73 | mvns rTmp1, rTmp1 74 | str rTmp1, [rWork, #(\base + iSynthOn)] 75 | 1: 76 | ldr rTmp2, [rWork, #(\base + iSynthTone)] 77 | orrs rTmp1, rTmp1, rTmp2 78 | beq 1f 79 | ldr rTmp1, [rWork, #(\base + iSynthNoise)] 80 | orrs rTmp1, rTmp1, rNoise 81 | bne 2f 82 | 1: 83 | ldr rTmp1, [rWork, #(\base + iSynthOut)] 84 | add rOut, rOut, rTmp1 85 | 2: 86 | .endm 87 | 88 | .extern PSGWork 89 | 90 | .text 91 | .global PSGUpdate 92 | .type PSGUpdate, %function 93 | PSGUpdate: 94 | push {r4-r6, lr} 95 | ldr rWork, =#PSGWork 96 | ldr rStep, [rWork, #iStep] 97 | movs rOut, #0 98 | 99 | ldr rTmp1, [rWork, #(iNoise + iNoiseCount)] 100 | add rTmp1, rTmp1, rStep 101 | str rTmp1, [rWork, #(iNoise + iNoiseCount)] 102 | ldr rTmp2, [rWork, #(iNoise + iNoiseLimit)] 103 | subs rTmp3, rTmp1, rTmp2 104 | ldr rTmp1, [rWork, #(iNoise + iNoiseSeed)] 105 | bhi 1f 106 | str rTmp3, [rWork, #(iNoise + iNoiseCount)] 107 | movs rTmp2, #9 108 | ands rTmp2, rTmp2, rTmp1 109 | lsrs rTmp3, rTmp2, #3 110 | eors rTmp2, rTmp2, rTmp3 111 | lsls rTmp2, rTmp2, #15 112 | lsrs rTmp1, rTmp1, #1 113 | orrs rTmp1, rTmp1, rTmp2 114 | uxth rTmp1, rTmp1 115 | str rTmp1, [rWork, #(iNoise + iNoiseSeed)] 116 | 1: 117 | movs rNoise, #1 118 | ands rNoise, rNoise, rTmp1 119 | 120 | UpdateTone iSynth0 121 | UpdateTone iSynth1 122 | UpdateTone iSynth2 123 | 124 | pop {r4-r6, pc} 125 | .size PSGUpdate, . - PSGUpdate 126 | -------------------------------------------------------------------------------- /src/SCC.c: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Takashi Toyoshima 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of the authors nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | #include "SCC.h" 31 | 32 | // Constant variables to improve readability. 33 | enum { 34 | CLK_MSX = 3579545UL, 35 | CLK_4MHZ = 4000000UL, 36 | }; 37 | 38 | typedef struct { 39 | uint32_t tp; 40 | uint32_t ml; 41 | } Channel; 42 | 43 | typedef struct { 44 | uint32_t limit; 45 | uint32_t count; 46 | uint32_t offset; 47 | uint32_t vol; 48 | uint32_t tone; 49 | uint8_t wt[32]; 50 | } Synth; 51 | 52 | struct { 53 | uint32_t step; 54 | Synth synth[5]; 55 | 56 | uint32_t fout; 57 | Channel channel[5]; 58 | } SCCWork; 59 | 60 | void SCCInit(uint32_t sample_rate) { 61 | SCCWork.step = CLK_MSX; 62 | SCCWork.fout = sample_rate; 63 | for (int i = 0; i < 5; ++i) { 64 | SCCWork.synth[i].limit = 0; 65 | SCCWork.synth[i].count = 0; 66 | SCCWork.synth[i].offset = 0; 67 | SCCWork.synth[i].vol = 0; 68 | SCCWork.synth[i].tone = 1; 69 | for (int j = 0; j < 32; ++j) 70 | SCCWork.synth[i].wt[j] = 0; 71 | } 72 | } 73 | 74 | bool SCCWrite(uint8_t reg, uint8_t value) { 75 | // Register map is compatible with SCC+. 76 | if (reg <= 0x9f) { 77 | int ch = reg >> 5; 78 | int offset = reg & 0x1f; 79 | SCCWork.synth[ch].wt[offset] = value; 80 | } else if (reg <= 0xa9) { 81 | int ch = (reg - 0xa0) >> 1; 82 | if (reg & 1) 83 | SCCWork.channel[ch].tp = (SCCWork.channel[ch].tp & 0x00ff) | ((uint16_t)(value & 0x0f) << 8); 84 | else 85 | SCCWork.channel[ch].tp = (SCCWork.channel[ch].tp & 0x0f00) | value; 86 | SCCWork.synth[ch].limit = (uint32_t)SCCWork.channel[ch].tp * SCCWork.fout; 87 | } else if (reg <= 0xae) { 88 | int ch = reg - 0xaa; 89 | SCCWork.channel[ch].ml = value & 0x0f; 90 | SCCWork.synth[ch].vol = SCCWork.channel[ch].ml; 91 | } else if (reg == 0xaf) { 92 | SCCWork.synth[0].tone = value & (1 << 0); 93 | SCCWork.synth[1].tone = value & (1 << 1); 94 | SCCWork.synth[2].tone = value & (1 << 2); 95 | SCCWork.synth[3].tone = value & (1 << 3); 96 | SCCWork.synth[4].tone = value & (1 << 4); 97 | } else if (reg == 0xff) { 98 | // Virtual Clock 99 | if (value == 0) 100 | SCCWork.step = CLK_MSX; 101 | else 102 | SCCWork.step = CLK_4MHZ; 103 | } 104 | // TODO: mode register. 105 | return true; 106 | } 107 | 108 | bool SCCRead(uint8_t reg, uint8_t* value) { 109 | switch (reg) { 110 | case 0xfe: // minor version 111 | *value = 1; 112 | break; 113 | case 0xff: // major version 114 | *value = 1; 115 | break; 116 | default: 117 | return false; 118 | } 119 | return true; 120 | } 121 | -------------------------------------------------------------------------------- /src/SCCUpdate.S: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Takashi Toyoshima 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of the authors nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | .syntax unified 31 | .cpu cortex-m0 32 | .align 2 33 | .thumb 34 | .thumb_func 35 | 36 | #define rOut r0 37 | #define rWork r1 38 | #define rStep r2 39 | #define rMask r3 40 | #define rTableOffset r4 41 | #define rTmp1 r5 42 | #define rTmp2 r6 43 | #define rTmp3 r7 44 | 45 | #define iStep 0 46 | #define iSynth 4 47 | 48 | #define iSynthLimit 0 49 | #define iSynthCount 4 50 | #define iSynthOffset 8 51 | #define iSynthVol 12 52 | #define iSynthTone 16 53 | #define iSynthWaveTable 20 54 | #define iSynthSize 52 55 | 56 | .macro UpdateTone 57 | ldr rTmp1, [rWork, #(iSynth + iSynthCount)] 58 | add rTmp1, rTmp1, rStep 59 | str rTmp1, [rWork, #(iSynth + iSynthCount)] 60 | ldr rTmp2, [rWork, #(iSynth + iSynthLimit)] 61 | subs rTmp3, rTmp1, rTmp2 62 | ldr rTmp1, [rWork, #(iSynth + iSynthOffset)] 63 | bhi 1f 64 | str rTmp3, [rWork, #(iSynth + iSynthCount)] 65 | adds rTmp1, rTmp1, #1 66 | ands rTmp1, rTmp1, rMask 67 | str rTmp1, [rWork, #(iSynth + iSynthOffset)] 68 | 1: 69 | ldr rTmp2, [rWork, #(iSynth + iSynthTone)] 70 | orrs rTmp2, rTmp2, rTmp2 71 | beq 1f 72 | add rTmp1, rTmp1, rWork 73 | ldrsb rTmp1, [rTmp1, rTableOffset] 74 | ldr rTmp2, [rWork, #(iSynth + iSynthVol)] 75 | muls rTmp1, rTmp1, rTmp2 76 | add rOut, rOut, rTmp1 77 | 1: 78 | .endm 79 | 80 | .extern SCCWork 81 | 82 | .text 83 | .global SCCUpdate 84 | .type SCCUpdate, %function 85 | SCCUpdate: 86 | push {r4-r7, lr} 87 | ldr rWork, =#SCCWork 88 | ldr rStep, [rWork, #iStep] 89 | movs rOut, #0 90 | movs rMask, #0x1f 91 | movs rTableOffset, #(iSynth + iSynthWaveTable) 92 | 93 | UpdateTone 94 | adds rWork, rWork, #iSynthSize 95 | UpdateTone 96 | adds rWork, rWork, #iSynthSize 97 | UpdateTone 98 | adds rWork, rWork, #iSynthSize 99 | UpdateTone 100 | adds rWork, rWork, #iSynthSize 101 | UpdateTone 102 | 103 | asrs rOut, rOut, #4 104 | pop {r4-r7, pc} 105 | .size SCCUpdate, . - SCCUpdate 106 | -------------------------------------------------------------------------------- /src/SoundCortex.c: -------------------------------------------------------------------------------- 1 | // Copyright 2019, Takashi Toyoshima 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of the authors nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | #include 31 | 32 | #include "BuildConfig.h" 33 | #include "SoundCortex.h" 34 | 35 | uint16_t SoundCortexUpdate() { 36 | #if defined(BUILD_MIDI) 37 | MIDIUpdate(21, true, 120); // 21.3usec 38 | #endif 39 | // TODO: Use signed signals for both. 40 | #if defined(BUILD_PSG) && !defined(BUILD_SCC) 41 | return PSGUpdate(); 42 | #elif !defined(BUILD_PSG) && defined(BUILD_SCC) 43 | return 320 + (SCCUpdate() >> 1); 44 | #elif defined(BUILD_PSG) && defined(BUILD_SCC) 45 | return 160 + (PSGUpdate() >> 1) + (SCCUpdate() >> 2); 46 | #else 47 | return 0; 48 | #endif 49 | } 50 | 51 | #if defined(BUILD_I2C) 52 | // I2C Slave handling code. 53 | static uint8_t i2c_addr = 0; 54 | static uint8_t i2c_data_index = 0; 55 | static uint8_t i2c_data_addr = 0; 56 | 57 | void I2CSlaveStart(uint8_t addr) { 58 | i2c_addr = addr; 59 | i2c_data_index = 0; 60 | } 61 | 62 | bool I2CSlaveWrite(uint8_t data) { 63 | if (i2c_data_index == 0) { 64 | i2c_data_addr = data; 65 | } else if (i2c_data_index == 1) { 66 | # if defined(BUILD_PSG) && !defined(BUILD_SCC) 67 | return PSGWrite(i2c_data_addr, data); 68 | # elif !defined(BUILD_PSG) && defined(BUILD_SCC) 69 | return SCCWrite(i2c_data_addr, data); 70 | # elif defined(BUILD_PSG) && defined(BUILD_SCC) 71 | if (i2c_addr == PSG_ADDRESS) 72 | return PSGWrite(i2c_data_addr, data); 73 | return SCCWrite(i2c_data_addr, data); 74 | # else 75 | return false; 76 | # endif 77 | } else { 78 | return false; 79 | } 80 | i2c_data_index++; 81 | return true; 82 | } 83 | 84 | bool I2CSlaveRead(uint8_t* data) { 85 | # if defined(BUILD_PSG) && !defined(BUILD_SCC) 86 | return PSGRead(i2c_data_addr, data); 87 | # elif !defined(BUILD_PSG) && defined(BUILD_SCC) 88 | return SCCRead(i2c_data_addr, data); 89 | # elif defined(BUILD_PSG) && defined(BUILD_SCC) 90 | if (i2c_addr == PSG_ADDRESS) 91 | return PSGRead(i2c_data_addr, data); 92 | return SCCRead(i2c_data_addr, data); 93 | # else 94 | return false; 95 | # endif 96 | } 97 | #endif 98 | 99 | #if defined(BUILD_SPI) 100 | static uint8_t spi_chip_select = PSG_ADDRESS; 101 | 102 | void SPISlaveWrite16(uint16_t data) { 103 | if ((data >> 8) == 0xff) 104 | spi_chip_select = data; 105 | #if defined(BUILD_PSG) 106 | else if (spi_chip_select == PSG_ADDRESS) 107 | PSGWrite(data >> 8, data); 108 | #endif 109 | #if defined(BUILD_SCC) 110 | else if (spi_chip_select == SCC_ADDRESS) 111 | SCCWrite(data >> 8, data); 112 | #endif 113 | } 114 | #endif 115 | 116 | #if defined(BUILD_IOEXT) 117 | static uint8_t psg_address = 0; 118 | static uint8_t scc_address = 0; 119 | 120 | bool IOEXTSlaveAccess(uint8_t port) { 121 | switch (port) { 122 | #if defined(BUILD_PSG) 123 | case PSG_ADDRESS_PORT: 124 | case PSG_DATA_PORT: 125 | break; 126 | #endif 127 | #if defined(BUILD_SCC) 128 | case SCC_ADDRESS_PORT: 129 | case SCC_DATA_PORT: 130 | break; 131 | #endif 132 | default: 133 | return false; 134 | } 135 | return true; 136 | } 137 | 138 | bool IOEXTSlaveWrite(uint8_t port, uint8_t data) { 139 | switch (port) { 140 | #if defined(BUILD_PSG) 141 | case PSG_ADDRESS_PORT: 142 | psg_address = data; 143 | break; 144 | case PSG_DATA_PORT: 145 | PSGWrite(psg_address, data); 146 | break; 147 | #endif 148 | #if defined(BUILD_SCC) 149 | case SCC_ADDRESS_PORT: 150 | scc_address = data; 151 | break; 152 | case SCC_DATA_PORT: 153 | SCCWrite(scc_address, data); 154 | break; 155 | #endif 156 | default: 157 | return false; 158 | } 159 | return true; 160 | } 161 | 162 | bool IOEXTSlaveRead(uint8_t port, uint8_t* data) { 163 | switch (port) { 164 | #if defined(BUILD_PSG) 165 | case PSG_ADDRESS_PORT: 166 | *data = psg_address; 167 | break; 168 | case PSG_DATA_PORT: 169 | PSGRead(psg_address, data); 170 | break; 171 | #endif 172 | #if defined(BUILD_SCC) 173 | case SCC_ADDRESS_PORT: 174 | *data = scc_address; 175 | break; 176 | case SCC_DATA_PORT: 177 | SCCRead(scc_address, data); 178 | break; 179 | #endif 180 | default: 181 | return false; 182 | } 183 | return true; 184 | } 185 | #endif 186 | 187 | void SlaveInit(uint8_t address1, uint8_t address2) { 188 | #if defined(BUILD_I2C) 189 | I2CSlaveInit(address1, address2); 190 | #endif 191 | #if defined(BUILD_SPI) 192 | SPISlaveInit(); 193 | #endif 194 | #if defined(BUILD_IOEXT) 195 | IOEXTSlaveInit(); 196 | #endif 197 | } 198 | 199 | void SoundCortexInit(uint32_t sample_rate) { 200 | #if defined(BUILD_PSG) && !defined(BUILD_SCC) 201 | PSGInit(sample_rate); 202 | SlaveInit(PSG_ADDRESS, 0); 203 | #elif !defined(BUILD_PSG) && defined(BUILD_SCC) 204 | SCCInit(sample_rate); 205 | SlaveInit(SCC_ADDRESS, 0); 206 | #elif defined(BUILD_PSG) && defined(BUILD_SCC) 207 | PSGInit(sample_rate); 208 | SCCInit(sample_rate); 209 | SlaveInit(PSG_ADDRESS, SCC_ADDRESS); 210 | #else 211 | #endif 212 | #if defined(BUILD_MIDI) 213 | MIDIInit(SMF); 214 | #endif 215 | } 216 | --------------------------------------------------------------------------------