BBC BASIC: inline assembly
First one that comes to mind would be the BBC BASIC family.
Beside many great features to access the OS, Assembly code could be directly inserted. It would be assembled right between the BASIC lines before and after.
10 P%=8000 : REM set Assembly location
20 [
30 LDA #1
30 JSR some_function
40 RTS
50 ]
60 CALL 8000 : REM now execute it
The feature originally started out with Acorn's Atom, followed by the BBC Micro (Acorn Proton) and it's offsprings (all the way to the Electron). BBC BASIC as it was called by now was ported from the 6502 to other popular CPUs, foremost of course to all the ones supported by BBC Micro Tube modules, like Z80, NS32016 or ARM (Standard BASIC for RiscOS).
Especially the Z80 Version became wide spread as machines like Sinclair's Z88 (as Tofro mentioned) or Amstrad's N100/200 series did feature it - now of course supporting Zilog syntax :))
In more recent years it also has been ported (and extended) for Windows and TI 83 calculators (also Z80 based).
AFAICT it's still under somewhat active development by its original creators.
There are also several new(er) reimplementations available, so it might be a good idea to search a bit for the desired machine/OS (like Linux).
Amstrad CPC: Resident System Extension
As comments on question pointed out, inline assembly source consumes much more space than just machine code bytes, plus assembly time.
A memory and time efficient compromise
One can reap those other benefits mentioned in question: (1) memory-efficient, (2) time-efficient, (3) information sharing/exchange with basic.
One option is basically to consider assembly routines a kind of library or system extension, rather than part of BASIC program. With proper calling mechanisms, such libraries can be used from basic very conveniently. They are just developed with separate tool, not directly inside BASIC source.
How Amstrad did it
That's the approach taken by Amstrad when developing the CPC 464. Amstrad BASIC allows to register extension routines made of Z80 machine code. BASIC can then call them exchanging typed parameters: integers passed as values or pointers, strings as pointers. This allows changing BASIC variables (kind of multiple return values).
It can be used with libraries supplied as ROM or loadable into RAM from tape or disc. Short reference: RSX on CPCWiki
Actual example of BASIC call
One can program this:
10 memory &9000 : LOAD"extensio.bin" : call &9000 'register RSX
20 a$="mystring" : |myuppercasecommand,@a$ : print a$ 'would print MYSTRING
30 |mywhatevercommand,myintegerargument1,@myargument2passedaspointer
Amstrad used it for their extensions
Disc drive
This was convenient enough that Amstrad used this very mechanism to provide BASIC access to hardware extensions like the floppy disc drive with functions like |DISC
, |TAPE
, |REN,"oldname.bad","newname.bas"
, etc (details: AMSDOS on CPCWiki).
Hardware-based speech synthesizer
For example, the Amstrad SSA-1 Speech Synthesizer provided convenient routines callable from BASIC. This would be a valid BASIC program using speech synthetizer:
10 ' User has to first run program supplied by speech synthetizer manufacturer.
20 |say,"Hello, what is your name?"
30 input name$
40 r$="Pleased to meet you " + name$ + "." : |say,@r$
Official documentation
To create and register personal extensions, Amstrad provided an assembly-level API , documented in SOFT 968 Section 10: Expansion ROMs, Resident System Extensions and RAM Programs.
Extract:
This example uses an invented external command which takes a string of characters, looks
these up in an index and returns a reference number. The external command is assumed to be
designed to be called from BASIC as follows:
|REFNUM,@CHARTRING$,INDEXNUM,@REFNUM
i.e. The first parameter is a string (whose address is passed) which is to be looked up. The
second parameter is a number specifying which index to use, and the third parameter is a
variable (whose address is passed) which is to be set to the required reference number.
Apple II: Monitor, Mini-Assembler, Ampersand, USR
While the Monitor and Mini-Assembler were not technically part of BASIC, they were available in ROM and machine language code they produced could easily be run from BASIC.
The Monitor
From BASIC, you could access the monitor with the ubiquitous "CALL -151" command. It would also come up if a BRK instruction was executed. The system monitor provided direct access to system memory (including ROM and memory-mapped hardware) and various features useful for directly manipulating memory. The monitor contained a disassembler which could be accessed by inputting an address followed by 'L', e.g. "300L" will disassemble the code located at (hexadecimal) address 300. You could continue on disassembling more code just by typing 'L', as the monitor remembers the last address it was operating on. Simply typing an address with no command would dump the memory at that location in hexadecimal form. The monitor also saved and displayed the CPU registers (and restored them before executing a command) and also supported an 'S' command which single-stepped through an assembly program, providing actually rather useful debugging features.
Within the monitor, you could then access the mini-assembler with the command "F666G" (nobody said these commands would be easy to remember). The "G" command tells the monitor to run the program located at the specified address, so "F666G" simply runs the program at that location, which happens to be the mini-assembler. If you loaded the mini-assembler from disk you instead would give the address of wherever in memory you put it. The //e and later allowed you to enter the mini-assembler with the simpler command "!" (which happened also to be the prompt character for it), which also allowed them to move the code.
The Mini-Assembler
While not technically part of BASIC, the Apple II family supported a mini-assembler, which was created by Steve Wozniak himself. The mini-assembler was included in the original Apple II ROM with Integer BASIC (supposedly, Woz wrote Integer BASIC itself using the mini-assembler). Systems with Applesoft in ROM lost the mini-assembler, but it later returned in the Enhanced //e, //c, //c+ and IIgs. Those computers without the mini-assembler in ROM could load it from disk or tape. All versions of the Apple II had a disassembler in ROM.
The mini-assembler provided a minimalistic interpretation of assembly language. While it supported all the instructions and addressing modes available on the installed CPU, it had few other features. Features not found in the mini-assembler but that were common in more full-featured assemblers included labels, code relocation, pseudo-opcodes, decimal numbers, embedded data, or much of anything else besides "[address] mnemonic operand". It did allow you to pass commands to the monitor, so you had not only assembler but memory poke/dump/disassembler/debugger available from the same command line. Errors would produce a beep and a printout of the input that was in error, and you exited by entering a simple blank line.
The Ampersand
Applesoft BASIC also had an unusual ampersand command which, while not originally intended for public consumption, eventually became a more efficient way of calling machine-language code than the usual DATA/POKE/CALL commands. The ampersand hook allowed the programmer to attach machine-language code to an Applesoft program, where it would be managed by Applesoft and loaded/saved along with the rest of the BASIC program. Data could be passed to the ampersand command directly from BASIC, and could not only assign variables, but even modify the language itself - adding additional flow control constructs, for example. While this was nice for the BASIC programmer it was more work for the assembly programmer. In this way the extra code functioned more like a language extension than a specific assembly-language routine, and the additional code didn't appear in the BASIC program listing. (While DATA statements containing assembly routines aren't particularly easy to understand, they're right there and you can at least see them).
USR()
USR() was like CALL, but it could pass and return a value (in the accumulator). Like CALL, additional data could be passed in free zero-page locations, the keyboard buffer, or some other transient storage. Unlike CALL, you could only have ONE routine attached to the USR hook, so it is really only useful in limited circumstances.
Commodore C128 (CBM BASIC 7.0)
BASIC 7.0, the vastly improved BASIC in 128 mode, has some features that simplify the process of combining BASIC and ML.
In addition to calling an ML routine, the SYS statement can also pass values from BASIC to ML. The values must be in the range 0-255 and are placed in the microprocessor's registers just before the ML routine takes over. Simply tack them onto the end of the SYS command, separated by commas. Conversely, the RREG command lets you read the processor's registers from BASIC after an ML routine has finished.
The BLOAD command can bring in any ML module with no fuss or bother. The file loads into the same memory area from which it was saved, and BASIC continues with the next command. This is much simpler than the gyrations required in earlier versions of Commodore BASIC.
(please add other Machines/Basics and their workings by editing this answer)