The title of this question is now trivially answered. But what is the purpose of having LOC as well as ORG?
To control location within generated binary file independent from location assumed by the assembly code.
Keep in mind, early tools leave a way finer controll than one is used today - after all, there wasn't much of a canon how it should work, so best tools were the ones able to fit to each owns style.
In what situation is LOC necessary or useful?
When would de-synchronizing the two counters be desirable?
For sure not in trivial programs, but once leaving that there are many ways to make it useful. The most important is to save disk/tape space and speed up loading.
Does it have something to do with relocatable assembly?
Kind of, but it's rather about modularization within a single file.
Relocation is about collecting information about code to address relation, which is used to patch a program at a later time to make it work on a specific address and/or to combine multiple programs by resolving pointers (addresses) inbetween. Essentially leaving the decision about location to a later stage of binding or as late as load time.
Relocation essentially defers any (well, most) decisions about location during assembly/compile to later stages.
Quite a handy thing, except, it does need a sophisticated system of linkers and loaders, depending on stage applied and capabilities needed. Similar it needs a set of conventions to work with - and as a side effect some constructs may be hard to create or need a lot of setup to configure them.
Having the ability to independent control of placing and assuming location direct in a programs source allows to gather many benefits of complex application structures without the need for sophisticated linkers, loaders and overlay managers.
In what situation is LOC necessary or useful?
Lets take the most obvious, overlays, as example. With rather limited memory overlays were the way to go (*1). Of course one could put every overlay into a dedicated file and load it in times of need using whatever the file system and loader offered. Rather laborious when having many smaller modules that not only need to be loaded independent, but as well use the same address reference to global memory items they had to work with. I guess we all remember (early) C programming with tons of include files and constant haggling with including just as much as needed and so on. Imagine doing the same with punch cards stacks. Similar, noone will love you if you deliver an application, or some functions as a series of dozends of files that had to be kept consistent (*2)?
Sounds like a nightmare? Well, it was. Not as bad as it sounds, but bad enough.
So what about putting all into one source stack (file), delivering a single binary? With an independent LOC this can easy be done. Imagine a structure like this:
START APROG
ORG 0
B MAIN * Goto MAIN
COMMON DS 0D
... DS ... * Some common code and data
... DS ...
ORG 1*1024 * Real Programmers make code readable :))
MAIN DS 0H
... ... * Main Program Code
EOMAIN DS 0D * End of MAIN
* Lets define the buffer to execute overlays and its size
OVLBUF EQU 4*1024 * Overlay Area starts at 4 Ki
OVLSIZ EQU 1024 * Hey, 1 KiB is a lot of code!
ORG OVLBUF
OVLINIT DS 0H
... Code for a initial overlay,
... maybe containing initialisation code,
... to be discarded after first run?
EOLOAD DS 0D * End address of static loaded
* So far pretty standard, isn't it?
* But watch it when the next one comes
OVLSTRT
ORG 0*OVLSIZ+OVLSTRT * or 5*1024
LOC OVLBUF * that is 4*1024
OVL0 DS 0H
... Code for the first (real) overlay
* But watch it when the next one comes
ORG 1*OVLSIZ+OVLSTRT * or 6*1024
LOC OVLBUF * that is 4*1024
OVL1 DS 0H
... Code for the second overlay
... and so on (*3)
END
This in this program all overlays can be written once, they all use the same definition for global data and all get outputted into a single binary file. But the code is generated in a way as if each of the overlay sections would have been assembled at the buffers address.
At startup (*4) only the part up to EOLOAD is loaded and execution started. It contains the main application including the overlay manager. Whenever one of the overlays is needed, the 'manager' would go to the programs disk file, read that overlay into the buffer and call it.
Now, 'manager' sounds big, but in reality it takes an overlay number 0..n, multiplies it by OVLSIZ, adds OVLSTRT and reads from the overlay binary, starting at the value calculated, in length OVLSIZ, to OVLBUF. So essentially two assembly instructions and a file read call (*5).
Looks like a handy way to have arbitrary large programs in limited space, doesn't it? And everything done with just the LOC and a bit of brainpower. No need for any tools. Stuff like that will run on bare metal - which was as important back then (*6), as it wasn't just about size or less capable tools, but no tools at all. The Assembler is all needed.
Of course that is not the only usage. Another would be assembling a program that has parts that need to end up in specific memory locations. While it may (or may not) be assembled with ORG, it would end up with many 'empty' data inbetween. At least without segmentation control. And with segmentation it needs a loader capable of moving that segments into their designated spaces.
In fact, it is what compilers and their tools spit out: a binary blob, which does hold already relocated code, but not at the offsets (addresses) they are intended to run, so an OS' loader has to do that part. Hard on bare metal.
Interestingly, if one takes a close look at more modern tools like CC65, it would reveal that the binary produced by the linker is exactly that, a sequence of segments, located at one offset but intended to run at another. With a single segment it's a direct loadable one, with a more complex structure a tiny loader stub is required. The linkage file delivers all needed information :)) (*7)
Last but not least, the addition of LOC is the way to fit programs into any memory shape needed. Like when fitting stuff into code crated by a separate compiler, or creating compiler or an OS from scratch. Stuff important in the early days with its usefulness almost forgotten in a world cushioned on gigabytes of software layers.
With ORG and LOC an Assembly programmer can design the binary created exactly the way he wants it, creating the things he needs without (much) need for other tools.
*1 - This got as far as (later) transaction systems handling each and every transaction code as an overlay, loaded when that transaction was called -in memory strapped systems all into the same buffer, making it quite slow, but making it run.
*2 - Yes, I know, today it's common to simply deliver 40,000 files for a notepad like application and expect them all to arrive well and unmangled at some wired configured Windows machine at the end of the world - aka your next door neighbour.
The art of packaging got lost soon after DOOM.
*3 - And yes, I know, this is made to be packaged in a nice set of macros doing all the calculation, but this is about showing the workings.
*4 - Which, if the OS doesn't support partial load, might be done by a tiny startup stub, doing nothing else but replacing itself by the overlay files content up to EOLOAD.
*5 - Well, in a real one may add a check against max module number and as well provide a way to have overlays call each other - chaining that is, not subroutine call. And no, don't ask me about what spaghetti code that may end in. WE DID NOT HAVE THE MEANS TO WAST GIGABYTES ON SUCH MONDANE STUFF. So, call chaining it was :)
*6 - It still is for that tiny crew of bare metal programmers still around. The ones that bring all your nifty complex systems like Linux onto a new SoC.
*7 - I think I remember we already had exactly these two questions already about CC65 - which does it produce a huge empty file when using ORG and how to load one that has segments.