Annotation of truecrypt/crypto/aes_x86.asm, revision 1.1.1.4

1.1       root        1: 
                      2: ; ---------------------------------------------------------------------------
                      3: ; Copyright (c) 1998-2007, Brian Gladman, Worcester, UK. All rights reserved.
                      4: ; 
                      5: ; LICENSE TERMS
                      6: ; 
                      7: ; The free distribution and use of this software is allowed (with or without
                      8: ; changes) provided that:
                      9: ; 
                     10: ;  1. source code distributions include the above copyright notice, this
                     11: ;     list of conditions and the following disclaimer;
                     12: ; 
                     13: ;  2. binary distributions include the above copyright notice, this list
                     14: ;     of conditions and the following disclaimer in their documentation;
                     15: ; 
                     16: ;  3. the name of the copyright holder is not used to endorse products
                     17: ;     built using this software without specific written permission.
                     18: ; 
                     19: ; DISCLAIMER
                     20: ; 
                     21: ; This software is provided 'as is' with no explicit or implied warranties
                     22: ; in respect of its properties, including, but not limited to, correctness
                     23: ; and/or fitness for purpose.
                     24: ; ---------------------------------------------------------------------------
                     25: ; Issue 20/12/2007
                     26: ;
                     27: ; This code requires ASM_X86_V1C to be set in aesopt.h. It requires the C files
                     28: ; aeskey.c and aestab.c for support.
                     29: 
1.1.1.2   root       30: ;
1.1.1.3   root       31: ; Adapted for TrueCrypt:
1.1.1.4 ! root       32: ; - Compatibility with NASM and GCC
1.1.1.2   root       33: ;
                     34: 
1.1       root       35: ; An AES implementation for x86 processors using the YASM (or NASM) assembler.
                     36: ; This is an assembler implementation that covers encryption and decryption
                     37: ; only and is intended as a replacement of the C file aescrypt.c. It hence
                     38: ; requires the file aeskey.c for keying and aestab.c for the AES tables. It
                     39: ; employs full tables rather than compressed tables.
                     40: 
                     41: ; This code provides the standard AES block size (128 bits, 16 bytes) and the
                     42: ; three standard AES key sizes (128, 192 and 256 bits). It has the same call
                     43: ; interface as my C implementation. The ebx, esi, edi and ebp registers are
                     44: ; preserved across calls but eax, ecx and edx and the artihmetic status flags
                     45: ; are not.  It is also important that the defines below match those used in the
                     46: ; C code.  This code uses the VC++ register saving conentions; if it is used
                     47: ; with another compiler, conventions for using and saving registers may need to
                     48: ; be checked (and calling conventions).  The YASM command line for the VC++
                     49: ; custom build step is:
                     50: ;
                     51: ;    yasm -Xvc -f win32 -o "$(TargetDir)\$(InputName).obj" "$(InputPath)"
                     52: ;
                     53: ;  The calling intefaces are:
                     54: ;
                     55: ;     AES_RETURN aes_encrypt(const unsigned char in_blk[],
                     56: ;                   unsigned char out_blk[], const aes_encrypt_ctx cx[1]);
                     57: ;
                     58: ;     AES_RETURN aes_decrypt(const unsigned char in_blk[],
                     59: ;                   unsigned char out_blk[], const aes_decrypt_ctx cx[1]);
                     60: ;
                     61: ;     AES_RETURN aes_encrypt_key<NNN>(const unsigned char key[],
                     62: ;                                            const aes_encrypt_ctx cx[1]);
                     63: ;
                     64: ;     AES_RETURN aes_decrypt_key<NNN>(const unsigned char key[],
                     65: ;                                            const aes_decrypt_ctx cx[1]);
                     66: ;
                     67: ;     AES_RETURN aes_encrypt_key(const unsigned char key[],
                     68: ;                           unsigned int len, const aes_decrypt_ctx cx[1]);
                     69: ;
                     70: ;     AES_RETURN aes_decrypt_key(const unsigned char key[],
                     71: ;                           unsigned int len, const aes_decrypt_ctx cx[1]);
                     72: ;
                     73: ; where <NNN> is 128, 102 or 256.  In the last two calls the length can be in
                     74: ; either bits or bytes.
                     75: ;
                     76: ; Comment in/out the following lines to obtain the desired subroutines. These
                     77: ; selections MUST match those in the C header file aes.h
                     78: 
                     79: ; %define AES_128                 ; define if AES with 128 bit keys is needed
                     80: ; %define AES_192                 ; define if AES with 192 bit keys is needed
                     81: %define AES_256                 ; define if AES with 256 bit keys is needed
                     82: ; %define AES_VAR                 ; define if a variable key size is needed
                     83: %define ENCRYPTION              ; define if encryption is needed
                     84: %define DECRYPTION              ; define if decryption is needed
                     85: %define AES_REV_DKS             ; define if key decryption schedule is reversed
                     86: %define LAST_ROUND_TABLES       ; define if tables are to be used for last round
                     87: 
                     88: ; offsets to parameters
                     89: 
                     90: in_blk  equ     4   ; input byte array address parameter
                     91: out_blk equ     8   ; output byte array address parameter
                     92: ctx     equ    12   ; AES context structure
                     93: stk_spc equ    20   ; stack space
                     94: %define parms  12   ; parameter space on stack
                     95: 
                     96: ; The encryption key schedule has the following in memory layout where N is the
                     97: ; number of rounds (10, 12 or 14):
                     98: ;
                     99: ; lo: | input key (round 0)  |  ; each round is four 32-bit words
                    100: ;     | encryption round 1   |
                    101: ;     | encryption round 2   |
                    102: ;     ....
                    103: ;     | encryption round N-1 |
                    104: ; hi: | encryption round N   |
                    105: ;
                    106: ; The decryption key schedule is normally set up so that it has the same
                    107: ; layout as above by actually reversing the order of the encryption key
                    108: ; schedule in memory (this happens when AES_REV_DKS is set):
                    109: ;
                    110: ; lo: | decryption round 0   | =              | encryption round N   |
                    111: ;     | decryption round 1   | = INV_MIX_COL[ | encryption round N-1 | ]
                    112: ;     | decryption round 2   | = INV_MIX_COL[ | encryption round N-2 | ]
                    113: ;     ....                       ....
                    114: ;     | decryption round N-1 | = INV_MIX_COL[ | encryption round 1   | ]
                    115: ; hi: | decryption round N   | =              | input key (round 0)  |
                    116: ;
                    117: ; with rounds except the first and last modified using inv_mix_column()
                    118: ; But if AES_REV_DKS is NOT set the order of keys is left as it is for
                    119: ; encryption so that it has to be accessed in reverse when used for
                    120: ; decryption (although the inverse mix column modifications are done)
                    121: ;
                    122: ; lo: | decryption round 0   | =              | input key (round 0)  |
                    123: ;     | decryption round 1   | = INV_MIX_COL[ | encryption round 1   | ]
                    124: ;     | decryption round 2   | = INV_MIX_COL[ | encryption round 2   | ]
                    125: ;     ....                       ....
                    126: ;     | decryption round N-1 | = INV_MIX_COL[ | encryption round N-1 | ]
                    127: ; hi: | decryption round N   | =              | encryption round N   |
                    128: ;
                    129: ; This layout is faster when the assembler key scheduling provided here
                    130: ; is used.
                    131: ;
                    132: ; The DLL interface must use the _stdcall convention in which the number
                    133: ; of bytes of parameter space is added after an @ to the sutine's name.
                    134: ; We must also remove our parameters from the stack before return (see
                    135: ; the do_exit macro). Define DLL_EXPORT for the Dynamic Link Library version.
                    136: 
                    137: ;%define DLL_EXPORT
                    138: 
                    139: ; End of user defines
                    140: 
                    141: %ifdef AES_VAR
                    142: %ifndef AES_128
                    143: %define AES_128
                    144: %endif
                    145: %ifndef AES_192
                    146: %define AES_192
                    147: %endif
                    148: %ifndef AES_256
                    149: %define AES_256
                    150: %endif
                    151: %endif
                    152: 
                    153: %ifdef AES_VAR
                    154: %define KS_LENGTH       60
                    155: %elifdef AES_256
                    156: %define KS_LENGTH       60
                    157: %elifdef AES_192
                    158: %define KS_LENGTH       52
                    159: %else
                    160: %define KS_LENGTH       44
                    161: %endif
                    162: 
                    163: ; These macros implement stack based local variables
                    164: 
                    165: %macro  save 2
                    166:     mov     [esp+4*%1],%2
                    167: %endmacro
                    168: 
                    169: %macro  restore 2
                    170:     mov     %1,[esp+4*%2]
                    171: %endmacro
                    172: 
                    173: ; the DLL has to implement the _stdcall calling interface on return
                    174: ; In this case we have to take our parameters (3 4-byte pointers)
                    175: ; off the stack
                    176: 
                    177: %macro  do_name 1-2 parms
                    178: %ifndef DLL_EXPORT
                    179:     align 32
                    180:     global  %1
                    181: %1:
                    182: %else
                    183:     align 32
                    184:     global  %1@%2
1.1.1.4 ! root      185:     export  _%1@%2
1.1       root      186: %1@%2:
                    187: %endif
                    188: %endmacro
                    189: 
                    190: %macro  do_call 1-2 parms
                    191: %ifndef DLL_EXPORT
                    192:     call    %1
                    193:     add     esp,%2
                    194: %else
                    195:     call    %1@%2
                    196: %endif
                    197: %endmacro
                    198: 
                    199: %macro  do_exit  0-1 parms
                    200: %ifdef DLL_EXPORT
                    201:     ret %1
                    202: %else
                    203:     ret
                    204: %endif
                    205: %endmacro
                    206: 
                    207: %ifdef  ENCRYPTION
                    208: 
1.1.1.4 ! root      209:     extern  t_fn
1.1       root      210: 
1.1.1.4 ! root      211: %define etab_0(x)   [t_fn+4*x]
        !           212: %define etab_1(x)   [t_fn+1024+4*x]
        !           213: %define etab_2(x)   [t_fn+2048+4*x]
        !           214: %define etab_3(x)   [t_fn+3072+4*x]
1.1       root      215: 
                    216: %ifdef LAST_ROUND_TABLES
                    217: 
1.1.1.4 ! root      218:     extern  t_fl
1.1       root      219: 
1.1.1.4 ! root      220: %define eltab_0(x)  [t_fl+4*x]
        !           221: %define eltab_1(x)  [t_fl+1024+4*x]
        !           222: %define eltab_2(x)  [t_fl+2048+4*x]
        !           223: %define eltab_3(x)  [t_fl+3072+4*x]
1.1       root      224: 
                    225: %else
                    226: 
1.1.1.4 ! root      227: %define etab_b(x)   byte [t_fn+3072+4*x]
1.1       root      228: 
                    229: %endif
                    230: 
                    231: ; ROUND FUNCTION.  Build column[2] on ESI and column[3] on EDI that have the
                    232: ; round keys pre-loaded. Build column[0] in EBP and column[1] in EBX.
                    233: ;
                    234: ; Input:
                    235: ;
                    236: ;   EAX     column[0]
                    237: ;   EBX     column[1]
                    238: ;   ECX     column[2]
                    239: ;   EDX     column[3]
                    240: ;   ESI     column key[round][2]
                    241: ;   EDI     column key[round][3]
                    242: ;   EBP     scratch
                    243: ;
                    244: ; Output:
                    245: ;
                    246: ;   EBP     column[0]   unkeyed
                    247: ;   EBX     column[1]   unkeyed
                    248: ;   ESI     column[2]   keyed
                    249: ;   EDI     column[3]   keyed
                    250: ;   EAX     scratch
                    251: ;   ECX     scratch
                    252: ;   EDX     scratch
                    253: 
                    254: %macro rnd_fun 2
                    255: 
                    256:     rol     ebx,16
                    257:     %1      esi, cl, 0, ebp
                    258:     %1      esi, dh, 1, ebp
                    259:     %1      esi, bh, 3, ebp
                    260:     %1      edi, dl, 0, ebp
                    261:     %1      edi, ah, 1, ebp
                    262:     %1      edi, bl, 2, ebp
                    263:     %2      ebp, al, 0, ebp
                    264:     shr     ebx,16
                    265:     and     eax,0xffff0000
                    266:     or      eax,ebx
                    267:     shr     edx,16
                    268:     %1      ebp, ah, 1, ebx
                    269:     %1      ebp, dh, 3, ebx
                    270:     %2      ebx, dl, 2, ebx
                    271:     %1      ebx, ch, 1, edx
                    272:     %1      ebx, al, 0, edx
                    273:     shr     eax,16
                    274:     shr     ecx,16
                    275:     %1      ebp, cl, 2, edx
                    276:     %1      edi, ch, 3, edx
                    277:     %1      esi, al, 2, edx
                    278:     %1      ebx, ah, 3, edx
                    279: 
                    280: %endmacro
                    281: 
                    282: ; Basic MOV and XOR Operations for normal rounds
                    283: 
                    284: %macro  nr_xor  4
                    285:     movzx   %4,%2
                    286:     xor     %1,etab_%3(%4)
                    287: %endmacro
                    288: 
                    289: %macro  nr_mov  4
                    290:     movzx   %4,%2
                    291:     mov     %1,etab_%3(%4)
                    292: %endmacro
                    293: 
                    294: ; Basic MOV and XOR Operations for last round
                    295: 
                    296: %ifdef LAST_ROUND_TABLES
                    297: 
                    298:     %macro  lr_xor  4
                    299:         movzx   %4,%2
                    300:         xor     %1,eltab_%3(%4)
                    301:     %endmacro
                    302: 
                    303:     %macro  lr_mov  4
                    304:         movzx   %4,%2
                    305:         mov     %1,eltab_%3(%4)
                    306:     %endmacro
                    307: 
                    308: %else
                    309: 
                    310:     %macro  lr_xor  4
                    311:         movzx   %4,%2
                    312:         movzx   %4,etab_b(%4)
                    313:     %if %3 != 0
                    314:         shl     %4,8*%3
                    315:     %endif
                    316:         xor     %1,%4
                    317:     %endmacro
                    318: 
                    319:     %macro  lr_mov  4
                    320:         movzx   %4,%2
                    321:         movzx   %1,etab_b(%4)
                    322:     %if %3 != 0
                    323:         shl     %1,8*%3
                    324:     %endif
                    325:     %endmacro
                    326: 
                    327: %endif
                    328: 
                    329: %macro enc_round 0
                    330: 
                    331:     add     ebp,16
                    332:     save    0,ebp
                    333:     mov     esi,[ebp+8]
                    334:     mov     edi,[ebp+12]
                    335: 
                    336:     rnd_fun nr_xor, nr_mov
                    337: 
                    338:     mov     eax,ebp
                    339:     mov     ecx,esi
                    340:     mov     edx,edi
                    341:     restore ebp,0
                    342:     xor     eax,[ebp]
                    343:     xor     ebx,[ebp+4]
                    344: 
                    345: %endmacro
                    346: 
                    347: %macro enc_last_round 0
                    348: 
                    349:     add     ebp,16
                    350:     save    0,ebp
                    351:     mov     esi,[ebp+8]
                    352:     mov     edi,[ebp+12]
                    353: 
                    354:     rnd_fun lr_xor, lr_mov
                    355: 
                    356:     mov     eax,ebp
                    357:     restore ebp,0
                    358:     xor     eax,[ebp]
                    359:     xor     ebx,[ebp+4]
                    360: 
                    361: %endmacro
                    362: 
                    363:     section .text align=32
                    364: 
                    365: ; AES Encryption Subroutine
                    366: 
1.1.1.4 ! root      367:     do_name aes_encrypt
1.1       root      368: 
                    369:     sub     esp,stk_spc
                    370:     mov     [esp+16],ebp
                    371:     mov     [esp+12],ebx
                    372:     mov     [esp+ 8],esi
                    373:     mov     [esp+ 4],edi
                    374: 
                    375:     mov     esi,[esp+in_blk+stk_spc] ; input pointer
                    376:     mov     eax,[esi   ]
                    377:     mov     ebx,[esi+ 4]
                    378:     mov     ecx,[esi+ 8]
                    379:     mov     edx,[esi+12]
                    380: 
                    381:     mov     ebp,[esp+ctx+stk_spc]    ; key pointer
                    382:     movzx   edi,byte [ebp+4*KS_LENGTH]
                    383:     xor     eax,[ebp   ]
                    384:     xor     ebx,[ebp+ 4]
                    385:     xor     ecx,[ebp+ 8]
                    386:     xor     edx,[ebp+12]
                    387: 
                    388: ; determine the number of rounds
                    389: 
                    390:     cmp     edi,10*16
                    391:     je      .3
                    392:     cmp     edi,12*16
                    393:     je      .2
                    394:     cmp     edi,14*16
                    395:     je      .1
                    396:     mov     eax,-1
                    397:     jmp     .5
                    398: 
                    399: .1: enc_round
                    400:     enc_round
                    401: .2: enc_round
                    402:     enc_round
                    403: .3: enc_round
                    404:     enc_round
                    405:     enc_round
                    406:     enc_round
                    407:     enc_round
                    408:     enc_round
                    409:     enc_round
                    410:     enc_round
                    411:     enc_round
                    412:     enc_last_round
                    413: 
                    414:     mov     edx,[esp+out_blk+stk_spc]
                    415:     mov     [edx],eax
                    416:     mov     [edx+4],ebx
                    417:     mov     [edx+8],esi
                    418:     mov     [edx+12],edi
                    419:     xor     eax,eax
                    420: 
                    421: .5: mov     ebp,[esp+16]
                    422:     mov     ebx,[esp+12]
                    423:     mov     esi,[esp+ 8]
                    424:     mov     edi,[esp+ 4]
                    425:     add     esp,stk_spc
                    426:     do_exit
                    427: 
                    428: %endif
                    429: 
                    430: %ifdef  DECRYPTION
                    431: 
1.1.1.4 ! root      432:     extern  t_in
1.1       root      433: 
1.1.1.4 ! root      434: %define dtab_0(x)   [t_in+4*x]
        !           435: %define dtab_1(x)   [t_in+1024+4*x]
        !           436: %define dtab_2(x)   [t_in+2048+4*x]
        !           437: %define dtab_3(x)   [t_in+3072+4*x]
1.1       root      438: 
                    439: %ifdef LAST_ROUND_TABLES
                    440: 
1.1.1.4 ! root      441:     extern  t_il
1.1       root      442: 
1.1.1.4 ! root      443: %define dltab_0(x)  [t_il+4*x]
        !           444: %define dltab_1(x)  [t_il+1024+4*x]
        !           445: %define dltab_2(x)  [t_il+2048+4*x]
        !           446: %define dltab_3(x)  [t_il+3072+4*x]
1.1       root      447: 
                    448: %else
                    449: 
                    450:     extern  _t_ibox
                    451: 
                    452: %define dtab_x(x)   byte [_t_ibox+x]
                    453: 
                    454: %endif
                    455: 
                    456: %macro irn_fun 2
                    457: 
                    458:     rol eax,16
                    459:     %1      esi, cl, 0, ebp
                    460:     %1      esi, bh, 1, ebp
                    461:     %1      esi, al, 2, ebp
                    462:     %1      edi, dl, 0, ebp
                    463:     %1      edi, ch, 1, ebp
                    464:     %1      edi, ah, 3, ebp
                    465:     %2      ebp, bl, 0, ebp
                    466:     shr     eax,16
                    467:     and     ebx,0xffff0000
                    468:     or      ebx,eax
                    469:     shr     ecx,16
                    470:     %1      ebp, bh, 1, eax
                    471:     %1      ebp, ch, 3, eax
                    472:     %2      eax, cl, 2, ecx
                    473:     %1      eax, bl, 0, ecx
                    474:     %1      eax, dh, 1, ecx
                    475:     shr     ebx,16
                    476:     shr     edx,16
                    477:     %1      esi, dh, 3, ecx
                    478:     %1      ebp, dl, 2, ecx
                    479:     %1      eax, bh, 3, ecx
                    480:     %1      edi, bl, 2, ecx
                    481: 
                    482: %endmacro
                    483: 
                    484: ; Basic MOV and XOR Operations for normal rounds
                    485: 
                    486: %macro  ni_xor  4
                    487:     movzx   %4,%2
                    488:     xor     %1,dtab_%3(%4)
                    489: %endmacro
                    490: 
                    491: %macro  ni_mov  4
                    492:     movzx   %4,%2
                    493:     mov     %1,dtab_%3(%4)
                    494: %endmacro
                    495: 
                    496: ; Basic MOV and XOR Operations for last round
                    497: 
                    498: %ifdef LAST_ROUND_TABLES
                    499: 
                    500: %macro  li_xor  4
                    501:     movzx   %4,%2
                    502:     xor     %1,dltab_%3(%4)
                    503: %endmacro
                    504: 
                    505: %macro  li_mov  4
                    506:     movzx   %4,%2
                    507:     mov     %1,dltab_%3(%4)
                    508: %endmacro
                    509: 
                    510: %else
                    511: 
                    512:     %macro  li_xor  4
                    513:         movzx   %4,%2
                    514:         movzx   %4,dtab_x(%4)
                    515:     %if %3 != 0
                    516:         shl     %4,8*%3
                    517:     %endif
                    518:         xor     %1,%4
                    519:     %endmacro
                    520: 
                    521:     %macro  li_mov  4
                    522:         movzx   %4,%2
                    523:         movzx   %1,dtab_x(%4)
                    524:     %if %3 != 0
                    525:         shl     %1,8*%3
                    526:     %endif
                    527:     %endmacro
                    528: 
                    529: %endif
                    530: 
                    531: %macro dec_round 0
                    532: 
                    533: %ifdef AES_REV_DKS
                    534:     add     ebp,16
                    535: %else
                    536:     sub     ebp,16
                    537: %endif
                    538:     save    0,ebp
                    539:     mov     esi,[ebp+8]
                    540:     mov     edi,[ebp+12]
                    541: 
                    542:     irn_fun ni_xor, ni_mov
                    543: 
                    544:     mov     ebx,ebp
                    545:     mov     ecx,esi
                    546:     mov     edx,edi
                    547:     restore ebp,0
                    548:     xor     eax,[ebp]
                    549:     xor     ebx,[ebp+4]
                    550: 
                    551: %endmacro
                    552: 
                    553: %macro dec_last_round 0
                    554: 
                    555: %ifdef AES_REV_DKS
                    556:     add     ebp,16
                    557: %else
                    558:     sub     ebp,16
                    559: %endif
                    560:     save    0,ebp
                    561:     mov     esi,[ebp+8]
                    562:     mov     edi,[ebp+12]
                    563: 
                    564:     irn_fun li_xor, li_mov
                    565: 
                    566:     mov     ebx,ebp
                    567:     restore ebp,0
                    568:     xor     eax,[ebp]
                    569:     xor     ebx,[ebp+4]
                    570: 
                    571: %endmacro
                    572: 
                    573:     section .text
                    574: 
                    575: ; AES Decryption Subroutine
                    576: 
1.1.1.4 ! root      577:     do_name aes_decrypt
1.1       root      578: 
                    579:     sub     esp,stk_spc
                    580:     mov     [esp+16],ebp
                    581:     mov     [esp+12],ebx
                    582:     mov     [esp+ 8],esi
                    583:     mov     [esp+ 4],edi
                    584: 
                    585: ; input four columns and xor in first round key
                    586: 
                    587:     mov     esi,[esp+in_blk+stk_spc] ; input pointer
                    588:     mov     eax,[esi   ]
                    589:     mov     ebx,[esi+ 4]
                    590:     mov     ecx,[esi+ 8]
                    591:     mov     edx,[esi+12]
                    592:     lea     esi,[esi+16]
                    593: 
                    594:     mov     ebp,[esp+ctx+stk_spc]    ; key pointer
                    595:     movzx   edi,byte[ebp+4*KS_LENGTH]
                    596: %ifndef  AES_REV_DKS        ; if decryption key schedule is not reversed
                    597:     lea     ebp,[ebp+edi]   ; we have to access it from the top down
                    598: %endif
                    599:     xor     eax,[ebp   ]    ; key schedule
                    600:     xor     ebx,[ebp+ 4]
                    601:     xor     ecx,[ebp+ 8]
                    602:     xor     edx,[ebp+12]
                    603: 
                    604: ; determine the number of rounds
                    605: 
                    606:     cmp     edi,10*16
                    607:     je      .3
                    608:     cmp     edi,12*16
                    609:     je      .2
                    610:     cmp     edi,14*16
                    611:     je      .1
                    612:     mov     eax,-1
                    613:     jmp     .5
                    614: 
                    615: .1: dec_round
                    616:     dec_round
                    617: .2: dec_round
                    618:     dec_round
                    619: .3: dec_round
                    620:     dec_round
                    621:     dec_round
                    622:     dec_round
                    623:     dec_round
                    624:     dec_round
                    625:     dec_round
                    626:     dec_round
                    627:     dec_round
                    628:     dec_last_round
                    629: 
                    630: ; move final values to the output array.
                    631: 
                    632:     mov     ebp,[esp+out_blk+stk_spc]
                    633:     mov     [ebp],eax
                    634:     mov     [ebp+4],ebx
                    635:     mov     [ebp+8],esi
                    636:     mov     [ebp+12],edi
                    637:     xor     eax,eax
                    638: 
                    639: .5: mov     ebp,[esp+16]
                    640:     mov     ebx,[esp+12]
                    641:     mov     esi,[esp+ 8]
                    642:     mov     edi,[esp+ 4]
                    643:     add     esp,stk_spc
                    644:     do_exit
                    645: 
                    646: %endif

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.