Michael Abrash's Graphics Programming Black Book--chapter1
https://github.com/jagregory/abrash-black-book
https://www.gamedev.net/tutorials/_/technical/graphics-programming-and-theory/graphics-programming-black-book-r1698/
https://github.com/jagregory/abrash-black-book/blob/master/src/chapter-01.md
LISTING 1.1 L1-1.C
/*
* Program to calculate the 16-bit checksum of all bytes in the
* specified file. Obtains the bytes one at a time via read(),
* letting DOS perform all data buffering.
*/
a checksum of the WordPerfect version 4.2 thesaurus file, TH.WP (362,293 bytes in size)
BC3.1的编译命令:bcc L1-1.C
L1-1.exe wb.bmp -- 随便找了一个文件
LISTING 1.2 L1-2.C
/*
* Program to calculate the 16-bit checksum of the stream of bytes
* from the specified file. Obtains the bytes one at a time in
* assembler, via direct calls to DOS.
*/
LISTING 1.3 L1-3.ASM
; Assembler subroutine to perform a 16-bit checksum on the file
; opened on the passed-in handle. Stores the result in the
; passed-in checksum variable. Returns 1 for success, 0 for error.
;
; Call as:
; int ChecksumFile(unsigned int Handle, unsigned int *Checksum);
;
; where:
; Handle = handle # under which file to checksum is open
; Checksum = pointer to unsigned int variable checksum is
; to be stored in
bcc L1-2.C L1-3.ASM
L1-2.exe wb.bmp
The checksum is: 11325 --- 运行明显比L1-1快一些
LISTING 1.4 L1-4.C
采用DOS调用,带缓冲区的,用getc()代替了read()
/*
* Program to calculate the 16-bit checksum of the stream of bytes
* from the specified file. Obtains the bytes one at a time via
* getc(), allowing C to perform data buffering.
*/
bcc L1-4.C
L1-4.exe wb.bmp
明显又快了一些。
LISTING 1.5 L1-5.C
#define BUFFER_SIZE 0x8000 /* 32Kb data buffer */
自己开一个32K缓冲区。换回read()
/*
* Program to calculate the 16-bit checksum of the stream of bytes
* from the specified file. Buffers the bytes internally, rather
* than letting C or DOS do the work.
*/
bcc L1-5.C
L1-5.exe wb.bmp
速度又快了
LISTING 1.6 L1-6.C
/*
* Program to calculate the 16-bit checksum of the stream of bytes
* from the specified file. Buffers the bytes internally, rather
* than letting C or DOS do the work, with the time-critical
* portion of the code written in optimized assembler.
*/
LISTING 1.7 L1-7.ASM
; Assembler subroutine to perform a 16-bit checksum on a block of
; bytes 1 to 64K in size. Adds checksum for block into passed-in
; checksum.
;
; Call as:
; void ChecksumChunk(unsigned char *Buffer,
; unsigned int BufferLength, unsigned int *Checksum);
;
; where:
; Buffer = pointer to start of block of bytes to checksum
; BufferLength = # of bytes to checksum (0 means 64K, not 0)
; Checksum = pointer to unsigned int variable checksum is
;stored in
ChecksumLoop:
lodsb ;get the next byte
add dx,ax ;add it into the checksum total
loop ChecksumLoop ;continue for all bytes in block
mov [bx],dx ;save the new checksum
lodsb速度很快,比c的循环Checksum += (unsigned int) *WorkingPtr++; 快
bcc L1-6.C L1-7.ASM
L1-6.exe wb.bmp
得到最快的版本。
程序是逐步优化的,很经典的例子,虽然DOS比较老了,但是思想不老。
附上各阶段程序
L1-1.C
- /*
- * Program to calculate the 16-bit checksum of all bytes in the
- * specified file. Obtains the bytes one at a time via read(),
- * letting DOS perform all data buffering.
- */
- #include
- #include
-
- main(int argc, char *argv[]) {
- int Handle;
- unsigned char Byte;
- unsigned int Checksum;
- int ReadLength;
-
- if ( argc != 2 ) {
- printf("usage: checksum filename\n");
- exit(1);
- }
- if ( (Handle = open(argv[1], O_RDONLY | O_BINARY)) == -1 ) {
- printf("Can't open file: %s\n", argv[1]);
- exit(1);
- }
-
- /* Initialize the checksum accumulator */
- Checksum = 0;
-
- /* Add each byte in turn into the checksum accumulator */
- while ( (ReadLength = read(Handle, &Byte, sizeof(Byte))) > 0 ) {
- Checksum += (unsigned int) Byte;
- }
- if ( ReadLength == -1 ) {
- printf("Error reading file %s\n", argv[1]);
- exit(1);
- }
-
-
- /* Report the result */
- printf("The checksum is: %u\n", Checksum);
- exit(0);
- }
L1-2.C L1-3.ASM
- /*
- * Program to calculate the 16-bit checksum of the stream of bytes
- * from the specified file. Obtains the bytes one at a time in
- * assembler, via direct calls to DOS.
- */
-
- #include
- #include
-
- main(int argc, char *argv[]) {
- int Handle;
- unsigned char Byte;
- unsigned int Checksum;
- int ReadLength;
-
- if ( argc != 2 ) {
- printf("usage: checksum filename\n");
- exit(1);
- }
- if ( (Handle = open(argv[1], O_RDONLY | O_BINARY)) == -1 ) {
- printf("Can't open file: %s\n", argv[1]);
- exit(1);
- }
- if ( !ChecksumFile(Handle, &Checksum) ) {
- printf("Error reading file %s\n", argv[1]);
- exit(1);
- }
-
- /* Report the result */
- printf("The checksum is: %u\n", Checksum);
- exit(0);
- }
- ; Assembler subroutine to perform a 16-bit checksum on the file
- ; opened on the passed-in handle. Stores the result in the
- ; passed-in checksum variable. Returns 1 for success, 0 for error.
- ;
- ; Call as:
- ; int ChecksumFile(unsigned int Handle, unsigned int *Checksum);
- ;
- ; where:
- ; Handle = handle # under which file to checksum is open
- ; Checksum = pointer to unsigned int variable checksum is
- ; to be stored in
- ;
- ; Parameter structure:
- ;
- Parms struc
- dw ? ;pushed BP
- dw ? ;return address
- Handle dw ?
- Checksum dw ?
- Parms ends
- ;
- .model small
- .data
- TempWord label word
- TempByte db ? ;each byte read by DOS will be stored here
- db 0 ;high byte of TempWord is always 0
- ;for 16-bit adds
- ;
- .code
- public _ChecksumFile
- _ChecksumFile proc near
- push bp
- mov bp,sp
- push si ;save C's register variable
- ;
- mov bx,[bp+Handle] ;get file handle
- sub si,si ;zero the checksum ;accumulator
- mov cx,1 ;request one byte on each ;read
- mov dx,offset TempByte ;point DX to the byte in
- ;which DOS should store
- ;each byte read
- ChecksumLoop:
- mov ah,3fh ;DOS read file function #
- int 21h ;read the byte
- jc ErrorEnd ;an error occurred
- and ax,ax ;any bytes read?
- jz Success ;no-end of file reached-we're done
- add si,[TempWord] ;add the byte into the
- ;checksum total
- jmp ChecksumLoop
- ErrorEnd:
- sub ax,ax ;error
- jmp short Done
- Success:
- mov bx,[bp+Checksum] ;point to the checksum variable
- mov [bx],si ;save the new checksum
- mov ax,1 ;success
- ;
- Done:
- pop si ;restore C's register variable
- pop bp
- ret
- _ChecksumFile endp
- end
L1-4.C
- /*
- * Program to calculate the 16-bit checksum of the stream of bytes
- * from the specified file. Obtains the bytes one at a time via
- * getc(), allowing C to perform data buffering.
- */
- #include
-
- main(int argc, char *argv[]) {
- FILE *CheckFile;
- int Byte;
- unsigned int Checksum;
-
- if ( argc != 2 ) {
- printf("usage: checksum filename\n");
- exit(1);
- }
- if ( (CheckFile = fopen(argv[1], "rb")) == NULL ) {
- printf("Can't open file: %s\n", argv[1]);
- exit(1);
- }
-
- /* Initialize the checksum accumulator */
- Checksum = 0;
-
- /* Add each byte in turn into the checksum accumulator */
- while ( (Byte = getc(CheckFile)) != EOF ) {
- Checksum += (unsigned int) Byte;
- }
-
- /* Report the result */
- printf("The checksum is: %u\n", Checksum);
- exit(0);
- }
L1-5.C
- /*
- * Program to calculate the 16-bit checksum of the stream of bytes
- * from the specified file. Buffers the bytes internally, rather
- * than letting C or DOS do the work.
- */
- #include
- #include
- #include
/* alloc.h for Borland, - malloc.h for Microsoft */
-
- #define BUFFER_SIZE 0x8000 /* 32Kb data buffer */
-
- main(int argc, char *argv[]) {
- int Handle;
- unsigned int Checksum;
- unsigned char *WorkingBuffer, *WorkingPtr;
- int WorkingLength, LengthCount;
-
- if ( argc != 2 ) {
- printf("usage: checksum filename\n");
- exit(1);
- }
- if ( (Handle = open(argv[1], O_RDONLY | O_BINARY)) == -1 ) {
- printf("Can't open file: %s\n", argv[1]);
- exit(1);
- }
-
- /* Get memory in which to buffer the data */
- if ( (WorkingBuffer = malloc(BUFFER_SIZE)) == NULL ) {
- printf("Can't get enough memory\n");
- exit(1);
- }
-
- /* Initialize the checksum accumulator */
- Checksum = 0;
-
- /* Process the file in BUFFER_SIZE chunks */
- do {
- if ( (WorkingLength = read(Handle, WorkingBuffer,
- BUFFER_SIZE)) == -1 ) {
- printf("Error reading file %s\n", argv[1]);
- exit(1);
- }
- /* Checksum this chunk */
- WorkingPtr = WorkingBuffer;
- LengthCount = WorkingLength;
- while ( LengthCount-- ) {
- /* Add each byte in turn into the checksum accumulator */
- Checksum += (unsigned int) *WorkingPtr++;
- }
- } while ( WorkingLength );
-
- /* Report the result */
- printf("The checksum is: %u\n", Checksum);
- exit(0);
- }
L1-6.C L1-7.ASM
- /*
- * Program to calculate the 16-bit checksum of the stream of bytes
- * from the specified file. Buffers the bytes internally, rather
- * than letting C or DOS do the work, with the time-critical
- * portion of the code written in optimized assembler.
- */
- #include
- #include
- #include
/* alloc.h for Borland, - malloc.h for Microsoft */
-
- #define BUFFER_SIZE 0x8000 /* 32K data buffer */
-
- main(int argc, char *argv[]) {
- int Handle;
- unsigned int Checksum;
- unsigned char *WorkingBuffer;
- int WorkingLength;
-
- if ( argc != 2 ) {
- printf("usage: checksum filename\n");
- exit(1);
- }
- if ( (Handle = open(argv[1], O_RDONLY | O_BINARY)) == -1 ) {
- printf("Can't open file: %s\n", argv[1]);
- exit(1);
- }
-
- /* Get memory in which to buffer the data */
- if ( (WorkingBuffer = malloc(BUFFER_SIZE)) == NULL ) {
- printf("Can't get enough memory\n");
- exit(1);
- }
-
- /* Initialize the checksum accumulator */
- Checksum = 0;
-
- /* Process the file in 32K chunks */
- do {
- if ( (WorkingLength = read(Handle, WorkingBuffer,
- BUFFER_SIZE)) == -1 ) {
- printf("Error reading file %s\n", argv[1]);
- exit(1);
- }
- /* Checksum this chunk if there's anything in it */
- if ( WorkingLength )
- ChecksumChunk(WorkingBuffer, WorkingLength, &Checksum);
- } while ( WorkingLength );
-
- /* Report the result */
- printf("The checksum is: %u\n", Checksum);
- exit(0);
- }
- ; Assembler subroutine to perform a 16-bit checksum on a block of
- ; bytes 1 to 64K in size. Adds checksum for block into passed-in
- ; checksum.
- ;
- ; Call as:
- ; void ChecksumChunk(unsigned char *Buffer,
- ; unsigned int BufferLength, unsigned int *Checksum);
- ;
- ; where:
- ; Buffer = pointer to start of block of bytes to checksum
- ; BufferLength = # of bytes to checksum (0 means 64K, not 0)
- ; Checksum = pointer to unsigned int variable checksum is
- ;stored in
- ;
- ; Parameter structure:
- ;
- Parms struc
- dw ? ;pushed BP
- dw ? ;return address
- Buffer dw ?
- BufferLength dw ?
- Checksum dw ?
- Parms ends
- ;
- .model small
- .code
- public _ChecksumChunk
- _ChecksumChunk proc near
- push bp
- mov bp,sp
- push si ;save C's register variable
- ;
- cld ;make LODSB increment SI
- mov si,[bp+Buffer] ;point to buffer
- mov cx,[bp+BufferLength] ;get buffer length
- mov bx,[bp+Checksum] ;point to checksum variable
- mov dx,[bx] ;get the current checksum
- sub ah,ah ;so AX will be a 16-bit value after LODSB
- ChecksumLoop:
- lodsb ;get the next byte
- add dx,ax ;add it into the checksum total
- loop ChecksumLoop ;continue for all bytes in block
- mov [bx],dx ;save the new checksum
- ;
- pop si ;restore C's register variable
- pop bp
- ret
- _ChecksumChunk endp
- end