SandboXP

Форк
0
/
ps2.js 
732 строки · 18.3 Кб
1
"use strict";
2

3
/** @const */
4
let PS2_LOG_VERBOSE = false;
5

6
/**
7
 * @constructor
8
 * @param {CPU} cpu
9
 * @param {BusConnector} bus
10
 */
11
function PS2(cpu, bus)
12
{
13
    /** @const @type {CPU} */
14
    this.cpu = cpu;
15

16
    /** @const @type {BusConnector} */
17
    this.bus = bus;
18

19
    /** @type {boolean} */
20
    this.enable_mouse_stream = false;
21

22
    /** @type {boolean} */
23
    this.use_mouse = false;
24

25
    /** @type {boolean} */
26
    this.have_mouse = true;
27

28
    /** @type {number} */
29
    this.mouse_delta_x = 0;
30
    /** @type {number} */
31
    this.mouse_delta_y = 0;
32
    /** @type {number} */
33
    this.mouse_clicks = 0;
34

35
    /** @type {boolean} */
36
    this.have_keyboard = true;
37

38
    /** @type {boolean} */
39
    this.enable_keyboard_stream = false;
40

41
    /** @type {boolean} */
42
    this.next_is_mouse_command = false;
43

44
    /** @type {boolean} */
45
    this.next_read_sample = false;
46

47
    /** @type {boolean} */
48
    this.next_read_led = false;
49

50
    /** @type {boolean} */
51
    this.next_handle_scan_code_set = false;
52

53
    /** @type {boolean} */
54
    this.next_read_rate = false;
55

56
    /** @type {boolean} */
57
    this.next_read_resolution = false;
58

59
    /**
60
     * @type {ByteQueue}
61
     */
62
    this.kbd_buffer = new ByteQueue(1024);
63

64
    this.last_port60_byte = 0;
65

66
    /** @type {number} */
67
    this.sample_rate = 100;
68

69
    /** @type {number} */
70
    this.resolution = 4;
71

72
    /** @type {boolean} */
73
    this.scaling2 = false;
74

75
    /** @type {number} */
76
    this.last_mouse_packet = -1;
77

78
    /**
79
     * @type {ByteQueue}
80
     */
81
    this.mouse_buffer = new ByteQueue(1024);
82

83
    /**
84
     * @type {boolean}
85
     * Also known as DBBOUT OBF - Output Buffer Full flag
86
     */
87
    this.next_byte_is_ready = false;
88

89
    /** @type {boolean} */
90
    this.next_byte_is_aux = false;
91

92
    this.bus.register("keyboard-code", function(code)
93
    {
94
        this.kbd_send_code(code);
95
    }, this);
96

97
    this.bus.register("mouse-click", function(data)
98
    {
99
        this.mouse_send_click(data[0], data[1], data[2]);
100
    }, this);
101

102
    this.bus.register("mouse-delta", function(data)
103
    {
104
        this.mouse_send_delta(data[0], data[1]);
105
    }, this);
106

107
    this.bus.register("mouse-wheel", function(data)
108
    {
109
        // TODO: Mouse Wheel
110
        // http://www.computer-engineering.org/ps2mouse/
111
    }, this);
112

113
    this.command_register = 1 | 4;
114
    // TODO: What should be the initial value?
115
    this.controller_output_port = 0;
116
    this.read_output_register = false;
117
    this.read_command_register = false;
118
    this.read_controller_output_port = false;
119

120
    cpu.io.register_read(0x60, this, this.port60_read);
121
    cpu.io.register_read(0x64, this, this.port64_read);
122

123
    cpu.io.register_write(0x60, this, this.port60_write);
124
    cpu.io.register_write(0x64, this, this.port64_write);
125
}
126

127
PS2.prototype.get_state = function()
128
{
129
    var state = [];
130

131
    state[0] = this.enable_mouse_stream;
132
    state[1] = this.use_mouse;
133
    state[2] = this.have_mouse;
134
    state[3] = this.mouse_delta_x;
135
    state[4] = this.mouse_delta_y;
136
    state[5] = this.mouse_clicks;
137
    state[6] = this.have_keyboard;
138
    state[7] = this.enable_keyboard_stream;
139
    state[8] = this.next_is_mouse_command;
140
    state[9] = this.next_read_sample;
141
    state[10] = this.next_read_led;
142
    state[11] = this.next_handle_scan_code_set;
143
    state[12] = this.next_read_rate;
144
    state[13] = this.next_read_resolution;
145
    //state[14] = this.kbd_buffer;
146
    state[15] = this.last_port60_byte;
147
    state[16] = this.sample_rate;
148
    state[17] = this.resolution;
149
    state[18] = this.scaling2;
150
    //state[19] = this.mouse_buffer;
151
    state[20] = this.command_register;
152
    state[21] = this.read_output_register;
153
    state[22] = this.read_command_register;
154
    state[23] = this.controller_output_port;
155
    state[24] = this.read_controller_output_port;
156

157
    return state;
158
};
159

160
PS2.prototype.set_state = function(state)
161
{
162
    this.enable_mouse_stream = state[0];
163
    this.use_mouse = state[1];
164
    this.have_mouse = state[2];
165
    this.mouse_delta_x = state[3];
166
    this.mouse_delta_y = state[4];
167
    this.mouse_clicks = state[5];
168
    this.have_keyboard = state[6];
169
    this.enable_keyboard_stream = state[7];
170
    this.next_is_mouse_command = state[8];
171
    this.next_read_sample = state[9];
172
    this.next_read_led = state[10];
173
    this.next_handle_scan_code_set = state[11];
174
    this.next_read_rate = state[12];
175
    this.next_read_resolution = state[13];
176
    //this.kbd_buffer = state[14];
177
    this.last_port60_byte = state[15];
178
    this.sample_rate = state[16];
179
    this.resolution = state[17];
180
    this.scaling2 = state[18];
181
    //this.mouse_buffer = state[19];
182
    this.command_register = state[20];
183
    this.read_output_register = state[21];
184
    this.read_command_register = state[22];
185
    this.controller_output_port = state[23];
186
    this.read_controller_output_port = state[24];
187

188
    this.next_byte_is_ready = false;
189
    this.next_byte_is_aux = false;
190
    this.kbd_buffer.clear();
191
    this.mouse_buffer.clear();
192

193
    this.bus.send("mouse-enable", this.use_mouse);
194
};
195

196
PS2.prototype.raise_irq = function()
197
{
198
    if(this.next_byte_is_ready)
199
    {
200
        // Wait until previous byte is read
201
        // http://halicery.com/Hardware/8042/8042_1503033_TXT.htm
202
        return;
203
    }
204

205
    // Kbd has priority over aux
206
    if(this.kbd_buffer.length)
207
    {
208
        this.kbd_irq();
209
    }
210
    else if(this.mouse_buffer.length)
211
    {
212
        this.mouse_irq();
213
    }
214
};
215

216
PS2.prototype.mouse_irq = function()
217
{
218
    this.next_byte_is_ready = true;
219
    this.next_byte_is_aux = true;
220

221
    if(this.command_register & 2)
222
    {
223
        dbg_log("Mouse irq", LOG_PS2);
224

225
        // Pulse the irq line
226
        // Note: can't lower immediately after rising, so lower before rising
227
        // http://www.os2museum.com/wp/ibm-ps2-model-50-keyboard-controller/
228
        this.cpu.device_lower_irq(12);
229
        this.cpu.device_raise_irq(12);
230
    }
231
};
232

233
PS2.prototype.kbd_irq = function()
234
{
235
    this.next_byte_is_ready = true;
236
    this.next_byte_is_aux = false;
237

238
    if(this.command_register & 1)
239
    {
240
        dbg_log("Keyboard irq", LOG_PS2);
241

242
        // Pulse the irq line
243
        // Note: can't lower immediately after rising, so lower before rising
244
        // http://www.os2museum.com/wp/ibm-ps2-model-50-keyboard-controller/
245
        this.cpu.device_lower_irq(1);
246
        this.cpu.device_raise_irq(1);
247
    }
248
};
249

250
PS2.prototype.kbd_send_code = function(code)
251
{
252
    if(this.enable_keyboard_stream)
253
    {
254
        dbg_log("adding kbd code: " + h(code), LOG_PS2);
255
        this.kbd_buffer.push(code);
256
        this.raise_irq();
257
    }
258
};
259

260
PS2.prototype.mouse_send_delta = function(delta_x, delta_y)
261
{
262
    if(!this.have_mouse || !this.use_mouse)
263
    {
264
        return;
265
    }
266

267
    // note: delta_x or delta_y can be floating point numbers
268

269
    var factor = this.resolution * this.sample_rate / 80;
270

271
    this.mouse_delta_x += delta_x * factor;
272
    this.mouse_delta_y += delta_y * factor;
273

274
    if(this.enable_mouse_stream)
275
    {
276
        var change_x = this.mouse_delta_x | 0,
277
            change_y = this.mouse_delta_y | 0;
278

279
        if(change_x || change_y)
280
        {
281
            var now = Date.now();
282

283
            //if(now - this.last_mouse_packet < 1000 / this.sample_rate)
284
            //{
285
            //    // TODO: set timeout
286
            //    return;
287
            //}
288

289
            this.mouse_delta_x -= change_x;
290
            this.mouse_delta_y -= change_y;
291

292
            this.send_mouse_packet(change_x, change_y);
293
        }
294
    }
295
};
296

297
PS2.prototype.mouse_send_click = function(left, middle, right)
298
{
299
    if(!this.have_mouse || !this.use_mouse)
300
    {
301
        return;
302
    }
303

304
    this.mouse_clicks = left | right << 1 | middle << 2;
305

306
    if(this.enable_mouse_stream)
307
    {
308
        this.send_mouse_packet(0, 0);
309
    }
310
};
311

312
PS2.prototype.send_mouse_packet = function(dx, dy)
313
{
314
    var info_byte =
315
            (dy < 0) << 5 |
316
            (dx < 0) << 4 |
317
            1 << 3 |
318
            this.mouse_clicks,
319
        delta_x = dx,
320
        delta_y = dy;
321

322
    this.last_mouse_packet = Date.now();
323

324
    //if(this.scaling2)
325
    //{
326
    //    // only in automatic packets, not 0xEB requests
327
    //    delta_x = this.apply_scaling2(delta_x);
328
    //    delta_y = this.apply_scaling2(delta_y);
329
    //}
330

331
    this.mouse_buffer.push(info_byte);
332
    this.mouse_buffer.push(delta_x);
333
    this.mouse_buffer.push(delta_y);
334

335
    if(PS2_LOG_VERBOSE)
336
    {
337
        dbg_log("adding mouse packets: " + [info_byte, dx, dy], LOG_PS2);
338
    }
339

340
    this.raise_irq();
341
};
342

343
PS2.prototype.apply_scaling2 = function(n)
344
{
345
    // http://www.computer-engineering.org/ps2mouse/#Inputs.2C_Resolution.2C_and_Scaling
346
    var abs = Math.abs(n),
347
        sign = n >> 31;
348

349
    switch(abs)
350
    {
351
        case 0:
352
        case 1:
353
        case 3:
354
            return n;
355
        case 2:
356
            return sign;
357
        case 4:
358
            return 6 * sign;
359
        case 5:
360
            return 9 * sign;
361
        default:
362
            return n << 1;
363
    }
364
};
365

366
PS2.prototype.port60_read = function()
367
{
368
    //dbg_log("port 60 read: " + (buffer[0] || "(none)"));
369

370
    this.next_byte_is_ready = false;
371

372
    if(!this.kbd_buffer.length && !this.mouse_buffer.length)
373
    {
374
        // should not happen
375
        dbg_log("Port 60 read: Empty", LOG_PS2);
376
        return this.last_port60_byte;
377
    }
378

379
    if(this.next_byte_is_aux)
380
    {
381
        this.cpu.device_lower_irq(12);
382
        this.last_port60_byte = this.mouse_buffer.shift();
383
        dbg_log("Port 60 read (mouse): " + h(this.last_port60_byte), LOG_PS2);
384
    }
385
    else
386
    {
387
        this.cpu.device_lower_irq(1);
388
        this.last_port60_byte = this.kbd_buffer.shift();
389
        dbg_log("Port 60 read (kbd)  : " + h(this.last_port60_byte), LOG_PS2);
390
    }
391

392
    if(this.kbd_buffer.length || this.mouse_buffer.length)
393
    {
394
        this.raise_irq();
395
    }
396

397
    return this.last_port60_byte;
398
};
399

400
PS2.prototype.port64_read = function()
401
{
402
    // status port
403

404
    var status_byte = 0x10;
405

406
    if(this.next_byte_is_ready)
407
    {
408
        status_byte |= 0x1;
409
    }
410
    if(this.next_byte_is_aux)
411
    {
412
        status_byte |= 0x20;
413
    }
414

415
    dbg_log("port 64 read: " + h(status_byte), LOG_PS2);
416

417
    return status_byte;
418
};
419

420
PS2.prototype.port60_write = function(write_byte)
421
{
422
    dbg_log("port 60 write: " + h(write_byte), LOG_PS2);
423

424
    if(this.read_command_register)
425
    {
426
        this.command_register = write_byte;
427
        this.read_command_register = false;
428

429
        // not sure, causes "spurious ack" in Linux
430
        //this.kbd_buffer.push(0xFA);
431
        //this.kbd_irq();
432

433
        dbg_log("Keyboard command register = " + h(this.command_register), LOG_PS2);
434
    }
435
    else if(this.read_output_register)
436
    {
437
        this.read_output_register = false;
438

439
        this.mouse_buffer.clear();
440
        this.mouse_buffer.push(write_byte);
441
        this.mouse_irq();
442
    }
443
    else if(this.next_read_sample)
444
    {
445
        this.next_read_sample = false;
446
        this.mouse_buffer.clear();
447
        this.mouse_buffer.push(0xFA);
448

449
        this.sample_rate = write_byte;
450
        dbg_log("mouse sample rate: " + h(write_byte), LOG_PS2);
451
        if(!this.sample_rate)
452
        {
453
            dbg_log("invalid sample rate, reset to 100", LOG_PS2);
454
            this.sample_rate = 100;
455
        }
456
        this.mouse_irq();
457
    }
458
    else if(this.next_read_resolution)
459
    {
460
        this.next_read_resolution = false;
461
        this.mouse_buffer.clear();
462
        this.mouse_buffer.push(0xFA);
463

464
        if(write_byte > 3)
465
        {
466
            this.resolution = 4;
467
            dbg_log("invalid resolution, resetting to 4", LOG_PS2);
468
        }
469
        else
470
        {
471
            this.resolution = 1 << write_byte;
472
            dbg_log("resolution: " + this.resolution, LOG_PS2);
473
        }
474
        this.mouse_irq();
475
    }
476
    else if(this.next_read_led)
477
    {
478
        // nope
479
        this.next_read_led = false;
480
        this.kbd_buffer.push(0xFA);
481
        this.kbd_irq();
482
    }
483
    else if(this.next_handle_scan_code_set)
484
    {
485
        this.next_handle_scan_code_set = false;
486

487
        this.kbd_buffer.push(0xFA);
488
        this.kbd_irq();
489

490
        if(write_byte)
491
        {
492
            // set scan code set
493
        }
494
        else
495
        {
496
            this.kbd_buffer.push(2);
497
        }
498
    }
499
    else if(this.next_read_rate)
500
    {
501
        // nope
502
        this.next_read_rate = false;
503
        this.kbd_buffer.push(0xFA);
504
        this.kbd_irq();
505
    }
506
    else if(this.next_is_mouse_command)
507
    {
508
        this.next_is_mouse_command = false;
509
        dbg_log("Port 60 data register write: " + h(write_byte), LOG_PS2);
510

511
        if(!this.have_mouse)
512
        {
513
            return;
514
        }
515

516
        // send ack
517
        this.kbd_buffer.clear();
518
        this.mouse_buffer.clear();
519
        this.mouse_buffer.push(0xFA);
520

521
        switch(write_byte)
522
        {
523
        case 0xE6:
524
            // set scaling to 1:1
525
            dbg_log("Scaling 1:1", LOG_PS2);
526
            this.scaling2 = false;
527
            break;
528
        case 0xE7:
529
            // set scaling to 2:1
530
            dbg_log("Scaling 2:1", LOG_PS2);
531
            this.scaling2 = true;
532
            break;
533
        case 0xE8:
534
            // set mouse resolution
535
            this.next_read_resolution = true;
536
            break;
537
        case 0xE9:
538
            // status request - send one packet
539
            this.send_mouse_packet(0, 0);
540
            break;
541
        case 0xEB:
542
            // request single packet
543
            dbg_log("unimplemented request single packet", LOG_PS2);
544
            this.send_mouse_packet(0, 0);
545
            break;
546
        case 0xF2:
547
            //  MouseID Byte
548
            this.mouse_buffer.push(0);
549
            this.mouse_buffer.push(0);
550

551
            this.mouse_clicks = this.mouse_delta_x = this.mouse_delta_y = 0;
552
            break;
553
        case 0xF3:
554
            // sample rate
555
            this.next_read_sample = true;
556
            break;
557
        case 0xF4:
558
            // enable streaming
559
            this.enable_mouse_stream = true;
560
            this.use_mouse = true;
561
            this.bus.send("mouse-enable", true);
562

563
            this.mouse_clicks = this.mouse_delta_x = this.mouse_delta_y = 0;
564
            break;
565
        case 0xF5:
566
            // disable streaming
567
            this.enable_mouse_stream = false;
568
            break;
569
        case 0xF6:
570
            // set defaults
571
            this.enable_mouse_stream = false;
572
            this.sample_rate = 100;
573
            this.scaling2 = false;
574
            this.resolution = 4;
575
            break;
576
        case 0xFF:
577
            // reset, send completion code
578
            dbg_log("Mouse reset", LOG_PS2);
579
            this.mouse_buffer.push(0xAA);
580
            this.mouse_buffer.push(0);
581

582
            this.use_mouse = true;
583
            this.bus.send("mouse-enable", true);
584

585
            this.enable_mouse_stream = false;
586
            this.sample_rate = 100;
587
            this.scaling2 = false;
588
            this.resolution = 4;
589

590
            this.mouse_clicks = this.mouse_delta_x = this.mouse_delta_y = 0;
591
            break;
592

593
        default:
594
            dbg_log("Unimplemented mouse command: " + h(write_byte), LOG_PS2);
595
        }
596

597
        this.mouse_irq();
598
    }
599
    else if (this.read_controller_output_port)
600
    {
601
        this.read_controller_output_port = false;
602
        this.controller_output_port = write_byte;
603
        // If we ever want to implement A20 masking, here is where 
604
        // we should turn the masking off if the second bit is on
605
    }
606
    else
607
    {
608
        dbg_log("Port 60 data register write: " + h(write_byte), LOG_PS2);
609

610
        // send ack
611
        this.mouse_buffer.clear();
612
        this.kbd_buffer.clear();
613
        this.kbd_buffer.push(0xFA);
614

615
        switch(write_byte)
616
        {
617
        case 0xED:
618
            this.next_read_led = true;
619
            break;
620
        case 0xF0:
621
            // get/set scan code set
622
            this.next_handle_scan_code_set = true;
623
            break;
624
        case 0xF2:
625
            // identify
626
            this.kbd_buffer.push(0xAB);
627
            this.kbd_buffer.push(83);
628
            break;
629
        case 0xF3:
630
            //  Set typematic rate and delay
631
            this.next_read_rate = true;
632
            break;
633
        case 0xF4:
634
            // enable scanning
635
            dbg_log("kbd enable scanning", LOG_PS2);
636
            this.enable_keyboard_stream = true;
637
            break;
638
        case 0xF5:
639
            // disable scanning
640
            dbg_log("kbd disable scanning", LOG_PS2);
641
            this.enable_keyboard_stream = false;
642
            break;
643
        case 0xF6:
644
            // reset defaults
645
            //this.enable_keyboard_stream = false;
646
            break;
647
        case 0xFF:
648
            this.kbd_buffer.clear();
649
            this.kbd_buffer.push(0xFA);
650
            this.kbd_buffer.push(0xAA);
651
            this.kbd_buffer.push(0);
652
            break;
653
        default:
654
            dbg_log("Unimplemented keyboard command: " + h(write_byte), LOG_PS2);
655
        }
656

657
        this.kbd_irq();
658
    }
659
};
660

661
PS2.prototype.port64_write = function(write_byte)
662
{
663
    dbg_log("port 64 write: " + h(write_byte), LOG_PS2);
664

665
    switch(write_byte)
666
    {
667
    case 0x20:
668
        this.kbd_buffer.clear();
669
        this.mouse_buffer.clear();
670
        this.kbd_buffer.push(this.command_register);
671
        this.kbd_irq();
672
        break;
673
    case 0x60:
674
        this.read_command_register = true;
675
        break;
676
    case 0xD1:
677
        this.read_controller_output_port = true;
678
        break;
679
    case 0xD3:
680
        this.read_output_register = true;
681
        break;
682
    case 0xD4:
683
        this.next_is_mouse_command = true;
684
        break;
685
    case 0xA7:
686
        // Disable second port
687
        dbg_log("Disable second port", LOG_PS2);
688
        this.command_register |= 0x20;
689
        break;
690
    case 0xA8:
691
        // Enable second port
692
        dbg_log("Enable second port", LOG_PS2);
693
        this.command_register &= ~0x20;
694
        break;
695
    case 0xA9:
696
        // test second ps/2 port
697
        this.kbd_buffer.clear();
698
        this.mouse_buffer.clear();
699
        this.kbd_buffer.push(0);
700
        this.kbd_irq();
701
        break;
702
    case 0xAA:
703
        this.kbd_buffer.clear();
704
        this.mouse_buffer.clear();
705
        this.kbd_buffer.push(0x55);
706
        this.kbd_irq();
707
        break;
708
    case 0xAB:
709
        // Test first PS/2 port
710
        this.kbd_buffer.clear();
711
        this.mouse_buffer.clear();
712
        this.kbd_buffer.push(0);
713
        this.kbd_irq();
714
        break;
715
    case 0xAD:
716
        // Disable Keyboard
717
        dbg_log("Disable Keyboard", LOG_PS2);
718
        this.command_register |= 0x10;
719
        break;
720
    case 0xAE:
721
        // Enable Keyboard
722
        dbg_log("Enable Keyboard", LOG_PS2);
723
        this.command_register &= ~0x10;
724
        break;
725
    case 0xFE:
726
        dbg_log("CPU reboot via PS2");
727
        this.cpu.reboot_internal();
728
        break;
729
    default:
730
        dbg_log("port 64: Unimplemented command byte: " + h(write_byte), LOG_PS2);
731
    }
732
};
733

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

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

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

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