This repository has been archived by the owner on Dec 24, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 91
/
Copy pathx86op0f.js
1789 lines (1675 loc) · 50.2 KB
/
x86op0f.js
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
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* @fileoverview Implements PCjs 0x0F two-byte opcodes
* @author <a href="mailto:[email protected]">Jeff Parsons</a>
* @version 1.0
* Created 2012-Sep-05
*
* Copyright © 2012-2016 Jeff Parsons <[email protected]>
*
* This file is part of PCjs, which is part of the JavaScript Machines Project (aka JSMachines)
* at <http://jsmachines.net/> and <http://pcjs.org/>.
*
* PCjs 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 3
* of the License, or (at your option) any later version.
*
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCjs. If not,
* see <http://www.gnu.org/licenses/gpl.html>.
*
* You are required to include the above copyright notice in every source code file of every
* copy or modified version of this work, and to display that copyright notice on every screen
* that loads or runs any version of this software (see Computer.COPYRIGHT).
*
* Some PCjs files also attempt to load external resource files, such as character-image files,
* ROM files, and disk image files. Those external resource files are not considered part of the
* PCjs program for purposes of the GNU General Public License, and the author does not claim
* any copyright as to their contents.
*/
"use strict";
if (NODE) {
var X86 = require("./x86");
}
/**
* op=0x0F,0x00 (GRP6 mem/reg)
*
* @this {X86CPU}
*/
X86.opGRP6 = function()
{
var bModRM = this.peekIPByte();
if ((bModRM & 0x38) < 0x10) { // possible reg values: 0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38
this.opFlags |= X86.OPFLAG.NOREAD;
}
this.decodeModGrpWord.call(this, this.aOpGrp6, X86.helpSRCNone);
};
/**
* op=0x0F,0x01 (GRP7 mem/reg)
*
* @this {X86CPU}
*/
X86.opGRP7 = function()
{
var bModRM = this.peekIPByte();
if (!(bModRM & 0x10)) {
this.opFlags |= X86.OPFLAG.NOREAD;
}
this.decodeModGrpWord.call(this, X86.aOpGrp7, X86.helpSRCNone);
};
/**
* opLAR()
*
* op=0x0F,0x02 (LAR reg,mem/reg)
*
* @this {X86CPU}
*/
X86.opLAR = function()
{
/*
* TODO: Consider swapping out this function whenever setProtMode() changes the mode to real-mode or V86-mode.
*/
if (!(this.regCR0 & X86.CR0.MSW.PE) || I386 && (this.regPS & X86.PS.VM)) {
X86.opInvalid.call(this);
return;
}
this.decodeModRegWord.call(this, X86.fnLAR);
};
/**
* opLSL()
*
* op=0x0F,0x03 (LSL reg,mem/reg)
*
* @this {X86CPU}
*/
X86.opLSL = function()
{
/*
* TODO: Consider swapping out this function whenever setProtMode() changes the mode to real-mode or V86-mode.
*/
if (!(this.regCR0 & X86.CR0.MSW.PE) || I386 && (this.regPS & X86.PS.VM)) {
X86.opInvalid.call(this);
return;
}
this.decodeModRegWord.call(this, X86.fnLSL);
};
/**
* opLOADALL286()
*
* op=0x0F,0x05 (LOADALL)
*
* From the "Undocumented iAPX 286 Test Instruction" document at http://www.pcjs.org/pubs/pc/reference/intel/80286/loadall/:
*
* Physical Address (Hex) Associated CPU Register
* 800-805 None
* 806-807 MSW
* 808-815 None
* 816-817 TR
* 818-819 Flag word
* 81A-81B IP
* 81C-81D LDT
* 81E-81F DS
* 820-821 SS
* 822-823 CS
* 824-825 ES
* 826-827 DI
* 828-829 SI
* 82A-82B BP
* 82C-82D SP
* 82E-82F BX
* 830-831 DX
* 832-833 CX
* 834-835 AX
* 836-83B ES descriptor cache
* 83C-841 CS descriptor cache
* 842-847 SS descriptor cache
* 848-84D DS descriptor cache
* 84E-853 GDTR
* 854-859 LDT descriptor cache
* 85A-85F IDTR
* 860-865 TSS descriptor cache
*
* @this {X86CPU}
*/
X86.opLOADALL286 = function()
{
if (this.nCPL) {
/*
* To use LOADALL, CPL must be zero.
*/
X86.helpFault.call(this, X86.EXCEPTION.GP_FAULT, 0, 0, true);
return;
}
this.setMSW(this.getShort(0x806));
this.regEDI = this.getShort(0x826);
this.regESI = this.getShort(0x828);
this.regEBP = this.getShort(0x82A);
this.regEBX = this.getShort(0x82E);
this.regEDX = this.getShort(0x830);
this.regECX = this.getShort(0x832);
this.regEAX = this.getShort(0x834);
this.segES.loadDesc6(0x836, this.getShort(0x824));
this.segCS.loadDesc6(0x83C, this.getShort(0x822));
this.segSS.loadDesc6(0x842, this.getShort(0x820));
this.segDS.loadDesc6(0x848, this.getShort(0x81E));
/*
* Unlike LOADALL386, there's no requirement for calling setPS() before loading segment registers;
* in fact, since we're not passing a CPL to setPS(), it may be preferable to have CS (and perhaps SS)
* already loaded, so that setPS() can query the CPL. TODO: Verify that CPL is set correctly.
*/
this.setPS(this.getShort(0x818));
/*
* It's important to call setIP() and setSP() *after* the segCS and segSS loads, so that the CPU's
* linear IP and SP registers (regLIP and regLSP) will be updated properly. Ordinarily that would be
* taken care of by simply using the CPU's setCS() and setSS() functions, but those functions call the
* default descriptor load() functions, and obviously here we must use loadDesc6() instead.
*/
this.setIP(this.getShort(0x81A));
this.setSP(this.getShort(0x82C));
/*
* The bytes at 0x851 and 0x85D "should be zeroes", as per the "Undocumented iAPX 286 Test Instruction"
* document, but the LOADALL issued by RAMDRIVE in PC-DOS 7.0 contains 0xFF in both of those bytes, resulting
* in very large addrGDT and addrIDT values. Obviously, we can't have that, so we load only the low byte
* of the second word for both of those registers.
*/
this.addrGDT = this.getShort(0x84E) | (this.getByte(0x850) << 16);
this.addrGDTLimit = this.addrGDT + this.getShort(0x852);
this.addrIDT = this.getShort(0x85A) | (this.getByte(0x85C) << 16);
this.addrIDTLimit = this.addrIDT + this.getShort(0x85E);
this.segLDT.loadDesc6(0x854, this.getShort(0x81C));
this.segTSS.loadDesc6(0x860, this.getShort(0x816));
/*
* Oddly, the above Intel document gives two contradictory cycle counts for LOADALL: 190 and 195.
* I'm going with 195, since both the PC Magazine Programmer's Technical Reference and Robert Collins
* (http://www.rcollins.org/articles/loadall/tspec_a3_doc.html) agree.
*/
this.nStepCycles -= 195;
/*
* TODO: LOADALL operation still needs to be verified in protected mode....
*/
if (DEBUG && DEBUGGER && (this.regCR0 & X86.CR0.MSW.PE)) this.stopCPU();
};
/**
* opCLTS()
*
* op=0x0F,0x06 (CLTS)
*
* @this {X86CPU}
*/
X86.opCLTS = function()
{
/*
* NOTE: The following code shouldn't need to also test X86.PS.VM, because V86-mode is CPL 3.
*/
if (this.nCPL) {
X86.helpFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
this.regCR0 &= ~X86.CR0.MSW.TS;
this.nStepCycles -= 2;
};
/**
* opLOADALL386()
*
* op=0x0F,0x07 (LOADALL ES:[EDI])
*
* Excerpt from Intel Internal Correspondence on "386 LOADALL Instruction" (undated), available as part of the
* PCjs Project at http://www.pcjs.org/pubs/pc/reference/intel/80386/loadall/
*
* 1.5. 386 LOADALL Memory Format
*
* The following tables define the LOADALL memory format. The LOADALL instruction uses a 512-byte block of
* memory, where the lowest addressed byte is given in ES:[(E)DI]. The area above offset CC hex is used for
* processor dependent registers (temporaries, invisible registers). These are loaded into the processor,
* but will not affect normal program execution. All values in the memory area are read from a four byte field,
* to keep the memory format DWORD aligned, but it is possible to locate memory area at a non-aligned address.
* In this case, the execution time of LOADALL will DOUBLE For this reason, the memory dump area should always
* be DWORD aligned.
*
* Offset Register
* ------ --------
* 0x00 CR0
* 0x04 EFLAGS
* 0x08 EIP
* 0x0C EDI
* 0x10 ESI
* 0x14 EBP
* 0x18 ESP
* 0x1C EBX
* 0x20 EDX
* 0x24 ECX
* 0x28 EAX
* 0x2C DR6
* 0x30 DR7
* 0x34 TSSR(TSSSelector-Word)
* 0x38 LDTR(LDTSelector-Word)
* 0x3C GS
* 0x40 FS
* 0x44 DS
* 0x48 SS
* 0x4C CS
* 0x50 ES
* 0x54 TSS(AR)
* 0x58 TSS(BASE)
* 0x5C TSS(LIMIT)
* 0x60 IDT(AR)
* 0x64 IDT(BASE)
* 0x68 IDT(LIMIT)
* 0x6C GDT(AR)
* 0x70 GDT(BASE)
* 0x74 GDT(LIMIT)
* 0x78 LDT(AR)
* 0x7C LDT(BASE)
* 0x80 LDT(LIMIT)
* 0x84 GS(AR)
* 0x88 GS(BASE)
* 0x8C GS(LIMIT)
* 0x90 FS(AR)
* 0x94 FS(BASE)
* 0x98 FS(LIMIT)
* 0x9C DS(AR)
* 0xA0 DS(BASE)
* 0xA4 DS(LIMIT)
* 0xA8 SS(AR)
* 0xAC SS(BASE)
* 0xB0 SS(LIMIT)
* 0xB4 CS(AR)
* 0xB8 CS(BASE)
* 0xBC CS(LIMIT)
* 0xC0 ES(AR)
* 0xC4 ES(BASE)
* 0xC8 ES(LIMIT)
*
* Each descriptor entry consists of 3 pieces:
*
* AR
* BASE
* LIMIT
*
* The AR part has the same format as the second dword of a segment descriptor except that only the AR byte
* (bits 8-15) and the G and B/D bits (bits 23 and 22) are used. All other bits in the AR field are ignored.
* The BASE and LIMIT parts contain full 32-bit values, fully expanded and unscrambled from the 386 descriptor.
* In particular, the LIMIT field loaded for a page granular segment gives a byte granular limit, so should
* contain the page limit*4096 plus 4095.
*
* @this {X86CPU}
*/
X86.opLOADALL386 = function()
{
if (this.nCPL) {
/*
* To use LOADALL, CPL must be zero.
*/
X86.helpFault.call(this, X86.EXCEPTION.GP_FAULT, 0, 0, true);
return;
}
var addr = this.segES.checkRead(this.regEDI & this.maskAddr, 0xCC);
if (addr !== X86.ADDR_INVALID) {
X86.helpLoadCR0.call(this, this.getLong(addr));
/*
* We need to call setPS() before loading any segment registers, because if the Virtual 8086 Mode (VM)
* bit is set in EFLAGS, the segment registers need to know that.
*/
var accSS = this.getLong(addr + 0xA8);
var cpl = (accSS & X86.DESC.ACC.DPL.MASK) >> X86.DESC.ACC.DPL.SHIFT;
this.setPS(this.getLong(addr + 0x04), cpl);
/*
* TODO: We have no use for the GDT(AR) at offset 0x6C or the IDT(AR) at offset 0x60, because
* we don't manage them as segment registers. Should we?
*/
this.addrGDT = this.getLong(addr + 0x70);
this.addrGDTLimit = this.addrGDT + this.getLong(addr + 0x74);
this.addrIDT = this.getLong(addr + 0x64);
this.addrIDTLimit = this.addrIDT + this.getLong(addr + 0x68);
this.segLDT.loadDesc(this.getLong(addr + 0x38), this.getLong(addr + 0x78), this.getLong(addr + 0x7C), this.getLong(addr + 0x80));
this.segTSS.loadDesc(this.getLong(addr + 0x34), this.getLong(addr + 0x54), this.getLong(addr + 0x58), this.getLong(addr + 0x5C));
this.regEDI = this.getLong(addr + 0x0C);
this.regESI = this.getLong(addr + 0x10);
this.regEBP = this.getLong(addr + 0x14);
this.regEBX = this.getLong(addr + 0x1C);
this.regEDX = this.getLong(addr + 0x20);
this.regECX = this.getLong(addr + 0x24);
this.regEAX = this.getLong(addr + 0x28);
this.segGS.loadDesc(this.getLong(addr + 0x3C), this.getLong(addr + 0x84), this.getLong(addr + 0x88), this.getLong(addr + 0x8C));
this.segFS.loadDesc(this.getLong(addr + 0x40), this.getLong(addr + 0x90), this.getLong(addr + 0x94), this.getLong(addr + 0x98));
this.segDS.loadDesc(this.getLong(addr + 0x44), this.getLong(addr + 0x9C), this.getLong(addr + 0xA0), this.getLong(addr + 0xA4));
this.segSS.loadDesc(this.getLong(addr + 0x48), accSS, this.getLong(addr + 0xAC), this.getLong(addr + 0xB0));
this.segCS.loadDesc(this.getLong(addr + 0x4C), this.getLong(addr + 0xB4), this.getLong(addr + 0xB8), this.getLong(addr + 0xBC));
this.segES.loadDesc(this.getLong(addr + 0x50), this.getLong(addr + 0xC0), this.getLong(addr + 0xC4), this.getLong(addr + 0xC8));
/*
* It's important to call setIP() and setSP() *after* the segCS and segSS loads, so that the CPU's
* linear IP and SP registers (regLIP and regLSP) will be updated properly. Ordinarily that would be
* taken care of by simply using the CPU's setCS() and setSS() functions, but those functions call the
* default descriptor load() functions, and obviously here we must use loadDesc() instead.
*/
this.setIP(this.getLong(addr + 0x08));
this.setSP(this.getLong(addr + 0x18));
/*
* TODO: We need to factor out the code that updates DR6 and DR7 from X86.opMOVdr(), so that we can
* more easily update DR6 and DR7 (which we're simply ignoring for now).
*/
}
/*
* According to Robert Collins (http://www.rcollins.org/articles/loadall/tspec_a3_doc.html), the 80386 LOADALL
* takes 122 cycles. Also, according the above-mentioned Intel document, if the memory buffer is not DWORD aligned,
* execution time will DOUBLE.
*/
this.nStepCycles -= (122 << ((addr & 0x3)? 1 : 0));
};
/**
* opMOVrc()
*
* op=0x0F,0x20 (MOV reg,ctlreg)
*
* NOTE: Since this instruction uses only 32-bit general-purpose registers, our ModRM decoders
* are going to be more hindrance than help, so we fully decode and execute the instruction ourselves.
*
* From PCMag_Prog_TechRef, p.476: "The 80386 executes the MOV to/from control registers (CRn) regardless
* of the setting of the MOD field. The MOD field should be set to 11, but an early 80386 documentation
* error indicated that the MOD field value was a don't care. Early versions of the 80486 detect
* a MOD != 11 as an illegal opcode. This was changed in later versions to ignore the value of MOD.
* Assemblers that generate MOD != 11 for these instructions will fail on some 80486s."
*
* And in fact, the COMPAQ DeskPro 386 ROM BIOS executes this instruction with MOD set to 00, so we have
* to ignore it.
*
* @this {X86CPU}
*/
X86.opMOVrc = function()
{
/*
* NOTE: The following code shouldn't need to also test X86.PS.VM, because V86-mode is CPL 3.
*/
if (this.nCPL) {
/*
* You're not allowed to read control registers if the current privilege level is not zero.
*/
X86.helpFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
var reg;
var bModRM = this.getIPByte();
switch((bModRM & 0x38) >> 3) {
case 0x0:
reg = this.regCR0;
break;
case 0x2:
reg = this.regCR2;
break;
case 0x3:
reg = this.regCR3;
break;
default:
X86.opUndefined.call(this);
return;
}
this.setReg(bModRM & 0x7, reg);
this.nStepCycles -= 6;
/*
* TODO: Implement BACKTRACK for this instruction (although Control registers are not likely to be a conduit for interesting data).
*/
};
/**
* opMOVrd()
*
* op=0x0F,0x21 (MOV reg,dbgreg)
*
* NOTE: Since this instruction uses only 32-bit general-purpose registers, our ModRM decoders
* are going to be more hindrance than help, so we fully decode and execute the instruction ourselves.
*
* @this {X86CPU}
*/
X86.opMOVrd = function()
{
/*
* NOTE: The following code shouldn't need to also test X86.PS.VM, because V86-mode is CPL 3.
*/
if (this.nCPL) {
/*
* You're not allowed to read control registers if the current privilege level is not zero.
*/
X86.helpFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
var bModRM = this.getIPByte();
var iSrc = (bModRM & 0x38) >> 3;
if (iSrc == 4 || iSrc == 5) {
X86.opUndefined.call(this);
return;
}
this.setReg(bModRM & 0x7, this.regDR[iSrc]);
this.nStepCycles -= 22;
/*
* TODO: Implement BACKTRACK for this instruction (although Debug registers are not likely to be a conduit for interesting data).
*/
};
/**
* opMOVcr()
*
* op=0x0F,0x22 (MOV ctlreg,reg)
*
* NOTE: Since this instruction uses only 32-bit general-purpose registers, our ModRM decoders
* are going to be more hindrance than help, so we fully decode and execute the instruction ourselves.
*
* From PCMag_Prog_TechRef, p.476: "The 80386 executes the MOV to/from control registers (CRn) regardless
* of the setting of the MOD field. The MOD field should be set to 11, but an early 80386 documentation
* error indicated that the MOD field value was a don't care. Early versions of the 80486 detect
* a MOD != 11 as an illegal opcode. This was changed in later versions to ignore the value of MOD.
* Assemblers that generate MOD != 11 for these instructions will fail on some 80486s."
*
* And in fact, the COMPAQ DeskPro 386 ROM BIOS executes this instruction with MOD set to 00, so we have
* to ignore it.
*
* @this {X86CPU}
*/
X86.opMOVcr = function()
{
/*
* NOTE: The following code shouldn't need to also test X86.PS.VM, because V86-mode is CPL 3.
*/
if (this.nCPL) {
/*
* You're not allowed to write control registers if the current privilege level is not zero.
*/
X86.helpFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
var bModRM = this.getIPByte();
var reg = this.getReg(bModRM & 0x7);
switch((bModRM & 0x38) >> 3) {
case 0x0:
X86.helpLoadCR0.call(this, reg);
this.nStepCycles -= 10;
break;
case 0x2:
this.regCR2 = reg;
this.nStepCycles -= 4;
break;
case 0x3:
X86.helpLoadCR3.call(this, reg);
this.nStepCycles -= 5;
break;
default:
X86.opUndefined.call(this);
return;
}
/*
* TODO: Implement BACKTRACK for this instruction (although Control registers are not likely to be a conduit for interesting data).
*/
};
/**
* opMOVdr()
*
* op=0x0F,0x23 (MOV dbgreg,reg)
*
* NOTE: Since this instruction uses only 32-bit general-purpose registers, our ModRM decoders
* are going to be more hindrance than help, so we fully decode and execute the instruction ourselves.
*
* @this {X86CPU}
*/
X86.opMOVdr = function()
{
/*
* NOTE: The following code shouldn't need to also test X86.PS.VM, because V86-mode is CPL 3.
*/
if (this.nCPL) {
/*
* You're not allowed to write control registers if the current privilege level is not zero.
*/
X86.helpFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
var bModRM = this.getIPByte();
var iDst = (bModRM & 0x38) >> 3;
if (iDst == 4 || iDst == 5) {
X86.opUndefined.call(this);
return;
}
var regDR = this.getReg(bModRM & 0x7);
if (regDR != this.regDR[iDst]) {
this.checkDebugRegisters(false);
this.regDR[iDst] = regDR;
this.checkDebugRegisters(true);
}
this.nStepCycles -= (iDst < 4? 22 : 14);
/*
* TODO: Implement BACKTRACK for this instruction (although Debug registers are not likely to be a conduit for interesting data).
*/
};
/**
* opMOVrt()
*
* op=0x0F,0x24 (MOV reg,tstreg)
*
* NOTE: Since this instruction uses only 32-bit general-purpose registers, our ModRM decoders
* are going to be more hindrance than help, so we fully decode and execute the instruction ourselves.
*
* @this {X86CPU}
*/
X86.opMOVrt = function()
{
/*
* NOTE: The following code shouldn't need to also test X86.PS.VM, because V86-mode is CPL 3.
*/
if (this.nCPL) {
/*
* You're not allowed to read control registers if the current privilege level is not zero.
*/
X86.helpFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
var bModRM = this.getIPByte();
var iSrc = (bModRM & 0x38) >> 3;
/*
* Only TR6 and TR7 are defined, and only for the 80386 and 80486. From the PC Magazine Prog. TechRef, p.64:
*
* "The 80386 provides two 32-bit test registers, TR6 and TR7, as a mechanism for programmers to verify proper
* operation of the Translation Lookaside Buffer (TLB) when power is applied to the chip. The TLB is a cache used
* internally by the 80386 to translate linear addresses to physical addresses."
*/
if (iSrc < 6) {
X86.opUndefined.call(this);
return;
}
this.setReg(bModRM & 0x7, this.regTR[iSrc]);
this.nStepCycles -= 12;
/*
* TODO: Implement BACKTRACK for this instruction (although Test registers are not likely to be a conduit for interesting data).
*/
};
/**
* opMOVtr()
*
* op=0x0F,0x26 (MOV tstreg,reg)
*
* NOTE: Since this instruction uses only 32-bit general-purpose registers, our ModRM decoders
* are going to be more hindrance than help, so we fully decode and execute the instruction ourselves.
*
* @this {X86CPU}
*/
X86.opMOVtr = function()
{
/*
* NOTE: The following code shouldn't need to also test X86.PS.VM, because V86-mode is CPL 3.
*/
if (this.nCPL) {
/*
* You're not allowed to write control registers if the current privilege level is not zero.
*/
X86.helpFault.call(this, X86.EXCEPTION.GP_FAULT, 0);
return;
}
var bModRM = this.getIPByte();
var iDst = (bModRM & 0x38) >> 3;
/*
* Only TR6 and TR7 are defined, and only for the 80386 and 80486. From the PC Magazine Prog. TechRef, p.64:
*
* "The 80386 provides two 32-bit test registers, TR6 and TR7, as a mechanism for programmers to verify proper
* operation of the Translation Lookaside Buffer (TLB) when power is applied to the chip. The TLB is a cache used
* internally by the 80386 to translate linear addresses to physical addresses."
*/
if (iDst < 6) {
X86.opUndefined.call(this);
return;
}
/*
* TODO: Do something useful with the Test registers.
*/
this.regTR[iDst] = this.getReg(bModRM & 0x7);
this.nStepCycles -= 12;
/*
* TODO: Implement BACKTRACK for this instruction (although Test registers are not likely to be a conduit for interesting data).
*/
};
/*
* NOTE: The following 16 new conditional jumps actually rely on the OPERAND override setting
* for determining whether a signed 16-bit or 32-bit displacement will be fetched, even though
* the ADDRESS override might seem more intuitive. Think of them as instructions that are loading
* a new operand into IP/EIP.
*
* Also, in 16-bit code, even though a signed rel16 value would seem to imply a range of -32768
* to +32767, any location within a 64Kb code segment outside that range can be reached by choosing
* a displacement in the opposite direction, causing the 16-bit value in EIP to underflow or overflow;
* any underflow or overflow doesn't matter, because only the low 16 bits of EIP are updated when a
* 16-bit OPERAND size is in effect.
*
* In fact, for 16-bit jumps, it's simpler to always think of rel16 as an UNSIGNED value added to
* the current EIP, where the result is then truncated to a 16-bit value. This is why we don't have
* to sign-extend rel16 before adding it to the current EIP.
*/
/**
* opJOw()
*
* op=0x0F,0x80 (JO rel16/rel32)
*
* @this {X86CPU}
*/
X86.opJOw = function()
{
var disp = this.getIPWord();
if (this.getOF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
return;
}
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpCFall;
};
/**
* opJNOw()
*
* op=0x0F,0x81 (JNO rel16/rel32)
*
* @this {X86CPU}
*/
X86.opJNOw = function()
{
var disp = this.getIPWord();
if (!this.getOF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
return;
}
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpCFall;
};
/**
* opJCw()
*
* op=0x0F,0x82 (JC rel16/rel32)
*
* @this {X86CPU}
*/
X86.opJCw = function()
{
var disp = this.getIPWord();
if (this.getCF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
return;
}
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpCFall;
};
/**
* opJNCw()
*
* op=0x0F,0x83 (JNC rel16/rel32)
*
* @this {X86CPU}
*/
X86.opJNCw = function()
{
var disp = this.getIPWord();
if (!this.getCF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
return;
}
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpCFall;
};
/**
* opJZw()
*
* op=0x0F,0x84 (JZ rel16/rel32)
*
* @this {X86CPU}
*/
X86.opJZw = function()
{
var disp = this.getIPWord();
if (this.getZF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
return;
}
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpCFall;
};
/**
* opJNZw()
*
* op=0x0F,0x85 (JNZ rel16/rel32)
*
* @this {X86CPU}
*/
X86.opJNZw = function()
{
var disp = this.getIPWord();
if (!this.getZF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
return;
}
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpCFall;
};
/**
* opJBEw()
*
* op=0x0F,0x86 (JBE rel16/rel32)
*
* @this {X86CPU}
*/
X86.opJBEw = function()
{
var disp = this.getIPWord();
if (this.getCF() || this.getZF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
return;
}
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpCFall;
};
/**
* opJNBEw()
*
* op=0x0F,0x87 (JNBE rel16/rel32)
*
* @this {X86CPU}
*/
X86.opJNBEw = function()
{
var disp = this.getIPWord();
if (!this.getCF() && !this.getZF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
return;
}
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpCFall;
};
/**
* opJSw()
*
* op=0x0F,0x88 (JS rel16/rel32)
*
* @this {X86CPU}
*/
X86.opJSw = function()
{
var disp = this.getIPWord();
if (this.getSF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
return;
}
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpCFall;
};
/**
* opJNSw()
*
* op=0x0F,0x89 (JNS rel16/rel32)
*
* @this {X86CPU}
*/
X86.opJNSw = function()
{
var disp = this.getIPWord();
if (!this.getSF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
return;
}
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpCFall;
};
/**
* opJPw()
*
* op=0x0F,0x8A (JP rel16/rel32)
*
* @this {X86CPU}
*/
X86.opJPw = function()
{
var disp = this.getIPWord();
if (this.getPF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
return;
}
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpCFall;
};
/**
* opJNPw()
*
* op=0x0F,0x8B (JNP rel16/rel32)
*
* @this {X86CPU}
*/
X86.opJNPw = function()
{
var disp = this.getIPWord();
if (!this.getPF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
return;
}
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpCFall;
};
/**
* opJLw()
*
* op=0x0F,0x8C (JL rel16/rel32)
*
* @this {X86CPU}
*/
X86.opJLw = function()
{
var disp = this.getIPWord();
if (!this.getSF() != !this.getOF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
return;
}
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpCFall;
};
/**
* opJNLw()
*
* op=0x0F,0x8D (JNL rel16/rel32)
*
* @this {X86CPU}
*/
X86.opJNLw = function()
{
var disp = this.getIPWord();
if (!this.getSF() == !this.getOF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
return;
}
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpCFall;
};
/**
* opJLEw()
*
* op=0x0F,0x8E (JLE rel16/rel32)
*
* @this {X86CPU}
*/
X86.opJLEw = function()
{
var disp = this.getIPWord();
if (this.getZF() || !this.getSF() != !this.getOF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
return;
}
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpCFall;
};
/**
* opJNLEw()
*
* op=0x0F,0x8F (JNLE rel16/rel32)
*
* @this {X86CPU}
*/
X86.opJNLEw = function()
{
var disp = this.getIPWord();
if (!this.getZF() && !this.getSF() == !this.getOF()) {
this.setIP(this.getIP() + disp);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpC;
return;
}
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpCFall;
};
/**
* opSETO()
*
* op=0x0F,0x90 (SETO b)
*
* @this {X86CPU}
*/
X86.opSETO = function()
{
X86.helpSETcc.call(this, X86.fnSETO);
};
/**
* opSETNO()
*
* op=0x0F,0x91 (SETNO b)
*
* @this {X86CPU}
*/
X86.opSETNO = function()
{
X86.helpSETcc.call(this, X86.fnSETO);
};