On the Utilities page I have two programs to convert to MOS Technology papertape format: KIMpaper, a command line utility, and ConvertHexFormat, a GUI app.
All in Freepascal/Lazarus source format, and tested on Linux (Raspberry PI OS) and Windows 10 64 bit. So the programs will run everywhere Lazarus is available (MS DOS, WIndows, Linux Mac OS).
KIMPAPER is written at the time the Micro-KIM appeared. CLI utility. Supports Binary to/from Papertape. Still runs fine on all platforms supported by Freepascal (Windows, MS DOS, Linux etc) after a recompilation, source available.
ConvertHexFormat is a more recent GUI utilitilty with many more 8 bit hex formats as input and output.
There were some bugs of course in older versions. V2 added the ability for multipart hex formats, records having a non-consecutive load address. That seems to wok fine since V2.1
In 2.2 a bug in MOS Papertape format for bigger files is fixed, the end-of-file record (record type 00, total line count) had a bug in the checksum calculation. KIMPAPER is and was correct in the calculation.
But in ConvertHexFormat it was wrong (as it still is in the well known srec utility in the Unix world!).
Programs to manipulate the binary and hex formatted files of interest for SBC owners. Intel hex, MOS papertape, Motorola S-record, binary, hex conversion fort eh 8 bit world.
Runs on Windows, Linux, Mac due to Lazarus and Freepascal. Source included.
The KIM-1 has two methods of loading programs:
– from audio files on the audio interface
– from papertape from a papertape reader connected to the teletype terminal
Loading from papertape is something that comes for the old Teletype with papertape reader and punch.
It is therefore accessible with the ‘L’ command in the TTY CLI.
The routine will not work in LED display/hex keyboard mode, as GETCH does not work there.
0868 1DF7 C9 4C CMP #'L' ; LOAD TAPE
0869 1DF9 F0 09 BEQ LOADV
0874 1E04 4C E7 1C LOADV JMP LOAD
0722 1CE7 ;
0723 1CE7 ; LOAD PAPER TAPE FROM TTY
0724 1CE7 ;
0725 1CE7 20 5A 1E LOAD JSR GETCH ; LOOK FOR FIRST CHAR
0726 1CEA C9 3B CMP #$3B ; SMICOLON
0727 1CEC D0 F9 BNE LOAD
0728 1CEE A9 00 LDA #$00
0729 1CF0 85 F7 STA CHKSUM
0730 1CF2 85 F6 STA CHKHI
0731 1CF4 ;
0732 1CF4 20 9D 1F JSR GETBYT ; GET BYTE COUNT
0733 1CF7 AA TAX ; SAVE IN X INDEX
0734 1CF8 20 91 1F JSR CHK ; COMPUTE CHECKSUM
0735 1CFB ;
0736 1CFB 20 9D 1F JSR GETBYT ; GET ADDRESS HI
0737 1CFE 85 FB STA POINTH
0738 1D00 20 91 1F JSR CHK
0739 1D03 20 9D 1F JSR GETBYT ; GET ADDRESS LO
0740 1D06 85 FA STA POINTL
0741 1D08 20 91 1F JSR CHK
0742 1D0B ;
0743 1D0B 8A TXA ; IF CNT=0 DONT
0744 1D0C F0 0F BEQ LOAD3 ; GET ANY DATA
0745 1D0E ;
0746 1D0E 20 9D 1F LOAD2 JSR GETBYT ; GET DATA
0747 1D11 91 FA STA (POINTL),Y ; STORE DATA
0748 1D13 20 91 1F JSR CHK
0749 1D16 20 63 1F JSR INCPT ; NEXT ADDRESS
0750 1D19 CA DEX
0751 1D1A D0 F2 BNE LOAD2
0752 1D1C E8 INX ; X=1 DATA RECORD
0753 1D1D ; X=0 LAST RECORD
0754 1D1D 20 9D 1F LOAD3 JSR GETBYT ; COMPARE CHKSUM
0755 1D20 C5 F6 CMP CHKHI
0756 1D22 D0 17 BNE LOADE1
0757 1D24 20 9D 1F JSR GETBYT
0758 1D27 C5 F7 CMP CHKSUM
0759 1D29 D0 13 BNE LOADER
0760 1D2B ;
0761 1D2B 8A TXA ; X=0 LAST RECORD
0762 1D2C D0 B9 BNE LOAD
0763 1D2E ;
0764 1D2E A2 0C LDX #$0C ; X-OFF KIM
0765 1D30 A9 27 LOAD8 LDA #$27
0766 1D32 8D 42 17 STA SBD ; DISABLE DATA IN
0767 1D35 20 31 1E JSR PRTST
0768 1D38 4C 4F 1C JMP START
0769 1D3B ;
0770 1D3B 20 9D 1F LOADE1 JSR GETBYT ; DUMMY
0771 1D3E A2 11 LOADER LDX #$11 ; X-OFF ERR KIM
0772 1D40 D0 EE BNE LOAD8
1141 1F91 18 CHK CLC
1142 1F92 65 F7 ADC CHKSUM
1143 1F94 85 F7 STA CHKSUM
1144 1F96 A5 F6 LDA CHKHI
1145 1F98 69 00 ADC #$00
1146 1F9A 85 F6 STA CHKHI
1147 1F9C 60 RTS
1148 1F9D ;
1149 1F9D ; GET 2 HEX CHAR'S AND PACK
1150 1F9D ; INTO INL AND INH
1151 1F9D X PRESERVED Y RETURNED = 0
1152 1F9D ; NON-HEX WILL BE LOADED AS NEAREST HEX EQU
1153 1F9D ;
1154 1F9D 20 5A 1E GETBYT JSR GETCH
1155 1FA0 20 AC 1F JSR PACK
1156 1FA3 20 5A 1E JSR GETCH
1157 1FA6 20 AC 1F JSR PACK
1158 1FA9 A5 F8 LDA INL
1159 1FAB 60 RTS
1160 1FAC ;
1161 1FAC ; SHIFT CHAR IN A INTO
1162 1FAC ; INL AND INH
1163 1FAC ;
1164 1FAC C9 30 PACK CMP #$30 ; CHECK FOR HEX
1165 1FAE 30 1B BMI UPDAT2
1166 1FB0 C9 47 CMP #$47 ; NOT HEX EXIT
1167 1FB2 10 17 BPL UPDAT2
1168 1FB4 C9 40 CMP #$40 ; CONVERT TO HEX
1169 1FB6 30 03 BMI UPDATE
1170 1FB8 18 CLC
1171 1FB9 69 09 ADC #$09
1172 1FBB 2A UPDATE ROL A
1173 1FBC 2A ROL A
1174 1FBD 2A ROL A
1175 1FBE 2A ROL A
1176 1FBF A0 04 LDY #$04 ; SHIFT INTO I/O BUFFER
1177 1FC1 2A UPDAT1 ROL A
1178 1FC2 26 F8 ROL INL
1179 1FC4 26 F9 ROL INH
1180 1FC6 88 DEY
1181 1FC7 D0 F8 BNE UPDAT1
1182 1FC9 A9 00 LDA #$00 ; A=0 IF HEX NUM
1183 1FCB 60 UPDAT2 RTS
(Photos by Dave Wiliams with the MOS KIM-1 Reproduction connected to a Teletype)
Now papertape format is a special MOS Technology format, already used in the TIM-1. See the KIM-1 user manual for a technical description.
This is for example the papertape output captured with the KIM-1 S command for the memory test program in the Fist Book of KIM
Load address, data and checksums are in the records.
A record is made up of:
‘;’ XX YYYY D..D CCCC
where
XX is number of databytes
YYYY is load address
D..D are XX databytes
CCCC is checksum, sum of XX YYYY and D.D)
What is happening in the code?
– a papertape record starts with a ‘;’, so line 725-727 look for that incoming to find start of a record
– the checksum is calculated per record, so cleared in 728 -730
– first two characters in record have byte count in record ($18 in examples above), 732-733 saved in X and added to checksum
– next the address to load the data in is read in and added to checksum (736-741)
– if count = 0 we are at the end of the papertape
– get the databytes in the record in a loop, add to checksum and store at load address (746-751)
– load checksum, compare to calculated checksum and report fatal error if not equal (754-759)
– continue loading records until last record (count 0 in X) (761-762)
– make input deaf via PB5 ($27 to SBD and print string KIM (0C)KIM and return (764-768)
error handling
– if low checksum wrong, read high checksum byte (770
– print string ERR KIM (771 -772) and return
CHK
– addition to 16 bit checksum, overflow ignored (1141-1147)
GETBYT
– read a character
– pack into byte
– read second character
– pack shift also into byte INL
PACK
– if char < '0' exit (1164)
- if char > ‘F’ exit (1166)
– if char <'A' add $09
- convert to binary (1172-1175)
- shift into INL (1176-1181)
- hex convert success with A = 0 (1182), not used here
The convert to binary works as follows:
'0' = $30
..
'9' = $39
'A' = $40 + $09 = $4A
..
'F' = $46 + $09 = $4F
So the shift of four leaves 0 .. F
Note that any non-hex character will load to checksum errors and are detected that way
Note also the null characters (value 00) inserted at the beginning of a papertape are discarded by the search for the ‘;’ starting character.
Multipart papertape format
The papertape format has in every record the address where to load the databytes. So you can have non-contiguous parts of memory loaded with one papertape file.
The Convert to hex 8 bit utility supports mulipart papertapes.
These can be loaded by the KIM-1 loader with no problems, since every record load address is read and used.
The KIM-1 Save to papertape can not produce multipart papertape files.
Michael Kowalski created the 6502 Simulator many years ago. It simulated the MOS 6502, CMOS 65C02, and the 6501. Daryl Rictor took the sources and updated with 65816 assembler and emulator support to 16MB memory. He also translated the Polish help to English in CHM format. It is a Windows 32 bit program and runs fine on Windows 10 64 bit.
The debugger/simulator is still only 65(C)02.
You can write code, assemble it, and run it with a debugger with breakpoints, step by step etcetera.
How to use
Entering the source code
Open a blank source file by clicking on (File / New)
Type in your assembly language program
Save it by doing (File / Save as). Use the suggested extension of .65s for your filename.
Assembling the source code
Assemble the source code by clicking on (Simulator / Assemble).
If there are any errors in your code, an error message should come up at this point.
Using the debugger
Turn on the debugger by clicking on (Simulator / Debugger), or press F6.
Go to the View menu and make the following windows visible:
– 6502 Registers
– 6502 Memory
– Identifiers
Find the assembled machine code in the Memory window. It should match up with the codes on the handout.
A better way of looking at the code is to open the Disassembler window ( View / Disassembler ) which shows you clearly which instructions produced which machine code bytes.
The identifiers window tells you which memory locations have been set aside for the variables that you declared at the bottom of your program. That is all it is for really, so once you have seen that, you can close it.
Running the program
To run the program you have various options, all listed on the Simulator menu. The most useful one from a teaching point of view is ( Simulator / Step Into) or F11.
Arrange your windows so that you can see the source code, the memory window, and the registers window ( and the Dissassembler window if you want) and use F11 to start to step through the code.
After each instruction, look at the state of the registers, and satisfy yourself that the instruction has done what you expected it to. Also, when you do a Store to Memory instruction, you should see the value pop up in the appropriate place in the memory window.
When you have finished running the program, click on (Simulator / Restart Program) before trying to run it again.
Undocumented directives
In the file parse6502.cpp in the subfolder CrystalEdit you can read some assemble directives and I/O ports not documented in the Help.
Some are not documented in the Help but are in the sourcefile crytaledit/Parse6502.cpp
.ASCII
Directives defining values of single bytes using passed arguments.
Description
.BYTE (.DB, .ASCII) directives generates and defines single byte values. Input data might be entered in numerical or string form. Numerical expressions are also accepted.
.ROM_AREA
Directive establishing memory protection area
Syntax
.ROM_AREA addr_from_expr, addr_to_expr
Example
.ROM_AREA $a000, $afff
.ROM_AREA Start, * ; from ‘Start’ to here
Description
.ROM_AREA turns on memory protection for a given range of addresses. Any attempt to write to this area will stop program execution. Write attempts to the EPROM usually indicate a bug and memory protection can facilitate locating such bugs. Specifying same start and end address turns protection off
.IO_WND
Directive setting terminal window size.
Syntax
.IO_WND cols_expr, rows_expr
Example
.IO_WND 40, 20; 40 columns, 20 rows
Description
.IO_WND directive sets size of terminal window. It requires two parameters: number of columns and rows.
Both columns and rows are limited to 1..255 range.
IO_AREA
Label representing beginning of simulator I/O area.
Syntax
IO_AREA = addr_expr ; set I/O area IO_AREA ; use I/O area, default $E000
Example
IO_CLS = IO_AREA + 0 ; clear window port
STA IO_AREA+1 ; put char
Description
IO_AREA label represents beginning of simulator I/O area. Simulator can detect read and write attempts
from/to its I/O area.
Starting from IO_AREA address consecutive bytes are treated as virtual ports.”
(w) means write only port, (r) read only, (r/w) read/write.
TERMINAL_CLS – clear terminal window, set cursor at (0,0) position
TERMINAL_OUT – output single character interpreting control characters.
Terminal can only recognize those characters:
– $d char (caret) moving cursor to the beginning of line,
– $a char (line feed) moving cursor to the next line and scrolling window if necessary,
– 8 char (backspace) moving one position to the left and erasing char below cursor.
TERMINAL_OUT_CHR – outputs single character; control chars are being output just like regular characters.
TERMINAL_OUT_HEX – outputs single byte as a two-digit hexadecimal number.
TERMINAL_IN – input single byte, returns 0 if there’s no characters available in terminal’s buffer
– when I/O terminal window is active it can accept keyboard input;
– press Ins key to paste clipboard’s contents into terminal.
TERMINAL_X_POS – cursor X position (column).
TERMINAL_Y_POS – cursor Y position (row).
The Kowalski simulator includes a primitive I/O console, which is memory-mapped at a location declared with the IO_AREA pseudo-op. For example, IO_AREA=$D000 will map the console in at $D000.
; Wait for input test
; IO area of the simulator has to be set at the address $e000
; (Option/Simulator/InOut memory area)
; In/Out Window will only accept input when it has focus (is active)
*= $0600
io_area = $e000
io_cls = io_area + 0 ; clear terminal window
io_putc = io_area + 1 ; put char
io_putr = io_area + 2 ; put raw char (doesn't interpret CR/LF)
io_puth = io_area + 3 ; put as hex number
io_getc = io_area + 4 ; get char
LDA #$a
STA io_putc ; this will move cursor to the next line
STA io_putr ; this will print character
LDA #'>'
STA io_putc
.wait
LDA io_getc
BEQ .wait
STA io_puth
JMP .wait
BRK
Simulator
Compared to 6502 and 65C02 microprocessors, the simulator is characterized by the following different components:
There is no relationship between the speed of different opcodes.
Illegal codes in 6502 mode are not executed, but cause the program to stop.
Illegal codes in 65C02 mode are treated as NOP statements and do not cause the program to stop.
BRK, RTS, and $DB are privileged because one of them, depending on the settings, ends the program.
Reserved bits in the Status register (P) are always set to 1 (as in 65C02, but not in 6502).
With the simulator you can run a program assembled from the built-in editor. Or you can load an Intel Hex file, a Motorola S-record file or a binary file. The type is determined by the extension, so you may have to rename your hex files. The simulator makes all memory available, only the IO_AREA is special.
Several Windows Can be opened during a debug session: Processor Status and registers, Memory, Disassembly, Text Input/output, Stack, ZeroPage, Identifiers (from assembled symbol table)
Example code
A good example of a Kowalski program is the source of Lee Davison’a EhBasic Minimal Monitor
; minimal monitor for EhBASIC and 6502 simulator V1.05
; To run EhBASIC on the simulator load and assemble [F7] this file, start the simulator
; running [F6] then start the code with the RESET [CTRL][SHIFT]R. Just selecting RUN
; will do nothing, you'll still have to do a reset to run the code.
.include "basic.asm"
; put the IRQ and MNI code in RAM so that it can be changed
IRQ_vec = VEC_SV+2 ; IRQ code vector
NMI_vec = IRQ_vec+$0A ; NMI code vector
; setup for the 6502 simulator environment
IO_AREA = $F000 ; set I/O area for this monitor
ACIAsimwr = IO_AREA+$01 ; simulated ACIA write port
ACIAsimrd = IO_AREA+$04 ; simulated ACIA read port
; now the code. all this does is set up the vectors and interrupt code
; and wait for the user to select [C]old or [W]arm start. nothing else
; fits in less than 128 bytes
*= $FF80 ; pretend this is in a 1/8K ROM
; reset vector points here
RES_vec
CLD ; clear decimal mode
LDX #$FF ; empty stack
TXS ; set the stack
; set up vectors and interrupt code, copy them to page 2
LDY #END_CODE-LAB_vec ; set index/count
LAB_stlp
LDA LAB_vec-1,Y ; get byte from interrupt code
STA VEC_IN-1,Y ; save to RAM
DEY ; decrement index/count
BNE LAB_stlp ; loop if more to do
; now do the signon message, Y = $00 here
LAB_signon
LDA LAB_mess,Y ; get byte from sign on message
BEQ LAB_nokey ; exit loop if done
JSR V_OUTP ; output character
INY ; increment index
BNE LAB_signon ; loop, branch always
LAB_nokey
JSR V_INPT ; call scan input device
BCC LAB_nokey ; loop if no key
AND #$DF ; mask xx0x xxxx, ensure upper case
CMP #'W' ; compare with [W]arm start
BEQ LAB_dowarm ; branch if [W]arm start
CMP #'C' ; compare with [C]old start
BNE RES_vec ; loop if not [C]old start
JMP LAB_COLD ; do EhBASIC cold start
LAB_dowarm
JMP LAB_WARM ; do EhBASIC warm start
; byte out to simulated ACIA
ACIAout
STA ACIAsimwr ; save byte to simulated ACIA
RTS
; byte in from simulated ACIA
ACIAin
LDA ACIAsimrd ; get byte from simulated ACIA
BEQ LAB_nobyw ; branch if no byte waiting
SEC ; flag byte received
RTS
LAB_nobyw
CLC ; flag no byte received
no_load ; empty load vector for EhBASIC
no_save ; empty save vector for EhBASIC
RTS
; vector tables
LAB_vec
.word ACIAin ; byte in from simulated ACIA
.word ACIAout ; byte out to simulated ACIA
.word no_load ; null load vector for EhBASIC
.word no_save ; null save vector for EhBASIC
; EhBASIC IRQ support
IRQ_CODE
PHA ; save A
LDA IrqBase ; get the IRQ flag byte
LSR ; shift the set b7 to b6, and on down ...
ORA IrqBase ; OR the original back in
STA IrqBase ; save the new IRQ flag byte
PLA ; restore A
RTI
; EhBASIC NMI support
NMI_CODE
PHA ; save A
LDA NmiBase ; get the NMI flag byte
LSR ; shift the set b7 to b6, and on down ...
ORA NmiBase ; OR the original back in
STA NmiBase ; save the new NMI flag byte
PLA ; restore A
RTI
END_CODE
LAB_mess
.byte $0D,$0A,"6502 EhBASIC [C]old/[W]arm ?",$00
; sign on string
; system vectors
*= $FFFA
.word NMI_vec ; NMI vector
.word RES_vec ; RESET vector
.word IRQ_vec ; IRQ vector
Written in 1976, Microsoft BASIC for the 8 bit MOS 6502 has been available for virtually every 6502-based computer. Also for the SBC’s on this site: KIM-1, SYM-1, AIM 65 and as a port of Applesoft on the Apple 1.
Binary versions and manuals are on the pages dedicated to these machines:
Build binaries from source on a Linux system (Raspberry PI OS)
First install CC65 package, the assembler and linker are required.
You need the CC65 package, a C and Macro assembler and linker for the 6502.
https://github.com/cc65/wiki/wiki is broken, https://cc65.github.io/getting-started.html is fine.
git clone https://github.com/cc65/cc65.git
cd cc65
make
sudo make avail
Now get the MS Basic source and assemble the binaries
https://github.com/mist64/msbasic
git clone https://github.com/mist64/msbasic
cd msbasic
./make.sh
cd tmp
ls
and you will see a directory of binaries (.bin), symbol table (.lbl) and object files (.o)
Compare the binary files with the binary files in the msbasic/orig folder and you will see hopefullyy they are identical!
It is not only nice to see the source, now you are able to customize a Microsoft Basic to your likings.
Steps as advised in the pagetable description:
1. Create a .cfg file by copying an existing one.
2. Adapt the make file for the new target.
3. Change the platform specific source files
An example is this post by Gordon Henderson who made a serial interfaced Commodore Basic by creating a new variant and tweaking some conditionals, replacing the screen editor with the line editing interface of older versions.
and perhaps other Replica’s with the Propeller IC. Report by Didier.
Didier has 2 replica, the Red one Ten, the older one green, both With a propelle which had the same problem but it occurred rarely
in fact apparently all the Replica 1 with the Propeller IC ten are affected more or less by this problem.
Issue
Users have reported every few seconds a “/” appears on their screen followed by a linefeed. This renders any data entry impossible.
The Replica 1 seems to act like an antenna, moving hands above the Replica can trigger it.
The problem is reported by Reactive Micro as Screen Noise Issue.
Try adding a 100k resistor to the USB module as pictured below. And if there is still noise then add a .1uF cap (100nF) to Pin28 of the Propelelr to either Ground or +5v.
As little as 10k can be used for +3.3v pullup, but anything smaller risks damage to the FTDI module. 100k is much safer in all regards. This helps hold the data line high. It seems the RX line is held high by default. And both lines are held high when connected to a USB data port, which is why the noise issue is not seen when connected to a PC. You can connect the resistor most simply to the USB module. Or to the rear of the PCB to pin 39 (Tx) and pin 12 or 32 (+3.3v) of the Propeller.
Fix by Didier
The Reactive Micro fix dows only reduce the noise but does not stop it completely.
But adding two 2 resistors definitively fix the problem.
The real problem is the floating lines STROBE and DA of the Propeller.
To really understand this noise bug you need to check at the same time:
the circuit diagram, the Wozmon initialization, and the Propeller code
the other modification are for a change from a PIA to a PIAT for my 6502 monitor
PIAT (6524) = PIA 6250 + TIMER (as it is mounted with my patch the PIAT replace totally the PIA
without any software change)
2 lines CA and STROBE are input at the same time…
they are acting like an antenna and capturing noise
for example, if I pass my hand 5 cm above the propeller
I start to see:
/
/
as if the replica was resetting.
In fact, when the replica received a full buffer of junk it jumps to reset code…
The problem comes from the propeller code…
STROBE is programmed sometimes as input and sometimes as output to permit both the PS/2 and ASCII keyboard
it is possible to fix the propeller code to avoid the parasite but in that case, you lose the ASCII keyboard
To fix that on the back of the board add a resistor of 10K between the STROBE PIN and GND
The same problem occurs for the DA line but it only happens during the time the machine was powered up but not yet reset… the same way a 10K resistor between DA PIN and GND fix the problem
The fix for STROBE and DA is therefore two resistors added on the back of the PIA.
Willem Aandewiel designed a tape device for the (micro)KIM. With a Wemos D1, ES8266 and ATTiny and some clever software to make the KIM believe a audio cassette recorder is connected.
All details here on Willem’s website.
Willem has now published the next generation, together with a 32K RAM card, of this device.
Demonstrates his design for a cassette interface for the Micro-KIM single board computer from Briel Computers (a replica of the 1970’s KIM-1 SBC). The original KIM-1 has a built-in cassette interface, but the Micro-KIM replica does not, so I designed and built his own. The design uses a single PIC micro-controller, is very reliable, supports all HyperTAPE speeds, and has the ability to save and play back recorded data into the KIM-1.