6502 Part 2 - EVM Driver

17 Aug 2024

Introduction

Right then, we're going into the land of Web3. What does that mean? It means memory mapping a Web3 interface onto an 8-bit machine of course! This article will be more a fantasy spec for a memory mapped Ethereum interface into an 8-bit machine. That 8-bit machine is running 6502 with our ZX Spectrum display that we developed in a previous article.

Motivation

I want my 8-bit games to interact with the blockchain alright! Honestly, I have no idea how that's going to work. To make this all viable you'd need some way to prove the state of the 6502 machine, in order to validate that you're at the correct part of the game to send a transaction, otherwise the game is just a weird UI. That may be coming soon thanks to the advancements made in the ZK world. Optimistic roll-ups would be an interesting solution that could be viable in the year 2024. Imagine that? A 6502 optimistic roll-up?!

Anyway, let's get to it. To the spec...

Spec

For the sake of this example, we're going to bootstrap window.ethereum and the Ethereum JSON-RPC [https://ethereum.org/en/developers/docs/apis/json-rpc/]. Let's define two registers, the Web3 Status Register (W3SR) and the Web3 Request Register (W3RR).

Memory map:

Register Address
W3SR 0x6000
W3RR 0x6001
W3MR 0x6002 - 0x6003

The W3SR is a read-only register which is in charge of reflecting the state of the current Web3 connection.

Bit Job
8 Unused
7 Has processed full message failed?
6 Has processed full message successfully?
5 Is processing message?
4 Unused
3 Is the wallet connected?
2 Is a request in progress to connect wallet?
1 Does a wallet exist?
---
title: Wallet connection status state transition
---
stateDiagram-v2
    s0: Initial - 0x00
    s1: Wallet exists - 0x01
    s3: Connecting - 0x03
    s5: Connected - 0x05

    s0 --> s1: window.ethereum exists
    s1 --> s3: Request to connect
    s3 --> s1: Fails to connect
    s3 --> s5: Connection successful
    s5 --> s1: Wallet discconects

The W3RR is a write-only register which is in charge of making requests to the Web3 provider.

Bit Job
8 Unused
7 Unused
6 Cancel processing of message
5 Send message
4 Unused
3 Unused
2 Request to connect wallet
1 Unused
---
title: Wallet connection request state transition
---
stateDiagram-v2
    s0: Initial - 0x0
    s2: Request to connect - 0x2

    s0 --> s2
    s2 --> s0

The W3MR is the register that we use to send messages to the JSON-RPC provider. The register is two bytes long, and stores the address of the message in memory. The message is stored in message pack form with a few custom extensions.

---
title: Web3 message state transition
---
stateDiagram-v2
    s01: Initial - 0x01
    s11: Processing message - 0x11
    s21: Message successful - 0x21
    s41: Message failed - 0x41

    s01 --> s11: Send message W3RR 0x10
    s11 --> s21: Message sent
    s21 --> s01
    s11 --> s01: Cancel processing W3RR 0x20
    s11 --> s41: Message failed
    s41 --> s01: Message failed

The Ethereum address that is connected is 20 bytes long and is stored from address 0x6002 to 0x6015 in little-endian format.

The chain ID is 4 bytes long and is stored from address 0x6016 to 0x6019 in little-endian format.

  • TODO - requested chain ID

  • TODO - how to transfer an RPC message?

    • use of a special msgpack extension
    • a new register flag for reading input
    • a new register flag for sending input
  • TODO - receive a response?

  • TODO - subscribing to events?

  • TODO - current block number?

  • TODO - have you specified this is for the Ethereum eco-system?

bitmap = $4000
attributes = $5800
last_attributes = $5B00
web3_status = $6000
web3_request = $6001
mouse_down = $3FFD
mouse_x = $3FFE
mouse_y = $3FFF

.macro STAI address
    LDX address
    STX $00
    LDX address + 1
    STX $01
    LDY #$00
    STA ($00),Y
.end

.macro TX2 address_1, address_2
    LDA address_1
    STA address_2
    LDA address_1 + 1
    STA address_2 + 1
.end

.macro CMP2 address_1, address_2
    LDA address_1
    CMP address_2
    BNE done
    LDA address_1 + 1
    CMP address_2 + 1
done:
    NOP
.end

.macro DEBUG address, pos
    LDA #%00100011
    STA attributes + pos
    LDA address
    STA bitmap + pos
    LDA address + 1
    STA bitmap + pos + 256
.end

init:
    JMP loop

mouse_record_x:
    .byte $00

mouse_record_y:
    .byte $00

update_mouse_record:
    LDA mouse_x
    STA mouse_record_x
    LDA mouse_y
    STA mouse_record_y
    RTS

mouse_attribute_offset:
    .word $0000

update_mouse_attribute_offset:
    LDA mouse_record_x
    AND #%11111000
    ROR
    ROR
    ROR
    STA mouse_attribute_offset
    LDA mouse_record_y
    AND #%00111000
    ASL
    ASL
    ORA mouse_attribute_offset
    STA mouse_attribute_offset
    LDA mouse_record_y
    AND #%11000000
    ROR
    ROR
    ROR
    ROR
    ROR
    ROR
    STA mouse_attribute_offset + 1
    RTS

attribute_address_offset:
    .word $0000

attribute_address_result:
    .word attributes

get_attribute_address_from_offset:
    CLC
    LDA #<attributes
    ADC attribute_address_offset
    STA attribute_address_result
    LDA #>attributes
    ADC attribute_address_offset + 1
    STA attribute_address_result + 1
    RTS

update_web3_request:
    LDA web3_status
    AND #%00000110
    BNE cancel_web3_connect
    LDA is_mouse_over_web3_square
    BNE try_web3_connect
    RTS

try_web3_connect:
    LDA web3_request
    ORA #%00000001
    STA web3_request
    RTS

cancel_web3_connect:
    LDA #$00
    STA web3_request
    RTS

is_mouse_over_web3_square:
    .byte $00

update_is_mouse_over_web3_square:
    CMP2 mouse_attribute_offset, web3_square_attribute_offset
    BNE set_is_mouse_over_web3_square_0
    LDA mouse_down
    BEQ set_is_mouse_over_web3_square_0
set_is_mouse_over_web3_square_1:
    LDA #$01
    STA is_mouse_over_web3_square
    RTS
set_is_mouse_over_web3_square_0:
    LDA #$00
    STA is_mouse_over_web3_square
    RTS

web3_square_attribute_offset:
    .word $001F

web3_square_state:
    .byte $00

update_web3_square_state:
    ;LDA is_mouse_over_web3_square
    LDA web3_status
    STA web3_square_state
    RTS

web3_square_attribute:
    .byte %00010000 ; no eth - red
    .byte %00110000 ; eth - yellow
    .byte %00000000
    .byte %01001000 ; connection requested - bright blue
    .byte %00000000
    .byte %01100000 ; connected - bright green

draw_web3_square:
    TX2 web3_square_attribute_offset, attribute_address_offset
    JSR get_attribute_address_from_offset
    LDX web3_square_state
    LDA web3_square_attribute,X
    STAI attribute_address_result
    RTS

draw_mouse_square:
    LDA is_mouse_over_web3_square
    BEQ draw_mouse_square_force
    RTS
draw_mouse_square_force:
    TX2 mouse_attribute_offset, attribute_address_offset
    JSR get_attribute_address_from_offset
    JSR get_mouse_colour
    STAI attribute_address_result
    RTS
get_mouse_colour:
    LDA mouse_down
    BNE return_mouse_down_colour
return_mouse_up_colour:
    LDA #%00110000
    RTS
return_mouse_down_colour:
    LDA #%01010000
    RTS

loop:
    JSR update_mouse_record
    JSR update_mouse_attribute_offset
    JSR update_is_mouse_over_web3_square
    JSR update_web3_request
    JSR update_web3_square_state
    JSR draw_mouse_square
    JSR draw_web3_square
    DEBUG mouse_attribute_offset, 2
    DEBUG web3_square_attribute_offset, 3
    DEBUG is_mouse_over_web3_square, 1
    DEBUG web3_status, 4
    JMP loop