summaryrefslogtreecommitdiff
path: root/sid/sidplay-libs-2.1.0/libsidplay/src/psiddrv.cpp
blob: 5a8cf894c32ace63a81e6d392eff3e25d25760c4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/***************************************************************************
                          psiddrv.cpp  -  PSID Driver Installtion
                             -------------------
    begin                : Fri Jul 27 2001
    copyright            : (C) 2001 by Simon White
    email                : s_a_white@email.com
 ***************************************************************************/
/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
/***************************************************************************
 *  $Log: psiddrv.cpp,v $
 *  Revision 1.25  2002/12/13 22:07:29  s_a_white
 *  C64 code fixed so a theres no decrement before checking the random value.
 *
 *  Revision 1.24  2002/11/27 00:16:51  s_a_white
 *  Make sure driver info gets reset and exported properly.
 *
 *  Revision 1.23  2002/11/25 21:09:42  s_a_white
 *  Reset address for old sidplay1 modes now directly passed to the CPU.  This
 *  prevents tune corruption and banking issues for the different modes.
 *
 *  Revision 1.22  2002/11/19 22:53:23  s_a_white
 *  Sidplay1 modes modified to make them nolonger require the psid driver.
 *
 *  Revision 1.21  2002/11/01 19:11:21  s_a_white
 *  Export random delay used in song.
 *
 *  Revision 1.20  2002/11/01 17:36:02  s_a_white
 *  Frame based support for old sidplay1 modes.
 *
 *  Revision 1.19  2002/10/02 19:42:59  s_a_white
 *  RSID support.
 *
 *  Revision 1.18  2002/09/12 21:01:32  s_a_white
 *  Added support for simulating the random delay before the user loads a
 *  program on a real C64.
 *
 *  Revision 1.17  2002/09/09 18:01:30  s_a_white
 *  Prevent m_info driver details getting modified when C64 crashes.
 *
 *  Revision 1.16  2002/07/21 19:39:40  s_a_white
 *  Proper error handling of reloc info overlapping load image.
 *
 *  Revision 1.15  2002/07/18 18:37:42  s_a_white
 *  Buffer overflow fixes for tunes providing bad reloc information.
 *
 *  Revision 1.14  2002/07/17 21:48:10  s_a_white
 *  PSIDv2NG reloc exclude region extension.
 *
 *  Revision 1.13  2002/03/12 18:43:59  s_a_white
 *  Tidy up handling of envReset on illegal CPU instructions.
 *
 *  Revision 1.12  2002/03/03 22:02:36  s_a_white
 *  Sidplay2 PSID driver length now exported.
 *
 *  Revision 1.11  2002/02/17 12:41:18  s_a_white
 *  Fixed to not so easily break when C64 code is modified.
 *
 *  Revision 1.10  2002/02/04 23:50:48  s_a_white
 *  Improved compatibilty with older sidplay1 modes.
 *
 *  Revision 1.9  2002/01/29 21:50:33  s_a_white
 *  Auto switching to a better emulation mode.  tuneInfo reloaded after a
 *  config.  Initial code added to support more than two sids.
 *
 *  Revision 1.8  2001/12/21 21:54:14  s_a_white
 *  Improved compatibility if Sidplay1 bankswitching mode.
 *
 *  Revision 1.7  2001/12/13 08:28:08  s_a_white
 *  Added namespace support to fix problems with xsidplay.
 *
 *  Revision 1.6  2001/11/16 19:23:18  s_a_white
 *  Fixed sign of buf for reloc65 call.
 *
 *  Revision 1.5  2001/10/28 21:28:35  s_a_white
 *  For none real mode if play != 0 we now always jump to irqjob instead of
 *  playAddr.
 *
 *  Revision 1.4  2001/10/02 18:31:24  s_a_white
 *  No longer dies if relocStartPages != 0 byr relocPages == 0.  For none real
 *  evironment modes, play is always followed even if interrupt handlers are
 *  installed.
 *
 *  Revision 1.3  2001/09/01 11:13:18  s_a_white
 *  Fixes sidplay1 environment modes.
 *
 *  Revision 1.2  2001/07/27 12:52:12  s_a_white
 *  Removed warning.
 *
 *  Revision 1.1  2001/07/27 12:12:23  s_a_white
 *  Initial release.
 *
 ***************************************************************************/

// --------------------------------------------------------
// The code here is use to support the PSID Version 2NG
// (proposal B) file format for player relocation support.
// --------------------------------------------------------
#include "sidendian.h"
#include "player.h"

#define PSIDDRV_MAX_PAGE 0xff

SIDPLAY2_NAMESPACE_START

const char *Player::ERR_PSIDDRV_NO_SPACE  = "ERROR: No space to install psid driver in C64 ram"; 
const char *Player::ERR_PSIDDRV_RELOC     = "ERROR: Failed whilst relocating psid driver";

extern "C" int reloc65(unsigned char** buf, int* fsize, int addr);

int Player::psidDrvInstall (SidTuneInfo &tuneInfo, sid2_info_t &info)
{
    uint_least16_t relocAddr;
    int startlp = tuneInfo.loadAddr >> 8;
    int endlp   = (tuneInfo.loadAddr + (tuneInfo.c64dataLen - 1)) >> 8;

    if (info.environment != sid2_envR)
    {   // Sidplay1 modes require no psid driver
        info.driverAddr   = 0;
        info.driverLength = 0;
        info.rnddelay     = 0;
        return 0;
    }

    // Check for free space in tune
    if (tuneInfo.relocStartPage == PSIDDRV_MAX_PAGE)
        tuneInfo.relocPages = 0;
    // Check if we need to find the reloc addr
    else if (tuneInfo.relocStartPage == 0)
    {   // Tune is clean so find some free ram around the
        // load image
        psidRelocAddr (tuneInfo, startlp, endlp);
    }
    else
    {   // Check reloc information mode
        int startrp = tuneInfo.relocStartPage;
        int endrp   = startrp + (tuneInfo.relocPages - 1);

        // New relocation implementation (exclude region)
        // to complement existing method rejected as being
        // unnecessary.  From tests in most cases this
        // method increases memory availibility.
        /*************************************************
        if ((startrp <= startlp) && (endrp >= endlp))
        {   // Is describing used space so find some free
            // ram outside this range
            psidRelocAddr (tuneInfo, startrp, endrp);
        }
        *************************************************/
    }

    if (tuneInfo.relocPages < 1)
    {
        m_errorString = ERR_PSIDDRV_NO_SPACE;
        return -1;
    }

    relocAddr = tuneInfo.relocStartPage << 8;

    {   // Place psid driver into ram
        uint8_t psid_driver[] = {
#          include "psiddrv.bin"
        };
        uint8_t *reloc_driver = psid_driver;
        int      reloc_size   = sizeof (psid_driver);

        if (!reloc65 (&reloc_driver, &reloc_size, relocAddr - 13))
        {
            m_errorString = ERR_PSIDDRV_RELOC;
            return -1;
        }

        // Adjust size to not included initialisation data.
        reloc_size -= 13;
        info.driverAddr   = relocAddr;
        info.driverLength = (uint_least16_t) reloc_size;
        // Round length to end of page
        info.driverLength += 0xff;
        info.driverLength &= 0xff00;

        m_ram[0x310] = JMPw;
        memcpy (&m_ram[0x0311], &reloc_driver[4], 9);
        memcpy (&m_rom[0xfffc], &reloc_driver[0], 2); /* RESET */

        {   // Experimental exit to basic support
            uint_least16_t addr;
            addr = endian_little16(&reloc_driver[2]);
            m_rom[0xa7ae] = JMPw;
            endian_little16 (&m_rom[0xa7af], 0xffe1);
            endian_little16 (&m_ram[0x0328], addr);
        }
        memcpy (&m_ram[relocAddr], &reloc_driver[13], reloc_size);
    }

    {   // Setup the Initial entry point
        uint_least16_t playAddr = tuneInfo.playAddr;
        uint_least16_t addr = relocAddr;

        // Tell C64 about song
        m_ram[addr++] = (uint8_t) tuneInfo.currentSong;
        if (tuneInfo.songSpeed == SIDTUNE_SPEED_VBI)
            m_ram[addr] = 0;
        else // SIDTUNE_SPEED_CIA_1A
            m_ram[addr] = 1;

        addr++;
        endian_little16 (&m_ram[addr], tuneInfo.initAddr);
        addr += 2;
        endian_little16 (&m_ram[addr], playAddr);
        addr += 2;
        // Below we limit the delay to something sensible.
        info.rnddelay = (uint_least16_t) (m_rand >> 3) & 0x0FFF;
        endian_little16 (&m_ram[addr], m_info.rnddelay);
        addr += 2;
        m_rand        = m_rand * 13 + 1;
        m_ram[addr++] = iomap (m_tuneInfo.initAddr);
        m_ram[addr++] = iomap (m_tuneInfo.playAddr);
    }
    return 0;
}


void Player::psidRelocAddr (SidTuneInfo &tuneInfo, int startp, int endp)
{   // Used memory ranges.
    bool pages[256];
    int  used[] = {0x00,   0x03,
                   0xa0,   0xbf,
                   0xd0,   0xff,
                   startp, endp};

    // Mark used pages in table.
    memset(pages, false, sizeof(pages));
    for (size_t i = 0; i < sizeof(used)/sizeof(*used); i += 2)
    {
        for (int page = used[i]; page <= used[i + 1]; page++)
            pages[page] = true;
    }

    {   // Find largest free range.
        int relocPages, lastPage = 0;
        tuneInfo.relocPages = 0;
        for (size_t page = 0; page < sizeof(pages)/sizeof(*pages); page++)
        {
            if (pages[page] == false)
                continue;
            relocPages = page - lastPage;
            if (relocPages > tuneInfo.relocPages)
            {
                tuneInfo.relocStartPage = lastPage;
                tuneInfo.relocPages     = relocPages;
            }
            lastPage = page + 1;
        }
    }

    if (tuneInfo.relocPages    == 0)
        tuneInfo.relocStartPage = PSIDDRV_MAX_PAGE;
}

SIDPLAY2_NAMESPACE_STOP