Yes, and that pretty much describes my "TI life". When I bought the 99/4A, I was still in school. At that time, Pascal was THE language. C had just emerged and wasn't used for anything important. Algol had just stepped down from the top for technical/scientific applications.
So I pretty quickly acquired the expansion box, including the p-code card. After a couple of years, I had upgraded to four DS/DD disks, built my own clock and I/O card and installed 64 K 16-bit wide RAM inside the console. That of course required quite a lot of assembly programming to make it work, but on the other hand, I've hardly written any stand-alone assembly program. Some smaller ones, but mainly for technical applicaitons.
But I have made a large number of assembly support routines, both for the p-system and also for Extended BASIC, but then mainly with other people in mind, since many more had Extended BASIC than the p-system. Still, I had half a dozen or so of friends who all used Pascal on the 99. I've understood that it was a bit rare at that time that so many Pascal users actually knew each other, and three of use lived within a few hundred meters from each other.
Most of what I've used the TI for has been text based, and then the p-system is 40 column wide window to an 80 column wide screen from the beginning. But by using assembly I've for example implemented the ability to display pop-up messages on the screen, without disturbing what's there from the beginning. That's not too difficult in theory, since the 80 column screen is in low RAM, but the visible window in VDP RAM. So just write to VDP RAM to display the window, then restore the screen and the original text is back. This also means that the pop-up will be visible regardless of which of the three windows the user is displaying.
But the logic in the assembly routine to wrap words in strings so they are not cut at the end of the window is quite complex. But done in assembly to give an instant display of the message.
All my own hardware has RAM in DSR space. If they aren't battery backed, I need to reload the programs on startup. To make that easy, I wrote a loader (in Pascal), which loads object files into memory at any location, even if a CRU bit must be set to enable that memory. The loader benefits from the p-system's assembler's capability to generate code files with multiple procedures in one segment. Thus I can simply assemble a code file containing all the different code files I want loaded in one file. I included extra header information about memory and CRU address to load to, so the different procedures can end up on different cards, and also in my own internal RAM expansion (which is paged with CRU bits) in one single run.
But to write to a word in memory that's paged in by a CRU bit requires assembly. Thus there's the function crupoke(value,addr,crubase: integer): boolean; external; linked to that Pascal program. But all other stuff is done by Pascal, since it's fast enough in that case. Pascal is also able to read directly from any sector in a file, or any sector on a unit, so no assembly is needed to access the code file in my special way. Crupoke is a function instead of a procedure, since it not only writes to memory, but also reads back and verifies that there is actually RAM at the accessed location. If there isn't, the program simply skips to the next procedure to load in the code file. By specifying a CRU address of 4000H (they can't be larger than 3FFFH), it loads to normal RAM instead. If I set the CRU address to 8000H, it loads the code, or rather data, to VDP RAM.
This is what I consider a good example of where almost all of the logic of a program is in high level language, which is easier to write and debug, but that critical part that can't be done in Pascal is written in assembly.