Commander X16 improvements

This commit is contained in:
Karol Stasiak 2019-09-16 22:55:08 +02:00
parent b1e5176aff
commit 08ef0beeb7
13 changed files with 314 additions and 22 deletions

View File

@ -20,7 +20,7 @@ For build instructions, see [Build instructions](./COMPILING.md).
* other Commodore computers: C16, Plus/4, C128, PET, VIC-20 (stock or with RAM extensions)
* other 6502-based machines: Famicom/NES, Atari 8-bit computers, BBC Micro, Apple II+/IIe/Enhanced IIe, Atari 2600 (experimental)
* other 6502-based machines: Famicom/NES, Atari 8-bit computers, BBC Micro, Apple II+/IIe/Enhanced IIe, Atari 2600 (experimental), Commander X16 (experimental)
* Z80-based machines: ZX Spectrum 48k, NEC PC-88, Amstrad CPC, MSX

View File

@ -60,6 +60,8 @@
* [Game Boyonly modules](stdlib/gb.md)
* [X16only modules](stdlib/x16.md)
## Implementation details
* [Variable storage](abi/variable-storage.md)

View File

@ -60,6 +60,8 @@
* [Game Boyonly modules](stdlib/gb.md)
* [X16only modules](stdlib/x16.md)
## Implementation details
* [Variable storage](abi/variable-storage.md)

View File

@ -112,6 +112,8 @@ See [the ROM vs RAM guide](../api/rom-vs-ram.md) for more information.
* `NULLPTR` physical value of `nullptr`, default 0
* `VERA_VERSION` on Commander X16, the version of the VERA chip: `7` for 0.7, `8` for 0.8
### Built-in preprocessor functions and operators
The `defined` function returns 1 if the feature is defined, 0 otherwise.

122
docs/stdlib/x16.md Normal file
View File

@ -0,0 +1,122 @@
[< back to index](../doc_index.md)
# Commander X16-oriented modules
**WARNING!** Commander X16 is not yet a finalised design.
Therefore, both the device itself and the modules for its support may change at any moment.
## x16_kernal module
The `x16_kernal` module is imported automatically on the X16 target.
Currently, it automatically imports the [`c64_kernal` module](./c64.md).
## `x16_hardware` module
The `x16_hardware` module is imported automatically on the X16 target.
#### `void set_ram_bank(byte)`
Switches the RAM segment at $A000-$BFFF.
#### `void set_rom_bank(byte)`
Switches the ROM segment at $C000-$DFFF.
#### `void vera_poke(int24 address, byte value)`
Writes a byte into the VERA memory space.
#### `void vera_fill(int24 address, byte value, word size)`
Writes `size` bytes into the VERA memory space.
#### `void vera_upload(int24 address, pointer source, byte size)`
#### `void vera_upload_large(int24 address, pointer source, word size)`
Copies `size` bytes from the RAM at address `source` into the VERA memory space at address `address`.
#### `struct vera_layer_setup`
Hardware register values for a video layer:
byte ctrl0
byte ctrl1
word map_base
word tile_base
word hscroll
word vscroll
#### `void set_vera_layer1(pointer.vera_layer_setup)`
Sets up the layer 1.
#### `void set_vera_layer2(pointer.vera_layer_setup)`
Sets up the layer 2.
#### `struct vera_sprite_data`
Hardware register values for a sprite:
word address
word x
word y
byte ctrl0
byte ctrl1
#### `const int24 VERA_COMPOSER_CTRL`
#### `const int24 VERA_PALETTE`
#### `const int24 VERA_LAYER_1`
#### `const int24 VERA_LAYER_2`
#### `const int24 VERA_SPRITE_CTRL`
#### `const int24 VERA_SPRITES`
Various addresses in the VERA memory space.
## `x16_joy` module
The `x16_joy` module implements a joystick driver compatible with the `joy` module.
#### `void read_joy1()`
Reads the joystick from the port 1.
#### `void read_joy2()`
Reads the joystick from the port 1.
#### `void read_also_joy1()`
Reads the joystick from the port 1 and adds its readouts to the current readouts.
#### `void read_also_joy2()`
Reads the joystick from the port 2 and adds its readouts to the current readouts.
#### `byte input_*`
The following variables have the value 1 if the key is pressed and 0 if not:
**Warning:** The assignment of NES controller buttons and keyboard keys may change in the future.
Variable | SNES controller | NES controller | Keyboard (joy 1 only)
---------------|-----------------|----------------|----------------------
`input_a` | A | |
`input_b` | B | A | Ctrl
`input_x` | X | B | Alt
`input_y` | Y | |
`input_start` | Start | Start | Enter
`input_select` | Select | Select | Space
`input_l` | L | |
`input_r` | R | |
`input_b` is an alias for `input_btn`. Single-button games should use `input_btn` for compatibility.
## x16_joy1_default module
Defines the joystick in port 1 as the default joystick.
#### `alias read_joy = read_joy1`

View File

@ -33,9 +33,9 @@ void main () {
vy[i] = 1 - (rand() & 2)
}
vera_upload_large($10000, sprite_bitmap.addr, sizeof(sprite_bitmap))
vera_upload_large($40800, sprites.addr, sizeof(sprites))
vera_upload_large(VERA_SPRITES, sprites.addr, sizeof(sprites))
// enable sprites:
vera_poke($40020, 1)
vera_poke(VERA_SPRITE_CTRL, 1)
while true {
for i,0,paralleluntil,SPRITE_COUNT {
p = sprites[i].pointer
@ -50,7 +50,7 @@ void main () {
if y == 0 { vy[i] = 1 }
if y >= 480-32 { vy[i] = 0-1 }
}
vera_upload_large($40800, sprites.addr, sizeof(sprites))
vera_upload_large(VERA_SPRITES, sprites.addr, sizeof(sprites))
}
}

View File

@ -2,12 +2,12 @@ void main() {
word i
byte hn, ln
// 256-colour text mode for layer 0
vera_poke($40000, $21)
vera_poke(VERA_LAYER_1, $21)
// 8×8 tiles, 64×64 tile map
vera_poke($40001, $35)
vera_poke(VERA_LAYER_1+1, $35)
// 2× zoom in
vera_poke($40041, $40)
vera_poke($40042, $40)
vera_poke(VERA_COMPOSER_CTRL+1, $40)
vera_poke(VERA_COMPOSER_CTRL+2, $40)
i = 0
while i < $2000 {
vera_poke(i, $A0)

View File

@ -7,29 +7,39 @@
arch=cmos
encoding=petscii
screen_encoding=petscr
modules=loader_0801,c64_kernal,x16_hardware,c64_panic,stdlib
modules=loader_0801,x16_kernal,x16_hardware,c64_panic,stdlib
[allocation]
; list of free zp pointer locations is the same as C64
zp_pointers=$FB,$FD,$43,$45,$47,$4B,$F7,$F9,$9E,$9B,$3D
segments=default,himem0
; Let's not use the BASIC:
zp_pointers=0-$8F,$FB-$FF
segments=default,himem_00,himem_ff
default_code_segment=default
segment_default_start=$80D
segment_default_codeend=$9eff
segment_default_datastart=after_code
segment_default_end=$9eff
segment_himem0_start=$c000
segment_himem0_codeend=$dfff
segment_himem0_datastart=after_code
segment_himem0_end=$dfff
segment_himem_00_start=$a000
segment_himem_00_codeend=$bfff
segment_himem_00_datastart=after_code
segment_himem_00_end=$bfff
segment_himem_00_bank=$00
segment_himem_ff_start=$a000
segment_himem_ff_codeend=$bfff
segment_himem_ff_datastart=after_code
segment_himem_ff_end=$bfff
segment_himem_ff_bank=$ff
[define]
CBM=1
COMMANDER_X16=1
WIDESCREEN=1
KEYBOARD=1
JOYSTICKS=0
JOYSTICKS=2
HAS_BITMAP_MODE=1
; Use VERA 0.7, as this is what the emulator implements:
VERA_VERSION=7
[output]
style=single

View File

@ -1,9 +1,35 @@
// TODO: bankswitching
#if VERA_VERSION == 7
const int24 VERA_COMPOSER_CTRL = $40040
const int24 VERA_PALETTE = $40200
const int24 VERA_LAYER_1 = $40000
const int24 VERA_LAYER_2 = $40010
const int24 VERA_SPRITE_CTRL = $40020
const int24 VERA_SPRITES = $40800
volatile byte vera_addr_hi @ $9f20
volatile byte vera_addr_mi @ $9f21
volatile byte vera_addr_lo @ $9f22
#elseif VERA_VERSION == 8
const int24 VERA_COMPOSER_CTRL = $F0000
const int24 VERA_PALETTE = $F1000
const int24 VERA_LAYER_1 = $F2000
const int24 VERA_LAYER_2 = $F3000
const int24 VERA_SPRITE_CTRL = $F4000
const int24 VERA_SPRITES = $F5000
volatile byte vera_addr_hi @ $9f22
volatile byte vera_addr_mi @ $9f21
volatile byte vera_addr_lo @ $9f20
volatile int24 vera_addr @ $9f20
#else
#error Unsupported VERA_VERSION
#endif
volatile byte vera_data1 @ $9f23
volatile byte vera_data2 @ $9f24
volatile byte vera_ctrl @ $9f25
@ -23,10 +49,19 @@ asm void set_vera_layer_internal(pointer.vera_layer_setup ax, byte y) {
sta __reg
stx __reg+1
stz vera_ctrl
#if VERA_VERSION == 7
lda $14
sta vera_addr_hi
stz vera_addr_mi
sty vera_addr_lo
#elseif VERA_VERSION == 8
lda $1F
sta vera_addr_hi
sty vera_addr_mi
stz vera_addr_lo
#else
#error Unsupported VERA_VERSION
#endif
ldy #0
__set_layer_internal_loop:
lda (__reg),y
@ -37,13 +72,25 @@ asm void set_vera_layer_internal(pointer.vera_layer_setup ax, byte y) {
? rts
}
asm void set_vera_layer0(pointer.vera_layer_setup ax) {
asm void set_vera_layer1(pointer.vera_layer_setup ax) {
#if VERA_VERSION == 7
? ldy #0
#elseif VERA_VERSION == 8
? ldy #$20
#else
#error Unsupported VERA_VERSION
#endif
? jmp set_vera_layer_internal
}
asm void set_vera_layer1(pointer.vera_layer_setup ax) {
? ldy #0
asm void set_vera_layer2(pointer.vera_layer_setup ax) {
#if VERA_VERSION == 7
? ldy #$10
#elseif VERA_VERSION == 8
? ldy #$30
#else
#error Unsupported VERA_VERSION
#endif
? jmp set_vera_layer_internal
}
@ -87,7 +134,7 @@ inline void vera_upload(int24 address, pointer source, byte size) {
? ldy #0
__vera_upload_loop:
? lda (__reg),y
? sta vera_data1
! sta vera_data1
? iny
? cpy size
? bne __vera_upload_loop
@ -101,3 +148,13 @@ struct vera_sprite_data {
byte ctrl0
byte ctrl1
}
inline asm void set_ram_bank(byte a) {
! STA $9F61
? RTS
}
inline asm void set_rom_bank(byte a) {
! STA $9F60
? RTS
}

89
include/x16_joy.mfk Normal file
View File

@ -0,0 +1,89 @@
void x16_reset_joy() {
input_dx = 0
input_dy = 0
input_a = 0
input_b = 0
input_x = 0
input_y = 0
input_start = 0
input_select = 0
input_l = 0
input_r = 0
}
alias reset_joy = x16_reset_joy!
// TODO: be more controller-agnostic?
// SNES:B NES:A keyboard:Ctrl
alias input_b = input_btn
// SNES:A
byte input_a
// SNES:X
byte input_x
// SNES:Y NES:B keyboard:Alt
byte input_y
// SNES/NES:Start keyboard:Enter
byte input_start
// SNES/NES:Select keyboard:Space
byte input_select
// SNES:L
byte input_l
// SNES:R
byte input_r
void read_joy1() {
x16_reset_joy()
read_also_joy1()
}
void read_joy2() {
x16_reset_joy()
read_also_joy2()
}
asm void read_also_joy1() {
JSR $FF06
LDA $F1
BNE __read_also_joy1_skip
LDA $EF
JSR x16_joy_byte0
LDA $F0
JSR x16_joy_byte1
__read_also_joy1_skip:
RTS
}
asm void read_also_joy2() {
JSR $FF06
LDA $F4
BNE __read_also_joy2_skip
LDA $F2
JSR x16_joy_byte0
LDA $F3
JSR x16_joy_byte1
__read_also_joy2_skip:
RTS
}
void x16_joy_byte0(byte value) {
if value & 8 == 0 { input_dy -= 1 }
if value & 4 == 0 { input_dy += 1 }
if value & 2 == 0 { input_dx -= 1 }
if value & 1 == 0 { input_dx += 1 }
if value & 16 == 0 { input_start += 1 }
if value & 32 == 0 { input_select += 1 }
if value & 64 == 0 { input_y += 1 }
if value & 128 == 0 { input_b += 1 }
}
void x16_joy_byte1(byte value) {
if value & 16 == 0 { input_r += 1 }
if value & 32 == 0 { input_l += 1 }
if value & 64 == 0 { input_x += 1 }
if value & 128 == 0 { input_a += 1 }
}

View File

@ -0,0 +1,5 @@
// set X16 joypad in port 1 as the default
import x16_joy
alias read_joy = read_joy1

2
include/x16_kernal.mfk Normal file
View File

@ -0,0 +1,2 @@
// Let's be lazy:
import c64_kernal

View File

@ -42,6 +42,7 @@ nav:
- C64-only modules: stdlib/c64.md
- NES-only modules: stdlib/nes.md
- Game Boyonly modules: stdlib/gb.md
- X16only modules: stdlib/x16.md
- Implementation details:
- Calling convention: abi/calling-convention.md
- Generated labels: abi/generated-labels.md