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 <stdio.h>
#include <fcntl.h>
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 <stdio.h>
#include <fcntl.h>
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 <stdio.h>
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 <stdio.h>
#include <fcntl.h>
#include <alloc.h> /* 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 <stdio.h>
#include <fcntl.h>
#include <alloc.h> /* 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