/Home /Archive /Syndicate /Blog /Support /About /Contact  
All Visual Basic Feeds in one place!





Get new drop: current VB9 Customer Technology Preview (CTP). Copy the database Saturn5_002.mdf and Saturn5_002_log.LDF to c:\. Zipped VB9 project directory tree.

 

The Hewlett-Packard manual exhibits the following command strings (last blog I said there were 256 possible and 236 used, but here there are 250 – the explanation is that I fold 19 command strings to uppercase, e.g., LBLa = LBLA, and there are five extension command strings, 19-5+236=250; this is admittedly a tiny bit of cruft in my program). This is taken right out of the database table “InstructionSet”:

 

0        

CLX      

ENG      

GSBA     

GTO9     

LBL2     

LSTX

RCL1

S        

ST/8     

ST+8     

STO4     

X<0?     

1        

COS      

ENT^     

GSBB     

GTOA     

LBL3     

-

RCL2

SCI      

ST/9     

ST+9     

STO5     

X<>Y?    

2        

COS-1    

e^x      

GSBC     

GTOB     

LBL4      

MRG

RCL3

SF0      

ST-0     

ST*0     

STO6     

X=Y?     

3        

DEG      

F0?      

GSBD     

GTOC     

LBL5     

N!

RCL4

SF1      

ST-1     

ST*1     

STO7     

X>Y?     

4        

/        

F1?      

GSBE     

GTOD     

LBL6     

->P

RCL5

SF2      

ST-2     

ST*2     

STO8     

X<=Y?    

5        

D->R     

F2?      

GSBa     

GTOE     

LBL7     

%

RCL6

SF3      

ST-3     

ST*3     

STO9     

<X>      

6        

DSP0     

F3?      

GSBb     

GTOa      

LBL8     

%CH

RCL7

SUM+     

ST-4     

ST*4     

STOA     

X^2      

7        

DSP1     

FRC      

GSBc     

GTOb     

LBL9     

Pi

RCL8

SUM-     

ST-5     

ST*5     

STOB     

X<->I    

8        

DSP2     

FIX      

GSBd     

GTOc     

LBLA     

+

RCL9

SIN      

ST-6     

ST*6     

STOC     

X<->Y    

9        

DSP3     

GRAD     

GSBe     

GTOd     

LBLB     

PREG

RCLA

SIN-1    

ST-7     

ST*7     

STOD     

Y^x      

.        

DSP4     

GSB0     

GSBi     

GTOe     

LBLC     

PRST

RCLB

SPC      

ST-8     

ST*8     

STOE     

 

1/X      

DSP5     

GSB1     

GTO0     

GTOi     

LBLD     

PRTX

RCLC

SQRT     

ST-9     

ST*9     

STOI     

 

10^x     

DSP6     

GSB2     

GTO1     

->HMS    

LBLE     

P<->S

RCLD

ST/0     

ST+0     

ST/i     

STOi     

 

ABS      

DSP7     

GSB3     

GTO2     

HMS->    

LBLa     

PSE

RCLE

ST/1     

ST+1     

ST-i     

TAN-1    

 

CF0      

DSP8     

GSB4     

GTO3     

HMS+     

LBLb     

->R

RCLi

ST/2     

ST+2     

ST+i     

TAN      

 

CF1      

DSP9     

GSB5     

GTO4     

INT      

LBLc     

Rv

RCLi

ST/3     

ST+3     

ST*i     

*        

 

CF2      

DSPi     

GSB6     

GTO5     

ISZI     

LBLd     

R^

RCLS

ST/4      

ST+4     

STO0     

WDTA     

 

CF3      

DSZI     

GSB7     

GTO6     

ISZi     

LBLe     

RAD

RND

ST/5     

ST+5     

STO1     

X<>0?    

 

CHS      

DSZi     

GSB8     

GTO7     

LBL0     

LN       

R->D

R/S

ST/6     

ST+6     

STO2     

X=0?     

 

CLRG     

EEX      

GSB9     

GTO8     

LBL1     

LOG      

RCL0

RTN

ST/7     

ST+7     

STO3     

X>0?     

 


The first task is to fill the dictionary ‘gas tank’ of static delegates with pairs of command strings and delegates, preparatory to going into a loop reading command strings and executing delegates. Do this in DelExec.Init. The first ten commands are DSP0, DSP1, …, DSP9 for setting up the number of digits to display on the calculator faceplate and in console-mode printouts. In the ironic fashion presented last time, these will all dispatch to a single command delegate, dspcmd:

    Sub Init()

        Dim digits = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}

        Dim labels = {"A", "B", "C", "D", "E"} REM folded to uppercase

        Dim ops = {"+", "-", "*", "/"}

       

        For Each i In digits

            staticDelegates.Add("DSP" + i, AddressOf dspcmd)

        Next

 

Here, we use + to stitch together strings; & would work just as well (I’m not sure if there is any difference). Let’s look at dspcmd:

    Function dspcmd(ByVal c As String, ByVal m As Integer) As Integer

        Savex()

        Dim q = c(3) REM fish out the digit from the command string

        Dim r As Integer = dpyPrecision

        If q = "i" Then

            r = reg(25) REM DSPI takes precision from Indirect register

        Else

            r = Val(q)

        End If

        If r >= 0 And r <= 9 Then

            dpyPrecision = r

        End If

        updateWholeUI()

        Return m + 1

    End Function

 

Sure enough, we fish out the digit pseudo-argument from the input string. The only interesting thing is the DSPI case, where the digit isn’t a digit at all, but an uncial i. The DSPi command—pronounced “Disp-Eye” or “Display Indirect”—takes the precision from the I, or Indirect, register. Cool! So we need to gas up the dictionary with one more command string for dspcmd:

        staticDelegates.Add("DSPI", AddressOf dspcmd)

 

for eleven so far (the command string is folded to uppercase). Now, let’s put in all the GTOx, GSBx, LBLx, STOx, and RCLx commands, where x can be any digit or any alphabetic label from A-E:

        For Each i In digits.Concat(labels)

            staticDelegates.Add("GTO" + i, AddressOf gtocmd)

            staticDelegates.Add("GSB" + i, AddressOf gsbcmd)

            staticDelegates.Add("LBL" + i, AddressOf lblcmd)

            staticDelegates.Add("RCL" + i, AddressOf rclcmd)

            staticDelegates.Add("STO" + i, AddressOf stocmd)

        Next

 

That takes care of 15 x 5=75 command strings for 86 so far. You can see, here, why I fold the labels to uppercase: it’s so I can clump together GTOa (uncial a) and GTOA (capital A), for instance, with RCLA (capital A), despite the fact that there is no RCLa command. This is appropriate since the pseudo-argument gets fished out of the actual command string, and all we have to do is remember to fold to uppercase for lookup—but not for copy of the command string passed to the delegate. Another way of saying this is that ONLY the insides of command functions are case-sensitive. Another excuse for this is that it helped me avoid useless ambiguities like 10^X versus 10^x. But I already admitted it was cruft; get over it.

Did you spot the opportunity for dynamic identifiers? Here is what I wanted to write:

        For Each cstr In {"GTO", "GSB", "LBL", "RCL", "STO"}

            For Each i In digits.Concat(labels)

                staticDelegates.Add(cstr + i, AddressOf (cstr + "cmd"))

            Next

        Next

 

In other words, I wanted to compute not just the command string, but the name of the delegate, too, to prevent the copy-and-paste error of, say, writing

            staticDelegates.Add("GTO" + i, AddressOf gtocmd)

            staticDelegates.Add("GSB" + i, AddressOf gtocmd)

 

Now this is the kind of annoying, stupid coding mistake that I know you never make, but, alas, I am not immune. It shows up like a land mine, much later, during calculator execution as subroutines mysteriously fail to return. But it is completely avoidable if we can write the desired code. So, what would that take? For one thing, the argument of AddressOf is not known at compile time, so the delegate can’t be constructed at compile time. So, isn’t it natural to call them late-bound delegates? We don’t have them yet, but scenarios like this point out the need, and we’re prototyping them for later development in VB.

Next, we need to fill up the tank with the storage-register arithmetic commands, STO+0, STO+1, and so on. These are a lot like the += , -=, *=, and /= operators in VB: they cumulatively side-effect the indicated registers by the indicated arithmetic operation taking the other argument from the X slot of the data stack:

        For Each i In digits

            For Each j In ops

                staticDelegates.Add("ST" + j + i, AddressOf stocmd)

            Next

        Next

        For Each j In ops

            staticDelegates.Add("ST" + j + "I", AddressOf stocmd)

        Next

 

These loops add 44 command strings, for 130 so far. We need four more case-folded indirect commands:

        staticDelegates.Add("GTOI", AddressOf gtocmd)

        staticDelegates.Add("GSBI", AddressOf gsbcmd)

        staticDelegates.Add("RCLI", AddressOf rclcmd)

        staticDelegates.Add("STOI", AddressOf stocmd)

 

for 134. There are exactly 100 more commands, accounting for the 236 required minus the two unimplemented (MRG and WDATA). The program does a quick check of this at the end of DelExec.Init, counting the command strings in the database and the command strings in the Dictionary, and diffing them both ways. If a string is in the database and not the dictionary, it’s unimplemented. Vice-versa, it’s an extension. See the source for all that.

Ok, now, sit down. Take a deep breath, because I am about to put on my best Halloween face and try to scare the pants off you. Remember dspcmd? Have you noticed that it is never called statically? Go ahead, to the source, find dspcmd in DelExec, highlight it with the mouse, and press Alt-F12. That will find all references to the symbol. Just its definition and its insertion into staticDelegates at command strings DSP+i and DSPI. No calls. So what’s the use of it? Answer: the REPL loop or the UI faceplate sends one of those 11 strings into the system, which looks up the delegate in the dictionary and dispatches to it through the delegate.

Ok, great, but since it’s not used directly in the simulator program, why should it be stored directly in the simulator program? Why not just read it out of the database, just like the calculator program that uses it, not to mention along with all the state variables and what not? Well, the answer is, of course we should. Just a matter of finding a way to store code in the database, that is, a way to convert code to data. Now, what I want to do is just cut the VB code and paste it into the database, and read it back in at dictionary-creation time. We call that code literals, and this scenario points out the need for it, and we’re prototyping it for later development in VB. In the mean time, we have to use more grotesque means. The only way of saving code as data in the current CLR is to disassemble it into IL, and the only ways to restore code from data is Reflection.Emit and Lightweight CodeGen (the last two links are superb; thanks, Joel Pobar). So, I wrote a little disassembler, and write back all the command functions into the database table “Disassemblies,” with foreign keys to another table called “CmdImplementations,” which stores byte-code streams for safety and redundancy. Oh, and, of course, I have a little assembler that writes the code back out into dynamic delegates in module DynExec. If you’ll forgive the length, dspcmd ends up, in “Disassemblies,” as

0

Nop

1

Call

Savex

6

Nop

7

ldarg.0

8

ldc.i4.3

9

callvirt

get_Chars

14

stloc.1

15

Ldsfld

dpyPrecision

20

stloc.2

21

ldloc.1

22

Call

ToString

27

Ldstr

"i"

32

ldc.i4.0

33

Call

CompareString

38

ldc.i4.0

39

Ceq

41

stloc.3

42

ldloc.3

43

brfalse.s

17

45

Ldsfld

reg

50

ldc.i4.s

25

52

ldelem.r8

53

call

Round

58

conv.ovf.i4

59

stloc.2

60

br.s

8

62

nop

63

ldloc.1

64

call

Val

69

stloc.2

70

nop

71

ldloc.2

72

ldc.i4.0

73

clt

75

ldc.i4.0

76

ceq

78

ldloc.2

79

ldc.i4.s

9

81

cgt

83

ldc.i4.0

84

ceq

86

and

87

stloc.3

88

ldloc.3

89

brfalse.s

6

91

ldloc.2

92

stsfld

dpyPrecision

97

nop

98

call

updateWholeUI

103

nop

104

ldarg.1

105

ldc.i4.1

106

add.ovf

107

stloc.0

108

br.s

0

110

ldloc.0

111

ret


 To execute all calculator commands in dynamic-delegate mode, enter the “R” command at the console prompt, then execute the Diagnostics or Primes programs as before. And don’t blame me if it goes into the weeds: it’s only proof-of-concept code.

© 2005 Serge Baranovsky. All rights reserved.
All feed content is property of original publisher. Designated trademarks and brands are the property of their respective owners.

This site is maintained by SubMain(), a division of vbCity.com, LLC