1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-06-02 00:41:42 +00:00
kickc/src/test/ref/examples/mega65/banked-music.asm
2024-01-02 19:21:16 +01:00

414 lines
15 KiB
NASM

// SID music located in another bank being played in a raster IRQ using memory mapping on the MEGA65
// Music is Cybernoid II by Jeroen Tel released in 1988 by Hewson https://csdb.dk/sid/?id=28140
// SID relocated using http://www.linusakesson.net/software/sidreloc/index.php
.cpu _45gs02
// MEGA65 platform PRG executable with banked code and data starting in MEGA65 mode.
.file [name="banked-music.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$2001]
.segmentdef Code [start=$2017]
.segmentdef Data [startAfter="Code"]
.segmentdef Banked [segments="CodeBanked, DataBanked"]
.segmentdef CodeBanked [start=$4000]
.segmentdef DataBanked [startAfter="CodeBanked"]
.segment Basic
.byte $0a, $20, $0a, $00, $fe, $02, $20, $30, $00 // 10 BANK 0
.byte $15, $20, $14, $00, $9e, $20 // 20 SYS
.text toIntString(main) // NNNN
.byte $00, $00, $00 //
/// Value that disables all CIA interrupts when stored to the CIA Interrupt registers
.const CIA_INTERRUPT_CLEAR_ALL = $7f
/// VICII IRQ Status/Enable Raster
// @see #IRQ_ENABLE #IRQ_STATUS
/// 0 | RST| Reaching a certain raster line. The line is specified by writing
/// | | to register 0xd012 and bit 7 of $d011 and internally stored by
/// | | the VIC for the raster compare. The test for reaching the
/// | | interrupt raster line is done in cycle 0 of every line (for line
/// | | 0, in cycle 1).
.const IRQ_RASTER = 1
/// DMA command copy
.const DMA_COMMAND_COPY = 0
/// Mask for PROCESSOR_PORT_DDR which allows only memory configuration to be written
.const PROCPORT_DDR_MEMORY_MASK = 7
/// RAM in 0xA000, 0xE000 I/O in 0xD000
.const PROCPORT_RAM_IO = 5
.const OFFSET_STRUCT_F018_DMAGIC_EN018B = 3
.const OFFSET_STRUCT_DMA_LIST_F018B_COUNT = 1
.const OFFSET_STRUCT_DMA_LIST_F018B_SRC = 3
.const OFFSET_STRUCT_DMA_LIST_F018B_DEST = 6
.const OFFSET_STRUCT_F018_DMAGIC_ADDRMB = 4
.const OFFSET_STRUCT_F018_DMAGIC_ADDRBANK = 2
.const OFFSET_STRUCT_F018_DMAGIC_ADDRMSB = 1
.const OFFSET_STRUCT_DMA_LIST_F018B_SRC_BANK = 5
.const OFFSET_STRUCT_DMA_LIST_F018B_DEST_BANK = 8
.const OFFSET_STRUCT_MOS4569_VICIII_KEY = $2f
.const OFFSET_STRUCT_MEGA65_VICIV_CONTROLB = $31
.const OFFSET_STRUCT_MEGA65_VICIV_CONTROLC = $54
.const OFFSET_STRUCT_MEGA65_VICIV_SIDBDRWD_LO = $5c
.const OFFSET_STRUCT_MOS6526_CIA_INTERRUPT = $d
.const OFFSET_STRUCT_MOS6569_VICII_RASTER = $12
.const OFFSET_STRUCT_MOS6569_VICII_CONTROL1 = $11
.const OFFSET_STRUCT_MOS6569_VICII_IRQ_ENABLE = $1a
.const OFFSET_STRUCT_MOS6569_VICII_IRQ_STATUS = $19
.const OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR = $20
/// Processor port data direction register
.label PROCPORT_DDR = 0
/// Processor Port Register controlling RAM/ROM configuration and the datasette
.label PROCPORT = 1
/// The VIC-II MOS 6567/6569
.label VICII = $d000
/// The VIC III MOS 4567/4569
.label VICIII = $d000
/// The VIC IV
.label VICIV = $d000
/// DMAgic F018 Controller
.label DMA = $d700
/// Default address of screen character matrix
.label DEFAULT_SCREEN = $800
/// The CIA#1: keyboard matrix, joystick #1/#2
.label CIA1 = $dc00
/// The vector used when the HARDWARE serves IRQ interrupts
.label HARDWARE_IRQ = $fffe
// Address after the end of the music
.label MUSIC_END = $5200
// Pointer to the music init routine
.label musicInit = MUSIC
// Pointer to the music play routine
.label musicPlay = MUSIC+3
.segment Code
// Raster IRQ routine
irq: {
pha
phx
phy
phz
// VICII->IRQ_STATUS = IRQ_RASTER
// Acknowledge the IRQ
ldz #IRQ_RASTER
stz VICII+OFFSET_STRUCT_MOS6569_VICII_IRQ_STATUS
// (VICII->BORDER_COLOR)++;
inc VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR
// memoryRemapBlock(0x40, 0x100)
// Remap memory to put music at $4000
jsr memoryRemapBlock
// (*musicPlay)()
// Play remapped SID
jsr musicPlay
// memoryRemap(0,0,0)
// Reset memory mapping
lda #<0
sta.z memoryRemap.upperPageOffset
sta.z memoryRemap.upperPageOffset+1
taz
sta.z memoryRemap.lowerPageOffset
sta.z memoryRemap.lowerPageOffset+1
jsr memoryRemap
// char raster = VICII->RASTER
// Wait for the next raster line
lda VICII+OFFSET_STRUCT_MOS6569_VICII_RASTER
__b2:
// while(VICII->RASTER==raster)
cmp VICII+OFFSET_STRUCT_MOS6569_VICII_RASTER
beq __b2
// (VICII->BORDER_COLOR)--;
dec VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR
// }
plz
ply
plx
pla
rti
}
main: {
// asm
// Stop IRQ's
sei
// memoryRemap(0,0,0)
// Map memory to BANK 0 : 0x00XXXX - giving access to I/O
lda #<0
sta.z memoryRemap.upperPageOffset
sta.z memoryRemap.upperPageOffset+1
taz
sta.z memoryRemap.lowerPageOffset
sta.z memoryRemap.lowerPageOffset+1
jsr memoryRemap
// VICIII->KEY = 0x47
// Enable MEGA65 features
ldz #$47
stz VICIII+OFFSET_STRUCT_MOS4569_VICIII_KEY
// VICIII->KEY = 0x53
ldz #$53
stz VICIII+OFFSET_STRUCT_MOS4569_VICIII_KEY
// VICIV->CONTROLB |= 0x40
// Enable 48MHz fast mode
lda #$40
ora VICIV+OFFSET_STRUCT_MEGA65_VICIV_CONTROLB
sta VICIV+OFFSET_STRUCT_MEGA65_VICIV_CONTROLB
// VICIV->CONTROLC |= 0x40
lda #$40
ora VICIV+OFFSET_STRUCT_MEGA65_VICIV_CONTROLC
sta VICIV+OFFSET_STRUCT_MEGA65_VICIV_CONTROLC
// *PROCPORT_DDR = PROCPORT_DDR_MEMORY_MASK
// no kernal or BASIC rom visible
ldz #PROCPORT_DDR_MEMORY_MASK
stz.z PROCPORT_DDR
// *PROCPORT = PROCPORT_RAM_IO
ldz #PROCPORT_RAM_IO
stz.z PROCPORT
// VICIV->SIDBDRWD_LO = 1
// open sideborder
ldz #1
stz VICIV+OFFSET_STRUCT_MEGA65_VICIV_SIDBDRWD_LO
// memcpy_dma4(1, (void*)0x0000, 0, upperCodeData, MUSIC_END-MUSIC)
// Transfer banked code/data to upper memory ($10000)
jsr memcpy_dma4
// memoryRemapBlock(0x40, 0x100)
// Remap [$4000-$5fff] to point to [$10000-$11fff]
jsr memoryRemapBlock
// asm
// Initialize SID
lda #0
// (*musicInit)()
jsr musicInit
// memoryRemap(0,0,0)
// Reset memory mapping
lda #<0
sta.z memoryRemap.upperPageOffset
sta.z memoryRemap.upperPageOffset+1
taz
sta.z memoryRemap.lowerPageOffset
sta.z memoryRemap.lowerPageOffset+1
jsr memoryRemap
// CIA1->INTERRUPT = CIA_INTERRUPT_CLEAR_ALL
// Set up raster interrupts C64 style
// Disable CIA 1 Timer IRQ
ldz #CIA_INTERRUPT_CLEAR_ALL
stz CIA1+OFFSET_STRUCT_MOS6526_CIA_INTERRUPT
// VICII->RASTER = 0xff
// Set raster line to 0xff
ldz #$ff
stz VICII+OFFSET_STRUCT_MOS6569_VICII_RASTER
// VICII->CONTROL1 &= 0x7f
lda #$7f
and VICII+OFFSET_STRUCT_MOS6569_VICII_CONTROL1
sta VICII+OFFSET_STRUCT_MOS6569_VICII_CONTROL1
// VICII->IRQ_ENABLE = IRQ_RASTER
// Enable Raster Interrupt
ldz #IRQ_RASTER
stz VICII+OFFSET_STRUCT_MOS6569_VICII_IRQ_ENABLE
// *HARDWARE_IRQ = &irq
// Set the IRQ routine
lda #<irq
sta HARDWARE_IRQ
lda #>irq
sta HARDWARE_IRQ+1
// asm
// Enable IRQ
cli
ldx #0
__b2:
// MUSIC[mem_destroy_i++]++;
inc MUSIC,x
inx
ldy #0
// Show unmapped MUSIC memory
__b3:
// for(char i=0;i<240;i++)
cpy #$f0
bcc __b4
jmp __b2
__b4:
// DEFAULT_SCREEN[i] = MUSIC[i]
lda MUSIC,y
sta DEFAULT_SCREEN,y
// for(char i=0;i<240;i++)
iny
jmp __b3
}
// Remap a single 8K memory block in the 64K address space of the 6502 to point somewhere else in the first 1MB memory space of the MEGA65.
// All the other 8K memory blocks will not be mapped and will point to their own address in the lowest 64K of the MEGA65 memory.
// blockPage: Page address of the 8K memory block to remap (ie. the block that is remapped is $100 * the passed page address.)
// memoryPage: Page address of the memory that the block should point to in the 1MB memory space of the MEGA65.
// Ie. the memory that will be pointed to is $100 * the passed page address. Only the lower 12bits of the passed value is used.
// void memoryRemapBlock(char blockPage, unsigned int memoryPage)
memoryRemapBlock: {
// Find the page offset (the number of pages to offset the block)
.const pageOffset = $100-$40
// Which block is being remapped? (0-7)
.const block = $40>>5
.const blockBits = 1<<block
// memoryRemap(blockBits, pageOffset, pageOffset)
lda #<pageOffset
sta.z memoryRemap.upperPageOffset
lda #>pageOffset
sta.z memoryRemap.upperPageOffset+1
ldz #blockBits
lda #<pageOffset
sta.z memoryRemap.lowerPageOffset
lda #>pageOffset
sta.z memoryRemap.lowerPageOffset+1
jsr memoryRemap
// }
rts
}
// Remap some of the eight 8K memory blocks in the 64K address space of the 6502 to point somewhere else in the first 1MB memory space of the MEGA65.
// After the remapping the CPU will access the mapped memory whenever it uses instructions that access a remapped block.
// See section 2.3.4 in http://www.zimmers.net/cbmpics/cbm/c65/c65manual.txt for a description of the CPU memory remapper of the C65.
// remapBlocks: Indicates which 8K blocks of the 6502 address space to remap. Each bit represents one 8K block
// - bit 0 Memory block $0000-$1fff. Use constant MEMORYBLOCK_0000.
// - bit 1 Memory block $2000-$3fff. Use constant MEMORYBLOCK_2000.
// - bit 2 Memory block $4000-$5fff. Use constant MEMORYBLOCK_4000.
// - bit 3 Memory block $6000-$7fff. Use constant MEMORYBLOCK_6000.
// - bit 4 Memory block $8000-$9fff. Use constant MEMORYBLOCK_8000.
// - bit 5 Memory block $a000-$bfff. Use constant MEMORYBLOCK_A000.
// - bit 6 Memory block $c000-$dfff. Use constant MEMORYBLOCK_C000.
// - bit 7 Memory block $e000-$ffff. Use constant MEMORYBLOCK_E000.
// lowerPageOffset: Offset that will be added to any remapped blocks in the lower 32K of memory (block 0-3).
// The offset is a page offset (meaning it is multiplied by 0x100). Only the lower 12bits of the passed value is used.
// - If block 0 ($0000-$1fff) is remapped it will point to lowerPageOffset*$100.
// - If block 1 ($2000-$3fff) is remapped it will point to lowerPageOffset*$100 + $2000.
// - If block 2 ($4000-$5fff) is remapped it will point to lowerPageOffset*$100 + $4000.
// - If block 3 ($6000-$7fff) is remapped it will point to lowerPageOffset*$100 + $6000.
// upperPageOffset: Offset that will be added to any remapped blocks in the upper 32K of memory (block 4-7).
// The offset is a page offset (meaning it is multiplied by 0x100). Only the lower 12bits of the passed value is used.
// - If block 4 ($8000-$9fff) is remapped it will point to upperPageOffset*$100 + $8000
// - If block 5 ($a000-$bfff) is remapped it will point to upperPageOffset*$100 + $a000.
// - If block 6 ($c000-$dfff) is remapped it will point to upperPageOffset*$100 + $c000.
// - If block 7 ($e000-$ffff) is remapped it will point to upperPageOffset*$100 + $e000.
// void memoryRemap(__register(Z) char remapBlocks, __zp(3) unsigned int lowerPageOffset, __zp(7) unsigned int upperPageOffset)
memoryRemap: {
.label aVal = $b
.label xVal = $a
.label __1 = 5
.label yVal = 9
.label zVal = 2
.label __6 = 6
.label lowerPageOffset = 3
.label upperPageOffset = 7
// char aVal = BYTE0(lowerPageOffset)
// lower blocks offset page low
lda.z lowerPageOffset
sta.z aVal
// remapBlocks << 4
tza
asl
asl
asl
asl
sta.z __1
// BYTE1(lowerPageOffset)
lda.z lowerPageOffset+1
// BYTE1(lowerPageOffset) & 0xf
and #$f
// char xVal = (remapBlocks << 4) | (BYTE1(lowerPageOffset) & 0xf)
// lower blocks to map + lower blocks offset high nibble
ora.z __1
sta.z xVal
// char yVal = BYTE0(upperPageOffset)
// upper blocks offset page
lda.z upperPageOffset
sta.z yVal
// remapBlocks & 0xf0
tza
and #$f0
sta.z __6
// BYTE1(upperPageOffset)
lda.z upperPageOffset+1
// BYTE1(upperPageOffset) & 0xf
and #$f
// char zVal = (remapBlocks & 0xf0) | (BYTE1(upperPageOffset) & 0xf)
// upper blocks to map + upper blocks offset page high nibble
ora.z __6
sta.z zVal
// asm
lda aVal
ldx xVal
ldy yVal
ldz zVal
map
eom
// }
rts
}
// Copy a memory block anywhere in first 4MB memory space using MEGA65 DMagic DMA
// Copies the values of num bytes from the location pointed to by source directly to the memory block pointed to by destination.
// - dest_bank The 64KB bank for the destination (0-127)
// - dest The destination address (within the MB and bank)
// - src_bank The 64KB bank for the source (0-127)
// - src The source address (within the MB and bank)
// - num The number of bytes to copy
// void memcpy_dma4(char dest_bank, void *dest, char src_bank, void *src, unsigned int num)
memcpy_dma4: {
.const num = MUSIC_END-MUSIC
.const dest_bank = 1
.const src_bank = 0
.label dest = 0
.label src = upperCodeData
// char dmaMode = DMA->EN018B
// Remember current F018 A/B mode
ldx DMA+OFFSET_STRUCT_F018_DMAGIC_EN018B
// memcpy_dma_command4.count = num
// Set up command
lda #<num
sta memcpy_dma_command4+OFFSET_STRUCT_DMA_LIST_F018B_COUNT
lda #>num
sta memcpy_dma_command4+OFFSET_STRUCT_DMA_LIST_F018B_COUNT+1
// memcpy_dma_command4.src_bank = src_bank
ldz #src_bank
stz memcpy_dma_command4+OFFSET_STRUCT_DMA_LIST_F018B_SRC_BANK
// memcpy_dma_command4.src = src
lda #<src
sta memcpy_dma_command4+OFFSET_STRUCT_DMA_LIST_F018B_SRC
lda #>src
sta memcpy_dma_command4+OFFSET_STRUCT_DMA_LIST_F018B_SRC+1
// memcpy_dma_command4.dest_bank = dest_bank
ldz #dest_bank
stz memcpy_dma_command4+OFFSET_STRUCT_DMA_LIST_F018B_DEST_BANK
// memcpy_dma_command4.dest = dest
lda #<dest
sta memcpy_dma_command4+OFFSET_STRUCT_DMA_LIST_F018B_DEST
lda #>dest
sta memcpy_dma_command4+OFFSET_STRUCT_DMA_LIST_F018B_DEST+1
// DMA->EN018B = 1
// Set F018B mode
ldz #1
stz DMA+OFFSET_STRUCT_F018_DMAGIC_EN018B
// DMA->ADDRMB = 0
// Set address of DMA list
ldz #0
stz DMA+OFFSET_STRUCT_F018_DMAGIC_ADDRMB
// DMA->ADDRBANK = 0
stz DMA+OFFSET_STRUCT_F018_DMAGIC_ADDRBANK
// DMA-> ADDRMSB = BYTE1(&memcpy_dma_command4)
ldz #>memcpy_dma_command4
stz DMA+OFFSET_STRUCT_F018_DMAGIC_ADDRMSB
// DMA-> ADDRLSBTRIG = BYTE0(&memcpy_dma_command4)
// Trigger the DMA (without option lists)
ldz #<memcpy_dma_command4
stz DMA
// DMA->EN018B = dmaMode
// Re-enable F018A mode
stx DMA+OFFSET_STRUCT_F018_DMAGIC_EN018B
// }
rts
}
.segment Data
// Array containing the banked upper memory code/data to be transferred to upper memory before execution
upperCodeData:
.segmentout [segments="Banked"]
// DMA list entry for copying data in the 1MB memory space
memcpy_dma_command4: .byte DMA_COMMAND_COPY
.word 0, 0
.byte 0
.word 0
.byte 0, 0
.word 0
.segment DataBanked
.pc = $4000 "MUSIC"
// SID tune at an absolute address
MUSIC:
.const music = LoadSid("Cybernoid_II_4000.sid")
.fill music.size, music.getData(i)