Ha. kid's stuff. I started with punching machine codes straight into memory
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.
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
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.
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.
When people talked about portable before, they meant code that used an abstraction that was platform agnostic. And that’s how it’s still used today. It’s just we have better abstractions now so our expectations of what is “portable” have gotten stricter.
Eg the P in POSIX (which is nearly 40 years old now) is “portable”. The point of POSIX was to provide common abstractions that one could build against to run on multiple different operating systems. It wasn’t about porting software, it was about preventing people from needing to constantly write platform-specific ports.
This is somewhat outdated information. POSIX.1-2001 added "posix_openpt" to create a pseudoterminal, and most POSIX implementations now support it–at least Linux, macOS, FreeBSD, NetBSD, OpenBSD, Solaris, z/OS, AIX, HP-UX, QNX, Minix and Cygwin do. (Of course, that's only true of current versions, if you go back a decade or more you'll find many of them hadn't implemented it yet.)
Saying something “can” be ported doesn’t make it portable. People would port games from the NES to the Master System, to 8 bit Micros but in most cases they were effectively complete rewrites because there wasn’t any common abstractions between those platforms.
Where as tools like POSIX provided abstractions to make code portable.
As I said before, the only reason you think the term has changed over the years is because the abstractions have gotten better and thus people’s expectations for how much effort should be required to port have gotten stricter. But that doesn’t mean the term means something totally new.
And by the way, I have authored portable terminal emulators and $SHELLs. ;)
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
The computer generates a random number between 1 and 100. And you have to guess. When you’re too high, the computer says “too high” and likewise when you’re too low.
It’s a great starter program because it teaches you strings (the output printed), integers, comparisons, conditionals, and iteration (you keep guessing until you get it right). And the whole thing only take around 20 lines (give or take).
Then the kids plays a few games of that.
And after the novelty of that game wears off, I tell them to customise it however they want. Eg different messages, different ranges to guess from, etc.
It’s the same way I teach Python to primary school / kindergarten kids.
The nice things about this is even if the kids don’t learn and remember the basic primitives, they still get a feel of “proper” coding in the same way that we did when we grew up. And they still get something they can play, even if the game itself is super basic (no pun intended).
I’m not saying this will work with every child, though. All kids are different. But it’s been super successful both at home and in the schools I’ve helped out at.
Do you have any blog, or any recommendation of books to read for such topics? Maybe I can find some "Programming for kids" book back from the 90s. I find teaching kids in general very hard, much harder than teaching myself because kids don't have the cognitive capacity as an adult.
https://www.abebooks.com/Absolute-Beginners-Guide-Qbasic-Per...
Also the Usborne series are classics and a lot of them have been made freely available:
Maybe I'll just leverage my programming experience and build something using online tutorials and a Dosbian box.
It wasn’t something I specifically planned in advance for. Which meant though whole experience was less like homework for them.
As I’m sure you know, sometimes kids just want to experience the stuff their parents do. And when they’re in that kind of mood it’s a lot easier to sit that at a computer than it would be normally.
Many 6502 and Z80 systems used bank switching to support more than 64KB of memory. That way you could have 128KB or more physical RAM in a machine with an only 16-bit address bus. MP/M–the multitasking/multiuser version of CP/M–had support for this as a standard feature, since it was hard to fit multiple processes/users into only 64KB; it was ported to the single-user/single-tasking version in CP/M 3.0
Essentially, this was doing virtual memory, not inside the CPU, but in one or more external chips. Actually, back in the 70s and 80s, it wasn't uncommon for a memory management unit (MMU) to be a separate chip (or even PCB) rather than an integrated part of the CPU–for many systems it was an optional add-on if you needed more memory, or wanted to run more advanced operating systems.
8-bit microprocessors used 16-bit addresses.
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.
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.
For me it is fascinating how today I can learn a foreign language, or how to code by interacting with the LLM.
What all of these languages have in common is that you can write meaningful programs entirely without pointers or manual memory management. In particular, all of these languages handle strings in a natural, high-level way (treating them as a value) and don't require you to allocate and free buffers for them. Perl goes a step further with arrays and hashmaps and employs a full garbage collector.
I have vague memories of trying C for the first time and getting completely lost and bogged down by all the pointers and memory management. My reaction was the same as yours: how does anyone program in this. Why bother with this complexity when you can just use Pascal where you simply don't have to.
Of course, the Pascal compiler was likely written in C or assembly and all the memory management still had to happen even if it was hidden away from me. To some people, this might mean that I “lost” something, but to me, it meant greater freedom as I was able to explore the world of higher-level programming which I found interesting, and not have to bother with the low-level details which I found tedious and even infuriating.
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... char FAR *p;
char FAR *mem = farmalloc(65536);
for (p = &mem[65535]; p >= &mem[0]; p--) {
dostuff(p);
}
Nice one.To be fair to Windows, good C courses should still teach this, but I'm not sure if they do :-)
It's UB to set a pointer to before the first element of an array, or after the last element plus one. So, if it knows the call to farmalloc/malloc returns the start of an object, a modern C compiler on a modern architecture may, in principle, optimise the above to an infinite loop.
I've seen something similar on architectures (long ago) where a zero-bit-pattern pointer was a valid memory address you might actually access. Of course p-1 is not less than p when p is zero.
The above example would cause an infinite loop on Win16's seg:off far memory model, but compiling on Win32 would not cause an infinite loop.
Problem is that far pointers only affect the offset, not the segment. So decrementing a 0 value offset would just wrap around to 0xFFFF and the segment would stay the same, so you're going from mem[0] to mem[65535] not mem[-1].
Although the code worked on Win32, and works on most modern C compilers, it's not guaranteed to work on modern C compilers, especially with aggressive optimisation turned on.
I'm explaining why the infinite loop actually occurs for those who haven't encountered the problem.
The problem would happen for an array whose beginning element starts at offset 0 for a particular segment and an iteration stop condition that uses ">= 0th element" that scans down the array. I used a 64K allocated array to ensure that the array base would match offset 0.
Problem would also occur if the end of the array aligns with the segment limit and the iteration end condition was "<= end element" and the scan moves up the array.
For either situation, the array could be < 64KB. One byte would be sufficient.
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.