HP-41: a M-code question



#5

Hi,

does anyone know how to program in M-Code
an instruction like XEQ IND 00 ?

It seems that

?NCXQ
24C7 is the entry point

with SETF7 for XEQ
or CLRF7 for GTO

-But in what CPU register must be stored the address 00 ?

Regards,
JMB.


#6

User register 0 is not in a CPU register. It's in the user memory "peripheral." See pages 31-33 of "HP41 MCODE for Beginners" for a description of the layout of user memory. (Register 0 is at $1FF of the user memory peripheral.) You access peripherals with the WRIT and READ instructions, after selecting the peripheral number with PRPHSLCT. HP41 user memory is peripheral 0.

Regards,
Howard

#7

This is a bit strange entry point to be calling from Mcode, since it will call a numeric label in User Code, so this makes me a bit curious why you would want to do this.
I have done something comparable in the past, a Mcode routine to skip a certain number of (User Code) lines with SKPLIN at 0x2AF9. Could be useful after a flag test or compare.

Meindert


#8

Hi,

in fact, I'm not quite sure of this entry point but
I would like to write a Gauss-Legendre integrator in M-code
in which the constants could be inserted, thus avoiding
the use of data registers to store these constants
( or very slow execution if they are in the "standard" program itself )

I've read in "Mcode for beginners" how to use status register c
to find the absolute address of register R00,
and I wonder if something like

... ( storing this absolute address in a proper CPU-register )
SETF7
?NCXQ
24C7 would be equivalent to XEQ IND 00

Regards,
JMB.


#9

I haven't tried that before. It does seem that f7 controls GTO/XEQ at that point. Looking @ ADRFCH 0004, it seems the register address is in the internal flag register, at 0008 it recalls it to C, clears the indirect bit then tests to see if it is a stack register (with ?FS 6, 5, 4).

If it is a stack register it stores the address in N then reads the register and returns with the unconverted LBL number (or text string) in C. Otherwise it's doing something with B in CHKADR.

A better way to implement such a thing might be to enter @ 24CC with the required LBL # or text string in C (in unconverted form), however, at that point R(X) controls the XEQ/GO choice. So one needs to execute SAVRC (27DF) before the above entry, or SAVRTN (27D3) if only XEQ is of interest.

It is not clear to me how you will use the function, SAVRTN will push the address located just after your function (actually the last byte of your function) in program memory, so it may be necessary to decrement the address twice before applying SAVRTN, yet then you are still presented with the problem of where you are at within the loop (no data are preserved within the CPU across user code calls). Initialize a counter in a ram register?

If you only want to execute a numeric LBL, time can be saved by skipping the BCDBIN call and just enter @ 24D1. Eg: something like:

XQNC GETPC 2950

XQNC DECADA 29CA

XQNC DECADA

XQNC SAVR10 27D5

LDI binary LBL number 0 to 63 (see the test @ 24D4)

GONC 24D1

Next time you see the process will be at your function entry point.
So you will need some kind of counter in a ram register for branch purpose at entry to function, the fastest type counter would be a text string in say R00 that initializes to text null (simple increment in binary).

Another speed up would be to save the address of the found subroutine and your function call somewhere in ram register so no search is necessary after initial call. Both these addresses would fit in the high nibbles of R00 if R00(X) is used for the counter.

I haven't considered the problem of keyboard execution of the function, it may be impossible to "come back" or require exotic poll programming (how about we put it @4000 ? ,just kidding).


Best


#10

Hi,

thank you all for these informations

Best regards

JMB.


#11

Now you have me thinking about your interesting idea.

Why bother with a LBL at all? Here is an idea, say your function name is, for example, GAUSS9. Then follow the function with the integrand! In this case one already knows where the entry to the user supplied integrand is, it might look like: (for example; F(X)=X**2+SIN(X))


store limits/step size/etc. at specified registers

CLA
ASTO 00
GAUSS9
SIN
LASTX
X^2
+
RTN


The execute address is already present in the user PC, the last byte of GAUSS ("fall into" the subroutine). One just needs to set up R(X) so that when RTN is executed it returns to the byte before GAUSS9.

Lots more details, such as where do we go when the function is done? etc. This could be the fastest integrator method.

Best


#12

Hi,

I've tested the following M-code routine:

34D ?NCXQ
09C 27D3
378 READ13(c)
03C RCR3
270 RAMSLCT
038 READ DATA
331 ?NCXQ
090 24CC
..........

After storing the global label name in R00, it works,
even if we SST the program in main memory.
Unfortunately, the following M-code instructions
( .... after ?NCXQ 24CC ) are NOT executed!

So it seems impossible to write a Gaussian integrator this way.
I did it in another way with the Gauss 5-point formula
after coding in M-code the 5 coefficients,
but using 5 entries in the FAT just for numbers is wasteful,
and it would be even worse for the 16-point formula...

Regards,
JMB.

P.S: I've read your last post and I don't really understand
how it works: the Gaussian formula needs to compute
the function at several arguments, so I don't see
how the M-code routine could change these arguments
if it is inserted in the function itself.
( sorry I'm only a beginner in M-code ... )

#13

     "the Gaussian formula needs to compute
the function at several arguments, so I don't see
how the M-code routine could change these arguments
if it is inserted in the function itself."
Guess I am not being sufficiently clear: A user code subroutine cannot be called by a microcode function, it will never return.

Thus one needs to use a "trick". One possible trick is to push the address of the function into the user code return stack so that when a user code RTN is hit, it returns to the function. It will return to the microcode at the entry point of the function.

In this code:

CLA
ASTO 00
GAUSS9 your function
SIN user code subroutine, f(X)
LASTX
X^2
+
RTN must end with RTN

SIN LASTX X^2 RTN is repeatedly executed until the integral is completed, for 5 point gauss with 10 sections it is executed 50 times. Your function must supply the value of X in X at each point and do your summation of the integral upon reentry (because at that point the result of the integrand, f(X), will be in X).

At entry to the function, it is necessary to determine which time we are going through the function, thus a ram counter is required, that's what the CLA ASTO 00 lines accomplish: initialize the counter (or use CLX STO 00).

At entry, READ e RCR 3 RAMSEL READ and the counter is now in C. If it's zero we know that this is the first time through (a result is not in X). Then increment the counter and WRITE (for next time through).

Except for the first time through: multiply f(X) (now in X) by the appropriate weight (determined by the counter) and sum it into a ram register. Every time through, except the last, using the counter, get the next abscissa and put it in X.

The function then modifies the user code return stack and falls into the user code subroutine. Here is what the exit from your function could look like (except for the last time through):

XQNC 2950 GETPC
B=A WPT save address of PC for re-insert
XQNC 29C8 DECAD+1 backup two bytes
XQNC 29C8 DECAD+1
XQNC 27D5 SAVR10 it's our return address
GONC 24F3 XEQ20+1 put it in the user code return stack

For the last time through, one could simply recall the value of the integral and put it in X and stop. Or one could jump to the line following the RTN and use user code to recall the result. Here is one way to do that:

XQNC 2950 GETPC
LDI 085 (hex value of RTN line, there must be a RTN)
M=C
XQNC 2AF7 NXLSST
C=0 XS
A<>B
A=C X
C=M
?A#C X
JNC +3
A<>B
JNC -9 (NXLSST)
A<>B
GONC 232F PUTPCX

As far as generating the abscissas and weights one option to consider is using synthetic functions RCL N and RCL M. One uses a synthetic 14 character text string in program then the above lines get the full precision data into X, for storage into registers.

Or, the first time into the function, the data could be generated and placed into registers, that's a lot of load digit instructions! Anyway, for multiple sections, ram registers should be used to avoid generating the Xi and Wi repeatedly.

On the other hand, if you are determined to not use registers for the Xi and Wi, or to generate higher order coef, a table grabber could be used so that each word holds a byte, instead of LD which takes two words to load a byte. For 12th order the table would be 84 words and possibly 10 words for the grabber.

Best


Edited: 2 July 2007, 9:50 a.m.


Possibly Related Threads...
Thread Author Replies Views Last Post
  HP-41(CL): The easiest way to transfer FOCAL programs from a Linux PC to the HP-41 Geir Isene 13 2,492 12-05-2013, 02:40 AM
Last Post: Hans Brueggemann
  HP PRIME: APP program code DISAPPEARS !! Joseph Ec 0 452 11-25-2013, 11:35 AM
Last Post: Joseph Ec
  Non-Prime question alert: Hp-41 and synthetic instructions Marcel Samek 11 1,785 11-04-2013, 09:31 PM
Last Post: sjthomas
  Where to the 32-bit version of User Code Utiltiy for HP-41 ? Olivier (Wa) 2 654 09-26-2013, 01:55 AM
Last Post: Olivier (Wa)
  A HP42S Code Editor Andreas 9 1,375 09-22-2013, 03:17 AM
Last Post: Andreas
  Dynamic Gaussian Quadrature code in Excel VBA Namir 4 811 07-30-2013, 07:37 PM
Last Post: Namir
  HP-65 Morse Code Dan Lewis 7 1,088 01-29-2013, 05:22 PM
Last Post: Mike T.
  hp 41 series wire-wrapped connector question. Matt Kernal 3 701 06-19-2012, 01:48 AM
Last Post: Luiz C. Vieira (Brazil)
  [WP34s] undefined OP-code fhub 21 1,994 04-28-2012, 04:09 AM
Last Post: Marcus von Cube, Germany
  Re: HP-41 Navigation PAC question Timo 0 399 02-27-2012, 04:06 AM
Last Post: Timo

Forum Jump: