Tag Archives: 6502

Adding a VGA terminal to my 6502

Using a LilyGo TTGO ESP32 VGA32, I’m connecting my breadboard 6502 to a serial vga terminal with its own keyboard.

Due to a lot of moving around, new places, new homes I dumped a lot of terminal hardware.
Also are those old terminals too big and use too much power.

I’m going to use this DIY screen.
https://www.henriaanstoot.nl/2021/03/24/broken-or-slow-laptop-screen-still-works/

Using the Libraries from Fabrizio Di Vittorio, named FabGL, you can transform this device into a dumb terminal, game device, VIC-20, a 8086 pc and more.
There are even some projects to turn this into a C64.

But the main thing I want to do: A simple terminal.
(I probably revisit the other options again at a later stage)

My Wozmon bios has bare minimum support for serial communication, so i have to do some bitbanging.
(6502 is using a 6551 ACIA)

Sound from the ESP32 VGA board.

  • Chipset: TTGO Micro32 (ESP32 240Mhz dual core processor)
  • Flash memory: 4MB
  • SRAM: 520KB
  • Built-in Bluetooth
  • Built-in Wi-Fi
  • Supply voltage: 3.3V DC or 5V DC
  • GPIO voltage: 3.3V*
  • USB to serial converter: CP2102 or CH9102F (drivers)
  • VGA connection
  • PS/2: keyboard connection
  • PS/2: mouse connection
  • Built-in Li-ion/Li-Po battery charging circuit: TP4054 chip can charge up to 500mA

Soldering a 6502 PCB

A while ago I started a soldering a 6502 bare SBC.

Note pin 1 is not connected, VPB (vector pull is not supported on this PCB. But i’m planning to design a new one anyway.)

I got it running now.

It has an EPROM with Wozmon and Basic for now.
I have to redo the address decoder, but I like the simple serial interface by Geoffrey. (I hate the PIC18F15Q41, made by Microchip, but still the best minimal option .. for now)

Probably the last time i’ve used a pic was in 1998

Machine code Monitor in ROM on real BBC Acorn Hardware

My BBC Acorn model B is working again. The original monitor is still dead.

Time to play with some machine code and ROMs.

My machine has a NFS rom installed. (NetFS)

Econet was Acorn Computers’s low-cost local area network system, intended for use by schools and small businesses. It was widely used in those areas, and was supported by a large number of different computer and server systems produced both by Acorn and by other companies.

I found a ROM online called Gremlin. It is a 16K rom file. But at the moment I only got some 28C64 (8k) or 28C256 (32k) eeproms.

32k it is. But de beeb having address line A14 floating high, I need to flash the upper 16k of the 32k ROM.

So I made the 16K rom into a 32K using cat

cp Gremlin\ v1.21\ \(1983\)\(Computer\ Concepts\).rom 16k.rom
cat Gremlin\ v1.21\ \(1983\)\(Computer\ Concepts\).rom >> 16k.rom

minipro -w 16k.rom -p AT28C256

I got the rom from this page:
https://acorn.huininga.nl/pub/unsorted/roms/Gremlin%20v1.21%20(1983)(Computer%20Concepts).rom

Below booting straight into the monitor program.

Manual:

6502 and Wozmon

Ben posted a youtube about Wozmon running on his 6502.

In 1976, Steve Wozniac wrote what’s commonly known simply as Wozmon.

Wozmon is a machine-code monitor program written by Wozniak for the Apple 1. In Only 256 bytes ! Being pure 6502 code easily adaptable.
A monitor program allows you to view/edit and run machine code using simple commands.

I’ve got a different setup as Ben’s computer.
But changing the necessary, and it will run on my 6502.

I’m not using rs232 voltage levels (-7 till -25 and +7 till +25 volts).
I’m using a usb serial uart standard 5v leveling stick …

For address decoder see other post

Simplified schematic of my UART/ACIA

Compiling the Wosmon gave me an error, DEC is not a valid opcode for a bare 6502 .. but we have a 65c02.
Solution: Add -c02 extra opcodes

error 1 in line 187 of "wozmon.s": illegal operand types
>                DEC                    ; Decrement A.

# fix .. add -c02
vasm6502_oldstyle -c02 -Fbin -dotdir wozmon.s

Below Apple I Manual with the sourcecode for Wozmon

Converting png images to composite pixels (6502)

Using searle’s design, i can draw pixels using composite video out.

Converting b/w png to hex include files, for usage in vasm I did the following.

#Python script to convert black levels to pixels
from PIL import Image
i = Image.open("fash.png")

pixels = i.load() # this is not a list, nor is it list()'able
width, height = i.size

all_pixels = []
for x in range(width):
    for y in range(height):
        cpixel = pixels[x, y]
        if cpixel[1] == 255:
            s = '\t.db 0x05,' + hex(int(x)) + ',' + hex(int(y))
            print (s)

Running and output example

python3 image.py > out

head out
	.db 0x05,0x1,0x16
	.db 0x05,0x1,0x18
	.db 0x05,0x1,0x19
	.db 0x05,0x2,0x7
	.db 0x05,0x2,0x8
	.db 0x05,0x2,0xc
	.db 0x05,0x2,0xd
	.db 0x05,0x2,0x17
	.db 0x05,0x3,0x5

Control codes and vasm include

01 (01) - Cursor home (Standard ASCII)
04 (04) - Cursor solid
05 (05) - Set graphics pixel (next two bytes = x,y) 
0C (12) - Clear screen (Standard ASCII)
0D (13) - Carriage return (Standard ASCII)
0E (14) - Set column 0 to 79 (2nd byte is the column number) or 0 to 39 for a 40 char line
0F (16) - Set row 0 to 24 (2nd byte is the row number)
1B (27) - ESC - reserved for ANSI sequences

vasm include part:

message: 
	.db 0x01,0x0c   ; home and clear
	.db 0x1b,0x2d   ; disable ansi translation
	include "out"   ; include hex "png"
	.db 0x00        ; end with 0 (part of message print routine)



IRQ redirect for other programs

I’ve got a ROM in my 6502 which can load programs at $0200.
When running own programs i want to use IRQ’s, but my rom is also using IRQ routines.
So i was wondering if i could ‘hijack’ this IRQ for own program purposes.

So i’ve altered the rom to use a vector in userspace.

  • CPU starts, getting vector from FFFC
  • Goto $8000 main ROM program
  • Setting a jmp routine on zero page $F0
    • 4C C9 FF
    • 4C C9 FF ; second time, first one will redirect
  • Running my program on $0200
  • Change first jmp C9 FF to my own IRQ part
    • Changed jmp vectors
    • 4C 6E 02 ; jmp $026E (myprg)
    • 4C C9 FF ; jmp $FFC9 (rom)
  • Run rest of program

All seems fine and dandy … buzzer is sounding, but no blinky leds.
When flashing the rom with only my program, everything works ..
So whats going on? .. anyone?

00000000  78 a9 6e 85 f1 a9 02 85  f2 58 a9 ff 8d 03 50 8d  |x.n......X....P.| ; sei , lda #$6e, store $f1, lda #$02, store $f2 - so address $026e
00000010  02 50 a9 00 8d 01 50 8d  00 50 85 04 20 45 02 a9  |.P....P..P.. E..|
00000020  77 8d 04 50 a9 07 8d 05  50 20 2f 02 4c 29 02 38  |w..P....P /.L).8|
00000030  a5 00 e5 04 c9 19 90 0c  a9 40 4d 00 50 8d 00 50  |.........@M.P..P|
00000040  a5 00 85 04 60 a9 00 85  00 85 01 85 02 85 03 a9  |....`...........|
00000050  c0 8d 0b 50 a9 a0 8d 0e  50 a9 0e 8d 08 50 a9 27  |...P....P....P.'|
00000060  8d 09 50 a9 00 8d 04 50  8d 05 50 58 60 40 48 a9  |..P....P..PX`@H.| ; at $6e opcode 48 (pha) 
00000070  0e 8d 08 50 a9 27 8d 09  50 e6 00 d0 0a e6 01 d0  |...P.'..P.......|
00000080  06 e6 02 d0 02 e6 03 68  4c f3 00                 |.......hL..|

ROM parts

MYIRQ = $F0                                     ; Own IRQ vector <=================== my additions/alterations

; Below definitions for VIA 1 my loadable program uses VIA 2
PORTB = $6000                                   ; VIA port B 
PORTA = $6001                                   ; VIA port A
DDRB = $6002                                    ; Data Direction Register B
DDRA = $6003                                    ; Data Direction Register A

-------------8<------- snip

PROGRAM_LOCATION = $0200                        ; memory location for user programs

    .org $8000

main:                                           ; boot routine, first thing loaded
    ldx #$ff                                    ; initialize the stackpointer with 0xff
    txs

; ISR redirect code  <=================== my additions/alterations
    sei
    lda #$4C
    sta MYIRQ
    sta MYIRQ + 3
    lda #<ISR
    sta MYIRQ + 1
    sta MYIRQ + 4
    lda #>ISR
    sta MYIRQ + 2
    sta MYIRQ + 5
    cli

; End ISR redirect <=================== till here

; below this the standard rom routines

-------------8<------- snip
    .org $FFC9                                  ; as close as possible to the ROM's end

ISR:                                                  <====================== Whole ISR not my code
CURRENT_RAM_ADDRESS = Z0                        ; a RAM address handle for indirect writing
    pha
    tya
    pha
    lda ISR_FIRST_RUN                           ; check whether we are called for the first time
    bne .write_data                             ; if not, just continue writing
    lda #1                                      ; otherwise set the first time marker
    sta ISR_FIRST_RUN                           ; and return from the interrupt
    jmp .doneisr
.write_data:
    lda #$01                                    ; progressing state of loading operation
    sta LOADING_STATE                           ; so program_ram routine knows, data's still flowing
    lda PORTB                                   ; load serial data byte
    ldy #0
    sta (CURRENT_RAM_ADDRESS),Y                 ; store byte at current RAM location
                                                ; increase the 16bit RAM location
    inc CURRENT_RAM_ADDRESS_L
    bne .doneisr
    inc CURRENT_RAM_ADDRESS_H
.doneisr
    pla                                         ; restore Y
    tay
    pla                                         ; restore A
    rti

    .org $fffc
    .word main                                  ; Main ROM program
    .word MYIRQ                                 ; Redirect to OWN irq vector <=================== my additions/alterations

RAM Program

; Second VIA stuff
PORTB = $5000
PORTA = $5001
DDRB = $5002
DDRA = $5003
;------------------8<-------------

; Vector pointer on zero page
MYIRQ = $F0 

ticks = $00 ; 4 Bytes
toggle_time = $04 ; 1 Byte

    .org $0200

start:
; IRQ REDIRECT
    sei               ; irq masked
    lda #<irq         ; get low byte IRQ routine address 
    sta MYIRQ + 1     ; store at $F1

    lda #>irq         ; get high part of address
    sta MYIRQ + 2     ; store at $F2
    cli               ; irq enabled
; IRQ END REDIRECT

; init of program part
    lda #%11111111
    sta DDRA
    sta DDRB
        lda #$00
    sta PORTA
    sta PORTB
    sta toggle_time
    jsr init_timer
    lda #$77
    sta T1CL
    lda #$07
    sta T1CH

loop:                       ; loop 
    jsr update_led          ; update led routine
    jmp loop

update_led:
    sec
    lda ticks
    sbc toggle_time
    cmp #25
    bcc exit_update_led
    ; Toggle led
    lda #%01000000
    eor PORTB
    sta PORTB
    lda ticks
    sta toggle_time
exit_update_led:
    rts

;-----------------------------8<------------- snip

irq:
    pha
    lda #$0e
    sta T2CL
    lda #$27
    sta T2CH
    inc ticks
    bne end_irq
    inc ticks + 1
    bne end_irq
    inc ticks + 2
    bne end_irq
    inc ticks + 3
end_irq:
    pla
    jmp MYIRQ + 3           ; jmp to vector which points to ROM routine ; should be $FFC9

6502 update

  • New amplifier part using a LM386
  • Buzzer and led on VIA 2, blinky and sound timed by the internal timers of the 6522
  • ACIA testing still going on, writing software
  • Mini matrix keyboard removed, and used the temporary cursor buttons for the test with a rom which allows for a 8bits upload method using a arduino and the 6522. (I’m working on the big keyboard)

Work in progress code

PORT2B = $5000 		; VIA PORTB
PORT2A = $5001 		; VIA PORTA
DDR2B = $5002  		; Data direction register
DDR2A = $5003  		; Data direction register

PORTB = $6000 		; display
PORTA = $6001 		; control display + matrix keyboard
DDRB = $6002  		; data direction register
DDRA = $6003  		; data direction register
SID = $7000   		; sid base address

E  = %10000000		; enable bit
RW = %01000000		; RW bit 
RS = %00100000		; Register Select bit 
HOME = %00000010 	; VIA PORTB HOME command
DADDR = %00010000 	; VIA DADDRESS

LINENO = $0200		; temp address linenumber (move to other location)
NEXTLINE = 40		; 2x16 Chars but internally 40


  .org $8000

reset:
  ldx #$ff
  txs		; reset stack

; ###################################################
; #                 DISPLAY CONTROL                 #
; ###################################################
; VIA Setup

  lda #%11111111 	; Set all pins on port B to output
  sta DDRB
  lda #%11100000 	; Set top 3 pins on port A to output
  sta DDRA

; DISPLAY Setup
  lda #%00111000 	; Set 8-bit mode; 2-line display; 5x8 font
  jsr lcd_instruction
  lda #%00001110 	; Display on; cursor on; blink off
  jsr lcd_instruction
  lda #%00000110 	; Increment and shift cursor; don't shift display
  jsr lcd_instruction
  lda #$00000001 	; Clear display
  jsr lcd_instruction

; ###################################################
; #             PRINT MESSAGE LINE NO 0             #
; ###################################################
  lda #0  		; set line number
  sta LINENO      	; store for subroutine
  jsr gotoline		; move cursor

  ldx #0		; message index pointer
print:
  lda message0,x 	; start of message
  beq nextprint      	; stop when null in message (asciiz <- Zero padded)
  jsr print_char	; print char
  inx			; incr index
  jmp print		; resume print
; ###################################################
; #             PRINT MESSAGE LINE NO 1             #
; ###################################################
nextprint:
  lda #1  		; set line number
  sta LINENO      	; store
  jsr gotoline
  ldx #0  		; index pointer                 
print2:
  lda message1,x  	; absolute address message + x in A
  beq sidsound        	; if x is 0, end of message     
  jsr print_char  	; jump subroutine
  inx             	; increment x
  jmp print2      	; loop print2



; ###################################################
; #             SID SOUND                           #
; ###################################################
sidsound:
  lda #0		
  sta SID+$5		; attack/decay duration
  	
  lda #250
  sta SID+$6		; sustain level/release duration
  	
  lda #$95		; frequency voice 1 low byte
  sta SID+$0
  	
  lda #$44		; frequency voice 1 high byte
  sta SID+$1
  
  lda #%00100001	; sawtooth + gate
  sta SID+$4		; control register voice 1
  
  lda #$0f		; filter mode and volume (bits 3-0 main volume)
  sta SID+$18		; filter mode and volume



; ###################################################
; #             2ND VIA                             #
; ###################################################
  lda #%11111111 	; set port A output
  sta DDR2A

  lda #%11111111	; all ones!
  sta PORT2A
; ###################################################
  lda #%11111111 	; set port A output
  sta DDR2A

  lda #%11111111	; all ones!
  sta PORT2A



; ###################################################
; #             MAIN PROGRAM LOOP                   #
; ###################################################
loop:
  jmp loop
;                   1234567812345678
message0: .asciiz  "VIA 1,2 SID TEST"
message1: .asciiz  "   FASH  2022   "

; ###################################################
; #             ONLY SUBROUTINES                    #
; ###################################################

; ###################################################
; #             Subroutine gotoline                 #
; # Moves character placement position on display   #
; # Needs : $LINENO ADDRESS                         #
; # Exit values : -                                 #
; # Destroys registers: -                           #
; ###################################################

gotoline:
  pha                             ; store a
  txa
  pha                             ; store x
  ldx LINENO
  lda #HOME                       ; cursor down
  jsr lcd_instruction
  lda #$80
nextline:
  ldx LINENO
  cpx #00
  beq endnextlines
loopline:
  adc #40
  jsr lcd_instruction
  dex
  stx LINENO
  jmp nextline
endnextlines:
  pla                             ; pop a
  tax                             ; a to x
  pla                             ; pop a
  rts




; ###################################################
; #             LCD SUBROUTINES                     #
; ###################################################
lcd_wait:
  pha
  lda #%00000000  ; Port B is input
  sta DDRB
lcdbusy:
  lda #RW
  sta PORTA
  lda #(RW | E)
  sta PORTA
  lda PORTB
  and #%10000000
  bne lcdbusy
  lda #RW
  sta PORTA
  lda #%11111111  ; Port B is output
  sta DDRB
  pla
  rts
lcd_instruction:
  jsr lcd_wait
  sta PORTB
  lda #0         ; Clear RS/RW/E bits
  sta PORTA
  lda #E         ; Set E bit to send instruction
  sta PORTA
  lda #0         ; Clear RS/RW/E bits
  sta PORTA
  rts
print_char:
  jsr lcd_wait
  sta PORTB
  lda #RS         ; Set RS; Clear RW/E bits
  sta PORTA
  lda #(RS | E)   ; Set E bit to send instruction
  sta PORTA
  lda #RS         ; Clear E bits
  sta PORTA
  rts

nmi:
  rti

irq:
  rti

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

6502 news

Divers new additions to the 6502 project

Above is my design for a hex keyboard to enter opcodes in hex using a simple monitor program.
i used a 74ls922 which can decode a 4×4 matrix. I’d rather had a 74ls723 which can encode 20 keys.

Nowhere to be found. So i have to think of a new plan.

Now it is configured as follows:

CDEF
89AB
4567
0123

When pressing the alternate key

addr
(to implement)
run
(1/2 implemented)
reset
(to implement)
step instruction
(to implement)
memory next
memory previous
PCB design for matrix hexboard with place for notes

Meanwhile i’ve ordered new keys (the ones i’ve been using for my photomanager project and wnat to have a setup like this:

??addrrunreset
CDEF?
89ABstep
4567mem next
0123mem prev

When you want to show the status of busses and alike, you can’t use a led and restistor directly on the bus, it will require too much current.
So i’ve been using below schematic which uses a darlington array.

Now i can display databus, address bus and i’ve been using this for address decoding logic and hex keyboard.

I’ve implemented a second VIA chip, and ordered components to amplify the SID sound part

6502 progress

Added second VIA chip. (For hex keyboard)

Skipped the sound setups with simple components or the Yamaha chip. Straight to the commodore SID chip. Added a amplifier and a speaker.

Added ROM functions for line printing. Picture with 2 lines, and my name in Japanese

Now I have to wait for components. I’ve made a simulation for a address decoder.

Rest I’ve put in previous posts as updates.

Meanwhile testing 6502 apps on Android


Kicad – Power-on-reset

UPDATE: 20220728 Added POC

The workshop at MCH2022 gave me the idea to make my next PCB not at home, but professionally.

I’m planning to make my 6502 on modular PCB’s when i’ve got the base part working.
( I probably will only make THT (Though Hole Technology) boards instead of smd )
So i’ll probably end up making a few boards, namely:

  • Power on reset
  • Clock module
  • Interconnect with arduino
  • CPU, memory and ROM
  • Display
  • 6522 Via
  • SID chip
  • Hex keyboard

This power-on reset is based on the original C64 part to reset the CPU when you power the machine on. With my 6502 i have to manually push reset to start booting.
(The CPU starts in a unknown state when you power it up, it needs a reset)

Schematic
PCB design
Rendering

Working POC