A record of my progress through the Ben Eater You Tube tutorial
rom = bytearray([0xea] * 32768 ) rom[0] = 0xa9 rom[1] = 0x42 rom[2] = 0x8d rom[3] = 0x00 rom[4] = 0x60 rom[0x7ffc] = 0x00 rom[0x7ffd] = 0x80 with open("rom.bin", "wb") as out_file: out_file.write(rom)
ROM file hexdump:
00000000 a9 42 8d 00 60 ea ea ea ea ea ea ea ea ea ea ea |.B..`...........| 00000010 ea ea ea ea ea ea ea ea ea ea ea ea ea ea ea ea |................| * 00007ff
Using Siglent Function Generator as the CPU PHI2 clock
### 8-bit Counter code = bytearray([ 0xa9, 0xff, # lda #$FF 0x8d, 0x02, 0x60, # sta $6002 : put 11111111 into VIA's Port B control register - all port pins are OUTPUT 0xa9, 0xff, # lda #$ff (start with all LEDs lit) 0x8d, 0x00, 0x60, # sta $6000 0x1a, # inc A 0x4c, 0x07, 0x80, # jmp $8007 ]) rom = code + bytearray([0xea] * (32768 - len(code))) rom[0x7ffc] = 0x00 rom[0x7ffd] = 0x80 with open("rom.bin", "wb") as out_file: out_file.write(rom);
This assembles as :
00000000 a9 ff 8d 02 60 a9 ff 8d 00 60 1a 4c 07 80 ea ea |....`....`.L....| 00000010 ea ea ea ea ea ea ea ea ea ea ea ea ea ea ea ea |................| * 00007ff0 ea ea ea ea ea ea ea ea ea ea ea ea 00 80 ea ea |................| 00008000
LEDs flash in binary count to 255 and cycle round… endlessly.
I wanted to see if I could extend the counter to 16-bits, using 2 VIA ports to drive the LEDs
To make life easier I moved to assembly
language and the VASM
assembler as recommended in the tutorial.
To count beyond a single 8-bit byte needs a way of holding 2 separate bytes of the count, the low byte and high byte, and only increment the high byte when the low byte rolls past 11111111
. Tricky with no RAM installed yet.
I used the 2 index registers X
and Y
. The carry bit is set as a result of arithmetic on the Accumulator register, so X
(low byte) has to be copied to the Accumulator for the addition of 1
each time round the loop.
.org $8000 reset: lda #$ff sta $6002 ; set port B pins to all output sta $6003 ; set port A pins to all output lda #$00 tax ; X holds the low byte. initialize to zero tay ; Y holds the high byte. initialize to zero loop: txa ; copy low byte to A adc #$01 ; add 1 tax ; copy new value to X bcs high_b ; if carry flag set, branch to increment the high byte print: txa ; bring the low byte to A sta $6000 ; send low byte to Port B LEDs tya ; bring the high byte to A sta $6001 ; send high byte to Port A LEDs jmp loop high_b: iny ; increment high byte in Y jmp print .org $fffc .word reset .word $0000
Assembled code
00000000 a9 ff 8d 02 60 8d 03 60 a9 00 aa a8 8a 69 01 aa |....`..`.....i..| 00000010 b0 0b 8a 8d 00 60 98 8d 01 60 4c 0c 80 c8 4c 12 |.....`...`L...L.| 00000020 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00007ff0 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 |................| 00008000
Add another 8 LEDs, to Port A, to show the High Byte….
It now counts to 1111111111111111
or $FFFF
or 65535
as displayed by the LEDs attached to both VIA ports.
is to add the LCD module in place of the flashing LEDs
Hello, w
and no more….8 Characters… what, why?
Lots of messing about with the assembler code to see if I could get all the Hello, world!
message to print to the LCD.
By adding the necessary instruction to change the display position I could use all 16 characters of the display, but I couldn't get it to print all the message in one go…. The same happened with a different LCD module.
Eventually I checked, and re-checked, the address and data lines between CPU, ROM and VIA and must have re-inserted one that was loose. Afterwards the display showed Hello, world!
correctly.
I assume the error was that an address line between CPU and ROM wasn't connected, so at one step of the program the ROM went to the wrong address and returned garbage data, and the program stopped, or just randomly stepped through the otherwise empty ROM?
Anyway, whatever the mechanism, ensuring all address & data lines were seated correctly cured the bug.
PORTB = $6000 PORTA = $6001 DDRB = $6002 DDRA = $6003 E = %01000000 RW = %00100000 RS = %00010000 LED = %10000000 .org $8000 reset: ldx #$ff txs lda #%11111111 ; Set all pins on port B to output sta DDRB lda #%00000000 ; Set all pins on port A to input sta DDRA jsr lcd_init lda #%00101000 ; Set 4-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 ldx #0 print1: lda message1,x beq print2 jsr print_char inx jmp print1 message1: .asciiz "Shed-o-tron II " message2: .asciiz "* * 22/8/22 * * " loop: lda #LED sta PORTB jmp loop print2: lda #%10101001 jsr lcd_instruction ldx #0 line2: lda message2,x beq loop jsr print_char inx jmp line2 lcd_wait: pha lda #%11110000 ; LCD data is input sta DDRB lcdbusy: lda #RW sta PORTB lda #(RW | E) sta PORTB lda PORTB ; Read high nibble pha ; and put on stack since it has the busy flag lda #RW sta PORTB lda #(RW | E) sta PORTB lda PORTB ; Read low nibble pla ; Get high nibble off stack and #%00001000 bne lcdbusy lda #RW sta PORTB lda #%11111111 ; LCD data is output sta DDRB pla rts lcd_init: lda #%00000010 ; Set 4-bit mode sta PORTB ora #E sta PORTB and #%00001111 sta PORTB rts lcd_instruction: jsr lcd_wait pha lsr lsr lsr lsr ; Send high 4 bits sta PORTB ora #E ; Set E bit to send instruction sta PORTB eor #E ; Clear E bit sta PORTB pla and #%00001111 ; Send low 4 bits sta PORTB ora #E ; Set E bit to send instruction sta PORTB eor #E ; Clear E bit sta PORTB rts print_char: jsr lcd_wait pha lsr lsr lsr lsr ; Send high 4 bits ora #RS ; Set RS sta PORTB ora #E ; Set E bit to send instruction sta PORTB eor #E ; Clear E bit sta PORTB pla and #%00001111 ; Send low 4 bits ora #RS ; Set RS sta PORTB ora #E ; Set E bit to send instruction sta PORTB eor #E ; Clear E bit sta PORTB rts ; Reset/IRQ vectors .org $fffc .word reset .word $0000
Github : https://github.com/gm4slv/6502