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
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