BBC BASIC for Windows
IDE and Compiler >> Compiler >> Compiling ASM program containing array labels
http://bb4w.conforums.com/index.cgi?board=compiler&action=display&num=1243826780

Compiling ASM program containing array labels
Post by David Williams on Jun 1st, 2009, 03:26am

Should this program be generating this error when compiled: "Crunch failed: assembler syntax error" ?

Code:
      DIM code% 256
      PROCassemble
      PRINT USR( code% )
      END
      
      DEF PROCassemble
      LOCAL pass%
      DIM label%(10)
      FOR pass%=0 TO 2 STEP 2
        P% = code%
        [OPT pass%
        
        .label%(0)
        mov eax, 123456
        ret
        
        ]
      NEXT pass%
      ENDPROC
 


It runs fine from the IDE, but it won't compile unless all the crunch options are unset. I doubt it's a bug,
although in a way I really hope that it is!


David.
Re: Compiling ASM program containing array labels
Post by admin on Jun 1st, 2009, 08:13am

Quote:
Should this program be generating this error when compiled: "Crunch failed: assembler syntax error" ?

The cruncher is more fussy about assembler syntax than the run-time interpreter, and when I suggested that you could use array elements as assembler labels I'd forgotten that it's not strictly allowed. I've checked on the 'original' (BBC Micro) BBC BASIC and it definitely does not allow array elements as labels.

So the cruncher is actually quite correct in rejecting this. The 'bug', if you like, is that the interpreter allows it (and that I recommended it!).

I'll have to give some thought to the best way of resolving this in the long term, because the Wiki also recommends the use of array elements as labels (in the article about simulating assembler macros).

Thanks for pointing this out.

Richard.
Re: Compiling ASM program containing array labels
Post by admin on Jun 1st, 2009, 4:20pm

Having thought about it, I'm sure it wouldn't be right for the compiler to allow array elements as assembler labels. Accordingly, I have modified the Wiki article which used this technique so that it no longer does so. I must try to reprogram my brain so that I don't recommend it again either!

I don't propose to modify the interpreter so that it also disallows array elements (it would be difficult); this feature will join the set of things which you can 'get away with' in the interpreter but not when compiled.

If you're still affected by the limitation of 1200 or so labels before a register name is generated (and I do intend to fix this in a future release) you can use structure members as labels (in both the interpreter and the compiler). In fact that's probably a nicer solution than array elements anyway:

Code:
      DIM code% 256
      PROCassemble
      PRINT USR( code% )
      END
      
      DEF PROCassemble
      LOCAL pass%
      DIM label{a,b,c,d}
      FOR pass%=0 TO 2 STEP 2
        P% = code%
        [OPT pass%
        jmp label.a
        .label.a
        mov eax, 123456
        ret
        ]
      NEXT pass%
      ENDPROC 

Richard.
Re: Compiling ASM program containing array labels
Post by David Williams on Jun 1st, 2009, 6:49pm

on Jun 1st, 2009, 4:20pm, Richard Russell wrote:
If you're still affected by the limitation of 1200 or so labels before a register name is generated (and I do intend to fix this in a future release) you can use structure members as labels (in both the interpreter and the compiler). In fact that's probably a nicer solution than array elements anyway:

Thanks for looking into this, I'm very grateful. I had spent two hours or possibly more replacing well over a hundred assembler labels in GFXLIB with array labels (with many more to do). And it worked beautifully... until I thought I might try compiling a simple test program! Then the blood drained from my face as the realisation dawned on me...

The 'structure members as labels' solution sounds nice for a small set of routines, but GFXLIB has over a hundred routines (I think) and I don't really fancy A) re-modifying the 50-or-so routines I've already modified to use array labels, and B) declaring a structure for each routine.

What with the aforementioned array labels problem and the issues arising from my attempt to fully modulize GFXLIB (namely, embedding of the library's modules and other resource files), I think it'd be prudent to cut my losses and leave GFXLIB as it is in its long, sprawling, linear form. If anyone wants to load it into the IDE, they'll just have to adjust the IDE's 'Initial user memory' setting.

Thanks again.


David.

Re: Compiling ASM program containing array labels
Post by admin on Jun 1st, 2009, 10:07pm

Quote:
The 'structure members as labels' solution sounds nice for a small set of routines, but GFXLIB has over a hundred routines (I think)

I don't know exactly how you tried to use the array-element method but I presume you didn't use one large array for all the labels but instead a separate array for each routine or small group of routines. In that case I would have thought a straightforward search-and-replace of (0) to .a, (1) to .b etc. would have done most of the conversion.

Of course you'd also have to replace each array declaration with an equivalent structure declaration, but assuming the size of each array is quite small (say fewer than 20 elements) that should be pretty easy too. You would necessarily need to use the same member names for each structure (otherwise the method offers no advantage over separate labels) so again a search-and-replace will do most of the work.

So maybe converting to the structure-member method wouldn't be too much effort after all.

Richard.

Re: Compiling ASM program containing array labels
Post by David Williams on Jun 1st, 2009, 10:37pm

on Jun 1st, 2009, 10:07pm, Richard Russell wrote:
I don't know exactly how you tried to use the array-element method but I presume you didn't use one large array for all the labels but instead a separate array for each routine or small group of routines. In that case I would have thought a straightforward search-and-replace of (0) to .a, (1) to .b etc. would have done most of the conversion.


I used a two-dimensional array: label%( numRoutines%-1, 9 ), allowing each of the main routines to have upto 10 'sub-labels'.

Just to illustrate, here's one routine from GFXLIB (routine #24):

Code:
        ;REM.
        ;REM.  ****************|******************************************************************************
        ;REM.  *               |                                                                             *
        ;REM.  *       Routine | (24) Plot                                                                   *
        ;REM.  *               |                                                                             *
        ;REM.  *       Purpose | Plots a raw 32bpp bitmap (or 'sprite')                                      *
        ;REM.  *               |                                                                             *
        ;REM.  *  Entry params | dispVars, bmAddr, bmW, bmH, bmX, bmY                                        *
        ;REM.  *               |                                                                             *
        ;REM.  *       Example | SYS GFXLIB_Plot, dispVars{}, ballspr%, 32, 32, ballx%, bally%               *
        ;REM.  *               |                                                                             *
        ;REM.  *         Notes | Black pixels (i.e. those of value &xx000000) are ignored (i.e. not plotted) *
        ;REM.  *               |                                                                             *
        ;REM.  ****************|******************************************************************************
        ;REM.
        
        .GFXLIB_Plot
        
        ;
        ; SYS GFXLIB_Plot, dispVars, bmAddr, bmW, bmH, bmX, bmY
        ;
        
        pushad
        
        ;REM. ESP+36 = dispVars
        ;REM. ESP+40 = bmAddr
        ;REM. ESP+44 = bmW
        ;REM. ESP+48 = bmH
        ;REM. ESP+52 = bmX
        ;REM. ESP+56 = bmY
        
        ;REM. --------------------------------------------------------------
        ;REM. Setup the entry calling params for the bitmap clipping routine
        ;REM. --------------------------------------------------------------
        
        mov eax, [esp+36]                       ; EAX = ptr to params blk
        
        cmp dword [eax+28], TRUE
        jne near label%(24,0)                   ; .GFXLIB_Plot__exit
        
        ;REM. copy GFXLIB_Plot's entry vars from the stack to varsblk (makes life somewhat easier)
        
        mov ebx,[esp+40] : mov [varsblk+64],ebx ; varsblk+64 = bmAddr
        mov ebx,[esp+44] : mov [varsblk+68],ebx ; varsblk+68 = bmW
        mov ebx,[esp+48] : mov [varsblk+72],ebx ; varsblk+72 = bmH
        mov ebx,[esp+52] : mov [varsblk+76],ebx ; varsblk+76 = bmX
        mov ebx,[esp+56] : mov [varsblk+80],ebx ; varsblk+80 = bmY
        
        ;REM. Call the clipping routine (GFXLIB_clip) after PUSHing the required vars onto the stack
        
        ;REM. SYS GFXLIB_clip, dispVars, bmW, bmH, bmX, bmY, clipValsBlk
        
        push (varsblk+128)                      ; ptr to clipValsBlk
        push dword [varsblk+80]                 ; bmY
        push dword [varsblk+76]                 ; bmX
        push dword [varsblk+72]                 ; bmH
        push dword [varsblk+68]                 ; bmW
        push eax                                ; ptr to params blk
        call GFXLIB_clip
        
        ;REM.
        ;REM. clipValsBlk
        ;REM.
        ;REM.      +128    plotFlag (TRUE or FALSE) - if FALSE, do not attempt to plot/display bitmap
        ;REM.      +132    clipFlag (TRUE or FALSE) - if TRUE then clipping is required
        ;REM.      +136    startX
        ;REM.      +140    startY
        ;REM.      +144    numRows
        ;REM.      +148    rowLen
        ;REM.      +152    skipRows
        ;REM.      +156    skipPxls
        ;REM.
        
        cmp [varsblk+128], dword TRUE           ; check plotFlag
        jne label%(24,0)                        ; GFXLIB_Plot__exit ; exit if plotFlag=FALSE
        
        ;REM.
        ;REM. bmBuffO% = bmBuffAddr% + 4*(params.bmBuffW%*clipvals.startY% + clipvals.startX%)
        ;REM. bmW4% = 4*bmW%
        ;REM. bmBuffW4% = 4*bmBuffW%
        ;REM. bmAddr% += 4*(clipvals.skipRows% + clipvals.skipPxls%)
        ;REM.
        ;REM. FOR Y%=clipvals.numRows%-1 TO 0 STEP -1
        ;REM.   FOR X%=clipvals.rowLen%-1 TO 0 STEP -1
        ;REM.     bmBuffO%!(4*X%) = bmAddr%!(4*X%)
        ;REM.   NEXT X%
        ;REM.   bmAddr% += bmW4%
        ;REM.   bmBuffO% += bmBuffW4%
        ;REM. NEXT Y%
        ;REM.
        
        ;----*----*----*----*----*----*----*----|
        
        ; compute initial/starting offset into the 'bmp buffer' (bmBuffO)
        
        ; EAX = ptr to params blk
        
        
        mov ebx, [eax + 0]                      ; load base addr of 'bmp buffer' (bmpBuffAddr)
        mov ecx, [varsblk + 140]                ; startY
        imul ecx, [eax + 4]                     ; = startY*bmpBuffW
        add ecx, [varsblk + 136]                ; = startY*bmpBuffW + startX
        shl ecx, 2                              ; = 4*(startY*bmpBuffW + startX)
        add ecx, ebx                            ; = bmpBuffAddr + 4*(startY*bmpBuffW + startX)
        
        mov edi, [varsblk + 64]                 ; bmAddr
        
        mov ebx, [varsblk + 152]                ; skipRows
        add ebx, [varsblk + 156]                ; skipRows+skipPxls
        shl ebx, 2                              ; 4*(skipRows + skipPxls)
        add edi, ebx                            ; bmAddr += 4*(skipRows + skipPxls)
        
        mov ebp, [varsblk + 68]                 ; bmW
        shl ebp, 2                              ; = 4*bmW
        
        mov eax, [eax + 4]                      ; bmBuffW
        shl eax, 2                              ; = 4*bmBuffW
        
        mov esi, [varsblk + 144]                ; numRows (Y-loop counter)
        dec esi                                 ; numRows -= 1
        
        .label%(24,1)                           ; .GFXLIB_Plot__yloop        
        push esi                                ; preserve ESI (Y-loop counter)
        
        mov esi, [varsblk + 148]                ; rowLen (X-loop counter)
        dec esi                                 ; rowLen -= 1
        
        .label%(24,2)                           ; .GFXLIB_Plot__xloop
        mov edx, [edi + 4*esi]                  ; load dword from source bitmap
        test edx, &00FFFFFF                     ; is it 0 ?
        jz label%(24,3)                         ; GFXLIB_Plot__next_pxl ; skip (i.e. don't plot) if so
        
        mov [ecx + 4*esi], edx                  ; write dword to destination bitmap buffer
        
        .label%(24,3)                           ; .GFXLIB_Plot__next_pxl
        dec esi                                 ; X -= 1
        jge label%(24,2)                        ; GFXLIB_Plot__xloop ; loop if X >= 0
        
        add edi, ebp                            ; bmAddr += 4*bmW
        add ecx, eax                            ; bmBuffAddr += 4*bmBuffW
        
        pop esi                                 ; recover numRows (Y-loop)
        dec esi                                 ; Y -= 1
        jge label%(24,1)                        ; GFXLIB_Plot__yloop ; loop if Y >= 0
        
        .label%(24,0)                           ; .GFXLIB_Plot__exit
        popad
        ret (6*4) 



I would have been perfectly happy with the 2D-array approach if only the Compiler was happy with it.


David.
Re: Compiling ASM program containing array labels
Post by admin on Jun 2nd, 2009, 08:34am

Quote:
I used a two-dimensional array

OK. Personally I'm not keen on that approach, partly because it runs somewhat contrary to the principle of 'information hiding' (having a data structure shared between more than one routine that needn't be) and partly because it makes the labels so 'anonymous' (all called 'label' something!).

I think my preferred approach (had it worked!) would have been to use a separate array for each routine, perhaps with the same name as the routine itself. So the routine GFXLIB_Plot would have had labels GFXLIB_Plot(0), GFXLIB_Plot(1) etc.

If I was faced with converting what you've got to a structure-member approach (which again I'd want to be self-contained to each routine) I'd write a little utility in BASIC to do it!

Richard.
Re: Compiling ASM program containing array labels
Post by David Williams on Jun 2nd, 2009, 10:04am

on Jun 2nd, 2009, 08:34am, Richard Russell wrote:
OK. Personally I'm not keen on that approach, partly because it runs somewhat contrary to the principle of 'information hiding' (having a data structure shared between more than one routine that needn't be) and partly because it makes the labels so 'anonymous' (all called 'label' something!).

You've probably noticed that I tend to comment my code verbosely! So, for me personally,
the 'anonymous' label names aren't a problem because I always include an informative comment
on the same line:

Code:
.label%(37,3)              ; .GFXLIB_AlphaBlend__skip

jmp label%(37,5)           ; GFXLIB_AlphaBlend__exit

etc. 



on Jun 2nd, 2009, 08:34am, Richard Russell wrote:
If I was faced with converting what you've got to a structure-member approach (which again I'd want to be self-contained to each routine) I'd write a little utility in BASIC to do it!

Well, again, thanks for another excellent suggestion, but I best channel my energy into
improving the efficiency of existing routines, developing new ones, writing more examples,
and finishing the currently very poor documentation.


David.