mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-03 04:32:21 +00:00
Commander X16 improvements
This commit is contained in:
parent
b1e5176aff
commit
08ef0beeb7
@ -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
|
||||
|
||||
|
@ -60,6 +60,8 @@
|
||||
|
||||
* [Game Boy–only modules](stdlib/gb.md)
|
||||
|
||||
* [X16–only modules](stdlib/x16.md)
|
||||
|
||||
## Implementation details
|
||||
|
||||
* [Variable storage](abi/variable-storage.md)
|
||||
|
@ -60,6 +60,8 @@
|
||||
|
||||
* [Game Boy–only modules](stdlib/gb.md)
|
||||
|
||||
* [X16–only modules](stdlib/x16.md)
|
||||
|
||||
## Implementation details
|
||||
|
||||
* [Variable storage](abi/variable-storage.md)
|
||||
|
@ -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
122
docs/stdlib/x16.md
Normal 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`
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
89
include/x16_joy.mfk
Normal 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 }
|
||||
}
|
5
include/x16_joy1_default.mfk
Normal file
5
include/x16_joy1_default.mfk
Normal 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
2
include/x16_kernal.mfk
Normal file
@ -0,0 +1,2 @@
|
||||
// Let's be lazy:
|
||||
import c64_kernal
|
@ -42,6 +42,7 @@ nav:
|
||||
- C64-only modules: stdlib/c64.md
|
||||
- NES-only modules: stdlib/nes.md
|
||||
- Game Boy–only modules: stdlib/gb.md
|
||||
- X16–only modules: stdlib/x16.md
|
||||
- Implementation details:
|
||||
- Calling convention: abi/calling-convention.md
|
||||
- Generated labels: abi/generated-labels.md
|
||||
|
Loading…
Reference in New Issue
Block a user