source: xtideuniversalbios/trunk/Assembly_Library/Inc/Emulate.inc

Last change on this file was 624, checked in by krille_n_, 19 months ago

Changes:

  • The BIOS version string has been changed to show the repository revision number instead of the useless "v2.0.0 beta 3+" string. In other words, the seemingly never ending beta is finally over! The version string is now updated by TortoiseSVN client side hook scripts (residing in \Tools) to be used when committing changes to the repository. It should also be possible to use these scripts with other subversion clients under Windows since they are essentially just regular batch (cmd) files!
  • The eSEG_STR macro has been changed to always disable interrupts. The workaround used for the buggy, early revisions of the 8088/8086 CPUs apparently does not work. Many thanks to Jim Leonard (Trixter) for reporting this problem!
  • Minor optimizations to the eBSF and eBSR macros.
File size: 16.5 KB
Line 
1; Project name  :   Emulation library
2; Description   :   Macros for emulating later x86 instructions with older
3;                   processors.
4;                   Macros are used so optimized builds can be done
5;                   easily for different processors.
6;
7;                   This file must be first to be included to
8;                   any source file.
9%ifndef EMULATE_INC
10%define EMULATE_INC
11
12; Defines for processor support (should be set in makefile).
13; Unsupported instructions will be emulated using macros.
14; If AT class PC is used (instead of XT), define USE_AT
15
16;%define USE_186                ; Define to use 18x/V20/V30 instructions
17;%define USE_286                ; Define to use 286 instructions
18;%define USE_386                ; Define to use 386 instructions
19;%define USE_AT                 ; Define for AT class machine
20
21%ifdef USE_NEC_V                ; This will run on NEC V20/V30 processors only
22    %define USE_186             ; Define to use 18x/V20/V30 instructions
23    %ifdef USE_UNDOC_INTEL OR USE_286 OR USE_386
24        %fatal "Conflicting processor define used together with USE_NEC_V!"
25    %endif
26%endif
27
28%ifdef USE_386
29    %define USE_286             ; Define to use 286 instructions
30%endif
31%ifdef USE_286
32    %define USE_186             ; Define to use 18x/V20/V30 instructions
33    %define USE_UNDOC_INTEL     ; Not supported by NEC V20/V30
34%endif
35
36%ifdef USE_386
37    CPU 386                     ; Allow instructions up to 386
38%elifdef USE_286
39    CPU 286                     ; Allow instructions up to 286
40%elifdef USE_186
41    CPU 186                     ; Allow instructions up to 188/186/V20/V30
42%else
43    CPU 8086                    ; Allow 8088/8086 instructions only
44%endif
45
46BITS 16                         ; Set 16 bit code generation
47
48; Alignments for jump targets.
49; Following values are optimal for different processor types:
50; 286 and 386SX         WORD (16-bit, 2 bytes)
51; 386DX and 486         DWORD (32-bit, 4 bytes)
52; Pentium and later     QWORD (64-bit, 8 bytes)
53%ifdef USE_AT
54    %ifdef USE_386
55        JUMP_ALIGN      EQU     4
56        WORD_ALIGN      EQU     2
57    %else ; USE_286
58        JUMP_ALIGN      EQU     2
59        WORD_ALIGN      EQU     2
60    %endif
61%else ; XT
62    %ifndef USE_086 ; 8088/V20 CPUs
63        JUMP_ALIGN      EQU     1   ; 2 is optimal for 8086 and NEC V30 CPUs but it's not worth the ROM space for most XT machines with 8088 or NEC V20 CPUs.
64        WORD_ALIGN      EQU     2   ; The same applies here but the cost of ROM space is negligible.
65    %else ; 8086/V30 CPUs
66        JUMP_ALIGN      EQU     2
67        WORD_ALIGN      EQU     2
68    %endif
69%endif
70
71;==========================================================================
72
73;--------------------------------------------------------------------
74; Find String In String
75;
76; FSIS
77;   Parameters:
78;       %1:     String to search for (case-insensitive)
79;       %2:     String to search in
80;   Returns:
81;   strpos:     Position of %1 in %2 if found, 0 if not found
82;--------------------------------------------------------------------
83%macro FSIS 2.nolist
84%defstr s1 %1
85%defstr s2 %2
86%strlen sl1 s1
87%strlen sl2 s2
88%assign strpos 0
89    %if sl1 <= sl2
90        %assign strpos sl2 - sl1 + 1
91        %rep strpos
92            %substr %%ss s2 strpos, sl1
93            %ifidni %%ss, s1
94                %exitrep
95            %else
96                %assign strpos strpos - 1
97            %endif
98        %endrep
99    %endif
100%endmacro
101
102
103%ifdef USE_NEC_V
104    %include "NEC_V.inc"
105%endif
106
107
108;--------------------------------------------------------------------
109; The undocumented instruction SALC (Set AL According to CF).
110; Available on all Intel processors and truly compatible clones.
111; Does not work on the NEC V20/V30 or Sony CXQ70108 processors.
112;
113; eSALC
114;   Parameters:
115;       Nothing
116;   Returns:
117;       AL:     FFh if CF=1
118;               00h if CF=0
119;   Corrupts registers:
120;       Nothing
121;--------------------------------------------------------------------
122%macro eSALC 0
123;   db      0D6h
124    salc
125%endmacro
126
127
128;--------------------------------------------------------------------
129; The AAD instruction (ASCII Adjust before Division).
130; Available on all Intel processors and truly compatible clones.
131; Does not work on the NEC V20/V30 or Sony CXQ70108 processors
132; unless %1 is 10 (0Ah).
133;
134; eAAD
135;   Parameters:
136;       %1:     Any 8 bit number (0...255)
137;   Returns:
138;       AL:     AH * %1 + AL
139;       AH:     0
140;       Flags:  Set according to result
141;   Corrupts registers:
142;       Nothing
143;--------------------------------------------------------------------
144%macro eAAD 1
145%ifndef CHECK_FOR_UNUSED_ENTRYPOINTS
146    %if %1 > 255
147        %error Invalid parameter passed to eAAD (%1 > 255)
148    %else
149        db      0D5h, %1
150    %endif
151%endif
152%endmacro
153
154
155;--------------------------------------------------------------------
156; The AAM instruction (ASCII Adjust after Multiplication).
157;
158; eAAM
159;   Parameters:
160;       %1:     Any 8 bit number except 0 (1...255)
161;   Returns:
162;       AL:     AL MOD %1
163;       AH:     AL / %1
164;       Flags:  Set according to result
165;   Corrupts registers:
166;       Nothing
167;--------------------------------------------------------------------
168%macro eAAM 1
169%ifndef CHECK_FOR_UNUSED_ENTRYPOINTS
170    %if %1 > 255
171        %error Invalid parameter passed to eAAM (%1 > 255)
172    %elif %1 = 0
173        %error Invalid parameter passed to eAAM (%1 = 0). This would cause a divide-by-zero exception!
174    %else
175        db      0D4h, %1
176    %endif
177%endif
178%endmacro
179
180
181;--------------------------------------------------------------------
182; Emulates BSF (Bit Scan Forward) instruction when necessary.
183; BSF is used to find index of least significant bit.
184;
185; eBSF
186;   Parameters:
187;       %1:     Destination WORD Register for bit index (not CX or same as %2!)
188;       %2:     Source WORD operand where to search bit (not CX or same as %1!)
189;   Returns:
190;       %1:     Index of lowest order bit from %2
191;       ZF:     Set if %2 is zero
192;               Cleared if %2 is non-zero
193;   Corrupts registers:
194;       Nothing
195;--------------------------------------------------------------------
196%macro eBSF 2
197%ifndef USE_386
198    FSIS    ], %2               ; %2 is a memory operand?
199    %if strpos
200        cmp     WORD %2, BYTE 0 ; Source operand is zero?
201        je      SHORT %%Return  ;  If so, return with ZF set
202    %else                       ; No, %2 is a register
203        test    %2, %2
204        jz      SHORT %%Return
205    %endif
206
207    push    cx
208
209%ifdef USE_NEC_V
210    mov     cx, -1
211
212ALIGN JUMP_ALIGN
213%%BitLoop:
214    inc     cx
215    eTEST1  %2, cl
216    jz      SHORT %%BitLoop
217    mov     %1, cx
218
219%else ; ~USE_NEC_V
220    mov     cx, 1<<15
221    mov     %1, -1
222
223ALIGN JUMP_ALIGN
224%%BitLoop:
225    rol     cx, 1               ; Prepare to test next bit
226    inc     %1                  ; Increment bit index
227    test    %2, cx              ; Bit set?
228    jz      SHORT %%BitLoop
229%endif
230
231    pop     cx
232
233%%Return:
234;-----------------------------------
235%else ; USE_386
236    bsf     %1, %2
237%endif
238%endmacro
239
240
241;--------------------------------------------------------------------
242; Emulates BSR (Bit Scan Reverse) instruction when necessary.
243; BSR is used to find index of most significant bit.
244;
245; eBSR
246;   Parameters:
247;       %1:     Destination WORD Register for bit index (not CX or same as %2!)
248;       %2:     Source WORD operand where to search bit (not CX or same as %1!)
249;   Returns:
250;       %1:     Index of highest order bit from %2
251;       ZF:     Set if %2 is zero
252;               Cleared if %2 is non-zero
253;   Corrupts registers:
254;       Nothing
255;--------------------------------------------------------------------
256%macro eBSR 2
257%ifndef USE_386
258    FSIS    ], %2               ; %2 is a memory operand?
259    %if strpos
260        cmp     WORD %2, BYTE 0 ; Source operand is zero?
261        je      SHORT %%Return  ;  If so, return with ZF set
262    %else                       ; No, %2 is a register
263        test    %2, %2
264        jz      SHORT %%Return
265    %endif
266
267    push    cx
268
269%ifdef USE_NEC_V
270    mov     cx, 16
271
272ALIGN JUMP_ALIGN
273%%BitLoop:
274    dec     cx
275    eTEST1  %2, cl
276    jz      SHORT %%BitLoop
277    mov     %1, cx
278
279%else ; ~USE_NEC_V
280    mov     cx, 1
281    mov     %1, 16
282
283ALIGN JUMP_ALIGN
284%%BitLoop:
285    ror     cx, 1               ; Prepare to test next bit
286    dec     %1                  ; Decrement bit index
287    test    %2, cx              ; Bit set?
288    jz      SHORT %%BitLoop
289%endif
290
291    pop     cx
292
293%%Return:
294;-----------------------------------
295%else ; USE_386
296    bsr     %1, %2
297%endif
298%endmacro
299
300
301;--------------------------------------------------------------------
302; Conditional Move.
303;
304; eCMOVcc
305;   Parameters:
306;       %1:     Destination data
307;       %2:     Source data
308;   Returns:
309;       Nothing
310;   Corrupts registers:
311;       Nothing
312;--------------------------------------------------------------------
313%macro eCMOVA 2
314    jbe     SHORT %%Return
315    mov     %1, %2
316%%Return:
317%endmacro
318
319%macro eCMOVC 2
320    jnc     SHORT %%Return
321    mov     %1, %2
322%%Return:
323%endmacro
324
325%macro eCMOVNC 2
326    jc      SHORT %%Return
327    mov     %1, %2
328%%Return:
329%endmacro
330
331%macro eCMOVZ 2
332    jnz     SHORT %%Return
333    mov     %1, %2
334%%Return:
335%endmacro
336
337%macro eCMOVNZ 2
338    jz      SHORT %%Return
339    mov     %1, %2
340%%Return:
341%endmacro
342
343%macro eCMOVE 2
344    eCMOVZ  %1, %2
345%endmacro
346
347%macro eCMOVNE 2
348    eCMOVNZ %1, %2
349%endmacro
350
351%macro eCMOVB 2
352    eCMOVC  %1, %2
353%endmacro
354
355%macro eCMOVS 2
356    jns     SHORT %%Return
357    mov     %1, %2
358%%Return:
359%endmacro
360
361%macro eCMOVNS 2
362    js      SHORT %%Return
363    mov     %1, %2
364%%Return:
365%endmacro
366
367
368;--------------------------------------------------------------------
369; Conditional Set.
370;
371; eSETcc
372;   Parameters:
373;       %1:     Destination data
374;   Returns:
375;       Nothing
376;   Corrupts registers:
377;       Flags
378;--------------------------------------------------------------------
379%macro eSETZ 1
380    mov     %1, 0           ; Clear while preserving flags
381    jnz     SHORT %%Return  ; Nothing to set
382    inc     %1
383%%Return:
384%endmacro
385
386%macro eSETNZ 1
387    mov     %1, 0           ; Clear while preserving flags
388    jz      SHORT %%Return  ; Nothing to set
389    inc     %1
390%%Return:
391%endmacro
392
393
394;--------------------------------------------------------------------
395; Moves byte with zero-extending to any Register.
396;
397; eMOVZX
398;   Parameters:
399;       %1:     Destination Register (SP not supported)
400;       %2:     Byte register or byte address
401;   Returns:
402;       Nothing
403;   Corrupts registers:
404;       FLAGS
405;--------------------------------------------------------------------
406%macro eMOVZX 2
407%ifndef USE_386
408    %ifidni %1, ax
409        mov     al, %2
410        xor     ah, ah
411    %elifidni %1, bx
412        mov     bl, %2
413        xor     bh, bh      ; %2 may use BX in effective address
414    %elifidni %1, cx
415        mov     cl, %2
416        xor     ch, ch
417    %elifidni %1, dx
418        mov     dl, %2
419        xor     dh, dh
420    %else   ; SI, DI, BP (all may be used in effective address)
421        FSIS    %1, %2
422        %if strpos
423            push    ax
424            mov     al, %2
425            xor     ah, ah
426            xchg    %1, ax
427            pop     ax
428        %else
429            xchg    %1, ax
430            mov     al, %2
431            xor     ah, ah
432            xchg    %1, ax
433        %endif
434    %endif
435;-----------------------------------
436%else
437    movzx   %1, %2
438%endif
439%endmacro
440
441
442;--------------------------------------------------------------------
443; Emulates PUSHA instruction when necessary.
444;
445; ePUSHA
446;   Parameters:
447;       Nothing
448;   Returns:
449;       Nothing
450;   Corrupts registers:
451;       Nothing
452;--------------------------------------------------------------------
453%macro ePUSHA 0
454%ifndef USE_186
455    push    ax
456    push    cx
457    push    dx
458    push    bx
459    push    sp
460    push    bp
461    push    si
462    push    di
463;-----------------------------------
464%else
465    pusha
466%endif
467%endmacro
468
469
470;--------------------------------------------------------------------
471; Emulates POPA instruction when necessary.
472;
473; ePOPA
474;   Parameters:
475;       Nothing
476;   Returns:
477;       Nothing
478;   Corrupts registers:
479;       Nothing
480;--------------------------------------------------------------------
481%macro ePOPA 0
482%ifndef USE_186
483    pop     di
484    pop     si
485    pop     bp
486    pop     ax      ; Skip SP
487    pop     bx
488    pop     dx
489    pop     cx
490    pop     ax
491;-----------------------------------
492%else
493    popa
494%endif
495%endmacro
496
497
498;--------------------------------------------------------------------
499; Emulates ENTER instruction when necessary.
500;
501; eENTER
502;   Parameters:
503;       %1:     Number of bytes to reserve from stack
504;       %2:     The lexical nesting level (not emulated, set to 0)
505;   Returns:
506;       SS:BP:  Ptr to old BP
507;               ([bp-2] points to highest local stack frame word)
508;   Corrupts registers:
509;       FLAGS
510;--------------------------------------------------------------------
511%macro eENTER 2
512%ifndef USE_186
513    push    bp
514    mov     bp, sp
515    sub     sp, %1
516;-----------------------------------
517%else
518    enter   %1, %2
519%endif
520%endmacro
521
522;--------------------------------------------------------------------
523; Emulates LEAVE instruction when necessary.
524;
525; eLEAVE
526;   Parameters:
527;       Nothing
528;   Returns:
529;       BP:     What it was before eENTER
530;   Corrupts registers:
531;       Nothing
532;--------------------------------------------------------------------
533%macro eLEAVE 0
534%ifndef USE_186
535    mov     sp, bp
536    pop     bp
537;-----------------------------------
538%else
539    leave
540%endif
541%endmacro
542
543
544;--------------------------------------------------------------------
545; Emulates LSS instruction when necessary.
546;
547; eLSS
548;   Parameters:
549;       %1:     Destination register
550;       %2:     Source memory address without brackets
551;   Returns:
552;       IF:     0 (interrupts disabled)
553;   Corrupts registers:
554;       Nothing
555;--------------------------------------------------------------------
556%macro eLSS 2
557%ifndef USE_386
558    cli                         ; Disable interrupts
559    mov     %1, [%2]            ; Load offset
560    mov     ss, [%2+2]          ; Load segment
561;-----------------------------------
562%else
563    lss     %1, [%2]
564%endif
565%endmacro
566
567
568;--------------------------------------------------------------------
569; Repeats string instruction with segment override.
570; This macro prevents 8088/8086 restart bug.
571;
572; eSEG_STR
573;   Parameters:
574;       %1:     REP/REPE/REPZ or REPNE/REPNZ prefix
575;       %2:     Source segment override (destination is always ES)
576;       %3:     String instruction
577;       %4:     An exclamation mark (!) if the state of the IF must
578;               be preserved (can not be used together with CMPS or
579;               SCAS instructions), otherwise it will be set on
580;               return from the macro (i.e. interrupts will be on)
581;       CX:     Repeat count
582;   Returns:
583;       FLAGS for CMPS and SCAS only
584;   Corrupts registers:
585;       FLAGS
586;--------------------------------------------------------------------
587%macro eSEG_STR 3-4
588%ifndef USE_186 ; 8088/8086 has string instruction restart bug when using more than one prefix
589%ifidn %4, !                ; Preserve the IF
590    FSIS    cmps, %3
591%ifn strpos
592    FSIS    scas, %3
593%endif
594%if strpos
595    %error "The state of the IF can not be preserved when using CMPS or SCAS!"
596%endif
597    pushf
598    cli
599    %1                      ; REP is the prefix that can be lost
600    %2                      ; SEG is the prefix that won't be lost
601    %3                      ; String instruction
602    popf
603%else                       ; No need to preserve the IF
604    cli
605    %1
606    %2
607    %3
608    sti
609%endif
610%else   ; No bug on V20/V30/188/186 and later
611    %2
612    %1 %3
613%endif
614%endmacro
615
616
617;--------------------------------------------------------------------
618; Bit shifts and rotates with immediate.
619;
620; eSHIFT_IM
621;   Parameters:
622;       %1:     Shift target
623;       %2:     Number of bits to shift
624;       %3:     Instruction (SHL, SHR, ROL, ROR, RCL, RCR)
625;   Returns:
626;       FLAGS
627;   Corrupts registers:
628;       Nothing
629;--------------------------------------------------------------------
630%macro eSHIFT_IM 3
631%ifndef CHECK_FOR_UNUSED_ENTRYPOINTS
632%ifndef USE_186
633    %ifidni %1, cl
634        times %2    %3      %1, 1
635    %elifidni %1, ch
636        times %2    %3      %1, 1
637    %elifidni %1, cx
638        times %2    %3      %1, 1
639    %else
640        %if %2 > 3  ; Size optimized value
641            push    cx
642            mov     cl, %2
643            %3      %1, cl
644            pop     cx
645        %else
646            times %2    %3      %1, 1
647        %endif
648    %endif
649;-----------------------------------
650%else
651    %3      %1, %2
652%endif
653%endif
654%endmacro
655
656%macro eSHR_IM 2
657    eSHIFT_IM   %1, %2, shr
658%endmacro
659
660%macro eSHL_IM 2
661%ifndef CHECK_FOR_UNUSED_ENTRYPOINTS
662%ifdef USE_386
663    %if %2 = 1
664        FSIS    ], %1
665        %if strpos
666            eSHIFT_IM   %1, %2, shl
667        %else
668            add     %1, %1  ; Same size but faster on 386 and 486.
669        %endif
670    %else
671        eSHIFT_IM   %1, %2, shl
672    %endif
673%else
674    eSHIFT_IM   %1, %2, shl
675%endif
676%endif
677%endmacro
678
679%macro eROR_IM 2
680    eSHIFT_IM   %1, %2, ror
681%endmacro
682
683%macro eROL_IM 2
684    eSHIFT_IM   %1, %2, rol
685%endmacro
686
687%macro eRCR_IM 2
688    eSHIFT_IM   %1, %2, rcr
689%endmacro
690
691%macro eRCL_IM 2
692%ifndef CHECK_FOR_UNUSED_ENTRYPOINTS
693%ifdef USE_386
694    %if %2 = 1
695        FSIS    ], %1
696        %if strpos
697            eSHIFT_IM   %1, %2, rcl
698        %else
699            adc     %1, %1  ; Same size but faster on 386 and 486.
700        %endif
701    %else
702        eSHIFT_IM   %1, %2, rcl
703    %endif
704%else
705    eSHIFT_IM   %1, %2, rcl
706%endif
707%endif
708%endmacro
709
710
711;--------------------------------------------------------------------
712; Emulates PUSH imm instruction when necessary.
713;
714; ePUSH_I
715;   Parameters:
716;       %1:     Immediate to push
717;   Returns:
718;       Nothing
719;   Corrupts registers:
720;       Nothing
721;--------------------------------------------------------------------
722%macro ePUSH_I 1
723%ifndef USE_186
724    push    bp                  ; Immediate goes here
725    push    bp
726    mov     bp, sp
727    mov     WORD [bp+2], %1
728    pop     bp
729;-----------------------------------
730%else
731    push    %1
732%endif
733%endmacro
734
735
736;--------------------------------------------------------------------
737; Emulates PUSH imm instruction when necessary.
738; ePUSH_T uses temporary register for faster performance
739; and smaller code size than ePUSH_I.
740;
741; ePUSH_T
742;   Parameters:
743;       %1:     Temporary Register
744;       %2:     Immediate to push
745;   Returns:
746;       Nothing
747;   Corrupts registers:
748;       %1
749;--------------------------------------------------------------------
750%macro ePUSH_T 2
751%ifndef USE_186
752    %ifidni %2, 0
753        xor     %1, %1
754    %else
755        mov     %1, %2
756    %endif
757    push    %1
758;-----------------------------------
759%else
760    push    %2
761%endif
762%endmacro
763
764
765%endif ; EMULATE_INC
Note: See TracBrowser for help on using the repository browser.