/* Copyright (C) 1996-1997 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // // r_edgea.s // x86 assembly-language edge-processing code. // #define SWQUAKE #include "asm_i386.h" #include "quakeasm.h" #include "asm_draw.h" #if id386 .data Ltemp: .long 0 float_1_div_0100000h: .long 0x35800000 // 1.0/(float)0x100000 float_point_999: .single 0.999 float_1_point_001: .single 1.001 .text //-------------------------------------------------------------------- #define edgestoadd 4+8 // note odd stack offsets because of interleaving #define edgelist 8+12 // with pushes .globl C(R_EdgeCodeStart) C(R_EdgeCodeStart): .globl C(R_InsertNewEdges) C(R_InsertNewEdges): pushl %edi pushl %esi // preserve register variables movl edgestoadd(%esp),%edx pushl %ebx movl edgelist(%esp),%ecx LDoNextEdge: movl et_u(%edx),%eax movl %edx,%edi LContinueSearch: movl et_u(%ecx),%ebx movl et_next(%ecx),%esi cmpl %ebx,%eax jle LAddedge movl et_u(%esi),%ebx movl et_next(%esi),%ecx cmpl %ebx,%eax jle LAddedge2 movl et_u(%ecx),%ebx movl et_next(%ecx),%esi cmpl %ebx,%eax jle LAddedge movl et_u(%esi),%ebx movl et_next(%esi),%ecx cmpl %ebx,%eax jg LContinueSearch LAddedge2: movl et_next(%edx),%edx movl et_prev(%esi),%ebx movl %esi,et_next(%edi) movl %ebx,et_prev(%edi) movl %edi,et_next(%ebx) movl %edi,et_prev(%esi) movl %esi,%ecx cmpl $0,%edx jnz LDoNextEdge jmp LDone .align 4 LAddedge: movl et_next(%edx),%edx movl et_prev(%ecx),%ebx movl %ecx,et_next(%edi) movl %ebx,et_prev(%edi) movl %edi,et_next(%ebx) movl %edi,et_prev(%ecx) cmpl $0,%edx jnz LDoNextEdge LDone: popl %ebx // restore register variables popl %esi popl %edi ret //-------------------------------------------------------------------- #define predge 4+4 .globl C(R_RemoveEdges) C(R_RemoveEdges): pushl %ebx movl predge(%esp),%eax Lre_loop: movl et_next(%eax),%ecx movl et_nextremove(%eax),%ebx movl et_prev(%eax),%edx testl %ebx,%ebx movl %edx,et_prev(%ecx) jz Lre_done movl %ecx,et_next(%edx) movl et_next(%ebx),%ecx movl et_prev(%ebx),%edx movl et_nextremove(%ebx),%eax movl %edx,et_prev(%ecx) testl %eax,%eax movl %ecx,et_next(%edx) jnz Lre_loop popl %ebx ret Lre_done: movl %ecx,et_next(%edx) popl %ebx ret //-------------------------------------------------------------------- #define pedgelist 4+4 // note odd stack offset because of interleaving // with pushes .globl C(R_StepActiveU) C(R_StepActiveU): pushl %edi movl pedgelist(%esp),%edx pushl %esi // preserve register variables pushl %ebx movl et_prev(%edx),%esi LNewEdge: movl et_u(%esi),%edi LNextEdge: movl et_u(%edx),%eax movl et_u_step(%edx),%ebx addl %ebx,%eax movl et_next(%edx),%esi movl %eax,et_u(%edx) cmpl %edi,%eax jl LPushBack movl et_u(%esi),%edi movl et_u_step(%esi),%ebx addl %ebx,%edi movl et_next(%esi),%edx movl %edi,et_u(%esi) cmpl %eax,%edi jl LPushBack2 movl et_u(%edx),%eax movl et_u_step(%edx),%ebx addl %ebx,%eax movl et_next(%edx),%esi movl %eax,et_u(%edx) cmpl %edi,%eax jl LPushBack movl et_u(%esi),%edi movl et_u_step(%esi),%ebx addl %ebx,%edi movl et_next(%esi),%edx movl %edi,et_u(%esi) cmpl %eax,%edi jnl LNextEdge LPushBack2: movl %edx,%ebx movl %edi,%eax movl %esi,%edx movl %ebx,%esi LPushBack: // push it back to keep it sorted movl et_prev(%edx),%ecx movl et_next(%edx),%ebx // done if the -1 in edge_aftertail triggered this cmpl $(C(edge_aftertail)),%edx jz LUDone // pull the edge out of the edge list movl et_prev(%ecx),%edi movl %ecx,et_prev(%esi) movl %ebx,et_next(%ecx) // find out where the edge goes in the edge list LPushBackLoop: movl et_prev(%edi),%ecx movl et_u(%edi),%ebx cmpl %ebx,%eax jnl LPushBackFound movl et_prev(%ecx),%edi movl et_u(%ecx),%ebx cmpl %ebx,%eax jl LPushBackLoop movl %ecx,%edi // put the edge back into the edge list LPushBackFound: movl et_next(%edi),%ebx movl %edi,et_prev(%edx) movl %ebx,et_next(%edx) movl %edx,et_next(%edi) movl %edx,et_prev(%ebx) movl %esi,%edx movl et_prev(%esi),%esi cmpl $(C(edge_tail)),%edx jnz LNewEdge LUDone: popl %ebx // restore register variables popl %esi popl %edi ret //-------------------------------------------------------------------- #define surf 4 // note this is loaded before any pushes .align 4 TrailingEdge: movl st_spanstate(%esi),%eax // check for edge inversion decl %eax jnz LInverted movl %eax,st_spanstate(%esi) movl st_insubmodel(%esi),%ecx movl 0x12345678,%edx // surfaces[1].st_next LPatch0: movl C(r_bmodelactive),%eax subl %ecx,%eax cmpl %esi,%edx movl %eax,C(r_bmodelactive) jnz LNoEmit // surface isn't on top, just remove // emit a span (current top going away) movl et_u(%ebx),%eax shrl $20,%eax // iu = integral pixel u movl st_last_u(%esi),%edx movl st_next(%esi),%ecx cmpl %edx,%eax jle LNoEmit2 // iu <= surf->last_u, so nothing to emit movl %eax,st_last_u(%ecx) // surf->next->last_u = iu; subl %edx,%eax movl %edx,espan_t_u(%ebp) // span->u = surf->last_u; movl %eax,espan_t_count(%ebp) // span->count = iu - span->u; movl C(current_iv),%eax movl %eax,espan_t_v(%ebp) // span->v = current_iv; movl st_spans(%esi),%eax movl %eax,espan_t_pnext(%ebp) // span->pnext = surf->spans; movl %ebp,st_spans(%esi) // surf->spans = span; addl $(espan_t_size),%ebp movl st_next(%esi),%edx // remove the surface from the surface movl st_prev(%esi),%esi // stack movl %edx,st_next(%esi) movl %esi,st_prev(%edx) ret LNoEmit2: movl %eax,st_last_u(%ecx) // surf->next->last_u = iu; movl st_next(%esi),%edx // remove the surface from the surface movl st_prev(%esi),%esi // stack movl %edx,st_next(%esi) movl %esi,st_prev(%edx) ret LNoEmit: movl st_next(%esi),%edx // remove the surface from the surface movl st_prev(%esi),%esi // stack movl %edx,st_next(%esi) movl %esi,st_prev(%edx) ret LInverted: movl %eax,st_spanstate(%esi) ret //-------------------------------------------------------------------- // trailing edge only Lgs_trailing: pushl $Lgs_nextedge jmp TrailingEdge .globl C(R_GenerateSpans) C(R_GenerateSpans): pushl %ebp // preserve caller's stack frame pushl %edi pushl %esi // preserve register variables pushl %ebx // clear active surfaces to just the background surface movl C(surfaces),%eax movl C(edge_head_u_shift20),%edx addl $(st_size),%eax // %ebp = span_p throughout movl C(span_p),%ebp movl $0,C(r_bmodelactive) movl %eax,st_next(%eax) movl %eax,st_prev(%eax) movl %edx,st_last_u(%eax) movl C(edge_head)+et_next,%ebx // edge=edge_head.next // generate spans cmpl $(C(edge_tail)),%ebx // done if empty list jz Lgs_lastspan Lgs_edgeloop: movl et_surfs(%ebx),%edi movl C(surfaces),%eax movl %edi,%esi andl $0xFFFF0000,%edi andl $0xFFFF,%esi jz Lgs_leading // not a trailing edge // it has a left surface, so a surface is going away for this span shll $(SURF_T_SHIFT),%esi addl %eax,%esi testl %edi,%edi jz Lgs_trailing // both leading and trailing call TrailingEdge movl C(surfaces),%eax // --------------------------------------------------------------- // handle a leading edge // --------------------------------------------------------------- Lgs_leading: shrl $16-SURF_T_SHIFT,%edi movl C(surfaces),%eax addl %eax,%edi movl 0x12345678,%esi // surf2 = surfaces[1].next; LPatch2: movl st_spanstate(%edi),%edx movl st_insubmodel(%edi),%eax testl %eax,%eax jnz Lbmodel_leading // handle a leading non-bmodel edge // don't start a span if this is an inverted span, with the end edge preceding // the start edge (that is, we've already seen the end edge) testl %edx,%edx jnz Lxl_done // if (surf->key < surf2->key) // goto newtop; incl %edx movl st_key(%edi),%eax movl %edx,st_spanstate(%edi) movl st_key(%esi),%ecx cmpl %ecx,%eax jl Lnewtop // main sorting loop to search through surface stack until insertion point // found. Always terminates because background surface is sentinel // do // { // surf2 = surf2->next; // } while (surf->key >= surf2->key); Lsortloopnb: movl st_next(%esi),%esi movl st_key(%esi),%ecx cmpl %ecx,%eax jge Lsortloopnb jmp LInsertAndExit // handle a leading bmodel edge .align 4 Lbmodel_leading: // don't start a span if this is an inverted span, with the end edge preceding // the start edge (that is, we've already seen the end edge) testl %edx,%edx jnz Lxl_done movl C(r_bmodelactive),%ecx incl %edx incl %ecx movl %edx,st_spanstate(%edi) movl %ecx,C(r_bmodelactive) // if (surf->key < surf2->key) // goto newtop; movl st_key(%edi),%eax movl st_key(%esi),%ecx cmpl %ecx,%eax jl Lnewtop // if ((surf->key == surf2->key) && surf->insubmodel) // { jz Lzcheck_for_newtop // main sorting loop to search through surface stack until insertion point // found. Always terminates because background surface is sentinel // do // { // surf2 = surf2->next; // } while (surf->key > surf2->key); Lsortloop: movl st_next(%esi),%esi movl st_key(%esi),%ecx cmpl %ecx,%eax jg Lsortloop jne LInsertAndExit // Do 1/z sorting to see if we've arrived in the right position movl et_u(%ebx),%eax subl $0xFFFFF,%eax movl %eax,Ltemp fildl Ltemp fmuls float_1_div_0100000h // fu = (float)(edge->u - 0xFFFFF) * // (1.0 / 0x100000); fld %st(0) // fu | fu fmuls st_d_zistepu(%edi) // fu*surf->d_zistepu | fu flds C(fv) // fv | fu*surf->d_zistepu | fu fmuls st_d_zistepv(%edi) // fv*surf->d_zistepv | fu*surf->d_zistepu | fu fxch %st(1) // fu*surf->d_zistepu | fv*surf->d_zistepv | fu fadds st_d_ziorigin(%edi) // fu*surf->d_zistepu + surf->d_ziorigin | // fv*surf->d_zistepv | fu flds st_d_zistepu(%esi) // surf2->d_zistepu | // fu*surf->d_zistepu + surf->d_ziorigin | // fv*surf->d_zistepv | fu fmul %st(3),%st(0) // fu*surf2->d_zistepu | // fu*surf->d_zistepu + surf->d_ziorigin | // fv*surf->d_zistepv | fu fxch %st(1) // fu*surf->d_zistepu + surf->d_ziorigin | // fu*surf2->d_zistepu | // fv*surf->d_zistepv | fu faddp %st(0),%st(2) // fu*surf2->d_zistepu | newzi | fu flds C(fv) // fv | fu*surf2->d_zistepu | newzi | fu fmuls st_d_zistepv(%esi) // fv*surf2->d_zistepv | // fu*surf2->d_zistepu | newzi | fu fld %st(2) // newzi | fv*surf2->d_zistepv | // fu*surf2->d_zistepu | newzi | fu fmuls float_point_999 // newzibottom | fv*surf2->d_zistepv | // fu*surf2->d_zistepu | newzi | fu fxch %st(2) // fu*surf2->d_zistepu | fv*surf2->d_zistepv | // newzibottom | newzi | fu fadds st_d_ziorigin(%esi) // fu*surf2->d_zistepu + surf2->d_ziorigin | // fv*surf2->d_zistepv | newzibottom | newzi | // fu faddp %st(0),%st(1) // testzi | newzibottom | newzi | fu fxch %st(1) // newzibottom | testzi | newzi | fu // if (newzibottom >= testzi) // goto Lgotposition; fcomp %st(1) // testzi | newzi | fu fxch %st(1) // newzi | testzi | fu fmuls float_1_point_001 // newzitop | testzi | fu fxch %st(1) // testzi | newzitop | fu fnstsw %ax testb $0x01,%ah jz Lgotposition_fpop3 // if (newzitop >= testzi) // { fcomp %st(1) // newzitop | fu fnstsw %ax testb $0x45,%ah jz Lsortloop_fpop2 // if (surf->d_zistepu >= surf2->d_zistepu) // goto newtop; flds st_d_zistepu(%edi) // surf->d_zistepu | newzitop| fu fcomps st_d_zistepu(%esi) // newzitop | fu fnstsw %ax testb $0x01,%ah jz Lgotposition_fpop2 fstp %st(0) // clear the FPstack fstp %st(0) movl st_key(%edi),%eax jmp Lsortloop Lgotposition_fpop3: fstp %st(0) Lgotposition_fpop2: fstp %st(0) fstp %st(0) jmp LInsertAndExit // emit a span (obscures current top) Lnewtop_fpop3: fstp %st(0) Lnewtop_fpop2: fstp %st(0) fstp %st(0) movl st_key(%edi),%eax // reload the sorting key Lnewtop: movl et_u(%ebx),%eax movl st_last_u(%esi),%edx shrl $20,%eax // iu = integral pixel u movl %eax,st_last_u(%edi) // surf->last_u = iu; cmpl %edx,%eax jle LInsertAndExit // iu <= surf->last_u, so nothing to emit subl %edx,%eax movl %edx,espan_t_u(%ebp) // span->u = surf->last_u; movl %eax,espan_t_count(%ebp) // span->count = iu - span->u; movl C(current_iv),%eax movl %eax,espan_t_v(%ebp) // span->v = current_iv; movl st_spans(%esi),%eax movl %eax,espan_t_pnext(%ebp) // span->pnext = surf->spans; movl %ebp,st_spans(%esi) // surf->spans = span; addl $(espan_t_size),%ebp LInsertAndExit: // insert before surf2 movl %esi,st_next(%edi) // surf->next = surf2; movl st_prev(%esi),%eax movl %eax,st_prev(%edi) // surf->prev = surf2->prev; movl %edi,st_prev(%esi) // surf2->prev = surf; movl %edi,st_next(%eax) // surf2->prev->next = surf; // --------------------------------------------------------------- // leading edge done // --------------------------------------------------------------- // --------------------------------------------------------------- // see if there are any more edges // --------------------------------------------------------------- Lgs_nextedge: movl et_next(%ebx),%ebx cmpl $(C(edge_tail)),%ebx jnz Lgs_edgeloop // clean up at the right edge Lgs_lastspan: // now that we've reached the right edge of the screen, we're done with any // unfinished surfaces, so emit a span for whatever's on top movl 0x12345678,%esi // surfaces[1].st_next LPatch3: movl C(edge_tail_u_shift20),%eax xorl %ecx,%ecx movl st_last_u(%esi),%edx subl %edx,%eax jle Lgs_resetspanstate movl %edx,espan_t_u(%ebp) movl %eax,espan_t_count(%ebp) movl C(current_iv),%eax movl %eax,espan_t_v(%ebp) movl st_spans(%esi),%eax movl %eax,espan_t_pnext(%ebp) movl %ebp,st_spans(%esi) addl $(espan_t_size),%ebp // reset spanstate for all surfaces in the surface stack Lgs_resetspanstate: movl %ecx,st_spanstate(%esi) movl st_next(%esi),%esi cmpl $0x12345678,%esi // &surfaces[1] LPatch4: jnz Lgs_resetspanstate // store the final span_p movl %ebp,C(span_p) popl %ebx // restore register variables popl %esi popl %edi popl %ebp // restore the caller's stack frame ret // --------------------------------------------------------------- // 1/z sorting for bmodels in the same leaf // --------------------------------------------------------------- .align 4 Lxl_done: incl %edx movl %edx,st_spanstate(%edi) jmp Lgs_nextedge .align 4 Lzcheck_for_newtop: movl et_u(%ebx),%eax subl $0xFFFFF,%eax movl %eax,Ltemp fildl Ltemp fmuls float_1_div_0100000h // fu = (float)(edge->u - 0xFFFFF) * // (1.0 / 0x100000); fld %st(0) // fu | fu fmuls st_d_zistepu(%edi) // fu*surf->d_zistepu | fu flds C(fv) // fv | fu*surf->d_zistepu | fu fmuls st_d_zistepv(%edi) // fv*surf->d_zistepv | fu*surf->d_zistepu | fu fxch %st(1) // fu*surf->d_zistepu | fv*surf->d_zistepv | fu fadds st_d_ziorigin(%edi) // fu*surf->d_zistepu + surf->d_ziorigin | // fv*surf->d_zistepv | fu flds st_d_zistepu(%esi) // surf2->d_zistepu | // fu*surf->d_zistepu + surf->d_ziorigin | // fv*surf->d_zistepv | fu fmul %st(3),%st(0) // fu*surf2->d_zistepu | // fu*surf->d_zistepu + surf->d_ziorigin | // fv*surf->d_zistepv | fu fxch %st(1) // fu*surf->d_zistepu + surf->d_ziorigin | // fu*surf2->d_zistepu | // fv*surf->d_zistepv | fu faddp %st(0),%st(2) // fu*surf2->d_zistepu | newzi | fu flds C(fv) // fv | fu*surf2->d_zistepu | newzi | fu fmuls st_d_zistepv(%esi) // fv*surf2->d_zistepv | // fu*surf2->d_zistepu | newzi | fu fld %st(2) // newzi | fv*surf2->d_zistepv | // fu*surf2->d_zistepu | newzi | fu fmuls float_point_999 // newzibottom | fv*surf2->d_zistepv | // fu*surf2->d_zistepu | newzi | fu fxch %st(2) // fu*surf2->d_zistepu | fv*surf2->d_zistepv | // newzibottom | newzi | fu fadds st_d_ziorigin(%esi) // fu*surf2->d_zistepu + surf2->d_ziorigin | // fv*surf2->d_zistepv | newzibottom | newzi | // fu faddp %st(0),%st(1) // testzi | newzibottom | newzi | fu fxch %st(1) // newzibottom | testzi | newzi | fu // if (newzibottom >= testzi) // goto newtop; fcomp %st(1) // testzi | newzi | fu fxch %st(1) // newzi | testzi | fu fmuls float_1_point_001 // newzitop | testzi | fu fxch %st(1) // testzi | newzitop | fu fnstsw %ax testb $0x01,%ah jz Lnewtop_fpop3 // if (newzitop >= testzi) // { fcomp %st(1) // newzitop | fu fnstsw %ax testb $0x45,%ah jz Lsortloop_fpop2 // if (surf->d_zistepu >= surf2->d_zistepu) // goto newtop; flds st_d_zistepu(%edi) // surf->d_zistepu | newzitop | fu fcomps st_d_zistepu(%esi) // newzitop | fu fnstsw %ax testb $0x01,%ah jz Lnewtop_fpop2 Lsortloop_fpop2: fstp %st(0) // clear the FP stack fstp %st(0) movl st_key(%edi),%eax jmp Lsortloop .globl C(R_EdgeCodeEnd) C(R_EdgeCodeEnd): //---------------------------------------------------------------------- // Surface array address code patching routine //---------------------------------------------------------------------- .align 4 .globl C(R_SurfacePatch) C(R_SurfacePatch): movl C(surfaces),%eax addl $(st_size),%eax movl %eax,LPatch4-4 addl $(st_next),%eax movl %eax,LPatch0-4 movl %eax,LPatch2-4 movl %eax,LPatch3-4 ret #endif // id386