- RichardRussell
- Posts: 1396
- Joined: Thu Jun 21, 2012 10:48 am
Re: Introduction to BBC BASIC
My attention has been drawn to this blog by Alan Pope, in which he describes his experiments in getting a Raspberry Pi 400 to boot straight into BBC BASIC. Looks like he could do with some help with aspects such as speeding up booting and getting it working full-screen.
- RichardRussell
- Posts: 1396
- Joined: Thu Jun 21, 2012 10:48 am
Re: Introduction to BBC BASIC
This really belongs in the 'Off topic dIscussion' board, where there is already a thread about Conway's Game of Life, but that board has been shut down temporarily because of spam. I'd just like to draw attention to two recent BBC BASIC implementations of the Game of Life which illustrate different aspects of the language. They both operate on a 512x512 grid (262,144 cells).
The first uses shader (GPU) code to do the actual calculations, so BBC BASIC is only being used as a 'wrapper' which compiles and executes the shader code. You may consider that to be cheating, but one of the strengths of BBC BASIC has always been that it comes with an embedded assembler that allows you to incorporate assembly language subroutines when interpreted BASIC isn't fast enough, and you can consider shader code to be a bit like that. Certainly it runs crazily fast, managing to update all 262,144 cells at frame rate on most platforms.
Here is the source code, which will run on a Raspberry Pi (tested on a RPi 4):
The second version is a 'pure BASIC' implementation, although I've made what use I can of BBC BASIC features such as 'array arithmetic' and structures. In updating and displaying each 'generation' there's not a single loop (not even an unrolled loop), just linear code! On my RPi 4 it runs at about one generation per second (for a 512x512 grid), compared with about 18 per second on my fastest PC:
The first uses shader (GPU) code to do the actual calculations, so BBC BASIC is only being used as a 'wrapper' which compiles and executes the shader code. You may consider that to be cheating, but one of the strengths of BBC BASIC has always been that it comes with an embedded assembler that allows you to incorporate assembly language subroutines when interpreted BASIC isn't fast enough, and you can consider shader code to be a bit like that. Certainly it runs crazily fast, managing to update all 262,144 cells at frame rate on most platforms.
Here is the source code, which will run on a Raspberry Pi (tested on a RPi 4):
Code: Select all
REM Conway's 'Game of Life': Shader version for BBC BASIC for SDL 2.0
REM Adapted from various examples at Shadertoy.com by Richard Russell
REM Size of grid (max = window size, must be power-of-two for WebGL):
VDU 23,22,512;512;8,16,16,0
GridW% = 512
GridH% = 512
REM Initial random pattern:
FOR C% = 1 TO 2048 : VDU 32+RND(94) : NEXT
OSCLI "GSAVE """ + @tmp$ + "initial.bmp"" 0,0," + STR$(GridW%*2) + "," + STR$(GridH%*2)
CLS
REM Install libraries:
INSTALL @lib$ + "shaderlib"
REM Define constants:
GL_NEAREST = &2600
GL_FRAMEBUFFER = &8D40
GL_COLOR_ATTACHMENT0 = &8CE0
REM Create arrays and structures:
DIM Vertex$(10), Fragment$(999), Final$(10), Float{0%,1%}
REM Fill vertex and fragment shader arrays from DATA statements:
PROC_readshader(Vertex$())
PROC_readshader(Fragment$())
PROC_readshader(Final$())
REM Initialise window and get its size:
ON MOVE IF @msg% <> 5 RETURN ELSE PROCcleanup
VDU 26
IF POS REM SDL thread sync
ScrW% = @size.x%
ScrH% = @size.y%
REM Ensure cleanup on exit:
ON CLOSE PROCcleanup : QUIT
ON ERROR PROCcleanup : MODE 3 : PRINT REPORT$ : END
REM Initialise shader library:
PROC_shaderinit(oVertex%, oFragment%)
SYS `glCreateShader`, GL_FRAGMENT_SHADER, @memhdc% TO oFinal%
`glGenFramebuffers` = FN_gpa("glGenFramebuffers")
`glBindFramebuffer` = FN_gpa("glBindFramebuffer")
`glFramebufferTexture2D` = FN_gpa("glFramebufferTexture2D")
`glDeleteFramebuffers` = FN_gpa("glDeleteFramebuffers")
REM Compile shaders:
PROC_compileshader(oVertex%, Vertex$(), "Vertex")
PROC_compileshader(oFragment%, Fragment$(), "Fragment")
PROC_compileshader(oFinal%, Final$(), "Final")
REM Link shaders:
oProgram1% = FN_useshaders(oVertex%, oFragment%)
oProgram2% = FN_useshaders(oVertex%, oFinal%)
REM Get pointers to uniforms:
SYS `glGetUniformLocation`, oProgram1%, "iResolution", @memhdc% TO pResolution1%
SYS `glGetUniformLocation`, oProgram1%, "iChannel0", @memhdc% TO pChannel0_1%
SYS `glGetUniformLocation`, oProgram2%, "iResolution", @memhdc% TO pResolution2%
SYS `glGetUniformLocation`, oProgram2%, "iChannel0", @memhdc% TO pChannel0_2%
REM Load and bind initial random texture:
SYS `glActiveTexture`, GL_TEXTURE0, @memhdc%
Texture1% = FN_loadtexture1(@tmp$ + "initial.bmp")
SYS `glTexParameteri`, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST, @memhdc%
Texture2% = FN_loadtexture1(@tmp$ + "initial.bmp")
SYS `glTexParameteri`, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST, @memhdc%
IF Texture1% = 0 OR Texture2% = 0 ERROR 100, "Couldn't load initial.bmp"
SYS `glUniform1i`, pChannel0_1%, 0, @memhdc%
SYS `glUniform1i`, pChannel0_2%, 0, @memhdc%
REM Create Framebuffer object:
DIM align{fbo%}
SYS `glGenFramebuffers`, 1, align{}, @memhdc%
oBuffer% = align.fbo%
IF oBuffer% = 0 ERROR 100, "Couldn't create framebuffer"
REM Render loop:
REPEAT
REM Stage one: render to texture:
Float.0% = FN_f4(GridW%) : Float.1% = FN_f4(GridH%)
SYS `glBindFramebuffer`, GL_FRAMEBUFFER, oBuffer%, @memhdc%
SYS `glFramebufferTexture2D`, GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, \
\ GL_TEXTURE_2D, Texture2%, 0, @memhdc%
SYS `glBindTexture`, GL_TEXTURE_2D, Texture1%, @memhdc%
SYS `glUseProgram`, oProgram1%, @memhdc%
SYS `glUniform2fv`, pResolution1%, 1, Float{}, @memhdc%
SYS `glDrawArrays`, GL_TRIANGLES, 0, 6, @memhdc%
REM Stage two: render to canvas:
Float.0% = FN_f4(ScrW%) : Float.1% = FN_f4(ScrH%)
SYS `glBindFramebuffer`, GL_FRAMEBUFFER, 0, @memhdc%
SYS `glBindTexture`, GL_TEXTURE_2D, Texture2%, @memhdc%
SYS `glUseProgram`, oProgram2%, @memhdc%
SYS `glUniform2fv`, pResolution2%, 1, Float{}, @memhdc%
PROC_render
SWAP Texture1%, Texture2%
UNTIL FALSE
END
DEF PROCcleanup
ON ERROR OFF
oBuffer% += 0 : IF oBuffer% SYS `glDeleteFramebuffers`, 1, ^oBuffer%, @memhdc%
Texture1% += 0 : IF Texture1% SYS `glDeleteTextures`, 1, ^Texture1%, @memhdc%
Texture2% += 0 : IF Texture2% SYS `glDeleteTextures`, 1, ^Texture2%, @memhdc%
oProgram2% += 0 : IF oProgram2% SYS `glDeleteProgram`, oProgram2%, @memhdc%
oFinal% += 0 : IF oFinal% SYS `glDeleteShader`, oFinal%, @memhdc%
oProgram1% += 0 : oVertex% += 0 : oFragment% += 0
PROC_shaderexit(oProgram1%, oVertex%, oFragment%)
*REFRESH ON
ENDPROC
REM Minimal vertex shader:
DATA "attribute vec4 vPosition;"
DATA "void main()"
DATA "{"
DATA "gl_Position = vPosition ;"
DATA "}"
DATA ""
REM Intermediate fragment shader (outputs to texture):
DATA "uniform vec2 iResolution;"
DATA "uniform sampler2D iChannel0;"
DATA "int cell( in vec2 p )"
DATA "{"
DATA "return (texture2D(iChannel0, p/iResolution.xy).g > 0.5 ) ? 1 : 0;"
DATA "}"
DATA "void main()"
DATA "{"
DATA "vec2 px = gl_FragCoord.xy;"
DATA "int k = cell(px+vec2(-1,-1)) + cell(px+vec2(0,-1)) + cell(px+vec2(1,-1))"
DATA " + cell(px+vec2(-1, 0)) + cell(px+vec2(1, 0))"
DATA " + cell(px+vec2(-1, 1)) + cell(px+vec2(0, 1)) + cell(px+vec2(1, 1));"
DATA "int e = cell(px);"
DATA "float f = (((k==2) && (e==1)) || (k==3)) ? 1.0 : 0.0;"
DATA "gl_FragColor = vec4( f, f, f, 1.0 );"
DATA "}"
DATA ""
REM Final fragment shader (outputs to canvas):
DATA "uniform vec2 iResolution;"
DATA "uniform sampler2D iChannel0;"
DATA "void main()"
DATA "{"
DATA "gl_FragColor = texture2D(iChannel0, gl_FragCoord.xy/iResolution.xy);"
DATA "}"
DATA ""
Code: Select all
REM Conway's 'Game of Life' in pure BASIC (with some cheating!)
REM By Richard Russell, http://www.bbcbasic.co.uk/, 13-Jan-2021
HIMEM = PAGE + 8000000
REM Set grid size and initialise screen:
W% = 512 : H% = 512
VDU 23,22,W%;H%;8,8,16,0
OFF
REM Define structures and arrays:
DIM bm{bfType{l&,h&}, bfSize%, bfReserved%, bfOffBits%, \
\ biSize%, biWidth%, biHeight%, biPlanes{l&,h&}, biBitCount{l&,h&}, \
\ biCompression%, biSizeImage%, biXPelsPerMeter%, biYPelsPerMeter%, \
\ biClrUsed%, biClrImportant%, palette%(255)}, g&(H%-1,W%-1), ft%(9)
REM Initialise bitmap structure:
bm.bfType.l& = ASC"B" : bm.bfType.h& = ASC"M"
bm.bfOffBits% = ^g&(0,0) - bm{} : bm.bfSize% = bm.bfOffBits% + W%*H%
bm.biSize% = 40 : bm.biWidth% = W% : bm.biHeight% = -H%
bm.biPlanes.l& = 1 : bm.biBitCount.l& = 8
bm.palette%(0) = &FF000000 : bm.palette%(1) = &FFFFFFFF
REM Initialise the grid to a random state:
FOR Y% = 0 TO H%-1
FOR X% = 0 TO W%-1
g&(Y%,X%) = RND(2) - 1
NEXT
NEXT
REM Create 8 copy arrays with gaps:
G% = W% * 2 + 2 : p%% = 0
DIM k&(H%-1,W%-1), p%% G%, l&(H%-1,W%-1), p%% G%, m&(H%-1,W%-1), p%% G%, n&(H%-1,W%-1), p%% G%
DIM o&(H%-1,W%-1), p%% G%, p&(H%-1,W%-1), p%% G%, q&(H%-1,W%-1), p%% G%, r&(H%-1,W%-1), p%% G%
REM Do some evil descriptor hacking to offset the arrays:
PROCoffset(k&(),W%+1) : PROCoffset(l&(),W%+1) : PROCoffset(m&(),W%+1) : PROCoffset(n&(),W%+1)
PROCoffset(o&(),W%+1) : PROCoffset(p&(),W%+1) : PROCoffset(q&(),W%+1) : PROCoffset(r&(),W%+1)
REM Repeat for each generation:
GCOL 15
R% = TIME
@%=&102010A
ft%() = 30
REPEAT
REM Display grid:
OSCLI "MDISPLAY " + STR$~bm{}
REM Make 8 copies of grid:
k&() = g&() : l&() = g&() : m&() = g&() : n&() = g&()
o&() = g&() : p&() = g&() : q&() = g&() : r&() = g&()
REM Offset arrays:
PROCoffset(k&(),-W%-1) : PROCoffset(l&(),-W%) : PROCoffset(m&(),-W%+1)
PROCoffset(n&(),-1) : PROCoffset(o&(),+1)
PROCoffset(p&(),+W%-1) : PROCoffset(q&(),+W%) : PROCoffset(r&(),+W%+1)
REM Sum the border cells:
k&() += l&() + m&() + n&() + o&() + p&() + q&() + r&()
REM Calculate next state (by Svein Svensson):
k&() OR= g&()
g&() = k&() * 80 DIV 240 : REM Relies on 8-bit wraparound
REM Undo the offsets:
PROCoffset(k&(),+W%+1) : PROCoffset(l&(),+W%) : PROCoffset(m&(),+W%-1)
PROCoffset(n&(),+1) : PROCoffset(o&(),-1)
PROCoffset(p&(),-W%+1) : PROCoffset(q&(),-W%) : PROCoffset(r&(),-W%-1)
REM Report speed in title bar:
T% = (T% + 1) MOD (DIM(ft%(),1) + 1)
N% = TIME
ft%(T%) = N% - R%
R% = N%
title$ = STR$(1000/SUM(ft%())) + " generations per second"
IF INKEY$(-256) = "W" THEN
SYS "SetWindowText", @hwnd%, title$
ELSE
SYS "SDL_SetWindowTitle", @hwnd%, title$, @memhdc%
ENDIF
UNTIL FALSE
END
REM This is non-portable code:
DEF PROCoffset(RETURN a&(), O%)
LOCAL i%%, p%%, W%, H% : p%% = PTR(a&())
W% = DIM(a&(),1) + 1 : H% = DIM(a&(),2) + 1
IF O% < 0 FOR i%% = p%% + O% + 9 TO p%% + 8 : ?i%% = 0 : NEXT
IF O% > 0 FOR i%% = p%% + W%*H% + 9 TO p%% + W%*H% + O% + 8 : ?i%% = 0 : NEXT
p%% += O% : PTR(a&()) = p%%
?p%% = 2 : p%%!1 = W% : p%%!5 = H%
ENDPROC
Re: Introduction to BBC BASIC
The shader code looks like the simplest example of such things I've seen ever and the best example of GPU computing I've seen on the Raspberry Pi! Which version did you write first, the GPU or CPU code?RichardRussell wrote: ↑Tue Feb 09, 2021 4:14 pmThis really belongs in the 'Off topic dIscussion' board, where there is already a thread about Conway's Game of Life, but that board has been shut down temporarily because of spam. I'd just like to draw attention to two recent BBC BASIC implementations of the Game of Life which illustrate different aspects of the language. They both operate on a 512x512 grid (262,144 cells).
The first uses shader (GPU) code to do the actual calculations, so BBC BASIC is only being used as a 'wrapper' which compiles and executes the shader code. You may consider that to be cheating, but one of the strengths of BBC BASIC has always been that it comes with an embedded assembler that allows you to incorporate assembly language subroutines when interpreted BASIC isn't fast enough, and you can consider shader code to be a bit like that. Certainly it runs crazily fast, managing to update all 262,144 cells at frame rate on most platforms.
Here is the source code, which will run on a Raspberry Pi (tested on a RPi 4):
The second version is a 'pure BASIC' implementation, although I've made what use I can of BBC BASIC features such as 'array arithmetic' and structures. In updating and displaying each 'generation' there's not a single loop (not even an unrolled loop), just linear code! On my RPi 4 it runs at about one generation per second (for a 512x512 grid), compared with about 18 per second on my fastest PC:Code: Select all
REM Conway's 'Game of Life': Shader version for BBC BASIC for SDL 2.0 REM Adapted from various examples at Shadertoy.com by Richard Russell REM Size of grid (max = window size, must be power-of-two for WebGL): VDU 23,22,512;512;8,16,16,0 GridW% = 512 GridH% = 512 REM Initial random pattern: FOR C% = 1 TO 2048 : VDU 32+RND(94) : NEXT OSCLI "GSAVE """ + @tmp$ + "initial.bmp"" 0,0," + STR$(GridW%*2) + "," + STR$(GridH%*2) CLS REM Install libraries: INSTALL @lib$ + "shaderlib" REM Define constants: GL_NEAREST = &2600 GL_FRAMEBUFFER = &8D40 GL_COLOR_ATTACHMENT0 = &8CE0 REM Create arrays and structures: DIM Vertex$(10), Fragment$(999), Final$(10), Float{0%,1%} REM Fill vertex and fragment shader arrays from DATA statements: PROC_readshader(Vertex$()) PROC_readshader(Fragment$()) PROC_readshader(Final$()) REM Initialise window and get its size: ON MOVE IF @msg% <> 5 RETURN ELSE PROCcleanup VDU 26 IF POS REM SDL thread sync ScrW% = @size.x% ScrH% = @size.y% REM Ensure cleanup on exit: ON CLOSE PROCcleanup : QUIT ON ERROR PROCcleanup : MODE 3 : PRINT REPORT$ : END REM Initialise shader library: PROC_shaderinit(oVertex%, oFragment%) SYS `glCreateShader`, GL_FRAGMENT_SHADER, @memhdc% TO oFinal% `glGenFramebuffers` = FN_gpa("glGenFramebuffers") `glBindFramebuffer` = FN_gpa("glBindFramebuffer") `glFramebufferTexture2D` = FN_gpa("glFramebufferTexture2D") `glDeleteFramebuffers` = FN_gpa("glDeleteFramebuffers") REM Compile shaders: PROC_compileshader(oVertex%, Vertex$(), "Vertex") PROC_compileshader(oFragment%, Fragment$(), "Fragment") PROC_compileshader(oFinal%, Final$(), "Final") REM Link shaders: oProgram1% = FN_useshaders(oVertex%, oFragment%) oProgram2% = FN_useshaders(oVertex%, oFinal%) REM Get pointers to uniforms: SYS `glGetUniformLocation`, oProgram1%, "iResolution", @memhdc% TO pResolution1% SYS `glGetUniformLocation`, oProgram1%, "iChannel0", @memhdc% TO pChannel0_1% SYS `glGetUniformLocation`, oProgram2%, "iResolution", @memhdc% TO pResolution2% SYS `glGetUniformLocation`, oProgram2%, "iChannel0", @memhdc% TO pChannel0_2% REM Load and bind initial random texture: SYS `glActiveTexture`, GL_TEXTURE0, @memhdc% Texture1% = FN_loadtexture1(@tmp$ + "initial.bmp") SYS `glTexParameteri`, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST, @memhdc% Texture2% = FN_loadtexture1(@tmp$ + "initial.bmp") SYS `glTexParameteri`, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST, @memhdc% IF Texture1% = 0 OR Texture2% = 0 ERROR 100, "Couldn't load initial.bmp" SYS `glUniform1i`, pChannel0_1%, 0, @memhdc% SYS `glUniform1i`, pChannel0_2%, 0, @memhdc% REM Create Framebuffer object: DIM align{fbo%} SYS `glGenFramebuffers`, 1, align{}, @memhdc% oBuffer% = align.fbo% IF oBuffer% = 0 ERROR 100, "Couldn't create framebuffer" REM Render loop: REPEAT REM Stage one: render to texture: Float.0% = FN_f4(GridW%) : Float.1% = FN_f4(GridH%) SYS `glBindFramebuffer`, GL_FRAMEBUFFER, oBuffer%, @memhdc% SYS `glFramebufferTexture2D`, GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, \ \ GL_TEXTURE_2D, Texture2%, 0, @memhdc% SYS `glBindTexture`, GL_TEXTURE_2D, Texture1%, @memhdc% SYS `glUseProgram`, oProgram1%, @memhdc% SYS `glUniform2fv`, pResolution1%, 1, Float{}, @memhdc% SYS `glDrawArrays`, GL_TRIANGLES, 0, 6, @memhdc% REM Stage two: render to canvas: Float.0% = FN_f4(ScrW%) : Float.1% = FN_f4(ScrH%) SYS `glBindFramebuffer`, GL_FRAMEBUFFER, 0, @memhdc% SYS `glBindTexture`, GL_TEXTURE_2D, Texture2%, @memhdc% SYS `glUseProgram`, oProgram2%, @memhdc% SYS `glUniform2fv`, pResolution2%, 1, Float{}, @memhdc% PROC_render SWAP Texture1%, Texture2% UNTIL FALSE END DEF PROCcleanup ON ERROR OFF oBuffer% += 0 : IF oBuffer% SYS `glDeleteFramebuffers`, 1, ^oBuffer%, @memhdc% Texture1% += 0 : IF Texture1% SYS `glDeleteTextures`, 1, ^Texture1%, @memhdc% Texture2% += 0 : IF Texture2% SYS `glDeleteTextures`, 1, ^Texture2%, @memhdc% oProgram2% += 0 : IF oProgram2% SYS `glDeleteProgram`, oProgram2%, @memhdc% oFinal% += 0 : IF oFinal% SYS `glDeleteShader`, oFinal%, @memhdc% oProgram1% += 0 : oVertex% += 0 : oFragment% += 0 PROC_shaderexit(oProgram1%, oVertex%, oFragment%) *REFRESH ON ENDPROC REM Minimal vertex shader: DATA "attribute vec4 vPosition;" DATA "void main()" DATA "{" DATA "gl_Position = vPosition ;" DATA "}" DATA "" REM Intermediate fragment shader (outputs to texture): DATA "uniform vec2 iResolution;" DATA "uniform sampler2D iChannel0;" DATA "int cell( in vec2 p )" DATA "{" DATA "return (texture2D(iChannel0, p/iResolution.xy).g > 0.5 ) ? 1 : 0;" DATA "}" DATA "void main()" DATA "{" DATA "vec2 px = gl_FragCoord.xy;" DATA "int k = cell(px+vec2(-1,-1)) + cell(px+vec2(0,-1)) + cell(px+vec2(1,-1))" DATA " + cell(px+vec2(-1, 0)) + cell(px+vec2(1, 0))" DATA " + cell(px+vec2(-1, 1)) + cell(px+vec2(0, 1)) + cell(px+vec2(1, 1));" DATA "int e = cell(px);" DATA "float f = (((k==2) && (e==1)) || (k==3)) ? 1.0 : 0.0;" DATA "gl_FragColor = vec4( f, f, f, 1.0 );" DATA "}" DATA "" REM Final fragment shader (outputs to canvas): DATA "uniform vec2 iResolution;" DATA "uniform sampler2D iChannel0;" DATA "void main()" DATA "{" DATA "gl_FragColor = texture2D(iChannel0, gl_FragCoord.xy/iResolution.xy);" DATA "}" DATA ""
Code: Select all
REM Conway's 'Game of Life' in pure BASIC (with some cheating!) REM By Richard Russell, http://www.bbcbasic.co.uk/, 13-Jan-2021 HIMEM = PAGE + 8000000 REM Set grid size and initialise screen: W% = 512 : H% = 512 VDU 23,22,W%;H%;8,8,16,0 OFF REM Define structures and arrays: DIM bm{bfType{l&,h&}, bfSize%, bfReserved%, bfOffBits%, \ \ biSize%, biWidth%, biHeight%, biPlanes{l&,h&}, biBitCount{l&,h&}, \ \ biCompression%, biSizeImage%, biXPelsPerMeter%, biYPelsPerMeter%, \ \ biClrUsed%, biClrImportant%, palette%(255)}, g&(H%-1,W%-1), ft%(9) REM Initialise bitmap structure: bm.bfType.l& = ASC"B" : bm.bfType.h& = ASC"M" bm.bfOffBits% = ^g&(0,0) - bm{} : bm.bfSize% = bm.bfOffBits% + W%*H% bm.biSize% = 40 : bm.biWidth% = W% : bm.biHeight% = -H% bm.biPlanes.l& = 1 : bm.biBitCount.l& = 8 bm.palette%(0) = &FF000000 : bm.palette%(1) = &FFFFFFFF REM Initialise the grid to a random state: FOR Y% = 0 TO H%-1 FOR X% = 0 TO W%-1 g&(Y%,X%) = RND(2) - 1 NEXT NEXT REM Create 8 copy arrays with gaps: G% = W% * 2 + 2 : p%% = 0 DIM k&(H%-1,W%-1), p%% G%, l&(H%-1,W%-1), p%% G%, m&(H%-1,W%-1), p%% G%, n&(H%-1,W%-1), p%% G% DIM o&(H%-1,W%-1), p%% G%, p&(H%-1,W%-1), p%% G%, q&(H%-1,W%-1), p%% G%, r&(H%-1,W%-1), p%% G% REM Do some evil descriptor hacking to offset the arrays: PROCoffset(k&(),W%+1) : PROCoffset(l&(),W%+1) : PROCoffset(m&(),W%+1) : PROCoffset(n&(),W%+1) PROCoffset(o&(),W%+1) : PROCoffset(p&(),W%+1) : PROCoffset(q&(),W%+1) : PROCoffset(r&(),W%+1) REM Repeat for each generation: GCOL 15 R% = TIME @%=&102010A ft%() = 30 REPEAT REM Display grid: OSCLI "MDISPLAY " + STR$~bm{} REM Make 8 copies of grid: k&() = g&() : l&() = g&() : m&() = g&() : n&() = g&() o&() = g&() : p&() = g&() : q&() = g&() : r&() = g&() REM Offset arrays: PROCoffset(k&(),-W%-1) : PROCoffset(l&(),-W%) : PROCoffset(m&(),-W%+1) PROCoffset(n&(),-1) : PROCoffset(o&(),+1) PROCoffset(p&(),+W%-1) : PROCoffset(q&(),+W%) : PROCoffset(r&(),+W%+1) REM Sum the border cells: k&() += l&() + m&() + n&() + o&() + p&() + q&() + r&() REM Calculate next state (by Svein Svensson): k&() OR= g&() g&() = k&() * 80 DIV 240 : REM Relies on 8-bit wraparound REM Undo the offsets: PROCoffset(k&(),+W%+1) : PROCoffset(l&(),+W%) : PROCoffset(m&(),+W%-1) PROCoffset(n&(),+1) : PROCoffset(o&(),-1) PROCoffset(p&(),-W%+1) : PROCoffset(q&(),-W%) : PROCoffset(r&(),-W%-1) REM Report speed in title bar: T% = (T% + 1) MOD (DIM(ft%(),1) + 1) N% = TIME ft%(T%) = N% - R% R% = N% title$ = STR$(1000/SUM(ft%())) + " generations per second" IF INKEY$(-256) = "W" THEN SYS "SetWindowText", @hwnd%, title$ ELSE SYS "SDL_SetWindowTitle", @hwnd%, title$, @memhdc% ENDIF UNTIL FALSE END REM This is non-portable code: DEF PROCoffset(RETURN a&(), O%) LOCAL i%%, p%%, W%, H% : p%% = PTR(a&()) W% = DIM(a&(),1) + 1 : H% = DIM(a&(),2) + 1 IF O% < 0 FOR i%% = p%% + O% + 9 TO p%% + 8 : ?i%% = 0 : NEXT IF O% > 0 FOR i%% = p%% + W%*H% + 9 TO p%% + W%*H% + O% + 8 : ?i%% = 0 : NEXT p%% += O% : PTR(a&()) = p%% ?p%% = 2 : p%%!1 = W% : p%%!5 = H% ENDPROC
Last edited by ejolson on Tue Feb 09, 2021 5:06 pm, edited 1 time in total.
- RichardRussell
- Posts: 1396
- Joined: Thu Jun 21, 2012 10:48 am
Re: Introduction to BBC BASIC
I did the GPU version first, not least because it's not entirely original. As noted in the comments, I used a few different implementations at Shadertoy.com as inspiration, borrowing what I liked and throwing away what I didn't!
Re: Introduction to BBC BASIC
You can also install BBC Basic in any PC with GNU/Linux with this script:
https://gitlab.com/cpcbegin/retromultiinstaller
(Debian/Ubuntu family).
https://gitlab.com/cpcbegin/retromultiinstaller
(Debian/Ubuntu family).
Trucos y recetas para raspberry pi:
http://malagaoriginal.blogspot.com.es/search/label/raspberry%20pi
http://malagaoriginal.blogspot.com.es/search/label/raspberry%20pi
- RichardRussell
- Posts: 1396
- Joined: Thu Jun 21, 2012 10:48 am
Re: Introduction to BBC BASIC
What version is that? Presumably, given the 'retro' tag, it's something ancient! Of course I wouldn't want BBC BASIC's heritage to be forgotten, especially in this 40th Anniversary year, but personally I can't see the attraction of 'retro computing'.
Both of the Game of Life implementations above should run in BBC BASIC for SDL 2.0 on any of the supported platforms (Windows, Linux, MacOS, Raspbian, Android, iOS, in-browser). They will also run in BBC BASIC for Windows but the BB4W version of the shader library is required; it can be downloaded from here.
- RichardRussell
- Posts: 1396
- Joined: Thu Jun 21, 2012 10:48 am
Re: Introduction to BBC BASIC
I've released version 1.20a of BBC BASIC for SDL 2.0 - the cross-platform programming language for Windows, MacOS, Linux, Raspbian, Android, iOS and in-browser. The changes in this version are as follows:
- BASIC Interpreter / Run Time Engine
Fixed FOR...NEXT loops misbehaving with negative non-integer STEP on ARM editions (Raspberry Pi, Android, iOS).
Fixed *KEY not correctly handling strings such as |!|H (ARM and 64-bit editions only).
- IDEs and Utilities
Fixed compiler.bbc incorrectly crunching structure members in rare circumstances.
Fixed SDLIDE.bbc crashing in MacOS if the Tab key was pressed.
- Libraries
Modified webgllib.bbc to be 64-bit compatible (it can be used on platforms other than in-browser).
Modified filedlg.bbc so that you can type a full (absolute) path into the File name box.
Modified menulib.bbc so that drop-down menus can be hidden from the menu bar (by setting their title to an empty string).
Fixed editbox.bbc not reliably setting the 'changed' flag, and mispositioning the caret on blank lines.
- Example Programs
Modified Ceefax.bbc to add function key shortcuts (f1-f4) for the coloured buttons; also fixed a crash if a page number with a leading zero was entered.
Modified sudoku.bbc so the timeout only applies to the 'Count' option, not to 'Solve', so it can solve any puzzle even on slow machines.
- RichardRussell
- Posts: 1396
- Joined: Thu Jun 21, 2012 10:48 am
Re: Introduction to BBC BASIC
That is perhaps worth emphasising. webgllib.bbc was written to support 3D graphics in the in-browser edition of BBCSDL, because WebGL doesn't have the Fixed Function Pipeline (in OpenGL and OpenGL ES) that the other editions rely on. To make it work it was necessary to use shaders instead (and I was pleased to be able to achieve that, despite a very limited experience of shader programming).RichardRussell wrote: ↑Thu Mar 04, 2021 9:33 amModified webgllib.bbc to be 64-bit compatible (it can be used on platforms other than in-browser).
It was only after I'd got it going that I realised that it actually worked better than the other libraries (ogllib.bbc and gleslib.bbc) in that lighting calculations are performed per pixel rather than per vertex. This can dramatically improve the realism of the end result, especially in cases when spotlights are used, and/or there are specular reflections.
Fortunately not only does it work better in those respects, it runs on all the other platforms (Windows, MacOS, Linux, Raspbian, Android, iOS) too! So if you want to take advantage of the improved rendering quality simply INSTALL webgllib instead of ogllib. It's not quite 100% compatible but it comes close (you'll notice a particularly dramatic effect if you perform the substitution in skaters.bbc!).
- RichardRussell
- Posts: 1396
- Joined: Thu Jun 21, 2012 10:48 am
Re: Introduction to BBC BASIC
I have updated the Console Mode editions of BBC BASIC to version 0.32. The changes in this version are:
- Fixed the *KEY command not accepting (e.g.) |!|H meaning character code 136.
- Fixed a bug in the Raspbian edition (only) causing FOR...NEXT to misbehave with a negative, non-integer, STEP.
- Added new example programs calendar.bbc and sortdemo.bbc.
- Windows edition (64-bits)
- MacOS edition (64-bits)
- Mac M1 native edition*
- Linux edition (64-bits)
- Raspbian edition (32-bits)
- RichardRussell
- Posts: 1396
- Joined: Thu Jun 21, 2012 10:48 am
Re: Introduction to BBC BASIC
Here's a YouTube video of my BBC BASIC Pinball program running on a Raspberry Pi 4. The program is fewer than 300 lines (about 10 Kbytes) and relies heavily on Box2D for the physics and OpenGL for the 3D graphics. Although Box2D is only 2D, it's appropriate for an inclined plane (which is what Pinball effectively is).
I think this is a good example of what 'modern' BBC BASIC can achieve with relatively little code. I'd be interested to get a feel for how it compares with other more 'popular' languages. Could you do the same thing in as little Python code, for example, and would it run on as many platforms (Windows, MacOS, Linux, Android, iOS and in-browser)?
I think this is a good example of what 'modern' BBC BASIC can achieve with relatively little code. I'd be interested to get a feel for how it compares with other more 'popular' languages. Could you do the same thing in as little Python code, for example, and would it run on as many platforms (Windows, MacOS, Linux, Android, iOS and in-browser)?
Re: Introduction to BBC BASIC
How difficult would it be to make lights blink on the simulated pinball machine?RichardRussell wrote: ↑Fri Mar 26, 2021 6:07 pmHere's a YouTube video of my BBC BASIC Pinball program running on a Raspberry Pi 4. The program is fewer than 300 lines (about 10 Kbytes) and relies heavily on Box2D for the physics and OpenGL for the 3D graphics. Although Box2D is only 2D, it's appropriate for an inclined plane (which is what Pinball effectively is).
I think this is a good example of what 'modern' BBC BASIC can achieve with relatively little code. I'd be interested to get a feel for how it compares with other more 'popular' languages. Could you do the same thing in as little Python code, for example, and would it run on as many platforms (Windows, MacOS, Linux, Android, iOS and in-browser)?
- RichardRussell
- Posts: 1396
- Joined: Thu Jun 21, 2012 10:48 am
Re: Introduction to BBC BASIC
Not trivial, unfortunately. The most obvious way would probably be to separate out, as independent 3D objects, the items that you want to light up. Then you would be able to change either the associated texture or the associated material to achieve the wanted effect. But currently only the ball, flippers and plunger are separate objects (for obvious reasons); everything else is one big 3D model.
An easier method, but one which would be very wasteful of memory, would be to create a set of different textures corresponding to all the different possible patterns of lights. But if there are n independent lights, you'd need to create 2^n different textures, each of which is 2048x2048 pixels (16 Mbytes)! But if there are only a few lights and you've got enough GPU texture memory available it would be straightforward.
Yet another method, which could work for the 'mushrooms' at least, would be to add some narrow-beam spotlights, each of which illuminates just one mushroom. Then you could just wind the associated light up and down. But currently there's a maximum of five lights, which limits the applicability of this technique.
All in all it would be an interesting challenge for somebody to tackle.
- RichardRussell
- Posts: 1396
- Joined: Thu Jun 21, 2012 10:48 am
Re: Introduction to BBC BASIC
I've now experimented with that technique. To be effective it's necessary to turn down the overall lighting level considerably to make the 'lit' object really show up. Whilst that's no doubt quite true to life, it does make the whole thing a bit dim:RichardRussell wrote: ↑Sun Mar 28, 2021 7:19 pmYet another method, which could work for the 'mushrooms' at least, would be to add some narrow-beam spotlights
Re: Introduction to BBC BASIC
That is impressive. Vary impressive, indeed.RichardRussell wrote: ↑Fri Mar 26, 2021 6:07 pmHere's a YouTube video of my BBC BASIC Pinball program running on a Raspberry Pi 4.
- RichardRussell
- Posts: 1396
- Joined: Thu Jun 21, 2012 10:48 am
Re: Introduction to BBC BASIC
I've released version 1.21a of BBC BASIC for SDL 2.0 - the cross-platform programming language for Windows, MacOS, Linux, Raspbian, Android, iOS and in-browser. The changes in this version are as follows:
- BASIC Interpreter / Run Time Engine
Mapped keyboard shortcuts Cmd+A, Cmd+C, Cmd+F, Cmd+V, Cmd+X and Cmd+Z to their Ctrl equivalents for improved compatibility with Apple Macs.
Fixed a couple of minor differences between the 32-bit x86 editions and the ARM/64-bit editions.
- IDEs and Utilities
Added compiler directives to SDLIDE.bbc to simplify building a standalone version in BB4W.
Modified memusage.bbc, profiler.bbc, searchin.bbc and sdldebug.bbc to be compatible with BB4W as well as BBCSDL.
- Libraries
Added box2ddbg.bbc to support Box2D Debug Graphics, largely compatible with the equivalent BB4W library.
Fixed a bug in box2dlib.bbc affecting Gear Joints on 64-bit platforms.
Fixed a bug in dlglib.bbc causing the trackbar control not to scale to the specified range.
Fixed a bug in treeview.bbc causing mouse clicks not to be reliably detected.
- Example Programs
Added pinball.bbc (in examples/physics) which demonstrates combining Box2D and 3D rendering.
Added voronoi.bbc (in examples/graphics/) which is another shader demo.
Modified recorder.bbc to increase the range of level adjustment (100% is now 'mid-range').
Modified the other demos in examples/physics to take advantage of the box2ddbg library (press D to display the Debug Graphics).
Re: Introduction to BBC BASIC
Thanks, Richard! I always appreciate your new releases!
‘Remember the Golden Rule of Selling: “Do not resort to violence.”’ — McGlashan.
Pronouns: he/him
Pronouns: he/him
- RichardRussell
- Posts: 1396
- Joined: Thu Jun 21, 2012 10:48 am
Re: Introduction to BBC BASIC
I have updated the Console Mode editions of BBC BASIC to version 0.33. The changes in this version are:
- QUIT n now sets the exit code (n must be greater than or equal to zero).
- FOR...NEXT loop variables are now promoted to a float if they go outside the 64-bit integer range.
- Fixed a bug which could affect assigning to a sub-string using RIGHT$(s$)=, i.e. with the second parameter omitted.
- Added a new example program sudoku.bbc which is an adaptation of the Sudoku solver supplied with BB4W and BBCSDL.
- Windows edition (64-bits)
- MacOS edition (64-bits)
- Mac M1 native edition*
- Linux edition (64-bits)
- Raspbian edition (32-bits)
Re: Introduction to BBC BASIC
Thank you very much.
Why is the "compiled" version faster?
For example, such a simple test:
1 PRINT "START"
2 T=TIME
3 A=1
4 B=0
5 FOR I=0 TO 999
6 FOR J=0 TO 9999
7 A=A+0.000001
8 B=B+SIN(A)
9 NEXT J
10 NEXT I
11 PRINT A
12 PRINT B
13 PRINT"Time taken=";(TIME-T)/100;" seconds"
7.3 seconds and 4.46 seconds
Michał
Why is the "compiled" version faster?
For example, such a simple test:
1 PRINT "START"
2 T=TIME
3 A=1
4 B=0
5 FOR I=0 TO 999
6 FOR J=0 TO 9999
7 A=A+0.000001
8 B=B+SIN(A)
9 NEXT J
10 NEXT I
11 PRINT A
12 PRINT B
13 PRINT"Time taken=";(TIME-T)/100;" seconds"
7.3 seconds and 4.46 seconds
Michał
- RichardRussell
- Posts: 1396
- Joined: Thu Jun 21, 2012 10:48 am
Re: Introduction to BBC BASIC
It will be because of the actions of the 'cruncher':
- Comments (REM statements) are deleted.
- Unnecessary spaces are removed.
- Multiple short lines are concatenated into longer lines.
- Variables are replaced by embedded pointers.
The resulting program is still interpreted, but the overheads are reduced.
- RichardRussell
- Posts: 1396
- Joined: Thu Jun 21, 2012 10:48 am
Re: Introduction to BBC BASIC
This simulation of Newton's Cradle is another example of combining 3D graphics with calculations performed by the Box2D physics engine, made easy by the libraries supplied with BBC BASIC for SDL 2.0:
https://www.youtube.com/watch?v=T4TYj8Pt1wc
If you want to run it yourself the source code and other required files can be found here. To operate the simulation press and hold the 1, 2, 4 and/or 5 keys to lift the relevant ball(s); try different combinations and see if you can predict what will happen from the laws of conservation of energy and momentum!
You can change the viewpoint using the cursor keys and PgUp/PgDn to zoom.
https://www.youtube.com/watch?v=T4TYj8Pt1wc
If you want to run it yourself the source code and other required files can be found here. To operate the simulation press and hold the 1, 2, 4 and/or 5 keys to lift the relevant ball(s); try different combinations and see if you can predict what will happen from the laws of conservation of energy and momentum!
You can change the viewpoint using the cursor keys and PgUp/PgDn to zoom.
- RichardRussell
- Posts: 1396
- Joined: Thu Jun 21, 2012 10:48 am
Re: Introduction to BBC BASIC
I've released version 1.22a of BBC BASIC for SDL 2.0 - the cross-platform programming language for Windows, MacOS, Linux, Raspberry Pi OS, Android, iOS and in-browser. The changes in this version are as follows:
- BASIC Interpreter / Run Time Engine
Cmd+left, Cmd+right, Cmd+up and Cmd+down are mapped to Home, End, Ctrl+Home and Ctrl+End, respectively, for improved compatibility with Apple Macs.
In the in-browser edition *RUN now runs JavaScript code (previously it did nothing useful) e.g.Code: Select all
OSCLI "window.open(""http://www.rtrussell.co.uk"")"
The QUIT n statement can now accept a parameter of zero (ARM and 64-bit editions only, 32-bit x86 editions always could).
A few performance optimisations have been carried out (e.g. fixing memory alignment issues).
- IDEs and Utilities
In SDLIDE.bbc Alt+left and Alt+right are mapped to Word Left and Word Right, respectively, for improved compatibility with Apple Macs.
Added a -P command-line switch to SDLIDE.bbc to activate the profiler.
The Android and iOS editions report if a later version is available.
- Libraries
Changed gleslib.bbc and ogllib.bbc to call glLightfv by address rather than name.
- Example Programs
Added Newton's Cradle simulation (cradle.bbc in examples/physics).
Bundled the set of instructive Box2D demos in examples/physics/samples (these all use Debug Draw graphics).
Updated sudoku.bbc to add Tidy and New Game options (the difficulty of the generated game is very variable!).
- RichardRussell
- Posts: 1396
- Joined: Thu Jun 21, 2012 10:48 am
Re: Introduction to BBC BASIC
I have updated the Console Mode editions of BBC BASIC to version 0.34. The changes in this version are:
- I've (hopefully) worked around the apparent bug in MinGW's implementation of the _ftelli64() function.
- Execution speed has been improved by about 10% by optimising the expression evaluator.
- RichardRussell
- Posts: 1396
- Joined: Thu Jun 21, 2012 10:48 am
Re: Introduction to BBC BASIC
I've released version 1.23a of BBC BASIC for SDL 2.0 - the cross-platform programming language for Windows, MacOS, Linux, Raspberry Pi OS, Android, iOS and in-browser. The changes in this version are as follows:
- BASIC Interpreter / Run Time Engine
An experimental build for the 64-bit Raspberry Pi OS has been added (with many thanks to Simon Willcocks who wrote the AArch64 assembler which made it possible). This runs more than twice as fast as the 32-bit Pi OS edition!
On a MODE change, the window will be centered only if it would otherwise extend beyond the edge of the desktop.
If SDL's message system is unavailable (which it currently is on 64-bit Pi OS), fatal errors will be reported in a MODE 7 window.
- IDEs and Utilities
Extended SDLIDE.bbc so Ctrl+Backspace is delete word left and Ctrl+Delete is delete word right. Alt (Option) may be used instead of Ctrl, for compatibility with Apple Macs.
Extended the Cross Reference utility to report mismatched quotes, parentheses and braces.
Fixed a couple of minor bugs in the Cruncher, one of which could cause the output file to be larger than it needed to be.
Updated BBCEdit to version 0.38.1, with thanks to Andy Parkes.
- Libraries
Added msgbox.bbc which provides support for Message Boxes, similar to those available in Windows.
Updated filedlg to save and restore the VDU state (e.g. colours, viewports).
Updated the Box2D libraries to be compatible with the 64-bit Raspberry Pi OS.
Extended sdldebug.bbc and timerlib.bbc to include a 64-bit ARM (AArch64) version of the assembler code.
- Example Programs
Extended hello.bbc to include a 64-bit ARM (AArch64) version of the assembler code.
Simplified telstar.bbc by using the built-in MODE 7 font in BBCSDL (and MODE7LIB in BB4W).
Updated several examples to replace SDL's message boxes with BBC BASIC-created ones.
- RichardRussell
- Posts: 1396
- Joined: Thu Jun 21, 2012 10:48 am
Re: Introduction to BBC BASIC
Here's a YouTube video of David Williams' SubZapII running in BBC BASIC on a Raspberry Pi 4 with 64-bit OS. This is 100% BASIC code (no assembler), with direct calls to the SDL 2.0 API to take advantage of the accelerated 2D graphics.
- RichardRussell
- Posts: 1396
- Joined: Thu Jun 21, 2012 10:48 am
Re: Introduction to BBC BASIC
I have updated the Console Mode editions of BBC BASIC to version 0.35. The changes in this version are:
- An edition for the 64-bit Raspberry Pi OS (currently in beta test) has been added. This typically runs more than twice as fast as the 32-bit Raspberry Pi edition.
- The Mac M1 ('Apple Silicon') edition now has an AArch64 assembler. It has limited usefulness because Apple enforce the Hardened Runtime on this platform, but you could still use it to export assembled code to a file.