Apologies for the long wall of text.
In that case: are there any modren microprocessors that have on-board ADCs that I might look at [... and] code in assembly language
I started with Atmel AVRs, specifically the ones with native USB 1.1 interface: AT90USB1286 and ATmega32U4 in particular. AVR assembly is easy, and you have plenty of registers (which is the main difference to x86 instruction sets). At that time, Atmel wasn't part of Microchip, so I used
avr-gcc and
avr-libc; these are an excellent starting point since
avr-libc header files describe the registers and its
crt (C runtime) files contain the assembly needed to initialize the microcontroller (including copying initialized variables from Flash to RAM), and
avr-gcc contains an assembler (GNU
as for AVR). You can basically follow standard
avr-gcc programming procedures, but just feed assembly source
.s files to
gcc.
ARMv7e-m architecture (ARM Cortex-M0/M3/M4/M7) as used on many microcontrollers is not that different, really, except that one must understand that they have two instruction set modes: A32 (
.arm assembly directive), and T32 (
.thumb assembly directive), T32/Thumb yielding much more compact code. Both can be used in the same assembly source file when the Unified Assembly Language is used (which is what
arm-gcc and GNU
as for ARM use). When you understand that, you won't get confused looking at the
ARMv7-M architecture Reference Manual and the instruction sets' documentation.
The problem with Cortex-M microcontrollers is that most have a relatively complex/large bootloader, not only handling the Flash-to-SRAM setup stuff, but also an option to load new firmware via USB (which many have as a built-in peripheral). One interesting exception is the Teensy 4.x series based on NXP i.MX RT1062 (512k+512k of RAM, runs at 600 MHz), as the proprietary bootloader chip (a separate preprogrammed microcontroller you can buy from pjrc.com also), because the separate bootloader chip handles new firmware uploads by copying the necessary firmware to RAM via JTAG, so your own firmware only needs to handle the bootup and runtime preparation stuff, which is open source code at
Paul Stoffregen's github. I use these extensively as ad-hoc tools (because the USB interface can sustain 25+ Mbytes/sec to host, or from host to Teensy — over 200 Mbits/s — using standard USB serial drivers and simple code written in Arduino environment in C/C++) because of their sheer computing and I/O power and development simplicity. An assembly loop toggling specific output pins in the same bank can reach about 180 MHz frequency, if I recall correctly...
Even though I've always programmed these mostly in C/C++, there are still places like low-latency interrupts with simple tasks, like updating a single variable, where assembly can make a big difference (for example, by using minimal registers). One can even tell GCC to not use specific registers at all (for AVR and ARMv7-M), so one can reserve those for use in the minimal-latency interrupts.
My own preferred development toolchain is based on plain
Makefiles (and GNU
make) and a
gcc- and
binutils-based toolchain. I do have written an occasional
GDB plugin for examining memory structures easier (and there is a Teensy GDB proxy you can use to do that in real time), my bugs tend to nowadays be either trivial (off by ones, typos) or complicated interactions between different parts of the code, so I rarely use a debugger anymore. Even for the code editor, syntax highlighting tends to suffice, although for larger projects split over many files I will reach for an editor that can track C/C++ keywords across files.
All that said, while I know all of the above works in Windows also, I haven't used Windows in a couple of decades, so cannot offer any detailed advice there. In particular, I'm not sure which exact installers to use. I do know that if you used a Teensy, installed
Arduino 2.3.0 or newer, and latest
Teensyduino, you'd get a full tested working toolchain installed, that would work perfectly for purely assembly code also, when compiled using GCC (which simply calls its companion
as assembler with all the correct command-line options).
If you do tend towards Teensy 4.0 (which are designed by
PJRC.com but manufactured nowadays by
Sparkfun), I do recommend you get two of them, or perhaps a 4.0 and a 4.1, with two 8 MB PSRAMs and the Ethernet Kit for the 4.1. They have the exact same core, just expose different peripherals, so if you encounter an oddity, you can verify it happens with the other one also and thus exclude hardware issues in one unit only.
I prefer microcontrollers with native USB interfaces and not USB-to-Serial bridges (like on most Arduino boards), because my very first microcontroller project was an arcade plank (using AT90USB1286, Teensy 2.0++) with a proper arcade microswitch joystick and buttons, so I could play online Flash games. It had a 16-position absolute encoder connected to four input pins, so I could select the layout at run time. You see, most online games use keyboard keys for controls, so this one behaved as one, and the layout just selected between predefined sets. Because the USB interface is native, I could use USB HID protocol, and not need any drivers on any operating system, just plug it in and go. (Later on, when USB 3 came along, some USB 3 host chipsets have issues with USB 1.1 Full Speed devices, and need an USB 2.0 hub in between to work properly. This just in case you play with AVRs or other microcontrollers with USB 1.1 support: an USB 2.0 hub in between almost always solves the compatibility issues.)
ATmega32U4 is still widely used for custom keyboard projects.
Another microcontroller family you might have much fun with would be WCH CH32X033 and CH32X035 RISC-V (QingKe core), with native USB, working on either 5V or 3.3V supply (and corresponding CMOS logic), and only need a single supply decoupling cap, no crystal or anything (see
this OSHW CH32X035F7P6 example dev board). Several CH32X035 models have USB PD capability (see e.g.
this OSHW CH32X035 USB PD tester), making it particularly interesting for USB-C PD powered projects. In Windows, you do need to install the
CH372 driver for WCH microcontrollers. There is an
Arduino CH32X035 core for it, which includes everything needed to bring it up including the C runtime (
crt files). I do believe the RISC-V instruction set itself is very well designed and nice to use, just haven't used it in assembly in enough anger yet to be absolutely certain.
If you want to make your own boards at JLCPCB/PCBWay/others, I'd say CH32X035 are the way to go. These even have the bootloader in ROM, triggered by pulling UDP (USB+) to VCC when powering it, which allows uploading a new firmware with just a simple Python script; and the
$8 WCH-LinkE from official WCH AliExpress store can be used with the two-wire debug interface (SWCLK and SWDIO) for debugging and firmware uploads. Again, the toolchain would be based on GNU GCC, just with the RISC-V target this time. Feed
gcc with assembly source files, and it will call
as with all the necessary command line options to build the binaries (in ELF object file format) correctly.