There's not single answer here because you have a number of compromises involved.
Calling Conventions
For example, in assembly language you get (roughly) three choices about how to pass parameters to functions. One is that you pass parameters on the stack, about like a high level language would. Another is that you specify common register usage throughout the project. A third is that you attempt to pass parameters in registers that make sense for the specific function at hand. Yet a fourth is (more or less) a combination of 2 and 3: specify common register usage on a per-type basis; for example, pass the first two pointers in SI and DI, and the first two integers in AX and DX.
A stack-based convention tends to improve productivity--f(a, b)
will always be equivalent to something like:
push a
push b
call f
If you use a completely common register-based convention, it's almost as easy--something like:
mov ax, a
mov bx, b
call f
...but a function-specific convention means you'll need to look up f
and see what registers it expects a
and b
to be passed in. A type-based system is a little easier, since you undoubtedly already know the types of a
and b
, and get accustomed to the convention--but it still requires a little more thought than a completely common convention (especially in assembly language, where types are often rather loosely defined).
If you care purely about productivity, the winner is almost certainly a stack-based convention. But that also reduces the code's efficiency--especially on mid-1980's processors, that didn't have a cache so every memory reference went straight to main memory.
For what it's worth: MS-DOS used a common, register-based convention throughout. Windows used a stack-based convention from the beginning.
Hardware Manipulation
In theory, Windows 1.0 was a layer on top of MS-DOS, so all hardware manipulation should theoretically have gone through DOS (or at least the BIOS). But (for one obvious example) DOS provided no access for drawing graphics at all, and although the BIOS did provide minimal support, it was grossly inadequate for Windows' needs.
At the time, graphics was mostly EGA and VGA, which had designs that were somewhat non-trivial to deal with in assembly language, but substantially more difficult in higher level languages.
At the same time, a large part of the idea of anything like Windows is for most of the code to deal with some higher level abstractions, so code that's specific to a particular piece of hardware is isolated in its own little module. So even if you used assembly language inside that module, it would be fairly easy to write the rest of the code in a higher level language.
Tools
People who previously worked in mainframe environments (especially IBMs) often overestimate the sophistication of the tools Microsoft was using in the Windows 1.0 timeframe. For an obvious example, @Raffzahn's answer assumes an assembler that supports block-structured @if
statements. Microsoft started to support that in MASM 6.0, (or maybe 6.1--my memory's a bit fuzzy), which didn't become available until around the Windows 3.x timeframe--well after Windows 1.0 was obsolete.
At least to my knowledge, the source code to Windows 1.x has never been released--but source to Windows NT 4 and Win2K were both leaked decades ago, and to support running 16-bit Windows code both of those included the full source to the Windows 3.1 kernel.exe (among other things). I never looked at it personally, but from what I recall of what I've read about it, it appears to have used source code for something like MASM 4.
Optimization
Assembly language on an x86 has almost uniquely poor productivity because it has almost no such thing as a truly general purpose register. On an x86, you frequently go through some fairly nasty contortions to avoid register spillage (which tended to be quite expensive at the time).
Especially in the Windows 1.0 development time-frame (early to mid-1980's) you had to pay pretty close attention to optimization. Windows 1.0 ran entirely in real mode, so it had only 640K of RAM available. As it was, Windows itself occupied around 430K (going from memory, so that could be a little off, but not drastically). As such, Windows 1 left only around 200K of RAM for your executables to use. That made it a lot more of proof of concept than a useful tool.
Without nearly a lot of care in optimization, it probably wouldn't have even qualified as a proof of concept. For one example of careful optimization, look through the code for BitBlt
. They basically created a small virtual machine to do bit blitting. When you call BitBlt
, it compiles a small program for that VM on the stack, then executes it. Initially it can seem pretty weird, but once you figure out what's going on, you start to realize that it's very nice code--and very fast at what it does.
I'm honestly a bit uncertain how much difference language makes in this respect. As a really general rule of thumb, higher level languages tend to make it easier to do algorithmic improvements, while lower level languages make it easier to get fairly low-level improvements (like using registers better, so you do fewer references to main memory).
My immediate guess is that this probably favored assembly language overall. Under the circumstances, they probably cared more about memory usage than speed, and that tends to be easier to optimize with assembly language (at least in my experience).