Proposed opcode addition to µKenbak-1 instruction set, plus ideas about extending the instruction space

85 views
Skip to first unread message

Tom C

unread,
Jun 22, 2019, 11:19:39 PM6/22/19
to uken...@googlegroups.com
Edit: 6/25/19:

It turns out my CMPA proposal is not possible, as the Kenbak doesn't use status register bits for doing conditional jumps.  Instead, it actually tests the state of the register (so a "jump if A is equal" literally means jump if A == 0).  Since the whole point of the CMPA was avoid modifying A, that doesn't work.  I'm going to leave the orignal post here (but turn it into italics), to avoid confusing people who read the thread fromt he top.

This may not fly well for Kenbak purists, but I would like to propose an additional opcode for the µKenbak-1 emulator.

Currently the opcodes 031x, where x is a memory address type 3-7, is treated as a two-byte NOP.

In the same way that 0360 has been used for the SYStem call, I propose that 031x be treated as a CMPA instruction, something that was not provided in the original instruction set.  It is unfortunate that it wasn't, since SUBA used the opcode 01x , and CMPA is just a SUBA without storing the result.  With the middle digit the same (1), I don't know why Mr. Blankenbaker left that as a NOP; the decoding couldn't have been that hard.  Any existing 031x opcode can be replaced by two 0200 bytes.

The advantage of having a CMPA instruction is obvious, it allows one to compare the value in A with something without destroying it (requiring another instruction to reload it).  With the limited memory in the µKenbak-1, saving two bytes here and there can make a difference.

Obviously, if one had a "real" Kenbak-1 (as I once did), this could have been accomplished by modifying the logic using the available schematics, since the CPU was implemented in discrete 7400 ICs.  In fact I modified my machine to add an interrupt capability so I could efficiently read paper tape from an ASR-33 Teletype (I later removed the modification before selling it).

To be completely compatible with the original Kenbak-1, this new feature (CMPA opcode) could be enabled by setting a second flag (b1) in the RTC flags register, 010, to 1.

End of abandoned proposal.  The remaining proposal, to extend the memory 10 1K, is still active:

Another, much bigger addition I've thought about would be the ability to extend the Kenbak-1's program memory to 1024 bytes (1 KB), in a way that doesn't impact current programs.   Looking a the current emulator code, it appears there is a little over 4KB of program space, and 1200 bytes of RAM available, so this would be doable.

It turns out the unconditional branch instruction  (jmp in my instruction set, or JMP DIRECT in the original) only requires the lower three bits to be in the range 3-7, but otherwise they are not used.  If one always sets bit 2, then one could use the last two bits as a page #.  The current opcode for jmp (0344 in my assembler) would still go to the base page 0.  But opcodes 0345, 0346, and 0347 would go to pages 1 (address 0400-0777), 2 (address 01000-1377), or 3 (addresses (01400-01777).  Opcodes of 0343 could be converted to 0344 in the emulator, to make them work with page 0 by default.

Any data references in instructions on the other pages would still go to the base page, so all data would be placed in page 0.  The only exception, is that indirect jumps would go to the current page, to allow for subroutine returns to calls in the same page.

This would only work for unconditional jumps.  Unconditional calls would always go to the current page (otherwise we would have to allocate two bytes for the return address, getting too messy).  Not as nice as being able to call from one page to another, but that would be a big change and impact all existing code.  If one is currently using 0344 for unconditional jumps, existing code is unaffected.

So one could continue a long program into page 1, then continue it again into page 2 etc.

Since all data references would still go to page 0, there would be no special locations (000-003, 0200-0203, and 0377) in the extended memory, so programs could use all of the space (768 bytes in three pages).

The bits for the additional two pages could be kept in bits b7 and b6 of location 0203 (X status register).

Once again, to be completely compatible with the original Kenbak-1, this new feature could be enabled/disabled using another flag (b2) in the RTC flags register, 010, so if disabled (0), jumps using 0345-0347 would always stay on page 0.

I would support both of these changes in my assembler, adding a cmpa opcode, and also jmp1 (0345), jmp2 (0346), and jmp3 (0347) opcaodes, which would be automatically generated when a user coded a jump to a label in another page.  The existing jmp instruction would be left as is (0344) for page 0.

Tom

Mark Wilson

unread,
Jun 23, 2019, 4:06:14 PM6/23/19
to Altair-Duino
I am personally less keen on these changes. 

I added the current SYSX operations as a pragmatic way of giving KENBAK code access to the ecosystem it's being emulated in. They also made it easier to for me to write non-trivial (although still essentially "toy") programs by providing access to a clock, a source of random numbers etc.
But it was still KENBAK code, with the limitations and challenges that implies.

I am soon to add additional operations to give read/write access to EEPROM. That's perhaps a stretch, but is pragmatic, useful and simple and fits within the existing KENBAK-SYSX split.

To me, adding additional op-codes and extending the address space feels like a step too far.  
I'm also unsure of the motivation for the changes.  We are emulating the KENBAK-1.

But I'm open to the changes if the community is keen, if not, perhaps the answer is that you fork the sketch repository, call it KENBAK-2?

Mark

Tom C

unread,
Jun 23, 2019, 4:55:41 PM6/23/19
to uken...@googlegroups.com
Mark,

Thanks for your comments.

I proposed these changes, because I think they would have been the kind of changes that one could have made on the original Kenbak-1, by adding additional 1024-bit shift registers and modifying the logic a little, since the CPU was discrete 7400-series logic.  As I've already written, I modified my original Kenbak-1 back in the 1970's to add an interrupt capability (it stored the current PC in location 004 since I could easily copy it from 003 and then it continued execution from 005).  It wasn't too hard to modify Mr. Blankenbaker's circuit since the schematics and state diagrams were part of the documentation.

I had just gotten my BSEE degree two years prior, and was doing logic design full-time during the day, so it was a fun project. I used a perfboard for the extra circuitry.  I remember having to make only one cut on the main circuit board, everything else just attached to existing pads.  I removed the board and repaired the cut trace before selling it several years ago. 

I bought the Kenbak-1 in 1971 because I had just started going to night school for my MSCS degree, and wanted a machine to write assembly code on that was not as initimidating as the UNIVAC 1108 mainframe the school made available.  I really liked all the addressing modes of the Kenbak.

I also thought about adding more memory a couple years later, but by that time the Sphere-1 (with a 6800 and 20KB of RAM) came out (in 1975) and I got and built one of them instead (it was a kit).  Since the 6800 had an A, B and X register, it felt right at home -- was just missing indirect addressing.

I don't know why there wasn't a CMPA instruction in the original Kenbak instruction set.  Certainly an easy modification (do a SUBA and just don't store the result).

So in my opnion, the kinds of changes I'm proposing are very much in line with the original machine's design philosophy.  Existing code will continue to run unchanged (assuming the use of 0344 for unconditional jumps, and only using 1 byte (0200) nop's.  If one modified all 0343 opcodes in the emulator by adding one before operating unconditional jumps, then they would be compatible too. 

If you're not interested in going this direction, then I will probably give it a go.  I will of course leave your name as the primary (like 99%) author.

Thanks,
Tom

Mark Wilson

unread,
Jun 23, 2019, 5:04:25 PM6/23/19
to Altair-Duino
OK, well I may be more keen when I have the current changes off my plate and after seeing where your exploration of the required code changes goes.

M

Frank P.

unread,
Jun 23, 2019, 5:13:21 PM6/23/19
to Altair-Duino
I kind of agree about keeping everything firmware specific and non-Kenbak isolated under SYSX. I understand about being able to make hardware changes to the Kenbak-1, but that sort of opens the door to, well, how should we put it... everything and anything. because that's what you could do to a Kenbak-1 if you wanted to take it in all sorts of directions. I could say, lets add an XOR instruction, but I sort of had fun writing my own XOR with what I had, and trying to get it down to the minimum number of bytes.

Tom C

unread,
Jun 23, 2019, 5:44:49 PM6/23/19
to uken...@googlegroups.com
Frank,

Yes, it's occurred to me that the µKenbak-1 box would be an ideal platform to tinker around and create one's own 8-bit instruction sets, having nothing to do with the original Kenbak-1.  That's not my goal here.  As I said, I was trying to propose changes that would make existing programs run as is, while expanding it in a logical direction.

However as completely separate projects, inventing instruction sets is also a lot of fun. I did that in a grad school class using a computer at the Argonne National Laboratory that had a modifiable instruction set (horizontal microprogrammed).  Who knows, a Kenbak RISC machine?  (Although I like all the addressing modes in the CISC architecture.)  Anyhow, it would be interesting to see how different instruction sets affect the code size.

That's one of the unique things I liked about the Kenbak -- it's CPU was modifiable if one was inclined to tinker.  And now, instead of having to use an X-Acto knife to cut traces and a soldering iron, I can do it in software.  Unlike the 8080 emulator, where it would be sacrilegious to modify the instruction set.

Tom

Tom C

unread,
Jun 24, 2019, 6:49:19 PM6/24/19
to uken...@googlegroups.com
I have updated my assembler (for testing purposes only at this point, not released yet) to test out my memory and opcode extensions.  Here is what my first test program listing looks like
:
0004:               001 # Test program for uKenbak-1 assembler version 1.4 (not released yet).
0004:               002 # Program doesn't do anything useful, just testing new syntax for 1K
0004:               003 # and cmpa extension.   Assemble using -x option: kenbak_asm -x test1k.asm
0004:               004
0004:               005 # start on original page 0
0004: 123 002       006 page2   ldb     #2
0006: 134 013       007         stb     data
0010: 346 10,002    008         jmp     newpage jump to page 2
0012: 000           009 good    hlt
0013: 000           010 data    db 0
0014:               011
1000:               012         org     01000   beginning of page 2
1000: 200           013         nop             just to make low bits of pc non-zero
1001: 200           014         nop
1002: 024 013       015 newpage lda     data    still references page 0
1004: 313 002       016         cmpa    #2      new opcode
1006: 043 012       017         jane    bad     jump on same page
1010: 344 00,012    018         jmp     good    jump back to page 0
1012: 000           019 bad     hlt

So the memory is now assumed to go from 0000 to 1777.  There are four pages of 256 bytes each.  You can only use unconditional jumps to go from one to the other (as mentioned before, I am using the two lower bits of the unconditional opcode, 0344-0347, to specify the page).

When a jump to a page other than the current one is coded, the listing shows the target as two numbers, e.g. 10,002 on line 008 of the listing above.  The 002 is what goes into the object code.  The 10 is just a clue to the reader than this needs to be added to the topmost digit of the operand to form the real address.  For example, if it read 04,120 this would mean the real address is actually 0520.

This listing also shows the use of the new opcode (cmpa) at line 016, which replaces the unneeded two-byte nop.

Isn't this easier than dealing with EEPROM overlays?  With the latter, you will have to write the program in pieces, and line up the labels with equates which could get out of sync.

Tom Lake

unread,
Jun 24, 2019, 9:13:46 PM6/24/19
to Altair-Duino
Just speaking for myself, as long as all original Kenbak-1 programs run unmodified, I don't have a problem with extensions.

Tom L

Jim Manley

unread,
Jun 25, 2019, 10:42:59 AM6/25/19
to Altair-Duino
Yeah, what Tom said, also speaks for me.

I’m using the uKENBAK-1 to teach high school students computing, as well as on systems ranging from a single bit (a blinky-light based on s 555 timer IC), on up through S-100 8-bit, early PC 16-bit, Mac and Raspberry Pi 32-bit (even the Original Flavor Mac was 32-bit internally, well, OK, physically 24-bit, in address space), and current 64-bit, multi-core craziness.

They’ll eventually get to modified hardware and software, but, Rome wasn’t built in a single byte/night (pah-rump!).  I’ll be here all week, and please tip your waiters and waitresses generously, Ladies and gentlemen ... I get 20%, after all!

All the Best, Jim

Tom C

unread,
Jun 25, 2019, 1:56:01 PM6/25/19
to uken...@googlegroups.com
It turns out my CMPA proposal is not possible, as the Kenbak doesn't use status register bits for doing conditional jumps.  Instead, it actually tests the state of the register (so a "jump if A is equal" literally means jump if A == 0).  Since the whole point of the CMPA was to avoid modifying A (by doing a SUBA without storing the result), that scheme doesn't work.

However, my second proposal to increase the memory to 1KB and use the two lower bits of the unconditional jump instruction (0344-0347) as page bits is still active, and in fact I have updated Mark's emulator code, programmed a chip, and tested the scheme out using a program similar to the one I posted yesterday.  Works great.

When entering it by hand (I am still waiting for my serial adapter board to allow uploads), I discovered I needed a way to change the page bits from the keyboard, so I added two new buttons combinations:

Pressing STOP+SET sets the page # (0-3) for the PC to what's in Bit1 and Bit0 of the input register.

Pressing STOP+DISP displays the current page # on the display.

Note the "real" page bits, i.e. the ones used by the emulated CPU, are the top two bits of the X carry/overflow register, 0203.  I chose that location simply because its address was similar to the program counter address, 003.  These two bits are multiplied by 256 and added to the PC (003) to form the 10-bit address into the 1 KB memory.

All of these additions are only enabled if a new persistent control bit (Bit1, value 2) is set in the RTC user RAM byte 010.  For most users, it is currently set to 1, so this would mean setting it to 3.

Even with the 1 KB extension enabled, existing programs should run just fine, as long as they use either 0343 or 0344 for unconditional jumps.

When enabled, writing to and reading from the EEPROM is always the entire RAM space, 1024 bytes.  I didn't want to mess with Mark's slot size scheme.  If you want to save or load some smaller programs, you just have to turn the extension bit off (set the RTC user RAM byte 010 back to 1 instead of 3).

I've sent these changes along to Mark; it's up to him whether to incorporate them into his official version of the emulator.  I hope he does, as they in no way affect the use of it for existing programs.

Anyhow, for now I've got a 1 KB Kenbak-1 (or maybe I should call it a Kenbak-1KB)




Mark Wilson

unread,
Jun 25, 2019, 3:44:09 PM6/25/19
to Altair-Duino
Thanks Tom. 
I'll look at the diffs when I can.  I am still hesitant though. 
It seems using your fork on Github would be the perfect way to resolve the Great 8-Bit vs 10-Bit Schism of 2019.

But maybe I'll come around...

Tom Wilson

unread,
Jun 25, 2019, 4:14:08 PM6/25/19
to Altair-Duino
Does the compiler you're building the firmware with support conditional compilation?

#ifdef MODEL_REAL
...
#ifdef MODEL_ENHANCED

Mark Wilson

unread,
Jun 25, 2019, 4:22:11 PM6/25/19
to Altair-Duino
Yes it does, but that only makes the code more complicated.

Tom C

unread,
Jun 25, 2019, 4:54:52 PM6/25/19
to Altair-Duino
All of my additions are enabled/disabled using bit1 of the first byte of the persistent RTC user RAM.  They took up only a few hundred bytes (at most) of the ATMega's 32 KB program space.  Using conditional compilation would require two different binaries, and you would have to swap chips to change modes.

Tom

Tom C

unread,
Jul 18, 2019, 11:38:11 AM7/18/19
to uken...@googlegroups.com
To all -- 

As I wrote last month, my original proposal to add a CMPA instruction wasn't possible, for the reasons listed following this message. 

In my 1K paged extension, all data references always go to the first page (like zero page addressing in the 6502 and 680x), since the operand field is limited to 8 bits. However in writing sample code for my extension, I have found it would be useful to be able to put constant tables, strings, etc. in the other pages and leave the first page for variables.  So instead of my originally proposed CMPA, I am now proposing to add a LDACP (load accumulator A, current page) instruction with the unused opcode format 031x (well, used only for a 2-byte nop) I was going to use for the CMPA.

This would allow a string for example to be sent out using LDACP str,X  where str is the location of a DS pseudo-op on the same page as the code.  Note that if the LDACP instruction is used with indirect addressing, the indirect reference will still go to the fist page, but the 8-bit address it pulls from there will be assumed to be on the current page.

I've also found a error in my original design; if one writes: opcode (PC) or opcode (PC),X then the full 10-bit value of the extended PC should be used as an indirect.  I was not recognizing this as a special case and was using only the 8-bits of the location, which made no sense.

Like my other proposals, I feel this extension is in the spirit of the original Kenbak-1, since the schematics were all available in 1971 and the CPU was discrete logic and could be modified -- as I did on the "real" Kenbak-1 that I owned for 30 years.

I'm still testing all of this out, and will post it when I feel it's ready -- probably in a week or so.  I just got my serial port working last week after waiting for my module for a month.  See this message for a downloader I wrote.

I have been in contact with Mark Wilson, and we have decided that I should post my extension as a separate repo, and not combine into his.  However I will try to keep my up to date with any change she makes; for example my version already incorporates the EEPROM  extension he recently added.

When I was using my Kenbak-1 back in the 1970s, I had it connected to an ASR-33 Teletype.  That allowed me to read off of the paper tape, and also punch paper tape, using Teletype control codes to turn these devices on and off.  (When the punch was going, it wouldn't be printing so you could create paper tapes with object code.)  So I'm think of writing yet another Python program (in addition to my assembler and downloader) to emulate an ASR-33 with a simulated paper tape reader and punch (except I won't be limiting it to 10 CPS except maybe as an option).

Tom
Reply all
Reply to author
Forward
0 new messages