SandboXP

Форк
0
/
dma.js 
424 строки · 13.8 Кб
1
"use strict";
2

3
/**
4
 * @constructor
5
 * @param {CPU} cpu
6
 */
7
function DMA(cpu)
8
{
9
    /** @const @type {CPU} */
10
    this.cpu = cpu;
11

12
    this.channel_page = new Uint8Array(8);
13
    this.channel_pagehi = new Uint8Array(8);
14
    this.channel_addr = new Uint16Array(8);
15
    this.channel_addr_init = new Uint16Array(8);
16
    this.channel_count = new Uint16Array(8);
17
    this.channel_count_init = new Uint16Array(8);
18
    this.channel_mask = new Uint8Array(8);
19
    this.channel_mode = new Uint8Array(8);
20
    this.unmask_listeners = [];
21

22
    this.lsb_msb_flipflop = 0;
23

24
    var io = cpu.io;
25

26
    io.register_write(0x00, this, this.port_addr_write.bind(this, 0));
27
    io.register_write(0x02, this, this.port_addr_write.bind(this, 1));
28
    io.register_write(0x04, this, this.port_addr_write.bind(this, 2));
29
    io.register_write(0x06, this, this.port_addr_write.bind(this, 3));
30
    io.register_write(0x01, this, this.port_count_write.bind(this, 0));
31
    io.register_write(0x03, this, this.port_count_write.bind(this, 1));
32
    io.register_write(0x05, this, this.port_count_write.bind(this, 2));
33
    io.register_write(0x07, this, this.port_count_write.bind(this, 3));
34

35
    io.register_read(0x00, this, this.port_addr_read.bind(this, 0));
36
    io.register_read(0x02, this, this.port_addr_read.bind(this, 1));
37
    io.register_read(0x04, this, this.port_addr_read.bind(this, 2));
38
    io.register_read(0x06, this, this.port_addr_read.bind(this, 3));
39
    io.register_read(0x01, this, this.port_count_read.bind(this, 0));
40
    io.register_read(0x03, this, this.port_count_read.bind(this, 1));
41
    io.register_read(0x05, this, this.port_count_read.bind(this, 2));
42
    io.register_read(0x07, this, this.port_count_read.bind(this, 3));
43

44
    io.register_write(0xC0, this, this.port_addr_write.bind(this, 4));
45
    io.register_write(0xC4, this, this.port_addr_write.bind(this, 5));
46
    io.register_write(0xC8, this, this.port_addr_write.bind(this, 6));
47
    io.register_write(0xCC, this, this.port_addr_write.bind(this, 7));
48
    io.register_write(0xC2, this, this.port_count_write.bind(this, 4));
49
    io.register_write(0xC6, this, this.port_count_write.bind(this, 5));
50
    io.register_write(0xCA, this, this.port_count_write.bind(this, 6));
51
    io.register_write(0xCE, this, this.port_count_write.bind(this, 7));
52

53
    io.register_read(0xC0, this, this.port_addr_read.bind(this, 4));
54
    io.register_read(0xC4, this, this.port_addr_read.bind(this, 5));
55
    io.register_read(0xC8, this, this.port_addr_read.bind(this, 6));
56
    io.register_read(0xCC, this, this.port_addr_read.bind(this, 7));
57
    io.register_read(0xC2, this, this.port_count_read.bind(this, 4));
58
    io.register_read(0xC6, this, this.port_count_read.bind(this, 5));
59
    io.register_read(0xCA, this, this.port_count_read.bind(this, 6));
60
    io.register_read(0xCE, this, this.port_count_read.bind(this, 7));
61

62
    io.register_write(0x87, this, this.port_page_write.bind(this, 0));
63
    io.register_write(0x83, this, this.port_page_write.bind(this, 1));
64
    io.register_write(0x81, this, this.port_page_write.bind(this, 2));
65
    io.register_write(0x82, this, this.port_page_write.bind(this, 3));
66
    io.register_write(0x8F, this, this.port_page_write.bind(this, 4));
67
    io.register_write(0x8B, this, this.port_page_write.bind(this, 5));
68
    io.register_write(0x89, this, this.port_page_write.bind(this, 6));
69
    io.register_write(0x8A, this, this.port_page_write.bind(this, 7));
70

71
    io.register_read(0x87, this, this.port_page_read.bind(this, 0));
72
    io.register_read(0x83, this, this.port_page_read.bind(this, 1));
73
    io.register_read(0x81, this, this.port_page_read.bind(this, 2));
74
    io.register_read(0x82, this, this.port_page_read.bind(this, 3));
75
    io.register_read(0x8F, this, this.port_page_read.bind(this, 4));
76
    io.register_read(0x8B, this, this.port_page_read.bind(this, 5));
77
    io.register_read(0x89, this, this.port_page_read.bind(this, 6));
78
    io.register_read(0x8A, this, this.port_page_read.bind(this, 7));
79

80
    io.register_write(0x487, this, this.port_pagehi_write.bind(this, 0));
81
    io.register_write(0x483, this, this.port_pagehi_write.bind(this, 1));
82
    io.register_write(0x481, this, this.port_pagehi_write.bind(this, 2));
83
    io.register_write(0x482, this, this.port_pagehi_write.bind(this, 3));
84
    io.register_write(0x48B, this, this.port_pagehi_write.bind(this, 5));
85
    io.register_write(0x489, this, this.port_pagehi_write.bind(this, 6));
86
    io.register_write(0x48A, this, this.port_pagehi_write.bind(this, 7));
87

88
    io.register_read(0x487, this, this.port_pagehi_read.bind(this, 0));
89
    io.register_read(0x483, this, this.port_pagehi_read.bind(this, 1));
90
    io.register_read(0x481, this, this.port_pagehi_read.bind(this, 2));
91
    io.register_read(0x482, this, this.port_pagehi_read.bind(this, 3));
92
    io.register_read(0x48B, this, this.port_pagehi_read.bind(this, 5));
93
    io.register_read(0x489, this, this.port_pagehi_read.bind(this, 6));
94
    io.register_read(0x48A, this, this.port_pagehi_read.bind(this, 7));
95

96
    io.register_write(0x0A, this, this.port_singlemask_write.bind(this, 0));
97
    io.register_write(0xD4, this, this.port_singlemask_write.bind(this, 4));
98
    io.register_write(0x0F, this, this.port_multimask_write.bind(this, 0));
99
    io.register_write(0xDE, this, this.port_multimask_write.bind(this, 4));
100

101
    io.register_read(0x0F, this, this.port_multimask_read.bind(this, 0));
102
    io.register_read(0xDE, this, this.port_multimask_read.bind(this, 4));
103

104
    io.register_write(0x0B, this, this.port_mode_write.bind(this, 0));
105
    io.register_write(0xD6, this, this.port_mode_write.bind(this, 4));
106

107
    io.register_write(0x0C, this, this.portC_write);
108
    io.register_write(0xD8, this, this.portC_write);
109
}
110

111
DMA.prototype.get_state = function()
112
{
113
    return [
114
        this.channel_page,
115
        this.channel_pagehi,
116
        this.channel_addr,
117
        this.channel_addr_init,
118
        this.channel_count,
119
        this.channel_count_init,
120
        this.channel_mask,
121
        this.channel_mode,
122
        this.lsb_msb_flipflop,
123
    ];
124
};
125

126
DMA.prototype.set_state = function(state)
127
{
128
    this.channel_page = state[0];
129
    this.channel_pagehi = state[1];
130
    this.channel_addr = state[2];
131
    this.channel_addr_init = state[3];
132
    this.channel_count = state[4];
133
    this.channel_count_init = state[5];
134
    this.channel_mask = state[6];
135
    this.channel_mode = state[7];
136
    this.lsb_msb_flipflop = state[8];
137
};
138

139
DMA.prototype.port_count_write = function(channel, data_byte)
140
{
141
    dbg_log("count write [" + channel + "] = " + h(data_byte), LOG_DMA);
142

143
    this.channel_count[channel] =
144
        this.flipflop_get(this.channel_count[channel], data_byte, false);
145

146
    this.channel_count_init[channel] =
147
        this.flipflop_get(this.channel_count_init[channel], data_byte, true);
148
};
149

150
DMA.prototype.port_count_read = function(channel)
151
{
152
    dbg_log("count read [" + channel + "] -> " + h(this.channel_count[channel]), LOG_DMA);
153
    return this.flipflop_read(this.channel_count[channel]);
154
};
155

156
DMA.prototype.port_addr_write = function(channel, data_byte)
157
{
158
    dbg_log("addr write [" + channel + "] = " + h(data_byte), LOG_DMA);
159

160
    this.channel_addr[channel] =
161
        this.flipflop_get(this.channel_addr[channel], data_byte, false);
162

163
    this.channel_addr_init[channel] =
164
        this.flipflop_get(this.channel_addr_init[channel], data_byte, true);
165
};
166

167
DMA.prototype.port_addr_read = function(channel)
168
{
169
    dbg_log("addr read [" + channel + "] -> " + h(this.channel_addr[channel]), LOG_DMA);
170
    return this.flipflop_read(this.channel_addr[channel]);
171
};
172

173
DMA.prototype.port_pagehi_write = function(channel, data_byte)
174
{
175
    dbg_log("pagehi write [" + channel + "] = " + h(data_byte), LOG_DMA);
176
    this.channel_pagehi[channel] = data_byte;
177
};
178

179
DMA.prototype.port_pagehi_read = function(channel)
180
{
181
    dbg_log("pagehi read [" + channel + "]", LOG_DMA);
182
    return this.channel_pagehi[channel];
183
};
184

185
DMA.prototype.port_page_write = function(channel, data_byte)
186
{
187
    dbg_log("page write [" + channel + "] = " + h(data_byte), LOG_DMA);
188
    this.channel_page[channel] = data_byte;
189
};
190

191
DMA.prototype.port_page_read = function(channel)
192
{
193
    dbg_log("page read [" + channel + "]", LOG_DMA);
194
    return this.channel_page[channel];
195
};
196

197
DMA.prototype.port_singlemask_write = function(channel_offset, data_byte)
198
{
199
    var channel = (data_byte & 0x3) + channel_offset;
200
    var value = data_byte & 0x4 ? 1 : 0;
201
    dbg_log("singlechannel mask write [" + channel + "] = " + value, LOG_DMA);
202
    this.update_mask(channel, value);
203
};
204

205
DMA.prototype.port_multimask_write = function(channel_offset, data_byte)
206
{
207
    dbg_log("multichannel mask write: " + h(data_byte), LOG_DMA);
208
    for(var i = 0; i < 4; i++)
209
    {
210
        this.update_mask(channel_offset + i, data_byte & (1 << i));
211
    }
212
};
213

214
DMA.prototype.port_multimask_read = function(channel_offset)
215
{
216
    var value = 0;
217
    value |= this.channel_mask[channel_offset + 0];
218
    value |= this.channel_mask[channel_offset + 1] << 1;
219
    value |= this.channel_mask[channel_offset + 2] << 2;
220
    value |= this.channel_mask[channel_offset + 3] << 3;
221
    dbg_log("multichannel mask read: " + h(value), LOG_DMA);
222
    return value;
223
};
224

225
DMA.prototype.port_mode_write = function(channel_offset, data_byte)
226
{
227
    var channel = (data_byte & 0x3) + channel_offset;
228
    dbg_log("mode write [" + channel + "] = " + h(data_byte), LOG_DMA);
229
    this.channel_mode[channel] = data_byte;
230
};
231

232
DMA.prototype.portC_write = function(data_byte)
233
{
234
    dbg_log("flipflop reset", LOG_DMA);
235
    this.lsb_msb_flipflop = 0;
236
};
237

238
DMA.prototype.on_unmask = function(fn, this_value)
239
{
240
    this.unmask_listeners.push({
241
        fn: fn,
242
        this_value: this_value,
243
    });
244
};
245

246
DMA.prototype.update_mask = function(channel, value)
247
{
248
    if(this.channel_mask[channel] !== value)
249
    {
250
        this.channel_mask[channel] = value;
251

252
        if(!value)
253
        {
254
            dbg_log("firing on_unmask(" + channel + ")", LOG_DMA);
255
            for(var i = 0; i < this.unmask_listeners.length; i++)
256
            {
257
                this.unmask_listeners[i].fn.call(
258
                    this.unmask_listeners[i].this_value,
259
                    channel
260
                );
261
            }
262
        }
263
    }
264
};
265

266
// read data, write to memory
267
DMA.prototype.do_read = function(buffer, start, len, channel, fn)
268
{
269
    var read_count = this.count_get_8bit(channel),
270
        addr = this.address_get_8bit(channel);
271

272
    dbg_log("DMA write channel " + channel, LOG_DMA);
273
    dbg_log("to " + h(addr) + " len " + h(read_count), LOG_DMA);
274

275
    if(len < read_count)
276
    {
277
        dbg_log("DMA should read more than provided: " + h(len) + " " + h(read_count), LOG_DMA);
278
    }
279

280
    if(start + read_count > buffer.byteLength)
281
    {
282
        dbg_log("DMA read outside of buffer", LOG_DMA);
283
        fn(true);
284
    }
285
    else
286
    {
287
        var cpu = this.cpu;
288
        this.channel_addr[channel] += read_count;
289

290
        buffer.get(start, read_count, function(data)
291
        {
292
            cpu.write_blob(data, addr);
293
            fn(false);
294
        });
295
    }
296
};
297

298
// write data, read memory
299
// start and len in bytes
300
DMA.prototype.do_write = function(buffer, start, len, channel, fn)
301
{
302
    var read_count = (this.channel_count[channel] + 1) & 0xFFFF,
303
        bytes_per_count = channel >= 5 ? 2 : 1,
304
        read_bytes = read_count * bytes_per_count,
305
        addr = this.address_get_8bit(channel),
306
        unfinished = false,
307
        want_more = false,
308
        autoinit = this.channel_mode[channel] & 0x10;
309

310
    dbg_log("DMA write channel " + channel, LOG_DMA);
311
    dbg_log("to " + h(addr) + " len " + h(read_bytes), LOG_DMA);
312

313
    if(len < read_bytes)
314
    {
315
        dbg_log("DMA should read more than provided", LOG_DMA);
316
        read_count = Math.floor(len / bytes_per_count);
317
        read_bytes = read_count * bytes_per_count;
318
        unfinished = true;
319
    }
320
    else if(len > read_bytes)
321
    {
322
        dbg_log("DMA attempted to read more than provided", LOG_DMA);
323
        want_more = true;
324
    }
325

326
    if(start + read_bytes > buffer.byteLength)
327
    {
328
        dbg_log("DMA write outside of buffer", LOG_DMA);
329
        fn(true);
330
    }
331
    else
332
    {
333
        this.channel_addr[channel] += read_count;
334
        this.channel_count[channel] -= read_count;
335
        // when complete, counter should underflow to 0xFFFF
336

337
        if(!unfinished && autoinit)
338
        {
339
            dbg_log("DMA autoinit", LOG_DMA);
340
            this.channel_addr[channel] = this.channel_addr_init[channel];
341
            this.channel_count[channel] = this.channel_count_init[channel];
342
        }
343

344
        buffer.set(start,
345
                this.cpu.mem8.subarray(addr, addr + read_bytes),
346
                () =>
347
                {
348
                    if(want_more && autoinit)
349
                    {
350
                        dbg_log("DMA continuing from start", LOG_DMA);
351
                        this.do_write(buffer, start + read_bytes, len - read_bytes, channel, fn);
352
                    }
353
                    else
354
                    {
355
                        fn(false);
356
                    }
357
                }
358
            );
359
    }
360
};
361

362
DMA.prototype.address_get_8bit = function(channel)
363
{
364
    var addr = this.channel_addr[channel];
365

366
    // http://wiki.osdev.org/ISA_DMA#16_bit_issues
367
    if(channel >= 5)
368
    {
369
        addr = (addr << 1);
370
    }
371

372
    addr &= 0xFFFF;
373
    addr |= this.channel_page[channel] << 16;
374
    addr |= this.channel_pagehi[channel] << 24;
375

376
    return addr;
377
};
378

379
DMA.prototype.count_get_8bit = function(channel)
380
{
381
    var count = this.channel_count[channel] + 1;
382

383
    if(channel >= 5)
384
    {
385
        count *= 2;
386
    }
387

388
    return count;
389
};
390

391
DMA.prototype.flipflop_get = function(old_dword, new_byte, continuing)
392
{
393
    if(!continuing)
394
    {
395
        this.lsb_msb_flipflop ^= 1;
396
    }
397

398
    if(this.lsb_msb_flipflop)
399
    {
400
        // low byte
401
        return old_dword & ~0xFF | new_byte;
402
    }
403
    else
404
    {
405
        // high byte
406
        return old_dword & ~0xFF00 | new_byte << 8;
407
    }
408
};
409

410
DMA.prototype.flipflop_read = function(dword)
411
{
412
    this.lsb_msb_flipflop ^= 1;
413

414
    if(this.lsb_msb_flipflop)
415
    {
416
        // low byte
417
        return dword & 0xFF;
418
    }
419
    else
420
    {
421
        // high byte
422
        return (dword >> 8) & 0xFF;
423
    }
424
};
425

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.