Another assembler for the µKenbak-1

244 views
Skip to first unread message

Tom C

unread,
Jun 9, 2019, 1:26:59 PM6/9/19
to uken...@googlegroups.com


I see there is another assembler for the µKenbak here, but frankly I never did like the original mnemonics.  So I decided to
write my own patterned after the 6800, although I used shr/shl instead of asr and asl.  And I used variations of call for Jump
and Mark instead of the 6800 jsr, since the conditional jumps and subroutine calls would have the same names.  Here's the
listing output for my test program for the assembler, just to show the syntax, it doesn't do anything useful:


004:               001 # Test program for uKenbak-1 assembler version 1.3 or later (6/15/19).
004: 002 # Program doesm't do anything useful, just testing syntax.
004: 003 # By default, PC will start at 4.
004: 004 # arithmetic, load/store and logical ops and addressing modes
004: 003 010 005 lbl adda #8 immediate value in decimal
006: 103 010 006 addb #010 same thing in octal
010: 024 001 007 lda b memory reference (using predefined reg b for reg transfer)
012: 235 220 008 stx (ptr) indirect reference
014: 306 204 009 ora data,x indexed
016: 337 220 010 lnega (ptr),x indirect/indexed
020: 011 # jumps and calls
020: 344 004 012 jmp lbl unconditional jump
022: 043 030 013 jane lbl2-2 jump if a not equal 0, , using arithmetic with label
024: 257 221 014 jxgt (ptr+one) jump indirect if x gt 0, using arithmetic inside ()
026: 364 041 015 call sub call to subroutine
030: 174 220 016 cbeq (ptr) call indirect to subroutine if b equal 0
032: 017 # set and skips
032: 018 lbl2
032: 122 204 019 set1 fox,2 set bit 2 of data to 1
034: 272 205 020 skp0 data+1,7 skip if bit 7 of data is 0
036: 021 # shifts and rotates
036: 001 022 shra 1+SHIFT shift a right 4 places
037: 361 023 rolb 2 rotate b left 2 places
040: 000 024 hlt halt
041: 000 025 sub db 0 leave room for return address
042: 200 026 nop no op
043: 023 222 027 lda #0222
045: 123 144 028 ldb #100
047: 360 029 sys system call (uKenbak-1 extension)
050: 023 222 030 sysp 0222,100 system call using parameters
052: 123 144
054: 360
055: 354 041 031 jmp (sub) return
057: 032
204: 033 fox org 0204 placing data after overflow/carry regs
204: 123 034 data db 0123 data in octal
205: 067 035 db 55 data in decimal
206: 124 145 040 036 ds "Te st" string with embedded space
211: 163 164
213: 124 145 042 037 ds 'Te"st' string with embedded apostrophe
216: 163 164
001: 038 one equ 1
003: 039 SHIFT equ 3
220: 000 040 ptr db 0 placehold for indirect
221: 000 041 db 0

Input can be in upper or lower case, case ignored. Syntax is: label opcode operands comment # global comment All fields delimited by one or more spaces or tabs. All fields optional except opcode, although a completely blank line is okay,
and a label by itself is okay. Global comments (no opcode) must begin with # in column 1. A label on a line by itself cannot
have a comment (since the first word would be taken as an opcode). Opcodes in table below. All should be obvious. Variations of "call" used for jump and mark.

adda,addb,addx,suba,subb,subx,lda,ldb,ldx,sta,stb,stx,ora,anda,lnega,
jmp,jane,jaeq,jalt,jage,jagt,jbne,jbeq,jblt,jbge,jbgt,jxne,jxeq,jxlt,jxge,jxgt,
call,cane,caeq,calt,cage,cagt,cbne,cbeq,cblt,cbge,cbgt,cxne,cxeq,cxlt,cxge,cxgt,
set0,set1,skp0,skp1
shra,rora,shla,rola,shrb,rorb,shlb,rolb,
hlt,nop

Pseudo-ops (discussed below):

sys,sysp,org,db,ds,equ


Immediate addressing uses # prefix, indirect addressing denoted using (), indexing by ,x and bit fields for set/skip by ,n
where n=0-7. Only decimal and octal supported; octal constants denoted by leading 0, e.g. 077 (not Python syntax of 0o).
Simple arithmetic in operands e.g. data+2 or lbl-lb2 is now allowed. See examples above, such as lbl-2, (ptr+one), 1+SHIFT
etc. Operands (except strings) cannot have embedded spaces.

Strings (ds pseudo-op) can be delimited by quotes "test" or apostrophes 'test'. Spaces okay.

Default origin is 4, changeable using org opcode. Data can be defined using db opcode (data byte).

In addition to the sys pseudo-op, which generates an octal 360 opcode, there is also a sysp macro that takes one or two
parameters (separated by a comma). The first parameter is loaded into the A accumulator before the call, and the second
(if present) into the B accumulator. Immediate addressing is implied (don't use #'s). Equated symbols can be used however.

So:
SYS_DELAY equ 0222
sysp SYS_DELAY,100 delay for 100 ms

is the same as:
lda #0222
ldb #100
sys delay for 100 ms

Predefined registers/locations are a, b, c, x, pc, out, in, oca, ocb, ocx, in where the latter three are the overflow/carry registers.

Tabs are converted to blanks in the output listing file. Default tab width is 8, but this can be changed with the tab pseudo-op:
e.g. tab 4 Setting tab 0 turns off the tab expansion.

Run on command line using: kenbak_asm filename.asm (standalone) or: python kenbak_asm.py filename.asm (from Python source) Listing output will go to standard out. Save it using >filename.lst is desired. Octal output will go to filename.out. Errors denoted
using asterisks in the listing file instead of numbers in the address, opcode and operand fields.

The Python 3 script, the test program above, as well as a stand-alone executable for those that don't have Python installed, are
available from one of my websites:


http://www.softwest.com/kenbak_asm/kenbak_asm.exe

http://www.softwest.com/kenbak_asm/kenbak_asm.py
http://www.softwest.com/kenbak_asm/test.asm


Run the test source file using the standalone exe simply by typing: kenbak_asm test.asm in a Command Prompt.


I'm releasing all of this into the public domain, so feel free to distribute if you wish.

Frank P.

unread,
Jun 9, 2019, 1:42:07 PM6/9/19
to uken...@googlegroups.com
Does this support assemble-time expressions in the operand field, (e.g.: sta lbl+1)?

On Sunday, June 9, 2019 at 1:26:59 PM UTC-4, Tom C wrote:

I see there is another aseembler for the µKenbak here, but frankly I never did like the original mnemonics.  So I decided to write my own patterned after the 6800, although I used shr/shl instead of asr and asl.  And I used variations of call for Jump and Mark instead of the 6800 jsr, since the conditional jumps and subroutine calls would have the same names.  Here's the listing output for my test program for the assembler, just to show the syntax, it doesn't do anything useful:


004:           # test program for µKenbak-1 assembler.  By default, will start at 4
004:           # arithmetic, load/store and logical ops and addressing modes
004: 003 010   lbl adda    #8      immediate value in decimal
006: 103 010       addb    #010    same thing in octal
010: 024 001       lda     b       memory reference (using predefined reg b for reg transfer)
012: 235 206       stx     (ptr)   indirect reference
014: 306 204       ora     data,x  indexed
016: 337 206       lnega   (ptr),x    indirect/indexed
020:           #jumps and calls
020: 344 004       jmp     lbl     unconditional jump
022: 043 032       jane    lbl2    jump if a not equal 0
024: 257 206       jxgt    (ptr)   jump indirect if x gt 0
026: 364 041       call    sub     call to subroutine
030: 174 206       cbeq    (ptr)   call indirect to subroutine if b equal 0
032:           # set and skips
032:           lbl2
032: 122 204       set1    data,2  set bit 2 of data to 1
034: 272 204       skp0    data,7  skip if bit 7 of data is 0
036:           # shifts and rotates
036: 001           shra    4       shift a right 4 places
037: 361           rolb    2       rotate b left 2 places
040: 000           hlt             halt
041: 000       sub db 0            leave room for return address
042: 200           nop             no op
043: 360           sys             system call (µKenbak-1 extension)
044: 354 041       jmp (sub)       return
046:           
204:               org 0204        placing data after overflow/carry regs
204: 123       data    db  0123    data in octal
205: 067               db  55      data in decimal
206: 000       ptr     db  0       placehold for indirect

Input can be in upper or lower case, case ignored.  Syntax is:

label  opcode  operands  comment
# global comment

All fields delimited by one or more spaces or tabs.  All fields optional except opcode, although
a completely blank line is okay, and a label by itself is okay.  Global comments (no opcode) must
begin with # in column 1. A label on a line by itself cannot have a comment (since the first word
would be taken as an opcode).

Opcodes in table below.  All should be obvious.  Variations of "call" used for jump and mark.

   adda,addb,addx,suba,subb,subx,
lda,ldb,,ldx,,sta,stb,stx,
ora,anda,lnega,
jmp,
jane,jaeq,jalt,jage,jagt,
jbne,jbeq,jblt,jbge,jbgt,
jxne,jxeq,jxlt,jxge,jxgt,
call,
cane,caeq,calt,cage,cagt,
cbne,cbeq,cblt,cbge,cbgt,
cxne,cxeq,cxlt,cxge,cxgt,
set0,set1,skp0,skp1
shra,rora,shla,rola,
shrb,rorb,shlb,rolb,
hlt,nop,sys,
org,db


Immediate addressing uses # prefix, indirect addressing denoted using (), indexing by ,x and bit fields for set/skip by ,n where n=0-7. Only decimal and octal supported; octal constants denoted by leading 0, e.g. 077 (not Python syntax of 0o). Arithmetic in operands e.g. data+2 is not allowed. Default origin is 4, changeable using org opcode. Data can be defined using db opcode (data byte). Predefined registers/locations are a, b, c, x, pc, out, in, oca, ocb, ocx, in where the latter three are the overflow/carry registers. Run on command line using: kenbak_asm filename.asm (standalone) or: python kenbak_asm.py filename.asm (from Python source) Listing output will go to standard out. Save it using >filename.lst is desired. Octal output will go to filename.out. Errors denoted using asterisks in the listing file instead of numbers in the address, opcode and operand fields.
 

The Python 3 script, the test program above, as well as a stand-alone executable for those that don't have Python installed, are available from one my website:

Run the standalone simply by typing: kenbak_asm test.asm in a Command Prompt.


I'm releasing all of this into the public domain, so feel free to distribute if you wish.  Let me know if you find any bugs or have suggestions or other comments, at tcro...@softwest.com

Charley Jones

unread,
Jun 9, 2019, 3:13:01 PM6/9/19
to Frank P., Altair-Duino
I see from his docs:
Arithmetic in operands e.g. data+2
is not allowed.
But not something that can’t be figured out. 


Sent from my iPhone 7!

On Jun 9, 2019, at 10:42 AM, Frank P. <fcpr...@gmail.com> wrote:

Does this support compile-time expressions in the operand field, (e.g.: sta lbl+1)?

--
You received this message because you are subscribed to the Google Groups "Altair-Duino" group.
To unsubscribe from this group and stop receiving emails from it, send an email to altair-duino...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/altair-duino/53193abf-02ed-4d5a-8d10-a56f44c675bc%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Tom Lake

unread,
Jun 9, 2019, 3:30:04 PM6/9/19
to Altair-Duino
Nice! I've always been partial to Motorola mnemonics even though most of my systems have had Intel or Intel-compatible CPUs.

Thanks!

Tom L


On Sunday, June 9, 2019 at 1:26:59 PM UTC-4, Tom C wrote:

I see there is another aseembler for the µKenbak here, but frankly I never did like the original mnemonics.  So I decided to write my own patterned after the 6800, although I used shr/shl instead of asr and asl.  And I used variations of call for Jump and Mark instead of the 6800 jsr, since the conditional jumps and subroutine calls would have the same names.  Here's the listing output for my test program for the assembler, just to show the syntax, it doesn't do anything useful:


004:           # test program for µKenbak-1 assembler.  By default, will start at 4
004:           # arithmetic, load/store and logical ops and addressing modes
004: 003 010   lbl adda    #8      immediate value in decimal
006: 103 010       addb    #010    same thing in octal
010: 024 001       lda     b       memory reference (using predefined reg b for reg transfer)
012: 235 206       stx     (ptr)   indirect reference
014: 306 204       ora     data,x  indexed
016: 337 206       lnega   (ptr),x    indirect/indexed
020:           #jumps and calls
020: 344 004       jmp     lbl     unconditional jump
022: 043 032       jane    lbl2    jump if a not equal 0
024: 257 206       jxgt    (ptr)   jump indirect if x gt 0
026: 364 041       call    sub     call to subroutine
030: 174 206       cbeq    (ptr)   call indirect to subroutine if b equal 0
032:           # set and skips
032:           lbl2
032: 122 204       set1    data,2  set bit 2 of data to 1
034: 272 204       skp0    data,7  skip if bit 7 of data is 0
036:           # shifts and rotates
036: 001           shra    4       shift a right 4 places
037: 361           rolb    2       rotate b left 2 places
040: 000           hlt             halt
041: 000       sub db 0            leave room for return address
042: 200           nop             no op
043: 360           sys             system call (µKenbak-1 extension)
044: 354 041       jmp (sub)       return
046:           
204:               org 0204        placing data after overflow/carry regs
204: 123       data    db  0123    data in octal
205: 067               db  55      data in decimal
206: 000       ptr     db  0       placehold for indirect

Input can be in upper or lower case, case ignored.  Syntax is:

label  opcode  operands  comment
# global comment

All fields delimited by one or more spaces or tabs.  All fields optional except opcode, although
a completely blank line is okay, and a label by itself is okay.  Global comments (no opcode) must
begin with # in column 1. A label on a line by itself cannot have a comment (since the first word
would be taken as an opcode).

Opcodes in table below.  All should be obvious.  Variations of "call" used for jump and mark.

   adda,addb,addx,suba,subb,subx,
lda,ldb,,ldx,,sta,stb,stx,
ora,anda,lnega,
jmp,
jane,jaeq,jalt,jage,jagt,
jbne,jbeq,jblt,jbge,jbgt,
jxne,jxeq,jxlt,jxge,jxgt,
call,
cane,caeq,calt,cage,cagt,
cbne,cbeq,cblt,cbge,cbgt,
cxne,cxeq,cxlt,cxge,cxgt,
set0,set1,skp0,skp1
shra,rora,shla,rola,
shrb,rorb,shlb,rolb,
hlt,nop,sys,
org,db


Immediate addressing uses # prefix, indirect addressing denoted using (), indexing by ,x and
bit fields for set/skip by ,n where n=0-7.  Only decimal and octal supported; octal constants
denoted by leading 0, e.g. 077 (not Python syntax of 0o).  Arithmetic in operands e.g. data+2
is not allowed.

Default origin is 4, changeable using org opcode.  Data can be defined using db opcode (data byte).

Predefined registers/locations are a, b, c, x, pc, out, in, oca, ocb, ocx, in where the latter three
are the overflow/carry registers.

Run on command line using: kenbak_asm filename.asm	(standalone)
                       or: python kenbak_asm.py filename.asm	(from Python source)

Listing output will go to standard out.  Save it using >filename.lst is desired.  Octal output will go to filename.out. Errors denoted using asterisks in the listing file instead of numbers in the
address, opcode and operand fields.
 

The Python 3 script, the test program above, as well as a stand-alone executable for those that don't have Python installed, are available from one my website:

Tom C

unread,
Jun 9, 2019, 4:56:13 PM6/9/19
to uken...@googlegroups.com
Does this support assemble-time expressions in the operand field, (e.g.: sta lbl+1)?

Not currently, but I could add that pretty easily.  Will try to do so tonight.  Probably should also add an EQU pseudo-op.

John Kennedy

unread,
Jun 9, 2019, 5:41:54 PM6/9/19
to Altair-Duino
Looks neat! But at the risk of proving my ignorance, how can I get the code into the uKenBak?

Frank P.

unread,
Jun 9, 2019, 5:50:46 PM6/9/19
to Altair-Duino
That would be good. With only 248 program bytes to work with, super tight code is a must, and storing data in the immediate byte(s) of an instruction and/or other use of self-modifying code is (was) de rigueur. Adding EQU could make the code more readable too, e.g.:

saveda     equ restorea+1
               ...
               sta  saveda
               ...
restorea   lda #0

Frank P.

unread,
Jun 9, 2019, 5:54:48 PM6/9/19
to uken...@googlegroups.com
Push the pretty buttons :), or if you've added a serial port, use Mark's serial port loader (see his docs, Extension 8 Send/Receive Memory, and serial.txt).

Tom C

unread,
Jun 9, 2019, 7:21:45 PM6/9/19
to Altair-Duino
The output file is in a format designed to be loaded directly via the serial loader (extension #8).  I haven't tried that yet.  Otherwise, you have to key in the octal values that are on the left side of the listing file.  e.g. for the line:

004: 003 010 lbl adda #8 immediate value in decimal

you would set the address to 004, and then start keying in values, 003, 010 etc from the listing.

Frank P.

unread,
Jun 9, 2019, 7:25:39 PM6/9/19
to Altair-Duino
Adding a serial port is shown...

In either case, adding a capacitor and resistor to the ATMega328 pin 1 also permits the firmware to be uploaded via the serial port (see topics above), so the chip never needs to be removed from the board again.

Mark Wilson

unread,
Jun 9, 2019, 7:38:33 PM6/9/19
to Altair-Duino
Note also that if you're working on something reasonably involved, and you are set up to be able to reprogram the uKenbak in place, there's a more streamlined way than edit, assemble, upload, run, rinse, repeat.
You can bake your program into the sketch, replacing one of the built-in programs, for example the Sieve of Eratosthenes (program #6).
See Programs.cpp. It includes a simple assembler.  That would let you develop your Kenbak program in the Arduino IDE. You could also configure the uKenbak to auto-run it. Make a change, upload, and it's running.
Sketch space is very tight, you might need to comment-out other built-ins to make space. In fact you'll see I've already done this with the Sieve, replaced the asm code with PROGMEM bytes.

Tom C

unread,
Jun 9, 2019, 9:00:14 PM6/9/19
to uken...@googlegroups.com
It does now, just updated (V1.1).  operand+operand, operand-operand with or without parens.  Also added equ pseudo op.  Files updated on my server.

Original posting edited to show changes.

Frank P.

unread,
Jun 9, 2019, 9:31:17 PM6/9/19
to uken...@googlegroups.com
Thanks Tom... I'll give it a try on my adding machine program tomorrow.
Dave's assembler is great too.
Message has been deleted
Message has been deleted

Frank P.

unread,
Jun 9, 2019, 10:25:43 PM6/9/19
to Altair-Duino
Whoops, just noticed no support for string literals, e.g.:
INPUT           DS "00000000000000000000"  # Input text string (up to 20 digits)

Could use 20 DBs:
INPUT           DB 060
                    DB 060
                    ...

but that seems inconvenient and doesn't reflect its stringish nature.

Tom C

unread,
Jun 10, 2019, 1:01:59 AM6/10/19
to Altair-Duino
Frank,

Just for you, I added a string pseudo-op, DS.  See new example code.  Does not support control chars however, still need db for those.

Tom

Frank P.

unread,
Jun 10, 2019, 9:51:15 AM6/10/19
to uken...@googlegroups.com
So I tried it on my serial port Adding Machine program and it almost worked. See attached AddingMachineV11a.* files.
The two errors were:

At address 242, the negative operand is not represented properly in octal (should be 354, not -24).

At address 376, the last instruction is repeated twice in the listing, but not output at all in the .out file.

I also noticed a difference (but not an error per-se) between your assembler and Dave Hunter's assembler:
   You use the "=0" condition (4) for UNC jumps/calls
   Dave uses the "!=0" condition (3) for UNC jumps/calls
Both instructions are equivalent in function (as would they be if any of the other 3 conditions were used).

I used the .exe file because I couldn't get the Python file to work...
   It complained about the µ character (and the strange character before it) in the comments
   It complained about the end= in the prints
I'm not familiar enough with Python to decipher these errors.

Lastly, might I suggest you output 3-digit octal numbers (like Dave's assembler) instead of 4-digits because a file with 3-digits can be sent to the uKenbak-1 at 38400 baud without loss, while 4-digits causes problems.


AddingMachineV11a.asm
AddingMachineV11a.lst
AddingMachineV11a.out

Tom C

unread,
Jun 10, 2019, 11:15:16 AM6/10/19
to Altair-Duino
Thanks, your program was a nice test for the assembler.  Will look at the issues tonight.  I use 344 for jumps because I belive that was what Kenbak's examples used.

Tom
Lastly, might I suggest you output 3-digit octal numbers (like Dave's assembler) instead of 4-digits because a file with 3-digits can be sent to the uKenbak-1 at 9600 baud without loss, while 4-digits causes problems.


Frank P.

unread,
Jun 10, 2019, 11:31:51 AM6/10/19
to uken...@googlegroups.com
Tom, here's another problem:
   Address 3 in the .out file is incorrect (it's 0, should be 310).
There is yet another problem (because the program doesn't run properly) but I haven't found it yet.
EDIT: No, there isn't another problem, it was just pilot error.

Frank P.

unread,
Jun 10, 2019, 12:40:47 PM6/10/19
to Altair-Duino
One more thing (utters Lieutenant Columbo just as he's about to leave)...

I found that even if you output 3-digit octal numbers in the output file, the uKenbak-1 will have trouble loading it at 38400 baud with the two carriage returns and one linefeed after each 16 values. Without those carriage returns and line feed (i.e. a single line with all 256 values, or fewer than 256 values with an "e" after the last comma), the file will load fine at 38400 baud. The reason that it is desirable to be able to load at that baud rate is it is inconvenient to change the baud rate in the terminal program to a lower rate to load the program, and then back to 38400 to run the program.

The .oct file produced by Dave's assembler does produce a single line with comma-separated 3-digit octal values and a terminating "e", and that seems to be the ideal format for loading into the uKenbak-1.

Tom C

unread,
Jun 10, 2019, 1:07:49 PM6/10/19
to Altair-Duino
Yeah I saw that too.  I hadn't expected people to set the PC using a DB op.  Will fix.

Tom C

unread,
Jun 10, 2019, 1:13:37 PM6/10/19
to Altair-Duino
Frank,

could you post the files from Dave's assembler for the same program so I can compare them?  thx.

Tom

Frank P.

unread,
Jun 10, 2019, 1:16:55 PM6/10/19
to Altair-Duino
Sure Tom. See attached.
AddingMachineV11.asm
AddingMachineV11.lst
AddingMachineV11.oct

Tom C

unread,
Jun 11, 2019, 1:27:13 AM6/11/19
to Altair-Duino
Frank,

Program updated.  Should have all fixes.  My octal output now matches Dave's, except for the difference in unconditional JMP opcodes which I discussed.

The problem you ran into with the end="" is because this is written for Python 3.6 and later, as mentioned in the program.  I assume you are using Python 2.

Tom

Frank P.

unread,
Jun 11, 2019, 7:28:32 AM6/11/19
to uken...@googlegroups.com
Excellent Tom! Now all I have to do is decide which assembly format I like better. I think this one is easier to remember, and the .exe file will make it more widely usable.

Yes, Python 2.7.6 is what comes installed by default in Windows 10 "Ubuntu on Windows", so I installed 2.7.15 on Windows itself - it actually has a pretty recent date (April 30, 2018), and it's a 64-bit program, so I had assumed it was fairly current. Is Python 3.x.x backward compatible with 2.*.*?

Tom C

unread,
Jun 11, 2019, 10:07:46 AM6/11/19
to Altair-Duino
I never liked the original Kenbak mnemonics.  I actually had one of the original Kenbak-1s -- I bought mine in 1971 for $750.  I was just starting grad school for my MSCS degree and wanted something to practice assembly language on that was simpler than the school's UNIVAC 1108.  I bought a used ASR-33 Teletype that I used for input and output, writing a bit-banged RS-232 interface using one of the input switches and output LEDs.   Because the CPU was discrete 7400 logic, I actually modified the CPU to add an interrupt capability on the input bit.  So I just had to key in a small bootloader, and then load programs from paper tape.

Then four years later, still in school (I was going to night school, one class at a time), I bought a Sphere-1 kit with a 6800 and 20 KB of static RAM.  It came out at the same time as the Altair 8800, but I liked the Motorola instruction set better.  No surprise, it had two accumulators A and B and an index register X.  It felt right at home.  I wrote a word processor for the Sphere, plus a PL/M compiler, which were both being sold.  My publisher gave me an Apple ][ when it first came out, and I ported the word processor to it.  More about my Sphere here.

So my Kenbak sat on a shelf in our garage for the next 30 years.  I sold mine to a vintage computer collector fifteen years ago for way more than I paid for it.  Would be worth a lot more now, but I needed the money at the time.    I donated my Sphere-1 to a computer museum about the same time I sold the Kenbak-1 (we were moving and I didn't want to take all this stuff with me).

Python 3.x is highly compatible with 2.x.  One of the changes though was they made the print statement a function rather than a built-in.  So you always have to use parentheses.  And the way of handling end of lines changed, as you discovered.  I highly recommend you switch, as 2.x will not be supported past 2020.

Tom

John Kennedy

unread,
Jun 11, 2019, 10:25:22 AM6/11/19
to Altair-Duino
Wow, a real pioneer! There is a KenBak-1 at the Living Computers Museum in Seattle where I spend too much time, but it's one of the few behind glass. Looks way bigger than you might expect.
The Sphere sounds like it was ahead of its time.I wonder if they had picked an 8080 CPU would it be more well-known today?

Tom C

unread,
Jun 12, 2019, 12:36:05 AM6/12/19
to Altair-Duino
New version (1.22) uploaded with a minor enhancement; if there are any errors detected during assembly, it prints the total at the bottom of the listing output and the output file is nulled.

Tom C

unread,
Jun 15, 2019, 10:55:13 PM6/15/19
to uken...@googlegroups.com
New version 1.32, as of 6/16/19.  See original post for download locations.  New features:

1. Are you tired of setting up operands for sys calls?  In addition to the sys pseudo-op, which generates an octal 360 opcode, there is now also a sysp macro that takes one or two parameters (separated by a comma).  The first parameter is loaded into the A accumulator before the call, and the second (if present) into the B accumulator.  Immediate addressing is implied (don't use #'s). Equated symbols can be used however.

So:
SYS_DELAY equ 0222

                    sysp  SYS_DELAY,100   delay for 100 ms

is the same as:
                    lda     #0222
                    ldb     #100
                    sys                                     delay for 100 ms

More examples:

SYS_GETSECONDS  equ    0000
SYS_SETSECONDS  equ    0200
SYS_GETMINUTES   equ     0001
SYS_SETMINUTES   equ     0201
SYS_GETRANDOM   equ     0021
SYS_SETSEED        equ     0221
SYS_DELAY             equ     0222
SYS_READSERIAL   equ     0023
SYS_WRITESERIAL  equ     0223

                   sysp SYS_SETMINUTES,0125      set RTC minutes to 55 (0125 in BCD)

                   sysp SYS_SETSEED,0                 seed random number generator from RTC

                   sysp SYS_GETRANDOM               get a random number in B

                   ldb    char
                   sysp SYS_WRITESERIAL             send value in char to serial port (note can't use the two-operand form here, because not immediate)

                   sysp SYS_WRITESERIAL,015     send new line to serial port

                   sysp SYS_READSERIAL              return value in serial port in B


2. Tabs in the source file are now converted to blanks in the output listing file.  Default tab width is 8, but this can be change with the tab pseudo-op:  e.g. tab 4   Setting tab 0 turns off the tab expansion.

3. Duplicate labels now properly generate an error, unless the symbol is a predefined register and the value isn't being changed.  So:

     org    0
A   db     0

no longer generates an error like it did in version 1.22.

4. Command line option -e added to uses spaces instead of commas in output file, to make it compatible with Kenbak-1 JS emulator found here:


    e.g.   kenbak_asm -e test.asm

    then copy and paste contents of the .out file into the Memory Loader window of the JS emualtor and press Load.

Note: programs run fine on the JS emulator, except of course the emulator doesn't support any of the uKenbak-1 extensions; they are just treated as no-ops.


Frank P.

unread,
Jul 4, 2019, 10:45:07 AM7/4/19
to Altair-Duino
Tom, I ran into what I think is a little bug... if I try to assemble a program with a DS pseudo-op in it with a string operand that contains any commas, the assembler silently refuses to assemble the line (just leaves *s in the listing and doesn't advance the PC). What do you think?

Tom C

unread,
Jul 4, 2019, 4:07:49 PM7/4/19
to Altair-Duino
Yep, sounds like a bug.  I search for the comma, looking for ,x or,n (indexed addressing, or bit shift), and that is apparently happening before I process the quotes for the string.

I'm out of town for the July 4th weekend, but will fix this when I come back on Sunday.  Meanwhile you'll have to use two strings with a db in th middle for the comma.

I also just finally got my serial board from China, so I'll be testing that with my Kenbak-1K extensions on Sunday too.

Thanks for using my assembler and creating lots of good test cases for it!

Tom

Tom C

unread,
Jul 8, 2019, 12:50:58 AM7/8/19
to Altair-Duino

Frank P.

unread,
Jul 8, 2019, 7:59:58 AM7/8/19
to Altair-Duino
Great Tom... tested and works fine now.

Tom C

unread,
Jul 21, 2019, 8:17:01 PM7/21/19
to uken...@googlegroups.com
Version 2.0 of the assembler has now been released and is on my website:

It supports the new extensions for my uKenbak-1K emulator (which will be shortly released in a separate post), but it also has some new features for anyone using the assembler.


1. Escape characters for DS pseudo-op: you can now embed \r \n \t and \0 in a DS string, for carriage return, line feed, tab and NULL instead of needing to use DB's.  Use two \\ to indicate a single \ character.  Thus to specify a zero-terminated string which includes a CR/LF:

    

mystring   DS    "Hello World\r\n\0"     comment if any


2.  Multi-byte data defintions.  You can now enter as many operands as needed after the DB pseudo-op, either numbers or symbols.  But no spaces after the commas:

mydata     DB    1,055,fox               comment if any


See the first message in the topic for more information.   The Python source file (.py) also has program documentation at the beginning.


Frank Prindle

unread,
Jul 23, 2019, 10:02:36 AM7/23/19
to uKenbak-1
Tom, I just got a chance to try this on my overlayed adding machine program, and it goes completely haywire, whereas version 1.32 worked just fine. It seems to completely ignore my "ORG 4" directive, and it's all downhill from there. Attached is the source code for overlay 1, the good listing (from 1.32), and the bad listing (from 2.0). It also fails this way with version 1.41 if that gives you any clue.
AddingMachineV12o1.asm
AddingMachineV12o1.good.lst
AddingMachineV12o1.lst

Tom C

unread,
Jul 23, 2019, 11:17:12 AM7/23/19
to uKenbak-1
It's because you have code at byte 0377, which bumps the program counter to 0400, which is treated as an error (shouldn't be, as long as you don't put code there).

I'll fix this tonight, but as a temporary fix, try running  it with the -x option:       kenbak_asm -x AddingMachineV12o1.asm

The -x enables the extensions for my 1K version, where address 0400 is legal.  Shouldn't otherwise affect your code

I can always count on you to test the edge cases!

Thanks,
Tom

Frank Prindle

unread,
Jul 23, 2019, 11:58:41 AM7/23/19
to uKenbak-1
Yes, I somehow often end up using every byte! The code at 0377 is the reason why you have to go through the double press of button 0 when saving this program to EEPROM slot 0. Without that double press, the code at 0377 gets stored corrupted. For the same reason, you can't touch the buttons while the program is running.

Tom C

unread,
Jul 23, 2019, 4:35:42 PM7/23/19
to uKenbak-1
Frank,

BTW, looking at your code, you don't need to put a # in front of a comment when it is on the same line as an assembler statement, because operands can't have any spaces (except for strings, and believe me that made the parsing interesting).  Anyway, anything after an operand is treated as a comment.  For this reason you can't have a comment on a line that has only a label, but no opcode/operand.

So the # is only needed for at the beginning of full-line comments. Obviously the extraneous # isn't hurting anything. A lot of other assemblers do require a special character in front of all comments.

Tom

Frank Prindle

unread,
Jul 23, 2019, 6:13:58 PM7/23/19
to uKenbak-1
Just force of habit; I'm a C/C++ programmer since forever, so "//..." is part of my world (as was /*...*/ in the olden days). I can muddle through most other high order languages (though FORTH is a major paradigm-shift) and did lots of 6502/6510 and PDP-8 assembly, but literally never did 8080/z80/8086 assembly or any of the Motorola stuff.

Tom C

unread,
Jul 24, 2019, 1:28:33 AM7/24/19
to uKenbak-1
Fxied.  New version 2.01 on my website (same links as before).  So you don't need the -x anymore.

On Tuesday, July 23, 2019 at 7:02:36 AM UTC-7, Frank Prindle wrote:

Frank Prindle

unread,
Jul 24, 2019, 6:55:37 AM7/24/19
to uKenbak-1
Works great now.

Ardunaut

unread,
Nov 3, 2019, 1:19:53 AM11/3/19
to uken...@googlegroups.com
Learning python while making an IDE for the Kenbak-1
Trying to make an editor with highlighting for the Tom Crosley's kenbak-1 assembler, 
So far I only had to change "#" to ";" to identify comments better and not confuse them with the "#" used to specify "immediate".
Also is easy to see the code in other assembler editors (they don't recognice the kenbak op-codes of course.).
This will be a short Winter... :)

kenbak_IdeDev_03_py-(1).png



On Sunday, June 9, 2019 at 1:26:59 PM UTC-4, Tom C wrote:


I see there is another assembler for the µKenbak here, but frankly I never did like the original mnemonics.  So I decided to
write my own patterned after the 6800, although I used shr/shl instead of asr and asl.  And I used variations of call for Jump
and Mark instead of the 6800 jsr, since the conditional jumps and subroutine calls would have the same names.  Here's the
listing output for my test program for the assembler, just to show the syntax, it doesn't do anything useful:


004:               001 # Test program for uKenbak-1 assembler version 1.3 or later (6/15/19).
004: 002 # Program doesm't do anything useful, just testing syntax.
004: 003 # By default, PC will start at 4.
004: 004 # arithmetic, load/store and logical ops and addressing modes
004: 003 010 005 lbl adda #8 immediate value in decimal
006: 103 010 006 addb #010 same thing in octal
010: 024 001 007 lda b memory reference (using predefined reg b for reg transfer)
012: 235 220 008 stx (ptr) indirect reference
014: 306 204 009 ora data,x indexed
016: 337 220 010 lnega (ptr),x indirect/indexed
020: 011 # jumps and calls
020: 344 004 012 jmp lbl unconditional jump
022: 043 030 013 jane lbl2-2 jump if a not equal 0, , using arithmetic with label
024: 257 221 014 jxgt (ptr+one) jump indirect if x gt 0, using arithmetic inside ()
026: 364 041 015 call sub call to subroutine
030: 174 220 016 cbeq (ptr) call indirect to subroutine if b equal 0
032: 017 # set and skips
032: 018 lbl2
032: 122 204 019 set1 fox,2 set bit 2 of data to 1
034: 272 205 020 skp0 data+1,7 skip if bit 7 of data is 0
036: 021 # shifts and rotates
036: 001 022 shra 1+SHIFT shift a right 4 places
037: 361 023 rolb 2 rotate b left 2 places
040: 000 024 hlt halt
041: 000 025 sub db 0 leave room for return address
042: 200 026 nop no op
043: 023 222 027 lda #0222
045: 123 144 028 ldb #100
047: 360 029 sys system call (uKenbak-1 extension)
050: 023 222 030 sysp 0222,100 system call using parameters
052: 123 144
054: 360
055: 354 041 031 jmp (sub) return
057: 032
204: 033 fox org 0204 placing data after overflow/carry regs
204: 123 034 data db 0123 data in octal
205: 067 035 db 55 data in decimal
206: 124 145 040 036 ds "Te st" string with embedded space
211: 163 164
213: 124 145 042 037 ds 'Te"st' string with embedded apostrophe
216: 163 164
001: 038 one equ 1
003: 039 SHIFT equ 3
220: 000 040 ptr db 0 placehold for indirect
221: 000 041 db 0

Input can be in upper or lower case, case ignored. Syntax is: label opcode operands comment # global comment All fields delimited by one or more spaces or tabs. All fields optional except opcode, although a completely blank line is okay,
and a label by itself is okay. Global comments (no opcode) must begin with # in column 1. A label on a line by itself cannot
have a comment (since the first word would be taken as an opcode). Opcodes in table below. All should be obvious. Variations of "call" used for jump and mark.

adda,addb,addx,suba,subb,subx,lda,ldb,ldx,sta,stb,stx,ora,anda,lnega,
jmp,jane,jaeq,jalt,jage,jagt,jbne,jbeq,jblt,jbge,jbgt,jxne,jxeq,jxlt,jxge,jxgt,
call,cane,caeq,calt,cage,cagt,cbne,cbeq,cblt,cbge,cbgt,cxne,cxeq,cxlt,cxge,cxgt,
set0,set1,skp0,skp1
shra,rora,shla,rola,shrb,rorb,shlb,rolb,
hlt,nop

Pseudo-ops (discussed below):

sys,sysp,org,db,ds,equ


Immediate addressing uses # prefix, indirect addressing denoted using (), indexing by ,x and bit fields for set/skip by ,n
where n=0-7. Only decimal and octal supported; octal constants denoted by leading 0, e.g. 077 (not Python syntax of 0o).
Simple arithmetic in operands e.g. data+2 or lbl-lb2 is now allowed. See examples above, such as lbl-2, (ptr+one), 1+SHIFT
etc. Operands (except strings) cannot have embedded spaces.

Strings (ds pseudo-op) can be delimited by quotes "test" or apostrophes 'test'. Spaces okay.

Default origin is 4, changeable using org opcode. Data can be defined using db opcode (data byte).

In addition to the sys pseudo-op, which generates an octal 360 opcode, there is also a sysp macro that takes one or two

parameters (separated by a comma). The first parameter is loaded into the A accumulator before the call, and the second
(if present) into the B accumulator. Immediate addressing is implied (don't use #'s). Equated symbols can be used however.

So:
SYS_DELAY equ 0222
sysp SYS_DELAY,100 delay for 100 ms

is the same as:
lda #0222
ldb #100
sys delay for 100 ms

Predefined registers/locations are a, b, c, x, pc, out, in, oca, ocb, ocx, in where the latter three are the overflow/carry registers.

Tabs are converted to blanks in the output listing file. Default tab width is 8, but this can be changed with the tab pseudo-op:

e.g. tab 4 Setting tab 0 turns off the tab expansion.

Run on command line using: kenbak_asm filename.asm	(standalone)
                       or: python kenbak_asm.py filename.asm	(from Python source)

Listing output will go to standard out.  Save it using >filename.lst is desired.  Octal output will go to filename.out. Errors denoted
using asterisks in the listing file instead of numbers in the address, opcode and operand fields.

The Python 3 script, the test program above, as well as a stand-alone executable for those that don't have Python installed, are
available from one of my websites:

Run the test source file using the standalone exe simply by typing: kenbak_asm test.asm in a Command Prompt.


I'm releasing all of this into the public domain, so feel free to distribute if you wish.

Reply all
Reply to author
Forward
0 new messages