6502 computer - part 2 - now with solder!

Wow. Its been over a year since I last posted anything.

Over the past two years, I’ve been working as the project manager for a large software/infrastructure implementation, so that has taken up most of my time. That project started a month or two before COVID hit and we basically powered through the entire pandemic with our primary team on-site for all but a few weeks when our state issued a shelter in place order.

We finally launched on November first of 2021, so now I am mainly supporting the organization while everyone gets up to speed. That means that I’ve started to have some free time again on nights and weekends. Yay!

Since the launch, I’ve converted this site to a new backend and revisited the 6502 computer that I started about a year ago.

In a previous post, I made a version of Ben Eater’s 6502 breadboard computer. I changed around the memory map and added some dual port video ram and an MC6847 to output composite video.

I then got caught up in other things and left it sitting on the desk for a while.

The breadboard connections were always a little flakey, so I started rebuilding it on another breadboard and then just decided to solder it up on some strip board.

I took inspiration from the RC2014 and how it used pin headers and sockets for cards and a backplane, so I started out building the CPU/ROM board and a backplane.

Below is the pin layout for the backplane. I’m not currently using pins 31 through 40, so I’ve got some room for expansion.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
A15
A14
A13
A12
A11
A10
A09
A08
A07
A06
A05
A04
A03
A02
A01
GND
+5V
R/W
RST
CLK
IRQ
NMIB
D0
D1
D2
D3
D4
D5
D6
D7

/images/cpu_rom_board_web.jpg

I used the slow clock breadboard module and the arduino mega to monitor the signals on the backplane to verify that things were working correctly.

/images/cpu_rom_test_web.jpg

/images/first_backplane_test_web.jpg

With the CPU and ROM working, I built a RAM board.

/images/ram_board_web.jpg

/images/ram_testing_web.jpg

The RAM tested good with the slow clock, so the next step is to give it a real clock. I used a 1MHz can oscillator and added a reset switch to another board.

/images/clock_reset_board_web.jpg

Next step is video. I started a new board with some dual port RAM to use as video memory.

/images/vram_board_web.jpg

Next, I added the MC6847 and a clock circuit to drive it. I hooked it up temporarily with some alligator clips to verify that video is working.

/images/first_video_board_web.jpg

I wrote ABC to the VRAM and the MC6847 displayed it along with the rest of the initialized garbage.

/images/first_vid_test_web.jpg

With that working, I added a composite socket to the final board.

/images/final_video_board_web.jpg

I’ve now exceeded the capacity of my original backplane, so I extended it and added a power input with a switch and LED.

/images/backplane_power_web.jpg

/images/side_view_web.jpg

Next, I wanted a keyboard input. To do that, I built a board with a WC65C22 Versatile Interface Adapter or VIA.

I wired it up to a yellow LED to blink to test the board.

/images/via_test_web.jpg

With that working, I next built a shift register board to read in the PS/2 keyboard data. The circuit is pretty much the same that Ben used in his video. The schematic I referenced is here.

/images/kb_shift_reg_web.jpg

That seemed to work, so I put it all together and tested, but I was getting double characters every time I pressed a key.

/images/double_characters_web.jpg

I ended up having to reduce the resistor value in the RC circuit on the shift register board from 33k to 22k to fix the double characters.

/images/final_screen_web.jpg

The final configuration is below. I 3D printed a little base plate with support stand-offs for the backplane to sit on.

/images/final_configuration_web.jpg

The current code I’m using is:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
; pointers

KB_WPTR  = $0000
KB_RPTR  = $0001
KB_FLAGS = $0002
CURSOR   = $0003

KB_BUFFER = $0200 ; 256 byte kb buffer 0200-02ff

; constants
RELEASE = %00000001
SHIFT   = %00000010
 
SCREEN = $8000

VRAM_0 = $8000 ; 1st quarter of VRAM
VRAM_1 = $8100 ; 2nd quarter of VRAM
VRAM_2 = $8200 ; 3rd quarter of VRAM
VRAM_3 = $8300 ; 4th quarter of VRAM

PORTB = $8400
PORTA = $8401
DDRB  = $8402
DDRA  = $8403
PCR   = $840c
IFR   = $840d
IER   = $840e

; register select pins on 6522
; RS0 <-> A0 = $8401
; RS1 <-> A1 = $8402
; RS2 <-> A2 = $8404
; RS3 <-> A3 = $8408

  .org $c000 ; testing vram

reset:
  ldx #$ff 	; load ff in x register 
  txs 		; transfer x to s to initialize stack pointers

  lda #$01
  sta PCR
  lda #$82
  sta IER
  cli

  lda #%11111111 ; set all pins on port B to output
  sta DDRB

  lda #%00000000 ; set all pins on port A to input
  sta DDRA

  lda #$00      ; initialize pointers to 0
  sta KB_WPTR
  sta KB_RPTR
  sta KB_FLAGS
  sta CURSOR

  jsr CLRSCR 	; clear the screen

loop:
  sei			; disable interrupts
  lda KB_RPTR		; compare kb read and write pointers
  cmp KB_WPTR
  cli			; enable interrupts
  bne key_pressed	; jump to key_pressed if keyboard read and write pointers differ
  jmp loop		

key_pressed:
  ldx KB_RPTR		; load kb pointer 
  lda KB_BUFFER, x	; read kb_buffer at read pointer
  inc KB_RPTR		; increment read pointer
  
  ldx CURSOR		; load cursor pointer
  sta SCREEN, x 	; store character in A register to screen at cursor offset
  inc CURSOR		; increment cursor
  
  jmp loop		; jump back to top of loop

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Clear Screen
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
CLRSCR:		; clear the screen
  pha
  tya
  pha

  ldy #00
  lda #$20
VRAM_INIT:
  sta VRAM_0,y
  sta VRAM_1,y
  sta VRAM_2,y
  sta VRAM_3,y
  cpy #$ff
  iny
  bne VRAM_INIT

  pla
  tay
  pla
  rts		
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

irq:
  pha
  txa
  pha

  lda KB_FLAGS
  and #RELEASE ; check if we're releasing a key
  beq read_key ; otherwise, read the key

  lda KB_FLAGS
  eor #RELEASE ; flip the releasing bit
  sta KB_FLAGS
  
  lda PORTA    ; read key value thats being released
  cmp #$12 ; left shift
  beq shift_up
  cmp #$59 ; right shift
  beq shift_up
  jmp exit

shift_up:
  lda KB_FLAGS
  eor #SHIFT ; flip the shift bit
  sta KB_FLAGS
  jmp exit
  
read_key:
  lda PORTA
  cmp #$f0
  beq key_release
  cmp #$12 ; left shift
  beq shift_down
  cmp #$59 ; right shift
  beq shift_down
  cmp #$76 ; escape key
  beq escape_down
  cmp #$66
  beq backspace_down

  tax 
  lda KB_FLAGS
  and #SHIFT
  bne shifted_key

  lda keymap, x
  jmp push_key

shifted_key:
  lda keymap_shifted, x

push_key:
  ldx KB_WPTR
  sta KB_BUFFER, x
  inc KB_WPTR
  jmp exit

shift_down:
  lda KB_FLAGS
  ora #SHIFT
  sta KB_FLAGS
  jmp exit

escape_down:
  jsr CLRSCR
  pha
  lda #$00
  sta CURSOR
  pla
  jmp exit

backspace_down:
  pha
  txa
  pha
  dec CURSOR
  ldx CURSOR		; load cursor pointer
  lda #$20
  sta SCREEN, x 	; store character in A register to screen at cursor offset
  
  pla
  tax
  pla
  jmp exit

key_release:
  lda KB_FLAGS
  ora #RELEASE
  sta KB_FLAGS

exit:
  pla
  tax
  pla
  rti

nmi:
  rti
  
  .org $fd00

keymap:
  .byte "????????????? `?" ; 00-0F
  .byte "?????Q1???ZSAW2?" ; 10-1F
  .byte "?CXDE43?? VFTR5?" ; 20-2F
  .byte "?NBHGY6???MJU78?" ; 30-3F
  .byte "?,KIO09??./L;P-?" ; 40-4F
  .byte "??'?[=?????]?\??" ; 50-5F
  .byte "?????????1?47???" ; 60-6F
  .byte "0.2568???+3-*9??" ; 70-7F
  .byte "????????????????" ; 80-8F
  .byte "????????????????" ; 90-9F
  .byte "????????????????" ; A0-AF
  .byte "????????????????" ; B0-BF
  .byte "????????????????" ; C0-CF
  .byte "????????????????" ; D0-DF
  .byte "????????????????" ; E0-EF
  .byte "????????????????" ; F0-FF

keymap_shifted:
  .byte "????????????? ~?" ; 00-0F
  .byte "?????Q!???ZSAW@?" ; 10-1F
  .byte "?CXDE$#?? VFTR%?" ; 20-2F
  .byte "?NBHGY^???MJU&*?" ; 30-3F
  .byte "?<KIO)(??>?L;P_?" ; 40-4F
  .byte '??"?{+?????}?|??' ; 50-5F
  .byte "?????????1?47???" ; 60-6F
  .byte "0.2568???+3-*9??" ; 70-7F
  .byte "????????????????" ; 80-8F
  .byte "????????????????" ; 90-9F
  .byte "????????????????" ; A0-AF
  .byte "????????????????" ; B0-BF
  .byte "????????????????" ; C0-CF
  .byte "????????????????" ; D0-DF
  .byte "????????????????" ; E0-EF
  .byte "????????????????" ; F0-FF

  .org $fffa
  .word nmi
  .word reset
  .word irq