Maybe that is part of the nostalgia for the 8-bit days: the practice of "slow programming"? It was relaxing, sort of like those "slow tv" shows.
OT: others are the first times I drew graphics on the amber monitor of a PC clone and adding data to a BE database and then seeing said data on a webpage. I think my security was about the same as current-day Lovable :)
OOT: Thinking about it, I believe the pure wonder & joy came because it seemed like magic, but then you figured out how it worked which gave you an immense feeling of power and self-determination. I worry that with AI today you'll get the former and never the latter, which will have serious, long-term ramifications. I hope I'm wrong.
I figured out exactly what I wanted to do, wrote it out very carefully on paper, looked up all the hex codes, wrote those out very carefully, adjusted addresses etc. as needed. This took many hours spread over weeks. Then I entered the whole thing on the hex keyboard and display of a primitive development board each time I needed it, perhaps an hour or so each time (I think I only did it twice). It worked first time, because why wouldn't it if you were sufficiently meticulous about your work in a process that was very obviously very intolerant of the tiniest mistake?
I must admit my program (gather input information from some transducers, do some calculations, present some results) was likely somewhere between one and two orders of magnitude simpler than a complete Basic interpreter!
You had to carefully calculate the address distance for a branch instruction. The release of DISMON was a revolution: suddenly the computer did all of that for you. It even supported moving pieces of code to a different address to make space for inserting more instructions somewhere!
You would use the "monitor program" to poke hex data into memory locations, starting at a nominated RAM memory address. Needed to be done VERY CAREFULLY. one bad hex digit and your program likely crash. After entering all the program into memory, you would use a monitor program , command to get tell the CPU to jump to a certain address and start executing a program there. OLD computer magazines often had such hex listings printed in them.
* a single Z80 (which was nicer than 8080, because the latter needed an extra clock IC) * 256 bytes of static RAM * 1 8bit D-Latch (Flipflop) with 8 LEDs * 1 8bit D-Latch with 8 dip switches * 2 8bit buffers that can go tristrate between the hex-thingies and the address & data bus * 2 rotating hex thingies: you could rotate them between 0-f for data * 2 rotating hex ones for address
Now, how did it work? I pulled nBUSRQ down (bus request). This made the Z80 go tri-state. It also made my buffers non-tristate.
Then I used 2 of the hex-thingies to set the address and 2 of them to set the data. Now I set nWR to low and that would write the entered data byte into the SRAM.
When I was done, I turned nBUSRQ back to high, so the Z80 again was the boss of the bus. And then I simply used nRESET to low to make the Z80 execute my program starting at address 0.
I did only tiny programs, like adder, some PWN/sound wave program, an traffic light simulator.
But, since I soldered this all by myself, I had no "monitor program" like the KIM-1 had. So I had to do all of that by hardware.
(Note that while a lot of this was unjustifiable even then - my first real paying job in computers was down a step, working on a Z80-modded IMSAI so while most code was on 8" floppies, doing machine code in binary on toggle switches "just to fix something quick" was a legitimate skill :-)
With these three commands, and a working memory of the hex that corresponded to the opcodes, you could be reasonably productive writing short 6502 programs.
Fun and extremely niche fact: the typo CALL -161 jumped into the monitor, but only after the weird side effect of unhooking the sniffer that watched for DOS 3.3 commands sent via the ROM's text I/O. Not only did this make text print faster -- useful when you were visually dumping memory to the screen to find something quickly, but it also protected against accidentally destroying your disk if you unwittingly printed a sequence like ^M^DINIT HELLO^M, which would trigger the disk initialization routine.
Ancient assembly hacker tip: A JSR/RTS sequence can be simplified to a JMP. To continue your example, the bytes you provided decompile as follows (assuming Apple 2 monitor ROM at $F800):
PRINTBANG:
A9 21 LDA #$21
20 F0 FD JSR COUT1
60 RTS
So, you load the A register with #$21, which is the ASCII code for `!` in hexadecimal, and then call COUT1 which will output the character in the A register, printing the exclamation point. When COUT1 is finished, it will return to you using an RTS instruction, and then you'll return to your caller using another RTS instruction.Since the call to COUT1 is a tail call, you can borrow its RTS instruction to return to your caller and save one instruction. So the following code is equivalent to yours but a little bit smaller and faster:
PRINTBANG:
A9 21 LDA #$21
4C F0 FD JMP COUT1
Notice that the JSR instruction has become a JMP.Another related trick: When you have a routine that callers often want to call twice—say to print `!!` for extreme exclamations—you may be tempted to create a do-it-twice call:
PRINTBANG2:
20 00 03 JSR PRINTBANG
20 00 03 JSR PRINTBANG
60 RTS
But now we know better and code it this way: PRINTBANG2:
20 00 03 JSR PRINTBANG
4C 00 03 JMP PRINTBANG
But we can do even better! We can eliminate the jump and save three more bytes by placing our do-it-twice routine immediately before the routine it is calling: PRINTBANG2:
20 03 03 JSR PRINTBANG
PRINTBANG:
A9 21 LDA #$21
4C F0 FD JMP COUT1
When PRINTBANG2 is called, it calls PRINTBANG to print an exclamation point, but when PRINTBANG returns, it returns to PRINTBANG2, which promplty falls through to PRINTBANG, calling it again and printing a second exclamation point. This time, however, when PRINTBANG returns, it will return to the original caller.Back when every byte mattered, these tricks were useful to know.
I haven't seen that in a long time! Young me was too young to understand 2s complement arithmetic, or that addresses would wrap around. I just thought it was some kind of magic.
The mini-assembler didn't understand symbols (other than the opcodes themselves), but it did understand the addressing modes. This saved you from, for instance, hand calculating branch offsets.
Not all Apple II models included it, but I can't find a reference right now. Ours had it.
[0] https://www.callapple.org/vintage-apple-computers/apple-ii/a... (1978)
Monitor changes also included a return of the mini-assembler, absent since the days of Integer BASIC. It was activated by entering a “!” command in the Monitor, instead of a jump to a memory location as in the older Apple II. Features also added included the ability to enter ASCII characters directly into memory, and an “S” command to make it possible to search memory for a byte sequence. Interrupt handling was also improved. However, the “L” command to disassemble 6502 code still did not handle the new 65c02 opcodes as did the IIc disassembler.
Source: https://www.apple2history.org/history/ah07/#07:~:text=Monito...
You go to a start address, switch to data mode, and just start plugging in hex…
Some people did burn the code into electrically programmable PROMs completely by hand, using just a power supply, some wires and switches. Anyone could do this at home, but it was extremely tedious even for a tiny 256 byte chip.
With small stuff you could run the code and drop back out to the OS. Generally you'd load and run the binary to test it and then back to loading the editor and source code, edit that, reassemble. Repeat ad infinitum.
Much of the ZX81 and Spectrum code I wrote was initially done on pen & paper and developed like that.
When I got my first in-house job in 1984 they were using TRS80s for editing and assembling which also had a custom hardware link to transfer the binary to the Spectrum.
By the time we got onto Atari & Amigas there was much better custom hardware, such as SNASM, which also facilitated remote debugging from a PC.
I've been staring at, fixing up enough 6502 op-codes ands their machine code equivalents that I'm beginning to remember the common ones too.
I guess whiteboard interview technique came from there. Probably rational was - If they can do it, why can’t you?
You can take the instruction set of a simple microprocessor, and lay out its instruction set in a tabular format. This made it relatively easy to see the correspondence between an instruction in assembly language and its numerical code.
Handy "reference cards" were published for looking up the codes, printed on nice thick plastic. You can find these in a google image search for your favorite micro. I got by with a photocopy. Along with that, a programmer would have their own notes and diagrams taped to the walls for quick reference. Machines were simple enough that you really could have all of the vital information available at a glance.
Today, the "datasheet" for a sophisticated 32 bit microcontroller is many thousands of pages.
Microprocessors had relatively few transistors by today's standards, so the instruction decode logic had to be simple. Logical, if I may say so. This resulted in being able to describe operators and their operands as blocks of bits in an instruction word.
I arrived late enough in the game that personal computers already existed, yet I got myself started by hand assembling for the microprocessor circuits that I brought up from scratch.
1) In the Saturday morning programming class that I attended just to get the computer time, an experienced student had a card deck and a brand new student asked to see it, and the former unfortunately agreed. The new student had no idea that the order mattered and scattered them all over his desk.
2) I rode a Honda 90 over Beverly Glen to attend UCLA. One morning I had a 300 card PL/1 program bungied onto the back of the motorcycle. The car in front of me slammed on its brakes when the signal turned at Beverly Glen and Sunset, I rear-ended them, and my deck went flying all over the road.
And that $200 being out of bounds went pretty low: for the first couple years of my programming life, floppy disks were beyond my means, so any code I wrote lived in its official version as hand-written code and would have to be retyped any time I wanted it.
Like blowing into a gourd is still available to a modern saxophonist.
But I think I'll stick to my LLMs and the fond memories of spending a few evenings sussing out resolute performance from an RP2040 PIO's whole-several assembly instructions. :)
He wasn't happy about the position he was in while we were chatting, except it paid some bills and it provided good health insurance.
For comparison, I never learned any 6502.
But we both answered the phones the same way in that call center, and both we worked to give callers the best troubleshooting advice we could, given our understanding and ability to control that system.
Anyway, my career in writing 6502 machine code in pencil pretty much ended at "Hello World".
Apple 1 users had a keyboard. But its developers may have been using a hex keypad, or an array of toggle switches.
You could go a long way with mov/jz/jnz etc and basic bios calls
I was a little older than 4, but I learned to transcribe random snippets from the programming manual into the computer about the time I learned to read; and later I'd program (Pascal, not assembly) by hand writing when in school and then run home and try it out. What else were you going to do?
As near as I can tell, the assembly is not formatted in a way that would actually assemble? Furthermore, there are missing labels, errors… My understanding is that the assembly is there more or less as documentation for the machine code—it was the hex that the person purchasing the book was expected to enter into KIM.
[1] https://archive.org/details/The_First_Book_of_KIM
A new game for the fifty-year-old KIM-1: https://engineersneedart.com/blog/yahtc/yahtc.html
Vimes boots theory exemplified!
https://nemanjatrifunovic.substack.com/p/pdp-10-and-microcom...
10 FOR N=0 TO 28
20 READ B: POKE 32768+N,B
30 NEXT N
40 RANDOMIZE USR 32768
50 DATA 62,2,205,1,22,17,16,128
60 DATA 1,13,0,205,60,32,24,245
70 DATA 73,32,65,77,32,65,87,69
80 DATA 83,79,77,69,13Of course more recently I've been doing a lot of z80-based CP/M retro-fun. But "62, xx" is obviously LD A,xx, and similarly the 205 is CD which is CALL.
Fun stuf, thanks for the post, you are indeed AWESOME.
I could have sworn the ROM spelled it RANDOMISE with the UK spelling though.
Kids these days don't know how good they have it, even when playing around with an ESP32.
The first time was in my first job writing OpenGL ES 2.0 blending operations for the Imagination Technologies SGX 535, which was a remarkably advanced little GPU for its time. Alpha blending happened through special instructions in the fragment/pixel shader. We did have a compiler, but it didn't handle blending, for whatever reason.
Through testing I noticed that NVidia GPUs performed blending incorrectly, according to the specification, which was a big surprise. Only years later I figured out that the specification had some errors; I now would bet that NVidia was doing the correct thing.
The second time I had to assemble instructions by hand happened while working on a stripped down version of the Radeon 7xxx series that shipped on some mobile phones. I don't recall what the problem was, whether we were adding a feature or optimizing the microcode generated for a particular OpenGL operation, but it involved a rarely-used feature called user-defined clipping planes. We didn't have an assembler for that GPU; all microcode was assembled by hand.
The third time happened at NVidia, and I will be vague, as not enough time has passed for me to be comfortable going into any detail. I had an idea for how to provide hardware acceleration for persistent CUDA kernels, which eventually was marketed as Cluster Launch Control (CLC). In order to quantify the benefit of adding some hardware, when it was possible to approximate that functionality in software, I needed to test the most efficient software implementation that was possible, and the compiler wasn't quite giving the assembly I wanted, so I had to manually patch some instructions post-compilation. Why did I not use the assembler that was available? I couldn't, because the assembly language did not provide a way to express the exact feature I had to patch.
First for new hardware would be a monitor program. Either write one, or port one from other hardware. A monitor program allows the inspection and edit of memory data and also to run programs in such memory. The monitor program often placed in ROM for convenance. A 6502 cross assembler, could be used to avoid the need for hand assembly the program to 6502 machine code.
Probably where WOZMON came from.
Then using WOZMON start writing a VERY simple basic interpreter.
Maybe the monitor program was borrowed from other hardware that used a 6502 ( would need slight mods for the Apple 1 ) NOTE This is just a possibility - I actually have NO idea I suspect WOZ would have invented his own in any case ...
(OFF TOPIC - I messed around with a 68008 chip in mid 1980s?? and I recall I found a book with a complete montor program in for this chip - I can not recall the name of the book. )
Just design and build a virtual CPU architecture in the language of your choice:) (not verilog but php, c, rust, zig or whatever)
You create architecture, then OPCODES, then you can write simple programs in machine code.
Then its SUPER EASY to write an assembler and disassembler.
Then forth compiler.
I did it all once as I was homeless on my phone in the mall .
Fun and educational, even if you don't know assembly prior.
Any book or resource? How do you do this without assembly?
I assume he wrote BASIC first to make sure he knew the computer he was building would be capable of running BASIC. And also not too capable, since the goal was a cheap computer capable of running BASIC.
But, he did write a simple monitor (and burn it into PROM) before actually running BASIC. Just 256 bytes: No disassembler, just the ability to read/modify and execute hex bytes. Before Woz created the tape interface, you had to type BAISC into the monitor as hex every time you powered up the computer.
Time travel!
However, there was an interesting period of a few years where assembler tools were either unavailable or fairly expensive, so it was common to use the computer's BASIC to write an assembly language monitor program. Such program listings were pretty common in early hobbyist 'zines. It was also pretty common to use BASIC to POKE in a quick bootstrap program to load a value in a register and jump to an address, which I could usually do from memory. After 40 years I still remember the 6809 RTS was $39 and NOP was $12, two very handy instructions to POKE a quick mod into a program.
Since I was entirely self-taught on that first computer with zero prior education and no books other than the included manuals and a 6809 CPU reference card a very kind sales rep at Motorola mailed to me for free, I learned hex representation that way. It actually still seems a bit weird when I see other hex notations. :-)
I still have it in a box, as well as a working sample of the computer in my collection.
I wish I couldn't, and could remember useful stuff instead.
The handwritten interpreter is fairly common knowledge since Woz has told that story many times, but I don't recall any accounts of how he bridged the gap from paper to silicon. All of the discussion about the machine language monitor doesn't really address the question. At best, it moves the question from programming a ROM for BASIC to programming a ROM with the machine language monitor. Even with the monitor, additional hardware would be required to program a ROM. At the very least, a latch would be required to hold the address and data. Of course, it is possible that he did it without the computer (using a rig that let him "toggle" the pins). It is also possible that he used another computer to program the ROM, but, as he said, he didn't have the money to program BASIC on a computer so why would that be different for a ROM?
See: https://retrocomputing.stackexchange.com/questions/18321/how...
He used equipment at his day job at HP
Edit: Actually, I think the Apple I only had the monitor in ROM; Basic was typed in using the monitor, and then saved/loaded using cassette. Apple II put Basic in ROM; the shipping version used mask ROM; maybe they used PROMs in development, although at the time PROMs generally had half the capacity of mask ROMs.
I got a letter from one of them asking if that was OK -- it was just signed Steve."
They didn't bother Competing with Apple for nearly two decades, and once they entered the market, they were beating Apple about a year and a half after launch.
Loading Integer BASIC from cassette was a thing, but that was after the autostart ROM was introduced since AppleSoft and Integer BASIC were not compatible.
The source (assembly) code for Woz's SWEET16 is in the Apple II Reference Manual. I learned a lot by working through it back in the day.
The 'anlaytical engine' was partialy built, after a successfull smaller prototype, of which some of the original equipment exists, AND a full version has been built now, on the original plans, and after some refinement, works.
Dont know if the Lovelace algorythim has been coded and run through it, but we can be sure that they were on the right track, but operating in close to an absolute vacume, and would have had to fend off acusations of witch craft, idolitry, and the risk of bieng burnt at the stake, or worse.There were even pre-made paper forms for writing code, like the IBM COBOL program sheets and similar for assembler.
Even now, if I’m working on a tough issue, I still print out code sections on paper and hash it out with a pencil.
I will never forget the thrill of keying in a moderately-sized program I'd hand-coded -- a few hundred lines probably -- running it, fixing one or two typos, and then having the program just work. I'm sure they had bugs, but the fact was all that hand-coding effort paid off as programs that ran successfully on the first try.
Real story: sometime in 80s I was in bed with a nasty flu. Pain-in-the-eyeballs flu, 40°C body temp. Could not read, coult not watch TV, could not stand any light lit near me, radio caused booming in me poor head.
So I started programming, mentally, in BASIC. One of the outcomes of this activity was an algorithm that compressed any text to mere three bytes, but that is another story...
Maybe it was like Checksum, MD5 or SHA.
'ead. Northern much?
I remember that was a book that went on to say that you should learn machine code (that is, raw opcodes), not assembly, to program the c-64. Not sure I would agree, but it’s definitely feasible.
The only thing that is strange in the Woz story is that you would usually start by creating a dirt-poor monitor/assembler so that you would have a sane environment to do further development in. That was the approach manuals from the era suggested for new hardware (eg Rodnay Zack’s)
He wrote a monitor as well, but the premise here is that he wrote the BASIC interpreter before there was a computer at all, so a monitor wouldn't have helped.
And we had/have people at Cloudflare hand writing assembly to get specific performance out of our systems.
Having established my chops though, I'll say that contrary to the comments on this thread that's not "how people did it back then". The typical tool chain for professionals was to run an assembler (and often an emulator too) on some larger computer such as a pdp-11 or a dec-10. Anyone interested in this particular period should read billg's latest book "Source Code". It's very detailed on this subject and tells how he and Paul Allen were able to write an 8080 BASIC interpreter despite never having seen or used an 8080. He does describe writing portions of what was to become the interpreter while on a hiking trip (as in, while actually hiking), so he could do my thing of writing code in his head.
https://www.folklore.org/MacBasic.html