About your vsync/hsync routines. If you are on a cd, you are using the bios. There's a piece of code in the bios that'll be called at each vsync/hsync and check if there are user specified routines (or callback if you want), and call them. I guess you are setting the vsync/hsync with ex_setvec?
So basically your vsync/hsync routines can be anywhere. You can't completely overwrite/replace interrupt bios routines. The mpr 1 is not accessible because it's always set to $f8 (RAM). mpr 0 is set to $ff (ROM).
I think you mixed up MPR and bank. They are 2 different things. A bank is a bloc of 8 kilobytes in ROM. A MPR is a register used to compute an address.
The PC-Engine has an 8 bits cpu with 16 bits addressing. As the addresses are represented as 16bits values, the CPU can't access the whole memory space which is 2M bytes wide. We will need an address of 21 bits to directly access it. So the trick used is to split the 16 bits address (the logical address) in 2 parts. One part will specify a memory space block of 8kB and the second part an offset in that block. This will gives us 13 bits for the 8kB offset and 3 bits for the 8kB block to use. But with 3 bits you can only specify 8 blocks. That's where MPR registers enter the game. The 3 bits don't specify directly a 8kB block but a MPR register. And this register stores a byte. So you can have up to 256 8kB block.
Erm.. Maybe a piece of C code will be clearer:
Code: Select all
// The address of an instruction. For example lda $cd81
uint16_t logical_address = 0xcd81;
uint8_t mprIndex = logical_address >> 13;
uint16_t offset = logical_address & 0x1fff;
// The real physical address
uint32_t physical_address = (mprRegister[mprIndex] << 13) + offset;
The other confusing stuff may be the .bank and .org directives.
.bank i tells the assembler that your code/data will be stored at the
ith 8kB block of your file.
.org has a double role (and this is fucking annoying sometimes). It tells the assembler which is the logical address of your code/data. ie in which mpr your bank will be mapped. The second role is to specify the bank offset where your code/data will stored.
Again a little bit of C code:
Code: Select all
uint8_t bank = 9;
uint16_t org = 0x6000;
// let's say that this pointer points to the compiled code
uint8_t *data;
// pceas will write your code/data like this
fwrite(data, 1, (bank * 8192) + (org & 0x1fff), output);
And for the second purpose of
.org, suppose the assembler is compiling this piece of code
Code: Select all
.bank 11
.org $6000
data:
.db 1,2,3,4
foo:
clx
cly
loop:
lda data, X
sta [_di], Y
iny
inx
cpx #4
bne loop
rts
The values of data, foo and loop are computed with the help of the
.org value. data will be equal to $6000, foo $6004 and loop $6006. This means that your code will be translated as:
Code: Select all
.bank 11
.org $6000
data:
.db 1,2,3,4
foo:
clx
cly
loop:
lda $6000, X ; data = $6000
sta [_di], Y
iny
inx
cpx #4
bne $6006 ; loop = $6006
rts
If you want to use this code, you'll have to first set the MPR used in your .org to the bank you specify in .bank. Here the mpr used is ($6000 >> 13) = 3. If you do a "jsr foo" and you forgot to map bank 11 to mpr 3, you'll end up where the mpr was set to and it will not be the place you wanted.
Code: Select all
lda #11 ; Map bank 11
tam #3 ; to MPR register 3
stw $2200, <_di ; _di will point to bss ram area
jsr foo ; You'll jump to your code
; and copy 1,2,3,4 into the first 4 bytes of the bss
Let's say you wrote this another piece of code:
Code: Select all
.bank 9
.org $6000
stuff
.db 5,6,7,8
bar:
clx
cly
boink:
lda data, X
sta [_di], Y
iny
inx
cmp #8
bne boink
This code will copy the bytes stored at data into the area pointed by _di. It will stop if the byte copied is equal to 8.
Ok. Now imagine that we now do this:
Code: Select all
lda #9
tam #3
stw $2200, <_di
jsr foo
What will the first 4 bytes of $2200 contain?
You have 2 hours.