2
\$\begingroup\$

At this point, the user will probably want to know if this system is Long Mode capable, which is indicated by the word CPUID in green. This is determined by attempting to toggle bit 21 in EFLAGS.

5BD  C6460004          mov      [bp], RED       Assume CPUID incapable  
5C1  55                push     bp  
5C2  89E5              mov      bp, sp  
5C4  669C              pushfd                   Preserve original state
5C6  669C              pushfd   
5C8  804EFA20          or       [bp-6], 20H 
5CC  669D              popfd                    Set flag
5CE  669C              pushfd                   Re-read it
5D0  58                pop      ax  
5D1  58                pop      ax              AX = High order bits
5D2  669D              popfd                    Restore original flags
5D4  5D                pop      bp
5D5  0FBAE005          bt       ax, 5           Is bit 21 on    
5D9  7328              jnc      603             No if CY=0

Now we'll test if CPU has Extended Function support.

5DB  C6460006          mov      [bp], BROWN 
5DF  66B801000080      mov      eax, 0x80000000 Ext Func supported? 
5E5  6650              push     eax 
5E7  0FA2              cpuid    
5E9  6659              pop      ecx 
5EB  6639C8            cmp      eax, ecx            
5EE  7613              jna      603         AX >= 80000001H

5F0  C646000E          mov      [bp], YELLOW    
5F4  B001              mov      al, 1       AX = Read feature bits  
5F6  0FA2              cpuid    
5F8  660FBAE21D        bt       edx, 29     Long Mode?  
5FD  7304              jnc      603

5FF  C6460002          mov      [bp], GREEN

Display CPUID with selected color

603  31C9              xor      cx, cx  
605  89CB              mov      bx, cx  
607  8A5E00            mov      bl, [bp]        Selected color  
60A  BA0706            mov      dx, 607H        Line 7 column 8 
60D  E818FF            call     528

Finally, determine state of A20 line and for now only because it works, Fast A20 Gate has been implemented.

610  56                push     si  
611  1E                push     ds  
612  06                push     es  
613  C6460002          mov      [bp], GREEN 
617  B90080            mov      cx, 8000H      Retry count

This sets up DS:SI =    560H    Is volatile as not needed anymore
             ES:DI = 100560H

61A  8CCE              mov       si, cs 
61C  C1E604            shl       si, 4  
61F  81C66000          add       si, 60H       Offset from CS   
623  89F7              mov       di, si 
625  83C710            add       di, 16 
628  31C0              xor       ax, ax 
62A  8ED8              mov       ds, ax 
62C  48                dec       ax 
62D  8EC0              mov       es, ax 
62F  57                push      di 
630  56                push      si 
631  AD                lodsw    
632  F7D8              neg       ax 
634  AB                stosw    
635  3944FE            cmp       [si-2], ax   If equal A20 is off
638  5E                pop       si 
639  5F                pop       di 
63A  0F858E00          jnz       6cc          Bounce A20 already on

Do Fast A20

63E  C646000E          mov       [bp], YELLOW   
642  E492              in        al, 92H    
644  0C02              or        al, 2  
646  E692              out       92H, al    
648  49                dec       cx 
649  75E4              jnz       62f        Execute test again

NOTE: There is 125 bytes reserved here for additional methods of
      enabling A20.

6C8  C6460004          mov       [bp], RED   A20 enabling failed

Display "A20" in one of the three colors

6CC  07                pop       es 
6CD  1F                pop       ds 
6CE  5E                pop       si 
6CF  8A5E00            mov       bl, [bp]   
6D2  31C9              xor       cx, cx 
6D4  B21C              mov       dl, 0x1c   
6D6  E84FFE            call      528    
6D9  EB25              jmp      700

From here to end of sector is where I2H (6DB) lives and can be viewed at the bottom of this post

\$\endgroup\$
2
\$\begingroup\$

These observations are for the most in the order of the code

At this point, the user will probably want to know if this system is Long Mode capable, which is indicated by the word CPUID in green. This is determined by attempting to toggle bit 21 in EFLAGS.

Seeing "This is determined by..." makes this paragraph somewhat mis-leading because it would seem that Long Mode capability is determined merely by looking at EFLAGS[21]. Your code correctly does it in a 3-step process, but that's not something that the reader sees right away. People with less time on their hands might have given up reading already!

push     bp  
mov      bp, sp  
...
or       [bp-6], 20H 
...
pop      bp

You've used the BP register to set a bit in a stacked number. This can be optimized by writing or [esp+2], 20H. Although this instruction requires 2 extra bytes (ASP prefix and SIB byte), you gain 4 bytes from dropping the push, mov, and pop instructions.
There's a trade off here. If in a similar situation, you were to manipulate several stacked values in a row then using BP would be shorter.

bt       ax, 5           Is bit 21 on    
jnc      603             No if CY=0

Bit 5 lives in the AL register. Testing to see if it is on or off can be shortened by not using the double opcode instruction BT. Best choice here is the Modr/m byte less instruction test al, imm8.

test    al, 20h
jz      603          ;Bit 21 is off
5DF  66B801000080      mov      eax, 0x80000000 Ext Func supported? 

Noticed the discrepancy between the dump and the instruction's immediate? If you copy/pasted this code then there's something amiss with the disassembler, else it's a typo.

push     eax 
cpuid    
pop      ecx 
cmp      eax, ecx            
jna      603         AX >= 80000001H
mov      [bp], YELLOW    
mov      al, 1       AX = Read feature bits  
cpuid    
bt       edx, 29     Long Mode?  
jnc      603

With the instruction mov al, 1 you're avoiding to have to write the much longer mov eax, 80000001H. Very understandable but the 1 byte inc ax would still be shorter.
There's also some danger lurking in this code snippet. Who is to say that Intel/AMD in future iterations of the processor will not be returning non-zero values in EAX[8,31] ? Already today some leaf numbers aren't used.

push    eax           ;(1)
cpuid
xchg    ecx, eax      ;Result from CPUID
pop     eax           ;(1)
cmp     ecx, eax
jna     603
mov     [bp], YELLOW
inc     ax
cpuid
bt      edx, 29     Long Mode?  
jnc     603

By popping exactly 80000000h, you guarantee that the inc ax will produce the desired 80000001h.
I've used xchg ecx, eax because it's 1 byte shorter than mov ecx, eax.

mov      bx, cx  
mov      bl, [bp]        Selected color  

Since the first instruction just wants to zero the BH register, you could shave off 1 byte by writing:

movzx    bx, [bp]        Selected color  
push      di
push      si
lodsw    
neg       ax 
stosw    
cmp       [si-2], ax   If equal A20 is off
pop       si 
pop       di

When I first saw this use of string primitives I expected to see a range of addresses to be compared, but you only process a single word. Next code then uses less instructions and is a few bytes shorter. Note the use of the Segment Override Prefix.

mov     ax, [si]
neg     ax
mov     [es:di], ax
cmp     [si], ax

Using the previous code you can also stop using DI and shave off another 4 bytes since you know that DI=SI+16:

mov     ax, [si]
neg     ax
mov     [es:si+16], ax
cmp     [si], ax

One final point about this code is about the use of neg. The negation of zero is also zero. That's why this kind of testing often uses not which guarantees the value gets modified. This doesn't impact your code however since you've chosen an address that certainly does not contain zero. But choosing a fixed address like eg. 21h*4 (DOS isn't loaded for sure) would make setting up SI much shorter and even allow you to not use SI at all.

mov     ax, [0084h]
not     ax
mov     [es:0084h+16], ax
cmp     [0084h], ax
61F  81C66000          add       si, 60H       Offset from CS   

I don't know what happened here, but I think the compiler ought to give you the sign-extended version of this instruction automatically. Bytes 83h,C6h,60h.

648  49                dec       cx 
649  75E4              jnz       62f        Execute test again

This of course is just loop. 1 byte shorter.

My last concern

6D4  B21C              mov       dl, 0x1c

You didn't initialize the DH register because you know it has the correct value from the code above. That's fine but given the separation between setting it up and using it, and considering the note you've included, ...

NOTE: There is 125 bytes reserved here for additional methods of enabling A20.

... I wonder if you will remember to preserve DH the day you're ready to add these additional methods?

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ In future posts, this idea of showing opcodes is not working out as intended and it does require a lot of manual massaging, which leads to discrepancies as in 32 bit values for CPUID. As you've pointed out with DL, the solution was to write a copy of it to that area denoted as scratch as even those 14 values preserved in the preamble will soon to be purged. \$\endgroup\$ – Shift_Left Jul 9 '17 at 15:13
  • 1
    \$\begingroup\$ The NASM assembler does provide additional levels of optimization and I think once enabled, sign extension will be dealt with. To maintain a one to one correlation between source and object, it would be a simple matter of manually optimizing by add si, byte 0x60. \$\endgroup\$ – Shift_Left Jul 9 '17 at 15:27
  • \$\begingroup\$ There was a version of that algorithm quite some time ago where neg vs not was an issue, as I was pointing to an area of memory that had zeros. Now a pointer to a known place in boot code guarantees something other than zero so either works. \$\endgroup\$ – Shift_Left Jul 9 '17 at 15:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.