This page contains some hints, tips and advanced techniques in the use of BBC BASIC for SDL 2.0 which you won't find elsewhere in the manual. Don't worry if some of them seem like gobbledegook: if you can't understand them you don't need to know about them! If you have any suggestions for additions to this section please let us know.
The default output bitmap used by BBC BASIC for SDL 2.0 is normally 2048 x 2048 pixels. Whilst this should be large enough for the majority of display settings and applications you may occasionally want to increase its size. For example you might want to extend the technique used in the example program SCROLL.BBC to scroll over an even larger 'canvas'. The procedure below allows you to do that:
Note that increasing the size of the text viewport beyond 2048 x 2048 pixels (e.g. using VDU 26 or VDU 28) is liable to crash BASIC. The routine above deliberately leaves the text viewport unchanged.DEF PROChugewindow(W%, H%) LOCAL P%, old%%, new%% IF (@platform% AND &F) < 3 P% = &16362004 ELSE P% = &16762004 SYS "SDL_GetRenderTarget", @memhdc% TO old%% SYS "SDL_CreateTexture", @memhdc%, P%, 2, W%, H% TO new%% IF @platform% AND &40 ELSE old%% = !^old%% : new%% = !^new%% IF new%% = 0 ERROR 100, "Couldn't create target texture" SYS "SDL_SetRenderTarget", @memhdc%, new%% SYS "SDL_DestroyTexture", old%%, @memhdc% SYS "SDL_SetRenderDrawColor", @memhdc%, &FF, &FF, &FF, &FF SYS "SDL_RenderClear", @memhdc% SYS "SDL_SetWindowSize", @hwnd%, W%, H%, @memhdc% @size.x% = W% @size.y% = H% VDU 24,0;0;W%*2-2;H%*2-2;16 ENDPROC
If you need to speed up floating point (real number) calculations you may want to do some of the work in assembly language. The BBC BASIC for SDL 2.0 x86 assemblers make this relatively easy by incorporating all the floating point instructions but you have to be careful when passing floating point numbers from BASIC to the assembler code. Specifically you should pass only numeric variables with a # suffix (64-bit doubles):
The same applies if you are passing a floating point value to a function in a DLL. If this requires a pointer to the value pass that as follows:CALL code, fpvalue#
It is rather more complicated if you are passing the value itself, rather than a pointer, because then you need to use a different method depending on whether your program is running on a 32-bit or 64-bit CPU:SYS "function", ^fpvalue#
In the event that you need to pass an array of floating point values to a DLL function the address passed must be that of the first element of the array:IF @platform% AND &40 THEN IF fpvalue#=0 THEN ?(^fpvalue#+7)=&80 SYS "function", fpvalue# TO result% ELSE SYS "function", !^fpvalue#, !(^fpvalue#+4) TO result% ENDIF
Note that the assembler code or DLL must be expecting double precision (64-bit) floating-point numbers. BBC BASIC for SDL 2.0 has no built-in support for ordinary 32-bit floats (but see the FN_f4 library routine for a conversion function).SYS "function", ^fparray#(0)
LOCAL arrays are stored on the stack and special precautions are required as a result. Firstly, avoid swapping a local array with a global array (i.e. one stored on the heap). Because SWAP exchanges the array pointers (rather than the array data) the 'global' array may end up pointing to data on the stack, which will become invalid on exit from the function or procedure in which the array was defined. Any subsequent attempt to access the array data will fail, and may crash BASIC. You can safely copy the array, because the data is copied rather than the pointer:
Secondly, be careful if your program uses LOCAL arrays and ON ERROR. If an error occurs in a procedure or function in which a local array is defined, BASIC will jump immediately to the ON ERROR routine without passing through the ENDPROC or end-of-function statement. The 'local' array will still exist, but will point to an invalid area of memory (errors cause the stack to be discarded), so again any subsequent attempt to access the contents of the array will fail and may cause a crash. To protect against this either ensure errors (even Escape) cannot occur in those functions or procedures, use ON ERROR LOCAL and RESTORE LOCAL to clear the local array(s), or make sure all your local arrays have different names from any global arrays:DEF PROChint LOCAL localarray() DIM localarray(10) SWAP globalarray(),localarray() : REM Don't do this! globalarray() = localarray() : REM This is OK ENDPROC
Simply changing the name of the global array from temp to (for example) Temp would avoid any problems in the event of an error occurring inside the procedure.DIM temp(10) : REM Global array DEF PROChint LOCAL temp() : REM Don't do this if ON ERROR is active ENDPROC
Because CALLed and INSTALLed modules cannot use line numbers, the traditional form of the RESTORE statement cannot be used to move the data pointer into such modules. However the relative form RESTORE +n can. A convenient way of arranging this is to associate a restore procedure with each independent block of data you might want to use:
Then, when you want to READ the data (which can be done from the main program, or another installed module) you just call the appropriate restore procedure first:DEF PROCrestore1 : RESTORE +1 : ENDPROC DATA 1, 2, 3, 4, 5, 6, 7, etc DATA ... DEF PROCrestore2 : RESTORE +1 : ENDPROC DATA 8, 9, 10, 11, 12, 13, etc DATA ...
Using this technique you can hide your data away in a separate module.PROCrestore1 READ A, B, C, D, E, F, G, etc PROCrestore2 READ a, b, c, d, e, f, g, etc
The manual gives the following example of the use of ON MOVE:
This will work, and guarantees that the PROCmove procedure will be called once for each ON MOVE event, but there is a snag: because the procedure can itself be interrupted by a subsequent ON MOVE, the events may not be processed in the order in which they arrive! It may well be more important to ensure that the last ON MOVE is processed last, even if it means that some earlier ones are discarded. You can achieve this behaviour by passing the parameters in a global array. For example, to forward the ON MOVE event to a toolbar and status bar so they resize themselves correctly:ON MOVE PROCmove(@msg%,@lparam%) : RETURN
It is important that the three elements of the Move%() array are used in the same statement, so another interrupt cannot occur between them.DIM Move%(2) ON MOVE Move%()=@msg%,@wparam%,@lparam% : PROCmove : RETURN ...... DEF PROCmove SYS "SendMessage", hToolbar%, Move%(0), Move%(1), Move%(2) SYS "SendMessage", hStatbar%, Move%(0), Move%(1), Move%(2) ENDPROC
Testing for several mutually-exclusive possibilities using nested IF...ENDIF statements can be messy:
The unhelpful indentation and the multiple ENDIFs make it unclear what is happening. The same thing can be achieved more elegantly by using the CASE statement in a cunning way:IF abc%<10 THEN state% = 1 ELSE IF abc%=10 THEN state% = 2 ELSE IF abc%>20 THEN state% = 3 ELSE state% = 99 ENDIF ENDIF ENDIF
Notice the use of CASE TRUE to force the interpreter to test the truth of each of the conditional expressions.CASE TRUE OF WHEN abc%<10: state%=1 WHEN abc%=10: state%=2 WHEN abc%>20: state%=3 OTHERWISE: state%=99 ENDCASE
CONTENTS |
HOME |