JSR, SBR and RET: Passing Parameters Into a Routine Without a Controller Tag
Type 4, Code 31 on a 1756-L83E, and the routine it pointed at was three rungs long. The general instructions reference gives three separate ways to earn that code and every one of them is about counting: a JSR with fewer input parameters than its SBR, a RET with fewer return parameters than its JSR, and a RET instruction sitting in a main routine where it does not belong.
What those three have in common is the thing worth knowing. A JSR does not “call” a routine in the way a function call works in a language you have used — it copies a list of values in, runs the rungs, and copies a list of values back out, and the copying is positional. Get the lists different lengths and there is nothing sensible for the controller to do.
That mechanism is also why you never need a controller-scope tag to feed a subroutine.
What actually moves when the rung goes true
Five things happen in order, and only two of them involve your logic.
The rung goes true and the JSR is reached, at which point nothing has moved. Then the input parameters are copied, by value, in the order they are listed on the instruction face, into the tags named on the SBR at the top of the subroutine — so a REAL called Bag_Weight in the calling routine becomes a REAL called Gross inside the subroutine, and from that moment they are two separate values in two separate places. The subroutine’s rungs then run on its own tags. Finally the RET copies the return parameters back into the tags named on the JSR, and the scan of the subroutine ends there. Both copies are by value in both directions, which has a consequence people trip on the first time: change Gross inside the subroutine and Bag_Weight in the caller does not move until a RET puts it there.
Positional, not by name. The order on the instruction face is the whole contract.

Both boxes are program-scoped tags in the same program. The controller tag list is not involved at any point in this picture.
Every tag in that figure is local. The calling routine’s tags live under Parameters and Local Tags for the program, the subroutine’s tags live in the same place, and nothing about the arrangement requires a name that the rest of the controller can see.
That is the whole answer to “without a global tag”. The parameters are the interface.
The instinct that produces controller tags here is worth naming, because it is nearly universal: you need one routine to hand a number to another routine, the two are in different places in the tree, and a controller-scope tag is the fastest thing that works. It does work. It also means that six months later cross-referencing that tag returns fourteen rungs across four programs, and no one can tell you which of them is the writer that matters.
Where the counts and the types have to agree
The reference manual is unusually direct about the type rule, and it prints it as a warning rather than as a note.
For each parameter in an SBR or RET instruction, use the same data type — including any array dimensions — as the corresponding parameter in the JSR instruction, because different data types may yield unexpected results. Not an error. Unexpected results. A DINT passed into an INT receiver is a positional byte-level copy of the kind the COP instruction performs, and it will do something arithmetically explicable and operationally wrong. The counts, unlike the types, do fault: a JSR with fewer inputs than its SBR is type 4 code 31, a RET with fewer returns than its JSR is the same code, and so is a RET that somebody left in a main routine. A JSR that jumps to a fault routine is type 4 code 990, or whatever code you supplied yourself.
The types do not fault. Only the counts do, and that asymmetry costs people days.

Three different mistakes, one fault code. Code 31 tells you the family of the problem and never the instance, so the routine name in the Major Faults tab is the only lead you get.
The placement rules are short and they are load-bearing. SBR goes first in the routine if there are input parameters at all; RET goes last if there are return parameters. Ladder lets you add extra RET instructions to exit early on a condition, and function block permits exactly one. The ceilings are 40 input parameters, 40 return parameters, and 25 levels of nesting — and if you are anywhere near forty of anything, the thing you are building is not a subroutine.
A JSR can call any routine except the main routine of a program. That one is not a guideline, it is a stated restriction.
One set of local tags, and what that costs
Here is the part that is not in the manual, because it is a consequence rather than a statement: a subroutine is not reentrant.
Call the same routine twice on one scan, once for station 1 and once for station 2, and both calls use the same Gross, the same Offset and the same Answer, because a routine is not instantiated the way an Add-On Instruction is. In the ordinary case that is fine and it is what people mean by a reusable subroutine — each RET copies the answer out before the next JSR copies the next set in, so both stations get correct numbers. What is holding that together is the ordering and nothing else. Put the second call in a different task, or leave a value in a local tag on one call expecting to find it there on the next, and the same three tags now have two owners with no arbitration between them.
One routine, one set of tags, however many JSR instructions point at it.

Serially reusable, not reentrant. Both answers here are right, and the reason is the sequence rather than the design.
Anything that needs two live copies at once needs an AOI, where every instance gets its own backing tag.
What prescan does to all of this
This one is quiet enough to miss and it explains a whole class of first-scan oddity.
On prescan, the controller executes every subroutine in the project. The rung is treated as false, RET instructions are ignored specifically so that all the rungs past them still get prescanned, and here is the part that matters: input and return parameters are not passed. So the subroutine is prescanned with whatever its local tags already hold, which after a download is zero and after a mode change is whatever the last scan left. A subroutine invoked from more than one place is prescanned once, not once per caller. Postscan takes the same action as prescan.
None of that hurts a routine that only calculates. It hurts a routine that latches something, or that uses the first-scan bit inside itself while its parameters are still empty.
Initialise inside the subroutine, not on the way in. The parameters are not there yet when prescan runs.
The third option, and what it is actually for
Program parameters get raised whenever this subject comes up, and they solve a neighbouring problem rather than this one.
The program parameters manual, publication 1756-PM021E-EN-P, defines four kinds. Input parameters are passed by value and refreshed before each scan of a program, so their values do not change under the logic mid-scan. Output parameters are passed by value and copied out at the end of the program’s execution, and they support fan-out to several destinations. InOut parameters are passed by reference — the manual describes them as a pointer that closely resembles an alias tag — so their values genuinely can change during execution. Public parameters are an encapsulated controller-scope tag living at program level, for large structures shared between programs. All of that operates at the boundary of a program, which means program parameters are the replacement for controller tags between programs, while JSR parameters are the replacement for them between routines.
Program boundary, not routine boundary. That is the entire distinction between them.
One detail in that manual belongs in this article because it is the same hazard in a different coat: the copy for a BOOL, SINT, INT, DINT, LINT or REAL parameter will not be interrupted, but the copy of any other predefined or user-defined type may be interrupted by a task switch. A UDT handed across a program boundary can be torn halfway. The manual’s own suggested remedy is to buffer it with a CPS.

Every marked row comes from the same difference. A subroutine has one set of storage; an AOI has one per instance.
If the logic is heading towards Structured Text rather than more ladder, the JSR syntax survives the move intact — JSR with a routine name, an input count and the parameter lists, SBR with the receivers, RET with the values going back — and the six constructs that replace the rest of the routine are a short read.
What to do with the subroutine you already have
Open its Parameters and Local Tags and look at what is there. If the routine reads controller-scope tags directly, those are its real inputs and they are undeclared — write them down, then move them onto the JSR as input parameters one at a time, adding the matching receiver to the SBR as you go so the counts never disagree between downloads.
Undeclared inputs are the ones that break when somebody else edits the caller.
Then check the Major Faults tab habit. A type 4 code 31 names the program and the routine, so before you go looking at rungs, count the parameters on the JSR against the parameters on the SBR and count the RET against the JSR. Two counts, thirty seconds, and it is one of those three every time. The major fault code list is worth having open while you do it.