Audio tape hardware and ROM

The second RRIOT, named 6530-003, in the KIM-1 is nearly free to use by the user. The two parallel ports, the timer, the RAM, not used by the software.

But the ROM is used, it contains two routines to dump and load data on audio cassette tape. The hardware for this is connected to the 6530-002.

The hardware for audio out is simple, the software does all the work of producing audio tones at PB7, with some pulse shaping with a capacitor/resistor and a ‘low’
or ‘high’ amplitude output.

Audio in is a bit more complicated. The PLL LM565 Phase Locked Loop detects the frequency of incoming audio signals, the output of the PLL is converted to TTL level by the comparator LM311. This is is the only part of the KIM-1 hardware requiring 12V.
See here the datasheet of the LM565.

On pin7 a TTL signal is seen if there is a KIM-1 compatible audio signal is coming in.
Read Appendix E of the User manual to see how all this works.

The two routines, LOADT and DUMPT, are described on the next pages. The style of the programming is a different from the main ROM. It is better structured and easily readable.

Where the 6530-002 papertape routines use indexed zeropage addressing to load/save bytes in memory, the 6530-003 uses a so called Volatile Execution Block VEB in page 23.
A small routine is constructed there during the dump and load.

For DUMPT
AD yy xx 60 LDA xxyy RTS

for LOADT
8D yy xx 4C TAP STA xxyy XX JMP LOADT12

The address xxyy is incremented during the load or dump.

DUMPT and LOADT are completely independent of the monitor in the KIM 6530-002. So you have to start the routines via the GO command or key. The routines end with a call to START in the main routine, where an error condition is indicated with the current address: 0000 is succes, FFFF is error.

It would have been nice to integrate DUMPT and LOADT with command in the TTY loop for example and return via the normal exit routine, displaying ERR KIM as the papertape routines do.
Also making it callable as subroutine might have nice for programs like the KB9 MOS Technology Basic for the KIM or Focal instead of falling back to the KIM monitor.

Expanding the KIM-1

On the page KIM-1 Memory layout you can see the KIM-1 in its basic form of address decoding is limited to 8K address space, repeated 8 times in the 64K address space.
So the KIM-1 can boot from the KIM-1 ROM since address 17FA is the same as FFFA.

The basic memory decoder in the KIM-1

Simple expansion
A first memory expansion often used in the KIM-1 is to use K1, K2, K3 and K4. When you place RAM there, location 0400-13FF become available. The KIM-1 still sees 8K repeated in the 64K
My first expansion was made this way, four 1K SRAM cards made 4K RAM extra available. Great for Tiny Basic.
This is described in the Radio Bulletin article with the 1K B.E.M. static RAM card.

Chapter 6 of the User manual has good advice on the memory decoding.

If we want to expand the access of the 6502 to more than *K , we need to involve address lines A13, A14 and A15 and use DECODE ENABLE to limit K0-K7 to the first 8K of the address space..

This was my second expansion, as described in this article.
With 4K RAM cards, 2114 SRAM based.

You see here A13, A14, A15 connected to a 74145 used as 3 to 8 decoder. Again the 74145 is used for its open collector outputs.
This delivers signals 8K0 to 8K7, where 8K0 selects 0000-1FFF, 8K1 2000-3FFF and so on, 8K7 selects E000-EFFF.
By tying signal 8K0 to 8K7 we repeat 0000-1FFF to E000-FFFF and the KIM-1 ROM vector is mapped in for a RESET. So we gain access to 2000-DFFF for expansion.
The second and third 74145 is an example to deliver Kx signals for 4KB memory parts. I added 32K SRAM this way with 8 4K RAM cards.

If you want access to address space E000-FFFF, you have to provide ROM at FFFA-FFFF, or map this address space to 17FA-17FF.

The same principles are used in the Micro-KIM and PAL-1 RAM card. and the PAL-2 full decode.

PAL-1 and Micro-KIM RAM board

PAL-2 decoder

KIM-1 memory layout

The 6502 has an 16 bit address bus, so it can access 64KB of memory, made up of RAM, ROM and I/O devices.
Address decoding is the hardware that enables the devices in that memory space at the desired address.

The basic address decoding is made up of a 74145, a TTL IC that decodes 4 bit to 10 bits, here used as 3 to 8 decoder. Te 74145 has open collector outputs, so outputs can be tied together.

74145 function table

Memory decoder in the KIM-1

According the User manual this leads to the memory layout as shown above. Note the K0-K7 signals that are connected to the hardware devices such a RAM, and the RRIOTs.
The Kx signal covers a 1KB block of memory, a ‘Page’ is 256 bytes, 4 pages in a Kx block. So K0 addresses pages 0..3, K5 has page 23 which you will see mentioned in the source as the RAM locations in use by the monitor.

Incomplete memory decoding
The picture from the user manual above is a bit misleading. Since address A13, A14 and A15 are not included in the decoder, they are effectively ignored. The KIM-1 sees a maximum of 8K memory this way 0000-1FFF. Why still using 16 bit addresses? In fact since the higher address lines are ignored, address 0000 is also address 2000, 4000 etc with 8K steps to E000.
And the vectors at address 1FFA in the KIM ROM are also found at FFFA, so the RESET vector works. Simple and effective.
The KIM-1 in its basic form of address decoding is limited to 8K address space, repeated 8 times in the 64K address space.

DE Decode enable
The fourth input D of the 74145 is connected to pin A-K of the Application connector. For the basic address decoding as 3 to 8 decoder pin D of the 74145 has to be connected to ground.
This is the essential wire on the Application connector to let a KIM-1 function.
DE is essential for external devices to take over the 6502 address space and allow expansion utilizing the full address space.

Conclusion
The KIM-1 has access to a 8K memory space. Any address is truncated to 13 bits in hardware, so if you use an address 2000 and up it is mapped into 0000-1FFF.

Further reading
Chapter 6 of the User manual has good advice on the memory decoding.

On the next page, Expand the KIM-1, you will see how to add RAM, ROM and I/O to the KIM-1 and use the full address space, with full decode and using Decode Enable.

LED Display and keyboard

Routines to light LED display and checking for a key pressed by multiplexing the seven segment LED and checking if a key is pressed. Must be called in a loop, since the lighting is only for a short time.

This complex looking circuit is the magic that makes the LED Display and keyboard work.

Actually this is made up of two circuits: the multiplexed LED display and the keyboard matrix.

The multiplexed LED display

By multiplexing the lighting of the seven segment LED displays not much special hardware is required to have a hex keypad and 6 digit serviced.
All LED displays have the segment inputs connected to each other and Port A PA0-PA6 as output are connected to the led segments a-g, PA0 = a .. PA6 = g.
The 74145 outputs O4..)9 are connected to the cathodes of the corresponding LED display.
So by setting the PB1..PB4 inputs of the 74145 one LED display is lighted, the others are off. By lighting this for a small delay and then stepping to the next display all LEDs are lighted after each other. If this is done fast enough the slow human eyes will not see this as a flickering light.
This is what is happening in the SCAND(1) routine.















Reading the keyboard matrix

The keyboard matrix is also read out by multiplexing rows and columns checking for a short circuit if a key is pressed, via
PA0..6 as inputs and the outputs of the 74145 00-03.
By selecting row 0 to 3 (output 00 to 03 of the 74145) and reading PA0 to PA6 a pressed key is detected.
In the GETKEY routine a key is detected this way, debounced and converted to a key number 00.14.

PB1-4 to 74145 decoder

The 74145 serves the multiplexing. From the RRIOT Port B PB1 .. PB4 to A..D inputs decodes to 10 outputs 00..09.
00 – 03 Keyboard KB Row 0-3
04 – O9 outputs switch LED display 1..6 on/off one by one.
Also available on the Application connector!

Part of the keypad is the TTY/KB switch, connected via Application connector 21-V = PA0 to O3 KB Row 3 connected via switch/jumper

1030   1EFE             ;
1031   1EFE             ;       SUB TO DETERMINE IF KEY IS 
1032   1EFE             ;       DEPRESSED OR CONDITION OF SSW 
1033   1EFE             ;            KEY NOT DEP OR TTY MODE     A=0
1034   1EFE             ;            KEY DEP OR KB MODE      A NOT ZERO
1035   1EFE             ;
1036   1EFE             ;
1037   1EFE A0 03       AK      LDY   #$03       ;  3 ROWS
1038   1F00 A2 01               LDX   #$01       ; DIGIT 0
1039   1F02             ;
1040   1F02 A9 FF       ONEKEY  LDA   #$FF
1041   1F04 8E 42 17    AK1     STX   SBD        ;  OUTPUT DIGIT
1042   1F07 E8                  INX              ;  GET NEXT DIGIT
1043   1F08 E8                  INX   
1044   1F09 2D 40 17            AND   SAD        ; INPUT SEGMENTS
1045   1F0C 88                  DEY   
1046   1F0D D0 F5               BNE   AK1
1047   1F0F                     
1048   1F0F A0 07               LDY   #$07
1049   1F11 8C 42 17            STY   SBD
1050   1F14             ;
1051   1F14 09 80               ORA   #$80
1052   1F16 49 FF               EOR   #$FF
1053   1F18 60                  RTS  

What is happening here?

  • Three rows, start with first digit 0 (1037-1040)
    • Select digit by setting 74145 to O(X) via PB1-4 (1041)
    • Check if key pressed, A <> 0 (1044)
    • next row until all rows done (1045-1046
  • restore default PB1 and PB2 1, PB3, PB4 0: Os low
  • return
    A=0 if key not depressed or TTY mode
    A<>0 if key depressed or KB mod

SCAND
show digits for a short time form current cell address and contents

1054   1F19             ;		
1055   1F19             ;       SUB OUTPUT TO 7-SEGMENT DISPLAY **
1056   1F19             ;
1057   1F19 A0 00       SCAND   LDY   #$00       ; GET DATA SPECIFIED 
1058   1F1B B1 FA               LDA   (POINTL),Y ; BY POINT
1059   1F1D 85 F9               STA   INH        ; SET UP DISPLAY BUFFER
1060   1F1F A9 7F               LDA   #$7F       ; CHANGE SEG
1061   1F21 8D 41 17            STA   PADD       ; TO OUTPUT
1062   1F24             ;		
1063   1F24 A2 09               LDX   #$09       ; INIT DIGIT NUMBER
1064   1F26 A0 03               LDY   #$03       ; OUTPUT 3 BYTES
1065   1F28             ;
1066   1F28 B9 F8 00    SCAND1  LDA   INL,Y      ; GET BYTE
1067   1F2B 4A                  LSR   A          ; GET MSD
1068   1F2C 4A                  LSR   A
1069   1F2D 4A                  LSR   A
1070   1F2E 4A                  LSR   A
1071   1F2F 20 48 1F            JSR   CONVD      ; OUTPUT CHAR
1072   1F32 B9 F8 00            LDA   INL,Y      ; GET BYTE AGAIN
1073   1F35 29 0F               AND   #$0F       ; GET LSD
1074   1F37 20 48 1F            JSR   CONVD      ; OUTPUT CHAR
1075   1F3A 88                  DEY              ; SET UP FOR NEXT BYTE
1076   1F3B D0 EB               BNE   SCAND1
1077   1F3D 8E 42 17            STX   SBD        ; ALL DIGITS OFF
1078   1F40 A9 00               LDA   #$00       ; CHANGE SEGMENT
1079   1F42 8D 41 17            STA   PADD       ; TO INPUTS
1080   1F45 4C FE 1E            JMP   AK         ; GET ANY KEY

SCAND display four digits of address and two digits of content.
3 bytes from F9..FA are converted to hex on the six digits.

What is happening here?

  • load current cell FA, FB to display buffer INH (1057..1058)
  • X = 9 is selection of digit number PB1..PB4 (04..09 of 74145)
  • Y = 3, number of bytes
    • load low part of byte (1066..1070)
    • display via CONVD (1071)
    • load high part of bye
    • display via CONVD (1074)
    • do next byte (1075..1076)
  • set all displays off PB1..PB4= 0 (1077)
  • PA0..PA6 to inputs (1078..1079)
  • return via AK (1080)

CONVD
Lights segment of current select digit for a short time.
Segments output via PA0..PA6.
Hex to segment conversion via TABLE lookup
Digit value in Y
X is digit number in PB1..PB4 format

1081   1F48             ; 		
1082   1F48             ;       CONVERT AND DISPLAY HEX 
1083   1F48             ;       USED BY SCAND ONLY
1084   1F48             ;
1085   1F48 84 FC       CONVD   STY   TEMP       ; SAVE Y
1086   1F4A A8                  TAY              ; USE CHAR AS INDEX
1087   1F4B B9 E7 1F            LDA   TABLE,Y    ; LOOKUP CONVERSION
1088   1F4E A0 00               LDY   #$00       ; TURN OFF SEGMENTS
1089   1F50 8C 40 17            STY   SAD       
1090   1F53 8E 42 17            STX   SBD        ; OUTPUT DIGIT ENABLE
1091   1F56 8D 40 17            STA   SAD        ; OUT PUT SEGMENTS
1092   1F59             		
1093   1F59 A0 7F               LDY   #$7F       ; DELAY 500 CYCLES APPROX.
1094   1F5B 88          CONVD1  DEY  
1095   1F5C D0 FD               BNE   CONVD1
1096   1F5E             	;
1097   1F5E E8                  INX              ; GET NEXT DIGIT NUM
1098   1F5F E8                  INX              ; ADD 2
1099   1F60 A4 FC               LDY TEMP  ; RESTORE Y
1100   1F62 60                  RTS

What is happening here?

  • convert hex to segment via TABLE lookup (1086 .. 1087)
  • turn off all segments PA0..PA6 (1088..1089)
  • enable digit via SBD = X (1090)
  • light segment via SAD = A, keep PA7 to 1 (1091)
  • delay some time (1093..1095
  • X = next display 2 hex per hex byte (1097)

[/code]

GETKEY
Get key pressed:
– Key pressed: A is key number
– No key: A = 15

Key values are:
0..9 = $00 ..$09
AD = $10 address mode
DA = $11 data mode
+ = $12 step
GO = $13 GO execute
PC = $14 PC mode

1108   1F6A             ;
1109   1F6A             ;       GET KEY FROM KEY BOARD 
1110   1F6A             ;       RETURN WITH A=KEY VALUE
1111   1F6A             ;       A GT. 15 TEHN ILLEGAL OR NO KEY
1112   1F6A             ;
1113   1F6A             ;
1114   1F6A A2 21       GETKEY  LDX   #$21       ; START AT DIGIT 0 
1115   1F6C A0 01       GETKE5  LDY   #$01       ; GET 1 ROW
1116   1F6E 20 02 1F            JSR   ONEKEY
1117   1F71 D0 07               BNE   KEYIN      ; A=0 NO KEY
1118   1F73 E0 27               CPX   #$27       ; TEST FOR DIGIT 2
1119   1F75 D0 F5               BNE   GETKE5
1120   1F77 A9 15               LDA   #$15       ; 15=NOKEY
1121   1F79 60                  RTS   

; key pressed

1122   1F7A A0 FF       KEYIN   LDY   #$FF
1123   1F7C 0A          KEYIN1  ASL   A          ; SHIFT LEFT
1124   1F7D B0 03               BCS   KEYIN2     ; UNTIL Y=KEY NUM
1125   1F7F C8                  INY   
1126   1F80 10 FA               BPL   KEYIN1
1127   1F82 8A          KEYIN2  TXA   
1128   1F83 29 0F               AND   #$0F       ; MASK MSD
1129   1F85 4A                  LSR   A          ; DIVIDE BY 2
1130   1F86 AA                  TAX   
1131   1F87 98                  TYA   
1132   1F88 10 03               BPL   KEYIN4
1133   1F8A 18          KEYIN3  CLC   
1134   1F8B 69 07               ADC   #$07       ; MULT (X-1) TIMES A
1135   1F8D CA          KEYIN4  DEX   
1136   1F8E D0 FA               BNE   KEYIN3
1137   1F90 60                  RTS
1138   1F91             ;

What is happening here?

  • check if key pressed (1114..1119)
  • return with $15 if none (1120)
  • calculate key number 00..14 from position in matrix (1122..1136)

TABLE
HEX to 7 segment lookup table, HEX number 0..F to segment a..g

bit 0 = a
bit 1 = b
bit 2 = c
bit 3 = d
bit 4 = e
bit 5 = f
bit 6 = g
bit 7 = 1 to keep PA7, the TTY output to 1

Seven segment layout
   a              
  ---
f| g  | b
  ---
e|    | c
  ---
   d

Examples
hex 0 is all 1 except g:    1011 1111 = $BF
hex A is all 1 except d:    1111 0111 = $F7
hex F is all 1 except b,c,d 1111 0001 = $F1
1200   1FE7             ;       TABLE HEX TO 7 SEGMENT
1201   1FE7             ;              0   1   2   3   4   5   6   7  
1202   1FE7 BF 86 DB CF TABLE   .BYTE  $BF,$86,$DB,$CF,$E6,$ED,$FD,$87
1202   1FEB E6 ED FD 87 
1203   1FEF             ;              8   9   A   B   C   D   E   F   
1204   1FEF FF EF F7 FC         .BYTE  $FF,$EF,$F7,$FC,$B9,$DE,$F9,$F1
1204   1FF3 B9 DE F9 F1 
1205   1FF7             ;

The following two pages from the First Book of KIM are also interesting to see what is happening here and how to expand the routine with a larger amount of characters to show on the display.


Main LED display and keyboard

Command execution from hex keyboard

0654   1C77             ;       MAIN ROTINE FOR KEY BOARD 
0655   1C77             ;       AND DISPLAY   
0656   1C77             ;
0657   1C77 20 19 1F    TTYKB   JSR   SCAND      ; IF A=0 NO KEY 
0658   1C7A D0 D3               BNE   START
0659   1C7C A9 01       TTYKB1  LDA   #$01
0660   1C7E 2C 40 17            BIT   SAD
0661   1C81 F0 CC               BEQ   START
0662   1C83 20 19 1F            JSR   SCAND
0663   1C86 F0 F4               BEQ   TTYKB1
0664   1C88 20 19 1F            JSR   SCAND
0665   1C8B F0 EF               BEQ   TTYKB1
0666   1C8D             ;		      
0667   1C8D 20 6A 1F            JSR   GETKEY
0668   1C90 C9 15               CMP   #$15
0669   1C92 10 BB               BPL   START
0670   1C94 C9 14               CMP   #$14
0671   1C96 F0 44               BEQ   PCCMD      ; DISPLAY PC
0672   1C98 C9 10               CMP   #$10       ; ADDR MODE=1
0673   1C9A F0 2C               BEQ   ADDRM
0674   1C9C C9 11               CMP   #$11       ; DATA MODE=1
0675   1C9E F0 2C               BEQ   DATAM
0676   1CA0 C9 12               CMP   #$12       ; STEP
0677   1CA2 F0 2F               BEQ   STEP
0678   1CA4 C9 13               CMP   #$13       ; RUN
0679   1CA6 F0 31               BEQ   GOV
0680   1CA8 0A                  ASL   A          ; SHIFT CHAR INTO HIGH
0681   1CA9 0A                  ASL   A          ; ORDER NIBBLE
0682   1CAA 0A                  ASL   A
0683   1CAB 0A                  ASL   A
0684   1CAC 85 FC               STA   TEMP       ; STORE IN TEMP
0685   1CAE A2 04               LDX   #$04
0686   1CB0 A4 FF       DATA1   LDY   MODE       ; TEST MODE 1=ADDR
0687   1CB2 D0 0A               BNE   ADDR       ; MODE=0 DATA
0688   1CB4 B1 FA               LDA  (POINTL),Y  ; GET DATA       
0689   1CB6 06 FC               ASL   TEMP       ; SHIFT CHAR
0690   1CB8 2A                  ROL   A          ; SHIFT DATA
0691   1CB9 91 FA               STA   (POINTL),Y ; STORE OUT DATA
0692   1CBB 4C C3 1C            JMP   DATA2
0693   1CBE             ;            
0694   1CBE 0A          ADDR    ASL   A          ; SHIFT CHAR
0695   1CBF 26 FA               ROL   POINTL     ; SHIFT ADDR
0696   1CC1 26 FB               ROL   POINTH     ; SHIFT ADDR HI
0697   1CC3 CA          DATA2   DEX  
0698   1CC4 D0 EA               BNE   DATA1      ; DO 4 TIMES
0699   1CC6 F0 08               BEQ   DATAM2     ; EXIT HERE
0700   1CC8             ;		
0701   1CC8 A9 01       ADDRM   LDA   #$01
0702   1CCA D0 02               BNE   DATAM1
0703   1CCC             ;
0704   1CCC A9 00       DATAM   LDA   #$00
0705   1CCE 85 FF       DATAM1  STA   MODE
0706   1CD0 4C 4F 1C    DATAM2  JMP   START
0707   1CD3             ;            
0708   1CD3 20 63 1F    STEP    JSR   INCPT                              
0709   1CD6 4C 4F 1C            JMP   START
0710   1CD9             ;
0711   1CD9 4C C8 1D    GOV     JMP   GOEXEC                              
0712   1CDC             ;
0713   1CDC             ;
0714   1CDC             ;       DISPLAY PC BY MOVING 
0715   1CDC             ;       PC TO POINT   
0716   1CDC             ;
0717   1CDC A5 EF       PCCMD   LDA   PCL                                 
0718   1CDE 85 FA               STA   POINTL
0719   1CE0 A5 F0               LDA   PCH
0720   1CE2 85 FB               STA   POINTH
0721   1CE4 4C 4F 1C            JMP   START

0714   1CDC             ;       DISPLAY PC BY MOVING 
0715   1CDC             ;       PC TO POINT   
0716   1CDC             ;
0717   1CDC A5 EF       PCCMD   LDA   PCL                                 
0718   1CDE 85 FA               STA   POINTL
0719   1CE0 A5 F0               LDA   PCH
0720   1CE2 85 FB               STA   POINTH
0721   1CE4 4C 4F 1C            JMP   START

What is happening here?

    check if TTY and light display (657 .. 665)
    get key

      key 15 and above exit 667..669)
      key 14 do display PC with PCCMD, move PC into current address (670..671, 717 721))
      key 13 do GOV jump GOEXEC
      key 12 do STEP, increment current address (708)
      key 11 do mode data (680-721)
      key 10 do mode address (680-721)
      key 0..9 shift key into display from right and update current cell

Save to papertape format

The TTY command Q dumps a MOS Technology papertape format to the console.

This requires setting up the end address at 17F7 and 17F8 and selecting the startadres as the current address (POINTL, POINH).

Note that the record count is always $18, so the dump continues beyond the end address specified!

Example run

KIM
0000 00 17F7
17F7 00 FF.
17F8 00 02.
17F9 00 200
0200 00 Q
;180200000000000000000000000000000000000000000000000000001A
;1802180000000000000000000000000000000000000000000000000032
;180230000000000000000000000000000000000000000000000000004A
;1802480000000000000000000000000000000000000000000000000062
;180260000000000000000000000000000000000000000000000000007A
;1802780000000000000000000000000000000000000000000000000092
;18029000000000000000000000000000000000000000000000000000AA
;1802A800000000000000000000000000000000000000000000000000C2
;1802C000000000000000000000000000000000000000000000000000DA
;1802D800000000000000000000000000000000000000000000000000F2
;1802F0000000000000000000000000000000000000000000000000010A
;00000B000B
0000 00

The papertape format is described on the Load papertape format.

0866   1DF3 C9 51               CMP   #&#039;Q&#039;       ; DUMP FROM OPEN CELL TO HI LIMIT
0867   1DF5 F0 0A               BEQ   DUMPV

0873   1E01 4C 42 1D    DUMPV   JMP   DUMP


0778   1D42 A9 00       DUMP    LDA   #$00                                
0779   1D44 85 F8               STA   INL
0780   1D46 85 F9               STA   INH        ; CLEAR RECORD COUNT
0781   1D48 A9 00       DUMP0   LDA   #$00
0782   1D4A 85 F6               STA   CHKHI      ; CLEAR CHKSUM
0783   1D4C 85 F7               STA   CHKSUM
0784   1D4E             ;
0785   1D4E 20 2F 1E            JSR   CRLF       ; PRINT CR LF
0786   1D51 A9 3B               LDA   #$3B       ;  PRINT SEMICOLON
0787   1D53 20 A0 1E            JSR   OUTCH
0788   1D56 A5 FA               LDA   POINTL     ; TEST POINT GT OR ET
0789   1D58 CD F7 17            CMP   EAL        ;  HI LIMIT GOTO EXIT
0790   1D5B A5 FB               LDA   POINTH
0791   1D5D ED F8 17            SBC   EAH
0792   1D60 90 18               BCC DUMP4
0793   1D62             ;		
0794   1D62 A9 00               LDA   #$00       ;  PRINT LAST RECORD
0795   1D64 20 3B 1E            JSR   PRTBYT     ; 0 BYTES
0796   1D67 20 CC 1F            JSR   OPEN
0797   1D6A 20 1E 1E            JSR   PRTPNT
0798   1D6D             ;
0799   1D6D A5 F6               LDA   CHKHI      ; PRINT CHKSUM
0800   1D6F 20 3B 1E            JSR   PRTBYT     ; FOR LAST RECORD
0801   1D72 A5 F7               LDA   CHKSUM
0802   1D74 20 3B 1E            JSR   PRTBYT
0803   1D77 4C 64 1C            JMP   CLEAR
0804   1D7A             ;             
0805   1D7A A9 18       DUMP4   LDA   #$18       ; PRINT 24 BYTE COUNT 
0806   1D7C AA                  TAX              ; SAVE AS INDEX
0807   1D7D 20 3B 1E            JSR   PRTBYT
0808   1D80 20 91 1F            JSR   CHK
0809   1D83 20 1E 1E            JSR   PRTPNT
0810   1D86             ;		
0811   1D86 A0 00       DUMP2   LDY   #$00       ; PRINT 24 BYTES
0812   1D88 B1 FA               LDA   (POINTL),Y ; GET DATA
0813   1D8A 20 3B 1E            JSR   PRTBYT     ; PRINT DATA
0814   1D8D 20 91 1F            JSR   CHK        ; COMP CHKSUM
0815   1D90 20 63 1F            JSR   INCPT      ; INCREMENT POINT
0816   1D93 CA                  DEX   
0817   1D94 D0 F0               BNE   DUMP2
0818   1D96             ;
0819   1D96 A5 F6               LDA   CHKHI      ; PRINT CHKSUM
0820   1D98 20 3B 1E            JSR   PRTBYT
0821   1D9B A5 F7               LDA   CHKSUM
0822   1D9D 20 3B 1E            JSR   PRTBYT
0823   1DA0 E6 F8               INC   INL        ; INCR RECORD CNT
0824   1DA2 D0 02               BNE   DUMP3
0825   1DA4 E6 F9               INC   INH
0826   1DA6 4C 48 1D    DUMP3   JMP   DUMP0

1184   1FCC             ;
1185   1FCC A5 F8       OPEN    LDA   INL        ; MOVE I/O BUFFER TO POINT
1186   1FCE 85 FA               STA   POINTL
1187   1FD0 A5 F9               LDA   INH        ; TRANSFER INH- POINTH
1188   1FD2 85 FB       	STA   POINTH
1189   1FD4 60          	RTS   

0917   1E3B 85 FC       PRTBYT  STA   TEMP                             
0918   1E3D 4A                  LSR   A           ; SHIFT CHAR RIGHT 4 BITS
0919   1E3E 4A                  LSR   A
0920   1E3F 4A                  LSR   A
0921   1E40 4A                  LSR   A
0922   1E41 20 4C 1E            JSR   HEXTA       ; CONVERT TO HEX AND PRINT
0923   1E44 A5 FC               LDA   TEMP        ; GET OTHER HALF
0924   1E46 20 4C 1E            JSR   HEXTA       ; CONVERT TO HEX AND PRINT
0925   1E49 A5 FC               LDA   TEMP        ; RESTORE BYTE IN A AND RETURN
0926   1E4B 60                  RTS   
0927   1E4C             ;
0928   1E4C 29 0F       HEXTA   AND   #$0F        ; MASK HI 4 BITS
0929   1E4E C9 0A               CMP   #$0A
0930   1E50 18                  CLC   
0931   1E51 30 02               BMI   HEXTA1
0932   1E53 69 07               ADC   #$07        ; ALPHA HEX
0933   1E55 69 30       HEXTA1  ADC   #$30        ; DEC HEX
0934   1E57 4C A0 1E            JMP   OUTCH       ; PRINT CHAR

1139   1F91             ;       SUB TO COMPUTE CHECKSUM 
1140   1F91             ;
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

0895   1E1E             ;       SUB TO PRINT POINTL,POINTH
0896   1E1E             ;
0897   1E1E A5 FB       PRTPNT  LDA   POINTH     ; PRINT POINTL, POINTH 
0898   1E20 20 3B 1E            JSR   PRTBYT
0899   1E23 20 91 1F            JSR   CHK
0900   1E26 A5 FA               LDA   POINTL
0901   1E28 20 3B 1E            JSR   PRTBYT
0902   1E2B 20 91 1F            JSR   CHK
0903   1E2E 60                  RTS

1102   1F63             ;       SUB TO INCREMENT POINT
1103   1F63             ;
1104   1F63 E6 FA       INCPT   INC   POINTL
1105   1F65 D0 02               BNE   INCPT2
1106   1F67 E6 FB               INC   POINTH
1107   1F69 60          INCPT2  RTS

What is happening here?

DUMP

  • clear record count and checksum per record (778 to 783
  • print CRLF and ‘;’ (785-787),
    note that CRLF is followed by six Null characters $00
  • check if end address is reached (subtract POINTL, POINTH for EAL EAH), then dump last record at 794)
    • print 00
    • print record count (796 – 797)
    • print checksum (799-802)
    • return to monitor at CLEAR, at 0000 (803)
  • write record contents
    • print $18 byte count and add to checksum (805-809)
    • get and print databytes in a loop (811-817) and add to checksum
    • print checksum

PRTBYT

  • hift off high part of byte (917-921)
  • convert to two hex characters and print (922)
  • convert low part to two hex chars and print (923 926)

HEXTA

  • mask off high 4 bits
  • if A..F add $07 (932)
  • add $30 to make ASCII (929, 933)
  • print character via OUTCH

Print string, the NULL character and X-OFF

There is a simple Print string routine PRTSTR (and CRLF)
In fact, it is one long string with text ‘KIM’ and ‘ERR’ and a CR and LF. and six trailing Null ($) characters and X-OFF characters.
The Null characters are to let the slow mechanic Teletype machine handle the incoming characters.
And there is the X-OFF($13) character, which stops printing papertape on a teletype.

The code parts where PRTSTR and CRLF are called are:

0636   1C4F  20 8C 1E           START   JSR   INIT1                              
 ..
0640   1C59 20 2F 1E            JSR   CRLF       ; PRT CR LF
0641   1C5C A2 0A               LDX   #$0A       ; TYPE OUT KIM
0642   1C5E 20 31 1E            JSR   PRTST
0643   1C61 4C AF 1D            JMP   SHOW1

0764   1D2E A2 0C               LDX   #$0C       ; X-OFF KIM
..
0767   1D35 20 31 1E            JSR   PRTST

0778   1D42 A9 00       DUMP    LDA   #$00                                
0779   1D44 85 F8               STA   INL
0780   1D46 85 F9               STA   INH        ; CLEAR RECORD COUNT
0781   1D48 A9 00       DUMP0   LDA   #$00
0782   1D4A 85 F6               STA   CHKHI      ; CLEAR CHKSUM
0783   1D4C 85 F7               STA   CHKSUM
0784   1D4E             ;
0785   1D4E 20 2F 1E            JSR   CRLF       ; PRINT CR LF
0786   1D51 A9 3B               LDA   #$3B       ;  PRINT SEMICOLON

0828   1DA9 20 CC 1F    SPACE   JSR   OPEN       ; OPEN NEW CELL 1
0829   1DAC 20 2F 1E    SHOW    JSR   CRLF       ; PRINT CR LF
0830   1DAF 20 1E 1E    SHOW1   JSR   PRTPNT
0831   1DB2 20 9E 1E            JSR   OUTSP      ; PRT SPACE
0832   1DB5 A0 00               LDY   #$00       ; PRINT DATA SPECIFIED
0833   1DB7 B1 FA               LDA   (POINTL),Y ; BY POINT AD = LDA EXT
0834   1DB9 20 3B 1E            JSR   PRTBYT
0835   1DBC 20 9E 1E            JSR   OUTSP      ; PRT SPACE
0836   1DBF 4C 64 1C            JMP   CLEAR

The PRTSTR routine prints first six Null characters and depending on the X register value:
CRLF = $OD $0A $00 $00 $00 $00 $00 $00
KIM = $13 KIM $0D $0A $00 $00 $00 $00 $00 $00
ERR KIM = $13 ERR $13 KIM $00 $0D $0A $00 $00 $00 $00 $00 $00

0905   1E2F             ;       PRINT STRING OF ASCII CHAR FROM 
0906   1E2F             ;       TOP+X TO TOP
0907   1E2F             ;
0908   1E2F A2 07       CRLF    LDX   #$07
0909   1E31 BD D5 1F    PRTST   LDA   TOP,X
0910   1E34 20 A0 1E            JSR   OUTCH
0911   1E37 CA                  DEX   
0912   1E38 10 F7               BPL   PRTST       ; STOP ON INDEX ZERO
0913   1E3A 60                  RTS

1195   1FD5             ;		TABLES
1196   1FD5             ; 
1197   1FD5 00 00 00 00 TOP     .BYTE  $00, $00, $00, $00, $00, $00, $0A, $0D, "MIK"          
1197   1FD9 00 00 0A 0D 
1197   1FDD 4D 49 4B 
1198   1FE0 20 13 52 52         .BYTE  ' ',$13, "RRE", ' ', $13               
1198   1FE4 45 20 13 

What is happening here?

The KIM-1 monitor types out three strings.
X=07 types CRLF
X=0C type KIM CRLF (at startup)
X=11 type ERR KIM CRLF (when papertape loading ends in in error)
all with trailing 6x 00
KIM and ERR are both preceeded by a X-OFF($13) character, which stops printing papertape on a teletype.

The subroutine PRTSTR
– fetches a character from the table at offset in X
– prints the character via OUTCH
– decrements X, back one character in the table
– and loops until X = 0 and the string printed

NMI and IRQ and BRK

NMI, BRK and IRQ handling

The 6502 NMI interrupt is available as ST key on the hex keyboard via a NE556 debounce circuit. As is the RESET hardware interrupt
The IRQ line is unconnected (quiet via a resistor pull)

The 6502 vectors are, mapped to FFF7 – if the KIM-1 is unexpanded.

1210   1FF7             ;       ** INTERRUPT VECTORS **
1211   1FFA             
1212   1FFA              
1213   1FFA 1C 1C       NMIENT  .WORD NMIT
1214   1FFC 22 1C       RSTENT  .WORD RST
1215   1FFE 1F 1C       IRQENT  .WORD IRQT

For RESET handling see here.

0602   1C1C             ;             
0603   1C1C 6C FA 17    NMIT    JMP   (NMIV)     ; NON-MASKABLE INTERRUPT TRAP 
0604   1C1F 6C FE 17    IRQT    JMP   (IRQV)     ; INTERRUPT TRAP 

These entries are not initialized by the KIM-1 monitor, that is up to the user.

The interrupt handler, saves all CPU registers at the relevant zeropage locations and restarts the KIM- monitor.

0069   00EF                        .ORG $00EF      
0070   00EF             ;       	MPU REG.  SAVX AREA IN PAGE 0   
0071   00EF             ;
0072   00EF             PCL     .BLOCK  1          ; PROGRAM CNT LOW
0073   00F0             PCH     .BLOCK  1          ; PROGRAM CNT HI
0074   00F1             PREG    .BLOCK  1          ; CURRENT STATUS REG.
0075   00F2             SPUSER  .BLOCK  1          ; CURRENT STACK POINT
0076   00F3             ACC     .BLOCK  1          ; ACCUMULATOR
0077   00F4             YREG    .BLOCK  1          ; Y INDEX
0078   00F5             XREG    .BLOCK  1          ; X INDEX

0587   1C00 85 F3       SAVE    STA   ACC        ; KIM ENTRY VIA STOP (NMI) 
0588   1C02 68                  PLA              ; OR BRK (IRQ)
0589   1C03 85 F1               STA   PREG
0590   1C05 68                  PLA              ; KIM ENTRY VIA JSR (A LOST) 
0591   1C06 85 EF               STA   PCL
0592   1C08 85 FA               STA   POINTL
0593   1C0A 68                  PLA   
0594   1C0B 85 F0               STA   PCH
0595   1C0D 85 FB               STA   POINTH
0596   1C0F 84 F4               STY   YREG
0597   1C11 86 F5               STX   XREG
0598   1C13 BA                  TSX   
0599   1C14 86 F2               STX   SPUSER
0600   1C16 20 88 1E            JSR   INITS
0601   1C19 4C 4F 1C            JMP   START

What is happening here?

  • The SAVE interrupt handler stores all relevant CPU registers to zeropage. So these can be inspected or altered and will be restored if a GO command is given (see the GOEXEC routine)
  • The NMI and IRQ/BRK is handled via the vectors at 17FA/FB. These are not initialized by the KIM-1 satrtup routines!
  • To use the NMI line one must fill the NMI vector at 17FA. For SST and ST key this has to hold $1C00, the SAVE routine.
  • To use a hardware IRQ or a BRK instruction the IRQ vector at 17FE has to be filled, SAVE $1C00 is the recommended vector.

TTY DELAY routines

After RESET the baudrate is determined by measuring the length of the start bit of an incoming serial character. This means any character is usable where the first data bit is the opposite of the start bit. The KIM-1 User manual suggest RUBOUT ($7F 1111111) but ENTER ($0D 0000 1101) also works fine. SPACE ($20 0010 0000) for example does not work, any character with an odd value is OK.

0612   1C2A A9 FF               LDA   #$FF       ; COUNT START BIT
0613   1C2C 8D F3 17            STA   CNTH30     ; ZERO CNTH30
0614   1C2F A9 01               LDA   #$01       ; MASK HI ORDER BITS
0615   1C31 2C 40 17    DET1    BIT   SAD        ; TEST 
0616   1C34 D0 19               BNE   START      ; KEYBD SSW TEST
0617   1C36 30 F9               BMI   DET1       ; START BIT TEST
0618   1C38 A9 FC               LDA   #$FC
0619   1C3A 18          DET3    CLC              ; THIS LOOP COUNTS 
0620   1C3B 69 01               ADC   #$01       ; THE START BIT TIME
0621   1C3D 90 03               BCC   DET2
0622   1C3F EE F3 17            INC   CNTH30
0623   1C42 AC 40 17    DET2    LDY   SAD        ; CHECK FOR END OF START BIT 
0624   1C45 10 F3               BPL   DET3
0625   1C47 8D F2 17            STA   CNTL30
0626   1C4A A2 08               LDX   #$08
0627   1C4C 20 6A 1E            JSR   GET5       ; GET REST OF THE CHAR, 
0628   1C4F                                      ; TEST CHAR HERE

What is happening here?

– bit 7 (PB7) is tested until it becomes 0 (BIT SAD and BMI DET1 loop)
– the time is counted and kept in CNTH30 and CNTL30
– bit 7 is tested for becoming 1 (LDY SAD and BPL DET3)
– the rest of the character is read in by jumping into GETCH , the actual character received is not tested.

1006   1ED4             ;		
1007   1ED4             ;       DELAY 1 BIT TIME   
1008   1ED4             ;       AS DETERMINED BY DETCPS
1009   1ED4             ;
1010   1ED4 AD F3 17    DELAY   LDA   CNTH30     ; THIS LOOP SIMULATES 
1011   1ED7 8D F4 17            STA   TIMH       ; DETCPS SECTIONS AND WILL DELAY
1012   1EDA AD F2 17            LDA   CNTL30     ; 1 BIT TIME
1013   1EDD 38          DE2     SEC   
1014   1EDE E9 01       DE4     SBC   #$01
1015   1EE0 B0 03               BCS   DE3
1016   1EE2 CE F4 17            DEC   TIMH
1017   1EE5 AC F4 17    DE3     LDY   TIMH
1018   1EE8 10 F3               BPL   DE2
1019   1EEA 60                  RTS
1020   1EEB             ;		
1021   1EEB             ;                          DELAY 1/2 BIT TIME   
1022   1EEB AD F3 17    DEHALF  LDA   CNTH30     ; DOUBLE RIGHT SHIFT OF DELAY                              
1023   1EEE 8D F4 17            STA   TIMH       ; CONSTANT FOR A DIVE 2 
1024   1EF1 AD F2 17            LDA   CNTL30
1025   1EF4 4A                  LSR   A
1026   1EF5 4E F4 17            LSR   TIMH
1027   1EF8 90 E3               BCC   DE2
1028   1EFA 09 80               ORA   #$80
1029   1EFC B0 E0               BCS   DE4

The actual delay routines use the same logic as DETCPS. IN DEHALF the delay time is divided by 2 and jumped into DELAY.

SPEED CONTROL OF KIM-1 TTY PORT
KIM User notes Vol 06

If you are having problems trying to use a high speed terminal with the KIM YTY port the following information might be useful. To start with, the Reset/ Rubout sequence activates a subroutine called DETCPS (1C2AH to IC4EH)
which determines two constants CNTL30 (17F2N1 and CNTH30 (12F3H). These are used to time the serial TTY port via subroutines DELAY (1E4DH to IEEAH) and DEHALF (1EEBH to 1EFDH). DELAY and DEHALF are called by GETCH (
1E5AH to 1E87H) which inputs one character, and OUTCH (1EA0H to 1ED3H1) which outputs one character.

CNTL30 and CNTH30 are the whole key to trimming up the TTY port speed. You can change the baudrate without going through the Reset/Rubout sequence simply by changing one or both of these constants.
The following list gives the values of CNTL30 and CNTH30 for several speeds as determined by my particular KIM-I Reset/Rubout sequence.

BAUD   110  150  300  600  1200 1800 2400 4800 9600 
CNTL30 85H  D8H  EBH  74H  38H  24H  1AH  06H  03H 
CNTH30 02H  01H  OOH  00H  00H  00H  00H  00H  00H 

Now for the interesting part. At lower speeds (110 through 1800 Baud) everything works fine with these values. But, at higher speeds problems arise.
Repeated Reset/Rubout sequences kept producing CNTL30 = 1AH for 2400 Baud. This value did work for most functions like examine and fill memory. however, for tape dump (Q command) intermittent characters were lost.
This problem was eliminated by substituting either 18H or 19H In CNTL30. All functions worked perfectly for either value.
At 4800 Baud I was unable to use the terminal at all. It acted more like it wanted to work at CNTL30 = 07H rather than at 06H, but no value between 05H and OAH would make it work.
At 9600 Baud the slow functions like examine and fill would work, but tape dump (Q) resulted in severe loss of characters.
The problem at these speeds is that the quantization level between allowed speeds (i.e. integer values of CNTL30) is so large that unless you are lucky you will not hit close enough to your terminal speed.
Since CNTL30 as used in a software timing loop to count instruction sequences (thus machine cycles) the high Baud can be fine tuned with the system clock.
You should be able to use the technique described by R. H. Burhans (page 10, issue #5. May 77, KIM-1/6502 Users Notes) to perform this fine tuning.

For casual use, 2400 Baud (or maybe even 110 Baud) is satisfactory with no hardware modifications.
However, if you have a dedicated high speed terminal you may find it worthwhile to fine tune either the KIM-1 clock or the terminal clock.
This will allow you to utilise the KIM-1 software (like GETCH and OUTCH) at high speeds. After you become accustomed to it. 9600 Baud is nice. 2400 ok, 1200 a bit of a drag. and anything less unbearable.
Finally, since CNTL30 • 03H (greater than zero) for 9600 Baud you should be able to fine tune for 19,200 Baud. and maybe (but probably not) for 38,400 Baud. Dwight D. Egbert

Jim mcClahanan notes

The PAL-1 (just like the KIM-1) uses a ‘soft UART’ or ‘bit banger’ for its serial I/O. I’m not a fan of this approach, but at the same time it demonstrates what could be accomplished with a minimial amount of hardware. The PAL-1 automatically figures out the appropriate delay between bits of the serial character when you press enter after a reboot. I have found that decreasing the value actually significantly improves the odds of an error-free load of larger punchtape format files. Below is a table for values found and suggest for $17F2.

Baud    Found   New
300     $EA     $E8
1200    $37     $35
2400    $1A     $18
4800    $0B     $0A

I haven’t tried to optimize the delay values. Right now I’m using 5 ms between characters and 500 ms between lines when doing 8K transfers and with the modified values I usually am successful. With the default values, it seemed like even with longer delays things would slip out of synchronization at some point more often than not on large transfers.

OUTCH send a character to TTY

OUTCH sends one character via the serial output vai bitbanging
8N1: All 8 databits are sent. One stopbit.

A destroyed, Y=FF, X preserved.

0979   1E9E             ;       PRINT 1 CHAR   CHAR=A
0980   1E9E             ;       X IS PRESERVED, Y RETURNED = FF
0981   1E9E             ;       OUTSP    PRINTS 1 SPACE   
0982   1E9E             ;
0983   1E9E A9 20       OUTSP   LDA   #$20       
0984   1EA0 85 FE       OUTCH   STA   CHAR
0985   1EA2 86 FD               STX   TMPX
0986   1EA4 20 D4 1E            JSR   DELAY      ; 10/11 BIT CODE SYNC
0987   1EA7 AD 42 17            LDA   SBD        ; START BIT
0988   1EAA 29 FE               AND   #$FE
0989   1EAC 8D 42 17            STA   SBD
0990   1EAF 20 D4 1E            JSR   DELAY
0991   1EB2 A2 08               LDX   #$08
0992   1EB4 AD 42 17    OUT1    LDA   SBD        ; DATA BIT
0993   1EB7 29 FE               AND   #$FE
0994   1EB9 46 FE               LSR   CHAR
0995   1EBB 69 00               ADC   #$00       
0996   1EBD 8D 42 17            STA   SBD
0997   1EC0 20 D4 1E            JSR   DELAY
0998   1EC3 CA                  DEX   
0999   1EC4 D0 EE               BNE   OUT1
1000   1EC6 AD 42 17            LDA   SBD        ; STOP BIT
1001   1EC9 09 01               ORA   #$01
1002   1ECB 8D 42 17            STA   SBD
1003   1ECE 20 D4 1E            JSR   DELAY      ; STOP BIT
1004   1ED1 A6 FD               LDX   TMPX       ; RESTORE INDEX
1005   1ED3 60                  RTS

A subroutine that sends out an 8 bit character via the serial TTY output followed by one stop bit via bitbanging to PB7. Contrary to GETCH this allow 8 bits characters.
The receiving device should be set to 8N1 (8 databits, no parity, 1 stopbit)
Timing is done with the 1 bit DELAY routine.
A is lost, Y = FF, X preserved.

What is happening here?

  • The character to send is stored at zeropage CHAR (984).
  • save X
  • A 1 bit delay, just to be sure (986)
  • set PB7 to the start bit 0 (987-989)
  • a 1 bit delay (980)
    • get current value of PB7 (LDA SBD and AND #$FE) (982)
    • shift databit into carry (LSR CHAR) (994)
    • set carry to PB7 (ADC #$00 and STA SBD) (985)
    • delay 1 bit (987)
    • repeat for 8 databits
    • (998-99)

  • set stop bit 1 (1000-1002)
  • delay 1 bit, leave PB7 in rest state (1003)
  • restore X