I am doing some retro 16bit computing using Borland C++ 3.1 (and TASM) in Dosbox.
I am reading a book by Christopher Lampton - Gardens of Imagination (1994) - and I lost the floppy disk that came with the book.
Which means that I have to write code off of pages.
Has been a smooth ride so far.
Now, I am going to add optimization to my little raycaster, and I added fixmul
, fixdiv
and drawwall
and it all worked fine.
All hell broke lose upon adding the drawfloorrow
function.. :(
TASM complains that width
, lightindex
, xincrement
, etc are undefined symbols.
That's odd, because with my extremely limited assembly knowledge: am I not defining them as symbols?
I am confused.
And I know next to nothing about assembly, so if there are a couple of veteran assembly programmers out there that can spot my mistake(s), I would be happy. :)
.MODEL large
.CODE
.386
PUBLIC _fixmul,_fixdiv,_drawwall
PUBLIC _drawfloorrow
_fixmul PROC
ARG arg1:DWORD, arg2:DWORD
push bp ; set up BP register
mov bp, sp
mov eax, arg1 ; get first argument into EAX
imul arg2 ; multiply it by second argument
shrd eax,edx,16 ; shift high and low bytes into DX:AX
pop bp
ret
_fixmul ENDP
_fixdiv PROC
ARG numer:DWORD, denom:DWORD
push bp ; set up BP register
mov bp,sp
mov eax,numer ; put dividend into EAX
mov edx,eax ; copy it into EDX
sar edx,16 ; shift high 16 bits of EDX back into EAX
shl eax,16 ; shift low 16 bits of EAX into high 16 bits
idiv denom ; divide by divisor
shld edx,eax,16 ; get result
pop bp
ret
_fixdiv ENDP
COLUMNLOOP MACRO
shld edi,edx,16 ; move integral portion of bitmap
; pointer into DI
mov al,es:[ebx + eax] ; get lightsourced color
mov gs:[si],al ; copy pixel color to screen column
sub edx,ecx ; add increment to bitmap pointer
sub si,bp ; point to next pixel in wall column
ENDM
_drawwall PROC
ARG screenptr:DWORD, bitmapptr:DWORD, height:WORD, increment:DWORD, litelevel:DWORD
push bp ; save BP
mov bp,sp ; set up stack pointer
mov bx,height ; get height in BX
mov ax,200 ; calculate number of pixels to skip
sub ax,bx ; leave result in AX
mov ecx,increment ; get increment in ECX
lgs si,screenptr ; get screen index in GS:SI
lfs di,bitmapptr ; get pointer to bitmap in FS:DI
mov ebx,0 ; clear out EBX
les bx,litelevel ; get lightsource table addr in BX
mov dx,di ; copy increment in DX
shl edx,16 ; reverse the bytes
imul ax,21 ; calculate jump address
mov di,offset walloop ; add start of loop....
add di,ax ; ...to offset in loop
mov bp,320 ; store constant in BP
xor eax,eax ; clear out EAX
jmp di ; jump unto unrolled loop
walloop:
REPT 200 ; repeat macro 200 times
COLUMNLOOP
ENDM
pop bp ; restore BP
ret
_drawwall ENDP
FLOORLOOP MACRO REP
LOCAL SKIPPIXEL
lgs bx,[botptr] ; get pointer to BOTS array
mov al,gs:[bx] ; get current bottom position
mov bx,[rownum] ; get current row number
cmp al,bl ; compare the two
ja SKIPPIXEL ; jump if floor pixel behind the wall
shld edi,edx,10 ; (int)x / 64
shld ebx,ecx,10 ; (int)y / 64
and ebx,15 ; clear out junk in EBX
shl ebx,4 ; multiply y * 16
and edi,15 ; clear out junk in EDI
add bx,di ; BX = (int)y / 64 * 16 + (int)x / 64
mov al,es:[ebp + ebx] ; get tile number in AL
lgs bx,[texture] ; point GS:BX at texture list
mov edi,gs:[ebx + (eax * 4)] ; get pointer to texture map
mov [textureptr],edi ; save texturemap pointer
shld edi,ecx,16 ; calculate (int)y % 64 * 320 + x % 64
shld ebx,edx,16
and edi,63
and ebx,63
imul di,320
add di,bx
lgs bx,[textureptr] ; get pointer to texture
mov al,gs:[bx + di] ; get pixel color
lgs bx,[lightIndex] ; point to lightsource table
mov al,gs:[ebx + eax] ; get lightsourced color
mov fs:[si + rep],al ; put it on screen
SKIPPIXEL:
add dword ptr [botptr],1 ; advance bottom pointer
add ecx,[yincrement] ; add increments to get
add edx,[xincrement] ; next pixel coordinate
ENDM
_drawfloorrow PROC
ARG row:WORD,screenptr:DWORD,texturelist:DWORD,floormap:DWORD,litelevel:DWORD,bots:DWORD,xinc:DWORD,yinc:DWORD,x:DWORD,y:DWORD,w:WORD
push bp ; save BP
mov bp,sp ; set up stack pointer
mov bx,w ; move parameters into memory variables
mov [width],bx
mov ebx,litelevel
mov [lightindex],ebx
mov bx,row
mov [rownum],bx
mov [colnum],0
mov ecx,y
mov edx,x
lfs si,screenptr
mov ebx,xinc
mov [xincrement],ebx
mov ebx,texturelist
mov [texture],ebx
mov ebx,bots
mov [botptr],ebx
les bp,floormap
xor eax,eax ; clear the EAX register
floor:
FLOORLOOP 0 ; unroll FLOORLOOP 8 times
FLOORLOOP 1
FLOORLOOP 2
FLOORLOOP 3
FLOORLOOP 4
FLOORLOOP 5
FLOORLOOP 6
FLOORLOOP 7
add si,8 ; advance screen pointers
add [colnum],8 ; increase column count
mov bx,[colnum] ; have we covered entire viewport?
cmp bx,[width]
jb floor ; if not, do it again
pop bp ; else return to caller
ret
_drawfloorrow ENDP
END
Errors start to appear with this code:
mov [width],bx
Disclaimer: Someone in the comments said that I do not know what a symbol is. Of course I do. :)
I just don't know how to create them in assembly.
I create symbols all the time in C and C++, and other languages.
EDIT:
Could it be that the ASM source code file is missing a data segment?
Gist here : OPTI.ASM
This seems to be a library which has to be linked together with other libraries and the main program. I guess the missing symbols are defined in another module. EXTRN declares an external symbol.
Add
EXTRN width:word, rownum:word, colnum:word, xincrement:dword, yincrement:dword, texture:dword, textureptr:dword, lightindex:dword, botptr:dword
to the beginning of the source code and you get no more errors. Then you have to find out where the symbols reside.