Jeremy Leach's 32-bit Maths

lanternfish

Senior Member
I have tried to use Jeremy Leach's 32-bit Math 'Virtual CPU' code (http://www.picaxeforum.co.uk/showthread.php?t=10165&highlight=32-bit+maths) to no avail.

It always results in an "ERROR 2" message, which seems to be a divide by zero error.

Lines 260 - 264 appear to be store the operand values but doesn't seem to do so when I run it in the simulator (28X2).

I am very keen to use this code and would greatly appreciate some clarification from Jeremy and/or others on its use.

Cheers
 

hippy

Ex-Staff (retired)
It always helps to provide the code you're using to save others from having to go through the process of cutting and pasting, or at least show what the lines suspected of being faulty are. The easier you make it for people, the more likely you are to get a response.

I didn't try the code so cannot tell which lines you refer to. My first thought is it's to do with PEEK/POKE as the use of SFR changed slightly on the 28X2 but I cannot see how that would actually affect things ( pokeable SFR started at $50 on pre-X2 ).

It may be a simulation issue. Have you tried simulating the code on another PICAXE or running on a physical chip ?
 

lanternfish

Senior Member
Sorry about that.

I have just been running Jeremy's code as written by him. This was so I could get a good understanding of what is happening within it before utilising it in my own app.

The following section is the one that doesn't seem to work properly.

Will simulate it on another picaxe as you have suggested.

Eeprom 0,("+B-C*D/E=A")

'Example calculation: (64321 - 1052 * 761 / 4523 )
'w0 = result ; A
w1 = 64321 ; B
w2 = 1052 ; C
w3 = 761 ; D
w4 = 4523 ; E
ProgramCounter = Procedure0StartAddress ' ProgramCounter = b13
Gosub ExecuteProcedure
ExecuteProcedure:
'ON ENTRY: ProgramCounter has been set to the start of the Procedure
' Stored in Eeprom from 0
' Save the Program Counter @ 97
Poke ProgramCounterAddress,ProgramCounter 'Save the ProgramCounter

'Load General Purpose registers with corresponding w0 to w4 values
For Address1 = 50 To 59
Address2 = Address1 + GPRStartAddressLessb0Address ' Address1 + 37 (87 - 50)
Peek Address1,Temp1Byte ' Temp1Byte = b12
Poke Address2,Temp1Byte
Next

'Set the Accumulator to 0
AccumulatorMSW = 0
AccumulatorLSW = 0
 

hippy

Ex-Staff (retired)
If you post the complete code, so it can be loaded into the Programming Editor and run, it may encourage others to have a look at it, or it may become a case of waiting for Jeremy to pass by.

I tried copying the code from the PDF but that only works for me a page at a time and it was losing end of lines so, while I was prepared to run the code to see what happens and help if I could, I don't have the inclination to do a lot of preliminary work to solve a problem which isn't mine. It's really a case of helping others to help yourself. The easier you make if for people to help the more likely they will.
 

lanternfish

Senior Member
Attached is full listing.

In respect of the 28X2, I think I now understand what needs to be changed. By changing the values of

Symbol RTStartLessb4Address = 26 '(80 - 54)
Symbol GPRStartAddressLessb0Address = 37 '(87 - 50)


to point to the respective sfr addresses for b0 and b4, the program runs without error. There may be other values that will need to be amended.

And I just need to try a few test runs with other (expected) values for B to E to check any alterations I make.

The code should execute pretty quickly with an X2 at greater than 8MHz.

And thanks for the comments re posting full code etc. I'll try to be more thorough in future.

Cheers

'**********************************************
'* 32-Bit (unsigned) maths on a PICAXE *
'* J.Leach 2006 *
'**********************************************

'****************************
'**** PROCEDURE POINTERS ****
'****************************
Symbol Procedure0StartAddress = 0

'********************************
'**** VIRTUAL PROGRAM MEMORY ****
'********************************
Eeprom 0,("+B-C*D/E=A")

'**************************
'**** PICAXE VARIABLES ****
'**************************
'Word 0 (b0 and b1)
Symbol OperandLSW = w0
Symbol OperandLSWLSB = b0
Symbol OperandLSWMSB = b1
'Word 1 (b2 and b3)
Symbol T = w1
Symbol Temp2Word = w1
Symbol InstructionOperand = b2
'Word 2 (b4 and b5)
Symbol AccumulatorLSW = w2
Symbol AccumulatorLSWLSB = b4
Symbol AccumulatorLSWMSB = b5
'Word 3 (b6 and b7)
Symbol AccumulatorMSW = w3
'Word 4 (b8 and b9)
Symbol S = w4
Symbol Temp1Word = w4
'Word 5 (b10 and b11)
Symbol OperandMSW = w5
Symbol Address = b10
Symbol Address1 = b10
Symbol Address2 = b11
'Word 6 (b12 and b13)
Symbol InstructionCode = b12
Symbol Temp1Byte = b12
Symbol ProgramCounter = b13
Symbol Temp2Byte = b13
Symbol ErrorFlag = b13
Symbol Index = b13

'*******************
'**** CONSTANTS ****
'*******************
Symbol LCDOutPin = 6 'LCD used for alerting, but other methods can be used.

'Addresses
Symbol RTStartAddress = 80
Symbol RTEndAddress = 83
Symbol DenominatorLSBAddress = 84
Symbol DenominatorMSBAddress = 85
Symbol ErrorFlagAddress = 86
Symbol GPRStartAddress = 87
Symbol ProgramCounterAddress = 97
'Error constants
Symbol ERROR_Overflow = 0
Symbol ERROR_NegativeResult = 1
Symbol ERROR_DivideByZero = 2
'Other
Symbol RTStartLessb4Address = 26 '(80 - 54)
Symbol GPRStartAddressLessb0Address = 37 '(87 - 50)

'**************
'**** MAIN ****
'**************
Main:
Serout LCDOutPin,N2400,(254,1) 'Clear LCD. NOT really part of this module.
Pause 5000

'Example calculation: (64321 - 1052 * 761 / 4523 )
w1 = 64321
w2 = 1052
w3 = 761
w4 = 4523
ProgramCounter = Procedure0StartAddress
Gosub ExecuteProcedure
End

'*************************************************
'**** VIRTUAL ARITHMETIC AND LOGIC UNIT (ALU) ****
'*************************************************

Add:
'Performs : Accumulator = Accumulator + Operand
'Jumps to error routine on overflow
'Uses both words of Operand (because used by Multiply and Divide routines)
'ON EXIT: Operand is same as on entry

Poke ErrorFlagAddress,ERROR_Overflow

'Add LSW
AccumulatorLSW = AccumulatorLSW + OperandLSW
If AccumulatorLSW >= OperandLSW Then Add_1
'Add Carry to MSW and jump to error routine if overflow
AccumulatorMSW = AccumulatorMSW + 1
If AccumulatorMSW = 0 Then CPU_Error

'Add MSW
Add_1:
AccumulatorMSW = AccumulatorMSW + OperandMSW
'Jump to error routine if overflow
If AccumulatorMSW < OperandMSW Then CPU_Error
Return

Subtract:
'Performs : Accumulator = Accumulator + OperandLSW
'Jumps to error routine if the result is less than zero
'Uses only the LSW of Operand
'ON EXIT: Operand is same as on entry

Poke ErrorFlagAddress,ERROR_NegativeResult

'Subtract LSW
Temp1Word = AccumulatorLSW
AccumulatorLSW = AccumulatorLSW - OperandLSW

If Temp1Word >= AccumulatorLSW Then Subtract_1
'Borrow from MSW and jump to error routine if this will make the overall result
'negative.
If AccumulatorMSW = 0 Then CPU_Error
AccumulatorMSW = AccumulatorMSW - 1

'Note: No need to Subtract MSW as only OperandLSW is being used
Subtract_1:
Goto Fetch

Multiply:
'Performs : Accumulator = Accumulator * OperandLSW
'Jumps to error routine on overflow
'Uses only the LSW of Operand
'ON EXIT: Operand is corrupted

Poke ErrorFlagAddress,ERROR_Overflow

'Calculate the higher multiple and keep in Accumulator
Temp1Word = AccumulatorLSW
Temp2Word = AccumulatorMSW
AccumulatorLSW = 0
AccumulatorMSW = OperandLSW * Temp2Word
Temp2Word = OperandLSW ** Temp2Word
'Check for overflow
If Temp2Word > 0 Then CPU_Error

'Calculate the lower multiple and put in Operand
OperandMSW = Temp1Word ** OperandLSW
OperandLSW = Temp1Word * OperandLSW

'Add the multiples to get the final result
Gosub Add
Goto Fetch

Divide:
'Performs : Accumulator = Accumulator / OperandLSW
'Jumps to error routine if divide by zero
'Uses only the LSW of Operand
'ON EXIT: Operand is corrupted

Poke ErrorFlagAddress,ERROR_DivideByZero

'Check for error
If OperandLSW = 0 Then CPU_Error

'Zero the Running Total
For Address = RTStartAddress To RTEndAddress
Poke Address,0
Next

'Calculate the quotient and remainder for 65535/OperandLSW
S = 65535 / OperandLSW
T = 65535 // OperandLSW

'Store the denominator
Poke DenominatorLSBAddress,OperandLSWLSB
Poke DenominatorMSBAddress,OperandLSWMSB

Divide_1:
'Calculate S * AccumulatorMSW in the Operand, and add to Running Total.
'Note: uses variables very carefully !
OperandLSW = AccumulatorMSW
Gosub SwapAccumulatorWithRT
OperandMSW = S ** OperandLSW
OperandLSW = S * OperandLSW
Gosub Add
'Update the running total
Gosub SwapAccumulatorWithRT

'Calculate the new Numerator
OperandMSW = 0
OperandLSW = AccumulatorLSW + AccumulatorMSW
If OperandLSW > AccumulatorMSW Then Divide_2
OperandMSW = 1
Divide_2:
AccumulatorLSW = AccumulatorMSW * T
AccumulatorMSW = AccumulatorMSW ** T
Gosub Add

'Check to see if the new numerator is a single word. Loop back if it isn't
If AccumulatorMSW > 0 Then Divide_1

Temp1Word = AccumulatorLSW
'Retrieve the running total
Gosub SwapAccumulatorWithRT
'Retrieve the denominator
Peek DenominatorLSBAddress,OperandLSWLSB
Peek DenominatorMSBAddress,OperandLSWMSB
'Calculate AccumulatorLSW/Demominator and store in Operand
OperandLSW = Temp1Word / OperandLSW
OperandMSW = 0
'and add to the Running total to give the final result
Gosub Add
Goto Fetch

SwapAccumulatorWithRT:
'Swaps the Accumulator value with the Running Total value
For Address1 = RTStartAddress To RTEndAddress
Address2 = Address1 - RTStartLessb4Address
Peek Address1,Temp1Byte
Peek Address2,Temp2Byte
Poke Address1,Temp2Byte
Poke Address2,Temp1Byte
Next
Return

StoreAccumulatorLSW:
'ON EXIT : The specified w0-w4 variable is loaded with the LSW of the Accumulator.
'The 'Return' statement will end the use of the Virtual CPU
'and return to 'normal' PICAXE code.

Address = InstructionOperand - "A" * 2 + 50
Poke Address,AccumulatorLSWLSB
Address = Address + 1
Poke Address,AccumulatorLSWMSB
Return

'*******************************
'**** VIRTUAL CPU ROUTINES *****
'*******************************

CPU_Error:
Peek ErrorFlagAddress,ErrorFlag
Serout LCDOutPin,N2400,(254,128,"ERROR: ",#ErrorFlag," ")
End

ExecuteProcedure:
'ON ENTRY: ProgramCounter has been set to the start of the Procedure

'Save the Program Counter
Poke ProgramCounterAddress,ProgramCounter 'Save the ProgramCounter

'Load General Purpose registers with corresponding w0 to w4 values
For Address1 = 50 To 59
Address2 = Address1 + GPRStartAddressLessb0Address
Peek Address1,Temp1Byte
Poke Address2,Temp1Byte
Next

'Set the Accumulator to 0
AccumulatorMSW = 0
AccumulatorLSW = 0

'*************************
'**** VIRTUAL DECODER ****
'*************************

Fetch:
'Fetch an Instruction code and operand from Program Memory
Peek ProgramCounterAddress,ProgramCounter 'Retrieve the ProgramCounter
Read ProgramCounter,InstructionCode
ProgramCounter = ProgramCounter + 1
Read ProgramCounter,InstructionOperand
ProgramCounter = ProgramCounter + 1
Poke ProgramCounterAddress,ProgramCounter 'Save the ProgramCounter

'Load the contents of the specified Register into OperandLSW
Address = InstructionOperand - "A" * 2 + GPRStartAddress
Peek Address,OperandLSWLSB
Address = Address + 1
Peek Address,OperandLSWMSB
OperandMSW = 0

Execute:
'Execute the loaded Instruction

Lookdown InstructionCode,("+","-","*","/","="),Index
Branch Index,(Instruction_Add,Subtract,Multiply,Divide,StoreAccumulatorLSW)

Instruction_Add:
Gosub Add
Goto Fetch
 

hippy

Ex-Staff (retired)
In respect of the 28X2, I think I now understand what needs to be changed. By changing the values of ... to point to the respective sfr addresses for b0 and b4, the program runs without error.
Thanks for the code, and I think you've cracked it. I haven't had a chance to run the code but if it's using Peek and Poke to move 'b' variables to and from other SFR that will likely cause problems.

On the pre-X1 PICAXE's the 'b0' to 'b13' variables are at SFR $32 to $3F ( I think that's consistent on all ), on the X1 I recall "it's complicated", and on the X2 they are mapped to $00 to $0D ( accessed via Peek/Poke ). Not sure why the code doesn't work for pre-X1, non-18X but it may be using other SFR that do not exist, $C0 to $FF, and/or, $70 to $7F is shared with $F0 to $FF. I'm used to think of SFR with addresses in hex so in decimal take a bit of mental gymnastics for me.

Ideally one would have constants at the top of code defining "Symbol SFR_ADDRESS_B0" and such, and write the code so it works automatically when changed but it has to be appreciated that not everyone has the time to produce 'perfect code' ( if there ever is such a thing ) nor has an obligation to do so, so I don't mean that with any offence to Jeremy or anyone else who posts code - One has to take what one's given and be grateful for the generosity they show and the groundwork done, and I'm not saying that anyone didn't appreciate that !
 

lanternfish

Senior Member
On the pre-X1 PICAXE's the 'b0' to 'b13' variables are at SFR $32 to $3F ( I think that's consistent on all ), on the X1 I recall "it's complicated", and on the X2 they are mapped to $00 to $0D ( accessed via Peek/Poke ). Not sure why the code doesn't work for pre-X1, non-18X but it may be using other SFR that do not exist, $C0 to $FF, and/or, $70 to $7F is shared with $F0 to $FF. I'm used to think of SFR with addresses in hex so in decimal take a bit of mental gymnastics for me.
This is explains the 28X1 sim error which went something like this: "Not a good idea to poke to this location".

And thanks for the info on register loactions for the earlier picaxe chips.

Ideally one would have constants at the top of code defining "Symbol SFR_ADDRESS_B0" and such, and write the code so it works automatically when changed but it has to be appreciated that not everyone has the time to produce 'perfect code' ( if there ever is such a thing ) nor has an obligation to do so, so I don't mean that with any offence to Jeremy or anyone else who posts code - One has to take what one's given and be grateful for the generosity they show and the groundwork done, and I'm not saying that anyone didn't appreciate that !
Good hint. And I certainly am grateful for this particular piece of code. Must have been a difficult though rewarding exercise.

Thanks for the program, Jeremy. And thanks for your assistance with this Hippy.

Cheers
 
Top