March 05, 2021, 07:45:37 PM

Author Topic: Exiting Case/select statements  (Read 653 times)

Offline _PJ_

Re: Exiting Case/select statements
« Reply #15 on: January 30, 2021, 07:25:41 PM »
Hi

Admittedly I've not read all the various replies on this topic, but I can tell you, that, as mentioned it is not necessary to break from case statements as only TRUE cases are evaluated.

However, if you for example have some extra specific logic for specific cases that may not warrant their own Case (because they share a lot of other functions otherwise) then you can use Exit as aa 'break' from the Select:


Select (n)
Case 0
 .
Case 1,2,3,4,5
   DoTheThingsForAll5Cases()
  If (n Mod 2) Then Exit
  DoSomethingElseThatOnlyAppliesTo2and4()
Case 6
.
Case 7
.
Default
...
[/code]

I find if you have largenumbers of  Select clause, you can save some process by grouping them such as:

Code: [Select]
if (n < 33)
 Select (n)
   Case 0
   ...
  Case 32
 End Select
Else if (n>66)
 Select (n)
  Case 67
  ...
  Case 69

 End Select
Else
 Case 33
 ...
 Case66
End if


By expanding on this principle, you can make an effective binary tree to drastically reduce the number of "Checks" made to identify the Selected value and speed up the processing.



Offline Baggey

Re: Exiting Case/select statements
« Reply #16 on: January 31, 2021, 01:53:00 AM »
After a bit of experimenting, I've discovered that reflection is slower than I originally thought.  About 3.5x slower than using Select/Case.  But using function pointers in an array was fastest taking 1/3 the time of Select/Case.

Reflection and Function Pointers dosent really mean anything to me  :o

So is this something like a function pointer?

Yes it's a different dialect of Basic and the dreaded GOTO/GOSUB  >:D is in there  :-X

Code: [Select]
psub oo_opcode(op_byte)

on op_byte gosub op_0,op_1,op_2,op_3,op_4,op_5,op_6,op_7,op_8,op_9,op_10,op_11,op_12,op_13,op_14,op_15,op_16,op_17,op_18,op_19,op_20,op_21,op_22,op_23,op_24,op_25,op_26,op_27,op_28,op_29,op_30,op_31,op_32,op_33,op_34,op_35,op_36,op_37,op_38,op_39,op_40,op_41,op_42,op_43,op_44,op_45,op_46,op_47,op_48,op_49,op_50,op_51,op_52,op_53,op_54,op_55,op_56,op_57,op_58,op_59,op_60,op_61,op_62,op_63,op_64,op_65,op_66,op_67,op_68,op_69,op_70,op_71,op_72,op_73,op_74,op_75,op_76,op_77,op_78,op_79,op_80,op_81,op_82,op_83,op_84,op_85,op_86,op_87,op_88,op_89,op_90,op_91,op_92,op_93,op_94,op_95,op_96,op_97,op_98,op_99,op_100,op_101,op_102,op_103,op_104,op_105,op_106,op_107,op_108,op_109,op_110,op_111,op_112,op_113,op_114,op_115,op_116,op_117,op_118,op_119,op_120,op_121,op_122,op_123,op_124,op_125,op_126,op_127,op_128,op_129,op_130,op_131,op_132,op_133,op_134,op_135,op_136,op_137,op_138,op_139,op_140,op_141,op_142,op_143,op_144,op_145,op_146,op_147,op_148,op_149,op_150,op_151,op_152,op_153,op_154,op_155,op_156,op_157,op_158,op_159,op_160,op_161,op_162,op_163,op_164,op_165,op_166,op_167,op_168,op_169,op_170,op_171,op_172,op_173,op_174,op_175,op_176,op_177,op_178,op_179,op_180,op_181,op_182,op_183,op_184,op_185,op_186,op_187,op_188,op_189,op_190,op_191,op_192,op_193,op_194,op_195,op_196,op_197,op_198,op_199,op_200,op_201,op_202,op_203,op_204,op_205,op_206,op_207,op_208,op_209,op_210,op_211,op_212,op_213,op_214,op_215,op_216,op_217,op_218,op_219,op_220,op_221,op_222,op_223,op_224,op_225,op_226,op_227,op_228,op_229,op_230,op_231,op_232,op_233,op_234,op_235,op_236,op_237,op_238,op_239,op_240,op_241,op_242,op_243,op_244,op_245,op_246,op_247,op_248,op_249,op_250,op_251,op_252,op_253,op_254,op_255

return

op_0:  // NOP

nxtpc=1

if monitormode=true

if hex_or_dec = false
// Decimal representation
text 1200,placeopcodeslne,digits$(pc,5)
text 1270,placeopcodeslne,digits$(op_byte,3)
text 1460,placeopcodeslne,"NOP"
else
// Hex representation
text 1200,placeopcodeslne,right$(hex$(pc),4)
text 1270,placeopcodeslne,right$(hex$(op_byte),2)
text 1460,placeopcodeslne,"NOP"
endif
endif

// Do nothing!

if execute=true

; no flags affected
fs = fs
fz = fz
f5 = f5
fh = fh
f3 = f3
fpv = fpv
fn = fn
fc = fc
exeTstates = 4

endif

return ;done


op_1:  // LD BC,nn

op$="LD BC,"
nxtpc=3

if monitormode=true
address=pcptr+pc
lsb=peekbyte(pcptr+pc+1)
msb=peekbyte(pcptr+pc+2)
num=msb*256+lsb

if hex_or_dec = false
// Decimal representation
text 1200,placeopcodeslne,digits$(pc,5)
text 1270,placeopcodeslne,digits$(op_byte,3)
text 1320,placeopcodeslne,digits$(lsb,3)
text 1370,placeopcodeslne,digits$(msb,3)
text 1460,placeopcodeslne,"LD BC , "+digits$(num,5)
else
// Hex representation
text 1200,placeopcodeslne,right$(hex$(pc),4)
text 1270,placeopcodeslne,right$(hex$(op_byte),2)
text 1320,placeopcodeslne,right$(hex$(lsb),2)
text 1370,placeopcodeslne,right$(hex$(msb),2)
text 1460,placeopcodeslne,"LD BC , "+right$(hex$(num),4)
endif
endif

if execute=true

lsb=peekbyte(savepc+1)
msb=peekbyte(savepc+2)
regc=lsb
regb=msb

; no flags affected
fs = fs
fz = fz
f5 = f5 ; Not used
fh = fh
f3 = f3 ; Not used
fpv = fpv
fn = fn
fc = fc

exeTstates = 10
endif
return ; DONE


op_2:  // LD (BC),A

nxt_pc=1

if monitormode=true

if hex_or_dec = false
// Decimal representation
text 1200,placeopcodeslne,digits$(pc,5)
text 1270,placeopcodeslne,digits$(op_byte,3)
text 1460,placeopcodeslne,"LD ( BC ) , A"+digits$(num,5)
else
// Hex representation
text 1200,placeopcodeslne,right$(hex$(pc),4)
text 1270,placeopcodeslne,right$(hex$(op_byte),2)
text 1460,placeopcodeslne,"LD ( BC ) , A"+right$(hex$(num),4)
endif
endif

if execute=true
; memorybank positon bc will be loaded with the value of a
; we could have array postion (b*256)+c = a but i think banks are needed for speed
rega=(regb*256)+regc  ; a will loaded in to memory location poke bc,a

; no flags affected
fs = fs
fz = fz
f5 = f5
fh = fh
f3 = f3
fpv = fpv
fn = fn
fc = fc

endif
exeTstates = 7
return ;done

This is very fast compared to the Select/Case Block! It's not really a decsion but a jump on which Byte/Opcode i have. But i don't have a diddly squat how to program/Code this in BlitZMax  :'( Just the use of Select/Case.

Kind Regards Baggey
Currently Running a PC that just Aint fast enough!?
ZX Spectrum 48k, NEXT, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip.

Jesus was only famous because of his DAD.

Offline TomToad

Re: Exiting Case/select statements
« Reply #17 on: January 31, 2021, 02:47:04 AM »
Functions in BlitzMax are first-class functions.  That means a function can be treated the same as an object.  Stored in arrays and lists, and passed into and from other functions.

So you define a function in the normal way
Code: BlitzMax
  1. Function MyFunc:int(a:int, b:int)
  2.     DoStuff()
  3. End Function

Then you can store a pointer to the function in another variable
Code: BlitzMax
  1. Local AltFunc:Int(a:int, b:int) = MyFunc

Now you can call either result = MyFunc(10,20) or result = AltFunc(10,20), they both call the same function

So now you can write a function for each of your opcodes and place each function into an array
Code: BlitzMax
  1. 'define the functions
  2. Function Op0:Int()
  3.     'put code here
  4. End Function
  5.  
  6. Function Op1:Int()
  7.     'put code here
  8. End function
  9.  
  10. 'define more functions
  11.  
  12. Local FuncArray:Int()[256] 'create the array
  13. 'assign the functions to the array elements
  14. FuncArray[0] = Op0
  15. FuncArray[1] = Op1
  16. 'fill the rest of the array
  17.  
  18. 'get next opcode
  19. opcode = GetNextOpcode()
  20. 'call the proper function
  21. FuncArray[opcode]()
  22.  

Takes a bit more typing than with select/case, but works faster.
------------------------------------------------
8 rabbits equals 1 rabbyte.

Offline Baggey

Re: Exiting Case/select statements
« Reply #18 on: February 01, 2021, 07:57:41 AM »
Thanks guy's, Something to try speeding up my emulation  ;D

Kind Regards Baggey
Currently Running a PC that just Aint fast enough!?
ZX Spectrum 48k, NEXT, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip.

Jesus was only famous because of his DAD.

Offline TomToad

Re: Exiting Case/select statements
« Reply #19 on: February 01, 2021, 01:13:04 PM »
Don't know if this would be of any use to you, but javidx9 on YouTube has videos on how to make a NES emulator.  He is using C++ and his own graphics engine, but most of the techniques he uses translates to BlitzMax.

https://youtube.com/playlist?list=PLrOv9FMX8xJHqMvSGB_9G9nZZ_4IgteYf
------------------------------------------------
8 rabbits equals 1 rabbyte.

Offline Baggey

Re: Exiting Case/select statements
« Reply #20 on: February 02, 2021, 06:18:45 AM »
Quote
He is using C++

Proably should be put some where else. But i use Blitzmax 1.5. The reason im using Blitzmax 1.5 is you install it and use it  ;D Tried many other languages but when you start using librarys, Sdll's and linking to Modules and Make files and other stuff ive even forgot about. That needs to be there and then it dosent work! You kind of give up on them.

Oh well, getting old and set in my ways.  :o

Its like buying a car but before you can use it you need to get some wheels, get some breaks, get some tyres an engine, but do i need petrol or diesel?  :))

Kind Regards Baggey
Currently Running a PC that just Aint fast enough!?
ZX Spectrum 48k, NEXT, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip.

Jesus was only famous because of his DAD.

Offline Baggey

Re: Exiting Case/select statements
« Reply #21 on: February 05, 2021, 07:09:11 AM »
Takes a bit more typing than with select/case, but works faster.

So had a little play with the code and thought id be crafty to safe sometime typing and use a "for and next loop" for defining the function pointers.

Code: [Select]
' OO Opcodes

SuperStrict

'Import "Z80.bmx"

' Create OO opcodes Array

Local OO:Byte()[256] ' Create OO opcodes Array

' Assign the Functions into the Array elements Individualy
'OO[0] = OOO
        'OO[1] = OO1
'OO[2] = OO2

' Or

' As A big Line
'///Local OO:Byte()[]=[OOO,OO1,OO2]

' Or Automate the decleration process
' However the array pointers dont appear to be byte, short, int, string !!!????

For Local x:Byte = 0 To 2
Local text:String="OOO"
text$=text$+String.fromint(x)
text$= Right$(text$,3)
OO[x] = text
Next


 KeyDown(key_space)
Print
Print "OO Opcodes"
Print
' Call Opcode 0
OO[0]
' Call Opcode 2
OO[2]
' Call Opcode 1
OO[1]
Print
Print "Press SPACE to EXIT"

Repeat Until KeyDown(key_space)


Function OOO:Byte()' NOP
Print "0 ... NOP"
End Function

Function OO1:Byte()' LD BC,nn
 Print "1 ... LD BC,nn"
End Function

Function OO2:Byte()' LD (BC),A
 Print "2 ... LD (BC),A"
End Function

But the decleration dosent seem to be in any form of data format how would this be done ar can't it?  ???

Oh, How do i paste code in a BlitMax style window?

Kind Regards Baggey

Currently Running a PC that just Aint fast enough!?
ZX Spectrum 48k, NEXT, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip.

Jesus was only famous because of his DAD.

Online Steve Elliott

Re: Exiting Case/select statements
« Reply #22 on: February 05, 2021, 07:38:09 AM »
Quote
Its like buying a car but before you can use it you need to get some wheels, get some breaks, get some tyres an engine, but do i need petrol or diesel?  :))

I agree.  There was a discussion here about Python and the same problem.   :))
Windows 10 64-bit, 16Gb RAM, Intel i5 3.2 GHz, Nvidia GeForce GTX 1050 (2Gb)
MacOS Big Sur 64-bit, 8Gb RAM, Intel i5 2.3 Ghz, Intel Iris Plus Graphics 640 1536 MB
Linux Mint 19.3 64-bit, 16Gb RAM, Intel i5 3.2 GHz, Nvidia GeForce GTX 1050 (2Gb)
Raspberry Pi 400, Pi4, BBC B, C64, ZX Spectrum

Offline Derron

Re: Exiting Case/select statements
« Reply #23 on: February 05, 2021, 08:10:52 AM »
blitzmax style {code=Blitzmax}your code{/code}. Replace curly brackets with square brackets.


Regarding your problem: Multiple variants possible:

Have a codegenerator.bmx which just outputs the "code" you then place in your main.bmx file. So no direct usage.
Code: BlitzMax
  1. For local i:int = 0 to 255
  2.   print "myarr["+i+"] = func"+(RSet(i, 3).Replace(" ", "0"))
  3. Next
  4.  
-> copy the output code, place the block of code in your file, done



Or you rely on "reflection" - as it might have trouble to find global functions, you store all your functios in eg a type "TOpCodes".
Then during start/initialization of your app (as it is slower than the raw array-access TomToad wrote about - but during startup the additional 1ms or so can be neglected).
So during start you do:
Code: BlitzMax
  1. SuperStrict
  2. Framework Brl.StandardIO
  3. Import Brl.Reflection
  4. Import Brl.Retro 'for RSet()
  5.  
  6.  
  7. Type TOpCodes
  8.   Function Func000:Int(); Print "000"; End Function
  9.   Function Func001:Int(); Print "001"; End Function
  10. End Type
  11.  
  12. Local opCodeFunctions:Int()[255]
  13.  
  14. Local t:TTypeId = TTypeId.ForName("TOpCodes") 'find the type by name, not a given type instance
  15. If Not t Then Throw "Did you rename your TOpCodes?"
  16. Local f:TFunction
  17. For Local i:Int = 0 To 255
  18.   f = t.FindFunction("Func" + (RSet(i, 3).Replace(" ", "0")))
  19.   If f Then opCodeFunctions[i] = f.FunctionPtr()
  20. Next
  21.  
  22.  
  23. 'test execute
  24. opCodeFunctions[1]()
  25.  

You could also enhance the code to find a function like "FuncUnsupportedOpcode" which gets assigned if "f = null" ... so any opcode you use in the code - but somehow did not define in the type results in a throw/print/... message.


bye
Ron

Offline TomToad

Re: Exiting Case/select statements
« Reply #24 on: February 05, 2021, 10:12:06 AM »
Your function names cannot start with a number, they must start with a letter or underscore.  So you can have Function Op001() or Function _001(), but not Function 001().
------------------------------------------------
8 rabbits equals 1 rabbyte.

Offline Baggey

Re: Exiting Case/select statements
« Reply #25 on: February 05, 2021, 10:23:35 AM »
Your function names cannot start with a number, they must start with a letter or underscore.  So you can have Function Op001() or Function _001(), but not Function 001().

Hi, Tom its a Capital O dont need the _ then.

DAMM it works until 100  :-X   Oh WELL Seemed eaisier to read rather than "_1OO" or "O1OO" Supouse now it's what one prefer's?

Kind Regards Baggey
Currently Running a PC that just Aint fast enough!?
ZX Spectrum 48k, NEXT, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip.

Jesus was only famous because of his DAD.

Offline Baggey

Re: Exiting Case/select statements
« Reply #26 on: February 05, 2021, 10:26:52 AM »
Hi Derron thanks for the code tip.

Ive rewritten it:-

Code: BlitzMax
  1. ' Make a command line string for cutting and pasting
  2.  
  3.         Local text:String=""
  4.         Local LongtextString:String=""
  5.        
  6.         For Local i:Int = 0 To 255
  7.        
  8.                 text=(RSet(i, 3).Replace("0", "O"))
  9.                 text=text.Replace(" ", "O")
  10.                 If i<255 Then text=text+"," Else text=text+"]"
  11.                 LongTextString=LongTextString+text
  12.        
  13.         Next
  14.        
  15.         ' Note for some reason a )[ together throw a newline in printing out the string?
  16.        
  17.         text="Global OO:Byte()[]=["
  18.         text=text+LongTextString
  19.        
  20.         Print text

Nice little trick that saved me loads of time on typing! but ive had to learn some more commands in BlitzMax to spit this text line out .LOL.  :P

Thanks All for the ideas. Now to get typing.

Kind Regards Baggey
Currently Running a PC that just Aint fast enough!?
ZX Spectrum 48k, NEXT, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip.

Jesus was only famous because of his DAD.

Offline Derron

Re: Exiting Case/select statements
« Reply #27 on: February 05, 2021, 12:23:53 PM »
text=(RSet(i, 3).Replace("0", "O"))
text=text.Replace(" ", "O")


I am quite sure that this does NOT create what you want it to create.

i = 0
text = Rset("0", 3) 'text="  0"
text = text.Replace("0", "O") 'text="  O"
text = text.Replace(" ", "O") 'text="OOO"

i = 1
text = Rset("1", 3) 'text="  1"
text = text.Replace("0", "O") 'text="  1"
text = text.Replace(" ", "O") 'text="OO1"


I assume you want to a structure like "O+number" and number having leading zeros?
If so - then copy my code and just prepend the "O"
text = "O" + (RSet(i, 3).Replace(" ", "0"))

i = 0
text = "O" + (RSet("0", 3).Replace(" ", "0")) 'O000

i = 1
text = "O" + (RSet("1", 3).Replace(" ", "0")) 'O001


PS: The Reflection code should be versatile enough too - for this case here. It is only executed on startup (so not each cycle) and always contains all functions you have defined - no "accidentally forgetting to recreate the copy-paste code".
But ... do as you prefer and what seems the "most easiest/versatile" approach (to you).


bye
Ron



Offline Baggey

Re: Exiting Case/select statements
« Reply #28 on: February 05, 2021, 01:59:59 PM »
Thankyou again, as Tom pointed out you've got to use "_100" I found out when i got to 100. It dosent like the number Thats in front, thats why i used Capitol "O"

So after the tips heres my code for banging out the Template for OO_Opcodes 0 to 255 as Array Function pointers 8)

Note, for some reason a )[ together throw a newline carrige return! in printing out the string? in the OUTPUT window Maybe a BUG?

Code: BlitzMax
  1. ' Make a command line string for cutting and pasting
  2.  
  3.         Local text:String=""
  4.         Local LongtextString:String=""
  5.         Local i:Int=0
  6.        
  7.         Print
  8.        
  9.         For i = 0 To 255
  10.        
  11.                 text=(RSet(i, 3).Replace(" ", "0"))
  12.                 If i<255 Then text="_"+text+"," Else text="_"+text+"]"
  13.                 LongTextString=LongTextString+text
  14.        
  15.         Next
  16.        
  17.         ' Note for some reason a )[ together throw a newline carrige return! in printing out the string?
  18.        
  19.         text="Global OO:Byte(_[]=["
  20.         text=text+LongTextString
  21.        
  22.         Print text
  23.        
  24. '       Now For the Function part
  25.  
  26.         For i = 0 To 255
  27.        
  28.                 text=(RSet(i, 3).Replace(" ", "0"))
  29.                
  30.                 Print
  31.                 LongTextString="  Function _"+text+":Byte()' Opcode here"
  32.                 Print LongTextString
  33.                 Print "'"
  34.                 Print "  End Function"
  35.                
  36.         Next
  37.  

Wow what a lot of typing that saves. Now i need to reflect on Reflection  ??? I Assume this is to pass variables into my Functions Id like to pass one byte in!
For now i could cheat with a Global variable thats seen by each Function. But i like the style of passing variables in.

Thankyou everyone who's helping me learn Blitzmax. It indeed is a nice language.

Kind Regards Baggey
Currently Running a PC that just Aint fast enough!?
ZX Spectrum 48k, NEXT, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip.

Jesus was only famous because of his DAD.

Offline Derron

Re: Exiting Case/select statements
« Reply #29 on: February 05, 2021, 04:13:42 PM »
text=(RSet(i, 3).Replace(" ", "0"))
If i<255 Then text="_"+text+"," Else text="_"+text+"]"
LongTextString=LongTextString+text


Could also be written as
LongTextString :+ "_" + (RSet(i, 3).Replace(" ", "0"))
If i = 255 Then LongTextString :+ "]"

(just wrote it to make you aware of ":+" to skip "text = text + x")


Regarding "passing bytes" .. you could have your functions accept a parameter?
function _000(param:int)

via reflection you can also pass parameters - but it is quite slow. So best is to have all the functions accept the same parameters (could be ":object" if passing type instances or strings)


bye
Ron


 

SimplePortal 2.3.6 © 2008-2014, SimplePortal