// // // this is the fully working tristrip.ss from before // trapezoom // // Phil 14 April 1992 // // // // // // void tristrip ( void (*init)(int,int,int,int), // void (*scan)(int*,int*,int,int,int,int,int,int,int,int,int), // int *framebuffer, // int *zbuffer, // triangle *head, // int n_tris, // int processor_shift, // int processor_id, // int stride, // int pxlsize ) //{{{ register allocation #define param1 r16 #define param2 r17 #define param3 r18 #define param4 r19 #define param5 r20 #define param6 r21 #define param7 r22 #define param8 r23 #define param9 r24 #define param10 r25 #define param11 r26 #define param12 r27 #define at r3 #define y r4 #define fbl r5 #define zbl r6 #define x r7 #define xr r8 #define dyx r9 #define dyxr r10 #define dyr r11 #define dyg r12 #define dyb r13 #define dyz r14 #define pmask r15 #define pid r28 // overload the processor id register with the y hop value #define yhop r28 #define r r18 #define g r19 #define b r20 #define z r21 #define dx r22 #define dxr r23 #define dxg r24 #define dxb r25 #define dxz r26 #define rscan r29 #define rstride r30 //}}} //{{{ structure offsets //{{{ vertex offsets // typedef struct s_vert { // struct s_vert *next; // POINT position; // POINT planeEqn; // float snorm [3]; // float gnorm [3]; // float colour [3]; // float texcoords [2]; // } VERTEX; // from start of structure #define position_offs 4 #define vert_x_offs 4 #define vert_y_offs 8 #define vert_z_offs 12 #define plane_offs 20 #define snorm_offs 36 #define gnorm_offs 48 #define colour_offs 60 #define vert_r_offs 60 #define vert_g_offs 64 #define vert_b_offs 68 //}}} //{{{ triangle offsets (plane equation etc.) // typedef struct s_triangle { // struct s_triangle *next; // edge *edgy; // edge *ziggy; // int dr_by_dx; // int dg_by_dx; // int db_by_dx; // int dz_by_dx; // } triangle; #define next_offs 0 #define edgy_offs 4 #define ziggy_offs 8 #define dr_by_dx_offs 12 #define dg_by_dx_offs 16 #define db_by_dx_offs 20 #define dz_by_dx_offs 24 //}}} //{{{ edge offsets // typedef struct struct_edge { // VERTEX *v0, *v1, *vmin, *vmax; // int dx_by_dy; // int dr_by_dy; // int dg_by_dy; // int db_by_dy; // int dz_by_dy; // } edge; #define v0_offs 0 #define v1_offs 4 #define vmin_offs 8 #define vmax_offs 12 #define dx_by_dy_offs 16 #define dr_by_dy_offs 20 #define dg_by_dy_offs 24 #define db_by_dy_offs 28 #define dz_by_dy_offs 32 //}}} //}}} //{{{ stack frame layout #define nlocals -160 #define locals 160 #define fpoffs 156 #define retoffs 152 #define r1offs 148 #define roffs 144 #define goffs 140 #define boffs 136 #define zoffs 132 #define strideoffs 128 #define trioffs 124 #define ledgeoffs 120 #define redgeoffs 116 #define aedgeoffs 112 #define headoffs 108 #define ntrisoffs 104 #define pshiftoffs 100 #define initoffs 96 #define r4offs 92 #define r5offs 88 #define r6offs 84 #define r7offs 80 #define r8offs 76 #define r9offs 72 #define r10offs 68 #define r11offs 64 #define r12offs 60 #define r13offs 56 #define r14offs 52 #define r15offs 48 #define fboffs 44 #define f2offs 40 #define f4offs 32 #define f6offs 24 #define zboffs 16 //}}} //{{{ temporary registers // head aliases b #define head param5 // n aliases z #define n param6 // e_ab aliases dx #define e_ab param7 // e_bc aliases dxr #define e_bc param8 // e_ac aliases dxg #define e_ac param9 // va aliases dxb #define va param10 // vb aliases dxz #define vb param11 // vc safe #define vc param12 // ledge aliases dyx #define ledge r9 // redge aliases dyxr #define redge r10 // aedge aliases dyr #define aedge r11 //}}} //{{{ bacon,lettuce,tomato #define bge(a,b,label)\ subs r0,b,r31;\ adds r31,a,r0;\ bnc label //}}} .align 8 .text //{{{ _reg_dump .globl _reg_dump .align 8 _reg_dump:: //{{{ proc entry - save r1 r2 r3 addu -256, sp, sp st.l r1,0(sp) adds 256,sp,r1 st.l r1,4(sp) st.l fp,8(sp) //}}} //{{{ save r4..r31 st.l r4, 12(sp) st.l r5, 16(sp) st.l r6, 20(sp) st.l r7, 24(sp) st.l r8, 28(sp) st.l r9, 32(sp) st.l r10, 36(sp) st.l r11, 40(sp) st.l r12, 44(sp) st.l r13, 48(sp) st.l r14, 52(sp) st.l r15, 56(sp) st.l r16, 60(sp) st.l r17, 64(sp) st.l r18, 68(sp) st.l r19, 72(sp) st.l r20, 76(sp) st.l r21, 80(sp) st.l r22, 84(sp) st.l r23, 88(sp) st.l r24, 92(sp) st.l r25, 96(sp) st.l r26, 100(sp) st.l r27, 104(sp) st.l r28, 108(sp) st.l r29, 112(sp) st.l r30, 116(sp) st.l r31, 120(sp) //}}} call _trace_regs mov sp, r16 //{{{ save r4..r31 ld.l 12(sp) , r4 ld.l 16(sp) , r5 ld.l 20(sp) , r6 ld.l 24(sp) , r7 ld.l 28(sp) , r8 ld.l 32(sp) , r9 ld.l 36(sp) , r10 ld.l 40(sp) , r11 ld.l 44(sp) , r12 ld.l 48(sp) , r13 ld.l 52(sp) , r14 ld.l 56(sp) , r15 ld.l 60(sp) , r16 ld.l 64(sp) , r17 ld.l 68(sp) , r18 ld.l 72(sp) , r19 ld.l 76(sp) , r20 ld.l 80(sp) , r21 ld.l 84(sp) , r22 ld.l 88(sp) , r23 ld.l 92(sp) , r24 ld.l 96(sp) , r25 ld.l 100(sp) , r26 ld.l 104(sp) , r27 ld.l 108(sp) , r28 ld.l 112(sp) , r29 ld.l 116(sp) , r30 ld.l 120(sp) , r31 //}}} //{{{ proc exit ld.l 0(sp),r1 ld.l 8(sp),fp bri r1 addu 256, sp, sp //}}} //}}} //{{{ call _reg_dump nop //}}} //{{{ _trapezoid that works ish // this coding vaguely works ... // .align 8 // // WORST CASE 28 ticks per line, NO MEMORY HITS! // // subs y,at,r0 <-- now folded into main body code // _trapezoid:: // bnc .do_not_collect_200_pounds // ixfr r1,f4 // NOTE f2..f7 unused in inner loops at present ! // and y,pmask,r31 // xor r31,pid,r0 // r31 == y&pmask - if y&pmask!=pid, DONT call // shra 16,xr,dx // .trap_loop: // bnc .skip_call // normally we DO skip the call // // shra 16,x,r31 // r31 = xl // subs dx,r31,dx // dx = xr - xl // subs 0,r31,r0 // carry clear if dx>0 // shl 2,r31,r31 // doesnt affect carry, avoids freeze condition // bnc .skip_call // // prepare to call fn // adds fbl,r31,param1 // adds zbl,r31,param2 // // save volatile registers // ixfr r,f2 // calli rscan // GO MAN GO // ixfr g,f3 // // fxfr f3,g // fxfr f2,r // // .skip_call: // adds 1, y, y // adds x, dyx, x // adds xr,dyxr,xr // adds g, dyg, g // adds r, dyr, r // adds b, dyb, b // adds z,dyz,z // and y, pmask, r31 // bnc .skip_bmp // skip the bump if y&pmask!=0 // // bump fbl, zbl // adds fbl,rstride,fbl // adds zbl,rstride,zbl // // .skip_bmp: // subs y,at,r0 // now folded into main body code // shra 16,xr,dx // doenst affect carry flag, avoid bc.t freeze // bc.t .trap_loop // this exits on y>at // xor r31,pid,r0 // r31 == y&pmask - if y&pmask!=pid, DONT call // fxfr f4,r1 // .do_not_collect_200_pounds: // bri r1 // nop //}}} //{{{ _trapezoid //{{{ C coding of trapezoid (from tristrip.c) // // while (y>16; // dx =(xr>>16)-r31; // // if (dx > 0) { // /* save rgbz */ // (*rscan)(fbl+r31, zbl+r31, r,g,b,z, dx, dxr,dxg,dxb,dxz); // /* restore rgbz */ // } // } // // x+=dyx; xr+=dyxr; r+=dyr; g+=dyg; b+=dyb; z+=dyz; // // y++; // // if ((y&pmask) == 0) { // fbl+=rstride; // zbl+=rstride; // } // } //}}} .align 8 // WORST CASE 28 ticks per line, NO MEMORY HITS! // subs y,at,r0 <-- now folded into main body code _trapezoid:: bnc .do_not_collect_200_pounds // // OK - we have some crapoid code here - all the processors walk up // all the scanlines, and check to see if this is their scanline // // so we need to modify this to - firstly get the real rgbzx by // walking up until (y&pmask)==pid // now we shift all the slopes up by pshift. // now we walk up n scanlines at a time, doing each line UNTIL (y>=at) // now we shift all the slopes back down, and carry on adding until // y==at. Then we exit. Slopes are as they were before the shift, we // are on the same scanline we would have been on. All should be well. // // ixfr r1,f4 // NOTE f2..f7 unused in inner loops at present ! // // while ((y&pmask) != pid) { // y++; // x+=dx; // xr+=dxr; // r+=dr; // g+=dg; // b+=db; // z+=dz; // if ((y&pmask)==0) { // zbl+=rstride; // fbl+=rstride; // } // } // dx<<=2; // dxr<<=2; // dr<<=2; // dg<<=2; // db<<=2; // dz<<=2; // // while (y>=2; // dxr>>=2; // dr>>=2; // dg>>=2; // db>>=2; // dz>>=2; // // while (y>at) { // x-=dx; // xr-=dxr; // r-=dr; // g-=dg; // b-=db; // z-=dz; // } // // and y,pmask,r31 xor r31,pid,r0 // r31 == y&pmask - if y&pmask!=pid, DONT call shra 16,xr,dx .trap_loop: bnc.t .skip_call1 // normally we DO skip the call adds 1, y, y shra 16,x,r31 // r31 = xl subs dx,r31,dx // dx = xr - xl subs 0,dx,r0 // carry clear if dx>0 shl 2,r31,r31 // doesnt affect carry, avoids freeze condition bnc.t .skip_call1 adds 1, y, y // prepare to call fn adds fbl,r31,param1 adds zbl,r31,param2 // save volatile registers ixfr r,f2 calli rscan // GO MAN GO ixfr g,f3 fxfr f3,g fxfr f2,r .skip_call0: adds 1, y, y .skip_call1: adds xr,dyxr,xr adds g, dyg, g adds r, dyr, r adds b, dyb, b adds z, dyz,z adds x, dyx, x and y, pmask, r31 bnc.t .skip_bmp1 // skip the bump if y&pmask!=0 subs y,at,r0 // now folded into main body code // bump fbl, zbl adds fbl,rstride,fbl adds zbl,rstride,zbl .skip_bmp: subs y,at,r0 // now folded into main body code .skip_bmp1: shra 16,xr,dx // doenst affect carry flag, avoid bc.t freeze bc.t .trap_loop // this exits on y>at xor r31,pid,r0 // r31 == y&pmask - if y&pmask!=pid, DONT call fxfr f4,r1 .do_not_collect_200_pounds: bri r1 nop //}}} //{{{ _trapezoom .align 8 // seems to be 24 ticks per line, no memory hits // subs y,at,r0 <-- now folded into main body code // // // this is the zippywippy trapezoid which understands that it // has been given slopes shifted up by processor_shift // its only difference in behaviour is to add 'hop' to y rather // than 1. When y > at we exit, DOING NOTHING - the calling // code can then do smart things dependent on l/r ness of the // replace edge etc. // // so our trapezoom algorithm is simply // // while (y0 shl 2,r31,r31 // doesnt affect carry, avoids freeze condition bnc.t .skip_zoomcall adds yhop, y, y // prepare to call fn adds fbl,r31,param1 adds zbl,r31,param2 // save volatile registers ixfr r,f2 calli rscan // GO MAN GO ixfr g,f3 fxfr f3,g fxfr f2,r .skip_zoomcall: adds xr,dyxr,xr adds g, dyg, g adds r, dyr, r adds b, dyb, b adds z, dyz,z adds x, dyx, x adds fbl,rstride,fbl adds zbl,rstride,zbl subs y,at,r31 // now folded into main body code bc.t .trapzoom_loop // this exits on y>at shra 16,xr,dx // doesnt affect carry flag, avoid bc.t freeze // 24 ticks per scanline rendered.. fxfr f4,r1 // exit with r31 == y - at .exit_zoom: bri r1 nop //}}} //{{{ _setv // #define setv(yptr, atptr, l,r,act) \ // y =yptr[Y]; \ // ledge=l; redge=r; activate_edge=act; \ // at =atptr[Y] // below are ordered to try and minimize freezes #define _setv(yp,lp,rp,ap)\ ld.l vmin_offs(ap), r31;\ mov lp,ledge;\ ld.l vert_y_offs(yp), y;\ mov rp,redge;\ ld.l vert_y_offs(r31), at;\ br .l_and_r_decision;\ mov ap,aedge //}}} //{{{ _swap(p1,p2) #define _swap(p1,p2)\ mov p1,r31;\ mov p2,p1;\ mov r31,p2 // xor ledge,redge,ledge; // xor ledge,redge,redge; // xor ledge,redge,ledge //}}} //{{{ _astristrip .globl _astristrip .align 8 _astristrip:: //{{{ check for n > 0 subs r0, param6, r0 bc .procentry bri r1 nop //}}} //{{{ proc entry .procentry: addu nlocals, sp, sp st.l fp,fpoffs(sp) st.l r1,retoffs(sp) //}}} //{{{ save r4..r15 st.l r4, r4offs(sp) st.l r5, r5offs(sp) st.l r6, r6offs(sp) st.l r7, r7offs(sp) st.l r8, r8offs(sp) st.l r9, r9offs(sp) st.l r10, r10offs(sp) st.l r11, r11offs(sp) st.l r12, r12offs(sp) st.l r13, r13offs(sp) st.l r14, r14offs(sp) st.l r15, r15offs(sp) //}}} //{{{ save f2..f7 fst.d f2, f2offs(sp) fst.d f4, f4offs(sp) fst.d f6, f6offs(sp) //}}} //{{{ save and reorder params st.l param1, initoffs(sp) mov param2, rscan st.l param3, fboffs(sp) st.l param4, zboffs(sp) st.l param5, headoffs(sp) st.l param6, ntrisoffs(sp) st.l param7, pshiftoffs(sp) mov param8, pid shl 2,param9, rstride // fix stride for byte addressing or 1,r0,r31 shl param7,r31,pmask // 1 << param7 adds -1,pmask,pmask // pmask = (1<next; // edge *ab,*bc,*ac; // // ab=head->ziggy; ac=head->edgy; // bc=n->ziggy; ld.l headoffs(sp), head ld.l next_offs(head), n st.l n, headoffs(sp) // pre-walk triangle pointer ld.l ziggy_offs(head), e_ab ld.l edgy_offs(head), e_ac ld.l ziggy_offs(n), e_bc // /* there are only 3 cases - a lowest, b lowest, c lowest */ // { // int *a, *b, *c, *atp; // // // a=(int *) (&ab->v0->position); // b=(int *) (&ab->v1->position); // c=(int *) (&ac->v1->position); // ld.l v0_offs(e_ab), va ld.l v1_offs(e_ab), vb ld.l v1_offs(e_ac), vc // if (a[Y] < b[Y]) { ld.l vert_y_offs(va), param1 ld.l vert_y_offs(vb), param2 bge(param1,param2,.check_bc) // if (c[Y] < a[Y]) { ld.l vert_y_offs(vc), param2 bge(param2,param1,.a_lowest) // atp=(int *) (&ab->vmin->position); // setv(c,atp,ac,bc,ab); // } .c_lowest: _setv(vc,e_ac,e_bc,e_ab) .a_lowest: // else { // atp=(int *) (&bc->vmin->position); // setv(a,atp,ac,ab,bc); // } // } _setv(va,e_ac,e_ab,e_bc) .check_bc: // else { // if (b[Y] < c[Y]) { ld.l vert_y_offs(vc), param1 // since b still in param2 bge(param2,param1,.c_lowest) // atp=(int *) (&ac->vmin->position); // setv(b,atp,bc,ab,ac); // } _setv(vb,e_bc,e_ab,e_ac) // else { // atp=(int *) (&ab->vmin->position); // setv(c,atp,ac,bc,ab); // } // } // } // _setv(vc,e_ac,e_bc,e_ab) //}}} .l_and_r_decision: //{{{ now determine ABSOLUTELY SECURELY which edge is l and which is r // remember, we have not yet stored ledge/redge/aedge into memory // if (ledge->dx_by_dy == MINT) { ld.l dx_by_dy_offs(ledge),param1 // param1 = ledge->dx_by_dy ld.l dx_by_dy_offs(redge),param2 // param2 = redge->dx_by_dy xorh 0x8000,param1,r0 bnc.t .check_r_horiz xorh 0x8000,param2,r0 // most edges are NOT horizontal, so we TAKE this // if (ledge->vmin == redge->vmin) { ld.l vmin_offs(ledge),param1 ld.l vmin_offs(redge),param2 xor param1,param2,r0 bnc .lmin_not_rmin // branch taken if not the same // swap (ledge,redge, edge* ); _swap(ledge,redge) // activate_edge = (edge *) (1|(int) activate_edge); or 1,aedge,aedge // x = ((int *)(ledge->vmin->position))[X]; // } ld.l vmin_offs(ledge), r31 br .extract_plane_eqn ld.l vert_x_offs(r31),x .lmin_not_rmin: // else { // x = ((int *)(redge->vmin->position))[X]; // } // } ld.l vmin_offs(redge), r31 br .extract_plane_eqn ld.l vert_x_offs(r31),x .check_r_horiz: // else if (redge->dx_by_dy == MINT) { // xorh 0x8000,param2,r0 <--- this is now executed above in a delayed branch bnc.t .none_horiz subs r0,param1,r31 // again, this is most frequent case // first instruction of .none_horiz // if (redge->vmax == ledge->vmin) { ld.l vmax_offs(redge),param1 ld.l vmin_offs(ledge),param2 xor param1,param2,r0 bnc .rmax_not_lmin // swap ( ledge,redge, edge* ); _swap(ledge,redge) // x = ((int *)(redge->vmin->position))[X]; // } ld.l vmin_offs(redge), r31 br .extract_plane_eqn ld.l vert_x_offs(r31),x .rmax_not_lmin: // else { // activate_edge = (edge *) (1|(int) activate_edge); or 1,aedge,aedge // x = ((int *)(ledge->vmin->position))[X]; // } // } // ld.l vmin_offs(ledge), r31 br .extract_plane_eqn ld.l vert_x_offs(r31),x .none_horiz: // else { // if (redge->dx_by_dy < ledge->dx_by_dy) { // this was adds r31,param2,r0 // r31 holds -ledge->dx_by_dy bnc .dont_swap // swap(ledge,redge, edge* ); _swap(ledge,redge) // } // .dont_swap: // x = ((int *)(ledge->vmin->position))[X]; ld.l vmin_offs(ledge), r31 ld.l vert_x_offs(r31),x // // if (((int *)(ledge->vmax->position))[Y] > // ((int *)(redge->vmax->position))[Y]) // { // activate_edge = (edge *) (1|(int) activate_edge); ld.l vmax_offs(ledge), param1 ld.l vmax_offs(redge), param2 ld.l vert_y_offs(param1),param1 ld.l vert_y_offs(param2),param2 subs param2,param1,r0 bc.t .extract_plane_eqn or 1,aedge,aedge //}}} .extract_plane_eqn: // tidy up after previous stuff // st.l ledge, ledgeoffs(sp) // st.l redge, redgeoffs(sp) st.l aedge, aedgeoffs(sp) mov x,xr // since xr==x at start of triangle //{{{ extract plane eqn DONE // remember, head is still in param5 mov head,r31 ld.l dr_by_dx_offs(r31), dxr ld.l dg_by_dx_offs(r31), dxg ld.l db_by_dx_offs(r31), dxb ld.l dz_by_dx_offs(r31), dxz //}}} //{{{ set up initial values for {xyzrgb} DONE // mov ledge,r31 ld.l dx_by_dy_offs(redge), dyxr ld.l vmin_offs(r31), param1 // param1 = ledge->vmin ld.l vert_x_offs(param1), x ld.l vert_y_offs(param1), y ld.l vert_z_offs(param1), z ld.l vert_r_offs(param1), r ld.l vert_g_offs(param1), g ld.l vert_b_offs(param1), b ld.l dx_by_dy_offs(r31), dyx ld.l dr_by_dy_offs(r31), dyr ld.l dg_by_dy_offs(r31), dyg ld.l db_by_dy_offs(r31), dyb ld.l dz_by_dy_offs(r31), dyz //}}} // /* GO ! */ //{{{ compute start values of fbl,zbl DONE ld.l pshiftoffs(sp), r31 shr r31, y, r31 ixfr r31, f2 ixfr rstride, f4 fmlow.dd f2,f4,f2 ld.l fboffs(sp), fbl ld.l zboffs(sp), zbl fxfr f2,r31 adds fbl,r31,fbl adds zbl,r31,zbl //}}} //{{{ set up slopes for subsequent scans DONE // (*init)(dxr,dxg,dxb,dxz); // this can be optimised by passing hods-o-bogus parameters to init ld.l initoffs(sp), r31 calli r31 nop //}}} // ideally we call trapezoom here, passing it // 4x slopes .. we need to do the trap stuff, skipping, // until we are aligned with this processor. then we do the // trapzoom, hopping 2 (4,8) lines at a time. trapzoom exits // with y-at in r31, so IF ITS A LEFT HAND EDGE, de-click all // incrementers until aligned - this costs way less on a right // edge. Now extract stuff out of the new edge, walking up // until at (from the new edge) == y (from the exit of last trap) // then call trapzoom again // //{{{ trapezoid DONE call _trapezoid subs y,at,r0 //}}} //{{{ replace worn-out edge DONE ld.l aedgeoffs(sp), r31 and 1,r31,r0 bnc .activate_right // now activate left edge //{{{ activate left edge - happens every other triangle // ledge=activate_edge; ld.l vmin_offs(r31), param1 // param1 = aedge->vmin ld.l dx_by_dy_offs(r31), dyx ld.l dr_by_dy_offs(r31), dyr ld.l dg_by_dy_offs(r31), dyg ld.l db_by_dy_offs(r31), dyb ld.l dz_by_dy_offs(r31), dyz ld.l vert_x_offs(param1), x ld.l vert_z_offs(param1), z ld.l vert_r_offs(param1), r ld.l vert_g_offs(param1), g br .set_at ld.l vert_b_offs(param1), b //}}} .activate_right: // here activate right edge //{{{ easy case, right edge // redge = (edge *) (1 ^ (int) activate_edge); // xr =((int *)(&redge->vmin->position))[X]; // dyxr =redge->dx_by_dy; xor 1,r31,r31 ld.l vmin_offs(r31), param1 // r16 = redge->vmin ld.l dx_by_dy_offs(r31), dyxr ld.l position_offs(param1), xr //}}} // at=((int *) (&redge->vmax->position))[Y]; .set_at: // ld.l redgeoffs(sp), r31 ld.l vmax_offs(r31), r31 // since r31 holds aedge ! ld.l vert_y_offs(r31), at //}}} //{{{ trapezoid DONE call _trapezoid subs y,at,r0 //}}} //{{{ head=n; ntris--; } DONE /* i.e end loop */ ld.l ntrisoffs(sp), param1 adds -1, param1, param1 or r0,param1,r0 bnc.t .for_i_ntris st.l param1, ntrisoffs(sp) //}}} .restore: //{{{ restore f2..f7 fld.d f2offs(sp), f2 fld.d f4offs(sp), f4 fld.d f6offs(sp), f6 //}}} //{{{ restore r4..r15 ld.l r4offs(sp), r4 ld.l r5offs(sp), r5 ld.l r6offs(sp), r6 ld.l r7offs(sp), r7 ld.l r8offs(sp), r8 ld.l r9offs(sp), r9 ld.l r10offs(sp), r10 ld.l r11offs(sp), r11 ld.l r12offs(sp), r12 ld.l r13offs(sp), r13 ld.l r14offs(sp), r14 ld.l r15offs(sp), r15 //}}} //{{{ proc exit ld.l fpoffs(sp),fp ld.l retoffs(sp),r1 bri r1 addu locals, sp, sp //}}} //{{{ E S C A P E ! ! ! ! ! br .restore nop //}}} //}}}