[0]Made an AppleTalk chat client/server https://github.com/kalleboo/GlobalTalk-Chat
[1]The equivalent to HeapWalker I used was Metroweks ZoneRanger which was bundled with their compiler. It has a nice visualization of how fragmented the memory is https://bitbang.social/@kalleboo/116302075194704555
The differences were (a) that DOS+Windows was designed so that the same programs could run in both real mode, with overlaying, and 286 protected mode, with segmented virtual memory; and (b) that to really save on RAM DOS+Windows had ideas such as the data segments for DLLs being globally shared across all processes. These added all of the complications mentioned in the headlined article and more besides. It was the operating system, not the processor architecture.
The 68k didn't come with an MMU like the 286 so MacOS couldn't rely on virtual memory like OS/2 did but at least the flat memory space meant you didn't have to juggle 64k segments
Not as much of a strait jacket as Windows segmented-memory programming, but compared to Unix, it did feel constricting.
It did. It was bi-modal. There were at one point switches to the WIN command to tell it whether to come up in real mode or 286 protected mode. In the latter it definitely did use the features of protected mode.
It was the bi-modal nature that was the problem. Essentially, they had to design a whole layer that simulated when in real mode all of the load-on-demand stuff that the processor architecture supplied for free in 286 protected mode, and make it so that the thing would all work either way with no changes to applications.
Windows 3.0’s WIN.COM supported:
/R for real mode (8086)
/S for standard mode (16-bit protected mode)
/E for 386 Enhanced Mode (32-bit virtual machine manager (VMM), running Windows in VM1, and DOS apps in VM2+)
Similarly, while locking and unlocking memory blocks is no longer generally a concern, most programs still deal with files, and graphics programs still have to call map/unmap functions to access graphics data. All the same tools apply -- helper functions/libraries, RAII, and leak/sanitizer tools to dynamically detect usage errors.
I think the knowledge of underlying hardware is useful and good to know.
But also that sort of knowledge got dated pretty quickly in the early computer era. Further, the capabilities of things like optimizing compilers quickly got to a point where they'd outpace most hand written assembly. Today, it's basically just floating point operations where you can still do better than a compiler.
In the early days, you'd have the correct impression that the C compilers spat out utter garbage which was a lot slower than what you could hand craft. As optimization techniques got better and better, the work you did because the compiler was dumb ultimately would have gotten in the way.
It biased your selection of data structures and algorithms.
Max 64KB array size meant pointers to allocated structs and linked lists were much more popular back then versus 1 large array of structs.
The Win16 HANDLE memory allocation also meant you had to worry about how you handle structs which had pointers to others structs (a FAR ptr may not be a stable value, unless you locked the HANDLE for the duration of the allocation)
Then you had to worry about stuff that no college programming book talked about (ignore the lack of error checking):
char FAR *p;
char FAR *mem = farmalloc(65536);
for (p = &mem[65535]; p >= &mem[0]; p--) {
dostuff(p);
}
Welcome to an infinite loop...Game consoles like NES, SNES and Game Boy had additional hardware built in the cartridge to support memory mapping/bank switching.
For PCs, EMS (memory) provided a similar concept. It reserved a 64 kB window divided in 16 kB pages in the first 1 MB and allowed to map up to 32 MB.
Even with 32bit systems where you’d want more than 4GB RAM, application software still had 32 bit addresses (and thus 4GB memory limit).
I think it was a lot more common for 8bit systems to allow for 16 bit addressing though.
It’s been a while though. So hopefully I’m not misremembering things.
The 6502 and Z80 could use 16 bit addressing to access up to 64kb of memory. The 6502 had various other addressing systems, including iirc 8 bits, but none of them were wider tha 16 bits.
Though in fairness, I do mostly now just use those systems to teach my kids BASIC
8-bit microprocessors used 16-bit addresses.
I wonder if it's just that kids today (gods that makes me sound old!) are constantly surrounded by entertaining things to do - gaming, TV/films, music, social media.
I just hope eventually he loves reading and learns in a more traditional way instead of from laptops and pads.
Some rules are obvious -- cutoff mobiles and pads completely (he doesn't have access to them so it's for me), sit in the library and study from books (I believe this is even possible for programming topics as I can write on paper). Basically, cutting off everything electronics definitely helps -- even putting my phone in the bag improves productivity significantly.
But the problem is, my son is unruly. If I put him in the library, most likely he runs around and messes things up, which ends up we leave early without doing anything.
Some potential ideas to explore. Take what you want, leave what you don't.
a) if you're training for attention span, make sure the target is appropriate and also within reach of your child.
b) have a plan for the visit: when I helped at a school library, classes for kids in your kid's age group would come in, the librarian would read them a story, then the kids would look for a book, check out at the desk and read (or look at the book anyway) quietly until the end of the visit. I think we'd get about 40 minutes for a visit. Most days, at least some of the kids would be getting ansy before it was time to go.
c) Plan around your kid's activity needs. Some kids will do long still antention tasks better after doing some amount of physical activity. Some kids will do these kinds of things better after a meal. Some will do it better in the morning or the afternoon. Many kids will have a harder time if the library visit was a surprise. You know your kid, try to have your library visits when they're likely to work well. If he likes story time, try to visit when there's a story time available.
d) don't expect that you can both go to the library and work independently. You're going to the library with him, and he's going to need you to help him out for much of the time. But you might be able to find him a book together, then find you a book together, then sit down and read for a bit together.
e) if all you can get done is finding a book, no big deal. You can read at home too.
If a lion can figure out how to behave in the library, so can your kid ;) https://www.michelleknudsen.com/library_lion_77788.htm
For me it is fascinating how today I can learn a foreign language, or how to code by interacting with the LLM.
In fact, I’d argue it was more fun than programming Javascript these days.
Petzold's Programming Windows book, for example, devoted an entire chapter (chapter 7) to memory management, with diagrams and examples. In the 2nd edition (which I just pulled off the shelf to check) that chapter runs to some 40 pages.
It's complicated and janky as all get-out, but it made more sense if you were coming from 8080/Z80 development, as this was a scheme to ensure some degree of compatibility with 16-bit 8080 addressing while providing access to much more memory. 8086 was not binary compatible with 8080, but was designed so that 8080 programs could be machine converted to 8086 ones.
In languages like C, this took the form of three different types of pointers: NEAR, FAR, and HUGE. NEAR pointers were 16-bit offsets only, and dereferenced with respect to the current segment (usually in DS). FAR pointers were full segment:offset pairs but pointer arithmetic was only done on the offset which meant objects could be 64K max. HUGE pointers allowed for objects larger than 64k but at a significant performance cost.
When I wrote a binary translator, I ended up having to keep a translated return stack to optimize RET opcodes. That put me in exactly the same position as the Win16 kernel with regard to having to patch pointers (in case of Win16, just the segment part) on stack.
Of course I did not have the benefit of my guests calling a lock function, so I ended up having to run a garbage collection operation to determine which pointers are in use & take exceptions on now-invalidated segments. Lots of extra work that Windows didn't need: it's nice to be king :-)
One day I was encouraged to write a Windows Sockets emulation layer for ordinary dial-up shell accounts like those offered by netcom. The idea was to allow the use of the recently released Mosaic browser without an actual internet connection. I figured sure, no problem. I'll use curl or some other tool in the shell account to do the actual fetching of URLs, transfer styles over zmodem, and simulate all the tcp/ip calls in the DLL.
I couldn't even get started. The reason is that I couldn't understand how the different Windows applications could all share memory allocated at runtime in the winsock.dll.
I asked a highly experienced ex Microsoft person, and he just said what are you talking about. There's no API to allocate shared memory.
So I gave up. 6 months later someone else did it.
Around then I realized the truth: Windows 3.1 had no memory protection at all. Specifically all global variables in DLLs were shared by default. The hard part wasn't sharing memory among users of a DLL. If anything, the hard part was having good discipline to avoid sharing it.
Since I'd only used multiuser Unix in school, and I knew Windows supported multitasking (even if only the cooperative kind), I just couldn't wrap my head around the idea that I'm multitasking operating system could exist without memory protection.
This was the magic moment for me, learning Windows 3.0 programming. The idea that my program is no longer master of it's world, but instead is just something that gets loaded and called by Windows.
https://news.ycombinator.com/item?id=48424862
I'll just stop posting on HN.
A submission to survive most likely needs some initial push from non-organic voting.
It probably helps if you share you submission early with your colleagues and in other sites.
Win16 programming was an important formative phase in my career. There is a lot of wisdom in old solutions to thorny problems and knowing them often clues you to how one may adapt them to today's problem. For example, when CPU+GPU programming appeared i immediately imagined CPU memory accessed with "near" pointers and GPU memory accessed with "far" pointers with a switch to a pseudo-segment register.
It also conditioned a programmer to learn about various complexities involved and be careful in their programming i.e. it taught you discipline. You understood your compiler, OS and hardware better and how to write code keeping them all in mind. For example, i often say my study of embedded programming started with Win16!
Another bit of cleverness was "Thunking" between 16-bit and 32-bit code. Here is Raymond Chen on how it worked there and Why can’t you thunk between 32-bit and 64-bit Windows? - https://devblogs.microsoft.com/oldnewthing/20081020-00/?p=20...